diff options
100 files changed, 2983 insertions, 5390 deletions
diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index babfb6143bd9..f6882ad0b3c3 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -149,60 +149,15 @@ Device Instance and Driver Handling Driver Load ----------- -IRQ Registration -~~~~~~~~~~~~~~~~ - -The DRM core tries to facilitate IRQ handler registration and -unregistration by providing :c:func:`drm_irq_install()` and -:c:func:`drm_irq_uninstall()` functions. Those functions only -support a single interrupt per device, devices that use more than one -IRQs need to be handled manually. - -Managed IRQ Registration -'''''''''''''''''''''''' - -:c:func:`drm_irq_install()` starts by calling the irq_preinstall -driver operation. The operation is optional and must make sure that the -interrupt will not get fired by clearing all pending interrupt flags or -disabling the interrupt. - -The passed-in IRQ will then be requested by a call to -:c:func:`request_irq()`. If the DRIVER_IRQ_SHARED driver feature -flag is set, a shared (IRQF_SHARED) IRQ handler will be requested. - -The IRQ handler function must be provided as the mandatory irq_handler -driver operation. It will get passed directly to -:c:func:`request_irq()` and thus has the same prototype as all IRQ -handlers. It will get called with a pointer to the DRM device as the -second argument. - -Finally the function calls the optional irq_postinstall driver -operation. The operation usually enables interrupts (excluding the -vblank interrupt, which is enabled separately), but drivers may choose -to enable/disable interrupts at a different time. - -:c:func:`drm_irq_uninstall()` is similarly used to uninstall an -IRQ handler. It starts by waking up all processes waiting on a vblank -interrupt to make sure they don't hang, and then calls the optional -irq_uninstall driver operation. The operation must disable all hardware -interrupts. Finally the function frees the IRQ by calling -:c:func:`free_irq()`. - -Manual IRQ Registration -''''''''''''''''''''''' - -Drivers that require multiple interrupt handlers can't use the managed -IRQ registration functions. In that case IRQs must be registered and -unregistered manually (usually with the :c:func:`request_irq()` and -:c:func:`free_irq()` functions, or their :c:func:`devm_request_irq()` and -:c:func:`devm_free_irq()` equivalents). - -When manually registering IRQs, drivers must not set the -DRIVER_HAVE_IRQ driver feature flag, and must not provide the -irq_handler driver operation. They must set the :c:type:`struct -drm_device <drm_device>` irq_enabled field to 1 upon -registration of the IRQs, and clear it to 0 after unregistering the -IRQs. + +IRQ Helper Library +~~~~~~~~~~~~~~~~~~ + +.. kernel-doc:: drivers/gpu/drm/drm_irq.c + :doc: irq helpers + +.. kernel-doc:: drivers/gpu/drm/drm_irq.c + :export: Memory Manager Initialization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index bfecd21a8cdf..2d77c9580164 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst @@ -612,8 +612,8 @@ operation handler. Vertical Blanking and Interrupt Handling Functions Reference ------------------------------------------------------------ -.. kernel-doc:: include/drm/drm_irq.h +.. kernel-doc:: include/drm/drm_vblank.h :internal: -.. kernel-doc:: drivers/gpu/drm/drm_irq.c +.. kernel-doc:: drivers/gpu/drm/drm_vblank.c :export: diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 1bdb7356a310..95a517077eb0 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -177,19 +177,6 @@ following drivers still use ``struct_mutex``: ``msm``, ``omapdrm`` and Contact: Daniel Vetter, respective driver maintainers -Switch to drm_connector_list_iter for any connector_list walking ----------------------------------------------------------------- - -Connectors can be hotplugged, and we now have a special list of helpers to walk -the connector_list in a race-free fashion, without incurring deadlocks on -mutexes and other fun stuff. - -Unfortunately most drivers are not converted yet. At least all those supporting -DP MST hotplug should be converted, since for those drivers the difference -matters. See drm_for_each_connector_iter() vs. drm_for_each_connector(). - -Contact: Daniel Vetter - Core refactorings ================= diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index c156fecfb362..acc88942c2e5 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -16,7 +16,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ drm_framebuffer.o drm_connector.o drm_blend.o \ drm_encoder.o drm_mode_object.o drm_property.o \ drm_plane.o drm_color_mgmt.o drm_print.o \ - drm_dumb_buffers.o drm_mode_config.o + drm_dumb_buffers.o drm_mode_config.o drm_vblank.o drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o drm-$(CONFIG_DRM_VM) += drm_vm.o diff --git a/drivers/gpu/drm/arc/arcpgu_drv.c b/drivers/gpu/drm/arc/arcpgu_drv.c index 1926b200e4cb..3e43a5d4fb09 100644 --- a/drivers/gpu/drm/arc/arcpgu_drv.c +++ b/drivers/gpu/drm/arc/arcpgu_drv.c @@ -155,7 +155,6 @@ static int arcpgu_unload(struct drm_device *drm) arcpgu->fbdev = NULL; } drm_kms_helper_poll_fini(drm); - drm_vblank_cleanup(drm); drm_mode_config_cleanup(drm); return 0; diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index 0f49c4b12772..345c8357b273 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -340,7 +340,6 @@ err_register: } err_fbdev: drm_kms_helper_poll_fini(drm); - drm_vblank_cleanup(drm); err_vblank: pm_runtime_disable(drm->dev); err_pm_active: @@ -368,7 +367,6 @@ static void hdlcd_drm_unbind(struct device *dev) } drm_kms_helper_poll_fini(drm); component_unbind_all(dev, drm); - drm_vblank_cleanup(drm); pm_runtime_get_sync(drm->dev); drm_irq_uninstall(drm); pm_runtime_put_sync(drm->dev); diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index 0d3eb537d08b..01b13d219917 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -652,7 +652,6 @@ register_fail: drm_kms_helper_poll_fini(drm); fbdev_fail: pm_runtime_get_sync(dev); - drm_vblank_cleanup(drm); vblank_fail: malidp_se_irq_fini(drm); malidp_de_irq_fini(drm); @@ -692,7 +691,6 @@ static void malidp_unbind(struct device *dev) } drm_kms_helper_poll_fini(drm); pm_runtime_get_sync(dev); - drm_vblank_cleanup(drm); malidp_se_irq_fini(drm); malidp_de_irq_fini(drm); component_unbind_all(dev, drm); diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c index 53bfa56ca47a..53489859997b 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c @@ -140,13 +140,13 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) cfg); } -static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *c, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static enum drm_mode_status +atmel_hlcdc_crtc_mode_valid(struct drm_crtc *c, + const struct drm_display_mode *mode) { struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); - return atmel_hlcdc_dc_mode_valid(crtc->dc, adjusted_mode) == MODE_OK; + return atmel_hlcdc_dc_mode_valid(crtc->dc, mode); } static void atmel_hlcdc_crtc_disable(struct drm_crtc *c) @@ -315,7 +315,7 @@ static void atmel_hlcdc_crtc_atomic_flush(struct drm_crtc *crtc, } static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = { - .mode_fixup = atmel_hlcdc_crtc_mode_fixup, + .mode_valid = atmel_hlcdc_crtc_mode_valid, .mode_set = drm_helper_crtc_mode_set, .mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb, .mode_set_base = drm_helper_crtc_mode_set_base, diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index f4a3065f7f51..30dbffdb45a3 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -375,8 +375,9 @@ static const struct of_device_id atmel_hlcdc_of_match[] = { }; MODULE_DEVICE_TABLE(of, atmel_hlcdc_of_match); -int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, - struct drm_display_mode *mode) +enum drm_mode_status +atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, + const struct drm_display_mode *mode) { int vfront_porch = mode->vsync_start - mode->vdisplay; int vback_porch = mode->vtotal - mode->vsync_end; @@ -678,7 +679,6 @@ static void atmel_hlcdc_dc_unload(struct drm_device *dev) flush_workqueue(dc->wq); drm_kms_helper_poll_fini(dev); drm_mode_config_cleanup(dev); - drm_vblank_cleanup(dev); pm_runtime_get_sync(dev->dev); drm_irq_uninstall(dev); diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h index 433641b6e23b..b0596a84c1b8 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.h @@ -422,8 +422,9 @@ static inline void atmel_hlcdc_layer_init(struct atmel_hlcdc_layer *layer, layer->regmap = regmap; } -int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, - struct drm_display_mode *mode); +enum drm_mode_status +atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, + const struct drm_display_mode *mode); int atmel_hlcdc_create_planes(struct drm_device *dev); void atmel_hlcdc_plane_irq(struct atmel_hlcdc_plane *plane); diff --git a/drivers/gpu/drm/bridge/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix-anx78xx.c index a2a82366a771..9006578b9789 100644 --- a/drivers/gpu/drm/bridge/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix-anx78xx.c @@ -1061,18 +1061,18 @@ static int anx78xx_bridge_attach(struct drm_bridge *bridge) return 0; } -static bool anx78xx_bridge_mode_fixup(struct drm_bridge *bridge, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static enum drm_mode_status +anx78xx_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_mode *mode) { if (mode->flags & DRM_MODE_FLAG_INTERLACE) - return false; + return MODE_NO_INTERLACE; /* Max 1200p at 5.4 Ghz, one lane */ if (mode->clock > 154000) - return false; + return MODE_CLOCK_HIGH; - return true; + return MODE_OK; } static void anx78xx_bridge_disable(struct drm_bridge *bridge) @@ -1129,7 +1129,7 @@ static void anx78xx_bridge_enable(struct drm_bridge *bridge) static const struct drm_bridge_funcs anx78xx_bridge_funcs = { .attach = anx78xx_bridge_attach, - .mode_fixup = anx78xx_bridge_mode_fixup, + .mode_valid = anx78xx_bridge_mode_valid, .disable = anx78xx_bridge_disable, .mode_set = anx78xx_bridge_mode_set, .enable = anx78xx_bridge_enable, diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index e1637011e18a..77dcef00998c 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -328,7 +328,7 @@ static s32 __user *get_out_fence_for_crtc(struct drm_atomic_state *state, * Zero on success, error code on failure. Cannot return -EDEADLK. */ int drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, - struct drm_display_mode *mode) + const struct drm_display_mode *mode) { struct drm_mode_modeinfo umode; diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 636e561486a8..93b0221d5d0f 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -32,6 +32,7 @@ #include <drm/drm_atomic_helper.h> #include <linux/dma-fence.h> +#include "drm_crtc_helper_internal.h" #include "drm_crtc_internal.h" /** @@ -452,6 +453,69 @@ mode_fixup(struct drm_atomic_state *state) return 0; } +static enum drm_mode_status mode_valid_path(struct drm_connector *connector, + struct drm_encoder *encoder, + struct drm_crtc *crtc, + struct drm_display_mode *mode) +{ + enum drm_mode_status ret; + + ret = drm_encoder_mode_valid(encoder, mode); + if (ret != MODE_OK) { + DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] mode_valid() failed\n", + encoder->base.id, encoder->name); + return ret; + } + + ret = drm_bridge_mode_valid(encoder->bridge, mode); + if (ret != MODE_OK) { + DRM_DEBUG_ATOMIC("[BRIDGE] mode_valid() failed\n"); + return ret; + } + + ret = drm_crtc_mode_valid(crtc, mode); + if (ret != MODE_OK) { + DRM_DEBUG_ATOMIC("[CRTC:%d:%s] mode_valid() failed\n", + crtc->base.id, crtc->name); + return ret; + } + + return ret; +} + +static int +mode_valid(struct drm_atomic_state *state) +{ + struct drm_connector_state *conn_state; + struct drm_connector *connector; + int i; + + for_each_new_connector_in_state(state, connector, conn_state, i) { + struct drm_encoder *encoder = conn_state->best_encoder; + struct drm_crtc *crtc = conn_state->crtc; + struct drm_crtc_state *crtc_state; + enum drm_mode_status mode_status; + struct drm_display_mode *mode; + + if (!crtc || !encoder) + continue; + + crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + if (!crtc_state) + continue; + if (!crtc_state->mode_changed && !crtc_state->connectors_changed) + continue; + + mode = &crtc_state->mode; + + mode_status = mode_valid_path(connector, encoder, crtc, mode); + if (mode_status != MODE_OK) + return -EINVAL; + } + + return 0; +} + /** * drm_atomic_helper_check_modeset - validate state object for modeset changes * @dev: DRM device @@ -466,13 +530,15 @@ mode_fixup(struct drm_atomic_state *state) * 2. &drm_connector_helper_funcs.atomic_check to validate the connector state. * 3. If it's determined a modeset is needed then all connectors on the affected crtc * crtc are added and &drm_connector_helper_funcs.atomic_check is run on them. - * 4. &drm_bridge_funcs.mode_fixup is called on all encoder bridges. - * 5. &drm_encoder_helper_funcs.atomic_check is called to validate any encoder state. + * 4. &drm_encoder_helper_funcs.mode_valid, &drm_bridge_funcs.mode_valid and + * &drm_crtc_helper_funcs.mode_valid are called on the affected components. + * 5. &drm_bridge_funcs.mode_fixup is called on all encoder bridges. + * 6. &drm_encoder_helper_funcs.atomic_check is called to validate any encoder state. * This function is only called when the encoder will be part of a configured crtc, * it must not be used for implementing connector property validation. * If this function is NULL, &drm_atomic_encoder_helper_funcs.mode_fixup is called * instead. - * 6. &drm_crtc_helper_funcs.mode_fixup is called last, to fix up the mode with crtc constraints. + * 7. &drm_crtc_helper_funcs.mode_fixup is called last, to fix up the mode with crtc constraints. * * &drm_crtc_state.mode_changed is set when the input mode is changed. * &drm_crtc_state.connectors_changed is set when a connector is added or @@ -617,6 +683,10 @@ drm_atomic_helper_check_modeset(struct drm_device *dev, return ret; } + ret = mode_valid(state); + if (ret) + return ret; + return mode_fixup(state); } EXPORT_SYMBOL(drm_atomic_helper_check_modeset); diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 86a7637ba344..dc8cdfe1dcac 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -206,6 +206,39 @@ bool drm_bridge_mode_fixup(struct drm_bridge *bridge, EXPORT_SYMBOL(drm_bridge_mode_fixup); /** + * drm_bridge_mode_valid - validate the mode against all bridges in the + * encoder chain. + * @bridge: bridge control structure + * @mode: desired mode to be validated + * + * Calls &drm_bridge_funcs.mode_valid for all the bridges in the encoder + * chain, starting from the first bridge to the last. If at least one bridge + * does not accept the mode the function returns the error code. + * + * Note: the bridge passed should be the one closest to the encoder. + * + * RETURNS: + * MODE_OK on success, drm_mode_status Enum error code on failure + */ +enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_mode *mode) +{ + enum drm_mode_status ret = MODE_OK; + + if (!bridge) + return ret; + + if (bridge->funcs->mode_valid) + ret = bridge->funcs->mode_valid(bridge, mode); + + if (ret != MODE_OK) + return ret; + + return drm_bridge_mode_valid(bridge->next, mode); +} +EXPORT_SYMBOL(drm_bridge_mode_valid); + +/** * drm_bridge_disable - disables all bridges in the encoder chain * @bridge: bridge control structure * diff --git a/drivers/gpu/drm/drm_crtc_helper_internal.h b/drivers/gpu/drm/drm_crtc_helper_internal.h index 28295e5d0d9e..b5ac1581e623 100644 --- a/drivers/gpu/drm/drm_crtc_helper_internal.h +++ b/drivers/gpu/drm/drm_crtc_helper_internal.h @@ -26,7 +26,11 @@ * implementation details and are not exported to drivers. */ +#include <drm/drm_connector.h> +#include <drm/drm_crtc.h> #include <drm/drm_dp_helper.h> +#include <drm/drm_encoder.h> +#include <drm/drm_modes.h> /* drm_fb_helper.c */ #ifdef CONFIG_DRM_FBDEV_EMULATION @@ -63,3 +67,11 @@ static inline void drm_dp_aux_unregister_devnode(struct drm_dp_aux *aux) { } #endif + +/* drm_probe_helper.c */ +enum drm_mode_status drm_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode); +enum drm_mode_status drm_encoder_mode_valid(struct drm_encoder *encoder, + const struct drm_display_mode *mode); +enum drm_mode_status drm_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode); diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 222eb1a8549b..bfd237c15e76 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -2836,16 +2836,15 @@ static void drm_dp_mst_dump_mstb(struct seq_file *m, static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr, char *buf) { - int ret; int i; - for (i = 0; i < 4; i++) { - ret = drm_dp_dpcd_read(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS + (i * 16), &buf[i * 16], 16); - if (ret != 16) - break; + + for (i = 0; i < 64; i += 16) { + if (drm_dp_dpcd_read(mgr->aux, + DP_PAYLOAD_TABLE_UPDATE_STATUS + i, + &buf[i], 16) != 16) + return false; } - if (i == 4) - return true; - return false; + return true; } static void fetch_monitor_name(struct drm_dp_mst_topology_mgr *mgr, @@ -2909,42 +2908,24 @@ void drm_dp_mst_dump_topology(struct seq_file *m, mutex_lock(&mgr->lock); if (mgr->mst_primary) { u8 buf[64]; - bool bret; int ret; + ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, buf, DP_RECEIVER_CAP_SIZE); - seq_printf(m, "dpcd: "); - for (i = 0; i < DP_RECEIVER_CAP_SIZE; i++) - seq_printf(m, "%02x ", buf[i]); - seq_printf(m, "\n"); + seq_printf(m, "dpcd: %*ph\n", DP_RECEIVER_CAP_SIZE, buf); ret = drm_dp_dpcd_read(mgr->aux, DP_FAUX_CAP, buf, 2); - seq_printf(m, "faux/mst: "); - for (i = 0; i < 2; i++) - seq_printf(m, "%02x ", buf[i]); - seq_printf(m, "\n"); + seq_printf(m, "faux/mst: %*ph\n", 2, buf); ret = drm_dp_dpcd_read(mgr->aux, DP_MSTM_CTRL, buf, 1); - seq_printf(m, "mst ctrl: "); - for (i = 0; i < 1; i++) - seq_printf(m, "%02x ", buf[i]); - seq_printf(m, "\n"); + seq_printf(m, "mst ctrl: %*ph\n", 1, buf); /* dump the standard OUI branch header */ ret = drm_dp_dpcd_read(mgr->aux, DP_BRANCH_OUI, buf, DP_BRANCH_OUI_HEADER_SIZE); - seq_printf(m, "branch oui: "); - for (i = 0; i < 0x3; i++) - seq_printf(m, "%02x", buf[i]); - seq_printf(m, " devid: "); + seq_printf(m, "branch oui: %*phN devid: ", 3, buf); for (i = 0x3; i < 0x8 && buf[i]; i++) seq_printf(m, "%c", buf[i]); - - seq_printf(m, " revision: hw: %x.%x sw: %x.%x", buf[0x9] >> 4, buf[0x9] & 0xf, buf[0xa], buf[0xb]); - seq_printf(m, "\n"); - bret = dump_dp_payload_table(mgr, buf); - if (bret == true) { - seq_printf(m, "payload table: "); - for (i = 0; i < 63; i++) - seq_printf(m, "%02x ", buf[i]); - seq_printf(m, "\n"); - } + seq_printf(m, " revision: hw: %x.%x sw: %x.%x\n", + buf[0x9] >> 4, buf[0x9] & 0xf, buf[0xa], buf[0xb]); + if (dump_dp_payload_table(mgr, buf)) + seq_printf(m, "payload table: %*ph\n", 63, buf); } diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 3d8e8f878924..ba3f5fb21959 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -53,8 +53,9 @@ int drm_name_info(struct seq_file *m, void *data); int drm_clients_info(struct seq_file *m, void* data); int drm_gem_name_info(struct seq_file *m, void *data); -/* drm_irq.c */ +/* drm_vblank.c */ extern unsigned int drm_timestamp_monotonic; +void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe); /* IOCTLS */ int drm_wait_vblank(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index c7debaad67f8..3b04c25100ae 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -3,6 +3,25 @@ * * \author Rickard E. (Rik) Faith <[email protected]> * \author Gareth Hughes <[email protected]> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. */ /* @@ -32,429 +51,30 @@ * OTHER DEALINGS IN THE SOFTWARE. */ +#include <drm/drm_irq.h> #include <drm/drmP.h> -#include "drm_trace.h" -#include "drm_internal.h" #include <linux/interrupt.h> /* For task queue support */ -#include <linux/slab.h> #include <linux/vgaarb.h> #include <linux/export.h> -/* Retry timestamp calculation up to 3 times to satisfy - * drm_timestamp_precision before giving up. - */ -#define DRM_TIMESTAMP_MAXRETRIES 3 - -/* Threshold in nanoseconds for detection of redundant - * vblank irq in drm_handle_vblank(). 1 msec should be ok. - */ -#define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000 - -static bool -drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe, - struct timeval *tvblank, bool in_vblank_irq); - -static unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ - -/* - * Default to use monotonic timestamps for wait-for-vblank and page-flip - * complete events. - */ -unsigned int drm_timestamp_monotonic = 1; - -static int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */ - -module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); -module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); -module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); -MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)"); -MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]"); -MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); - -static void store_vblank(struct drm_device *dev, unsigned int pipe, - u32 vblank_count_inc, - struct timeval *t_vblank, u32 last) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - - assert_spin_locked(&dev->vblank_time_lock); - - vblank->last = last; - - write_seqlock(&vblank->seqlock); - vblank->time = *t_vblank; - vblank->count += vblank_count_inc; - write_sequnlock(&vblank->seqlock); -} - -/* - * "No hw counter" fallback implementation of .get_vblank_counter() hook, - * if there is no useable hardware frame counter available. - */ -static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe) -{ - WARN_ON_ONCE(dev->max_vblank_count != 0); - return 0; -} - -static u32 __get_vblank_counter(struct drm_device *dev, unsigned int pipe) -{ - if (drm_core_check_feature(dev, DRIVER_MODESET)) { - struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); - - if (crtc->funcs->get_vblank_counter) - return crtc->funcs->get_vblank_counter(crtc); - } - - if (dev->driver->get_vblank_counter) - return dev->driver->get_vblank_counter(dev, pipe); - - return drm_vblank_no_hw_counter(dev, pipe); -} - -/* - * Reset the stored timestamp for the current vblank count to correspond - * to the last vblank occurred. - * - * Only to be called from drm_crtc_vblank_on(). - * - * Note: caller must hold &drm_device.vbl_lock since this reads & writes - * device vblank fields. - */ -static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe) -{ - u32 cur_vblank; - bool rc; - struct timeval t_vblank; - int count = DRM_TIMESTAMP_MAXRETRIES; - - spin_lock(&dev->vblank_time_lock); - - /* - * sample the current counter to avoid random jumps - * when drm_vblank_enable() applies the diff - */ - do { - cur_vblank = __get_vblank_counter(dev, pipe); - rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false); - } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0); - - /* - * Only reinitialize corresponding vblank timestamp if high-precision query - * available and didn't fail. Otherwise reinitialize delayed at next vblank - * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid. - */ - if (!rc) - t_vblank = (struct timeval) {0, 0}; - - /* - * +1 to make sure user will never see the same - * vblank counter value before and after a modeset - */ - store_vblank(dev, pipe, 1, &t_vblank, cur_vblank); - - spin_unlock(&dev->vblank_time_lock); -} - -/* - * Call back into the driver to update the appropriate vblank counter - * (specified by @pipe). Deal with wraparound, if it occurred, and - * update the last read value so we can deal with wraparound on the next - * call if necessary. - * - * Only necessary when going from off->on, to account for frames we - * didn't get an interrupt for. - * - * Note: caller must hold &drm_device.vbl_lock since this reads & writes - * device vblank fields. - */ -static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, - bool in_vblank_irq) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - u32 cur_vblank, diff; - bool rc; - struct timeval t_vblank; - int count = DRM_TIMESTAMP_MAXRETRIES; - int framedur_ns = vblank->framedur_ns; - - /* - * Interrupts were disabled prior to this call, so deal with counter - * wrap if needed. - * NOTE! It's possible we lost a full dev->max_vblank_count + 1 events - * here if the register is small or we had vblank interrupts off for - * a long time. - * - * We repeat the hardware vblank counter & timestamp query until - * we get consistent results. This to prevent races between gpu - * updating its hardware counter while we are retrieving the - * corresponding vblank timestamp. - */ - do { - cur_vblank = __get_vblank_counter(dev, pipe); - rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq); - } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0); - - if (dev->max_vblank_count != 0) { - /* trust the hw counter when it's around */ - diff = (cur_vblank - vblank->last) & dev->max_vblank_count; - } else if (rc && framedur_ns) { - const struct timeval *t_old; - u64 diff_ns; - - t_old = &vblank->time; - diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old); - - /* - * Figure out how many vblanks we've missed based - * on the difference in the timestamps and the - * frame/field duration. - */ - diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns); - - if (diff == 0 && in_vblank_irq) - DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored." - " diff_ns = %lld, framedur_ns = %d)\n", - pipe, (long long) diff_ns, framedur_ns); - } else { - /* some kind of default for drivers w/o accurate vbl timestamping */ - diff = in_vblank_irq ? 1 : 0; - } - - /* - * Within a drm_vblank_pre_modeset - drm_vblank_post_modeset - * interval? If so then vblank irqs keep running and it will likely - * happen that the hardware vblank counter is not trustworthy as it - * might reset at some point in that interval and vblank timestamps - * are not trustworthy either in that interval. Iow. this can result - * in a bogus diff >> 1 which must be avoided as it would cause - * random large forward jumps of the software vblank counter. - */ - if (diff > 1 && (vblank->inmodeset & 0x2)) { - DRM_DEBUG_VBL("clamping vblank bump to 1 on crtc %u: diffr=%u" - " due to pre-modeset.\n", pipe, diff); - diff = 1; - } - - DRM_DEBUG_VBL("updating vblank count on crtc %u:" - " current=%u, diff=%u, hw=%u hw_last=%u\n", - pipe, vblank->count, diff, cur_vblank, vblank->last); - - if (diff == 0) { - WARN_ON_ONCE(cur_vblank != vblank->last); - return; - } - - /* - * Only reinitialize corresponding vblank timestamp if high-precision query - * available and didn't fail, or we were called from the vblank interrupt. - * Otherwise reinitialize delayed at next vblank interrupt and assign 0 - * for now, to mark the vblanktimestamp as invalid. - */ - if (!rc && in_vblank_irq) - t_vblank = (struct timeval) {0, 0}; - - store_vblank(dev, pipe, diff, &t_vblank, cur_vblank); -} - -static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return 0; - - return vblank->count; -} - -/** - * drm_accurate_vblank_count - retrieve the master vblank counter - * @crtc: which counter to retrieve - * - * This function is similar to @drm_crtc_vblank_count but this - * function interpolates to handle a race with vblank irq's. - * - * This is mostly useful for hardware that can obtain the scanout - * position, but doesn't have a frame counter. - */ -u32 drm_accurate_vblank_count(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - unsigned int pipe = drm_crtc_index(crtc); - u32 vblank; - unsigned long flags; - - WARN(!dev->driver->get_vblank_timestamp, - "This function requires support for accurate vblank timestamps."); - - spin_lock_irqsave(&dev->vblank_time_lock, flags); - - drm_update_vblank_count(dev, pipe, false); - vblank = drm_vblank_count(dev, pipe); - - spin_unlock_irqrestore(&dev->vblank_time_lock, flags); - - return vblank; -} -EXPORT_SYMBOL(drm_accurate_vblank_count); - -static void __disable_vblank(struct drm_device *dev, unsigned int pipe) -{ - if (drm_core_check_feature(dev, DRIVER_MODESET)) { - struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); - - if (crtc->funcs->disable_vblank) { - crtc->funcs->disable_vblank(crtc); - return; - } - } - - dev->driver->disable_vblank(dev, pipe); -} - -/* - * Disable vblank irq's on crtc, make sure that last vblank count - * of hardware and corresponding consistent software vblank counter - * are preserved, even if there are any spurious vblank irq's after - * disable. - */ -static void vblank_disable_and_save(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; - - assert_spin_locked(&dev->vbl_lock); - - /* Prevent vblank irq processing while disabling vblank irqs, - * so no updates of timestamps or count can happen after we've - * disabled. Needed to prevent races in case of delayed irq's. - */ - spin_lock_irqsave(&dev->vblank_time_lock, irqflags); - - /* - * Only disable vblank interrupts if they're enabled. This avoids - * calling the ->disable_vblank() operation in atomic context with the - * hardware potentially runtime suspended. - */ - if (vblank->enabled) { - __disable_vblank(dev, pipe); - vblank->enabled = false; - } - - /* - * Always update the count and timestamp to maintain the - * appearance that the counter has been ticking all along until - * this time. This makes the count account for the entire time - * between drm_crtc_vblank_on() and drm_crtc_vblank_off(). - */ - drm_update_vblank_count(dev, pipe, false); - - spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); -} - -static void vblank_disable_fn(unsigned long arg) -{ - struct drm_vblank_crtc *vblank = (void *)arg; - struct drm_device *dev = vblank->dev; - unsigned int pipe = vblank->pipe; - unsigned long irqflags; - - spin_lock_irqsave(&dev->vbl_lock, irqflags); - if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) { - DRM_DEBUG("disabling vblank on crtc %u\n", pipe); - vblank_disable_and_save(dev, pipe); - } - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); -} - -/** - * drm_vblank_cleanup - cleanup vblank support - * @dev: DRM device - * - * This function cleans up any resources allocated in drm_vblank_init. - */ -void drm_vblank_cleanup(struct drm_device *dev) -{ - unsigned int pipe; - - /* Bail if the driver didn't call drm_vblank_init() */ - if (dev->num_crtcs == 0) - return; - - for (pipe = 0; pipe < dev->num_crtcs; pipe++) { - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - - WARN_ON(READ_ONCE(vblank->enabled) && - drm_core_check_feature(dev, DRIVER_MODESET)); - - del_timer_sync(&vblank->disable_timer); - } - - kfree(dev->vblank); - - dev->num_crtcs = 0; -} -EXPORT_SYMBOL(drm_vblank_cleanup); +#include "drm_internal.h" /** - * drm_vblank_init - initialize vblank support - * @dev: DRM device - * @num_crtcs: number of CRTCs supported by @dev + * DOC: irq helpers * - * This function initializes vblank support for @num_crtcs display pipelines. + * The DRM core provides very simple support helpers to enable IRQ handling on a + * device through the drm_irq_install() and drm_irq_uninstall() functions. This + * only supports devices with a single interrupt on the main device stored in + * &drm_device.dev and set as the device paramter in drm_dev_alloc(). * - * Returns: - * Zero on success or a negative error code on failure. + * These IRQ helpers are strictly optional. Drivers which roll their own only + * need to set &drm_device.irq_enabled to signal the DRM core that vblank + * interrupts are working. Since these helpers don't automatically clean up the + * requested interrupt like e.g. devm_request_irq() they're not really + * recommended. */ -int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs) -{ - int ret = -ENOMEM; - unsigned int i; - - spin_lock_init(&dev->vbl_lock); - spin_lock_init(&dev->vblank_time_lock); - - dev->num_crtcs = num_crtcs; - - dev->vblank = kcalloc(num_crtcs, sizeof(*dev->vblank), GFP_KERNEL); - if (!dev->vblank) - goto err; - - for (i = 0; i < num_crtcs; i++) { - struct drm_vblank_crtc *vblank = &dev->vblank[i]; - - vblank->dev = dev; - vblank->pipe = i; - init_waitqueue_head(&vblank->queue); - setup_timer(&vblank->disable_timer, vblank_disable_fn, - (unsigned long)vblank); - seqlock_init(&vblank->seqlock); - } - - DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n"); - - /* Driver specific high-precision vblank timestamping supported? */ - if (dev->driver->get_vblank_timestamp) - DRM_INFO("Driver supports precise vblank timestamp query.\n"); - else - DRM_INFO("No driver support for vblank timestamp query.\n"); - - /* Must have precise timestamping for reliable vblank instant disable */ - if (dev->vblank_disable_immediate && !dev->driver->get_vblank_timestamp) { - dev->vblank_disable_immediate = false; - DRM_INFO("Setting vblank_disable_immediate to false because " - "get_vblank_timestamp == NULL\n"); - } - - return 0; - -err: - dev->num_crtcs = 0; - return ret; -} -EXPORT_SYMBOL(drm_vblank_init); /** * drm_irq_install - install IRQ handler @@ -462,14 +82,19 @@ EXPORT_SYMBOL(drm_vblank_init); * @irq: IRQ number to install the handler for * * Initializes the IRQ related data. Installs the handler, calling the driver - * irq_preinstall() and irq_postinstall() functions before and after the - * installation. + * &drm_driver.irq_preinstall and &drm_driver.irq_postinstall functions before + * and after the installation. * * This is the simplified helper interface provided for drivers with no special * needs. Drivers which need to install interrupt handlers for multiple * interrupts must instead set &drm_device.irq_enabled to signal the DRM core * that vblank interrupts are available. * + * @irq must match the interrupt number that would be passed to request_irq(), + * if called directly instead of using this helper function. + * + * &drm_driver.irq_handler is called to handle the registered interrupt. + * * Returns: * Zero on success or a negative error code on failure. */ @@ -531,9 +156,9 @@ EXPORT_SYMBOL(drm_irq_install); * drm_irq_uninstall - uninstall the IRQ handler * @dev: DRM device * - * Calls the driver's irq_uninstall() function and unregisters the IRQ handler. - * This should only be called by drivers which used drm_irq_install() to set up - * their interrupt handler. Other drivers must only reset + * Calls the driver's &drm_driver.irq_uninstall function and unregisters the IRQ + * handler. This should only be called by drivers which used drm_irq_install() + * to set up their interrupt handler. Other drivers must only reset * &drm_device.irq_enabled to false. * * Note that for kernel modesetting drivers it is a bug if this function fails. @@ -571,7 +196,7 @@ int drm_irq_uninstall(struct drm_device *dev) WARN_ON(drm_core_check_feature(dev, DRIVER_MODESET)); - vblank_disable_and_save(dev, i); + drm_vblank_disable_and_save(dev, i); wake_up(&vblank->queue); } spin_unlock_irqrestore(&dev->vbl_lock, irqflags); @@ -634,1187 +259,3 @@ int drm_legacy_irq_control(struct drm_device *dev, void *data, return -EINVAL; } } - -/** - * drm_calc_timestamping_constants - calculate vblank timestamp constants - * @crtc: drm_crtc whose timestamp constants should be updated. - * @mode: display mode containing the scanout timings - * - * Calculate and store various constants which are later - * needed by vblank and swap-completion timestamping, e.g, - * by drm_calc_vbltimestamp_from_scanoutpos(). They are - * derived from CRTC's true scanout timing, so they take - * things like panel scaling or other adjustments into account. - */ -void drm_calc_timestamping_constants(struct drm_crtc *crtc, - const struct drm_display_mode *mode) -{ - struct drm_device *dev = crtc->dev; - unsigned int pipe = drm_crtc_index(crtc); - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - int linedur_ns = 0, framedur_ns = 0; - int dotclock = mode->crtc_clock; - - if (!dev->num_crtcs) - return; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return; - - /* Valid dotclock? */ - if (dotclock > 0) { - int frame_size = mode->crtc_htotal * mode->crtc_vtotal; - - /* - * Convert scanline length in pixels and video - * dot clock to line duration and frame duration - * in nanoseconds: - */ - linedur_ns = div_u64((u64) mode->crtc_htotal * 1000000, dotclock); - framedur_ns = div_u64((u64) frame_size * 1000000, dotclock); - - /* - * Fields of interlaced scanout modes are only half a frame duration. - */ - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - framedur_ns /= 2; - } else - DRM_ERROR("crtc %u: Can't calculate constants, dotclock = 0!\n", - crtc->base.id); - - vblank->linedur_ns = linedur_ns; - vblank->framedur_ns = framedur_ns; - vblank->hwmode = *mode; - - DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n", - crtc->base.id, mode->crtc_htotal, - mode->crtc_vtotal, mode->crtc_vdisplay); - DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d\n", - crtc->base.id, dotclock, framedur_ns, linedur_ns); -} -EXPORT_SYMBOL(drm_calc_timestamping_constants); - -/** - * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper - * @dev: DRM device - * @pipe: index of CRTC whose vblank timestamp to retrieve - * @max_error: Desired maximum allowable error in timestamps (nanosecs) - * On return contains true maximum error of timestamp - * @vblank_time: Pointer to struct timeval which should receive the timestamp - * @in_vblank_irq: - * True when called from drm_crtc_handle_vblank(). Some drivers - * need to apply some workarounds for gpu-specific vblank irq quirks - * if flag is set. - * - * Implements calculation of exact vblank timestamps from given drm_display_mode - * timings and current video scanout position of a CRTC. This can be called from - * within get_vblank_timestamp() implementation of a kms driver to implement the - * actual timestamping. - * - * Should return timestamps conforming to the OML_sync_control OpenML - * extension specification. The timestamp corresponds to the end of - * the vblank interval, aka start of scanout of topmost-leftmost display - * pixel in the following video frame. - * - * Requires support for optional dev->driver->get_scanout_position() - * in kms driver, plus a bit of setup code to provide a drm_display_mode - * that corresponds to the true scanout timing. - * - * The current implementation only handles standard video modes. It - * returns as no operation if a doublescan or interlaced video mode is - * active. Higher level code is expected to handle this. - * - * This function can be used to implement the &drm_driver.get_vblank_timestamp - * directly, if the driver implements the &drm_driver.get_scanout_position hook. - * - * Note that atomic drivers must call drm_calc_timestamping_constants() before - * enabling a CRTC. The atomic helpers already take care of that in - * drm_atomic_helper_update_legacy_modeset_state(). - * - * Returns: - * - * Returns true on success, and false on failure, i.e. when no accurate - * timestamp could be acquired. - */ -bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, - unsigned int pipe, - int *max_error, - struct timeval *vblank_time, - bool in_vblank_irq) -{ - struct timeval tv_etime; - ktime_t stime, etime; - bool vbl_status; - struct drm_crtc *crtc; - const struct drm_display_mode *mode; - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - int vpos, hpos, i; - int delta_ns, duration_ns; - - if (!drm_core_check_feature(dev, DRIVER_MODESET)) - return false; - - crtc = drm_crtc_from_index(dev, pipe); - - if (pipe >= dev->num_crtcs || !crtc) { - DRM_ERROR("Invalid crtc %u\n", pipe); - return false; - } - - /* Scanout position query not supported? Should not happen. */ - if (!dev->driver->get_scanout_position) { - DRM_ERROR("Called from driver w/o get_scanout_position()!?\n"); - return false; - } - - if (drm_drv_uses_atomic_modeset(dev)) - mode = &vblank->hwmode; - else - mode = &crtc->hwmode; - - /* If mode timing undefined, just return as no-op: - * Happens during initial modesetting of a crtc. - */ - if (mode->crtc_clock == 0) { - DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe); - WARN_ON_ONCE(drm_drv_uses_atomic_modeset(dev)); - - return false; - } - - /* Get current scanout position with system timestamp. - * Repeat query up to DRM_TIMESTAMP_MAXRETRIES times - * if single query takes longer than max_error nanoseconds. - * - * This guarantees a tight bound on maximum error if - * code gets preempted or delayed for some reason. - */ - for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) { - /* - * Get vertical and horizontal scanout position vpos, hpos, - * and bounding timestamps stime, etime, pre/post query. - */ - vbl_status = dev->driver->get_scanout_position(dev, pipe, - in_vblank_irq, - &vpos, &hpos, - &stime, &etime, - mode); - - /* Return as no-op if scanout query unsupported or failed. */ - if (!vbl_status) { - DRM_DEBUG("crtc %u : scanoutpos query failed.\n", - pipe); - return false; - } - - /* Compute uncertainty in timestamp of scanout position query. */ - duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime); - - /* Accept result with < max_error nsecs timing uncertainty. */ - if (duration_ns <= *max_error) - break; - } - - /* Noisy system timing? */ - if (i == DRM_TIMESTAMP_MAXRETRIES) { - DRM_DEBUG("crtc %u: Noisy timestamp %d us > %d us [%d reps].\n", - pipe, duration_ns/1000, *max_error/1000, i); - } - - /* Return upper bound of timestamp precision error. */ - *max_error = duration_ns; - - /* Convert scanout position into elapsed time at raw_time query - * since start of scanout at first display scanline. delta_ns - * can be negative if start of scanout hasn't happened yet. - */ - delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos), - mode->crtc_clock); - - if (!drm_timestamp_monotonic) - etime = ktime_mono_to_real(etime); - - /* save this only for debugging purposes */ - tv_etime = ktime_to_timeval(etime); - /* Subtract time delta from raw timestamp to get final - * vblank_time timestamp for end of vblank. - */ - etime = ktime_sub_ns(etime, delta_ns); - *vblank_time = ktime_to_timeval(etime); - - DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n", - pipe, hpos, vpos, - (long)tv_etime.tv_sec, (long)tv_etime.tv_usec, - (long)vblank_time->tv_sec, (long)vblank_time->tv_usec, - duration_ns/1000, i); - - return true; -} -EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos); - -static struct timeval get_drm_timestamp(void) -{ - ktime_t now; - - now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real(); - return ktime_to_timeval(now); -} - -/** - * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent - * vblank interval - * @dev: DRM device - * @pipe: index of CRTC whose vblank timestamp to retrieve - * @tvblank: Pointer to target struct timeval which should receive the timestamp - * @in_vblank_irq: - * True when called from drm_crtc_handle_vblank(). Some drivers - * need to apply some workarounds for gpu-specific vblank irq quirks - * if flag is set. - * - * Fetches the system timestamp corresponding to the time of the most recent - * vblank interval on specified CRTC. May call into kms-driver to - * compute the timestamp with a high-precision GPU specific method. - * - * Returns zero if timestamp originates from uncorrected do_gettimeofday() - * call, i.e., it isn't very precisely locked to the true vblank. - * - * Returns: - * True if timestamp is considered to be very precise, false otherwise. - */ -static bool -drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe, - struct timeval *tvblank, bool in_vblank_irq) -{ - bool ret = false; - - /* Define requested maximum error on timestamps (nanoseconds). */ - int max_error = (int) drm_timestamp_precision * 1000; - - /* Query driver if possible and precision timestamping enabled. */ - if (dev->driver->get_vblank_timestamp && (max_error > 0)) - ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error, - tvblank, in_vblank_irq); - - /* GPU high precision timestamp query unsupported or failed. - * Return current monotonic/gettimeofday timestamp as best estimate. - */ - if (!ret) - *tvblank = get_drm_timestamp(); - - return ret; -} - -/** - * drm_crtc_vblank_count - retrieve "cooked" vblank counter value - * @crtc: which counter to retrieve - * - * Fetches the "cooked" vblank count value that represents the number of - * vblank events since the system was booted, including lost events due to - * modesetting activity. - * - * Returns: - * The software vblank counter. - */ -u32 drm_crtc_vblank_count(struct drm_crtc *crtc) -{ - return drm_vblank_count(crtc->dev, drm_crtc_index(crtc)); -} -EXPORT_SYMBOL(drm_crtc_vblank_count); - -/** - * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the - * system timestamp corresponding to that vblank counter value. - * @dev: DRM device - * @pipe: index of CRTC whose counter to retrieve - * @vblanktime: Pointer to struct timeval to receive the vblank timestamp. - * - * Fetches the "cooked" vblank count value that represents the number of - * vblank events since the system was booted, including lost events due to - * modesetting activity. Returns corresponding system timestamp of the time - * of the vblank interval that corresponds to the current vblank counter value. - * - * This is the legacy version of drm_crtc_vblank_count_and_time(). - */ -static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, - struct timeval *vblanktime) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - u32 vblank_count; - unsigned int seq; - - if (WARN_ON(pipe >= dev->num_crtcs)) { - *vblanktime = (struct timeval) { 0 }; - return 0; - } - - do { - seq = read_seqbegin(&vblank->seqlock); - vblank_count = vblank->count; - *vblanktime = vblank->time; - } while (read_seqretry(&vblank->seqlock, seq)); - - return vblank_count; -} - -/** - * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value - * and the system timestamp corresponding to that vblank counter value - * @crtc: which counter to retrieve - * @vblanktime: Pointer to struct timeval to receive the vblank timestamp. - * - * Fetches the "cooked" vblank count value that represents the number of - * vblank events since the system was booted, including lost events due to - * modesetting activity. Returns corresponding system timestamp of the time - * of the vblank interval that corresponds to the current vblank counter value. - */ -u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, - struct timeval *vblanktime) -{ - return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc), - vblanktime); -} -EXPORT_SYMBOL(drm_crtc_vblank_count_and_time); - -static void send_vblank_event(struct drm_device *dev, - struct drm_pending_vblank_event *e, - unsigned long seq, struct timeval *now) -{ - e->event.sequence = seq; - e->event.tv_sec = now->tv_sec; - e->event.tv_usec = now->tv_usec; - - trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe, - e->event.sequence); - - drm_send_event_locked(dev, &e->base); -} - -/** - * drm_crtc_arm_vblank_event - arm vblank event after pageflip - * @crtc: the source CRTC of the vblank event - * @e: the event to send - * - * A lot of drivers need to generate vblank events for the very next vblank - * interrupt. For example when the page flip interrupt happens when the page - * flip gets armed, but not when it actually executes within the next vblank - * period. This helper function implements exactly the required vblank arming - * behaviour. - * - * NOTE: Drivers using this to send out the &drm_crtc_state.event as part of an - * atomic commit must ensure that the next vblank happens at exactly the same - * time as the atomic commit is committed to the hardware. This function itself - * does **not** protect again the next vblank interrupt racing with either this - * function call or the atomic commit operation. A possible sequence could be: - * - * 1. Driver commits new hardware state into vblank-synchronized registers. - * 2. A vblank happens, committing the hardware state. Also the corresponding - * vblank interrupt is fired off and fully processed by the interrupt - * handler. - * 3. The atomic commit operation proceeds to call drm_crtc_arm_vblank_event(). - * 4. The event is only send out for the next vblank, which is wrong. - * - * An equivalent race can happen when the driver calls - * drm_crtc_arm_vblank_event() before writing out the new hardware state. - * - * The only way to make this work safely is to prevent the vblank from firing - * (and the hardware from committing anything else) until the entire atomic - * commit sequence has run to completion. If the hardware does not have such a - * feature (e.g. using a "go" bit), then it is unsafe to use this functions. - * Instead drivers need to manually send out the event from their interrupt - * handler by calling drm_crtc_send_vblank_event() and make sure that there's no - * possible race with the hardware committing the atomic update. - * - * Caller must hold event lock. Caller must also hold a vblank reference for - * the event @e, which will be dropped when the next vblank arrives. - */ -void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, - struct drm_pending_vblank_event *e) -{ - struct drm_device *dev = crtc->dev; - unsigned int pipe = drm_crtc_index(crtc); - - assert_spin_locked(&dev->event_lock); - - e->pipe = pipe; - e->event.sequence = drm_vblank_count(dev, pipe); - e->event.crtc_id = crtc->base.id; - list_add_tail(&e->base.link, &dev->vblank_event_list); -} -EXPORT_SYMBOL(drm_crtc_arm_vblank_event); - -/** - * drm_crtc_send_vblank_event - helper to send vblank event after pageflip - * @crtc: the source CRTC of the vblank event - * @e: the event to send - * - * Updates sequence # and timestamp on event for the most recently processed - * vblank, and sends it to userspace. Caller must hold event lock. - * - * See drm_crtc_arm_vblank_event() for a helper which can be used in certain - * situation, especially to send out events for atomic commit operations. - */ -void drm_crtc_send_vblank_event(struct drm_crtc *crtc, - struct drm_pending_vblank_event *e) -{ - struct drm_device *dev = crtc->dev; - unsigned int seq, pipe = drm_crtc_index(crtc); - struct timeval now; - - if (dev->num_crtcs > 0) { - seq = drm_vblank_count_and_time(dev, pipe, &now); - } else { - seq = 0; - - now = get_drm_timestamp(); - } - e->pipe = pipe; - e->event.crtc_id = crtc->base.id; - send_vblank_event(dev, e, seq, &now); -} -EXPORT_SYMBOL(drm_crtc_send_vblank_event); - -static int __enable_vblank(struct drm_device *dev, unsigned int pipe) -{ - if (drm_core_check_feature(dev, DRIVER_MODESET)) { - struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); - - if (crtc->funcs->enable_vblank) - return crtc->funcs->enable_vblank(crtc); - } - - return dev->driver->enable_vblank(dev, pipe); -} - -/** - * drm_vblank_enable - enable the vblank interrupt on a CRTC - * @dev: DRM device - * @pipe: CRTC index - * - * Returns: - * Zero on success or a negative error code on failure. - */ -static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - int ret = 0; - - assert_spin_locked(&dev->vbl_lock); - - spin_lock(&dev->vblank_time_lock); - - if (!vblank->enabled) { - /* - * Enable vblank irqs under vblank_time_lock protection. - * All vblank count & timestamp updates are held off - * until we are done reinitializing master counter and - * timestamps. Filtercode in drm_handle_vblank() will - * prevent double-accounting of same vblank interval. - */ - ret = __enable_vblank(dev, pipe); - DRM_DEBUG("enabling vblank on crtc %u, ret: %d\n", pipe, ret); - if (ret) { - atomic_dec(&vblank->refcount); - } else { - drm_update_vblank_count(dev, pipe, 0); - /* drm_update_vblank_count() includes a wmb so we just - * need to ensure that the compiler emits the write - * to mark the vblank as enabled after the call - * to drm_update_vblank_count(). - */ - WRITE_ONCE(vblank->enabled, true); - } - } - - spin_unlock(&dev->vblank_time_lock); - - return ret; -} - -/** - * drm_vblank_get - get a reference count on vblank events - * @dev: DRM device - * @pipe: index of CRTC to own - * - * Acquire a reference count on vblank events to avoid having them disabled - * while in use. - * - * This is the legacy version of drm_crtc_vblank_get(). - * - * Returns: - * Zero on success or a negative error code on failure. - */ -static int drm_vblank_get(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; - int ret = 0; - - if (!dev->num_crtcs) - return -EINVAL; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return -EINVAL; - - spin_lock_irqsave(&dev->vbl_lock, irqflags); - /* Going from 0->1 means we have to enable interrupts again */ - if (atomic_add_return(1, &vblank->refcount) == 1) { - ret = drm_vblank_enable(dev, pipe); - } else { - if (!vblank->enabled) { - atomic_dec(&vblank->refcount); - ret = -EINVAL; - } - } - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - - return ret; -} - -/** - * drm_crtc_vblank_get - get a reference count on vblank events - * @crtc: which CRTC to own - * - * Acquire a reference count on vblank events to avoid having them disabled - * while in use. - * - * Returns: - * Zero on success or a negative error code on failure. - */ -int drm_crtc_vblank_get(struct drm_crtc *crtc) -{ - return drm_vblank_get(crtc->dev, drm_crtc_index(crtc)); -} -EXPORT_SYMBOL(drm_crtc_vblank_get); - -/** - * drm_vblank_put - release ownership of vblank events - * @dev: DRM device - * @pipe: index of CRTC to release - * - * Release ownership of a given vblank counter, turning off interrupts - * if possible. Disable interrupts after drm_vblank_offdelay milliseconds. - * - * This is the legacy version of drm_crtc_vblank_put(). - */ -static void drm_vblank_put(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return; - - if (WARN_ON(atomic_read(&vblank->refcount) == 0)) - return; - - /* Last user schedules interrupt disable */ - if (atomic_dec_and_test(&vblank->refcount)) { - if (drm_vblank_offdelay == 0) - return; - else if (drm_vblank_offdelay < 0) - vblank_disable_fn((unsigned long)vblank); - else if (!dev->vblank_disable_immediate) - mod_timer(&vblank->disable_timer, - jiffies + ((drm_vblank_offdelay * HZ)/1000)); - } -} - -/** - * drm_crtc_vblank_put - give up ownership of vblank events - * @crtc: which counter to give up - * - * Release ownership of a given vblank counter, turning off interrupts - * if possible. Disable interrupts after drm_vblank_offdelay milliseconds. - */ -void drm_crtc_vblank_put(struct drm_crtc *crtc) -{ - drm_vblank_put(crtc->dev, drm_crtc_index(crtc)); -} -EXPORT_SYMBOL(drm_crtc_vblank_put); - -/** - * drm_wait_one_vblank - wait for one vblank - * @dev: DRM device - * @pipe: CRTC index - * - * This waits for one vblank to pass on @pipe, using the irq driver interfaces. - * It is a failure to call this when the vblank irq for @pipe is disabled, e.g. - * due to lack of driver support or because the crtc is off. - */ -void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - int ret; - u32 last; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return; - - ret = drm_vblank_get(dev, pipe); - if (WARN(ret, "vblank not available on crtc %i, ret=%i\n", pipe, ret)) - return; - - last = drm_vblank_count(dev, pipe); - - ret = wait_event_timeout(vblank->queue, - last != drm_vblank_count(dev, pipe), - msecs_to_jiffies(100)); - - WARN(ret == 0, "vblank wait timed out on crtc %i\n", pipe); - - drm_vblank_put(dev, pipe); -} -EXPORT_SYMBOL(drm_wait_one_vblank); - -/** - * drm_crtc_wait_one_vblank - wait for one vblank - * @crtc: DRM crtc - * - * This waits for one vblank to pass on @crtc, using the irq driver interfaces. - * It is a failure to call this when the vblank irq for @crtc is disabled, e.g. - * due to lack of driver support or because the crtc is off. - */ -void drm_crtc_wait_one_vblank(struct drm_crtc *crtc) -{ - drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc)); -} -EXPORT_SYMBOL(drm_crtc_wait_one_vblank); - -/** - * drm_crtc_vblank_off - disable vblank events on a CRTC - * @crtc: CRTC in question - * - * Drivers can use this function to shut down the vblank interrupt handling when - * disabling a crtc. This function ensures that the latest vblank frame count is - * stored so that drm_vblank_on can restore it again. - * - * Drivers must use this function when the hardware vblank counter can get - * reset, e.g. when suspending. - */ -void drm_crtc_vblank_off(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - unsigned int pipe = drm_crtc_index(crtc); - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - struct drm_pending_vblank_event *e, *t; - struct timeval now; - unsigned long irqflags; - unsigned int seq; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return; - - spin_lock_irqsave(&dev->event_lock, irqflags); - - spin_lock(&dev->vbl_lock); - DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n", - pipe, vblank->enabled, vblank->inmodeset); - - /* Avoid redundant vblank disables without previous - * drm_crtc_vblank_on(). */ - if (drm_core_check_feature(dev, DRIVER_ATOMIC) || !vblank->inmodeset) - vblank_disable_and_save(dev, pipe); - - wake_up(&vblank->queue); - - /* - * Prevent subsequent drm_vblank_get() from re-enabling - * the vblank interrupt by bumping the refcount. - */ - if (!vblank->inmodeset) { - atomic_inc(&vblank->refcount); - vblank->inmodeset = 1; - } - spin_unlock(&dev->vbl_lock); - - /* Send any queued vblank events, lest the natives grow disquiet */ - seq = drm_vblank_count_and_time(dev, pipe, &now); - - list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { - if (e->pipe != pipe) - continue; - DRM_DEBUG("Sending premature vblank event on disable: " - "wanted %u, current %u\n", - e->event.sequence, seq); - list_del(&e->base.link); - drm_vblank_put(dev, pipe); - send_vblank_event(dev, e, seq, &now); - } - spin_unlock_irqrestore(&dev->event_lock, irqflags); - - /* Will be reset by the modeset helpers when re-enabling the crtc by - * calling drm_calc_timestamping_constants(). */ - vblank->hwmode.crtc_clock = 0; -} -EXPORT_SYMBOL(drm_crtc_vblank_off); - -/** - * drm_crtc_vblank_reset - reset vblank state to off on a CRTC - * @crtc: CRTC in question - * - * Drivers can use this function to reset the vblank state to off at load time. - * Drivers should use this together with the drm_crtc_vblank_off() and - * drm_crtc_vblank_on() functions. The difference compared to - * drm_crtc_vblank_off() is that this function doesn't save the vblank counter - * and hence doesn't need to call any driver hooks. - */ -void drm_crtc_vblank_reset(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - unsigned long irqflags; - unsigned int pipe = drm_crtc_index(crtc); - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - - spin_lock_irqsave(&dev->vbl_lock, irqflags); - /* - * Prevent subsequent drm_vblank_get() from enabling the vblank - * interrupt by bumping the refcount. - */ - if (!vblank->inmodeset) { - atomic_inc(&vblank->refcount); - vblank->inmodeset = 1; - } - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - - WARN_ON(!list_empty(&dev->vblank_event_list)); -} -EXPORT_SYMBOL(drm_crtc_vblank_reset); - -/** - * drm_crtc_vblank_on - enable vblank events on a CRTC - * @crtc: CRTC in question - * - * This functions restores the vblank interrupt state captured with - * drm_crtc_vblank_off() again. Note that calls to drm_crtc_vblank_on() and - * drm_crtc_vblank_off() can be unbalanced and so can also be unconditionally called - * in driver load code to reflect the current hardware state of the crtc. - */ -void drm_crtc_vblank_on(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - unsigned int pipe = drm_crtc_index(crtc); - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return; - - spin_lock_irqsave(&dev->vbl_lock, irqflags); - DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n", - pipe, vblank->enabled, vblank->inmodeset); - - /* Drop our private "prevent drm_vblank_get" refcount */ - if (vblank->inmodeset) { - atomic_dec(&vblank->refcount); - vblank->inmodeset = 0; - } - - drm_reset_vblank_timestamp(dev, pipe); - - /* - * re-enable interrupts if there are users left, or the - * user wishes vblank interrupts to be enabled all the time. - */ - if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0) - WARN_ON(drm_vblank_enable(dev, pipe)); - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); -} -EXPORT_SYMBOL(drm_crtc_vblank_on); - -static void drm_legacy_vblank_pre_modeset(struct drm_device *dev, - unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - - /* vblank is not initialized (IRQ not installed ?), or has been freed */ - if (!dev->num_crtcs) - return; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return; - - /* - * To avoid all the problems that might happen if interrupts - * were enabled/disabled around or between these calls, we just - * have the kernel take a reference on the CRTC (just once though - * to avoid corrupting the count if multiple, mismatch calls occur), - * so that interrupts remain enabled in the interim. - */ - if (!vblank->inmodeset) { - vblank->inmodeset = 0x1; - if (drm_vblank_get(dev, pipe) == 0) - vblank->inmodeset |= 0x2; - } -} - -static void drm_legacy_vblank_post_modeset(struct drm_device *dev, - unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; - - /* vblank is not initialized (IRQ not installed ?), or has been freed */ - if (!dev->num_crtcs) - return; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return; - - if (vblank->inmodeset) { - spin_lock_irqsave(&dev->vbl_lock, irqflags); - drm_reset_vblank_timestamp(dev, pipe); - spin_unlock_irqrestore(&dev->vbl_lock, irqflags); - - if (vblank->inmodeset & 0x2) - drm_vblank_put(dev, pipe); - - vblank->inmodeset = 0; - } -} - -int drm_legacy_modeset_ctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_modeset_ctl *modeset = data; - unsigned int pipe; - - /* If drm_vblank_init() hasn't been called yet, just no-op */ - if (!dev->num_crtcs) - return 0; - - /* KMS drivers handle this internally */ - if (!drm_core_check_feature(dev, DRIVER_LEGACY)) - return 0; - - pipe = modeset->crtc; - if (pipe >= dev->num_crtcs) - return -EINVAL; - - switch (modeset->cmd) { - case _DRM_PRE_MODESET: - drm_legacy_vblank_pre_modeset(dev, pipe); - break; - case _DRM_POST_MODESET: - drm_legacy_vblank_post_modeset(dev, pipe); - break; - default: - return -EINVAL; - } - - return 0; -} - -static inline bool vblank_passed(u32 seq, u32 ref) -{ - return (seq - ref) <= (1 << 23); -} - -static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, - union drm_wait_vblank *vblwait, - struct drm_file *file_priv) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - struct drm_pending_vblank_event *e; - struct timeval now; - unsigned long flags; - unsigned int seq; - int ret; - - e = kzalloc(sizeof(*e), GFP_KERNEL); - if (e == NULL) { - ret = -ENOMEM; - goto err_put; - } - - e->pipe = pipe; - e->event.base.type = DRM_EVENT_VBLANK; - e->event.base.length = sizeof(e->event); - e->event.user_data = vblwait->request.signal; - - spin_lock_irqsave(&dev->event_lock, flags); - - /* - * drm_crtc_vblank_off() might have been called after we called - * drm_vblank_get(). drm_crtc_vblank_off() holds event_lock around the - * vblank disable, so no need for further locking. The reference from - * drm_vblank_get() protects against vblank disable from another source. - */ - if (!READ_ONCE(vblank->enabled)) { - ret = -EINVAL; - goto err_unlock; - } - - ret = drm_event_reserve_init_locked(dev, file_priv, &e->base, - &e->event.base); - - if (ret) - goto err_unlock; - - seq = drm_vblank_count_and_time(dev, pipe, &now); - - DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n", - vblwait->request.sequence, seq, pipe); - - trace_drm_vblank_event_queued(file_priv, pipe, - vblwait->request.sequence); - - e->event.sequence = vblwait->request.sequence; - if (vblank_passed(seq, vblwait->request.sequence)) { - drm_vblank_put(dev, pipe); - send_vblank_event(dev, e, seq, &now); - vblwait->reply.sequence = seq; - } else { - /* drm_handle_vblank_events will call drm_vblank_put */ - list_add_tail(&e->base.link, &dev->vblank_event_list); - vblwait->reply.sequence = vblwait->request.sequence; - } - - spin_unlock_irqrestore(&dev->event_lock, flags); - - return 0; - -err_unlock: - spin_unlock_irqrestore(&dev->event_lock, flags); - kfree(e); -err_put: - drm_vblank_put(dev, pipe); - return ret; -} - -static bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait) -{ - if (vblwait->request.sequence) - return false; - - return _DRM_VBLANK_RELATIVE == - (vblwait->request.type & (_DRM_VBLANK_TYPES_MASK | - _DRM_VBLANK_EVENT | - _DRM_VBLANK_NEXTONMISS)); -} - -/* - * Wait for VBLANK. - * - * \param inode device inode. - * \param file_priv DRM file private. - * \param cmd command. - * \param data user argument, pointing to a drm_wait_vblank structure. - * \return zero on success or a negative number on failure. - * - * This function enables the vblank interrupt on the pipe requested, then - * sleeps waiting for the requested sequence number to occur, and drops - * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that - * after a timeout with no further vblank waits scheduled). - */ -int drm_wait_vblank(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_vblank_crtc *vblank; - union drm_wait_vblank *vblwait = data; - int ret; - unsigned int flags, seq, pipe, high_pipe; - - if (!dev->irq_enabled) - return -EINVAL; - - if (vblwait->request.type & _DRM_VBLANK_SIGNAL) - return -EINVAL; - - if (vblwait->request.type & - ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK | - _DRM_VBLANK_HIGH_CRTC_MASK)) { - DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n", - vblwait->request.type, - (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK | - _DRM_VBLANK_HIGH_CRTC_MASK)); - return -EINVAL; - } - - flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; - high_pipe = (vblwait->request.type & _DRM_VBLANK_HIGH_CRTC_MASK); - if (high_pipe) - pipe = high_pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT; - else - pipe = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; - if (pipe >= dev->num_crtcs) - return -EINVAL; - - vblank = &dev->vblank[pipe]; - - /* If the counter is currently enabled and accurate, short-circuit - * queries to return the cached timestamp of the last vblank. - */ - if (dev->vblank_disable_immediate && - drm_wait_vblank_is_query(vblwait) && - READ_ONCE(vblank->enabled)) { - struct timeval now; - - vblwait->reply.sequence = - drm_vblank_count_and_time(dev, pipe, &now); - vblwait->reply.tval_sec = now.tv_sec; - vblwait->reply.tval_usec = now.tv_usec; - return 0; - } - - ret = drm_vblank_get(dev, pipe); - if (ret) { - DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", pipe, ret); - return ret; - } - seq = drm_vblank_count(dev, pipe); - - switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { - case _DRM_VBLANK_RELATIVE: - vblwait->request.sequence += seq; - vblwait->request.type &= ~_DRM_VBLANK_RELATIVE; - case _DRM_VBLANK_ABSOLUTE: - break; - default: - ret = -EINVAL; - goto done; - } - - if ((flags & _DRM_VBLANK_NEXTONMISS) && - vblank_passed(seq, vblwait->request.sequence)) - vblwait->request.sequence = seq + 1; - - if (flags & _DRM_VBLANK_EVENT) { - /* must hold on to the vblank ref until the event fires - * drm_vblank_put will be called asynchronously - */ - return drm_queue_vblank_event(dev, pipe, vblwait, file_priv); - } - - if (vblwait->request.sequence != seq) { - DRM_DEBUG("waiting on vblank count %u, crtc %u\n", - vblwait->request.sequence, pipe); - DRM_WAIT_ON(ret, vblank->queue, 3 * HZ, - vblank_passed(drm_vblank_count(dev, pipe), - vblwait->request.sequence) || - !READ_ONCE(vblank->enabled)); - } - - if (ret != -EINTR) { - struct timeval now; - - vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, &now); - vblwait->reply.tval_sec = now.tv_sec; - vblwait->reply.tval_usec = now.tv_usec; - - DRM_DEBUG("crtc %d returning %u to client\n", - pipe, vblwait->reply.sequence); - } else { - DRM_DEBUG("crtc %d vblank wait interrupted by signal\n", pipe); - } - -done: - drm_vblank_put(dev, pipe); - return ret; -} - -static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe) -{ - struct drm_pending_vblank_event *e, *t; - struct timeval now; - unsigned int seq; - - assert_spin_locked(&dev->event_lock); - - seq = drm_vblank_count_and_time(dev, pipe, &now); - - list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { - if (e->pipe != pipe) - continue; - if (!vblank_passed(seq, e->event.sequence)) - continue; - - DRM_DEBUG("vblank event on %u, current %u\n", - e->event.sequence, seq); - - list_del(&e->base.link); - drm_vblank_put(dev, pipe); - send_vblank_event(dev, e, seq, &now); - } - - trace_drm_vblank_event(pipe, seq); -} - -/** - * drm_handle_vblank - handle a vblank event - * @dev: DRM device - * @pipe: index of CRTC where this event occurred - * - * Drivers should call this routine in their vblank interrupt handlers to - * update the vblank counter and send any signals that may be pending. - * - * This is the legacy version of drm_crtc_handle_vblank(). - */ -bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe) -{ - struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; - unsigned long irqflags; - bool disable_irq; - - if (WARN_ON_ONCE(!dev->num_crtcs)) - return false; - - if (WARN_ON(pipe >= dev->num_crtcs)) - return false; - - spin_lock_irqsave(&dev->event_lock, irqflags); - - /* Need timestamp lock to prevent concurrent execution with - * vblank enable/disable, as this would cause inconsistent - * or corrupted timestamps and vblank counts. - */ - spin_lock(&dev->vblank_time_lock); - - /* Vblank irq handling disabled. Nothing to do. */ - if (!vblank->enabled) { - spin_unlock(&dev->vblank_time_lock); - spin_unlock_irqrestore(&dev->event_lock, irqflags); - return false; - } - - drm_update_vblank_count(dev, pipe, true); - - spin_unlock(&dev->vblank_time_lock); - - wake_up(&vblank->queue); - - /* With instant-off, we defer disabling the interrupt until after - * we finish processing the following vblank after all events have - * been signaled. The disable has to be last (after - * drm_handle_vblank_events) so that the timestamp is always accurate. - */ - disable_irq = (dev->vblank_disable_immediate && - drm_vblank_offdelay > 0 && - !atomic_read(&vblank->refcount)); - - drm_handle_vblank_events(dev, pipe); - - spin_unlock_irqrestore(&dev->event_lock, irqflags); - - if (disable_irq) - vblank_disable_fn((unsigned long)vblank); - - return true; -} -EXPORT_SYMBOL(drm_handle_vblank); - -/** - * drm_crtc_handle_vblank - handle a vblank event - * @crtc: where this event occurred - * - * Drivers should call this routine in their vblank interrupt handlers to - * update the vblank counter and send any signals that may be pending. - * - * This is the native KMS version of drm_handle_vblank(). - * - * Returns: - * True if the event was successfully handled, false on failure. - */ -bool drm_crtc_handle_vblank(struct drm_crtc *crtc) -{ - return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc)); -} -EXPORT_SYMBOL(drm_crtc_handle_vblank); diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 1b0c14ab3fff..00e6832a8c1a 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -38,6 +38,9 @@ #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_helper.h> #include <drm/drm_edid.h> +#include <drm/drm_modeset_helper_vtables.h> + +#include "drm_crtc_helper_internal.h" /** * DOC: output probing helper overview @@ -80,6 +83,61 @@ drm_mode_validate_flag(const struct drm_display_mode *mode, return MODE_OK; } +static enum drm_mode_status +drm_mode_validate_pipeline(struct drm_display_mode *mode, + struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + uint32_t *ids = connector->encoder_ids; + enum drm_mode_status ret = MODE_OK; + unsigned int i; + + /* Step 1: Validate against connector */ + ret = drm_connector_mode_valid(connector, mode); + if (ret != MODE_OK) + return ret; + + /* Step 2: Validate against encoders and crtcs */ + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + struct drm_encoder *encoder = drm_encoder_find(dev, ids[i]); + struct drm_crtc *crtc; + + if (!encoder) + continue; + + ret = drm_encoder_mode_valid(encoder, mode); + if (ret != MODE_OK) { + /* No point in continuing for crtc check as this encoder + * will not accept the mode anyway. If all encoders + * reject the mode then, at exit, ret will not be + * MODE_OK. */ + continue; + } + + ret = drm_bridge_mode_valid(encoder->bridge, mode); + if (ret != MODE_OK) { + /* There is also no point in continuing for crtc check + * here. */ + continue; + } + + drm_for_each_crtc(crtc, dev) { + if (!drm_encoder_crtc_ok(encoder, crtc)) + continue; + + ret = drm_crtc_mode_valid(crtc, mode); + if (ret == MODE_OK) { + /* If we get to this point there is at least + * one combination of encoder+crtc that works + * for this mode. Lets return now. */ + return ret; + } + } + } + + return ret; +} + static int drm_helper_probe_add_cmdline_mode(struct drm_connector *connector) { struct drm_cmdline_mode *cmdline_mode; @@ -113,6 +171,41 @@ static int drm_helper_probe_add_cmdline_mode(struct drm_connector *connector) return 1; } +enum drm_mode_status drm_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + + if (!crtc_funcs || !crtc_funcs->mode_valid) + return MODE_OK; + + return crtc_funcs->mode_valid(crtc, mode); +} + +enum drm_mode_status drm_encoder_mode_valid(struct drm_encoder *encoder, + const struct drm_display_mode *mode) +{ + const struct drm_encoder_helper_funcs *encoder_funcs = + encoder->helper_private; + + if (!encoder_funcs || !encoder_funcs->mode_valid) + return MODE_OK; + + return encoder_funcs->mode_valid(encoder, mode); +} + +enum drm_mode_status drm_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + const struct drm_connector_helper_funcs *connector_funcs = + connector->helper_private; + + if (!connector_funcs || !connector_funcs->mode_valid) + return MODE_OK; + + return connector_funcs->mode_valid(connector, mode); +} + #define DRM_OUTPUT_POLL_PERIOD (10*HZ) /** * drm_kms_helper_poll_enable - re-enable output polling. @@ -284,7 +377,11 @@ EXPORT_SYMBOL(drm_helper_probe_detect); * - drm_mode_validate_flag() checks the modes against basic connector * capabilities (interlace_allowed,doublescan_allowed,stereo_allowed) * - the optional &drm_connector_helper_funcs.mode_valid helper can perform - * driver and/or hardware specific checks + * driver and/or sink specific checks + * - the optional &drm_crtc_helper_funcs.mode_valid, + * &drm_bridge_funcs.mode_valid and &drm_encoder_helper_funcs.mode_valid + * helpers can perform driver and/or source specific checks which are also + * enforced by the modeset/atomic helpers * * 5. Any mode whose status is not OK is pruned from the connector's modes list, * accompanied by a debug message indicating the reason for the mode's @@ -428,9 +525,9 @@ retry: if (mode->status == MODE_OK) mode->status = drm_mode_validate_flag(mode, mode_flags); - if (mode->status == MODE_OK && connector_funcs->mode_valid) - mode->status = connector_funcs->mode_valid(connector, - mode); + if (mode->status == MODE_OK) + mode->status = drm_mode_validate_pipeline(mode, + connector); } prune: diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c new file mode 100644 index 000000000000..463e4d81fb0d --- /dev/null +++ b/drivers/gpu/drm/drm_vblank.c @@ -0,0 +1,1648 @@ +/* + * drm_irq.c IRQ and vblank support + * + * \author Rickard E. (Rik) Faith <[email protected]> + * \author Gareth Hughes <[email protected]> + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <drm/drm_vblank.h> +#include <drm/drmP.h> +#include <linux/export.h> + +#include "drm_trace.h" +#include "drm_internal.h" + +/* Retry timestamp calculation up to 3 times to satisfy + * drm_timestamp_precision before giving up. + */ +#define DRM_TIMESTAMP_MAXRETRIES 3 + +/* Threshold in nanoseconds for detection of redundant + * vblank irq in drm_handle_vblank(). 1 msec should be ok. + */ +#define DRM_REDUNDANT_VBLIRQ_THRESH_NS 1000000 + +static bool +drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe, + struct timeval *tvblank, bool in_vblank_irq); + +static unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ + +/* + * Default to use monotonic timestamps for wait-for-vblank and page-flip + * complete events. + */ +unsigned int drm_timestamp_monotonic = 1; + +static int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */ + +module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); +module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); +module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); +MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs] (0: never disable, <0: disable immediately)"); +MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]"); +MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); + +static void store_vblank(struct drm_device *dev, unsigned int pipe, + u32 vblank_count_inc, + struct timeval *t_vblank, u32 last) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + + assert_spin_locked(&dev->vblank_time_lock); + + vblank->last = last; + + write_seqlock(&vblank->seqlock); + vblank->time = *t_vblank; + vblank->count += vblank_count_inc; + write_sequnlock(&vblank->seqlock); +} + +/* + * "No hw counter" fallback implementation of .get_vblank_counter() hook, + * if there is no useable hardware frame counter available. + */ +static u32 drm_vblank_no_hw_counter(struct drm_device *dev, unsigned int pipe) +{ + WARN_ON_ONCE(dev->max_vblank_count != 0); + return 0; +} + +static u32 __get_vblank_counter(struct drm_device *dev, unsigned int pipe) +{ + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); + + if (crtc->funcs->get_vblank_counter) + return crtc->funcs->get_vblank_counter(crtc); + } + + if (dev->driver->get_vblank_counter) + return dev->driver->get_vblank_counter(dev, pipe); + + return drm_vblank_no_hw_counter(dev, pipe); +} + +/* + * Reset the stored timestamp for the current vblank count to correspond + * to the last vblank occurred. + * + * Only to be called from drm_crtc_vblank_on(). + * + * Note: caller must hold &drm_device.vbl_lock since this reads & writes + * device vblank fields. + */ +static void drm_reset_vblank_timestamp(struct drm_device *dev, unsigned int pipe) +{ + u32 cur_vblank; + bool rc; + struct timeval t_vblank; + int count = DRM_TIMESTAMP_MAXRETRIES; + + spin_lock(&dev->vblank_time_lock); + + /* + * sample the current counter to avoid random jumps + * when drm_vblank_enable() applies the diff + */ + do { + cur_vblank = __get_vblank_counter(dev, pipe); + rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, false); + } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0); + + /* + * Only reinitialize corresponding vblank timestamp if high-precision query + * available and didn't fail. Otherwise reinitialize delayed at next vblank + * interrupt and assign 0 for now, to mark the vblanktimestamp as invalid. + */ + if (!rc) + t_vblank = (struct timeval) {0, 0}; + + /* + * +1 to make sure user will never see the same + * vblank counter value before and after a modeset + */ + store_vblank(dev, pipe, 1, &t_vblank, cur_vblank); + + spin_unlock(&dev->vblank_time_lock); +} + +/* + * Call back into the driver to update the appropriate vblank counter + * (specified by @pipe). Deal with wraparound, if it occurred, and + * update the last read value so we can deal with wraparound on the next + * call if necessary. + * + * Only necessary when going from off->on, to account for frames we + * didn't get an interrupt for. + * + * Note: caller must hold &drm_device.vbl_lock since this reads & writes + * device vblank fields. + */ +static void drm_update_vblank_count(struct drm_device *dev, unsigned int pipe, + bool in_vblank_irq) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + u32 cur_vblank, diff; + bool rc; + struct timeval t_vblank; + int count = DRM_TIMESTAMP_MAXRETRIES; + int framedur_ns = vblank->framedur_ns; + + /* + * Interrupts were disabled prior to this call, so deal with counter + * wrap if needed. + * NOTE! It's possible we lost a full dev->max_vblank_count + 1 events + * here if the register is small or we had vblank interrupts off for + * a long time. + * + * We repeat the hardware vblank counter & timestamp query until + * we get consistent results. This to prevent races between gpu + * updating its hardware counter while we are retrieving the + * corresponding vblank timestamp. + */ + do { + cur_vblank = __get_vblank_counter(dev, pipe); + rc = drm_get_last_vbltimestamp(dev, pipe, &t_vblank, in_vblank_irq); + } while (cur_vblank != __get_vblank_counter(dev, pipe) && --count > 0); + + if (dev->max_vblank_count != 0) { + /* trust the hw counter when it's around */ + diff = (cur_vblank - vblank->last) & dev->max_vblank_count; + } else if (rc && framedur_ns) { + const struct timeval *t_old; + u64 diff_ns; + + t_old = &vblank->time; + diff_ns = timeval_to_ns(&t_vblank) - timeval_to_ns(t_old); + + /* + * Figure out how many vblanks we've missed based + * on the difference in the timestamps and the + * frame/field duration. + */ + diff = DIV_ROUND_CLOSEST_ULL(diff_ns, framedur_ns); + + if (diff == 0 && in_vblank_irq) + DRM_DEBUG_VBL("crtc %u: Redundant vblirq ignored." + " diff_ns = %lld, framedur_ns = %d)\n", + pipe, (long long) diff_ns, framedur_ns); + } else { + /* some kind of default for drivers w/o accurate vbl timestamping */ + diff = in_vblank_irq ? 1 : 0; + } + + /* + * Within a drm_vblank_pre_modeset - drm_vblank_post_modeset + * interval? If so then vblank irqs keep running and it will likely + * happen that the hardware vblank counter is not trustworthy as it + * might reset at some point in that interval and vblank timestamps + * are not trustworthy either in that interval. Iow. this can result + * in a bogus diff >> 1 which must be avoided as it would cause + * random large forward jumps of the software vblank counter. + */ + if (diff > 1 && (vblank->inmodeset & 0x2)) { + DRM_DEBUG_VBL("clamping vblank bump to 1 on crtc %u: diffr=%u" + " due to pre-modeset.\n", pipe, diff); + diff = 1; + } + + DRM_DEBUG_VBL("updating vblank count on crtc %u:" + " current=%u, diff=%u, hw=%u hw_last=%u\n", + pipe, vblank->count, diff, cur_vblank, vblank->last); + + if (diff == 0) { + WARN_ON_ONCE(cur_vblank != vblank->last); + return; + } + + /* + * Only reinitialize corresponding vblank timestamp if high-precision query + * available and didn't fail, or we were called from the vblank interrupt. + * Otherwise reinitialize delayed at next vblank interrupt and assign 0 + * for now, to mark the vblanktimestamp as invalid. + */ + if (!rc && in_vblank_irq) + t_vblank = (struct timeval) {0, 0}; + + store_vblank(dev, pipe, diff, &t_vblank, cur_vblank); +} + +static u32 drm_vblank_count(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + + if (WARN_ON(pipe >= dev->num_crtcs)) + return 0; + + return vblank->count; +} + +/** + * drm_accurate_vblank_count - retrieve the master vblank counter + * @crtc: which counter to retrieve + * + * This function is similar to @drm_crtc_vblank_count but this + * function interpolates to handle a race with vblank irq's. + * + * This is mostly useful for hardware that can obtain the scanout + * position, but doesn't have a frame counter. + */ +u32 drm_accurate_vblank_count(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + unsigned int pipe = drm_crtc_index(crtc); + u32 vblank; + unsigned long flags; + + WARN(!dev->driver->get_vblank_timestamp, + "This function requires support for accurate vblank timestamps."); + + spin_lock_irqsave(&dev->vblank_time_lock, flags); + + drm_update_vblank_count(dev, pipe, false); + vblank = drm_vblank_count(dev, pipe); + + spin_unlock_irqrestore(&dev->vblank_time_lock, flags); + + return vblank; +} +EXPORT_SYMBOL(drm_accurate_vblank_count); + +static void __disable_vblank(struct drm_device *dev, unsigned int pipe) +{ + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); + + if (crtc->funcs->disable_vblank) { + crtc->funcs->disable_vblank(crtc); + return; + } + } + + dev->driver->disable_vblank(dev, pipe); +} + +/* + * Disable vblank irq's on crtc, make sure that last vblank count + * of hardware and corresponding consistent software vblank counter + * are preserved, even if there are any spurious vblank irq's after + * disable. + */ +void drm_vblank_disable_and_save(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + unsigned long irqflags; + + assert_spin_locked(&dev->vbl_lock); + + /* Prevent vblank irq processing while disabling vblank irqs, + * so no updates of timestamps or count can happen after we've + * disabled. Needed to prevent races in case of delayed irq's. + */ + spin_lock_irqsave(&dev->vblank_time_lock, irqflags); + + /* + * Only disable vblank interrupts if they're enabled. This avoids + * calling the ->disable_vblank() operation in atomic context with the + * hardware potentially runtime suspended. + */ + if (vblank->enabled) { + __disable_vblank(dev, pipe); + vblank->enabled = false; + } + + /* + * Always update the count and timestamp to maintain the + * appearance that the counter has been ticking all along until + * this time. This makes the count account for the entire time + * between drm_crtc_vblank_on() and drm_crtc_vblank_off(). + */ + drm_update_vblank_count(dev, pipe, false); + + spin_unlock_irqrestore(&dev->vblank_time_lock, irqflags); +} + +static void vblank_disable_fn(unsigned long arg) +{ + struct drm_vblank_crtc *vblank = (void *)arg; + struct drm_device *dev = vblank->dev; + unsigned int pipe = vblank->pipe; + unsigned long irqflags; + + spin_lock_irqsave(&dev->vbl_lock, irqflags); + if (atomic_read(&vblank->refcount) == 0 && vblank->enabled) { + DRM_DEBUG("disabling vblank on crtc %u\n", pipe); + drm_vblank_disable_and_save(dev, pipe); + } + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); +} + +/** + * drm_vblank_cleanup - cleanup vblank support + * @dev: DRM device + * + * This function cleans up any resources allocated in drm_vblank_init. + * + * Drivers which don't use drm_irq_install() need to set &drm_device.irq_enabled + * themselves, to signal to the DRM core that vblank interrupts are enabled. + */ +void drm_vblank_cleanup(struct drm_device *dev) +{ + unsigned int pipe; + + /* Bail if the driver didn't call drm_vblank_init() */ + if (dev->num_crtcs == 0) + return; + + for (pipe = 0; pipe < dev->num_crtcs; pipe++) { + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + + WARN_ON(READ_ONCE(vblank->enabled) && + drm_core_check_feature(dev, DRIVER_MODESET)); + + del_timer_sync(&vblank->disable_timer); + } + + kfree(dev->vblank); + + dev->num_crtcs = 0; +} +EXPORT_SYMBOL(drm_vblank_cleanup); + +/** + * drm_vblank_init - initialize vblank support + * @dev: DRM device + * @num_crtcs: number of CRTCs supported by @dev + * + * This function initializes vblank support for @num_crtcs display pipelines. + * + * Returns: + * Zero on success or a negative error code on failure. + */ +int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs) +{ + int ret = -ENOMEM; + unsigned int i; + + spin_lock_init(&dev->vbl_lock); + spin_lock_init(&dev->vblank_time_lock); + + dev->num_crtcs = num_crtcs; + + dev->vblank = kcalloc(num_crtcs, sizeof(*dev->vblank), GFP_KERNEL); + if (!dev->vblank) + goto err; + + for (i = 0; i < num_crtcs; i++) { + struct drm_vblank_crtc *vblank = &dev->vblank[i]; + + vblank->dev = dev; + vblank->pipe = i; + init_waitqueue_head(&vblank->queue); + setup_timer(&vblank->disable_timer, vblank_disable_fn, + (unsigned long)vblank); + seqlock_init(&vblank->seqlock); + } + + DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n"); + + /* Driver specific high-precision vblank timestamping supported? */ + if (dev->driver->get_vblank_timestamp) + DRM_INFO("Driver supports precise vblank timestamp query.\n"); + else + DRM_INFO("No driver support for vblank timestamp query.\n"); + + /* Must have precise timestamping for reliable vblank instant disable */ + if (dev->vblank_disable_immediate && !dev->driver->get_vblank_timestamp) { + dev->vblank_disable_immediate = false; + DRM_INFO("Setting vblank_disable_immediate to false because " + "get_vblank_timestamp == NULL\n"); + } + + return 0; + +err: + dev->num_crtcs = 0; + return ret; +} +EXPORT_SYMBOL(drm_vblank_init); + +/** + * drm_crtc_vblank_waitqueue - get vblank waitqueue for the CRTC + * @crtc: which CRTC's vblank waitqueue to retrieve + * + * This function returns a pointer to the vblank waitqueue for the CRTC. + * Drivers can use this to implement vblank waits using wait_event() and related + * functions. + */ +wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc) +{ + return &crtc->dev->vblank[drm_crtc_index(crtc)].queue; +} +EXPORT_SYMBOL(drm_crtc_vblank_waitqueue); + + +/** + * drm_calc_timestamping_constants - calculate vblank timestamp constants + * @crtc: drm_crtc whose timestamp constants should be updated. + * @mode: display mode containing the scanout timings + * + * Calculate and store various constants which are later + * needed by vblank and swap-completion timestamping, e.g, + * by drm_calc_vbltimestamp_from_scanoutpos(). They are + * derived from CRTC's true scanout timing, so they take + * things like panel scaling or other adjustments into account. + */ +void drm_calc_timestamping_constants(struct drm_crtc *crtc, + const struct drm_display_mode *mode) +{ + struct drm_device *dev = crtc->dev; + unsigned int pipe = drm_crtc_index(crtc); + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + int linedur_ns = 0, framedur_ns = 0; + int dotclock = mode->crtc_clock; + + if (!dev->num_crtcs) + return; + + if (WARN_ON(pipe >= dev->num_crtcs)) + return; + + /* Valid dotclock? */ + if (dotclock > 0) { + int frame_size = mode->crtc_htotal * mode->crtc_vtotal; + + /* + * Convert scanline length in pixels and video + * dot clock to line duration and frame duration + * in nanoseconds: + */ + linedur_ns = div_u64((u64) mode->crtc_htotal * 1000000, dotclock); + framedur_ns = div_u64((u64) frame_size * 1000000, dotclock); + + /* + * Fields of interlaced scanout modes are only half a frame duration. + */ + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + framedur_ns /= 2; + } else + DRM_ERROR("crtc %u: Can't calculate constants, dotclock = 0!\n", + crtc->base.id); + + vblank->linedur_ns = linedur_ns; + vblank->framedur_ns = framedur_ns; + vblank->hwmode = *mode; + + DRM_DEBUG("crtc %u: hwmode: htotal %d, vtotal %d, vdisplay %d\n", + crtc->base.id, mode->crtc_htotal, + mode->crtc_vtotal, mode->crtc_vdisplay); + DRM_DEBUG("crtc %u: clock %d kHz framedur %d linedur %d\n", + crtc->base.id, dotclock, framedur_ns, linedur_ns); +} +EXPORT_SYMBOL(drm_calc_timestamping_constants); + +/** + * drm_calc_vbltimestamp_from_scanoutpos - precise vblank timestamp helper + * @dev: DRM device + * @pipe: index of CRTC whose vblank timestamp to retrieve + * @max_error: Desired maximum allowable error in timestamps (nanosecs) + * On return contains true maximum error of timestamp + * @vblank_time: Pointer to struct timeval which should receive the timestamp + * @in_vblank_irq: + * True when called from drm_crtc_handle_vblank(). Some drivers + * need to apply some workarounds for gpu-specific vblank irq quirks + * if flag is set. + * + * Implements calculation of exact vblank timestamps from given drm_display_mode + * timings and current video scanout position of a CRTC. This can be called from + * within get_vblank_timestamp() implementation of a kms driver to implement the + * actual timestamping. + * + * Should return timestamps conforming to the OML_sync_control OpenML + * extension specification. The timestamp corresponds to the end of + * the vblank interval, aka start of scanout of topmost-leftmost display + * pixel in the following video frame. + * + * Requires support for optional dev->driver->get_scanout_position() + * in kms driver, plus a bit of setup code to provide a drm_display_mode + * that corresponds to the true scanout timing. + * + * The current implementation only handles standard video modes. It + * returns as no operation if a doublescan or interlaced video mode is + * active. Higher level code is expected to handle this. + * + * This function can be used to implement the &drm_driver.get_vblank_timestamp + * directly, if the driver implements the &drm_driver.get_scanout_position hook. + * + * Note that atomic drivers must call drm_calc_timestamping_constants() before + * enabling a CRTC. The atomic helpers already take care of that in + * drm_atomic_helper_update_legacy_modeset_state(). + * + * Returns: + * + * Returns true on success, and false on failure, i.e. when no accurate + * timestamp could be acquired. + */ +bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, + unsigned int pipe, + int *max_error, + struct timeval *vblank_time, + bool in_vblank_irq) +{ + struct timeval tv_etime; + ktime_t stime, etime; + bool vbl_status; + struct drm_crtc *crtc; + const struct drm_display_mode *mode; + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + int vpos, hpos, i; + int delta_ns, duration_ns; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return false; + + crtc = drm_crtc_from_index(dev, pipe); + + if (pipe >= dev->num_crtcs || !crtc) { + DRM_ERROR("Invalid crtc %u\n", pipe); + return false; + } + + /* Scanout position query not supported? Should not happen. */ + if (!dev->driver->get_scanout_position) { + DRM_ERROR("Called from driver w/o get_scanout_position()!?\n"); + return false; + } + + if (drm_drv_uses_atomic_modeset(dev)) + mode = &vblank->hwmode; + else + mode = &crtc->hwmode; + + /* If mode timing undefined, just return as no-op: + * Happens during initial modesetting of a crtc. + */ + if (mode->crtc_clock == 0) { + DRM_DEBUG("crtc %u: Noop due to uninitialized mode.\n", pipe); + WARN_ON_ONCE(drm_drv_uses_atomic_modeset(dev)); + + return false; + } + + /* Get current scanout position with system timestamp. + * Repeat query up to DRM_TIMESTAMP_MAXRETRIES times + * if single query takes longer than max_error nanoseconds. + * + * This guarantees a tight bound on maximum error if + * code gets preempted or delayed for some reason. + */ + for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) { + /* + * Get vertical and horizontal scanout position vpos, hpos, + * and bounding timestamps stime, etime, pre/post query. + */ + vbl_status = dev->driver->get_scanout_position(dev, pipe, + in_vblank_irq, + &vpos, &hpos, + &stime, &etime, + mode); + + /* Return as no-op if scanout query unsupported or failed. */ + if (!vbl_status) { + DRM_DEBUG("crtc %u : scanoutpos query failed.\n", + pipe); + return false; + } + + /* Compute uncertainty in timestamp of scanout position query. */ + duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime); + + /* Accept result with < max_error nsecs timing uncertainty. */ + if (duration_ns <= *max_error) + break; + } + + /* Noisy system timing? */ + if (i == DRM_TIMESTAMP_MAXRETRIES) { + DRM_DEBUG("crtc %u: Noisy timestamp %d us > %d us [%d reps].\n", + pipe, duration_ns/1000, *max_error/1000, i); + } + + /* Return upper bound of timestamp precision error. */ + *max_error = duration_ns; + + /* Convert scanout position into elapsed time at raw_time query + * since start of scanout at first display scanline. delta_ns + * can be negative if start of scanout hasn't happened yet. + */ + delta_ns = div_s64(1000000LL * (vpos * mode->crtc_htotal + hpos), + mode->crtc_clock); + + if (!drm_timestamp_monotonic) + etime = ktime_mono_to_real(etime); + + /* save this only for debugging purposes */ + tv_etime = ktime_to_timeval(etime); + /* Subtract time delta from raw timestamp to get final + * vblank_time timestamp for end of vblank. + */ + etime = ktime_sub_ns(etime, delta_ns); + *vblank_time = ktime_to_timeval(etime); + + DRM_DEBUG_VBL("crtc %u : v p(%d,%d)@ %ld.%ld -> %ld.%ld [e %d us, %d rep]\n", + pipe, hpos, vpos, + (long)tv_etime.tv_sec, (long)tv_etime.tv_usec, + (long)vblank_time->tv_sec, (long)vblank_time->tv_usec, + duration_ns/1000, i); + + return true; +} +EXPORT_SYMBOL(drm_calc_vbltimestamp_from_scanoutpos); + +static struct timeval get_drm_timestamp(void) +{ + ktime_t now; + + now = drm_timestamp_monotonic ? ktime_get() : ktime_get_real(); + return ktime_to_timeval(now); +} + +/** + * drm_get_last_vbltimestamp - retrieve raw timestamp for the most recent + * vblank interval + * @dev: DRM device + * @pipe: index of CRTC whose vblank timestamp to retrieve + * @tvblank: Pointer to target struct timeval which should receive the timestamp + * @in_vblank_irq: + * True when called from drm_crtc_handle_vblank(). Some drivers + * need to apply some workarounds for gpu-specific vblank irq quirks + * if flag is set. + * + * Fetches the system timestamp corresponding to the time of the most recent + * vblank interval on specified CRTC. May call into kms-driver to + * compute the timestamp with a high-precision GPU specific method. + * + * Returns zero if timestamp originates from uncorrected do_gettimeofday() + * call, i.e., it isn't very precisely locked to the true vblank. + * + * Returns: + * True if timestamp is considered to be very precise, false otherwise. + */ +static bool +drm_get_last_vbltimestamp(struct drm_device *dev, unsigned int pipe, + struct timeval *tvblank, bool in_vblank_irq) +{ + bool ret = false; + + /* Define requested maximum error on timestamps (nanoseconds). */ + int max_error = (int) drm_timestamp_precision * 1000; + + /* Query driver if possible and precision timestamping enabled. */ + if (dev->driver->get_vblank_timestamp && (max_error > 0)) + ret = dev->driver->get_vblank_timestamp(dev, pipe, &max_error, + tvblank, in_vblank_irq); + + /* GPU high precision timestamp query unsupported or failed. + * Return current monotonic/gettimeofday timestamp as best estimate. + */ + if (!ret) + *tvblank = get_drm_timestamp(); + + return ret; +} + +/** + * drm_crtc_vblank_count - retrieve "cooked" vblank counter value + * @crtc: which counter to retrieve + * + * Fetches the "cooked" vblank count value that represents the number of + * vblank events since the system was booted, including lost events due to + * modesetting activity. + * + * Returns: + * The software vblank counter. + */ +u32 drm_crtc_vblank_count(struct drm_crtc *crtc) +{ + return drm_vblank_count(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_count); + +/** + * drm_vblank_count_and_time - retrieve "cooked" vblank counter value and the + * system timestamp corresponding to that vblank counter value. + * @dev: DRM device + * @pipe: index of CRTC whose counter to retrieve + * @vblanktime: Pointer to struct timeval to receive the vblank timestamp. + * + * Fetches the "cooked" vblank count value that represents the number of + * vblank events since the system was booted, including lost events due to + * modesetting activity. Returns corresponding system timestamp of the time + * of the vblank interval that corresponds to the current vblank counter value. + * + * This is the legacy version of drm_crtc_vblank_count_and_time(). + */ +static u32 drm_vblank_count_and_time(struct drm_device *dev, unsigned int pipe, + struct timeval *vblanktime) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + u32 vblank_count; + unsigned int seq; + + if (WARN_ON(pipe >= dev->num_crtcs)) { + *vblanktime = (struct timeval) { 0 }; + return 0; + } + + do { + seq = read_seqbegin(&vblank->seqlock); + vblank_count = vblank->count; + *vblanktime = vblank->time; + } while (read_seqretry(&vblank->seqlock, seq)); + + return vblank_count; +} + +/** + * drm_crtc_vblank_count_and_time - retrieve "cooked" vblank counter value + * and the system timestamp corresponding to that vblank counter value + * @crtc: which counter to retrieve + * @vblanktime: Pointer to struct timeval to receive the vblank timestamp. + * + * Fetches the "cooked" vblank count value that represents the number of + * vblank events since the system was booted, including lost events due to + * modesetting activity. Returns corresponding system timestamp of the time + * of the vblank interval that corresponds to the current vblank counter value. + */ +u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, + struct timeval *vblanktime) +{ + return drm_vblank_count_and_time(crtc->dev, drm_crtc_index(crtc), + vblanktime); +} +EXPORT_SYMBOL(drm_crtc_vblank_count_and_time); + +static void send_vblank_event(struct drm_device *dev, + struct drm_pending_vblank_event *e, + unsigned long seq, struct timeval *now) +{ + e->event.sequence = seq; + e->event.tv_sec = now->tv_sec; + e->event.tv_usec = now->tv_usec; + + trace_drm_vblank_event_delivered(e->base.file_priv, e->pipe, + e->event.sequence); + + drm_send_event_locked(dev, &e->base); +} + +/** + * drm_crtc_arm_vblank_event - arm vblank event after pageflip + * @crtc: the source CRTC of the vblank event + * @e: the event to send + * + * A lot of drivers need to generate vblank events for the very next vblank + * interrupt. For example when the page flip interrupt happens when the page + * flip gets armed, but not when it actually executes within the next vblank + * period. This helper function implements exactly the required vblank arming + * behaviour. + * + * NOTE: Drivers using this to send out the &drm_crtc_state.event as part of an + * atomic commit must ensure that the next vblank happens at exactly the same + * time as the atomic commit is committed to the hardware. This function itself + * does **not** protect again the next vblank interrupt racing with either this + * function call or the atomic commit operation. A possible sequence could be: + * + * 1. Driver commits new hardware state into vblank-synchronized registers. + * 2. A vblank happens, committing the hardware state. Also the corresponding + * vblank interrupt is fired off and fully processed by the interrupt + * handler. + * 3. The atomic commit operation proceeds to call drm_crtc_arm_vblank_event(). + * 4. The event is only send out for the next vblank, which is wrong. + * + * An equivalent race can happen when the driver calls + * drm_crtc_arm_vblank_event() before writing out the new hardware state. + * + * The only way to make this work safely is to prevent the vblank from firing + * (and the hardware from committing anything else) until the entire atomic + * commit sequence has run to completion. If the hardware does not have such a + * feature (e.g. using a "go" bit), then it is unsafe to use this functions. + * Instead drivers need to manually send out the event from their interrupt + * handler by calling drm_crtc_send_vblank_event() and make sure that there's no + * possible race with the hardware committing the atomic update. + * + * Caller must hold event lock. Caller must also hold a vblank reference for + * the event @e, which will be dropped when the next vblank arrives. + */ +void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, + struct drm_pending_vblank_event *e) +{ + struct drm_device *dev = crtc->dev; + unsigned int pipe = drm_crtc_index(crtc); + + assert_spin_locked(&dev->event_lock); + + e->pipe = pipe; + e->event.sequence = drm_vblank_count(dev, pipe); + e->event.crtc_id = crtc->base.id; + list_add_tail(&e->base.link, &dev->vblank_event_list); +} +EXPORT_SYMBOL(drm_crtc_arm_vblank_event); + +/** + * drm_crtc_send_vblank_event - helper to send vblank event after pageflip + * @crtc: the source CRTC of the vblank event + * @e: the event to send + * + * Updates sequence # and timestamp on event for the most recently processed + * vblank, and sends it to userspace. Caller must hold event lock. + * + * See drm_crtc_arm_vblank_event() for a helper which can be used in certain + * situation, especially to send out events for atomic commit operations. + */ +void drm_crtc_send_vblank_event(struct drm_crtc *crtc, + struct drm_pending_vblank_event *e) +{ + struct drm_device *dev = crtc->dev; + unsigned int seq, pipe = drm_crtc_index(crtc); + struct timeval now; + + if (dev->num_crtcs > 0) { + seq = drm_vblank_count_and_time(dev, pipe, &now); + } else { + seq = 0; + + now = get_drm_timestamp(); + } + e->pipe = pipe; + e->event.crtc_id = crtc->base.id; + send_vblank_event(dev, e, seq, &now); +} +EXPORT_SYMBOL(drm_crtc_send_vblank_event); + +static int __enable_vblank(struct drm_device *dev, unsigned int pipe) +{ + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + struct drm_crtc *crtc = drm_crtc_from_index(dev, pipe); + + if (crtc->funcs->enable_vblank) + return crtc->funcs->enable_vblank(crtc); + } + + return dev->driver->enable_vblank(dev, pipe); +} + +/** + * drm_vblank_enable - enable the vblank interrupt on a CRTC + * @dev: DRM device + * @pipe: CRTC index + * + * Returns: + * Zero on success or a negative error code on failure. + */ +static int drm_vblank_enable(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + int ret = 0; + + assert_spin_locked(&dev->vbl_lock); + + spin_lock(&dev->vblank_time_lock); + + if (!vblank->enabled) { + /* + * Enable vblank irqs under vblank_time_lock protection. + * All vblank count & timestamp updates are held off + * until we are done reinitializing master counter and + * timestamps. Filtercode in drm_handle_vblank() will + * prevent double-accounting of same vblank interval. + */ + ret = __enable_vblank(dev, pipe); + DRM_DEBUG("enabling vblank on crtc %u, ret: %d\n", pipe, ret); + if (ret) { + atomic_dec(&vblank->refcount); + } else { + drm_update_vblank_count(dev, pipe, 0); + /* drm_update_vblank_count() includes a wmb so we just + * need to ensure that the compiler emits the write + * to mark the vblank as enabled after the call + * to drm_update_vblank_count(). + */ + WRITE_ONCE(vblank->enabled, true); + } + } + + spin_unlock(&dev->vblank_time_lock); + + return ret; +} + +/** + * drm_vblank_get - get a reference count on vblank events + * @dev: DRM device + * @pipe: index of CRTC to own + * + * Acquire a reference count on vblank events to avoid having them disabled + * while in use. + * + * This is the legacy version of drm_crtc_vblank_get(). + * + * Returns: + * Zero on success or a negative error code on failure. + */ +static int drm_vblank_get(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + unsigned long irqflags; + int ret = 0; + + if (!dev->num_crtcs) + return -EINVAL; + + if (WARN_ON(pipe >= dev->num_crtcs)) + return -EINVAL; + + spin_lock_irqsave(&dev->vbl_lock, irqflags); + /* Going from 0->1 means we have to enable interrupts again */ + if (atomic_add_return(1, &vblank->refcount) == 1) { + ret = drm_vblank_enable(dev, pipe); + } else { + if (!vblank->enabled) { + atomic_dec(&vblank->refcount); + ret = -EINVAL; + } + } + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + + return ret; +} + +/** + * drm_crtc_vblank_get - get a reference count on vblank events + * @crtc: which CRTC to own + * + * Acquire a reference count on vblank events to avoid having them disabled + * while in use. + * + * Returns: + * Zero on success or a negative error code on failure. + */ +int drm_crtc_vblank_get(struct drm_crtc *crtc) +{ + return drm_vblank_get(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_get); + +/** + * drm_vblank_put - release ownership of vblank events + * @dev: DRM device + * @pipe: index of CRTC to release + * + * Release ownership of a given vblank counter, turning off interrupts + * if possible. Disable interrupts after drm_vblank_offdelay milliseconds. + * + * This is the legacy version of drm_crtc_vblank_put(). + */ +static void drm_vblank_put(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + + if (WARN_ON(pipe >= dev->num_crtcs)) + return; + + if (WARN_ON(atomic_read(&vblank->refcount) == 0)) + return; + + /* Last user schedules interrupt disable */ + if (atomic_dec_and_test(&vblank->refcount)) { + if (drm_vblank_offdelay == 0) + return; + else if (drm_vblank_offdelay < 0) + vblank_disable_fn((unsigned long)vblank); + else if (!dev->vblank_disable_immediate) + mod_timer(&vblank->disable_timer, + jiffies + ((drm_vblank_offdelay * HZ)/1000)); + } +} + +/** + * drm_crtc_vblank_put - give up ownership of vblank events + * @crtc: which counter to give up + * + * Release ownership of a given vblank counter, turning off interrupts + * if possible. Disable interrupts after drm_vblank_offdelay milliseconds. + */ +void drm_crtc_vblank_put(struct drm_crtc *crtc) +{ + drm_vblank_put(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_vblank_put); + +/** + * drm_wait_one_vblank - wait for one vblank + * @dev: DRM device + * @pipe: CRTC index + * + * This waits for one vblank to pass on @pipe, using the irq driver interfaces. + * It is a failure to call this when the vblank irq for @pipe is disabled, e.g. + * due to lack of driver support or because the crtc is off. + */ +void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + int ret; + u32 last; + + if (WARN_ON(pipe >= dev->num_crtcs)) + return; + + ret = drm_vblank_get(dev, pipe); + if (WARN(ret, "vblank not available on crtc %i, ret=%i\n", pipe, ret)) + return; + + last = drm_vblank_count(dev, pipe); + + ret = wait_event_timeout(vblank->queue, + last != drm_vblank_count(dev, pipe), + msecs_to_jiffies(100)); + + WARN(ret == 0, "vblank wait timed out on crtc %i\n", pipe); + + drm_vblank_put(dev, pipe); +} +EXPORT_SYMBOL(drm_wait_one_vblank); + +/** + * drm_crtc_wait_one_vblank - wait for one vblank + * @crtc: DRM crtc + * + * This waits for one vblank to pass on @crtc, using the irq driver interfaces. + * It is a failure to call this when the vblank irq for @crtc is disabled, e.g. + * due to lack of driver support or because the crtc is off. + */ +void drm_crtc_wait_one_vblank(struct drm_crtc *crtc) +{ + drm_wait_one_vblank(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_wait_one_vblank); + +/** + * drm_crtc_vblank_off - disable vblank events on a CRTC + * @crtc: CRTC in question + * + * Drivers can use this function to shut down the vblank interrupt handling when + * disabling a crtc. This function ensures that the latest vblank frame count is + * stored so that drm_vblank_on can restore it again. + * + * Drivers must use this function when the hardware vblank counter can get + * reset, e.g. when suspending. + */ +void drm_crtc_vblank_off(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + unsigned int pipe = drm_crtc_index(crtc); + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + struct drm_pending_vblank_event *e, *t; + struct timeval now; + unsigned long irqflags; + unsigned int seq; + + if (WARN_ON(pipe >= dev->num_crtcs)) + return; + + spin_lock_irqsave(&dev->event_lock, irqflags); + + spin_lock(&dev->vbl_lock); + DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n", + pipe, vblank->enabled, vblank->inmodeset); + + /* Avoid redundant vblank disables without previous + * drm_crtc_vblank_on(). */ + if (drm_core_check_feature(dev, DRIVER_ATOMIC) || !vblank->inmodeset) + drm_vblank_disable_and_save(dev, pipe); + + wake_up(&vblank->queue); + + /* + * Prevent subsequent drm_vblank_get() from re-enabling + * the vblank interrupt by bumping the refcount. + */ + if (!vblank->inmodeset) { + atomic_inc(&vblank->refcount); + vblank->inmodeset = 1; + } + spin_unlock(&dev->vbl_lock); + + /* Send any queued vblank events, lest the natives grow disquiet */ + seq = drm_vblank_count_and_time(dev, pipe, &now); + + list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { + if (e->pipe != pipe) + continue; + DRM_DEBUG("Sending premature vblank event on disable: " + "wanted %u, current %u\n", + e->event.sequence, seq); + list_del(&e->base.link); + drm_vblank_put(dev, pipe); + send_vblank_event(dev, e, seq, &now); + } + spin_unlock_irqrestore(&dev->event_lock, irqflags); + + /* Will be reset by the modeset helpers when re-enabling the crtc by + * calling drm_calc_timestamping_constants(). */ + vblank->hwmode.crtc_clock = 0; +} +EXPORT_SYMBOL(drm_crtc_vblank_off); + +/** + * drm_crtc_vblank_reset - reset vblank state to off on a CRTC + * @crtc: CRTC in question + * + * Drivers can use this function to reset the vblank state to off at load time. + * Drivers should use this together with the drm_crtc_vblank_off() and + * drm_crtc_vblank_on() functions. The difference compared to + * drm_crtc_vblank_off() is that this function doesn't save the vblank counter + * and hence doesn't need to call any driver hooks. + */ +void drm_crtc_vblank_reset(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + unsigned long irqflags; + unsigned int pipe = drm_crtc_index(crtc); + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + + spin_lock_irqsave(&dev->vbl_lock, irqflags); + /* + * Prevent subsequent drm_vblank_get() from enabling the vblank + * interrupt by bumping the refcount. + */ + if (!vblank->inmodeset) { + atomic_inc(&vblank->refcount); + vblank->inmodeset = 1; + } + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + + WARN_ON(!list_empty(&dev->vblank_event_list)); +} +EXPORT_SYMBOL(drm_crtc_vblank_reset); + +/** + * drm_crtc_vblank_on - enable vblank events on a CRTC + * @crtc: CRTC in question + * + * This functions restores the vblank interrupt state captured with + * drm_crtc_vblank_off() again. Note that calls to drm_crtc_vblank_on() and + * drm_crtc_vblank_off() can be unbalanced and so can also be unconditionally called + * in driver load code to reflect the current hardware state of the crtc. + */ +void drm_crtc_vblank_on(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + unsigned int pipe = drm_crtc_index(crtc); + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + unsigned long irqflags; + + if (WARN_ON(pipe >= dev->num_crtcs)) + return; + + spin_lock_irqsave(&dev->vbl_lock, irqflags); + DRM_DEBUG_VBL("crtc %d, vblank enabled %d, inmodeset %d\n", + pipe, vblank->enabled, vblank->inmodeset); + + /* Drop our private "prevent drm_vblank_get" refcount */ + if (vblank->inmodeset) { + atomic_dec(&vblank->refcount); + vblank->inmodeset = 0; + } + + drm_reset_vblank_timestamp(dev, pipe); + + /* + * re-enable interrupts if there are users left, or the + * user wishes vblank interrupts to be enabled all the time. + */ + if (atomic_read(&vblank->refcount) != 0 || drm_vblank_offdelay == 0) + WARN_ON(drm_vblank_enable(dev, pipe)); + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); +} +EXPORT_SYMBOL(drm_crtc_vblank_on); + +static void drm_legacy_vblank_pre_modeset(struct drm_device *dev, + unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + + /* vblank is not initialized (IRQ not installed ?), or has been freed */ + if (!dev->num_crtcs) + return; + + if (WARN_ON(pipe >= dev->num_crtcs)) + return; + + /* + * To avoid all the problems that might happen if interrupts + * were enabled/disabled around or between these calls, we just + * have the kernel take a reference on the CRTC (just once though + * to avoid corrupting the count if multiple, mismatch calls occur), + * so that interrupts remain enabled in the interim. + */ + if (!vblank->inmodeset) { + vblank->inmodeset = 0x1; + if (drm_vblank_get(dev, pipe) == 0) + vblank->inmodeset |= 0x2; + } +} + +static void drm_legacy_vblank_post_modeset(struct drm_device *dev, + unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + unsigned long irqflags; + + /* vblank is not initialized (IRQ not installed ?), or has been freed */ + if (!dev->num_crtcs) + return; + + if (WARN_ON(pipe >= dev->num_crtcs)) + return; + + if (vblank->inmodeset) { + spin_lock_irqsave(&dev->vbl_lock, irqflags); + drm_reset_vblank_timestamp(dev, pipe); + spin_unlock_irqrestore(&dev->vbl_lock, irqflags); + + if (vblank->inmodeset & 0x2) + drm_vblank_put(dev, pipe); + + vblank->inmodeset = 0; + } +} + +int drm_legacy_modeset_ctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_modeset_ctl *modeset = data; + unsigned int pipe; + + /* If drm_vblank_init() hasn't been called yet, just no-op */ + if (!dev->num_crtcs) + return 0; + + /* KMS drivers handle this internally */ + if (!drm_core_check_feature(dev, DRIVER_LEGACY)) + return 0; + + pipe = modeset->crtc; + if (pipe >= dev->num_crtcs) + return -EINVAL; + + switch (modeset->cmd) { + case _DRM_PRE_MODESET: + drm_legacy_vblank_pre_modeset(dev, pipe); + break; + case _DRM_POST_MODESET: + drm_legacy_vblank_post_modeset(dev, pipe); + break; + default: + return -EINVAL; + } + + return 0; +} + +static inline bool vblank_passed(u32 seq, u32 ref) +{ + return (seq - ref) <= (1 << 23); +} + +static int drm_queue_vblank_event(struct drm_device *dev, unsigned int pipe, + union drm_wait_vblank *vblwait, + struct drm_file *file_priv) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + struct drm_pending_vblank_event *e; + struct timeval now; + unsigned long flags; + unsigned int seq; + int ret; + + e = kzalloc(sizeof(*e), GFP_KERNEL); + if (e == NULL) { + ret = -ENOMEM; + goto err_put; + } + + e->pipe = pipe; + e->event.base.type = DRM_EVENT_VBLANK; + e->event.base.length = sizeof(e->event); + e->event.user_data = vblwait->request.signal; + + spin_lock_irqsave(&dev->event_lock, flags); + + /* + * drm_crtc_vblank_off() might have been called after we called + * drm_vblank_get(). drm_crtc_vblank_off() holds event_lock around the + * vblank disable, so no need for further locking. The reference from + * drm_vblank_get() protects against vblank disable from another source. + */ + if (!READ_ONCE(vblank->enabled)) { + ret = -EINVAL; + goto err_unlock; + } + + ret = drm_event_reserve_init_locked(dev, file_priv, &e->base, + &e->event.base); + + if (ret) + goto err_unlock; + + seq = drm_vblank_count_and_time(dev, pipe, &now); + + DRM_DEBUG("event on vblank count %u, current %u, crtc %u\n", + vblwait->request.sequence, seq, pipe); + + trace_drm_vblank_event_queued(file_priv, pipe, + vblwait->request.sequence); + + e->event.sequence = vblwait->request.sequence; + if (vblank_passed(seq, vblwait->request.sequence)) { + drm_vblank_put(dev, pipe); + send_vblank_event(dev, e, seq, &now); + vblwait->reply.sequence = seq; + } else { + /* drm_handle_vblank_events will call drm_vblank_put */ + list_add_tail(&e->base.link, &dev->vblank_event_list); + vblwait->reply.sequence = vblwait->request.sequence; + } + + spin_unlock_irqrestore(&dev->event_lock, flags); + + return 0; + +err_unlock: + spin_unlock_irqrestore(&dev->event_lock, flags); + kfree(e); +err_put: + drm_vblank_put(dev, pipe); + return ret; +} + +static bool drm_wait_vblank_is_query(union drm_wait_vblank *vblwait) +{ + if (vblwait->request.sequence) + return false; + + return _DRM_VBLANK_RELATIVE == + (vblwait->request.type & (_DRM_VBLANK_TYPES_MASK | + _DRM_VBLANK_EVENT | + _DRM_VBLANK_NEXTONMISS)); +} + +/* + * Wait for VBLANK. + * + * \param inode device inode. + * \param file_priv DRM file private. + * \param cmd command. + * \param data user argument, pointing to a drm_wait_vblank structure. + * \return zero on success or a negative number on failure. + * + * This function enables the vblank interrupt on the pipe requested, then + * sleeps waiting for the requested sequence number to occur, and drops + * the vblank interrupt refcount afterwards. (vblank IRQ disable follows that + * after a timeout with no further vblank waits scheduled). + */ +int drm_wait_vblank(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vblank_crtc *vblank; + union drm_wait_vblank *vblwait = data; + int ret; + unsigned int flags, seq, pipe, high_pipe; + + if (!dev->irq_enabled) + return -EINVAL; + + if (vblwait->request.type & _DRM_VBLANK_SIGNAL) + return -EINVAL; + + if (vblwait->request.type & + ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK | + _DRM_VBLANK_HIGH_CRTC_MASK)) { + DRM_ERROR("Unsupported type value 0x%x, supported mask 0x%x\n", + vblwait->request.type, + (_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK | + _DRM_VBLANK_HIGH_CRTC_MASK)); + return -EINVAL; + } + + flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; + high_pipe = (vblwait->request.type & _DRM_VBLANK_HIGH_CRTC_MASK); + if (high_pipe) + pipe = high_pipe >> _DRM_VBLANK_HIGH_CRTC_SHIFT; + else + pipe = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; + if (pipe >= dev->num_crtcs) + return -EINVAL; + + vblank = &dev->vblank[pipe]; + + /* If the counter is currently enabled and accurate, short-circuit + * queries to return the cached timestamp of the last vblank. + */ + if (dev->vblank_disable_immediate && + drm_wait_vblank_is_query(vblwait) && + READ_ONCE(vblank->enabled)) { + struct timeval now; + + vblwait->reply.sequence = + drm_vblank_count_and_time(dev, pipe, &now); + vblwait->reply.tval_sec = now.tv_sec; + vblwait->reply.tval_usec = now.tv_usec; + return 0; + } + + ret = drm_vblank_get(dev, pipe); + if (ret) { + DRM_DEBUG("crtc %d failed to acquire vblank counter, %d\n", pipe, ret); + return ret; + } + seq = drm_vblank_count(dev, pipe); + + switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { + case _DRM_VBLANK_RELATIVE: + vblwait->request.sequence += seq; + vblwait->request.type &= ~_DRM_VBLANK_RELATIVE; + case _DRM_VBLANK_ABSOLUTE: + break; + default: + ret = -EINVAL; + goto done; + } + + if ((flags & _DRM_VBLANK_NEXTONMISS) && + vblank_passed(seq, vblwait->request.sequence)) + vblwait->request.sequence = seq + 1; + + if (flags & _DRM_VBLANK_EVENT) { + /* must hold on to the vblank ref until the event fires + * drm_vblank_put will be called asynchronously + */ + return drm_queue_vblank_event(dev, pipe, vblwait, file_priv); + } + + if (vblwait->request.sequence != seq) { + DRM_DEBUG("waiting on vblank count %u, crtc %u\n", + vblwait->request.sequence, pipe); + DRM_WAIT_ON(ret, vblank->queue, 3 * HZ, + vblank_passed(drm_vblank_count(dev, pipe), + vblwait->request.sequence) || + !READ_ONCE(vblank->enabled)); + } + + if (ret != -EINTR) { + struct timeval now; + + vblwait->reply.sequence = drm_vblank_count_and_time(dev, pipe, &now); + vblwait->reply.tval_sec = now.tv_sec; + vblwait->reply.tval_usec = now.tv_usec; + + DRM_DEBUG("crtc %d returning %u to client\n", + pipe, vblwait->reply.sequence); + } else { + DRM_DEBUG("crtc %d vblank wait interrupted by signal\n", pipe); + } + +done: + drm_vblank_put(dev, pipe); + return ret; +} + +static void drm_handle_vblank_events(struct drm_device *dev, unsigned int pipe) +{ + struct drm_pending_vblank_event *e, *t; + struct timeval now; + unsigned int seq; + + assert_spin_locked(&dev->event_lock); + + seq = drm_vblank_count_and_time(dev, pipe, &now); + + list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) { + if (e->pipe != pipe) + continue; + if (!vblank_passed(seq, e->event.sequence)) + continue; + + DRM_DEBUG("vblank event on %u, current %u\n", + e->event.sequence, seq); + + list_del(&e->base.link); + drm_vblank_put(dev, pipe); + send_vblank_event(dev, e, seq, &now); + } + + trace_drm_vblank_event(pipe, seq); +} + +/** + * drm_handle_vblank - handle a vblank event + * @dev: DRM device + * @pipe: index of CRTC where this event occurred + * + * Drivers should call this routine in their vblank interrupt handlers to + * update the vblank counter and send any signals that may be pending. + * + * This is the legacy version of drm_crtc_handle_vblank(). + */ +bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe) +{ + struct drm_vblank_crtc *vblank = &dev->vblank[pipe]; + unsigned long irqflags; + bool disable_irq; + + if (WARN_ON_ONCE(!dev->num_crtcs)) + return false; + + if (WARN_ON(pipe >= dev->num_crtcs)) + return false; + + spin_lock_irqsave(&dev->event_lock, irqflags); + + /* Need timestamp lock to prevent concurrent execution with + * vblank enable/disable, as this would cause inconsistent + * or corrupted timestamps and vblank counts. + */ + spin_lock(&dev->vblank_time_lock); + + /* Vblank irq handling disabled. Nothing to do. */ + if (!vblank->enabled) { + spin_unlock(&dev->vblank_time_lock); + spin_unlock_irqrestore(&dev->event_lock, irqflags); + return false; + } + + drm_update_vblank_count(dev, pipe, true); + + spin_unlock(&dev->vblank_time_lock); + + wake_up(&vblank->queue); + + /* With instant-off, we defer disabling the interrupt until after + * we finish processing the following vblank after all events have + * been signaled. The disable has to be last (after + * drm_handle_vblank_events) so that the timestamp is always accurate. + */ + disable_irq = (dev->vblank_disable_immediate && + drm_vblank_offdelay > 0 && + !atomic_read(&vblank->refcount)); + + drm_handle_vblank_events(dev, pipe); + + spin_unlock_irqrestore(&dev->event_lock, irqflags); + + if (disable_irq) + vblank_disable_fn((unsigned long)vblank); + + return true; +} +EXPORT_SYMBOL(drm_handle_vblank); + +/** + * drm_crtc_handle_vblank - handle a vblank event + * @crtc: where this event occurred + * + * Drivers should call this routine in their vblank interrupt handlers to + * update the vblank counter and send any signals that may be pending. + * + * This is the native KMS version of drm_handle_vblank(). + * + * Returns: + * True if the event was successfully handled, false on failure. + */ +bool drm_crtc_handle_vblank(struct drm_crtc *crtc) +{ + return drm_handle_vblank(crtc->dev, drm_crtc_index(crtc)); +} +EXPORT_SYMBOL(drm_crtc_handle_vblank); diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 50294a7bd29d..35a8dfc93836 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -171,12 +171,13 @@ static int exynos_drm_suspend(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; if (pm_runtime_suspended(dev) || !drm_dev) return 0; - drm_modeset_lock_all(drm_dev); - drm_for_each_connector(connector, drm_dev) { + drm_connector_list_iter_begin(drm_dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { int old_dpms = connector->dpms; if (connector->funcs->dpms) @@ -185,7 +186,7 @@ static int exynos_drm_suspend(struct device *dev) /* Set the old mode back to the connector for resume */ connector->dpms = old_dpms; } - drm_modeset_unlock_all(drm_dev); + drm_connector_list_iter_end(&conn_iter); return 0; } @@ -194,12 +195,13 @@ static int exynos_drm_resume(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; if (pm_runtime_suspended(dev) || !drm_dev) return 0; - drm_modeset_lock_all(drm_dev); - drm_for_each_connector(connector, drm_dev) { + drm_connector_list_iter_begin(drm_dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { if (connector->funcs->dpms) { int dpms = connector->dpms; @@ -207,7 +209,7 @@ static int exynos_drm_resume(struct device *dev) connector->funcs->dpms(connector, dpms); } } - drm_modeset_unlock_all(drm_dev); + drm_connector_list_iter_end(&conn_iter); return 0; } @@ -376,7 +378,7 @@ static int exynos_drm_bind(struct device *dev) /* Probe non kms sub drivers and virtual display driver. */ ret = exynos_drm_device_subdrv_probe(drm); if (ret) - goto err_cleanup_vblank; + goto err_unbind_all; drm_mode_config_reset(drm); @@ -407,8 +409,6 @@ err_cleanup_fbdev: exynos_drm_fbdev_fini(drm); drm_kms_helper_poll_fini(drm); exynos_drm_device_subdrv_remove(drm); -err_cleanup_vblank: - drm_vblank_cleanup(drm); err_unbind_all: component_unbind_all(drm->dev, drm); err_mode_config_cleanup: diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 7fa21df5bcd7..92cfcae7b3d8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11400,6 +11400,7 @@ static bool check_digital_port_conflicts(struct drm_atomic_state *state) { struct drm_device *dev = state->dev; struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; unsigned int used_ports = 0; unsigned int used_mst_ports = 0; @@ -11408,7 +11409,8 @@ static bool check_digital_port_conflicts(struct drm_atomic_state *state) * list to detect the problem on ddi platforms * where there's just one encoder per digital port. */ - drm_for_each_connector(connector, dev) { + drm_connector_list_iter_begin(dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { struct drm_connector_state *connector_state; struct intel_encoder *encoder; @@ -11447,6 +11449,7 @@ static bool check_digital_port_conflicts(struct drm_atomic_state *state) break; } } + drm_connector_list_iter_end(&conn_iter); /* can't mix MST and SST/HDMI on the same port */ if (used_ports & used_mst_ports) diff --git a/drivers/gpu/drm/imx/imx-drm-core.c b/drivers/gpu/drm/imx/imx-drm-core.c index 50add2f9e250..95e2181963d9 100644 --- a/drivers/gpu/drm/imx/imx-drm-core.c +++ b/drivers/gpu/drm/imx/imx-drm-core.c @@ -278,7 +278,7 @@ static int imx_drm_bind(struct device *dev) /* Now try and bind all our sub-components */ ret = component_bind_all(dev, drm); if (ret) - goto err_vblank; + goto err_kms; drm_mode_config_reset(drm); @@ -316,8 +316,6 @@ err_fbhelper: err_unbind: #endif component_unbind_all(drm->dev, drm); -err_vblank: - drm_vblank_cleanup(drm); err_kms: drm_mode_config_cleanup(drm); err_unref: diff --git a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c index 6b08774e5501..6582e1f56d37 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_crtc.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_crtc.c @@ -221,6 +221,7 @@ static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc) struct drm_crtc *crtc = &mtk_crtc->base; struct drm_connector *connector; struct drm_encoder *encoder; + struct drm_connector_list_iter conn_iter; unsigned int width, height, vrefresh, bpc = MTK_MAX_BPC; int ret; int i; @@ -237,13 +238,15 @@ static int mtk_crtc_ddp_hw_init(struct mtk_drm_crtc *mtk_crtc) if (encoder->crtc != crtc) continue; - drm_for_each_connector(connector, crtc->dev) { + drm_connector_list_iter_begin(crtc->dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { if (connector->encoder != encoder) continue; if (connector->display_info.bpc != 0 && bpc > connector->display_info.bpc) bpc = connector->display_info.bpc; } + drm_connector_list_iter_end(&conn_iter); } ret = pm_runtime_get_sync(crtc->dev->dev); diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index 75382f5f0fce..2c605a406ad5 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -285,7 +285,6 @@ static void meson_drv_unbind(struct device *dev) drm_kms_helper_poll_fini(drm); drm_fbdev_cma_fini(priv->fbdev); drm_mode_config_cleanup(drm); - drm_vblank_cleanup(drm); drm_dev_unref(drm); } diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 9303daa79aba..e9189e59216b 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -2872,17 +2872,20 @@ nv50_msto_enable(struct drm_encoder *encoder) struct nv50_mstc *mstc = NULL; struct nv50_mstm *mstm = NULL; struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; u8 proto, depth; int slots; bool r; - drm_for_each_connector(connector, encoder->dev) { + drm_connector_list_iter_begin(encoder->dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { if (connector->state->best_encoder == &msto->encoder) { mstc = nv50_mstc(connector); mstm = mstc->mstm; break; } } + drm_connector_list_iter_end(&conn_iter); if (WARN_ON(!mstc)) return; diff --git a/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c b/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c index aaa8a58390f1..e1fa143a5625 100644 --- a/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c +++ b/drivers/gpu/drm/omapdrm/displays/connector-analog-tv.c @@ -14,8 +14,6 @@ #include <linux/platform_device.h> #include <linux/of.h> -#include <video/omap-panel-data.h> - #include "../dss/omapdss.h" struct panel_drv_data { @@ -25,8 +23,6 @@ struct panel_drv_data { struct device *dev; struct videomode vm; - - bool invert_polarity; }; static const struct videomode tvc_pal_vm = { @@ -95,13 +91,6 @@ static int tvc_enable(struct omap_dss_device *dssdev) in->ops.atv->set_timings(in, &ddata->vm); - if (!ddata->dev->of_node) { - in->ops.atv->set_type(in, OMAP_DSS_VENC_TYPE_COMPOSITE); - - in->ops.atv->invert_vid_out_polarity(in, - ddata->invert_polarity); - } - r = in->ops.atv->enable(in); if (r) return r; @@ -182,36 +171,10 @@ static struct omap_dss_driver tvc_driver = { .get_timings = tvc_get_timings, .check_timings = tvc_check_timings, - .get_resolution = omapdss_default_get_resolution, - .get_wss = tvc_get_wss, .set_wss = tvc_set_wss, }; -static int tvc_probe_pdata(struct platform_device *pdev) -{ - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct connector_atv_platform_data *pdata; - struct omap_dss_device *in, *dssdev; - - pdata = dev_get_platdata(&pdev->dev); - - in = omap_dss_find_output(pdata->source); - if (in == NULL) { - dev_err(&pdev->dev, "Failed to find video source\n"); - return -EPROBE_DEFER; - } - - ddata->in = in; - - ddata->invert_polarity = pdata->invert_polarity; - - dssdev = &ddata->dssdev; - dssdev->name = pdata->name; - - return 0; -} - static int tvc_probe_of(struct platform_device *pdev) { struct panel_drv_data *ddata = platform_get_drvdata(pdev); @@ -242,17 +205,9 @@ static int tvc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); ddata->dev = &pdev->dev; - if (dev_get_platdata(&pdev->dev)) { - r = tvc_probe_pdata(pdev); - if (r) - return r; - } else if (pdev->dev.of_node) { - r = tvc_probe_of(pdev); - if (r) - return r; - } else { - return -ENODEV; - } + r = tvc_probe_of(pdev); + if (r) + return r; ddata->vm = tvc_pal_vm; diff --git a/drivers/gpu/drm/omapdrm/displays/connector-dvi.c b/drivers/gpu/drm/omapdrm/displays/connector-dvi.c index d6875d9fcefa..05fa24a518c8 100644 --- a/drivers/gpu/drm/omapdrm/displays/connector-dvi.c +++ b/drivers/gpu/drm/omapdrm/displays/connector-dvi.c @@ -15,7 +15,6 @@ #include <linux/slab.h> #include <drm/drm_edid.h> -#include <video/omap-panel-data.h> #include "../dss/omapdss.h" @@ -228,8 +227,6 @@ static struct omap_dss_driver dvic_driver = { .get_timings = dvic_get_timings, .check_timings = dvic_check_timings, - .get_resolution = omapdss_default_get_resolution, - .read_edid = dvic_read_edid, .detect = dvic_detect, }; diff --git a/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c b/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c index 1ef130641bae..79cb69f1acf5 100644 --- a/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c +++ b/drivers/gpu/drm/omapdrm/displays/connector-hdmi.c @@ -17,7 +17,6 @@ #include <linux/of_gpio.h> #include <drm/drm_edid.h> -#include <video/omap-panel-data.h> #include "../dss/omapdss.h" @@ -196,8 +195,6 @@ static struct omap_dss_driver hdmic_driver = { .get_timings = hdmic_get_timings, .check_timings = hdmic_check_timings, - .get_resolution = omapdss_default_get_resolution, - .read_edid = hdmic_read_edid, .detect = hdmic_detect, .set_hdmi_mode = hdmic_set_hdmi_mode, diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c b/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c index f7a5731492d0..b1f6aa09f699 100644 --- a/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c +++ b/drivers/gpu/drm/omapdrm/displays/encoder-opa362.c @@ -157,14 +157,6 @@ static int opa362_check_timings(struct omap_dss_device *dssdev, return in->ops.atv->check_timings(in, vm); } -static void opa362_set_type(struct omap_dss_device *dssdev, - enum omap_dss_venc_type type) -{ - /* we can only drive a COMPOSITE output */ - WARN_ON(type != OMAP_DSS_VENC_TYPE_COMPOSITE); - -} - static const struct omapdss_atv_ops opa362_atv_ops = { .connect = opa362_connect, .disconnect = opa362_disconnect, @@ -175,8 +167,6 @@ static const struct omapdss_atv_ops opa362_atv_ops = { .check_timings = opa362_check_timings, .set_timings = opa362_set_timings, .get_timings = opa362_get_timings, - - .set_type = opa362_set_type, }; static int opa362_probe(struct platform_device *pdev) diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c b/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c index 13e32d02c884..947295f9e30f 100644 --- a/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c +++ b/drivers/gpu/drm/omapdrm/displays/encoder-tfp410.c @@ -22,7 +22,6 @@ struct panel_drv_data { struct omap_dss_device *in; int pd_gpio; - int data_lines; struct videomode vm; }; @@ -82,8 +81,6 @@ static int tfp410_enable(struct omap_dss_device *dssdev) return 0; in->ops.dpi->set_timings(in, &ddata->vm); - if (ddata->data_lines) - in->ops.dpi->set_data_lines(in, ddata->data_lines); r = in->ops.dpi->enable(in); if (r) @@ -226,7 +223,6 @@ static int tfp410_probe(struct platform_device *pdev) dssdev->type = OMAP_DISPLAY_TYPE_DPI; dssdev->output_type = OMAP_DISPLAY_TYPE_DVI; dssdev->owner = THIS_MODULE; - dssdev->phy.dpi.data_lines = ddata->data_lines; dssdev->port_num = 1; r = omapdss_register_output(dssdev); diff --git a/drivers/gpu/drm/omapdrm/displays/panel-dpi.c b/drivers/gpu/drm/omapdrm/displays/panel-dpi.c index 04ce8c5f2954..6468a765f3d1 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-dpi.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-dpi.c @@ -14,11 +14,9 @@ #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/of.h> -#include <linux/of_gpio.h> #include <linux/regulator/consumer.h> #include <linux/backlight.h> -#include <video/omap-panel-data.h> #include <video/of_display_timing.h> #include "../dss/omapdss.h" @@ -27,15 +25,10 @@ struct panel_drv_data { struct omap_dss_device dssdev; struct omap_dss_device *in; - int data_lines; - struct videomode vm; struct backlight_device *backlight; - /* used for non-DT boot, to be removed */ - int backlight_gpio; - struct gpio_desc *enable_gpio; struct regulator *vcc_supply; }; @@ -81,8 +74,6 @@ static int panel_dpi_enable(struct omap_dss_device *dssdev) if (omapdss_device_is_enabled(dssdev)) return 0; - if (ddata->data_lines) - in->ops.dpi->set_data_lines(in, ddata->data_lines); in->ops.dpi->set_timings(in, &ddata->vm); r = in->ops.dpi->enable(in); @@ -97,9 +88,6 @@ static int panel_dpi_enable(struct omap_dss_device *dssdev) gpiod_set_value_cansleep(ddata->enable_gpio, 1); - if (gpio_is_valid(ddata->backlight_gpio)) - gpio_set_value_cansleep(ddata->backlight_gpio, 1); - if (ddata->backlight) { ddata->backlight->props.power = FB_BLANK_UNBLANK; backlight_update_status(ddata->backlight); @@ -118,9 +106,6 @@ static void panel_dpi_disable(struct omap_dss_device *dssdev) if (!omapdss_device_is_enabled(dssdev)) return; - if (gpio_is_valid(ddata->backlight_gpio)) - gpio_set_value_cansleep(ddata->backlight_gpio, 0); - if (ddata->backlight) { ddata->backlight->props.power = FB_BLANK_POWERDOWN; backlight_update_status(ddata->backlight); @@ -173,51 +158,8 @@ static struct omap_dss_driver panel_dpi_ops = { .set_timings = panel_dpi_set_timings, .get_timings = panel_dpi_get_timings, .check_timings = panel_dpi_check_timings, - - .get_resolution = omapdss_default_get_resolution, }; -static int panel_dpi_probe_pdata(struct platform_device *pdev) -{ - const struct panel_dpi_platform_data *pdata; - struct panel_drv_data *ddata = platform_get_drvdata(pdev); - struct omap_dss_device *dssdev, *in; - int r; - - pdata = dev_get_platdata(&pdev->dev); - - in = omap_dss_find_output(pdata->source); - if (in == NULL) { - dev_err(&pdev->dev, "failed to find video source '%s'\n", - pdata->source); - return -EPROBE_DEFER; - } - - ddata->in = in; - - ddata->data_lines = pdata->data_lines; - - videomode_from_timing(pdata->display_timing, &ddata->vm); - - dssdev = &ddata->dssdev; - dssdev->name = pdata->name; - - r = devm_gpio_request_one(&pdev->dev, pdata->enable_gpio, - GPIOF_OUT_INIT_LOW, "panel enable"); - if (r) - goto err_gpio; - - ddata->enable_gpio = gpio_to_desc(pdata->enable_gpio); - - ddata->backlight_gpio = pdata->backlight_gpio; - - return 0; - -err_gpio: - omap_dss_put_device(ddata->in); - return r; -} - static int panel_dpi_probe_of(struct platform_device *pdev) { struct panel_drv_data *ddata = platform_get_drvdata(pdev); @@ -248,8 +190,6 @@ static int panel_dpi_probe_of(struct platform_device *pdev) if (IS_ERR(ddata->vcc_supply)) return PTR_ERR(ddata->vcc_supply); - ddata->backlight_gpio = -ENOENT; - bl_node = of_parse_phandle(node, "backlight", 0); if (bl_node) { ddata->backlight = of_find_backlight_by_node(bl_node); @@ -297,24 +237,9 @@ static int panel_dpi_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); - if (dev_get_platdata(&pdev->dev)) { - r = panel_dpi_probe_pdata(pdev); - if (r) - return r; - } else if (pdev->dev.of_node) { - r = panel_dpi_probe_of(pdev); - if (r) - return r; - } else { - return -ENODEV; - } - - if (gpio_is_valid(ddata->backlight_gpio)) { - r = devm_gpio_request_one(&pdev->dev, ddata->backlight_gpio, - GPIOF_OUT_INIT_LOW, "panel backlight"); - if (r) - goto err_gpio; - } + r = panel_dpi_probe_of(pdev); + if (r) + return r; dssdev = &ddata->dssdev; dssdev->dev = &pdev->dev; @@ -322,7 +247,6 @@ static int panel_dpi_probe(struct platform_device *pdev) dssdev->type = OMAP_DISPLAY_TYPE_DPI; dssdev->owner = THIS_MODULE; dssdev->panel.vm = ddata->vm; - dssdev->phy.dpi.data_lines = ddata->data_lines; r = omapdss_register_display(dssdev); if (r) { @@ -333,7 +257,6 @@ static int panel_dpi_probe(struct platform_device *pdev) return 0; err_reg: -err_gpio: omap_dss_put_device(ddata->in); return r; } diff --git a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c index ac5800c72cb4..76787a75a4dc 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-dsi-cm.c @@ -379,13 +379,6 @@ static const struct backlight_ops dsicm_bl_ops = { .update_status = dsicm_bl_update_status, }; -static void dsicm_get_resolution(struct omap_dss_device *dssdev, - u16 *xres, u16 *yres) -{ - *xres = dssdev->panel.vm.hactive; - *yres = dssdev->panel.vm.vactive; -} - static ssize_t dsicm_num_errors_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1116,9 +1109,6 @@ static struct omap_dss_driver dsicm_ops = { .update = dsicm_update, .sync = dsicm_sync, - .get_resolution = dsicm_get_resolution, - .get_recommended_bpp = omapdss_default_get_recommended_bpp, - .enable_te = dsicm_enable_te, .get_te = dsicm_get_te, diff --git a/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c b/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c index 43d21edb51f5..c90474afaebd 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-lgphilips-lb035q02.c @@ -49,8 +49,6 @@ struct panel_drv_data { struct spi_device *spi; - int data_lines; - struct videomode vm; struct gpio_desc *enable_gpio; @@ -159,8 +157,6 @@ static int lb035q02_enable(struct omap_dss_device *dssdev) if (omapdss_device_is_enabled(dssdev)) return 0; - if (ddata->data_lines) - in->ops.dpi->set_data_lines(in, ddata->data_lines); in->ops.dpi->set_timings(in, &ddata->vm); r = in->ops.dpi->enable(in); @@ -230,8 +226,6 @@ static struct omap_dss_driver lb035q02_ops = { .set_timings = lb035q02_set_timings, .get_timings = lb035q02_get_timings, .check_timings = lb035q02_check_timings, - - .get_resolution = omapdss_default_get_resolution, }; static int lb035q02_probe_of(struct spi_device *spi) @@ -289,7 +283,6 @@ static int lb035q02_panel_spi_probe(struct spi_device *spi) dssdev->type = OMAP_DISPLAY_TYPE_DPI; dssdev->owner = THIS_MODULE; dssdev->panel.vm = ddata->vm; - dssdev->phy.dpi.data_lines = ddata->data_lines; r = omapdss_register_display(dssdev); if (r) { diff --git a/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c b/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c index 2de27ba01552..df8132d3b9c6 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-nec-nl8048hl11.c @@ -25,8 +25,6 @@ struct panel_drv_data { struct videomode vm; - int data_lines; - int res_gpio; int qvga_gpio; @@ -153,8 +151,6 @@ static int nec_8048_enable(struct omap_dss_device *dssdev) if (omapdss_device_is_enabled(dssdev)) return 0; - if (ddata->data_lines) - in->ops.dpi->set_data_lines(in, ddata->data_lines); in->ops.dpi->set_timings(in, &ddata->vm); r = in->ops.dpi->enable(in); @@ -224,8 +220,6 @@ static struct omap_dss_driver nec_8048_ops = { .set_timings = nec_8048_set_timings, .get_timings = nec_8048_get_timings, .check_timings = nec_8048_check_timings, - - .get_resolution = omapdss_default_get_resolution, }; static int nec_8048_probe_of(struct spi_device *spi) diff --git a/drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c b/drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c index 04fe235b7cac..98d170aecaba 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-sharp-ls037v7dw01.c @@ -24,8 +24,6 @@ struct panel_drv_data { struct omap_dss_device *in; struct regulator *vcc; - int data_lines; - struct videomode vm; struct gpio_desc *resb_gpio; /* low = reset active min 20 us */ @@ -99,8 +97,6 @@ static int sharp_ls_enable(struct omap_dss_device *dssdev) if (omapdss_device_is_enabled(dssdev)) return 0; - if (ddata->data_lines) - in->ops.dpi->set_data_lines(in, ddata->data_lines); in->ops.dpi->set_timings(in, &ddata->vm); if (ddata->vcc) { @@ -194,8 +190,6 @@ static struct omap_dss_driver sharp_ls_ops = { .set_timings = sharp_ls_set_timings, .get_timings = sharp_ls_get_timings, .check_timings = sharp_ls_check_timings, - - .get_resolution = omapdss_default_get_resolution, }; static int sharp_ls_get_gpio_of(struct device *dev, int index, int val, @@ -289,7 +283,6 @@ static int sharp_ls_probe(struct platform_device *pdev) dssdev->type = OMAP_DISPLAY_TYPE_DPI; dssdev->owner = THIS_MODULE; dssdev->panel.vm = ddata->vm; - dssdev->phy.dpi.data_lines = ddata->data_lines; r = omapdss_register_display(dssdev); if (r) { diff --git a/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c b/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c index 5ab39e0060f2..346aefdb015f 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c @@ -32,8 +32,6 @@ #include <linux/of.h> #include <linux/of_gpio.h> -#include <video/omap-panel-data.h> - #include "../dss/omapdss.h" #define MIPID_CMD_READ_DISP_ID 0x04 @@ -69,7 +67,6 @@ struct panel_drv_data { struct omap_dss_device *in; int reset_gpio; - int datapairs; struct videomode vm; @@ -547,9 +544,6 @@ static int acx565akm_panel_power_on(struct omap_dss_device *dssdev) in->ops.sdi->set_timings(in, &ddata->vm); - if (ddata->datapairs > 0) - in->ops.sdi->set_datapairs(in, ddata->datapairs); - r = in->ops.sdi->enable(in); if (r) { pr_err("%s sdi enable failed\n", __func__); @@ -697,36 +691,8 @@ static struct omap_dss_driver acx565akm_ops = { .set_timings = acx565akm_set_timings, .get_timings = acx565akm_get_timings, .check_timings = acx565akm_check_timings, - - .get_resolution = omapdss_default_get_resolution, }; -static int acx565akm_probe_pdata(struct spi_device *spi) -{ - const struct panel_acx565akm_platform_data *pdata; - struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); - struct omap_dss_device *dssdev, *in; - - pdata = dev_get_platdata(&spi->dev); - - ddata->reset_gpio = pdata->reset_gpio; - - in = omap_dss_find_output(pdata->source); - if (in == NULL) { - dev_err(&spi->dev, "failed to find video source '%s'\n", - pdata->source); - return -EPROBE_DEFER; - } - ddata->in = in; - - ddata->datapairs = pdata->datapairs; - - dssdev = &ddata->dssdev; - dssdev->name = pdata->name; - - return 0; -} - static int acx565akm_probe_of(struct spi_device *spi) { struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); @@ -766,18 +732,9 @@ static int acx565akm_probe(struct spi_device *spi) mutex_init(&ddata->mutex); - if (dev_get_platdata(&spi->dev)) { - r = acx565akm_probe_pdata(spi); - if (r) - return r; - } else if (spi->dev.of_node) { - r = acx565akm_probe_of(spi); - if (r) - return r; - } else { - dev_err(&spi->dev, "platform data missing!\n"); - return -ENODEV; - } + r = acx565akm_probe_of(spi); + if (r) + return r; if (gpio_is_valid(ddata->reset_gpio)) { r = devm_gpio_request_one(&spi->dev, ddata->reset_gpio, diff --git a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c index f313dbfcbacb..cbf4c67c4933 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c @@ -35,8 +35,6 @@ struct panel_drv_data { struct omap_dss_device dssdev; struct omap_dss_device *in; - int data_lines; - struct videomode vm; struct spi_device *spi_dev; @@ -207,8 +205,6 @@ static int td028ttec1_panel_enable(struct omap_dss_device *dssdev) if (omapdss_device_is_enabled(dssdev)) return 0; - if (ddata->data_lines) - in->ops.dpi->set_data_lines(in, ddata->data_lines); in->ops.dpi->set_timings(in, &ddata->vm); r = in->ops.dpi->enable(in); @@ -423,7 +419,6 @@ static int td028ttec1_panel_probe(struct spi_device *spi) dssdev->type = OMAP_DISPLAY_TYPE_DPI; dssdev->owner = THIS_MODULE; dssdev->panel.vm = ddata->vm; - dssdev->phy.dpi.data_lines = ddata->data_lines; r = omapdss_register_display(dssdev); if (r) { diff --git a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td043mtea1.c b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td043mtea1.c index 0787dba44faa..20c6d8fe215a 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td043mtea1.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td043mtea1.c @@ -58,8 +58,6 @@ struct panel_drv_data { struct videomode vm; - int data_lines; - struct spi_device *spi; struct regulator *vcc_reg; int nreset_gpio; @@ -378,8 +376,6 @@ static int tpo_td043_enable(struct omap_dss_device *dssdev) if (omapdss_device_is_enabled(dssdev)) return 0; - if (ddata->data_lines) - in->ops.dpi->set_data_lines(in, ddata->data_lines); in->ops.dpi->set_timings(in, &ddata->vm); r = in->ops.dpi->enable(in); @@ -461,8 +457,6 @@ static struct omap_dss_driver tpo_td043_ops = { .set_mirror = tpo_td043_set_hmirror, .get_mirror = tpo_td043_get_hmirror, - - .get_resolution = omapdss_default_get_resolution, }; static int tpo_td043_probe_of(struct spi_device *spi) diff --git a/drivers/gpu/drm/omapdrm/dss/Kconfig b/drivers/gpu/drm/omapdrm/dss/Kconfig index f53adb944a0d..8b87d5cf45fc 100644 --- a/drivers/gpu/drm/omapdrm/dss/Kconfig +++ b/drivers/gpu/drm/omapdrm/dss/Kconfig @@ -49,19 +49,6 @@ config OMAP2_DSS_DPI help DPI Interface. This is the Parallel Display Interface. -config OMAP2_DSS_RFBI - bool "RFBI support" - depends on BROKEN - default n - help - MIPI DBI support (RFBI, Remote Framebuffer Interface, in Texas - Instrument's terminology). - - DBI is a bus between the host processor and a peripheral, - such as a display or a framebuffer chip. - - See http://www.mipi.org/ for DBI specifications. - config OMAP2_DSS_VENC bool "VENC support" default y diff --git a/drivers/gpu/drm/omapdrm/dss/Makefile b/drivers/gpu/drm/omapdrm/dss/Makefile index 75ec30f231c7..688195e448c5 100644 --- a/drivers/gpu/drm/omapdrm/dss/Makefile +++ b/drivers/gpu/drm/omapdrm/dss/Makefile @@ -8,7 +8,6 @@ obj-$(CONFIG_OMAP2_DSS) += omapdss.o omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o \ pll.o video-pll.o omapdss-$(CONFIG_OMAP2_DSS_DPI) += dpi.o -omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o diff --git a/drivers/gpu/drm/omapdrm/dss/core.c b/drivers/gpu/drm/omapdrm/dss/core.c index 6a3ebfcd7223..bdce4bfdf6e0 100644 --- a/drivers/gpu/drm/omapdrm/dss/core.c +++ b/drivers/gpu/drm/omapdrm/dss/core.c @@ -41,20 +41,8 @@ static struct { struct platform_device *pdev; - - const char *default_display_name; } core; -static char *def_disp_name; -module_param_named(def_disp, def_disp_name, charp, 0); -MODULE_PARM_DESC(def_disp, "default display name"); - -const char *omapdss_get_default_display_name(void) -{ - return core.default_display_name; -} -EXPORT_SYMBOL(omapdss_get_default_display_name); - enum omapdss_version omapdss_get_version(void) { struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; @@ -62,11 +50,6 @@ enum omapdss_version omapdss_get_version(void) } EXPORT_SYMBOL(omapdss_get_version); -struct platform_device *dss_get_core_pdev(void) -{ - return core.pdev; -} - int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask) { struct omap_dss_board_info *board_data = core.pdev->dev.platform_data; @@ -180,7 +163,6 @@ static void dss_disable_all_devices(void) static int __init omap_dss_probe(struct platform_device *pdev) { - struct omap_dss_board_info *pdata = pdev->dev.platform_data; int r; core.pdev = pdev; @@ -191,11 +173,6 @@ static int __init omap_dss_probe(struct platform_device *pdev) if (r) goto err_debugfs; - if (def_disp_name) - core.default_display_name = def_disp_name; - else if (pdata->default_display_name) - core.default_display_name = pdata->default_display_name; - return 0; err_debugfs: @@ -231,15 +208,6 @@ static int (*dss_output_drv_reg_funcs[])(void) __initdata = { #ifdef CONFIG_OMAP2_DSS_DSI dsi_init_platform_driver, #endif -#ifdef CONFIG_OMAP2_DSS_DPI - dpi_init_platform_driver, -#endif -#ifdef CONFIG_OMAP2_DSS_SDI - sdi_init_platform_driver, -#endif -#ifdef CONFIG_OMAP2_DSS_RFBI - rfbi_init_platform_driver, -#endif #ifdef CONFIG_OMAP2_DSS_VENC venc_init_platform_driver, #endif @@ -261,15 +229,6 @@ static void (*dss_output_drv_unreg_funcs[])(void) = { #ifdef CONFIG_OMAP2_DSS_VENC venc_uninit_platform_driver, #endif -#ifdef CONFIG_OMAP2_DSS_RFBI - rfbi_uninit_platform_driver, -#endif -#ifdef CONFIG_OMAP2_DSS_SDI - sdi_uninit_platform_driver, -#endif -#ifdef CONFIG_OMAP2_DSS_DPI - dpi_uninit_platform_driver, -#endif #ifdef CONFIG_OMAP2_DSS_DSI dsi_uninit_platform_driver, #endif diff --git a/drivers/gpu/drm/omapdrm/dss/dispc.c b/drivers/gpu/drm/omapdrm/dss/dispc.c index 5ac0145fbae6..fd7504b37e3b 100644 --- a/drivers/gpu/drm/omapdrm/dss/dispc.c +++ b/drivers/gpu/drm/omapdrm/dss/dispc.c @@ -40,6 +40,8 @@ #include <linux/regmap.h> #include <linux/of.h> #include <linux/component.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_blend.h> #include "omapdss.h" #include "dss.h" @@ -77,7 +79,7 @@ struct dispc_features { int (*calc_scaling) (unsigned long pclk, unsigned long lclk, const struct videomode *vm, u16 width, u16 height, u16 out_width, u16 out_height, - enum omap_color_mode color_mode, bool *five_taps, + u32 fourcc, bool *five_taps, int *x_predecim, int *y_predecim, int *decim_x, int *decim_y, u16 pos_x, unsigned long *core_clk, bool mem_to_mem); unsigned long (*calc_core_clk) (unsigned long pclk, @@ -158,7 +160,7 @@ enum omap_color_component { */ DISPC_COLOR_COMPONENT_RGB_Y = 1 << 0, /* used for UV component for - * OMAP_DSS_COLOR_YUV2, OMAP_DSS_COLOR_UYVY, OMAP_DSS_COLOR_NV12 + * DRM_FORMAT_YUYV, DRM_FORMAT_UYVY, DRM_FORMAT_NV12 * color formats on OMAP4 */ DISPC_COLOR_COMPONENT_UV = 1 << 1, @@ -905,78 +907,69 @@ static void dispc_ovl_set_row_inc(enum omap_plane_id plane, s32 inc) dispc_write_reg(DISPC_OVL_ROW_INC(plane), inc); } -static void dispc_ovl_set_color_mode(enum omap_plane_id plane, - enum omap_color_mode color_mode) +static void dispc_ovl_set_color_mode(enum omap_plane_id plane, u32 fourcc) { u32 m = 0; if (plane != OMAP_DSS_GFX) { - switch (color_mode) { - case OMAP_DSS_COLOR_NV12: + switch (fourcc) { + case DRM_FORMAT_NV12: m = 0x0; break; - case OMAP_DSS_COLOR_RGBX16: + case DRM_FORMAT_XRGB4444: m = 0x1; break; - case OMAP_DSS_COLOR_RGBA16: + case DRM_FORMAT_RGBA4444: m = 0x2; break; - case OMAP_DSS_COLOR_RGB12U: + case DRM_FORMAT_RGBX4444: m = 0x4; break; - case OMAP_DSS_COLOR_ARGB16: + case DRM_FORMAT_ARGB4444: m = 0x5; break; - case OMAP_DSS_COLOR_RGB16: + case DRM_FORMAT_RGB565: m = 0x6; break; - case OMAP_DSS_COLOR_ARGB16_1555: + case DRM_FORMAT_ARGB1555: m = 0x7; break; - case OMAP_DSS_COLOR_RGB24U: + case DRM_FORMAT_XRGB8888: m = 0x8; break; - case OMAP_DSS_COLOR_RGB24P: + case DRM_FORMAT_RGB888: m = 0x9; break; - case OMAP_DSS_COLOR_YUV2: + case DRM_FORMAT_YUYV: m = 0xa; break; - case OMAP_DSS_COLOR_UYVY: + case DRM_FORMAT_UYVY: m = 0xb; break; - case OMAP_DSS_COLOR_ARGB32: + case DRM_FORMAT_ARGB8888: m = 0xc; break; - case OMAP_DSS_COLOR_RGBA32: + case DRM_FORMAT_RGBA8888: m = 0xd; break; - case OMAP_DSS_COLOR_RGBX32: + case DRM_FORMAT_RGBX8888: m = 0xe; break; - case OMAP_DSS_COLOR_XRGB16_1555: + case DRM_FORMAT_XRGB1555: m = 0xf; break; default: BUG(); return; } } else { - switch (color_mode) { - case OMAP_DSS_COLOR_CLUT1: - m = 0x0; break; - case OMAP_DSS_COLOR_CLUT2: - m = 0x1; break; - case OMAP_DSS_COLOR_CLUT4: - m = 0x2; break; - case OMAP_DSS_COLOR_CLUT8: - m = 0x3; break; - case OMAP_DSS_COLOR_RGB12U: + switch (fourcc) { + case DRM_FORMAT_RGBX4444: m = 0x4; break; - case OMAP_DSS_COLOR_ARGB16: + case DRM_FORMAT_ARGB4444: m = 0x5; break; - case OMAP_DSS_COLOR_RGB16: + case DRM_FORMAT_RGB565: m = 0x6; break; - case OMAP_DSS_COLOR_ARGB16_1555: + case DRM_FORMAT_ARGB1555: m = 0x7; break; - case OMAP_DSS_COLOR_RGB24U: + case DRM_FORMAT_XRGB8888: m = 0x8; break; - case OMAP_DSS_COLOR_RGB24P: + case DRM_FORMAT_RGB888: m = 0x9; break; - case OMAP_DSS_COLOR_RGBX16: + case DRM_FORMAT_XRGB4444: m = 0xa; break; - case OMAP_DSS_COLOR_RGBA16: + case DRM_FORMAT_RGBA4444: m = 0xb; break; - case OMAP_DSS_COLOR_ARGB32: + case DRM_FORMAT_ARGB8888: m = 0xc; break; - case OMAP_DSS_COLOR_RGBA32: + case DRM_FORMAT_RGBA8888: m = 0xd; break; - case OMAP_DSS_COLOR_RGBX32: + case DRM_FORMAT_RGBX8888: m = 0xe; break; - case OMAP_DSS_COLOR_XRGB16_1555: + case DRM_FORMAT_XRGB1555: m = 0xf; break; default: BUG(); return; @@ -986,6 +979,18 @@ static void dispc_ovl_set_color_mode(enum omap_plane_id plane, REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1); } +static bool format_is_yuv(u32 fourcc) +{ + switch (fourcc) { + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_NV12: + return true; + default: + return false; + } +} + static void dispc_ovl_configure_burst_type(enum omap_plane_id plane, enum omap_dss_rotation_type rotation_type) { @@ -1136,7 +1141,7 @@ static u32 dispc_ovl_get_burst_size(enum omap_plane_id plane) return unit * 8; } -static enum omap_color_mode dispc_ovl_get_color_modes(enum omap_plane_id plane) +static const u32 *dispc_ovl_get_color_modes(enum omap_plane_id plane) { return dss_feat_get_supported_color_modes(plane); } @@ -1558,7 +1563,7 @@ static void dispc_ovl_set_scale_param(enum omap_plane_id plane, static void dispc_ovl_set_accu_uv(enum omap_plane_id plane, u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, - bool ilace, enum omap_color_mode color_mode, u8 rotation) + bool ilace, u32 fourcc, u8 rotation) { int h_accu2_0, h_accu2_1; int v_accu2_0, v_accu2_1; @@ -1596,33 +1601,32 @@ static void dispc_ovl_set_accu_uv(enum omap_plane_id plane, { 0, 1, 0, 1, -1, 1, 0, 1 }, }; - switch (rotation) { - case OMAP_DSS_ROT_0: + /* Note: DSS HW rotates clockwise, DRM_MODE_ROTATE_* counter-clockwise */ + switch (rotation & DRM_MODE_ROTATE_MASK) { + default: + case DRM_MODE_ROTATE_0: idx = 0; break; - case OMAP_DSS_ROT_90: - idx = 1; + case DRM_MODE_ROTATE_90: + idx = 3; break; - case OMAP_DSS_ROT_180: + case DRM_MODE_ROTATE_180: idx = 2; break; - case OMAP_DSS_ROT_270: - idx = 3; + case DRM_MODE_ROTATE_270: + idx = 1; break; - default: - BUG(); - return; } - switch (color_mode) { - case OMAP_DSS_COLOR_NV12: + switch (fourcc) { + case DRM_FORMAT_NV12: if (ilace) accu_table = accu_nv12_ilace; else accu_table = accu_nv12; break; - case OMAP_DSS_COLOR_YUV2: - case OMAP_DSS_COLOR_UYVY: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: accu_table = accu_yuv; break; default: @@ -1648,7 +1652,7 @@ static void dispc_ovl_set_scaling_common(enum omap_plane_id plane, u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, bool ilace, bool five_taps, - bool fieldmode, enum omap_color_mode color_mode, + bool fieldmode, u32 fourcc, u8 rotation) { int accu0 = 0; @@ -1702,7 +1706,7 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane_id plane, u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, bool ilace, bool five_taps, - bool fieldmode, enum omap_color_mode color_mode, + bool fieldmode, u32 fourcc, u8 rotation) { int scale_x = out_width != orig_width; @@ -1711,9 +1715,8 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane_id plane, if (!dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) return; - if ((color_mode != OMAP_DSS_COLOR_YUV2 && - color_mode != OMAP_DSS_COLOR_UYVY && - color_mode != OMAP_DSS_COLOR_NV12)) { + + if (!format_is_yuv(fourcc)) { /* reset chroma resampling for RGB formats */ if (plane != OMAP_DSS_WB) REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 8, 8); @@ -1721,10 +1724,10 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane_id plane, } dispc_ovl_set_accu_uv(plane, orig_width, orig_height, out_width, - out_height, ilace, color_mode, rotation); + out_height, ilace, fourcc, rotation); - switch (color_mode) { - case OMAP_DSS_COLOR_NV12: + switch (fourcc) { + case DRM_FORMAT_NV12: if (chroma_upscale) { /* UV is subsampled by 2 horizontally and vertically */ orig_height >>= 1; @@ -1736,11 +1739,10 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane_id plane, } break; - case OMAP_DSS_COLOR_YUV2: - case OMAP_DSS_COLOR_UYVY: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: /* For YUV422 with 90/270 rotation, we don't upsample chroma */ - if (rotation == OMAP_DSS_ROT_0 || - rotation == OMAP_DSS_ROT_180) { + if (!drm_rotation_90_or_270(rotation)) { if (chroma_upscale) /* UV is subsampled by 2 horizontally */ orig_width >>= 1; @@ -1750,7 +1752,7 @@ static void dispc_ovl_set_scaling_uv(enum omap_plane_id plane, } /* must use FIR for YUV422 if rotated */ - if (rotation != OMAP_DSS_ROT_0) + if ((rotation & DRM_MODE_ROTATE_MASK) != DRM_MODE_ROTATE_0) scale_x = scale_y = true; break; @@ -1782,7 +1784,7 @@ static void dispc_ovl_set_scaling(enum omap_plane_id plane, u16 orig_width, u16 orig_height, u16 out_width, u16 out_height, bool ilace, bool five_taps, - bool fieldmode, enum omap_color_mode color_mode, + bool fieldmode, u32 fourcc, u8 rotation) { BUG_ON(plane == OMAP_DSS_GFX); @@ -1791,60 +1793,59 @@ static void dispc_ovl_set_scaling(enum omap_plane_id plane, orig_width, orig_height, out_width, out_height, ilace, five_taps, - fieldmode, color_mode, + fieldmode, fourcc, rotation); dispc_ovl_set_scaling_uv(plane, orig_width, orig_height, out_width, out_height, ilace, five_taps, - fieldmode, color_mode, + fieldmode, fourcc, rotation); } static void dispc_ovl_set_rotation_attrs(enum omap_plane_id plane, u8 rotation, - enum omap_dss_rotation_type rotation_type, - bool mirroring, enum omap_color_mode color_mode) + enum omap_dss_rotation_type rotation_type, u32 fourcc) { bool row_repeat = false; int vidrot = 0; - if (color_mode == OMAP_DSS_COLOR_YUV2 || - color_mode == OMAP_DSS_COLOR_UYVY) { + /* Note: DSS HW rotates clockwise, DRM_MODE_ROTATE_* counter-clockwise */ + if (fourcc == DRM_FORMAT_YUYV || fourcc == DRM_FORMAT_UYVY) { - if (mirroring) { - switch (rotation) { - case OMAP_DSS_ROT_0: + if (rotation & DRM_MODE_REFLECT_X) { + switch (rotation & DRM_MODE_ROTATE_MASK) { + case DRM_MODE_ROTATE_0: vidrot = 2; break; - case OMAP_DSS_ROT_90: + case DRM_MODE_ROTATE_90: vidrot = 1; break; - case OMAP_DSS_ROT_180: + case DRM_MODE_ROTATE_180: vidrot = 0; break; - case OMAP_DSS_ROT_270: + case DRM_MODE_ROTATE_270: vidrot = 3; break; } } else { - switch (rotation) { - case OMAP_DSS_ROT_0: + switch (rotation & DRM_MODE_ROTATE_MASK) { + case DRM_MODE_ROTATE_0: vidrot = 0; break; - case OMAP_DSS_ROT_90: - vidrot = 1; + case DRM_MODE_ROTATE_90: + vidrot = 3; break; - case OMAP_DSS_ROT_180: + case DRM_MODE_ROTATE_180: vidrot = 2; break; - case OMAP_DSS_ROT_270: - vidrot = 3; + case DRM_MODE_ROTATE_270: + vidrot = 1; break; } } - if (rotation == OMAP_DSS_ROT_90 || rotation == OMAP_DSS_ROT_270) + if (drm_rotation_90_or_270(rotation)) row_repeat = true; else row_repeat = false; @@ -1855,8 +1856,7 @@ static void dispc_ovl_set_rotation_attrs(enum omap_plane_id plane, u8 rotation, * NV12 in 1D mode must use ROTATION=1. Otherwise DSS will fetch extra * rows beyond the framebuffer, which may cause OCP error. */ - if (color_mode == OMAP_DSS_COLOR_NV12 && - rotation_type != OMAP_DSS_ROT_TILER) + if (fourcc == DRM_FORMAT_NV12 && rotation_type != OMAP_DSS_ROT_TILER) vidrot = 1; REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), vidrot, 13, 12); @@ -1864,44 +1864,38 @@ static void dispc_ovl_set_rotation_attrs(enum omap_plane_id plane, u8 rotation, REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), row_repeat ? 1 : 0, 18, 18); - if (color_mode == OMAP_DSS_COLOR_NV12) { - bool doublestride = (rotation_type == OMAP_DSS_ROT_TILER) && - (rotation == OMAP_DSS_ROT_0 || - rotation == OMAP_DSS_ROT_180); + if (dss_feat_color_mode_supported(plane, DRM_FORMAT_NV12)) { + bool doublestride = + fourcc == DRM_FORMAT_NV12 && + rotation_type == OMAP_DSS_ROT_TILER && + !drm_rotation_90_or_270(rotation); + /* DOUBLESTRIDE */ REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), doublestride, 22, 22); } - } -static int color_mode_to_bpp(enum omap_color_mode color_mode) +static int color_mode_to_bpp(u32 fourcc) { - switch (color_mode) { - case OMAP_DSS_COLOR_CLUT1: - return 1; - case OMAP_DSS_COLOR_CLUT2: - return 2; - case OMAP_DSS_COLOR_CLUT4: - return 4; - case OMAP_DSS_COLOR_CLUT8: - case OMAP_DSS_COLOR_NV12: + switch (fourcc) { + case DRM_FORMAT_NV12: return 8; - case OMAP_DSS_COLOR_RGB12U: - case OMAP_DSS_COLOR_RGB16: - case OMAP_DSS_COLOR_ARGB16: - case OMAP_DSS_COLOR_YUV2: - case OMAP_DSS_COLOR_UYVY: - case OMAP_DSS_COLOR_RGBA16: - case OMAP_DSS_COLOR_RGBX16: - case OMAP_DSS_COLOR_ARGB16_1555: - case OMAP_DSS_COLOR_XRGB16_1555: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_RGB565: + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_YUYV: + case DRM_FORMAT_UYVY: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_XRGB1555: return 16; - case OMAP_DSS_COLOR_RGB24P: + case DRM_FORMAT_RGB888: return 24; - case OMAP_DSS_COLOR_RGB24U: - case OMAP_DSS_COLOR_ARGB32: - case OMAP_DSS_COLOR_RGBA32: - case OMAP_DSS_COLOR_RGBX32: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_RGBX8888: return 32; default: BUG(); @@ -1922,281 +1916,42 @@ static s32 pixinc(int pixels, u8 ps) return 0; } -static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, - u16 screen_width, - u16 width, u16 height, - enum omap_color_mode color_mode, bool fieldmode, - unsigned int field_offset, - unsigned *offset0, unsigned *offset1, - s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim) +static void calc_offset(u16 screen_width, u16 width, + u32 fourcc, bool fieldmode, + unsigned int field_offset, unsigned *offset0, unsigned *offset1, + s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim, + enum omap_dss_rotation_type rotation_type, u8 rotation) { u8 ps; - /* FIXME CLUT formats */ - switch (color_mode) { - case OMAP_DSS_COLOR_CLUT1: - case OMAP_DSS_COLOR_CLUT2: - case OMAP_DSS_COLOR_CLUT4: - case OMAP_DSS_COLOR_CLUT8: - BUG(); - return; - case OMAP_DSS_COLOR_YUV2: - case OMAP_DSS_COLOR_UYVY: - ps = 4; - break; - default: - ps = color_mode_to_bpp(color_mode) / 8; - break; - } + ps = color_mode_to_bpp(fourcc) / 8; - DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width, - width, height); + DSSDBG("scrw %d, width %d\n", screen_width, width); - /* - * field 0 = even field = bottom field - * field 1 = odd field = top field - */ - switch (rotation + mirror * 4) { - case OMAP_DSS_ROT_0: - case OMAP_DSS_ROT_180: + if (rotation_type == OMAP_DSS_ROT_TILER && + (fourcc == DRM_FORMAT_UYVY || fourcc == DRM_FORMAT_YUYV) && + drm_rotation_90_or_270(rotation)) { /* - * If the pixel format is YUV or UYVY divide the width - * of the image by 2 for 0 and 180 degree rotation. + * HACK: ROW_INC needs to be calculated with TILER units. + * We get such 'screen_width' that multiplying it with the + * YUV422 pixel size gives the correct TILER container width. + * However, 'width' is in pixels and multiplying it with YUV422 + * pixel size gives incorrect result. We thus multiply it here + * with 2 to match the 32 bit TILER unit size. */ - if (color_mode == OMAP_DSS_COLOR_YUV2 || - color_mode == OMAP_DSS_COLOR_UYVY) - width = width >> 1; - case OMAP_DSS_ROT_90: - case OMAP_DSS_ROT_270: - *offset1 = 0; - if (field_offset) - *offset0 = field_offset * screen_width * ps; - else - *offset0 = 0; - - *row_inc = pixinc(1 + - (y_predecim * screen_width - x_predecim * width) + - (fieldmode ? screen_width : 0), ps); - *pix_inc = pixinc(x_predecim, ps); - break; - - case OMAP_DSS_ROT_0 + 4: - case OMAP_DSS_ROT_180 + 4: - /* If the pixel format is YUV or UYVY divide the width - * of the image by 2 for 0 degree and 180 degree - */ - if (color_mode == OMAP_DSS_COLOR_YUV2 || - color_mode == OMAP_DSS_COLOR_UYVY) - width = width >> 1; - case OMAP_DSS_ROT_90 + 4: - case OMAP_DSS_ROT_270 + 4: - *offset1 = 0; - if (field_offset) - *offset0 = field_offset * screen_width * ps; - else - *offset0 = 0; - *row_inc = pixinc(1 - - (y_predecim * screen_width + x_predecim * width) - - (fieldmode ? screen_width : 0), ps); - *pix_inc = pixinc(x_predecim, ps); - break; - - default: - BUG(); - return; - } -} - -static void calc_dma_rotation_offset(u8 rotation, bool mirror, - u16 screen_width, - u16 width, u16 height, - enum omap_color_mode color_mode, bool fieldmode, - unsigned int field_offset, - unsigned *offset0, unsigned *offset1, - s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim) -{ - u8 ps; - u16 fbw, fbh; - - /* FIXME CLUT formats */ - switch (color_mode) { - case OMAP_DSS_COLOR_CLUT1: - case OMAP_DSS_COLOR_CLUT2: - case OMAP_DSS_COLOR_CLUT4: - case OMAP_DSS_COLOR_CLUT8: - BUG(); - return; - default: - ps = color_mode_to_bpp(color_mode) / 8; - break; - } - - DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width, - width, height); - - /* width & height are overlay sizes, convert to fb sizes */ - - if (rotation == OMAP_DSS_ROT_0 || rotation == OMAP_DSS_ROT_180) { - fbw = width; - fbh = height; - } else { - fbw = height; - fbh = width; + width *= 2; } /* * field 0 = even field = bottom field * field 1 = odd field = top field */ - switch (rotation + mirror * 4) { - case OMAP_DSS_ROT_0: - *offset1 = 0; - if (field_offset) - *offset0 = *offset1 + field_offset * screen_width * ps; - else - *offset0 = *offset1; - *row_inc = pixinc(1 + - (y_predecim * screen_width - fbw * x_predecim) + - (fieldmode ? screen_width : 0), ps); - if (color_mode == OMAP_DSS_COLOR_YUV2 || - color_mode == OMAP_DSS_COLOR_UYVY) - *pix_inc = pixinc(x_predecim, 2 * ps); - else - *pix_inc = pixinc(x_predecim, ps); - break; - case OMAP_DSS_ROT_90: - *offset1 = screen_width * (fbh - 1) * ps; - if (field_offset) - *offset0 = *offset1 + field_offset * ps; - else - *offset0 = *offset1; - *row_inc = pixinc(screen_width * (fbh * x_predecim - 1) + - y_predecim + (fieldmode ? 1 : 0), ps); - *pix_inc = pixinc(-x_predecim * screen_width, ps); - break; - case OMAP_DSS_ROT_180: - *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps; - if (field_offset) - *offset0 = *offset1 - field_offset * screen_width * ps; - else - *offset0 = *offset1; - *row_inc = pixinc(-1 - - (y_predecim * screen_width - fbw * x_predecim) - - (fieldmode ? screen_width : 0), ps); - if (color_mode == OMAP_DSS_COLOR_YUV2 || - color_mode == OMAP_DSS_COLOR_UYVY) - *pix_inc = pixinc(-x_predecim, 2 * ps); - else - *pix_inc = pixinc(-x_predecim, ps); - break; - case OMAP_DSS_ROT_270: - *offset1 = (fbw - 1) * ps; - if (field_offset) - *offset0 = *offset1 - field_offset * ps; - else - *offset0 = *offset1; - *row_inc = pixinc(-screen_width * (fbh * x_predecim - 1) - - y_predecim - (fieldmode ? 1 : 0), ps); - *pix_inc = pixinc(x_predecim * screen_width, ps); - break; - - /* mirroring */ - case OMAP_DSS_ROT_0 + 4: - *offset1 = (fbw - 1) * ps; - if (field_offset) - *offset0 = *offset1 + field_offset * screen_width * ps; - else - *offset0 = *offset1; - *row_inc = pixinc(y_predecim * screen_width * 2 - 1 + - (fieldmode ? screen_width : 0), - ps); - if (color_mode == OMAP_DSS_COLOR_YUV2 || - color_mode == OMAP_DSS_COLOR_UYVY) - *pix_inc = pixinc(-x_predecim, 2 * ps); - else - *pix_inc = pixinc(-x_predecim, ps); - break; - - case OMAP_DSS_ROT_90 + 4: - *offset1 = 0; - if (field_offset) - *offset0 = *offset1 + field_offset * ps; - else - *offset0 = *offset1; - *row_inc = pixinc(-screen_width * (fbh * x_predecim - 1) + - y_predecim + (fieldmode ? 1 : 0), - ps); - *pix_inc = pixinc(x_predecim * screen_width, ps); - break; - - case OMAP_DSS_ROT_180 + 4: - *offset1 = screen_width * (fbh - 1) * ps; - if (field_offset) - *offset0 = *offset1 - field_offset * screen_width * ps; - else - *offset0 = *offset1; - *row_inc = pixinc(1 - y_predecim * screen_width * 2 - - (fieldmode ? screen_width : 0), - ps); - if (color_mode == OMAP_DSS_COLOR_YUV2 || - color_mode == OMAP_DSS_COLOR_UYVY) - *pix_inc = pixinc(x_predecim, 2 * ps); - else - *pix_inc = pixinc(x_predecim, ps); - break; - - case OMAP_DSS_ROT_270 + 4: - *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps; - if (field_offset) - *offset0 = *offset1 - field_offset * ps; - else - *offset0 = *offset1; - *row_inc = pixinc(screen_width * (fbh * x_predecim - 1) - - y_predecim - (fieldmode ? 1 : 0), - ps); - *pix_inc = pixinc(-x_predecim * screen_width, ps); - break; - - default: - BUG(); - return; - } -} - -static void calc_tiler_rotation_offset(u16 screen_width, u16 width, - enum omap_color_mode color_mode, bool fieldmode, - unsigned int field_offset, unsigned *offset0, unsigned *offset1, - s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim) -{ - u8 ps; - - switch (color_mode) { - case OMAP_DSS_COLOR_CLUT1: - case OMAP_DSS_COLOR_CLUT2: - case OMAP_DSS_COLOR_CLUT4: - case OMAP_DSS_COLOR_CLUT8: - BUG(); - return; - default: - ps = color_mode_to_bpp(color_mode) / 8; - break; - } - - DSSDBG("scrw %d, width %d\n", screen_width, width); - - /* - * field 0 = even field = bottom field - * field 1 = odd field = top field - */ + *offset0 = field_offset * screen_width * ps; *offset1 = 0; - if (field_offset) - *offset0 = *offset1 + field_offset * screen_width * ps; - else - *offset0 = *offset1; + *row_inc = pixinc(1 + (y_predecim * screen_width - width * x_predecim) + (fieldmode ? screen_width : 0), ps); - if (color_mode == OMAP_DSS_COLOR_YUV2 || - color_mode == OMAP_DSS_COLOR_UYVY) + if (fourcc == DRM_FORMAT_YUYV || fourcc == DRM_FORMAT_UYVY) *pix_inc = pixinc(x_predecim, 2 * ps); else *pix_inc = pixinc(x_predecim, ps); @@ -2263,7 +2018,7 @@ static int check_horiz_timing_omap3(unsigned long pclk, unsigned long lclk, static unsigned long calc_core_clk_five_taps(unsigned long pclk, const struct videomode *vm, u16 width, u16 height, u16 out_width, u16 out_height, - enum omap_color_mode color_mode) + u32 fourcc) { u32 core_clk = 0; u64 tmp; @@ -2293,7 +2048,7 @@ static unsigned long calc_core_clk_five_taps(unsigned long pclk, do_div(tmp, out_width); core_clk = max_t(u32, core_clk, tmp); - if (color_mode == OMAP_DSS_COLOR_RGB24U) + if (fourcc == DRM_FORMAT_XRGB8888) core_clk <<= 1; } @@ -2356,7 +2111,7 @@ static unsigned long calc_core_clk_44xx(unsigned long pclk, u16 width, static int dispc_ovl_calc_scaling_24xx(unsigned long pclk, unsigned long lclk, const struct videomode *vm, u16 width, u16 height, u16 out_width, u16 out_height, - enum omap_color_mode color_mode, bool *five_taps, + u32 fourcc, bool *five_taps, int *x_predecim, int *y_predecim, int *decim_x, int *decim_y, u16 pos_x, unsigned long *core_clk, bool mem_to_mem) { @@ -2402,7 +2157,7 @@ static int dispc_ovl_calc_scaling_24xx(unsigned long pclk, unsigned long lclk, static int dispc_ovl_calc_scaling_34xx(unsigned long pclk, unsigned long lclk, const struct videomode *vm, u16 width, u16 height, u16 out_width, u16 out_height, - enum omap_color_mode color_mode, bool *five_taps, + u32 fourcc, bool *five_taps, int *x_predecim, int *y_predecim, int *decim_x, int *decim_y, u16 pos_x, unsigned long *core_clk, bool mem_to_mem) { @@ -2424,7 +2179,7 @@ again: if (*five_taps) *core_clk = calc_core_clk_five_taps(pclk, vm, in_width, in_height, out_width, - out_height, color_mode); + out_height, fourcc); else *core_clk = dispc.feat->calc_core_clk(pclk, in_width, in_height, out_width, out_height, @@ -2487,7 +2242,7 @@ again: static int dispc_ovl_calc_scaling_44xx(unsigned long pclk, unsigned long lclk, const struct videomode *vm, u16 width, u16 height, u16 out_width, u16 out_height, - enum omap_color_mode color_mode, bool *five_taps, + u32 fourcc, bool *five_taps, int *x_predecim, int *y_predecim, int *decim_x, int *decim_y, u16 pos_x, unsigned long *core_clk, bool mem_to_mem) { @@ -2521,7 +2276,7 @@ static int dispc_ovl_calc_scaling_44xx(unsigned long pclk, unsigned long lclk, return -EINVAL; } - if (*decim_x > 4 && color_mode != OMAP_DSS_COLOR_NV12) { + if (*decim_x > 4 && fourcc != DRM_FORMAT_NV12) { /* * Let's disable all scaling that requires horizontal * decimation with higher factor than 4, until we have @@ -2552,7 +2307,7 @@ static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk, enum omap_overlay_caps caps, const struct videomode *vm, u16 width, u16 height, u16 out_width, u16 out_height, - enum omap_color_mode color_mode, bool *five_taps, + u32 fourcc, bool *five_taps, int *x_predecim, int *y_predecim, u16 pos_x, enum omap_dss_rotation_type rotation_type, bool mem_to_mem) { @@ -2581,16 +2336,6 @@ static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk, 2 : max_decim_limit; } - if (color_mode == OMAP_DSS_COLOR_CLUT1 || - color_mode == OMAP_DSS_COLOR_CLUT2 || - color_mode == OMAP_DSS_COLOR_CLUT4 || - color_mode == OMAP_DSS_COLOR_CLUT8) { - *x_predecim = 1; - *y_predecim = 1; - *five_taps = false; - return 0; - } - decim_x = DIV_ROUND_UP(DIV_ROUND_UP(width, out_width), maxdownscale); decim_y = DIV_ROUND_UP(DIV_ROUND_UP(height, out_height), maxdownscale); @@ -2601,7 +2346,7 @@ static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk, return -EINVAL; ret = dispc.feat->calc_scaling(pclk, lclk, vm, width, height, - out_width, out_height, color_mode, five_taps, + out_width, out_height, fourcc, five_taps, x_predecim, y_predecim, &decim_x, &decim_y, pos_x, &core_clk, mem_to_mem); if (ret) @@ -2637,8 +2382,8 @@ static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk, static int dispc_ovl_setup_common(enum omap_plane_id plane, enum omap_overlay_caps caps, u32 paddr, u32 p_uv_addr, u16 screen_width, int pos_x, int pos_y, u16 width, u16 height, - u16 out_width, u16 out_height, enum omap_color_mode color_mode, - u8 rotation, bool mirror, u8 zorder, u8 pre_mult_alpha, + u16 out_width, u16 out_height, u32 fourcc, + u8 rotation, u8 zorder, u8 pre_mult_alpha, u8 global_alpha, enum omap_dss_rotation_type rotation_type, bool replication, const struct videomode *vm, bool mem_to_mem) @@ -2661,19 +2406,9 @@ static int dispc_ovl_setup_common(enum omap_plane_id plane, if (paddr == 0 && rotation_type != OMAP_DSS_ROT_TILER) return -EINVAL; - switch (color_mode) { - case OMAP_DSS_COLOR_YUV2: - case OMAP_DSS_COLOR_UYVY: - case OMAP_DSS_COLOR_NV12: - if (in_width & 1) { - DSSERR("input width %d is not even for YUV format\n", - in_width); - return -EINVAL; - } - break; - - default: - break; + if (format_is_yuv(fourcc) && (in_width & 1)) { + DSSERR("input width %d is not even for YUV format\n", in_width); + return -EINVAL; } out_width = out_width == 0 ? width : out_width; @@ -2693,11 +2428,11 @@ static int dispc_ovl_setup_common(enum omap_plane_id plane, out_height); } - if (!dss_feat_color_mode_supported(plane, color_mode)) + if (!dss_feat_color_mode_supported(plane, fourcc)) return -EINVAL; r = dispc_ovl_calc_scaling(pclk, lclk, caps, vm, in_width, - in_height, out_width, out_height, color_mode, + in_height, out_width, out_height, fourcc, &five_taps, &x_predecim, &y_predecim, pos_x, rotation_type, mem_to_mem); if (r) @@ -2710,26 +2445,15 @@ static int dispc_ovl_setup_common(enum omap_plane_id plane, DSSDBG("predecimation %d x %x, new input size %d x %d\n", x_predecim, y_predecim, in_width, in_height); - switch (color_mode) { - case OMAP_DSS_COLOR_YUV2: - case OMAP_DSS_COLOR_UYVY: - case OMAP_DSS_COLOR_NV12: - if (in_width & 1) { - DSSDBG("predecimated input width is not even for YUV format\n"); - DSSDBG("adjusting input width %d -> %d\n", - in_width, in_width & ~1); + if (format_is_yuv(fourcc) && (in_width & 1)) { + DSSDBG("predecimated input width is not even for YUV format\n"); + DSSDBG("adjusting input width %d -> %d\n", + in_width, in_width & ~1); - in_width &= ~1; - } - break; - - default: - break; + in_width &= ~1; } - if (color_mode == OMAP_DSS_COLOR_YUV2 || - color_mode == OMAP_DSS_COLOR_UYVY || - color_mode == OMAP_DSS_COLOR_NV12) + if (format_is_yuv(fourcc)) cconv = 1; if (ilace && !fieldmode) { @@ -2763,28 +2487,16 @@ static int dispc_ovl_setup_common(enum omap_plane_id plane, frame_height = height; } - if (rotation_type == OMAP_DSS_ROT_TILER) - calc_tiler_rotation_offset(screen_width, frame_width, - color_mode, fieldmode, field_offset, - &offset0, &offset1, &row_inc, &pix_inc, - x_predecim, y_predecim); - else if (rotation_type == OMAP_DSS_ROT_DMA) - calc_dma_rotation_offset(rotation, mirror, screen_width, - frame_width, frame_height, - color_mode, fieldmode, field_offset, - &offset0, &offset1, &row_inc, &pix_inc, - x_predecim, y_predecim); - else - calc_vrfb_rotation_offset(rotation, mirror, - screen_width, frame_width, frame_height, - color_mode, fieldmode, field_offset, - &offset0, &offset1, &row_inc, &pix_inc, - x_predecim, y_predecim); + calc_offset(screen_width, frame_width, + fourcc, fieldmode, field_offset, + &offset0, &offset1, &row_inc, &pix_inc, + x_predecim, y_predecim, + rotation_type, rotation); DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n", offset0, offset1, row_inc, pix_inc); - dispc_ovl_set_color_mode(plane, color_mode); + dispc_ovl_set_color_mode(plane, fourcc); dispc_ovl_configure_burst_type(plane, rotation_type); @@ -2794,7 +2506,7 @@ static int dispc_ovl_setup_common(enum omap_plane_id plane, dispc_ovl_set_ba0(plane, paddr + offset0); dispc_ovl_set_ba1(plane, paddr + offset1); - if (OMAP_DSS_COLOR_NV12 == color_mode) { + if (fourcc == DRM_FORMAT_NV12) { dispc_ovl_set_ba0_uv(plane, p_uv_addr + offset0); dispc_ovl_set_ba1_uv(plane, p_uv_addr + offset1); } @@ -2815,13 +2527,12 @@ static int dispc_ovl_setup_common(enum omap_plane_id plane, if (caps & OMAP_DSS_OVL_CAP_SCALE) { dispc_ovl_set_scaling(plane, in_width, in_height, out_width, out_height, ilace, five_taps, fieldmode, - color_mode, rotation); + fourcc, rotation); dispc_ovl_set_output_size(plane, out_width, out_height); dispc_ovl_set_vid_color_conv(plane, cconv); } - dispc_ovl_set_rotation_attrs(plane, rotation, rotation_type, mirror, - color_mode); + dispc_ovl_set_rotation_attrs(plane, rotation, rotation_type, fourcc); dispc_ovl_set_zorder(plane, caps, zorder); dispc_ovl_set_pre_mult_alpha(plane, caps, pre_mult_alpha); @@ -2834,25 +2545,25 @@ static int dispc_ovl_setup_common(enum omap_plane_id plane, static int dispc_ovl_setup(enum omap_plane_id plane, const struct omap_overlay_info *oi, - const struct videomode *vm, bool mem_to_mem) + const struct videomode *vm, bool mem_to_mem, + enum omap_channel channel) { int r; enum omap_overlay_caps caps = dss_feat_get_overlay_caps(plane); - enum omap_channel channel; const bool replication = true; - channel = dispc_ovl_get_channel_out(plane); - DSSDBG("dispc_ovl_setup %d, pa %pad, pa_uv %pad, sw %d, %d,%d, %dx%d ->" - " %dx%d, cmode %x, rot %d, mir %d, chan %d repl %d\n", + " %dx%d, cmode %x, rot %d, chan %d repl %d\n", plane, &oi->paddr, &oi->p_uv_addr, oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height, oi->out_width, oi->out_height, - oi->color_mode, oi->rotation, oi->mirror, channel, replication); + oi->fourcc, oi->rotation, channel, replication); + + dispc_ovl_set_channel_out(plane, channel); r = dispc_ovl_setup_common(plane, caps, oi->paddr, oi->p_uv_addr, oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height, - oi->out_width, oi->out_height, oi->color_mode, oi->rotation, - oi->mirror, oi->zorder, oi->pre_mult_alpha, oi->global_alpha, + oi->out_width, oi->out_height, oi->fourcc, oi->rotation, + oi->zorder, oi->pre_mult_alpha, oi->global_alpha, oi->rotation_type, replication, vm, mem_to_mem); return r; @@ -2874,25 +2585,24 @@ int dispc_wb_setup(const struct omap_dss_writeback_info *wi, OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA; DSSDBG("dispc_wb_setup, pa %x, pa_uv %x, %d,%d -> %dx%d, cmode %x, " - "rot %d, mir %d\n", wi->paddr, wi->p_uv_addr, in_width, - in_height, wi->width, wi->height, wi->color_mode, wi->rotation, - wi->mirror); + "rot %d\n", wi->paddr, wi->p_uv_addr, in_width, + in_height, wi->width, wi->height, wi->fourcc, wi->rotation); r = dispc_ovl_setup_common(plane, caps, wi->paddr, wi->p_uv_addr, wi->buf_width, pos_x, pos_y, in_width, in_height, wi->width, - wi->height, wi->color_mode, wi->rotation, wi->mirror, zorder, + wi->height, wi->fourcc, wi->rotation, zorder, wi->pre_mult_alpha, global_alpha, wi->rotation_type, replication, vm, mem_to_mem); - switch (wi->color_mode) { - case OMAP_DSS_COLOR_RGB16: - case OMAP_DSS_COLOR_RGB24P: - case OMAP_DSS_COLOR_ARGB16: - case OMAP_DSS_COLOR_RGBA16: - case OMAP_DSS_COLOR_RGB12U: - case OMAP_DSS_COLOR_ARGB16_1555: - case OMAP_DSS_COLOR_XRGB16_1555: - case OMAP_DSS_COLOR_RGBX16: + switch (wi->fourcc) { + case DRM_FORMAT_RGB565: + case DRM_FORMAT_RGB888: + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_XRGB4444: truncation = true; break; default: @@ -2935,11 +2645,6 @@ static int dispc_ovl_enable(enum omap_plane_id plane, bool enable) return 0; } -static bool dispc_ovl_enabled(enum omap_plane_id plane) -{ - return REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0); -} - static enum omap_dss_output_id dispc_mgr_get_supported_outputs(enum omap_channel channel) { return dss_feat_get_supported_outputs(channel); @@ -3787,11 +3492,6 @@ static void dispc_clear_irqstatus(u32 mask) dispc_write_reg(DISPC_IRQSTATUS, mask); } -static u32 dispc_read_irqenable(void) -{ - return dispc_read_reg(DISPC_IRQENABLE); -} - static void dispc_write_irqenable(u32 mask) { u32 old_mask = dispc_read_reg(DISPC_IRQENABLE); @@ -3800,6 +3500,9 @@ static void dispc_write_irqenable(u32 mask) dispc_clear_irqstatus((mask ^ old_mask) & mask); dispc_write_reg(DISPC_IRQENABLE, mask); + + /* flush posted write */ + dispc_read_reg(DISPC_IRQENABLE); } void dispc_enable_sidle(void) @@ -4225,10 +3928,9 @@ static const struct dispc_errata_i734_data { .ovli = { .screen_width = 1, .width = 1, .height = 1, - .color_mode = OMAP_DSS_COLOR_RGB24U, - .rotation = OMAP_DSS_ROT_0, - .rotation_type = OMAP_DSS_ROT_DMA, - .mirror = 0, + .fourcc = DRM_FORMAT_XRGB8888, + .rotation = DRM_MODE_ROTATE_0, + .rotation_type = OMAP_DSS_ROT_NONE, .pos_x = 0, .pos_y = 0, .out_width = 0, .out_height = 0, .global_alpha = 0xff, @@ -4266,7 +3968,7 @@ static int dispc_errata_i734_wa_init(void) return 0; i734_buf.size = i734.ovli.width * i734.ovli.height * - color_mode_to_bpp(i734.ovli.color_mode) / 8; + color_mode_to_bpp(i734.ovli.fourcc) / 8; i734_buf.vaddr = dma_alloc_writecombine(&dispc.pdev->dev, i734_buf.size, &i734_buf.paddr, GFP_KERNEL); @@ -4309,8 +4011,8 @@ static void dispc_errata_i734_wa(void) REG_FLD_MOD(DISPC_CONFIG, 0x1f, 8, 4); /* Setup and enable GFX plane */ - dispc_ovl_set_channel_out(OMAP_DSS_GFX, OMAP_DSS_CHANNEL_LCD); - dispc_ovl_setup(OMAP_DSS_GFX, &ovli, &i734.vm, false); + dispc_ovl_setup(OMAP_DSS_GFX, &ovli, &i734.vm, false, + OMAP_DSS_CHANNEL_LCD); dispc_ovl_enable(OMAP_DSS_GFX, true); /* Set up and enable display manager for LCD1 */ @@ -4350,7 +4052,6 @@ static void dispc_errata_i734_wa(void) static const struct dispc_ops dispc_ops = { .read_irqstatus = dispc_read_irqstatus, .clear_irqstatus = dispc_clear_irqstatus, - .read_irqenable = dispc_read_irqenable, .write_irqenable = dispc_write_irqenable, .request_irq = dispc_request_irq, @@ -4377,8 +4078,6 @@ static const struct dispc_ops dispc_ops = { .mgr_set_gamma = dispc_mgr_set_gamma, .ovl_enable = dispc_ovl_enable, - .ovl_enabled = dispc_ovl_enabled, - .ovl_set_channel_out = dispc_ovl_set_channel_out, .ovl_setup = dispc_ovl_setup, .ovl_get_color_modes = dispc_ovl_get_color_modes, }; @@ -4405,17 +4104,9 @@ static int dispc_bind(struct device *dev, struct device *master, void *data) return r; dispc_mem = platform_get_resource(dispc.pdev, IORESOURCE_MEM, 0); - if (!dispc_mem) { - DSSERR("can't get IORESOURCE_MEM DISPC\n"); - return -EINVAL; - } - - dispc.base = devm_ioremap(&pdev->dev, dispc_mem->start, - resource_size(dispc_mem)); - if (!dispc.base) { - DSSERR("can't ioremap DISPC\n"); - return -ENOMEM; - } + dispc.base = devm_ioremap_resource(&pdev->dev, dispc_mem); + if (IS_ERR(dispc.base)) + return PTR_ERR(dispc.base); dispc.irq = platform_get_irq(dispc.pdev, 0); if (dispc.irq < 0) { diff --git a/drivers/gpu/drm/omapdrm/dss/display.c b/drivers/gpu/drm/omapdrm/dss/display.c index 26cb59be045e..42279933790e 100644 --- a/drivers/gpu/drm/omapdrm/dss/display.c +++ b/drivers/gpu/drm/omapdrm/dss/display.c @@ -30,45 +30,6 @@ #include "omapdss.h" -void omapdss_default_get_resolution(struct omap_dss_device *dssdev, - u16 *xres, u16 *yres) -{ - *xres = dssdev->panel.vm.hactive; - *yres = dssdev->panel.vm.vactive; -} -EXPORT_SYMBOL(omapdss_default_get_resolution); - -int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) -{ - switch (dssdev->type) { - case OMAP_DISPLAY_TYPE_DPI: - if (dssdev->phy.dpi.data_lines == 24) - return 24; - else - return 16; - - case OMAP_DISPLAY_TYPE_DBI: - if (dssdev->ctrl.pixel_size == 24) - return 24; - else - return 16; - case OMAP_DISPLAY_TYPE_DSI: - if (dssdev->panel.dsi_pix_fmt == OMAP_DSS_DSI_FMT_RGB565) - return 16; - else - return 24; - case OMAP_DISPLAY_TYPE_VENC: - case OMAP_DISPLAY_TYPE_SDI: - case OMAP_DISPLAY_TYPE_HDMI: - case OMAP_DISPLAY_TYPE_DVI: - return 24; - default: - BUG(); - return 0; - } -} -EXPORT_SYMBOL(omapdss_default_get_recommended_bpp); - void omapdss_default_get_timings(struct omap_dss_device *dssdev, struct videomode *vm) { @@ -87,34 +48,21 @@ int omapdss_register_display(struct omap_dss_device *dssdev) int id; /* - * Note: this presumes all the displays are either using DT or non-DT, - * which normally should be the case. This also presumes that all - * displays either have an DT alias, or none has. + * Note: this presumes that all displays either have an DT alias, or + * none has. */ - - if (dssdev->dev->of_node) { - id = of_alias_get_id(dssdev->dev->of_node, "display"); - - if (id < 0) - id = disp_num_counter++; - } else { + id = of_alias_get_id(dssdev->dev->of_node, "display"); + if (id < 0) id = disp_num_counter++; - } snprintf(dssdev->alias, sizeof(dssdev->alias), "display%d", id); /* Use 'label' property for name, if it exists */ - if (dssdev->dev->of_node) - of_property_read_string(dssdev->dev->of_node, "label", - &dssdev->name); + of_property_read_string(dssdev->dev->of_node, "label", &dssdev->name); if (dssdev->name == NULL) dssdev->name = dssdev->alias; - if (drv && drv->get_resolution == NULL) - drv->get_resolution = omapdss_default_get_resolution; - if (drv && drv->get_recommended_bpp == NULL) - drv->get_recommended_bpp = omapdss_default_get_recommended_bpp; if (drv && drv->get_timings == NULL) drv->get_timings = omapdss_default_get_timings; diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c index 8a730a7afe76..86dbb65a6c28 100644 --- a/drivers/gpu/drm/omapdrm/dss/dpi.c +++ b/drivers/gpu/drm/omapdrm/dss/dpi.c @@ -32,7 +32,6 @@ #include <linux/string.h> #include <linux/of.h> #include <linux/clk.h> -#include <linux/component.h> #include "omapdss.h" #include "dss.h" @@ -61,12 +60,6 @@ static struct dpi_data *dpi_get_data_from_dssdev(struct omap_dss_device *dssdev) return container_of(dssdev, struct dpi_data, output); } -/* only used in non-DT mode */ -static struct dpi_data *dpi_get_data_from_pdev(struct platform_device *pdev) -{ - return dev_get_drvdata(&pdev->dev); -} - static enum dss_clk_source dpi_get_clk_src_dra7xx(enum omap_channel channel) { /* @@ -567,17 +560,6 @@ static int dpi_check_timings(struct omap_dss_device *dssdev, return 0; } -static void dpi_set_data_lines(struct omap_dss_device *dssdev, int data_lines) -{ - struct dpi_data *dpi = dpi_get_data_from_dssdev(dssdev); - - mutex_lock(&dpi->lock); - - dpi->data_lines = data_lines; - - mutex_unlock(&dpi->lock); -} - static int dpi_verify_pll(struct dss_pll *pll) { int r; @@ -732,34 +714,8 @@ static const struct omapdss_dpi_ops dpi_ops = { .check_timings = dpi_check_timings, .set_timings = dpi_set_timings, .get_timings = dpi_get_timings, - - .set_data_lines = dpi_set_data_lines, }; -static void dpi_init_output(struct platform_device *pdev) -{ - struct dpi_data *dpi = dpi_get_data_from_pdev(pdev); - struct omap_dss_device *out = &dpi->output; - - out->dev = &pdev->dev; - out->id = OMAP_DSS_OUTPUT_DPI; - out->output_type = OMAP_DISPLAY_TYPE_DPI; - out->name = "dpi.0"; - out->dispc_channel = dpi_get_channel(0); - out->ops.dpi = &dpi_ops; - out->owner = THIS_MODULE; - - omapdss_register_output(out); -} - -static void dpi_uninit_output(struct platform_device *pdev) -{ - struct dpi_data *dpi = dpi_get_data_from_pdev(pdev); - struct omap_dss_device *out = &dpi->output; - - omapdss_unregister_output(out); -} - static void dpi_init_output_port(struct platform_device *pdev, struct device_node *port) { @@ -804,68 +760,6 @@ static void dpi_uninit_output_port(struct device_node *port) omapdss_unregister_output(out); } -static int dpi_bind(struct device *dev, struct device *master, void *data) -{ - struct platform_device *pdev = to_platform_device(dev); - struct dpi_data *dpi; - - dpi = devm_kzalloc(&pdev->dev, sizeof(*dpi), GFP_KERNEL); - if (!dpi) - return -ENOMEM; - - dpi->pdev = pdev; - - dev_set_drvdata(&pdev->dev, dpi); - - mutex_init(&dpi->lock); - - dpi_init_output(pdev); - - return 0; -} - -static void dpi_unbind(struct device *dev, struct device *master, void *data) -{ - struct platform_device *pdev = to_platform_device(dev); - - dpi_uninit_output(pdev); -} - -static const struct component_ops dpi_component_ops = { - .bind = dpi_bind, - .unbind = dpi_unbind, -}; - -static int dpi_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &dpi_component_ops); -} - -static int dpi_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &dpi_component_ops); - return 0; -} - -static struct platform_driver omap_dpi_driver = { - .probe = dpi_probe, - .remove = dpi_remove, - .driver = { - .name = "omapdss_dpi", - .suppress_bind_attrs = true, - }, -}; - -int __init dpi_init_platform_driver(void) -{ - return platform_driver_register(&omap_dpi_driver); -} - -void dpi_uninit_platform_driver(void) -{ - platform_driver_unregister(&omap_dpi_driver); -} - int dpi_init_port(struct platform_device *pdev, struct device_node *port) { struct dpi_data *dpi; diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index 910754bf8cf9..835f49004bc3 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -5276,12 +5276,12 @@ static int dsi_init_pll_data(struct platform_device *dsidev) static int dsi_bind(struct device *dev, struct device *master, void *data) { struct platform_device *dsidev = to_platform_device(dev); + const struct dsi_module_id_data *d; u32 rev; int r, i; struct dsi_data *dsi; struct resource *dsi_mem; struct resource *res; - struct resource temp_res; dsi = devm_kzalloc(&dsidev->dev, sizeof(*dsi), GFP_KERNEL); if (!dsi) @@ -5311,67 +5311,20 @@ static int dsi_bind(struct device *dev, struct device *master, void *data) dsi->te_timer.data = 0; #endif - res = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "proto"); - if (!res) { - res = platform_get_resource(dsidev, IORESOURCE_MEM, 0); - if (!res) { - DSSERR("can't get IORESOURCE_MEM DSI\n"); - return -EINVAL; - } - - temp_res.start = res->start; - temp_res.end = temp_res.start + DSI_PROTO_SZ - 1; - res = &temp_res; - } - - dsi_mem = res; - - dsi->proto_base = devm_ioremap(&dsidev->dev, res->start, - resource_size(res)); - if (!dsi->proto_base) { - DSSERR("can't ioremap DSI protocol engine\n"); - return -ENOMEM; - } + dsi_mem = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "proto"); + dsi->proto_base = devm_ioremap_resource(&dsidev->dev, dsi_mem); + if (IS_ERR(dsi->proto_base)) + return PTR_ERR(dsi->proto_base); res = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "phy"); - if (!res) { - res = platform_get_resource(dsidev, IORESOURCE_MEM, 0); - if (!res) { - DSSERR("can't get IORESOURCE_MEM DSI\n"); - return -EINVAL; - } - - temp_res.start = res->start + DSI_PHY_OFFSET; - temp_res.end = temp_res.start + DSI_PHY_SZ - 1; - res = &temp_res; - } - - dsi->phy_base = devm_ioremap(&dsidev->dev, res->start, - resource_size(res)); - if (!dsi->phy_base) { - DSSERR("can't ioremap DSI PHY\n"); - return -ENOMEM; - } + dsi->phy_base = devm_ioremap_resource(&dsidev->dev, res); + if (IS_ERR(dsi->phy_base)) + return PTR_ERR(dsi->phy_base); res = platform_get_resource_byname(dsidev, IORESOURCE_MEM, "pll"); - if (!res) { - res = platform_get_resource(dsidev, IORESOURCE_MEM, 0); - if (!res) { - DSSERR("can't get IORESOURCE_MEM DSI\n"); - return -EINVAL; - } - - temp_res.start = res->start + DSI_PLL_OFFSET; - temp_res.end = temp_res.start + DSI_PLL_SZ - 1; - res = &temp_res; - } - - dsi->pll_base = devm_ioremap(&dsidev->dev, res->start, - resource_size(res)); - if (!dsi->pll_base) { - DSSERR("can't ioremap DSI PLL\n"); - return -ENOMEM; - } + dsi->pll_base = devm_ioremap_resource(&dsidev->dev, res); + if (IS_ERR(dsi->pll_base)) + return PTR_ERR(dsi->pll_base); dsi->irq = platform_get_irq(dsi->pdev, 0); if (dsi->irq < 0) { @@ -5386,31 +5339,17 @@ static int dsi_bind(struct device *dev, struct device *master, void *data) return r; } - if (dsidev->dev.of_node) { - const struct of_device_id *match; - const struct dsi_module_id_data *d; - - match = of_match_node(dsi_of_match, dsidev->dev.of_node); - if (!match) { - DSSERR("unsupported DSI module\n"); - return -ENODEV; - } - - d = match->data; - - while (d->address != 0 && d->address != dsi_mem->start) - d++; - - if (d->address == 0) { - DSSERR("unsupported DSI module\n"); - return -ENODEV; - } + d = of_match_node(dsi_of_match, dsidev->dev.of_node)->data; + while (d->address != 0 && d->address != dsi_mem->start) + d++; - dsi->module_id = d->id; - } else { - dsi->module_id = dsidev->id; + if (d->address == 0) { + DSSERR("unsupported DSI module\n"); + return -ENODEV; } + dsi->module_id = d->id; + /* DSI VCs initialization */ for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) { dsi->vc[i].source = DSI_VC_SOURCE_L4; @@ -5446,19 +5385,16 @@ static int dsi_bind(struct device *dev, struct device *master, void *data) dsi_init_output(dsidev); - if (dsidev->dev.of_node) { - r = dsi_probe_of(dsidev); - if (r) { - DSSERR("Invalid DSI DT data\n"); - goto err_probe_of; - } - - r = of_platform_populate(dsidev->dev.of_node, NULL, NULL, - &dsidev->dev); - if (r) - DSSERR("Failed to populate DSI child devices: %d\n", r); + r = dsi_probe_of(dsidev); + if (r) { + DSSERR("Invalid DSI DT data\n"); + goto err_probe_of; } + r = of_platform_populate(dsidev->dev.of_node, NULL, NULL, &dsidev->dev); + if (r) + DSSERR("Failed to populate DSI child devices: %d\n", r); + dsi_runtime_put(dsidev); if (dsi->module_id == 0) diff --git a/drivers/gpu/drm/omapdrm/dss/dss.c b/drivers/gpu/drm/omapdrm/dss/dss.c index fa99ec72d832..99e22ca972c7 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.c +++ b/drivers/gpu/drm/omapdrm/dss/dss.c @@ -1158,17 +1158,9 @@ static int dss_bind(struct device *dev) return r; dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0); - if (!dss_mem) { - DSSERR("can't get IORESOURCE_MEM DSS\n"); - return -EINVAL; - } - - dss.base = devm_ioremap(&pdev->dev, dss_mem->start, - resource_size(dss_mem)); - if (!dss.base) { - DSSERR("can't ioremap DSS\n"); - return -ENOMEM; - } + dss.base = devm_ioremap_resource(&pdev->dev, dss_mem); + if (IS_ERR(dss.base)) + return PTR_ERR(dss.base); r = dss_get_clocks(); if (r) diff --git a/drivers/gpu/drm/omapdrm/dss/dss.h b/drivers/gpu/drm/omapdrm/dss/dss.h index 5dd29c98143a..8dbf35f3ab23 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.h +++ b/drivers/gpu/drm/omapdrm/dss/dss.h @@ -219,7 +219,6 @@ struct seq_file; struct platform_device; /* core */ -struct platform_device *dss_get_core_pdev(void); int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask); void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask); int dss_set_min_bus_tput(struct device *dev, unsigned long tput); @@ -281,9 +280,6 @@ bool dss_div_calc(unsigned long pck, unsigned long fck_min, dss_div_calc_func func, void *data); /* SDI */ -int sdi_init_platform_driver(void) __init; -void sdi_uninit_platform_driver(void); - #ifdef CONFIG_OMAP2_DSS_SDI int sdi_init_port(struct platform_device *pdev, struct device_node *port); void sdi_uninit_port(struct device_node *port); @@ -315,9 +311,6 @@ void dsi_irq_handler(void); #endif /* DPI */ -int dpi_init_platform_driver(void) __init; -void dpi_uninit_platform_driver(void); - #ifdef CONFIG_OMAP2_DSS_DPI int dpi_init_port(struct platform_device *pdev, struct device_node *port); void dpi_uninit_port(struct device_node *port); @@ -389,10 +382,6 @@ void hdmi4_uninit_platform_driver(void); int hdmi5_init_platform_driver(void) __init; void hdmi5_uninit_platform_driver(void); -/* RFBI */ -int rfbi_init_platform_driver(void) __init; -void rfbi_uninit_platform_driver(void); - #ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS static inline void dss_collect_irq_stats(u32 irqstatus, unsigned *irq_arr) diff --git a/drivers/gpu/drm/omapdrm/dss/dss_features.c b/drivers/gpu/drm/omapdrm/dss/dss_features.c index 80c6440a0e08..0e599710dd95 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss_features.c +++ b/drivers/gpu/drm/omapdrm/dss/dss_features.c @@ -22,6 +22,7 @@ #include <linux/types.h> #include <linux/err.h> #include <linux/slab.h> +#include <drm/drm_fourcc.h> #include "omapdss.h" #include "dss.h" @@ -47,12 +48,10 @@ struct omap_dss_features { const int num_ovls; const enum omap_display_type *supported_displays; const enum omap_dss_output_id *supported_outputs; - const enum omap_color_mode *supported_color_modes; + const u32 **supported_color_modes; const enum omap_overlay_caps *overlay_caps; const struct dss_param_range *dss_params; - const enum omap_dss_rotation_type supported_rotation_types; - const u32 buffer_size_unit; const u32 burst_size_unit; }; @@ -231,96 +230,104 @@ static const enum omap_dss_output_id omap5_dss_supported_outputs[] = { OMAP_DSS_OUTPUT_DSI2, }; -static const enum omap_color_mode omap2_dss_supported_color_modes[] = { +#define COLOR_ARRAY(arr...) (const u32[]) { arr, 0 } + +static const u32 *omap2_dss_supported_color_modes[] = { + /* OMAP_DSS_GFX */ - OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 | - OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 | - OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 | - OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P, + COLOR_ARRAY( + DRM_FORMAT_RGBX4444, DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB888), /* OMAP_DSS_VIDEO1 */ - OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | - OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 | - OMAP_DSS_COLOR_UYVY, + COLOR_ARRAY( + DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_YUYV, + DRM_FORMAT_UYVY), /* OMAP_DSS_VIDEO2 */ - OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | - OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 | - OMAP_DSS_COLOR_UYVY, + COLOR_ARRAY( + DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_YUYV, + DRM_FORMAT_UYVY), }; -static const enum omap_color_mode omap3_dss_supported_color_modes[] = { +static const u32 *omap3_dss_supported_color_modes[] = { /* OMAP_DSS_GFX */ - OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 | - OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 | - OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 | - OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | - OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 | - OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32, + COLOR_ARRAY( + DRM_FORMAT_RGBX4444, DRM_FORMAT_ARGB4444, + DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGBA8888, DRM_FORMAT_RGBX8888), /* OMAP_DSS_VIDEO1 */ - OMAP_DSS_COLOR_RGB24U | OMAP_DSS_COLOR_RGB24P | - OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_RGB16 | - OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY, + COLOR_ARRAY( + DRM_FORMAT_XRGB8888, DRM_FORMAT_RGB888, + DRM_FORMAT_RGBX4444, DRM_FORMAT_RGB565, + DRM_FORMAT_YUYV, DRM_FORMAT_UYVY), /* OMAP_DSS_VIDEO2 */ - OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 | - OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | - OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_YUV2 | - OMAP_DSS_COLOR_UYVY | OMAP_DSS_COLOR_ARGB32 | - OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32, + COLOR_ARRAY( + DRM_FORMAT_RGBX4444, DRM_FORMAT_ARGB4444, + DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_YUYV, + DRM_FORMAT_UYVY, DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGBA8888, DRM_FORMAT_RGBX8888), }; -static const enum omap_color_mode omap4_dss_supported_color_modes[] = { +static const u32 *omap4_dss_supported_color_modes[] = { /* OMAP_DSS_GFX */ - OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 | - OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 | - OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 | - OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | - OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 | - OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32 | - OMAP_DSS_COLOR_ARGB16_1555 | OMAP_DSS_COLOR_RGBX16 | - OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_XRGB16_1555, + COLOR_ARRAY( + DRM_FORMAT_RGBX4444, DRM_FORMAT_ARGB4444, + DRM_FORMAT_RGB565, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGBA8888, DRM_FORMAT_RGBX8888, + DRM_FORMAT_ARGB1555, DRM_FORMAT_XRGB4444, + DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB1555), /* OMAP_DSS_VIDEO1 */ - OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U | - OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 | - OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 | - OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U | - OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY | - OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 | - OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 | - OMAP_DSS_COLOR_RGBX32, + COLOR_ARRAY( + DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444, + DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12, + DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_UYVY, + DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555, + DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444, + DRM_FORMAT_RGBX8888), /* OMAP_DSS_VIDEO2 */ - OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U | - OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 | - OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 | - OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U | - OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY | - OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 | - OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 | - OMAP_DSS_COLOR_RGBX32, + COLOR_ARRAY( + DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444, + DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12, + DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_UYVY, + DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555, + DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444, + DRM_FORMAT_RGBX8888), /* OMAP_DSS_VIDEO3 */ - OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U | - OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 | - OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 | - OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U | - OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY | - OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 | - OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 | - OMAP_DSS_COLOR_RGBX32, + COLOR_ARRAY( + DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444, + DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12, + DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_UYVY, + DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555, + DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444, + DRM_FORMAT_RGBX8888), /* OMAP_DSS_WB */ - OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U | - OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_ARGB16_1555 | - OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_NV12 | - OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_RGB24U | - OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_UYVY | - OMAP_DSS_COLOR_ARGB16 | OMAP_DSS_COLOR_XRGB16_1555 | - OMAP_DSS_COLOR_ARGB32 | OMAP_DSS_COLOR_RGBX16 | - OMAP_DSS_COLOR_RGBX32, + COLOR_ARRAY( + DRM_FORMAT_RGB565, DRM_FORMAT_RGBX4444, + DRM_FORMAT_YUYV, DRM_FORMAT_ARGB1555, + DRM_FORMAT_RGBA8888, DRM_FORMAT_NV12, + DRM_FORMAT_RGBA4444, DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, DRM_FORMAT_UYVY, + DRM_FORMAT_ARGB4444, DRM_FORMAT_XRGB1555, + DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB4444, + DRM_FORMAT_RGBX8888), }; static const enum omap_overlay_caps omap2_dss_overlay_caps[] = { @@ -602,7 +609,6 @@ static const struct omap_dss_features omap2_dss_features = { .supported_color_modes = omap2_dss_supported_color_modes, .overlay_caps = omap2_dss_overlay_caps, .dss_params = omap2_dss_param_range, - .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB, .buffer_size_unit = 1, .burst_size_unit = 8, }; @@ -622,7 +628,6 @@ static const struct omap_dss_features omap3430_dss_features = { .supported_color_modes = omap3_dss_supported_color_modes, .overlay_caps = omap3430_dss_overlay_caps, .dss_params = omap3_dss_param_range, - .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB, .buffer_size_unit = 1, .burst_size_unit = 8, }; @@ -645,7 +650,6 @@ static const struct omap_dss_features am35xx_dss_features = { .supported_color_modes = omap3_dss_supported_color_modes, .overlay_caps = omap3430_dss_overlay_caps, .dss_params = omap3_dss_param_range, - .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB, .buffer_size_unit = 1, .burst_size_unit = 8, }; @@ -664,7 +668,6 @@ static const struct omap_dss_features am43xx_dss_features = { .supported_color_modes = omap3_dss_supported_color_modes, .overlay_caps = omap3430_dss_overlay_caps, .dss_params = am43xx_dss_param_range, - .supported_rotation_types = OMAP_DSS_ROT_DMA, .buffer_size_unit = 1, .burst_size_unit = 8, }; @@ -683,7 +686,6 @@ static const struct omap_dss_features omap3630_dss_features = { .supported_color_modes = omap3_dss_supported_color_modes, .overlay_caps = omap3630_dss_overlay_caps, .dss_params = omap3_dss_param_range, - .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_VRFB, .buffer_size_unit = 1, .burst_size_unit = 8, }; @@ -704,7 +706,6 @@ static const struct omap_dss_features omap4430_es1_0_dss_features = { .supported_color_modes = omap4_dss_supported_color_modes, .overlay_caps = omap4_dss_overlay_caps, .dss_params = omap4_dss_param_range, - .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER, .buffer_size_unit = 16, .burst_size_unit = 16, }; @@ -724,7 +725,6 @@ static const struct omap_dss_features omap4430_es2_0_1_2_dss_features = { .supported_color_modes = omap4_dss_supported_color_modes, .overlay_caps = omap4_dss_overlay_caps, .dss_params = omap4_dss_param_range, - .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER, .buffer_size_unit = 16, .burst_size_unit = 16, }; @@ -744,7 +744,6 @@ static const struct omap_dss_features omap4_dss_features = { .supported_color_modes = omap4_dss_supported_color_modes, .overlay_caps = omap4_dss_overlay_caps, .dss_params = omap4_dss_param_range, - .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER, .buffer_size_unit = 16, .burst_size_unit = 16, }; @@ -764,7 +763,6 @@ static const struct omap_dss_features omap5_dss_features = { .supported_color_modes = omap4_dss_supported_color_modes, .overlay_caps = omap4_dss_overlay_caps, .dss_params = omap5_dss_param_range, - .supported_rotation_types = OMAP_DSS_ROT_DMA | OMAP_DSS_ROT_TILER, .buffer_size_unit = 16, .burst_size_unit = 16, }; @@ -800,7 +798,7 @@ enum omap_dss_output_id dss_feat_get_supported_outputs(enum omap_channel channel return omap_current_dss_features->supported_outputs[channel]; } -enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane_id plane) +const u32 *dss_feat_get_supported_color_modes(enum omap_plane_id plane) { return omap_current_dss_features->supported_color_modes[plane]; } @@ -810,11 +808,19 @@ enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane_id plane) return omap_current_dss_features->overlay_caps[plane]; } -bool dss_feat_color_mode_supported(enum omap_plane_id plane, - enum omap_color_mode color_mode) +bool dss_feat_color_mode_supported(enum omap_plane_id plane, u32 fourcc) { - return omap_current_dss_features->supported_color_modes[plane] & - color_mode; + const u32 *modes; + unsigned int i; + + modes = omap_current_dss_features->supported_color_modes[plane]; + + for (i = 0; modes[i]; ++i) { + if (modes[i] == fourcc) + return true; + } + + return false; } u32 dss_feat_get_buffer_size_unit(void) @@ -851,11 +857,6 @@ void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end) *end = omap_current_dss_features->reg_fields[id].end; } -bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type) -{ - return omap_current_dss_features->supported_rotation_types & rot_type; -} - void dss_features_init(enum omapdss_version version) { switch (version) { diff --git a/drivers/gpu/drm/omapdrm/dss/dss_features.h b/drivers/gpu/drm/omapdrm/dss/dss_features.h index 27fbe64935e8..c36436d27ff5 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss_features.h +++ b/drivers/gpu/drm/omapdrm/dss/dss_features.h @@ -90,13 +90,11 @@ unsigned long dss_feat_get_param_min(enum dss_range_param param); unsigned long dss_feat_get_param_max(enum dss_range_param param); enum omap_overlay_caps dss_feat_get_overlay_caps(enum omap_plane_id plane); bool dss_feat_color_mode_supported(enum omap_plane_id plane, - enum omap_color_mode color_mode); + u32 fourcc); u32 dss_feat_get_buffer_size_unit(void); /* in bytes */ u32 dss_feat_get_burst_size_unit(void); /* in bytes */ -bool dss_feat_rotation_type_supported(enum omap_dss_rotation_type rot_type); - bool dss_has_feature(enum dss_feat_id id); void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end); void dss_features_init(enum omapdss_version version); @@ -106,6 +104,6 @@ enum omap_dss_output_id dss_feat_get_supported_outputs(enum omap_channel channel int dss_feat_get_num_mgrs(void); int dss_feat_get_num_ovls(void); -enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane_id plane); +const u32 *dss_feat_get_supported_color_modes(enum omap_plane_id plane); #endif diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c index 87c53034c634..284b4942b9ac 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c @@ -696,11 +696,9 @@ static int hdmi4_bind(struct device *dev, struct device *master, void *data) mutex_init(&hdmi.lock); spin_lock_init(&hdmi.audio_playing_lock); - if (pdev->dev.of_node) { - r = hdmi_probe_of(pdev); - if (r) - return r; - } + r = hdmi_probe_of(pdev); + if (r) + return r; r = hdmi_wp_init(pdev, &hdmi.wp); if (r) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c index e05b7ac4f7dd..ed6001613405 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi4_core.c @@ -889,16 +889,9 @@ int hdmi4_core_init(struct platform_device *pdev, struct hdmi_core_data *core) struct resource *res; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); - if (!res) { - DSSERR("can't get CORE mem resource\n"); - return -EINVAL; - } - core->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(core->base)) { - DSSERR("can't ioremap CORE\n"); + if (IS_ERR(core->base)) return PTR_ERR(core->base); - } return 0; } diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5.c b/drivers/gpu/drm/omapdrm/dss/hdmi5.c index d13dce7e8079..441e1999d86a 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5.c @@ -728,11 +728,9 @@ static int hdmi5_bind(struct device *dev, struct device *master, void *data) mutex_init(&hdmi.lock); spin_lock_init(&hdmi.audio_playing_lock); - if (pdev->dev.of_node) { - r = hdmi_probe_of(pdev); - if (r) - return r; - } + r = hdmi_probe_of(pdev); + if (r) + return r; r = hdmi_wp_init(pdev, &hdmi.wp); if (r) diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c index 8de1d7b2ae55..ab179ec133c0 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi5_core.c @@ -910,16 +910,9 @@ int hdmi5_core_init(struct platform_device *pdev, struct hdmi_core_data *core) struct resource *res; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); - if (!res) { - DSSERR("can't get CORE IORESOURCE_MEM HDMI\n"); - return -EINVAL; - } - core->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(core->base)) { - DSSERR("can't ioremap HDMI core\n"); + if (IS_ERR(core->base)) return PTR_ERR(core->base); - } return 0; } diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi_phy.c b/drivers/gpu/drm/omapdrm/dss/hdmi_phy.c index 3ead47cccac5..fb5e4c724b4b 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi_phy.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi_phy.c @@ -233,16 +233,9 @@ int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy) return r; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); - if (!res) { - DSSERR("can't get PHY mem resource\n"); - return -EINVAL; - } - phy->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(phy->base)) { - DSSERR("can't ioremap TX PHY\n"); + if (IS_ERR(phy->base)) return PTR_ERR(phy->base); - } return 0; } diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi_pll.c b/drivers/gpu/drm/omapdrm/dss/hdmi_pll.c index b8bf6a9e5557..46239358655a 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi_pll.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi_pll.c @@ -180,16 +180,9 @@ int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll, pll->wp = wp; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll"); - if (!res) { - DSSERR("can't get PLL mem resource\n"); - return -EINVAL; - } - pll->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(pll->base)) { - DSSERR("can't ioremap PLLCTRL\n"); + if (IS_ERR(pll->base)) return PTR_ERR(pll->base); - } r = dsi_init_pll_data(pdev, pll); if (r) { diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c b/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c index 597ec9d87d1d..ab129df2e310 100644 --- a/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c +++ b/drivers/gpu/drm/omapdrm/dss/hdmi_wp.c @@ -287,17 +287,11 @@ int hdmi_wp_init(struct platform_device *pdev, struct hdmi_wp_data *wp) struct resource *res; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wp"); - if (!res) { - DSSERR("can't get WP mem resource\n"); - return -EINVAL; - } - wp->phys_base = res->start; - wp->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(wp->base)) { - DSSERR("can't ioremap HDMI WP\n"); + if (IS_ERR(wp->base)) return PTR_ERR(wp->base); - } + + wp->phys_base = res->start; return 0; } diff --git a/drivers/gpu/drm/omapdrm/dss/omapdss.h b/drivers/gpu/drm/omapdrm/dss/omapdss.h index b19dae1fd6c5..85953a0bc7c2 100644 --- a/drivers/gpu/drm/omapdrm/dss/omapdss.h +++ b/drivers/gpu/drm/omapdrm/dss/omapdss.h @@ -59,7 +59,6 @@ #define DISPC_IRQ_FRAMEDONE3 (1 << 30) struct omap_dss_device; -struct omap_overlay_manager; struct dss_lcd_mgr_config; struct snd_aes_iec958; struct snd_cea_861_aud_if; @@ -93,25 +92,7 @@ enum omap_channel { }; enum omap_color_mode { - OMAP_DSS_COLOR_CLUT1 = 1 << 0, /* BITMAP 1 */ - OMAP_DSS_COLOR_CLUT2 = 1 << 1, /* BITMAP 2 */ - OMAP_DSS_COLOR_CLUT4 = 1 << 2, /* BITMAP 4 */ - OMAP_DSS_COLOR_CLUT8 = 1 << 3, /* BITMAP 8 */ - OMAP_DSS_COLOR_RGB12U = 1 << 4, /* RGB12, 16-bit container */ - OMAP_DSS_COLOR_ARGB16 = 1 << 5, /* ARGB16 */ - OMAP_DSS_COLOR_RGB16 = 1 << 6, /* RGB16 */ - OMAP_DSS_COLOR_RGB24U = 1 << 7, /* RGB24, 32-bit container */ - OMAP_DSS_COLOR_RGB24P = 1 << 8, /* RGB24, 24-bit container */ - OMAP_DSS_COLOR_YUV2 = 1 << 9, /* YUV2 4:2:2 co-sited */ - OMAP_DSS_COLOR_UYVY = 1 << 10, /* UYVY 4:2:2 co-sited */ - OMAP_DSS_COLOR_ARGB32 = 1 << 11, /* ARGB32 */ - OMAP_DSS_COLOR_RGBA32 = 1 << 12, /* RGBA32 */ - OMAP_DSS_COLOR_RGBX32 = 1 << 13, /* RGBx32 */ - OMAP_DSS_COLOR_NV12 = 1 << 14, /* NV12 format: YUV 4:2:0 */ - OMAP_DSS_COLOR_RGBA16 = 1 << 15, /* RGBA16 - 4444 */ - OMAP_DSS_COLOR_RGBX16 = 1 << 16, /* RGBx16 - 4444 */ - OMAP_DSS_COLOR_ARGB16_1555 = 1 << 17, /* ARGB16 - 1555 */ - OMAP_DSS_COLOR_XRGB16_1555 = 1 << 18, /* xRGB16 - 1555 */ + _UNUSED_, }; enum omap_dss_load_mode { @@ -126,11 +107,6 @@ enum omap_dss_trans_key_type { OMAP_DSS_COLOR_KEY_VID_SRC = 1, }; -enum omap_rfbi_te_mode { - OMAP_DSS_RFBI_TE_MODE_1 = 1, - OMAP_DSS_RFBI_TE_MODE_2 = 2, -}; - enum omap_dss_signal_level { OMAPDSS_SIG_ACTIVE_LOW, OMAPDSS_SIG_ACTIVE_HIGH, @@ -169,17 +145,8 @@ enum omap_dss_display_state { }; enum omap_dss_rotation_type { - OMAP_DSS_ROT_DMA = 1 << 0, - OMAP_DSS_ROT_VRFB = 1 << 1, - OMAP_DSS_ROT_TILER = 1 << 2, -}; - -/* clockwise rotation angle */ -enum omap_dss_rotation_angle { - OMAP_DSS_ROT_0 = 0, - OMAP_DSS_ROT_90 = 1, - OMAP_DSS_ROT_180 = 2, - OMAP_DSS_ROT_270 = 3, + OMAP_DSS_ROT_NONE = 0, + OMAP_DSS_ROT_TILER = 1 << 0, }; enum omap_overlay_caps { @@ -191,10 +158,6 @@ enum omap_overlay_caps { OMAP_DSS_OVL_CAP_REPLICATION = 1 << 5, }; -enum omap_overlay_manager_caps { - OMAP_DSS_DUMMY_VALUE, /* add a dummy value to prevent compiler error */ -}; - enum omap_dss_clk_source { OMAP_DSS_CLK_SRC_FCK = 0, /* OMAP2/3: DSS1_ALWON_FCLK * OMAP4: DSS_FCLK */ @@ -220,27 +183,6 @@ enum omap_dss_output_id { OMAP_DSS_OUTPUT_HDMI = 1 << 6, }; -/* RFBI */ - -struct rfbi_timings { - int cs_on_time; - int cs_off_time; - int we_on_time; - int we_off_time; - int re_on_time; - int re_off_time; - int we_cycle_time; - int re_cycle_time; - int cs_pulse_width; - int access_time; - - int clk_div; - - u32 tim[5]; /* set by rfbi_convert_timings() */ - - int converted; -}; - /* DSI */ enum omap_dss_dsi_trans_mode { @@ -318,10 +260,9 @@ struct omap_overlay_info { u16 screen_width; u16 width; u16 height; - enum omap_color_mode color_mode; + u32 fourcc; u8 rotation; enum omap_dss_rotation_type rotation_type; - bool mirror; u16 pos_x; u16 pos_y; @@ -332,48 +273,6 @@ struct omap_overlay_info { u8 zorder; }; -struct omap_overlay { - struct kobject kobj; - struct list_head list; - - /* static fields */ - const char *name; - enum omap_plane_id id; - enum omap_color_mode supported_modes; - enum omap_overlay_caps caps; - - /* dynamic fields */ - struct omap_overlay_manager *manager; - - /* - * The following functions do not block: - * - * is_enabled - * set_overlay_info - * get_overlay_info - * - * The rest of the functions may block and cannot be called from - * interrupt context - */ - - int (*enable)(struct omap_overlay *ovl); - int (*disable)(struct omap_overlay *ovl); - bool (*is_enabled)(struct omap_overlay *ovl); - - int (*set_manager)(struct omap_overlay *ovl, - struct omap_overlay_manager *mgr); - int (*unset_manager)(struct omap_overlay *ovl); - - int (*set_overlay_info)(struct omap_overlay *ovl, - struct omap_overlay_info *info); - void (*get_overlay_info)(struct omap_overlay *ovl, - struct omap_overlay_info *info); - - int (*wait_for_go)(struct omap_overlay *ovl); - - struct omap_dss_device *(*get_device)(struct omap_overlay *ovl); -}; - struct omap_overlay_manager_info { u32 default_color; @@ -387,47 +286,6 @@ struct omap_overlay_manager_info { struct omap_dss_cpr_coefs cpr_coefs; }; -struct omap_overlay_manager { - struct kobject kobj; - - /* static fields */ - const char *name; - enum omap_channel id; - enum omap_overlay_manager_caps caps; - struct list_head overlays; - enum omap_display_type supported_displays; - enum omap_dss_output_id supported_outputs; - - /* dynamic fields */ - struct omap_dss_device *output; - - /* - * The following functions do not block: - * - * set_manager_info - * get_manager_info - * apply - * - * The rest of the functions may block and cannot be called from - * interrupt context - */ - - int (*set_output)(struct omap_overlay_manager *mgr, - struct omap_dss_device *output); - int (*unset_output)(struct omap_overlay_manager *mgr); - - int (*set_manager_info)(struct omap_overlay_manager *mgr, - struct omap_overlay_manager_info *info); - void (*get_manager_info)(struct omap_overlay_manager *mgr, - struct omap_overlay_manager_info *info); - - int (*apply)(struct omap_overlay_manager *mgr); - int (*wait_for_go)(struct omap_overlay_manager *mgr); - int (*wait_for_vsync)(struct omap_overlay_manager *mgr); - - struct omap_dss_device *(*get_device)(struct omap_overlay_manager *mgr); -}; - /* 22 pins means 1 clk lane and 10 data lanes */ #define OMAP_DSS_MAX_DSI_PINS 22 @@ -449,10 +307,9 @@ struct omap_dss_writeback_info { u16 buf_width; u16 width; u16 height; - enum omap_color_mode color_mode; + u32 fourcc; u8 rotation; enum omap_dss_rotation_type rotation_type; - bool mirror; u8 pre_mult_alpha; }; @@ -471,8 +328,6 @@ struct omapdss_dpi_ops { struct videomode *vm); void (*get_timings)(struct omap_dss_device *dssdev, struct videomode *vm); - - void (*set_data_lines)(struct omap_dss_device *dssdev, int data_lines); }; struct omapdss_sdi_ops { @@ -490,8 +345,6 @@ struct omapdss_sdi_ops { struct videomode *vm); void (*get_timings)(struct omap_dss_device *dssdev, struct videomode *vm); - - void (*set_datapairs)(struct omap_dss_device *dssdev, int datapairs); }; struct omapdss_dvi_ops { @@ -527,11 +380,6 @@ struct omapdss_atv_ops { void (*get_timings)(struct omap_dss_device *dssdev, struct videomode *vm); - void (*set_type)(struct omap_dss_device *dssdev, - enum omap_dss_venc_type type); - void (*invert_vid_out_polarity)(struct omap_dss_device *dssdev, - bool invert_polarity); - int (*set_wss)(struct omap_dss_device *dssdev, u32 wss); u32 (*get_wss)(struct omap_dss_device *dssdev); }; @@ -631,30 +479,6 @@ struct omap_dss_device { enum omap_display_type type; enum omap_display_type output_type; - union { - struct { - u8 data_lines; - } dpi; - - struct { - u8 channel; - u8 data_lines; - } rfbi; - - struct { - u8 datapairs; - } sdi; - - struct { - int module; - } dsi; - - struct { - enum omap_dss_venc_type type; - bool invert_polarity; - } venc; - } phy; - struct { struct videomode vm; @@ -662,18 +486,8 @@ struct omap_dss_device { enum omap_dss_dsi_mode dsi_mode; } panel; - struct { - u8 pixel_size; - struct rfbi_timings rfbi_timings; - } ctrl; - const char *name; - /* used to match device to driver */ - const char *driver_name; - - void *data; - struct omap_dss_driver *driver; union { @@ -709,8 +523,6 @@ struct omap_dss_device { int port_num; /* dynamic fields */ - struct omap_overlay_manager *manager; - struct omap_dss_device *dst; }; @@ -742,12 +554,6 @@ struct omap_dss_driver { void *buf, size_t size, u16 x, u16 y, u16 w, u16 h); - void (*get_resolution)(struct omap_dss_device *dssdev, - u16 *xres, u16 *yres); - void (*get_dimensions)(struct omap_dss_device *dssdev, - u32 *width, u32 *height); - int (*get_recommended_bpp)(struct omap_dss_device *dssdev); - int (*check_timings)(struct omap_dss_device *dssdev, struct videomode *vm); void (*set_timings)(struct omap_dss_device *dssdev, @@ -781,35 +587,22 @@ void omap_dss_put_device(struct omap_dss_device *dssdev); struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from); struct omap_dss_device *omap_dss_find_device(void *data, int (*match)(struct omap_dss_device *dssdev, void *data)); -const char *omapdss_get_default_display_name(void); - -int dss_feat_get_num_mgrs(void); -int dss_feat_get_num_ovls(void); -enum omap_color_mode dss_feat_get_supported_color_modes(enum omap_plane_id plane); - int omap_dss_get_num_overlay_managers(void); -struct omap_overlay_manager *omap_dss_get_overlay_manager(int num); int omap_dss_get_num_overlays(void); -struct omap_overlay *omap_dss_get_overlay(int num); int omapdss_register_output(struct omap_dss_device *output); void omapdss_unregister_output(struct omap_dss_device *output); struct omap_dss_device *omap_dss_get_output(enum omap_dss_output_id id); -struct omap_dss_device *omap_dss_find_output(const char *name); struct omap_dss_device *omap_dss_find_output_by_port_node(struct device_node *port); int omapdss_output_set_device(struct omap_dss_device *out, struct omap_dss_device *dssdev); int omapdss_output_unset_device(struct omap_dss_device *out); struct omap_dss_device *omapdss_find_output_from_display(struct omap_dss_device *dssdev); -struct omap_overlay_manager *omapdss_find_mgr_from_display(struct omap_dss_device *dssdev); -void omapdss_default_get_resolution(struct omap_dss_device *dssdev, - u16 *xres, u16 *yres); -int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev); void omapdss_default_get_timings(struct omap_dss_device *dssdev, struct videomode *vm); @@ -881,7 +674,6 @@ void dss_mgr_unregister_framedone_handler(enum omap_channel channel, struct dispc_ops { u32 (*read_irqstatus)(void); void (*clear_irqstatus)(u32 mask); - u32 (*read_irqenable)(void); void (*write_irqenable)(u32 mask); int (*request_irq)(irq_handler_t handler, void *dev_id); @@ -913,14 +705,12 @@ struct dispc_ops { unsigned int length); int (*ovl_enable)(enum omap_plane_id plane, bool enable); - bool (*ovl_enabled)(enum omap_plane_id plane); - void (*ovl_set_channel_out)(enum omap_plane_id plane, - enum omap_channel channel); int (*ovl_setup)(enum omap_plane_id plane, const struct omap_overlay_info *oi, - const struct videomode *vm, bool mem_to_mem); + const struct videomode *vm, bool mem_to_mem, + enum omap_channel channel); - enum omap_color_mode (*ovl_get_color_modes)(enum omap_plane_id plane); + const u32 *(*ovl_get_color_modes)(enum omap_plane_id plane); }; void dispc_set_ops(const struct dispc_ops *o); diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c index 655c5d73eac9..3c572b699ed3 100644 --- a/drivers/gpu/drm/omapdrm/dss/output.c +++ b/drivers/gpu/drm/omapdrm/dss/output.c @@ -133,19 +133,6 @@ struct omap_dss_device *omap_dss_get_output(enum omap_dss_output_id id) } EXPORT_SYMBOL(omap_dss_get_output); -struct omap_dss_device *omap_dss_find_output(const char *name) -{ - struct omap_dss_device *out; - - list_for_each_entry(out, &output_list, list) { - if (strcmp(out->name, name) == 0) - return omap_dss_get_device(out); - } - - return NULL; -} -EXPORT_SYMBOL(omap_dss_find_output); - struct omap_dss_device *omap_dss_find_output_by_port_node(struct device_node *port) { struct device_node *src_node; diff --git a/drivers/gpu/drm/omapdrm/dss/rfbi.c b/drivers/gpu/drm/omapdrm/dss/rfbi.c deleted file mode 100644 index 09724757366a..000000000000 --- a/drivers/gpu/drm/omapdrm/dss/rfbi.c +++ /dev/null @@ -1,1083 +0,0 @@ -/* - * linux/drivers/video/omap2/dss/rfbi.c - * - * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <[email protected]> - * - * Some code and ideas taken from drivers/video/omap/ driver - * by Imre Deak. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#define DSS_SUBSYS_NAME "RFBI" - -#include <linux/kernel.h> -#include <linux/dma-mapping.h> -#include <linux/export.h> -#include <linux/vmalloc.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/delay.h> -#include <linux/kfifo.h> -#include <linux/ktime.h> -#include <linux/hrtimer.h> -#include <linux/seq_file.h> -#include <linux/semaphore.h> -#include <linux/platform_device.h> -#include <linux/pm_runtime.h> -#include <linux/component.h> - -#include "omapdss.h" -#include "dss.h" - -struct rfbi_reg { u16 idx; }; - -#define RFBI_REG(idx) ((const struct rfbi_reg) { idx }) - -#define RFBI_REVISION RFBI_REG(0x0000) -#define RFBI_SYSCONFIG RFBI_REG(0x0010) -#define RFBI_SYSSTATUS RFBI_REG(0x0014) -#define RFBI_CONTROL RFBI_REG(0x0040) -#define RFBI_PIXEL_CNT RFBI_REG(0x0044) -#define RFBI_LINE_NUMBER RFBI_REG(0x0048) -#define RFBI_CMD RFBI_REG(0x004c) -#define RFBI_PARAM RFBI_REG(0x0050) -#define RFBI_DATA RFBI_REG(0x0054) -#define RFBI_READ RFBI_REG(0x0058) -#define RFBI_STATUS RFBI_REG(0x005c) - -#define RFBI_CONFIG(n) RFBI_REG(0x0060 + (n)*0x18) -#define RFBI_ONOFF_TIME(n) RFBI_REG(0x0064 + (n)*0x18) -#define RFBI_CYCLE_TIME(n) RFBI_REG(0x0068 + (n)*0x18) -#define RFBI_DATA_CYCLE1(n) RFBI_REG(0x006c + (n)*0x18) -#define RFBI_DATA_CYCLE2(n) RFBI_REG(0x0070 + (n)*0x18) -#define RFBI_DATA_CYCLE3(n) RFBI_REG(0x0074 + (n)*0x18) - -#define RFBI_VSYNC_WIDTH RFBI_REG(0x0090) -#define RFBI_HSYNC_WIDTH RFBI_REG(0x0094) - -#define REG_FLD_MOD(idx, val, start, end) \ - rfbi_write_reg(idx, FLD_MOD(rfbi_read_reg(idx), val, start, end)) - -enum omap_rfbi_cycleformat { - OMAP_DSS_RFBI_CYCLEFORMAT_1_1 = 0, - OMAP_DSS_RFBI_CYCLEFORMAT_2_1 = 1, - OMAP_DSS_RFBI_CYCLEFORMAT_3_1 = 2, - OMAP_DSS_RFBI_CYCLEFORMAT_3_2 = 3, -}; - -enum omap_rfbi_datatype { - OMAP_DSS_RFBI_DATATYPE_12 = 0, - OMAP_DSS_RFBI_DATATYPE_16 = 1, - OMAP_DSS_RFBI_DATATYPE_18 = 2, - OMAP_DSS_RFBI_DATATYPE_24 = 3, -}; - -enum omap_rfbi_parallelmode { - OMAP_DSS_RFBI_PARALLELMODE_8 = 0, - OMAP_DSS_RFBI_PARALLELMODE_9 = 1, - OMAP_DSS_RFBI_PARALLELMODE_12 = 2, - OMAP_DSS_RFBI_PARALLELMODE_16 = 3, -}; - -static int rfbi_convert_timings(struct rfbi_timings *t); -static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div); - -static struct { - struct platform_device *pdev; - void __iomem *base; - - unsigned long l4_khz; - - enum omap_rfbi_datatype datatype; - enum omap_rfbi_parallelmode parallelmode; - - enum omap_rfbi_te_mode te_mode; - int te_enabled; - - void (*framedone_callback)(void *data); - void *framedone_callback_data; - - struct omap_dss_device *dssdev[2]; - - struct semaphore bus_lock; - - struct videomode vm; - int pixel_size; - int data_lines; - struct rfbi_timings intf_timings; - - struct omap_dss_device output; -} rfbi; - -static inline void rfbi_write_reg(const struct rfbi_reg idx, u32 val) -{ - __raw_writel(val, rfbi.base + idx.idx); -} - -static inline u32 rfbi_read_reg(const struct rfbi_reg idx) -{ - return __raw_readl(rfbi.base + idx.idx); -} - -static int rfbi_runtime_get(void) -{ - int r; - - DSSDBG("rfbi_runtime_get\n"); - - r = pm_runtime_get_sync(&rfbi.pdev->dev); - WARN_ON(r < 0); - return r < 0 ? r : 0; -} - -static void rfbi_runtime_put(void) -{ - int r; - - DSSDBG("rfbi_runtime_put\n"); - - r = pm_runtime_put_sync(&rfbi.pdev->dev); - WARN_ON(r < 0 && r != -ENOSYS); -} - -static void rfbi_bus_lock(void) -{ - down(&rfbi.bus_lock); -} - -static void rfbi_bus_unlock(void) -{ - up(&rfbi.bus_lock); -} - -static void rfbi_write_command(const void *buf, u32 len) -{ - switch (rfbi.parallelmode) { - case OMAP_DSS_RFBI_PARALLELMODE_8: - { - const u8 *b = buf; - for (; len; len--) - rfbi_write_reg(RFBI_CMD, *b++); - break; - } - - case OMAP_DSS_RFBI_PARALLELMODE_16: - { - const u16 *w = buf; - BUG_ON(len & 1); - for (; len; len -= 2) - rfbi_write_reg(RFBI_CMD, *w++); - break; - } - - case OMAP_DSS_RFBI_PARALLELMODE_9: - case OMAP_DSS_RFBI_PARALLELMODE_12: - default: - BUG(); - } -} - -static void rfbi_read_data(void *buf, u32 len) -{ - switch (rfbi.parallelmode) { - case OMAP_DSS_RFBI_PARALLELMODE_8: - { - u8 *b = buf; - for (; len; len--) { - rfbi_write_reg(RFBI_READ, 0); - *b++ = rfbi_read_reg(RFBI_READ); - } - break; - } - - case OMAP_DSS_RFBI_PARALLELMODE_16: - { - u16 *w = buf; - BUG_ON(len & ~1); - for (; len; len -= 2) { - rfbi_write_reg(RFBI_READ, 0); - *w++ = rfbi_read_reg(RFBI_READ); - } - break; - } - - case OMAP_DSS_RFBI_PARALLELMODE_9: - case OMAP_DSS_RFBI_PARALLELMODE_12: - default: - BUG(); - } -} - -static void rfbi_write_data(const void *buf, u32 len) -{ - switch (rfbi.parallelmode) { - case OMAP_DSS_RFBI_PARALLELMODE_8: - { - const u8 *b = buf; - for (; len; len--) - rfbi_write_reg(RFBI_PARAM, *b++); - break; - } - - case OMAP_DSS_RFBI_PARALLELMODE_16: - { - const u16 *w = buf; - BUG_ON(len & 1); - for (; len; len -= 2) - rfbi_write_reg(RFBI_PARAM, *w++); - break; - } - - case OMAP_DSS_RFBI_PARALLELMODE_9: - case OMAP_DSS_RFBI_PARALLELMODE_12: - default: - BUG(); - - } -} - -static void rfbi_write_pixels(const void __iomem *buf, int scr_width, - u16 x, u16 y, - u16 w, u16 h) -{ - int start_offset = scr_width * y + x; - int horiz_offset = scr_width - w; - int i; - - if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 && - rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) { - const u16 __iomem *pd = buf; - pd += start_offset; - - for (; h; --h) { - for (i = 0; i < w; ++i) { - const u8 __iomem *b = (const u8 __iomem *)pd; - rfbi_write_reg(RFBI_PARAM, __raw_readb(b+1)); - rfbi_write_reg(RFBI_PARAM, __raw_readb(b+0)); - ++pd; - } - pd += horiz_offset; - } - } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_24 && - rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) { - const u32 __iomem *pd = buf; - pd += start_offset; - - for (; h; --h) { - for (i = 0; i < w; ++i) { - const u8 __iomem *b = (const u8 __iomem *)pd; - rfbi_write_reg(RFBI_PARAM, __raw_readb(b+2)); - rfbi_write_reg(RFBI_PARAM, __raw_readb(b+1)); - rfbi_write_reg(RFBI_PARAM, __raw_readb(b+0)); - ++pd; - } - pd += horiz_offset; - } - } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 && - rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_16) { - const u16 __iomem *pd = buf; - pd += start_offset; - - for (; h; --h) { - for (i = 0; i < w; ++i) { - rfbi_write_reg(RFBI_PARAM, __raw_readw(pd)); - ++pd; - } - pd += horiz_offset; - } - } else { - BUG(); - } -} - -static int rfbi_transfer_area(struct omap_dss_device *dssdev, - void (*callback)(void *data), void *data) -{ - u32 l; - int r; - struct omap_overlay_manager *mgr = rfbi.output.manager; - u16 width = rfbi.vm.hactive; - u16 height = rfbi.vm.vactive; - - /*BUG_ON(callback == 0);*/ - BUG_ON(rfbi.framedone_callback != NULL); - - DSSDBG("rfbi_transfer_area %dx%d\n", width, height); - - dss_mgr_set_timings(mgr, &rfbi.vm); - - r = dss_mgr_enable(mgr); - if (r) - return r; - - rfbi.framedone_callback = callback; - rfbi.framedone_callback_data = data; - - rfbi_write_reg(RFBI_PIXEL_CNT, width * height); - - l = rfbi_read_reg(RFBI_CONTROL); - l = FLD_MOD(l, 1, 0, 0); /* enable */ - if (!rfbi.te_enabled) - l = FLD_MOD(l, 1, 4, 4); /* ITE */ - - rfbi_write_reg(RFBI_CONTROL, l); - - return 0; -} - -static void framedone_callback(void *data) -{ - void (*callback)(void *data); - - DSSDBG("FRAMEDONE\n"); - - REG_FLD_MOD(RFBI_CONTROL, 0, 0, 0); - - callback = rfbi.framedone_callback; - rfbi.framedone_callback = NULL; - - if (callback != NULL) - callback(rfbi.framedone_callback_data); -} - -#if 1 /* VERBOSE */ -static void rfbi_print_timings(void) -{ - u32 l; - u32 time; - - l = rfbi_read_reg(RFBI_CONFIG(0)); - time = 1000000000 / rfbi.l4_khz; - if (l & (1 << 4)) - time *= 2; - - DSSDBG("Tick time %u ps\n", time); - l = rfbi_read_reg(RFBI_ONOFF_TIME(0)); - DSSDBG("CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, " - "REONTIME %d, REOFFTIME %d\n", - l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f, - (l >> 20) & 0x0f, (l >> 24) & 0x3f); - - l = rfbi_read_reg(RFBI_CYCLE_TIME(0)); - DSSDBG("WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, " - "ACCESSTIME %d\n", - (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f, - (l >> 22) & 0x3f); -} -#else -static void rfbi_print_timings(void) {} -#endif - - - - -static u32 extif_clk_period; - -static inline unsigned long round_to_extif_ticks(unsigned long ps, int div) -{ - int bus_tick = extif_clk_period * div; - return (ps + bus_tick - 1) / bus_tick * bus_tick; -} - -static int calc_reg_timing(struct rfbi_timings *t, int div) -{ - t->clk_div = div; - - t->cs_on_time = round_to_extif_ticks(t->cs_on_time, div); - - t->we_on_time = round_to_extif_ticks(t->we_on_time, div); - t->we_off_time = round_to_extif_ticks(t->we_off_time, div); - t->we_cycle_time = round_to_extif_ticks(t->we_cycle_time, div); - - t->re_on_time = round_to_extif_ticks(t->re_on_time, div); - t->re_off_time = round_to_extif_ticks(t->re_off_time, div); - t->re_cycle_time = round_to_extif_ticks(t->re_cycle_time, div); - - t->access_time = round_to_extif_ticks(t->access_time, div); - t->cs_off_time = round_to_extif_ticks(t->cs_off_time, div); - t->cs_pulse_width = round_to_extif_ticks(t->cs_pulse_width, div); - - DSSDBG("[reg]cson %d csoff %d reon %d reoff %d\n", - t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); - DSSDBG("[reg]weon %d weoff %d recyc %d wecyc %d\n", - t->we_on_time, t->we_off_time, t->re_cycle_time, - t->we_cycle_time); - DSSDBG("[reg]rdaccess %d cspulse %d\n", - t->access_time, t->cs_pulse_width); - - return rfbi_convert_timings(t); -} - -static int calc_extif_timings(struct rfbi_timings *t) -{ - u32 max_clk_div; - int div; - - rfbi_get_clk_info(&extif_clk_period, &max_clk_div); - for (div = 1; div <= max_clk_div; div++) { - if (calc_reg_timing(t, div) == 0) - break; - } - - if (div <= max_clk_div) - return 0; - - DSSERR("can't setup timings\n"); - return -1; -} - - -static void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t) -{ - int r; - - if (!t->converted) { - r = calc_extif_timings(t); - if (r < 0) - DSSERR("Failed to calc timings\n"); - } - - BUG_ON(!t->converted); - - rfbi_write_reg(RFBI_ONOFF_TIME(rfbi_module), t->tim[0]); - rfbi_write_reg(RFBI_CYCLE_TIME(rfbi_module), t->tim[1]); - - /* TIMEGRANULARITY */ - REG_FLD_MOD(RFBI_CONFIG(rfbi_module), - (t->tim[2] ? 1 : 0), 4, 4); - - rfbi_print_timings(); -} - -static int ps_to_rfbi_ticks(int time, int div) -{ - unsigned long tick_ps; - int ret; - - /* Calculate in picosecs to yield more exact results */ - tick_ps = 1000000000 / (rfbi.l4_khz) * div; - - ret = (time + tick_ps - 1) / tick_ps; - - return ret; -} - -static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div) -{ - *clk_period = 1000000000 / rfbi.l4_khz; - *max_clk_div = 2; -} - -static int rfbi_convert_timings(struct rfbi_timings *t) -{ - u32 l; - int reon, reoff, weon, weoff, cson, csoff, cs_pulse; - int actim, recyc, wecyc; - int div = t->clk_div; - - if (div <= 0 || div > 2) - return -1; - - /* Make sure that after conversion it still holds that: - * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff, - * csoff > cson, csoff >= max(weoff, reoff), actim > reon - */ - weon = ps_to_rfbi_ticks(t->we_on_time, div); - weoff = ps_to_rfbi_ticks(t->we_off_time, div); - if (weoff <= weon) - weoff = weon + 1; - if (weon > 0x0f) - return -1; - if (weoff > 0x3f) - return -1; - - reon = ps_to_rfbi_ticks(t->re_on_time, div); - reoff = ps_to_rfbi_ticks(t->re_off_time, div); - if (reoff <= reon) - reoff = reon + 1; - if (reon > 0x0f) - return -1; - if (reoff > 0x3f) - return -1; - - cson = ps_to_rfbi_ticks(t->cs_on_time, div); - csoff = ps_to_rfbi_ticks(t->cs_off_time, div); - if (csoff <= cson) - csoff = cson + 1; - if (csoff < max(weoff, reoff)) - csoff = max(weoff, reoff); - if (cson > 0x0f) - return -1; - if (csoff > 0x3f) - return -1; - - l = cson; - l |= csoff << 4; - l |= weon << 10; - l |= weoff << 14; - l |= reon << 20; - l |= reoff << 24; - - t->tim[0] = l; - - actim = ps_to_rfbi_ticks(t->access_time, div); - if (actim <= reon) - actim = reon + 1; - if (actim > 0x3f) - return -1; - - wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div); - if (wecyc < weoff) - wecyc = weoff; - if (wecyc > 0x3f) - return -1; - - recyc = ps_to_rfbi_ticks(t->re_cycle_time, div); - if (recyc < reoff) - recyc = reoff; - if (recyc > 0x3f) - return -1; - - cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div); - if (cs_pulse > 0x3f) - return -1; - - l = wecyc; - l |= recyc << 6; - l |= cs_pulse << 12; - l |= actim << 22; - - t->tim[1] = l; - - t->tim[2] = div - 1; - - t->converted = 1; - - return 0; -} - -/* xxx FIX module selection missing */ -static int rfbi_setup_te(enum omap_rfbi_te_mode mode, - unsigned hs_pulse_time, unsigned vs_pulse_time, - int hs_pol_inv, int vs_pol_inv, int extif_div) -{ - int hs, vs; - int min; - u32 l; - - hs = ps_to_rfbi_ticks(hs_pulse_time, 1); - vs = ps_to_rfbi_ticks(vs_pulse_time, 1); - if (hs < 2) - return -EDOM; - if (mode == OMAP_DSS_RFBI_TE_MODE_2) - min = 2; - else /* OMAP_DSS_RFBI_TE_MODE_1 */ - min = 4; - if (vs < min) - return -EDOM; - if (vs == hs) - return -EINVAL; - rfbi.te_mode = mode; - DSSDBG("setup_te: mode %d hs %d vs %d hs_inv %d vs_inv %d\n", - mode, hs, vs, hs_pol_inv, vs_pol_inv); - - rfbi_write_reg(RFBI_HSYNC_WIDTH, hs); - rfbi_write_reg(RFBI_VSYNC_WIDTH, vs); - - l = rfbi_read_reg(RFBI_CONFIG(0)); - if (hs_pol_inv) - l &= ~(1 << 21); - else - l |= 1 << 21; - if (vs_pol_inv) - l &= ~(1 << 20); - else - l |= 1 << 20; - - return 0; -} - -/* xxx FIX module selection missing */ -static int rfbi_enable_te(bool enable, unsigned line) -{ - u32 l; - - DSSDBG("te %d line %d mode %d\n", enable, line, rfbi.te_mode); - if (line > (1 << 11) - 1) - return -EINVAL; - - l = rfbi_read_reg(RFBI_CONFIG(0)); - l &= ~(0x3 << 2); - if (enable) { - rfbi.te_enabled = 1; - l |= rfbi.te_mode << 2; - } else - rfbi.te_enabled = 0; - rfbi_write_reg(RFBI_CONFIG(0), l); - rfbi_write_reg(RFBI_LINE_NUMBER, line); - - return 0; -} - -static int rfbi_configure_bus(int rfbi_module, int bpp, int lines) -{ - u32 l; - int cycle1 = 0, cycle2 = 0, cycle3 = 0; - enum omap_rfbi_cycleformat cycleformat; - enum omap_rfbi_datatype datatype; - enum omap_rfbi_parallelmode parallelmode; - - switch (bpp) { - case 12: - datatype = OMAP_DSS_RFBI_DATATYPE_12; - break; - case 16: - datatype = OMAP_DSS_RFBI_DATATYPE_16; - break; - case 18: - datatype = OMAP_DSS_RFBI_DATATYPE_18; - break; - case 24: - datatype = OMAP_DSS_RFBI_DATATYPE_24; - break; - default: - BUG(); - return 1; - } - rfbi.datatype = datatype; - - switch (lines) { - case 8: - parallelmode = OMAP_DSS_RFBI_PARALLELMODE_8; - break; - case 9: - parallelmode = OMAP_DSS_RFBI_PARALLELMODE_9; - break; - case 12: - parallelmode = OMAP_DSS_RFBI_PARALLELMODE_12; - break; - case 16: - parallelmode = OMAP_DSS_RFBI_PARALLELMODE_16; - break; - default: - BUG(); - return 1; - } - rfbi.parallelmode = parallelmode; - - if ((bpp % lines) == 0) { - switch (bpp / lines) { - case 1: - cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_1_1; - break; - case 2: - cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_2_1; - break; - case 3: - cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_1; - break; - default: - BUG(); - return 1; - } - } else if ((2 * bpp % lines) == 0) { - if ((2 * bpp / lines) == 3) - cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_2; - else { - BUG(); - return 1; - } - } else { - BUG(); - return 1; - } - - switch (cycleformat) { - case OMAP_DSS_RFBI_CYCLEFORMAT_1_1: - cycle1 = lines; - break; - - case OMAP_DSS_RFBI_CYCLEFORMAT_2_1: - cycle1 = lines; - cycle2 = lines; - break; - - case OMAP_DSS_RFBI_CYCLEFORMAT_3_1: - cycle1 = lines; - cycle2 = lines; - cycle3 = lines; - break; - - case OMAP_DSS_RFBI_CYCLEFORMAT_3_2: - cycle1 = lines; - cycle2 = (lines / 2) | ((lines / 2) << 16); - cycle3 = (lines << 16); - break; - } - - REG_FLD_MOD(RFBI_CONTROL, 0, 3, 2); /* clear CS */ - - l = 0; - l |= FLD_VAL(parallelmode, 1, 0); - l |= FLD_VAL(0, 3, 2); /* TRIGGERMODE: ITE */ - l |= FLD_VAL(0, 4, 4); /* TIMEGRANULARITY */ - l |= FLD_VAL(datatype, 6, 5); - /* l |= FLD_VAL(2, 8, 7); */ /* L4FORMAT, 2pix/L4 */ - l |= FLD_VAL(0, 8, 7); /* L4FORMAT, 1pix/L4 */ - l |= FLD_VAL(cycleformat, 10, 9); - l |= FLD_VAL(0, 12, 11); /* UNUSEDBITS */ - l |= FLD_VAL(0, 16, 16); /* A0POLARITY */ - l |= FLD_VAL(0, 17, 17); /* REPOLARITY */ - l |= FLD_VAL(0, 18, 18); /* WEPOLARITY */ - l |= FLD_VAL(0, 19, 19); /* CSPOLARITY */ - l |= FLD_VAL(1, 20, 20); /* TE_VSYNC_POLARITY */ - l |= FLD_VAL(1, 21, 21); /* HSYNCPOLARITY */ - rfbi_write_reg(RFBI_CONFIG(rfbi_module), l); - - rfbi_write_reg(RFBI_DATA_CYCLE1(rfbi_module), cycle1); - rfbi_write_reg(RFBI_DATA_CYCLE2(rfbi_module), cycle2); - rfbi_write_reg(RFBI_DATA_CYCLE3(rfbi_module), cycle3); - - - l = rfbi_read_reg(RFBI_CONTROL); - l = FLD_MOD(l, rfbi_module+1, 3, 2); /* Select CSx */ - l = FLD_MOD(l, 0, 1, 1); /* clear bypass */ - rfbi_write_reg(RFBI_CONTROL, l); - - - DSSDBG("RFBI config: bpp %d, lines %d, cycles: 0x%x 0x%x 0x%x\n", - bpp, lines, cycle1, cycle2, cycle3); - - return 0; -} - -static int rfbi_configure(struct omap_dss_device *dssdev) -{ - return rfbi_configure_bus(dssdev->phy.rfbi.channel, rfbi.pixel_size, - rfbi.data_lines); -} - -static int rfbi_update(struct omap_dss_device *dssdev, void (*callback)(void *), - void *data) -{ - return rfbi_transfer_area(dssdev, callback, data); -} - -static void rfbi_set_size(struct omap_dss_device *dssdev, u16 w, u16 h) -{ - rfbi.vm.hactive = w; - rfbi.vm.vactive = h; -} - -static void rfbi_set_pixel_size(struct omap_dss_device *dssdev, int pixel_size) -{ - rfbi.pixel_size = pixel_size; -} - -static void rfbi_set_data_lines(struct omap_dss_device *dssdev, int data_lines) -{ - rfbi.data_lines = data_lines; -} - -static void rfbi_set_interface_timings(struct omap_dss_device *dssdev, - struct rfbi_timings *timings) -{ - rfbi.intf_timings = *timings; -} - -static void rfbi_dump_regs(struct seq_file *s) -{ -#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, rfbi_read_reg(r)) - - if (rfbi_runtime_get()) - return; - - DUMPREG(RFBI_REVISION); - DUMPREG(RFBI_SYSCONFIG); - DUMPREG(RFBI_SYSSTATUS); - DUMPREG(RFBI_CONTROL); - DUMPREG(RFBI_PIXEL_CNT); - DUMPREG(RFBI_LINE_NUMBER); - DUMPREG(RFBI_CMD); - DUMPREG(RFBI_PARAM); - DUMPREG(RFBI_DATA); - DUMPREG(RFBI_READ); - DUMPREG(RFBI_STATUS); - - DUMPREG(RFBI_CONFIG(0)); - DUMPREG(RFBI_ONOFF_TIME(0)); - DUMPREG(RFBI_CYCLE_TIME(0)); - DUMPREG(RFBI_DATA_CYCLE1(0)); - DUMPREG(RFBI_DATA_CYCLE2(0)); - DUMPREG(RFBI_DATA_CYCLE3(0)); - - DUMPREG(RFBI_CONFIG(1)); - DUMPREG(RFBI_ONOFF_TIME(1)); - DUMPREG(RFBI_CYCLE_TIME(1)); - DUMPREG(RFBI_DATA_CYCLE1(1)); - DUMPREG(RFBI_DATA_CYCLE2(1)); - DUMPREG(RFBI_DATA_CYCLE3(1)); - - DUMPREG(RFBI_VSYNC_WIDTH); - DUMPREG(RFBI_HSYNC_WIDTH); - - rfbi_runtime_put(); -#undef DUMPREG -} - -static void rfbi_config_lcd_manager(struct omap_dss_device *dssdev) -{ - struct omap_overlay_manager *mgr = rfbi.output.manager; - struct dss_lcd_mgr_config mgr_config; - - mgr_config.io_pad_mode = DSS_IO_PAD_MODE_RFBI; - - mgr_config.stallmode = true; - /* Do we need fifohandcheck for RFBI? */ - mgr_config.fifohandcheck = false; - - mgr_config.video_port_width = rfbi.pixel_size; - mgr_config.lcden_sig_polarity = 0; - - dss_mgr_set_lcd_config(mgr, &mgr_config); - - /* - * Set rfbi.timings with default values, the hactive and vactive fields - * are expected to be already configured by the panel driver via - * omapdss_rfbi_set_size() - */ - rfbi.vm.hsync_len = 1; - rfbi.vm.hfront_porch = 1; - rfbi.vm.hback_porch = 1; - rfbi.vm.vsync_len = 1; - rfbi.vm.vfront_porch = 0; - rfbi.vm.vback_porch = 0; - - rfbi.vm.flags &= ~DISPLAY_FLAGS_INTERLACED; - rfbi.vm.flags &= ~DISPLAY_FLAGS_HSYNC_LOW; - rfbi.vm.flags |= DISPLAY_FLAGS_HSYNC_HIGH; - rfbi.vm.flags &= ~DISPLAY_FLAGS_VSYNC_LOW; - rfbi.vm.flags |= DISPLAY_FLAGS_VSYNC_HIGH; - rfbi.vm.flags &= ~DISPLAY_FLAGS_PIXDATA_NEGEDGE; - rfbi.vm.flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE; - rfbi.vm.flags &= ~DISPLAY_FLAGS_DE_LOW; - rfbi.vm.flags |= DISPLAY_FLAGS_DE_HIGH; - rfbi.vm.flags &= ~DISPLAY_FLAGS_SYNC_POSEDGE; - rfbi.vm.flags |= DISPLAY_FLAGS_SYNC_NEGEDGE; - - dss_mgr_set_timings(mgr, &rfbi.vm); -} - -static int rfbi_display_enable(struct omap_dss_device *dssdev) -{ - struct omap_dss_device *out = &rfbi.output; - int r; - - if (!out->dispc_channel_connected) { - DSSERR("failed to enable display: no output/manager\n"); - return -ENODEV; - } - - r = rfbi_runtime_get(); - if (r) - return r; - - r = dss_mgr_register_framedone_handler(out->manager, - framedone_callback, NULL); - if (r) { - DSSERR("can't get FRAMEDONE irq\n"); - goto err1; - } - - rfbi_config_lcd_manager(dssdev); - - rfbi_configure_bus(dssdev->phy.rfbi.channel, rfbi.pixel_size, - rfbi.data_lines); - - rfbi_set_timings(dssdev->phy.rfbi.channel, &rfbi.intf_timings); - - return 0; -err1: - rfbi_runtime_put(); - return r; -} - -static void rfbi_display_disable(struct omap_dss_device *dssdev) -{ - struct omap_dss_device *out = &rfbi.output; - - dss_mgr_unregister_framedone_handler(out->manager, - framedone_callback, NULL); - - rfbi_runtime_put(); -} - -static int rfbi_init_display(struct omap_dss_device *dssdev) -{ - rfbi.dssdev[dssdev->phy.rfbi.channel] = dssdev; - return 0; -} - -static void rfbi_init_output(struct platform_device *pdev) -{ - struct omap_dss_device *out = &rfbi.output; - - out->dev = &pdev->dev; - out->id = OMAP_DSS_OUTPUT_DBI; - out->output_type = OMAP_DISPLAY_TYPE_DBI; - out->name = "rfbi.0"; - out->dispc_channel = OMAP_DSS_CHANNEL_LCD; - out->owner = THIS_MODULE; - - omapdss_register_output(out); -} - -static void rfbi_uninit_output(struct platform_device *pdev) -{ - struct omap_dss_device *out = &rfbi.output; - - omapdss_unregister_output(out); -} - -/* RFBI HW IP initialisation */ -static int rfbi_bind(struct device *dev, struct device *master, void *data) -{ - struct platform_device *pdev = to_platform_device(dev); - u32 rev; - struct resource *rfbi_mem; - struct clk *clk; - int r; - - rfbi.pdev = pdev; - - sema_init(&rfbi.bus_lock, 1); - - rfbi_mem = platform_get_resource(rfbi.pdev, IORESOURCE_MEM, 0); - if (!rfbi_mem) { - DSSERR("can't get IORESOURCE_MEM RFBI\n"); - return -EINVAL; - } - - rfbi.base = devm_ioremap(&pdev->dev, rfbi_mem->start, - resource_size(rfbi_mem)); - if (!rfbi.base) { - DSSERR("can't ioremap RFBI\n"); - return -ENOMEM; - } - - clk = clk_get(&pdev->dev, "ick"); - if (IS_ERR(clk)) { - DSSERR("can't get ick\n"); - return PTR_ERR(clk); - } - - rfbi.l4_khz = clk_get_rate(clk) / 1000; - - clk_put(clk); - - pm_runtime_enable(&pdev->dev); - - r = rfbi_runtime_get(); - if (r) - goto err_runtime_get; - - msleep(10); - - rev = rfbi_read_reg(RFBI_REVISION); - dev_dbg(&pdev->dev, "OMAP RFBI rev %d.%d\n", - FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); - - rfbi_runtime_put(); - - dss_debugfs_create_file("rfbi", rfbi_dump_regs); - - rfbi_init_output(pdev); - - return 0; - -err_runtime_get: - pm_runtime_disable(&pdev->dev); - return r; -} - -static void rfbi_unbind(struct device *dev, struct device *master, void *data) -{ - struct platform_device *pdev = to_platform_device(dev); - - rfbi_uninit_output(pdev); - - pm_runtime_disable(&pdev->dev); - - return 0; -} - -static const struct component_ops rfbi_component_ops = { - .bind = rfbi_bind, - .unbind = rfbi_unbind, -}; - -static int rfbi_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &rfbi_component_ops); -} - -static int rfbi_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &rfbi_component_ops); - return 0; -} - -static int rfbi_runtime_suspend(struct device *dev) -{ - dispc_runtime_put(); - - return 0; -} - -static int rfbi_runtime_resume(struct device *dev) -{ - int r; - - r = dispc_runtime_get(); - if (r < 0) - return r; - - return 0; -} - -static const struct dev_pm_ops rfbi_pm_ops = { - .runtime_suspend = rfbi_runtime_suspend, - .runtime_resume = rfbi_runtime_resume, -}; - -static struct platform_driver omap_rfbihw_driver = { - .probe = rfbi_probe, - .remove = rfbi_remove, - .driver = { - .name = "omapdss_rfbi", - .pm = &rfbi_pm_ops, - .suppress_bind_attrs = true, - }, -}; - -int __init rfbi_init_platform_driver(void) -{ - return platform_driver_register(&omap_rfbihw_driver); -} - -void rfbi_uninit_platform_driver(void) -{ - platform_driver_unregister(&omap_rfbihw_driver); -} diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c b/drivers/gpu/drm/omapdrm/dss/sdi.c index 0620b9f8c231..d18ad58c5a19 100644 --- a/drivers/gpu/drm/omapdrm/dss/sdi.c +++ b/drivers/gpu/drm/omapdrm/dss/sdi.c @@ -27,7 +27,6 @@ #include <linux/platform_device.h> #include <linux/string.h> #include <linux/of.h> -#include <linux/component.h> #include "omapdss.h" #include "dss.h" @@ -253,11 +252,6 @@ static int sdi_check_timings(struct omap_dss_device *dssdev, return 0; } -static void sdi_set_datapairs(struct omap_dss_device *dssdev, int datapairs) -{ - sdi.datapairs = datapairs; -} - static int sdi_init_regulator(void) { struct regulator *vdds_sdi; @@ -327,8 +321,6 @@ static const struct omapdss_sdi_ops sdi_ops = { .check_timings = sdi_check_timings, .set_timings = sdi_set_timings, .get_timings = sdi_get_timings, - - .set_datapairs = sdi_set_datapairs, }; static void sdi_init_output(struct platform_device *pdev) @@ -355,59 +347,6 @@ static void sdi_uninit_output(struct platform_device *pdev) omapdss_unregister_output(out); } -static int sdi_bind(struct device *dev, struct device *master, void *data) -{ - struct platform_device *pdev = to_platform_device(dev); - - sdi.pdev = pdev; - - sdi_init_output(pdev); - - return 0; -} - -static void sdi_unbind(struct device *dev, struct device *master, void *data) -{ - struct platform_device *pdev = to_platform_device(dev); - - sdi_uninit_output(pdev); -} - -static const struct component_ops sdi_component_ops = { - .bind = sdi_bind, - .unbind = sdi_unbind, -}; - -static int sdi_probe(struct platform_device *pdev) -{ - return component_add(&pdev->dev, &sdi_component_ops); -} - -static int sdi_remove(struct platform_device *pdev) -{ - component_del(&pdev->dev, &sdi_component_ops); - return 0; -} - -static struct platform_driver omap_sdi_driver = { - .probe = sdi_probe, - .remove = sdi_remove, - .driver = { - .name = "omapdss_sdi", - .suppress_bind_attrs = true, - }, -}; - -int __init sdi_init_platform_driver(void) -{ - return platform_driver_register(&omap_sdi_driver); -} - -void sdi_uninit_platform_driver(void) -{ - platform_driver_unregister(&omap_sdi_driver); -} - int sdi_init_port(struct platform_device *pdev, struct device_node *port) { struct device_node *ep; diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c b/drivers/gpu/drm/omapdrm/dss/venc.c index 19d14957f566..a6bfb3918b8d 100644 --- a/drivers/gpu/drm/omapdrm/dss/venc.c +++ b/drivers/gpu/drm/omapdrm/dss/venc.c @@ -616,26 +616,6 @@ err: return r; } -static void venc_set_type(struct omap_dss_device *dssdev, - enum omap_dss_venc_type type) -{ - mutex_lock(&venc.venc_lock); - - venc.type = type; - - mutex_unlock(&venc.venc_lock); -} - -static void venc_invert_vid_out_polarity(struct omap_dss_device *dssdev, - bool invert_polarity) -{ - mutex_lock(&venc.venc_lock); - - venc.invert_polarity = invert_polarity; - - mutex_unlock(&venc.venc_lock); -} - static int venc_init_regulator(void) { struct regulator *vdda_dac; @@ -643,11 +623,7 @@ static int venc_init_regulator(void) if (venc.vdda_dac_reg != NULL) return 0; - if (venc.pdev->dev.of_node) - vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda"); - else - vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda_dac"); - + vdda_dac = devm_regulator_get(&venc.pdev->dev, "vdda"); if (IS_ERR(vdda_dac)) { if (PTR_ERR(vdda_dac) != -EPROBE_DEFER) DSSERR("can't get VDDA_DAC regulator\n"); @@ -783,9 +759,6 @@ static const struct omapdss_atv_ops venc_ops = { .set_timings = venc_set_timings, .get_timings = venc_get_timings, - .set_type = venc_set_type, - .invert_vid_out_polarity = venc_invert_vid_out_polarity, - .set_wss = venc_set_wss, .get_wss = venc_get_wss, }; @@ -869,17 +842,9 @@ static int venc_bind(struct device *dev, struct device *master, void *data) venc.wss_data = 0; venc_mem = platform_get_resource(venc.pdev, IORESOURCE_MEM, 0); - if (!venc_mem) { - DSSERR("can't get IORESOURCE_MEM VENC\n"); - return -EINVAL; - } - - venc.base = devm_ioremap(&pdev->dev, venc_mem->start, - resource_size(venc_mem)); - if (!venc.base) { - DSSERR("can't ioremap VENC\n"); - return -ENOMEM; - } + venc.base = devm_ioremap_resource(&pdev->dev, venc_mem); + if (IS_ERR(venc.base)) + return PTR_ERR(venc.base); r = venc_get_clocks(pdev); if (r) @@ -896,12 +861,10 @@ static int venc_bind(struct device *dev, struct device *master, void *data) venc_runtime_put(); - if (pdev->dev.of_node) { - r = venc_probe_of(pdev); - if (r) { - DSSERR("Invalid DT data\n"); - goto err_probe_of; - } + r = venc_probe_of(pdev); + if (r) { + DSSERR("Invalid DT data\n"); + goto err_probe_of; } dss_debugfs_create_file("venc", venc_dump_regs); diff --git a/drivers/gpu/drm/omapdrm/dss/video-pll.c b/drivers/gpu/drm/omapdrm/dss/video-pll.c index 7429de928d4e..fbd1263a29a4 100644 --- a/drivers/gpu/drm/omapdrm/dss/video-pll.c +++ b/drivers/gpu/drm/omapdrm/dss/video-pll.c @@ -150,33 +150,17 @@ struct dss_pll *dss_video_pll_init(struct platform_device *pdev, int id, /* PLL CONTROL */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, reg_name[id]); - if (!res) { - dev_err(&pdev->dev, - "missing platform resource data for pll%d\n", id); - return ERR_PTR(-ENODEV); - } - pll_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(pll_base)) { - dev_err(&pdev->dev, "failed to ioremap pll%d reg_name\n", id); + if (IS_ERR(pll_base)) return ERR_CAST(pll_base); - } /* CLOCK CONTROL */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, clkctrl_name[id]); - if (!res) { - dev_err(&pdev->dev, - "missing platform resource data for pll%d\n", id); - return ERR_PTR(-ENODEV); - } - clkctrl_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(clkctrl_base)) { - dev_err(&pdev->dev, "failed to ioremap pll%d clkctrl\n", id); + if (IS_ERR(clkctrl_base)) return ERR_CAST(clkctrl_base); - } /* CLKIN */ diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index dccd03726796..dd0ef40ca469 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -343,6 +343,19 @@ static void omap_crtc_destroy(struct drm_crtc *crtc) kfree(omap_crtc); } +static void omap_crtc_arm_event(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + + WARN_ON(omap_crtc->pending); + omap_crtc->pending = true; + + if (crtc->state->event) { + omap_crtc->event = crtc->state->event; + crtc->state->event = NULL; + } +} + static void omap_crtc_enable(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); @@ -355,8 +368,7 @@ static void omap_crtc_enable(struct drm_crtc *crtc) ret = drm_crtc_vblank_get(crtc); WARN_ON(ret != 0); - WARN_ON(omap_crtc->pending); - omap_crtc->pending = true; + omap_crtc_arm_event(crtc); spin_unlock_irq(&crtc->dev->event_lock); } @@ -366,6 +378,13 @@ static void omap_crtc_disable(struct drm_crtc *crtc) DBG("%s", omap_crtc->name); + spin_lock_irq(&crtc->dev->event_lock); + if (crtc->state->event) { + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + spin_unlock_irq(&crtc->dev->event_lock); + drm_crtc_vblank_off(crtc); } @@ -473,12 +492,7 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc, spin_lock_irq(&crtc->dev->event_lock); priv->dispc_ops->mgr_go(omap_crtc->channel); - - WARN_ON(omap_crtc->pending); - omap_crtc->pending = true; - - if (crtc->state->event) - omap_crtc->event = crtc->state->event; + omap_crtc_arm_event(crtc); spin_unlock_irq(&crtc->dev->event_lock); } diff --git a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c index 3cab06661a08..1dd3dafc59af 100644 --- a/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c +++ b/drivers/gpu/drm/omapdrm/omap_dmm_tiler.c @@ -388,7 +388,7 @@ struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, uint16_t w, u32 min_align = 128; int ret; unsigned long flags; - size_t slot_bytes; + u32 slot_bytes; BUG_ON(!validfmt(fmt)); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 663e930a7b0f..022029ea6972 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,13 +54,6 @@ 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) { @@ -81,15 +74,14 @@ 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 @@ -108,101 +100,28 @@ static void omap_atomic_complete(struct omap_atomic_state_commit *commit) drm_atomic_helper_commit_planes(dev, old_state, 0); + drm_atomic_helper_commit_hw_done(old_state); + + /* + * Wait for completion of the page flips to ensure that old buffers + * can't be touched by the hardware anymore before cleaning up planes. + */ 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); - - wake_up_all(&priv->commit.wait); - - 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; - - spin_lock(&priv->commit.lock); - pending = priv->commit.pending & commit->crtcs; - spin_unlock(&priv->commit.lock); - - 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; - } - - 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. - */ - 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); - - return 0; - -error: - drm_atomic_helper_cleanup_planes(dev, state); - return ret; -} +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 +133,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 +188,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 +314,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); @@ -447,53 +377,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 +405,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), @@ -608,6 +493,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, @@ -643,9 +529,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; @@ -671,11 +565,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); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 7a4c57eb6536..4bd1e9070b31 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -21,9 +21,8 @@ #define __OMAP_DRV_H__ #include <linux/module.h> -#include <linux/platform_data/omap_drm.h> #include <linux/types.h> -#include <linux/wait.h> +#include <linux/workqueue.h> #include <drm/drmP.h> #include <drm/drm_crtc_helper.h> @@ -39,15 +38,6 @@ struct omap_drm_usergart; -/* parameters which describe (unrotated) coordinates of scanout within a fb: */ -struct omap_drm_window { - uint32_t rotation; - int32_t crtc_x, crtc_y; /* signed because can be offscreen */ - uint32_t crtc_w, crtc_h; - uint32_t src_x, src_y; - uint32_t src_w, src_h; -}; - /* For KMS code that needs to wait for a certain # of IRQs: */ struct omap_irq_wait; @@ -93,13 +83,6 @@ struct omap_drm_private { spinlock_t wait_lock; /* protects the wait_list */ struct list_head wait_list; /* list of omap_irq_wait */ uint32_t irq_mask; /* enabled irqs in addition to wait_list */ - - /* atomic commit */ - struct { - wait_queue_head_t wait; - u32 pending; - spinlock_t lock; /* Protects commit.pending */ - } commit; }; @@ -158,8 +141,6 @@ struct drm_encoder *omap_connector_attached_encoder( struct drm_connector *connector); bool omap_connector_get_hdmi_mode(struct drm_connector *connector); -uint32_t omap_framebuffer_get_formats(uint32_t *pixel_formats, - uint32_t max_formats, enum omap_color_mode supported_modes); struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd); struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, @@ -167,7 +148,7 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, int omap_framebuffer_pin(struct drm_framebuffer *fb); void omap_framebuffer_unpin(struct drm_framebuffer *fb); void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, - struct omap_drm_window *win, struct omap_overlay_info *info); + struct drm_plane_state *state, struct omap_overlay_info *info); struct drm_connector *omap_framebuffer_get_next_connector( struct drm_framebuffer *fb, struct drm_connector *from); bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb); @@ -191,24 +172,18 @@ int omap_gem_mmap(struct file *filp, struct vm_area_struct *vma); int omap_gem_mmap_obj(struct drm_gem_object *obj, struct vm_area_struct *vma); int omap_gem_fault(struct vm_fault *vmf); -int omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op); -int omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op op); -int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op); -int omap_gem_op_async(struct drm_gem_object *obj, enum omap_gem_op op, - void (*fxn)(void *arg), void *arg); int omap_gem_roll(struct drm_gem_object *obj, uint32_t roll); -void omap_gem_cpu_sync(struct drm_gem_object *obj, int pgoff); -void omap_gem_dma_sync(struct drm_gem_object *obj, +void omap_gem_cpu_sync_page(struct drm_gem_object *obj, int pgoff); +void omap_gem_dma_sync_buffer(struct drm_gem_object *obj, enum dma_data_direction dir); -int omap_gem_get_paddr(struct drm_gem_object *obj, - dma_addr_t *paddr, bool remap); -void omap_gem_put_paddr(struct drm_gem_object *obj); +int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr); +void omap_gem_unpin(struct drm_gem_object *obj); int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages, bool remap); int omap_gem_put_pages(struct drm_gem_object *obj); uint32_t omap_gem_flags(struct drm_gem_object *obj); -int omap_gem_rotated_paddr(struct drm_gem_object *obj, uint32_t orient, - int x, int y, dma_addr_t *paddr); +int omap_gem_rotated_dma_addr(struct drm_gem_object *obj, uint32_t orient, + int x, int y, dma_addr_t *dma_addr); uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj); size_t omap_gem_mmap_size(struct drm_gem_object *obj); int omap_gem_tiled_stride(struct drm_gem_object *obj, uint32_t orient); diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c index 5ca0537bb427..ddf7a457951b 100644 --- a/drivers/gpu/drm/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -29,52 +29,34 @@ * framebuffer funcs */ -/* DSS to DRM formats mapping */ -static const struct { - enum omap_color_mode dss_format; - uint32_t pixel_format; -} formats[] = { +static const u32 formats[] = { /* 16bpp [A]RGB: */ - { OMAP_DSS_COLOR_RGB16, DRM_FORMAT_RGB565 }, /* RGB16-565 */ - { OMAP_DSS_COLOR_RGB12U, DRM_FORMAT_RGBX4444 }, /* RGB12x-4444 */ - { OMAP_DSS_COLOR_RGBX16, DRM_FORMAT_XRGB4444 }, /* xRGB12-4444 */ - { OMAP_DSS_COLOR_RGBA16, DRM_FORMAT_RGBA4444 }, /* RGBA12-4444 */ - { OMAP_DSS_COLOR_ARGB16, DRM_FORMAT_ARGB4444 }, /* ARGB16-4444 */ - { OMAP_DSS_COLOR_XRGB16_1555, DRM_FORMAT_XRGB1555 }, /* xRGB15-1555 */ - { OMAP_DSS_COLOR_ARGB16_1555, DRM_FORMAT_ARGB1555 }, /* ARGB16-1555 */ + DRM_FORMAT_RGB565, /* RGB16-565 */ + DRM_FORMAT_RGBX4444, /* RGB12x-4444 */ + DRM_FORMAT_XRGB4444, /* xRGB12-4444 */ + DRM_FORMAT_RGBA4444, /* RGBA12-4444 */ + DRM_FORMAT_ARGB4444, /* ARGB16-4444 */ + DRM_FORMAT_XRGB1555, /* xRGB15-1555 */ + DRM_FORMAT_ARGB1555, /* ARGB16-1555 */ /* 24bpp RGB: */ - { OMAP_DSS_COLOR_RGB24P, DRM_FORMAT_RGB888 }, /* RGB24-888 */ + DRM_FORMAT_RGB888, /* RGB24-888 */ /* 32bpp [A]RGB: */ - { OMAP_DSS_COLOR_RGBX32, DRM_FORMAT_RGBX8888 }, /* RGBx24-8888 */ - { OMAP_DSS_COLOR_RGB24U, DRM_FORMAT_XRGB8888 }, /* xRGB24-8888 */ - { OMAP_DSS_COLOR_RGBA32, DRM_FORMAT_RGBA8888 }, /* RGBA32-8888 */ - { OMAP_DSS_COLOR_ARGB32, DRM_FORMAT_ARGB8888 }, /* ARGB32-8888 */ + DRM_FORMAT_RGBX8888, /* RGBx24-8888 */ + DRM_FORMAT_XRGB8888, /* xRGB24-8888 */ + DRM_FORMAT_RGBA8888, /* RGBA32-8888 */ + DRM_FORMAT_ARGB8888, /* ARGB32-8888 */ /* YUV: */ - { OMAP_DSS_COLOR_NV12, DRM_FORMAT_NV12 }, - { OMAP_DSS_COLOR_YUV2, DRM_FORMAT_YUYV }, - { OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY }, + DRM_FORMAT_NV12, + DRM_FORMAT_YUYV, + DRM_FORMAT_UYVY, }; -/* convert from overlay's pixel formats bitmask to an array of fourcc's */ -uint32_t omap_framebuffer_get_formats(uint32_t *pixel_formats, - uint32_t max_formats, enum omap_color_mode supported_modes) -{ - uint32_t nformats = 0; - int i = 0; - - for (i = 0; i < ARRAY_SIZE(formats) && nformats < max_formats; i++) - if (formats[i].dss_format & supported_modes) - pixel_formats[nformats++] = formats[i].pixel_format; - - return nformats; -} - /* per-plane info for the fb: */ struct plane { struct drm_gem_object *bo; uint32_t pitch; uint32_t offset; - dma_addr_t paddr; + dma_addr_t dma_addr; }; #define to_omap_framebuffer(x) container_of(x, struct omap_framebuffer, base) @@ -83,9 +65,8 @@ struct omap_framebuffer { struct drm_framebuffer base; int pin_count; const struct drm_format_info *format; - enum omap_color_mode dss_format; struct plane planes[2]; - /* lock for pinning (pin_count and planes.paddr) */ + /* lock for pinning (pin_count and planes.dma_addr) */ struct mutex lock; }; @@ -130,7 +111,7 @@ static uint32_t get_linear_addr(struct plane *plane, + (x * format->cpp[n] / (n == 0 ? 1 : format->hsub)) + (y * plane->pitch / (n == 0 ? 1 : format->vsub)); - return plane->paddr + offset; + return plane->dma_addr + offset; } bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb) @@ -141,71 +122,95 @@ bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb) return omap_gem_flags(plane->bo) & OMAP_BO_TILED; } +/* Note: DRM rotates counter-clockwise, TILER & DSS rotates clockwise */ +static uint32_t drm_rotation_to_tiler(unsigned int drm_rot) +{ + uint32_t orient; + + switch (drm_rot & DRM_MODE_ROTATE_MASK) { + default: + case DRM_MODE_ROTATE_0: + orient = 0; + break; + case DRM_MODE_ROTATE_90: + orient = MASK_XY_FLIP | MASK_X_INVERT; + break; + case DRM_MODE_ROTATE_180: + orient = MASK_X_INVERT | MASK_Y_INVERT; + break; + case DRM_MODE_ROTATE_270: + orient = MASK_XY_FLIP | MASK_Y_INVERT; + break; + } + + if (drm_rot & DRM_MODE_REFLECT_X) + orient ^= MASK_X_INVERT; + + if (drm_rot & DRM_MODE_REFLECT_Y) + orient ^= MASK_Y_INVERT; + + return orient; +} + /* update ovl info for scanout, handles cases of multi-planar fb's, etc. */ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, - struct omap_drm_window *win, struct omap_overlay_info *info) + struct drm_plane_state *state, struct omap_overlay_info *info) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); const struct drm_format_info *format = omap_fb->format; struct plane *plane = &omap_fb->planes[0]; uint32_t x, y, orient = 0; - info->color_mode = omap_fb->dss_format; + info->fourcc = fb->format->format; - info->pos_x = win->crtc_x; - info->pos_y = win->crtc_y; - info->out_width = win->crtc_w; - info->out_height = win->crtc_h; - info->width = win->src_w; - info->height = win->src_h; + info->pos_x = state->crtc_x; + info->pos_y = state->crtc_y; + info->out_width = state->crtc_w; + info->out_height = state->crtc_h; + info->width = state->src_w >> 16; + info->height = state->src_h >> 16; - x = win->src_x; - y = win->src_y; + /* DSS driver wants the w & h in rotated orientation */ + if (drm_rotation_90_or_270(state->rotation)) + swap(info->width, info->height); - if (omap_gem_flags(plane->bo) & OMAP_BO_TILED) { - uint32_t w = win->src_w; - uint32_t h = win->src_h; + x = state->src_x >> 16; + y = state->src_y >> 16; - switch (win->rotation & DRM_MODE_ROTATE_MASK) { - default: - dev_err(fb->dev->dev, "invalid rotation: %02x", - (uint32_t)win->rotation); - /* fallthru to default to no rotation */ - case 0: - case DRM_MODE_ROTATE_0: - orient = 0; - break; - case DRM_MODE_ROTATE_90: - orient = MASK_XY_FLIP | MASK_X_INVERT; - break; - case DRM_MODE_ROTATE_180: - orient = MASK_X_INVERT | MASK_Y_INVERT; - break; - case DRM_MODE_ROTATE_270: - orient = MASK_XY_FLIP | MASK_Y_INVERT; - break; + if (omap_gem_flags(plane->bo) & OMAP_BO_TILED) { + uint32_t w = state->src_w >> 16; + uint32_t h = state->src_h >> 16; + + orient = drm_rotation_to_tiler(state->rotation); + + /* + * omap_gem_rotated_paddr() wants the x & y in tiler units. + * Usually tiler unit size is the same as the pixel size, except + * for YUV422 formats, for which the tiler unit size is 32 bits + * and pixel size is 16 bits. + */ + if (fb->format->format == DRM_FORMAT_UYVY || + fb->format->format == DRM_FORMAT_YUYV) { + x /= 2; + w /= 2; } - if (win->rotation & DRM_MODE_REFLECT_X) - orient ^= MASK_X_INVERT; - - if (win->rotation & DRM_MODE_REFLECT_Y) - orient ^= MASK_Y_INVERT; - - /* adjust x,y offset for flip/invert: */ - if (orient & MASK_XY_FLIP) - swap(w, h); + /* adjust x,y offset for invert: */ if (orient & MASK_Y_INVERT) y += h - 1; if (orient & MASK_X_INVERT) x += w - 1; - omap_gem_rotated_paddr(plane->bo, orient, x, y, &info->paddr); + /* Note: x and y are in TILER units, not pixels */ + omap_gem_rotated_dma_addr(plane->bo, orient, x, y, + &info->paddr); info->rotation_type = OMAP_DSS_ROT_TILER; + info->rotation = state->rotation ?: DRM_MODE_ROTATE_0; + /* Note: stride in TILER units, not pixels */ info->screen_width = omap_gem_tiled_stride(plane->bo, orient); } else { - switch (win->rotation & DRM_MODE_ROTATE_MASK) { + switch (state->rotation & DRM_MODE_ROTATE_MASK) { case 0: case DRM_MODE_ROTATE_0: /* OK */ @@ -214,26 +219,26 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, default: dev_warn(fb->dev->dev, "rotation '%d' ignored for non-tiled fb\n", - win->rotation); - win->rotation = 0; + state->rotation); break; } info->paddr = get_linear_addr(plane, format, 0, x, y); - info->rotation_type = OMAP_DSS_ROT_DMA; + info->rotation_type = OMAP_DSS_ROT_NONE; + info->rotation = DRM_MODE_ROTATE_0; info->screen_width = plane->pitch; } /* convert to pixels: */ info->screen_width /= format->cpp[0]; - if (omap_fb->dss_format == OMAP_DSS_COLOR_NV12) { + if (fb->format->format == DRM_FORMAT_NV12) { plane = &omap_fb->planes[1]; if (info->rotation_type == OMAP_DSS_ROT_TILER) { WARN_ON(!(omap_gem_flags(plane->bo) & OMAP_BO_TILED)); - omap_gem_rotated_paddr(plane->bo, orient, - x/2, y/2, &info->p_uv_addr); + omap_gem_rotated_dma_addr(plane->bo, orient, x/2, y/2, + &info->p_uv_addr); } else { info->p_uv_addr = get_linear_addr(plane, format, 1, x, y); } @@ -258,10 +263,10 @@ int omap_framebuffer_pin(struct drm_framebuffer *fb) for (i = 0; i < n; i++) { struct plane *plane = &omap_fb->planes[i]; - ret = omap_gem_get_paddr(plane->bo, &plane->paddr, true); + ret = omap_gem_pin(plane->bo, &plane->dma_addr); if (ret) goto fail; - omap_gem_dma_sync(plane->bo, DMA_TO_DEVICE); + omap_gem_dma_sync_buffer(plane->bo, DMA_TO_DEVICE); } omap_fb->pin_count++; @@ -273,8 +278,8 @@ int omap_framebuffer_pin(struct drm_framebuffer *fb) fail: for (i--; i >= 0; i--) { struct plane *plane = &omap_fb->planes[i]; - omap_gem_put_paddr(plane->bo); - plane->paddr = 0; + omap_gem_unpin(plane->bo); + plane->dma_addr = 0; } mutex_unlock(&omap_fb->lock); @@ -299,8 +304,8 @@ void omap_framebuffer_unpin(struct drm_framebuffer *fb) for (i = 0; i < n; i++) { struct plane *plane = &omap_fb->planes[i]; - omap_gem_put_paddr(plane->bo); - plane->paddr = 0; + omap_gem_unpin(plane->bo); + plane->dma_addr = 0; } mutex_unlock(&omap_fb->lock); @@ -386,7 +391,6 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, const struct drm_format_info *format = NULL; struct omap_framebuffer *omap_fb = NULL; struct drm_framebuffer *fb = NULL; - enum omap_color_mode dss_format = 0; unsigned int pitch = mode_cmd->pitches[0]; int ret, i; @@ -397,13 +401,11 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, format = drm_format_info(mode_cmd->pixel_format); for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (formats[i].pixel_format == mode_cmd->pixel_format) { - dss_format = formats[i].dss_format; + if (formats[i] == mode_cmd->pixel_format) break; - } } - if (!format || !dss_format) { + if (!format || i == ARRAY_SIZE(formats)) { dev_dbg(dev->dev, "unsupported pixel format: %4.4s\n", (char *)&mode_cmd->pixel_format); ret = -EINVAL; @@ -418,7 +420,6 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, fb = &omap_fb->base; omap_fb->format = format; - omap_fb->dss_format = dss_format; mutex_init(&omap_fb->lock); /* @@ -449,7 +450,7 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, if (size > omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i]) { dev_dbg(dev->dev, - "provided buffer object is too small! %d < %d\n", + "provided buffer object is too small! %zu < %d\n", bos[i]->size - mode_cmd->offsets[i], size); ret = -EINVAL; goto fail; @@ -458,7 +459,7 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, plane->bo = bos[i]; plane->offset = mode_cmd->offsets[i]; plane->pitch = pitch; - plane->paddr = 0; + plane->dma_addr = 0; } drm_helper_mode_fill_fb_struct(dev, fb, mode_cmd); diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 4e89dd537862..daf81a0a2899 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -106,7 +106,7 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, union omap_gem_size gsize; struct fb_info *fbi = NULL; struct drm_mode_fb_cmd2 mode_cmd = {0}; - dma_addr_t paddr; + dma_addr_t dma_addr; int ret; sizes->surface_bpp = 32; @@ -162,10 +162,9 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, * to it). Then we just need to be sure that we are able to re- * pin it in case of an opps. */ - ret = omap_gem_get_paddr(fbdev->bo, &paddr, true); + ret = omap_gem_pin(fbdev->bo, &dma_addr); if (ret) { - dev_err(dev->dev, - "could not map (paddr)! Skipping framebuffer alloc\n"); + dev_err(dev->dev, "could not pin framebuffer\n"); ret = -ENOMEM; goto fail; } @@ -193,11 +192,11 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth); drm_fb_helper_fill_var(fbi, helper, sizes->fb_width, sizes->fb_height); - dev->mode_config.fb_base = paddr; + dev->mode_config.fb_base = dma_addr; fbi->screen_base = omap_gem_vaddr(fbdev->bo); fbi->screen_size = fbdev->bo->size; - fbi->fix.smem_start = paddr; + fbi->fix.smem_start = dma_addr; fbi->fix.smem_len = fbdev->bo->size; /* if we have DMM, then we can use it for scrolling by just @@ -303,8 +302,8 @@ void omap_fbdev_free(struct drm_device *dev) fbdev = to_omap_fbdev(priv->fbdev); - /* release the ref taken in omap_fbdev_create() */ - omap_gem_put_paddr(fbdev->bo); + /* unpin the GEM object pinned in omap_fbdev_create() */ + omap_gem_unpin(fbdev->bo); /* this will free the backing object */ if (fbdev->fb) diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 68a75b829b71..5c5c86ddd6f4 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -50,7 +50,7 @@ struct omap_gem_object { uint32_t roll; /** - * paddr contains the buffer DMA address. It is valid for + * dma_addr contains the buffer DMA address. It is valid for * * - buffers allocated through the DMA mapping API (with the * OMAP_BO_MEM_DMA_API flag set) @@ -58,24 +58,24 @@ struct omap_gem_object { * - buffers imported from dmabuf (with the OMAP_BO_MEM_DMABUF flag set) * if they are physically contiguous (when sgt->orig_nents == 1) * - * - buffers mapped through the TILER when paddr_cnt is not zero, in + * - buffers mapped through the TILER when dma_addr_cnt is not zero, in * which case the DMA address points to the TILER aperture * * Physically contiguous buffers have their DMA address equal to the * physical address as we don't remap those buffers through the TILER. * * Buffers mapped to the TILER have their DMA address pointing to the - * TILER aperture. As TILER mappings are refcounted (through paddr_cnt) - * the DMA address must be accessed through omap_get_get_paddr() to - * ensure that the mapping won't disappear unexpectedly. References must - * be released with omap_gem_put_paddr(). + * TILER aperture. As TILER mappings are refcounted (through + * dma_addr_cnt) the DMA address must be accessed through omap_gem_pin() + * to ensure that the mapping won't disappear unexpectedly. References + * must be released with omap_gem_unpin(). */ - dma_addr_t paddr; + dma_addr_t dma_addr; /** - * # of users of paddr + * # of users of dma_addr */ - uint32_t paddr_cnt; + uint32_t dma_addr_cnt; /** * If the buffer has been imported from a dmabuf the OMAP_DB_DMABUF flag @@ -95,25 +95,12 @@ struct omap_gem_object { struct page **pages; /** addresses corresponding to pages in above array */ - dma_addr_t *addrs; + dma_addr_t *dma_addrs; /** * Virtual address, if mapped. */ void *vaddr; - - /** - * sync-object allocated on demand (if needed) - * - * Per-buffer sync-object for tracking pending and completed hw/dma - * read and write operations. - */ - struct { - uint32_t write_pending; - uint32_t write_complete; - uint32_t read_pending; - uint32_t read_complete; - } *sync; }; #define to_omap_bo(x) container_of(x, struct omap_gem_object, base) @@ -132,7 +119,7 @@ struct omap_gem_object { #define NUM_USERGART_ENTRIES 2 struct omap_drm_usergart_entry { struct tiler_block *block; /* the reserved tiler block */ - dma_addr_t paddr; + dma_addr_t dma_addr; struct drm_gem_object *obj; /* the current pinned obj */ pgoff_t obj_pgoff; /* page offset of obj currently mapped in */ @@ -195,7 +182,7 @@ static void evict_entry(struct drm_gem_object *obj, size_t size = PAGE_SIZE * n; loff_t off = mmap_offset(obj) + (entry->obj_pgoff << PAGE_SHIFT); - const int m = 1 + ((omap_obj->width << fmt) / PAGE_SIZE); + const int m = DIV_ROUND_UP(omap_obj->width << fmt, PAGE_SIZE); if (m > 1) { int i; @@ -267,7 +254,7 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj) for (i = 0; i < npages; i++) { addrs[i] = dma_map_page(dev->dev, pages[i], - 0, PAGE_SIZE, DMA_BIDIRECTIONAL); + 0, PAGE_SIZE, DMA_TO_DEVICE); if (dma_mapping_error(dev->dev, addrs[i])) { dev_warn(dev->dev, @@ -275,7 +262,7 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj) for (i = i - 1; i >= 0; --i) { dma_unmap_page(dev->dev, addrs[i], - PAGE_SIZE, DMA_BIDIRECTIONAL); + PAGE_SIZE, DMA_TO_DEVICE); } ret = -ENOMEM; @@ -290,7 +277,7 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj) } } - omap_obj->addrs = addrs; + omap_obj->dma_addrs = addrs; omap_obj->pages = pages; return 0; @@ -329,22 +316,17 @@ static int get_pages(struct drm_gem_object *obj, struct page ***pages) static void omap_gem_detach_pages(struct drm_gem_object *obj) { struct omap_gem_object *omap_obj = to_omap_bo(obj); + unsigned int npages = obj->size >> PAGE_SHIFT; + unsigned int i; - /* for non-cached buffers, ensure the new pages are clean because - * DSS, GPU, etc. are not cache coherent: - */ - if (omap_obj->flags & (OMAP_BO_WC|OMAP_BO_UNCACHED)) { - int i, npages = obj->size >> PAGE_SHIFT; - for (i = 0; i < npages; i++) { - if (omap_obj->addrs[i]) - dma_unmap_page(obj->dev->dev, - omap_obj->addrs[i], - PAGE_SIZE, DMA_BIDIRECTIONAL); - } + for (i = 0; i < npages; i++) { + if (omap_obj->dma_addrs[i]) + dma_unmap_page(obj->dev->dev, omap_obj->dma_addrs[i], + PAGE_SIZE, DMA_TO_DEVICE); } - kfree(omap_obj->addrs); - omap_obj->addrs = NULL; + kfree(omap_obj->dma_addrs); + omap_obj->dma_addrs = NULL; drm_gem_put_pages(obj, omap_obj->pages, true, false); omap_obj->pages = NULL; @@ -401,11 +383,11 @@ static int fault_1d(struct drm_gem_object *obj, pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT; if (omap_obj->pages) { - omap_gem_cpu_sync(obj, pgoff); + omap_gem_cpu_sync_page(obj, pgoff); pfn = page_to_pfn(omap_obj->pages[pgoff]); } else { BUG_ON(!is_contiguous(omap_obj)); - pfn = (omap_obj->paddr >> PAGE_SHIFT) + pgoff; + pfn = (omap_obj->dma_addr >> PAGE_SHIFT) + pgoff; } VERB("Inserting %p pfn %lx, pa %lx", (void *)vmf->address, @@ -442,7 +424,7 @@ static int fault_2d(struct drm_gem_object *obj, * into account in some of the math, so figure out virtual stride * in pages */ - const int m = 1 + ((omap_obj->width << fmt) / PAGE_SIZE); + const int m = DIV_ROUND_UP(omap_obj->width << fmt, PAGE_SIZE); /* We don't use vmf->pgoff since that has the fake offset: */ pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT; @@ -498,7 +480,7 @@ static int fault_2d(struct drm_gem_object *obj, return ret; } - pfn = entry->paddr >> PAGE_SHIFT; + pfn = entry->dma_addr >> PAGE_SHIFT; VERB("Inserting %p pfn %lx, pa %lx", (void *)vmf->address, pfn, pfn << PAGE_SHIFT); @@ -732,77 +714,92 @@ fail: * Memory Management & DMA Sync */ -/** - * shmem buffers that are mapped cached can simulate coherency via using - * page faulting to keep track of dirty pages +/* + * shmem buffers that are mapped cached are not coherent. + * + * We keep track of dirty pages using page faulting to perform cache management. + * When a page is mapped to the CPU in read/write mode the device can't access + * it and omap_obj->dma_addrs[i] is NULL. When a page is mapped to the device + * the omap_obj->dma_addrs[i] is set to the DMA address, and the page is + * unmapped from the CPU. */ static inline bool is_cached_coherent(struct drm_gem_object *obj) { struct omap_gem_object *omap_obj = to_omap_bo(obj); - return (omap_obj->flags & OMAP_BO_MEM_SHMEM) && - ((omap_obj->flags & OMAP_BO_CACHE_MASK) == OMAP_BO_CACHED); + return !((omap_obj->flags & OMAP_BO_MEM_SHMEM) && + ((omap_obj->flags & OMAP_BO_CACHE_MASK) == OMAP_BO_CACHED)); } /* Sync the buffer for CPU access.. note pages should already be * attached, ie. omap_gem_get_pages() */ -void omap_gem_cpu_sync(struct drm_gem_object *obj, int pgoff) +void omap_gem_cpu_sync_page(struct drm_gem_object *obj, int pgoff) { struct drm_device *dev = obj->dev; struct omap_gem_object *omap_obj = to_omap_bo(obj); - if (is_cached_coherent(obj) && omap_obj->addrs[pgoff]) { - dma_unmap_page(dev->dev, omap_obj->addrs[pgoff], - PAGE_SIZE, DMA_BIDIRECTIONAL); - omap_obj->addrs[pgoff] = 0; + if (is_cached_coherent(obj)) + return; + + if (omap_obj->dma_addrs[pgoff]) { + dma_unmap_page(dev->dev, omap_obj->dma_addrs[pgoff], + PAGE_SIZE, DMA_TO_DEVICE); + omap_obj->dma_addrs[pgoff] = 0; } } /* sync the buffer for DMA access */ -void omap_gem_dma_sync(struct drm_gem_object *obj, +void omap_gem_dma_sync_buffer(struct drm_gem_object *obj, enum dma_data_direction dir) { struct drm_device *dev = obj->dev; struct omap_gem_object *omap_obj = to_omap_bo(obj); + int i, npages = obj->size >> PAGE_SHIFT; + struct page **pages = omap_obj->pages; + bool dirty = false; - if (is_cached_coherent(obj)) { - int i, npages = obj->size >> PAGE_SHIFT; - struct page **pages = omap_obj->pages; - bool dirty = false; - - for (i = 0; i < npages; i++) { - if (!omap_obj->addrs[i]) { - dma_addr_t addr; - - addr = dma_map_page(dev->dev, pages[i], 0, - PAGE_SIZE, DMA_BIDIRECTIONAL); + if (is_cached_coherent(obj)) + return; - if (dma_mapping_error(dev->dev, addr)) { - dev_warn(dev->dev, - "%s: failed to map page\n", - __func__); - break; - } + for (i = 0; i < npages; i++) { + if (!omap_obj->dma_addrs[i]) { + dma_addr_t addr; - dirty = true; - omap_obj->addrs[i] = addr; + addr = dma_map_page(dev->dev, pages[i], 0, + PAGE_SIZE, dir); + if (dma_mapping_error(dev->dev, addr)) { + dev_warn(dev->dev, "%s: failed to map page\n", + __func__); + break; } - } - if (dirty) { - unmap_mapping_range(obj->filp->f_mapping, 0, - omap_gem_mmap_size(obj), 1); + dirty = true; + omap_obj->dma_addrs[i] = addr; } } + + if (dirty) { + unmap_mapping_range(obj->filp->f_mapping, 0, + omap_gem_mmap_size(obj), 1); + } } -/* Get physical address for DMA.. if 'remap' is true, and the buffer is not - * already contiguous, remap it to pin in physically contiguous memory.. (ie. - * map in TILER) +/** + * omap_gem_pin() - Pin a GEM object in memory + * @obj: the GEM object + * @dma_addr: the DMA address + * + * Pin the given GEM object in memory and fill the dma_addr pointer with the + * object's DMA address. If the buffer is not physically contiguous it will be + * remapped through the TILER to provide a contiguous view. + * + * Pins are reference-counted, calling this function multiple times is allowed + * as long the corresponding omap_gem_unpin() calls are balanced. + * + * Return 0 on success or a negative error code otherwise. */ -int omap_gem_get_paddr(struct drm_gem_object *obj, - dma_addr_t *paddr, bool remap) +int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr) { struct omap_drm_private *priv = obj->dev->dev_private; struct omap_gem_object *omap_obj = to_omap_bo(obj); @@ -810,8 +807,8 @@ int omap_gem_get_paddr(struct drm_gem_object *obj, mutex_lock(&obj->dev->struct_mutex); - if (!is_contiguous(omap_obj) && remap && priv->has_dmm) { - if (omap_obj->paddr_cnt == 0) { + if (!is_contiguous(omap_obj) && priv->has_dmm) { + if (omap_obj->dma_addr_cnt == 0) { struct page **pages; uint32_t npages = obj->size >> PAGE_SHIFT; enum tiler_fmt fmt = gem2fmt(omap_obj->flags); @@ -848,17 +845,17 @@ int omap_gem_get_paddr(struct drm_gem_object *obj, goto fail; } - omap_obj->paddr = tiler_ssptr(block); + omap_obj->dma_addr = tiler_ssptr(block); omap_obj->block = block; - DBG("got paddr: %pad", &omap_obj->paddr); + DBG("got dma address: %pad", &omap_obj->dma_addr); } - omap_obj->paddr_cnt++; + omap_obj->dma_addr_cnt++; - *paddr = omap_obj->paddr; + *dma_addr = omap_obj->dma_addr; } else if (is_contiguous(omap_obj)) { - *paddr = omap_obj->paddr; + *dma_addr = omap_obj->dma_addr; } else { ret = -EINVAL; goto fail; @@ -870,18 +867,23 @@ fail: return ret; } -/* Release physical address, when DMA is no longer being performed.. this - * could potentially unpin and unmap buffers from TILER +/** + * omap_gem_unpin() - Unpin a GEM object from memory + * @obj: the GEM object + * + * Unpin the given GEM object previously pinned with omap_gem_pin(). Pins are + * reference-counted, the actualy unpin will only be performed when the number + * of calls to this function matches the number of calls to omap_gem_pin(). */ -void omap_gem_put_paddr(struct drm_gem_object *obj) +void omap_gem_unpin(struct drm_gem_object *obj) { struct omap_gem_object *omap_obj = to_omap_bo(obj); int ret; mutex_lock(&obj->dev->struct_mutex); - if (omap_obj->paddr_cnt > 0) { - omap_obj->paddr_cnt--; - if (omap_obj->paddr_cnt == 0) { + if (omap_obj->dma_addr_cnt > 0) { + omap_obj->dma_addr_cnt--; + if (omap_obj->dma_addr_cnt == 0) { ret = tiler_unpin(omap_obj->block); if (ret) { dev_err(obj->dev->dev, @@ -892,7 +894,7 @@ void omap_gem_put_paddr(struct drm_gem_object *obj) dev_err(obj->dev->dev, "could not release unmap: %d\n", ret); } - omap_obj->paddr = 0; + omap_obj->dma_addr = 0; omap_obj->block = NULL; } } @@ -904,16 +906,16 @@ void omap_gem_put_paddr(struct drm_gem_object *obj) * specified orientation and x,y offset from top-left corner of buffer * (only valid for tiled 2d buffers) */ -int omap_gem_rotated_paddr(struct drm_gem_object *obj, uint32_t orient, - int x, int y, dma_addr_t *paddr) +int omap_gem_rotated_dma_addr(struct drm_gem_object *obj, uint32_t orient, + int x, int y, dma_addr_t *dma_addr) { struct omap_gem_object *omap_obj = to_omap_bo(obj); int ret = -EINVAL; mutex_lock(&obj->dev->struct_mutex); - if ((omap_obj->paddr_cnt > 0) && omap_obj->block && + if ((omap_obj->dma_addr_cnt > 0) && omap_obj->block && (omap_obj->flags & OMAP_BO_TILED)) { - *paddr = tiler_tsptr(omap_obj->block, orient, x, y); + *dma_addr = tiler_tsptr(omap_obj->block, orient, x, y); ret = 0; } mutex_unlock(&obj->dev->struct_mutex); @@ -934,9 +936,9 @@ int omap_gem_tiled_stride(struct drm_gem_object *obj, uint32_t orient) * increasing the pin count (which we don't really do yet anyways, * because we don't support swapping pages back out). And 'remap' * might not be quite the right name, but I wanted to keep it working - * similarly to omap_gem_get_paddr(). Note though that mutex is not + * similarly to omap_gem_pin(). Note though that mutex is not * aquired if !remap (because this can be called in atomic ctxt), - * but probably omap_gem_get_paddr() should be changed to work in the + * but probably omap_gem_unpin() should be changed to work in the * same way. If !remap, a matching omap_gem_put_pages() call is not * required (and should not be made). */ @@ -1034,7 +1036,7 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m) seq_printf(m, "%08x: %2d (%2d) %08llx %pad (%2d) %p %4d", omap_obj->flags, obj->name, kref_read(&obj->refcount), - off, &omap_obj->paddr, omap_obj->paddr_cnt, + off, &omap_obj->dma_addr, omap_obj->dma_addr_cnt, omap_obj->vaddr, omap_obj->roll); if (omap_obj->flags & OMAP_BO_TILED) { @@ -1046,7 +1048,7 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m) area->p1.x, area->p1.y); } } else { - seq_printf(m, " %d", obj->size); + seq_printf(m, " %zu", obj->size); } seq_printf(m, "\n"); @@ -1071,205 +1073,6 @@ void omap_gem_describe_objects(struct list_head *list, struct seq_file *m) #endif /* ----------------------------------------------------------------------------- - * Buffer Synchronization - */ - -static DEFINE_SPINLOCK(sync_lock); - -struct omap_gem_sync_waiter { - struct list_head list; - struct omap_gem_object *omap_obj; - enum omap_gem_op op; - uint32_t read_target, write_target; - /* notify called w/ sync_lock held */ - void (*notify)(void *arg); - void *arg; -}; - -/* list of omap_gem_sync_waiter.. the notify fxn gets called back when - * the read and/or write target count is achieved which can call a user - * callback (ex. to kick 3d and/or 2d), wakeup blocked task (prep for - * cpu access), etc. - */ -static LIST_HEAD(waiters); - -static inline bool is_waiting(struct omap_gem_sync_waiter *waiter) -{ - struct omap_gem_object *omap_obj = waiter->omap_obj; - if ((waiter->op & OMAP_GEM_READ) && - (omap_obj->sync->write_complete < waiter->write_target)) - return true; - if ((waiter->op & OMAP_GEM_WRITE) && - (omap_obj->sync->read_complete < waiter->read_target)) - return true; - return false; -} - -/* macro for sync debug.. */ -#define SYNCDBG 0 -#define SYNC(fmt, ...) do { if (SYNCDBG) \ - pr_err("%s:%d: " fmt "\n", __func__, __LINE__, ##__VA_ARGS__); \ - } while (0) - - -static void sync_op_update(void) -{ - struct omap_gem_sync_waiter *waiter, *n; - list_for_each_entry_safe(waiter, n, &waiters, list) { - if (!is_waiting(waiter)) { - list_del(&waiter->list); - SYNC("notify: %p", waiter); - waiter->notify(waiter->arg); - kfree(waiter); - } - } -} - -static inline int sync_op(struct drm_gem_object *obj, - enum omap_gem_op op, bool start) -{ - struct omap_gem_object *omap_obj = to_omap_bo(obj); - int ret = 0; - - spin_lock(&sync_lock); - - if (!omap_obj->sync) { - omap_obj->sync = kzalloc(sizeof(*omap_obj->sync), GFP_ATOMIC); - if (!omap_obj->sync) { - ret = -ENOMEM; - goto unlock; - } - } - - if (start) { - if (op & OMAP_GEM_READ) - omap_obj->sync->read_pending++; - if (op & OMAP_GEM_WRITE) - omap_obj->sync->write_pending++; - } else { - if (op & OMAP_GEM_READ) - omap_obj->sync->read_complete++; - if (op & OMAP_GEM_WRITE) - omap_obj->sync->write_complete++; - sync_op_update(); - } - -unlock: - spin_unlock(&sync_lock); - - return ret; -} - -/* mark the start of read and/or write operation */ -int omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op) -{ - return sync_op(obj, op, true); -} - -int omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op op) -{ - return sync_op(obj, op, false); -} - -static DECLARE_WAIT_QUEUE_HEAD(sync_event); - -static void sync_notify(void *arg) -{ - struct task_struct **waiter_task = arg; - *waiter_task = NULL; - wake_up_all(&sync_event); -} - -int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op) -{ - struct omap_gem_object *omap_obj = to_omap_bo(obj); - int ret = 0; - if (omap_obj->sync) { - struct task_struct *waiter_task = current; - struct omap_gem_sync_waiter *waiter = - kzalloc(sizeof(*waiter), GFP_KERNEL); - - if (!waiter) - return -ENOMEM; - - waiter->omap_obj = omap_obj; - waiter->op = op; - waiter->read_target = omap_obj->sync->read_pending; - waiter->write_target = omap_obj->sync->write_pending; - waiter->notify = sync_notify; - waiter->arg = &waiter_task; - - spin_lock(&sync_lock); - if (is_waiting(waiter)) { - SYNC("waited: %p", waiter); - list_add_tail(&waiter->list, &waiters); - spin_unlock(&sync_lock); - ret = wait_event_interruptible(sync_event, - (waiter_task == NULL)); - spin_lock(&sync_lock); - if (waiter_task) { - SYNC("interrupted: %p", waiter); - /* we were interrupted */ - list_del(&waiter->list); - waiter_task = NULL; - } else { - /* freed in sync_op_update() */ - waiter = NULL; - } - } - spin_unlock(&sync_lock); - kfree(waiter); - } - return ret; -} - -/* call fxn(arg), either synchronously or asynchronously if the op - * is currently blocked.. fxn() can be called from any context - * - * (TODO for now fxn is called back from whichever context calls - * omap_gem_op_finish().. but this could be better defined later - * if needed) - * - * TODO more code in common w/ _sync().. - */ -int omap_gem_op_async(struct drm_gem_object *obj, enum omap_gem_op op, - void (*fxn)(void *arg), void *arg) -{ - struct omap_gem_object *omap_obj = to_omap_bo(obj); - if (omap_obj->sync) { - struct omap_gem_sync_waiter *waiter = - kzalloc(sizeof(*waiter), GFP_ATOMIC); - - if (!waiter) - return -ENOMEM; - - waiter->omap_obj = omap_obj; - waiter->op = op; - waiter->read_target = omap_obj->sync->read_pending; - waiter->write_target = omap_obj->sync->write_pending; - waiter->notify = fxn; - waiter->arg = arg; - - spin_lock(&sync_lock); - if (is_waiting(waiter)) { - SYNC("waited: %p", waiter); - list_add_tail(&waiter->list, &waiters); - spin_unlock(&sync_lock); - return 0; - } - - spin_unlock(&sync_lock); - - kfree(waiter); - } - - /* no waiting.. */ - fxn(arg); - - return 0; -} - -/* ----------------------------------------------------------------------------- * Constructor & Destructor */ @@ -1290,7 +1093,7 @@ void omap_gem_free_object(struct drm_gem_object *obj) /* this means the object is still pinned.. which really should * not happen. I think.. */ - WARN_ON(omap_obj->paddr_cnt > 0); + WARN_ON(omap_obj->dma_addr_cnt > 0); if (omap_obj->pages) { if (omap_obj->flags & OMAP_BO_MEM_DMABUF) @@ -1301,15 +1104,13 @@ void omap_gem_free_object(struct drm_gem_object *obj) if (omap_obj->flags & OMAP_BO_MEM_DMA_API) { dma_free_wc(dev->dev, obj->size, omap_obj->vaddr, - omap_obj->paddr); + omap_obj->dma_addr); } else if (omap_obj->vaddr) { vunmap(omap_obj->vaddr); } else if (obj->import_attach) { drm_prime_gem_destroy(obj, omap_obj->sgt); } - kfree(omap_obj->sync); - drm_gem_object_release(obj); kfree(omap_obj); @@ -1400,7 +1201,7 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev, /* Allocate memory if needed. */ if (flags & OMAP_BO_MEM_DMA_API) { omap_obj->vaddr = dma_alloc_wc(dev->dev, size, - &omap_obj->paddr, + &omap_obj->dma_addr, GFP_KERNEL); if (!omap_obj->vaddr) goto err_release; @@ -1444,7 +1245,7 @@ struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size, omap_obj->sgt = sgt; if (sgt->orig_nents == 1) { - omap_obj->paddr = sg_dma_address(sgt->sgl); + omap_obj->dma_addr = sg_dma_address(sgt->sgl); } else { /* Create pages list from sgt */ struct sg_page_iter iter; @@ -1551,11 +1352,11 @@ void omap_gem_init(struct drm_device *dev) i, j, PTR_ERR(block)); return; } - entry->paddr = tiler_ssptr(block); + entry->dma_addr = tiler_ssptr(block); entry->block = block; - DBG("%d:%d: %dx%d: paddr=%pad stride=%d", i, j, w, h, - &entry->paddr, + DBG("%d:%d: %dx%d: dma_addr=%pad stride=%d", i, j, w, h, + &entry->dma_addr, usergart[i].stride_pfn << PAGE_SHIFT); } } diff --git a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c index 0dbe0306953d..863a881dd7cd 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c +++ b/drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c @@ -31,7 +31,7 @@ static struct sg_table *omap_gem_map_dma_buf( { struct drm_gem_object *obj = attachment->dmabuf->priv; struct sg_table *sg; - dma_addr_t paddr; + dma_addr_t dma_addr; int ret; sg = kzalloc(sizeof(*sg), GFP_KERNEL); @@ -41,7 +41,7 @@ static struct sg_table *omap_gem_map_dma_buf( /* camera, etc, need physically contiguous.. but we need a * better way to know this.. */ - ret = omap_gem_get_paddr(obj, &paddr, true); + ret = omap_gem_pin(obj, &dma_addr); if (ret) goto out; @@ -51,11 +51,11 @@ static struct sg_table *omap_gem_map_dma_buf( sg_init_table(sg->sgl, 1); sg_dma_len(sg->sgl) = obj->size; - sg_set_page(sg->sgl, pfn_to_page(PFN_DOWN(paddr)), obj->size, 0); - sg_dma_address(sg->sgl) = paddr; + sg_set_page(sg->sgl, pfn_to_page(PFN_DOWN(dma_addr)), obj->size, 0); + sg_dma_address(sg->sgl) = dma_addr; - /* this should be after _get_paddr() to ensure we have pages attached */ - omap_gem_dma_sync(obj, dir); + /* this must be after omap_gem_pin() to ensure we have pages attached */ + omap_gem_dma_sync_buffer(obj, dir); return sg; out: @@ -67,21 +67,11 @@ static void omap_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, struct sg_table *sg, enum dma_data_direction dir) { struct drm_gem_object *obj = attachment->dmabuf->priv; - omap_gem_put_paddr(obj); + omap_gem_unpin(obj); sg_free_table(sg); kfree(sg); } -static void omap_gem_dmabuf_release(struct dma_buf *buffer) -{ - struct drm_gem_object *obj = buffer->priv; - /* release reference that was taken when dmabuf was exported - * in omap_gem_prime_set().. - */ - drm_gem_object_unreference_unlocked(obj); -} - - static int omap_gem_dmabuf_begin_cpu_access(struct dma_buf *buffer, enum dma_data_direction dir) { @@ -112,7 +102,7 @@ static void *omap_gem_dmabuf_kmap_atomic(struct dma_buf *buffer, struct drm_gem_object *obj = buffer->priv; struct page **pages; omap_gem_get_pages(obj, &pages, false); - omap_gem_cpu_sync(obj, page_num); + omap_gem_cpu_sync_page(obj, page_num); return kmap_atomic(pages[page_num]); } @@ -128,7 +118,7 @@ static void *omap_gem_dmabuf_kmap(struct dma_buf *buffer, struct drm_gem_object *obj = buffer->priv; struct page **pages; omap_gem_get_pages(obj, &pages, false); - omap_gem_cpu_sync(obj, page_num); + omap_gem_cpu_sync_page(obj, page_num); return kmap(pages[page_num]); } @@ -157,7 +147,7 @@ static int omap_gem_dmabuf_mmap(struct dma_buf *buffer, static struct dma_buf_ops omap_dmabuf_ops = { .map_dma_buf = omap_gem_map_dma_buf, .unmap_dma_buf = omap_gem_unmap_dma_buf, - .release = omap_gem_dmabuf_release, + .release = drm_gem_dmabuf_release, .begin_cpu_access = omap_gem_dmabuf_begin_cpu_access, .end_cpu_access = omap_gem_dmabuf_end_cpu_access, .map_atomic = omap_gem_dmabuf_kmap_atomic, @@ -177,7 +167,7 @@ struct dma_buf *omap_gem_prime_export(struct drm_device *dev, exp_info.flags = flags; exp_info.priv = obj; - return dma_buf_export(&exp_info); + return drm_gem_dmabuf_export(dev, &exp_info); } /* ----------------------------------------------------------------------------- @@ -210,7 +200,7 @@ struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, get_dma_buf(dma_buf); - sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + sgt = dma_buf_map_attachment(attach, DMA_TO_DEVICE); if (IS_ERR(sgt)) { ret = PTR_ERR(sgt); goto fail_detach; @@ -227,7 +217,7 @@ struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, return obj; fail_unmap: - dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); + dma_buf_unmap_attachment(attach, sgt, DMA_TO_DEVICE); fail_detach: dma_buf_detach(dma_buf, attach); dma_buf_put(dma_buf); diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index 115104cdcc59..013b0bba712f 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -41,7 +41,6 @@ static void omap_irq_update(struct drm_device *dev) DBG("irqmask=%08x", irqmask); priv->dispc_ops->write_irqenable(irqmask); - priv->dispc_ops->read_irqenable(); /* flush posted write */ } static void omap_irq_wait_handler(struct omap_irq_wait *wait) @@ -183,12 +182,13 @@ static void omap_irq_fifo_underflow(struct omap_drm_private *priv, pr_cont("(0x%08x)\n", irqstatus); } -static void omap_irq_ocp_error_handler(u32 irqstatus) +static void omap_irq_ocp_error_handler(struct drm_device *dev, + u32 irqstatus) { if (!(irqstatus & DISPC_IRQ_OCP_ERR)) return; - DRM_ERROR("OCP error\n"); + dev_err_ratelimited(dev->dev, "OCP error\n"); } static irqreturn_t omap_irq_handler(int irq, void *arg) @@ -219,7 +219,7 @@ static irqreturn_t omap_irq_handler(int irq, void *arg) omap_crtc_error_irq(crtc, irqstatus); } - omap_irq_ocp_error_handler(irqstatus); + omap_irq_ocp_error_handler(dev, irqstatus); omap_irq_fifo_underflow(priv, irqstatus); spin_lock_irqsave(&priv->wait_lock, flags); diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index d3d6818c68f8..2160f64548e0 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -34,23 +34,8 @@ struct omap_plane { struct drm_plane base; enum omap_plane_id id; const char *name; - - uint32_t nformats; - uint32_t formats[32]; -}; - -struct omap_plane_state { - struct drm_plane_state base; - - unsigned int zorder; }; -static inline struct omap_plane_state * -to_omap_plane_state(struct drm_plane_state *state) -{ - return container_of(state, struct omap_plane_state, base); -} - static int omap_plane_prepare_fb(struct drm_plane *plane, struct drm_plane_state *new_state) { @@ -73,44 +58,19 @@ static void omap_plane_atomic_update(struct drm_plane *plane, struct omap_drm_private *priv = plane->dev->dev_private; struct omap_plane *omap_plane = to_omap_plane(plane); struct drm_plane_state *state = plane->state; - struct omap_plane_state *omap_state = to_omap_plane_state(state); struct omap_overlay_info info; - struct omap_drm_window win; int ret; DBG("%s, crtc=%p fb=%p", omap_plane->name, state->crtc, state->fb); memset(&info, 0, sizeof(info)); - info.rotation_type = OMAP_DSS_ROT_DMA; - info.rotation = OMAP_DSS_ROT_0; + info.rotation_type = OMAP_DSS_ROT_NONE; + info.rotation = DRM_MODE_ROTATE_0; info.global_alpha = 0xff; - info.mirror = 0; - info.zorder = omap_state->zorder; - - memset(&win, 0, sizeof(win)); - win.rotation = state->rotation; - win.crtc_x = state->crtc_x; - win.crtc_y = state->crtc_y; - win.crtc_w = state->crtc_w; - win.crtc_h = state->crtc_h; - - /* - * src values are in Q16 fixed point, convert to integer. - * omap_framebuffer_update_scanout() takes adjusted src. - */ - win.src_x = state->src_x >> 16; - win.src_y = state->src_y >> 16; - - if (drm_rotation_90_or_270(state->rotation)) { - win.src_w = state->src_h >> 16; - win.src_h = state->src_w >> 16; - } else { - win.src_w = state->src_w >> 16; - win.src_h = state->src_h >> 16; - } + info.zorder = state->zpos; /* update scanout: */ - omap_framebuffer_update_scanout(state->fb, &win, &info); + omap_framebuffer_update_scanout(state->fb, state, &info); DBG("%dx%d -> %dx%d (%d)", info.width, info.height, info.out_width, info.out_height, @@ -118,12 +78,10 @@ static void omap_plane_atomic_update(struct drm_plane *plane, DBG("%d,%d %pad %pad", info.pos_x, info.pos_y, &info.paddr, &info.p_uv_addr); - priv->dispc_ops->ovl_set_channel_out(omap_plane->id, - omap_crtc_channel(state->crtc)); - /* and finally, update omapdss: */ ret = priv->dispc_ops->ovl_setup(omap_plane->id, &info, - omap_crtc_timings(state->crtc), false); + omap_crtc_timings(state->crtc), false, + omap_crtc_channel(state->crtc)); if (ret) { dev_err(plane->dev->dev, "Failed to setup plane %s\n", omap_plane->name); @@ -138,11 +96,10 @@ static void omap_plane_atomic_disable(struct drm_plane *plane, struct drm_plane_state *old_state) { struct omap_drm_private *priv = plane->dev->dev_private; - struct omap_plane_state *omap_state = to_omap_plane_state(plane->state); struct omap_plane *omap_plane = to_omap_plane(plane); plane->state->rotation = DRM_MODE_ROTATE_0; - omap_state->zorder = plane->type == DRM_PLANE_TYPE_PRIMARY + plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id; priv->dispc_ops->ovl_enable(omap_plane->id, false); @@ -227,56 +184,20 @@ void omap_plane_install_properties(struct drm_plane *plane, drm_object_attach_property(obj, priv->zorder_prop, 0); } -static struct drm_plane_state * -omap_plane_atomic_duplicate_state(struct drm_plane *plane) -{ - struct omap_plane_state *state; - struct omap_plane_state *copy; - - if (WARN_ON(!plane->state)) - return NULL; - - state = to_omap_plane_state(plane->state); - copy = kmemdup(state, sizeof(*state), GFP_KERNEL); - if (copy == NULL) - return NULL; - - __drm_atomic_helper_plane_duplicate_state(plane, ©->base); - - return ©->base; -} - -static void omap_plane_atomic_destroy_state(struct drm_plane *plane, - struct drm_plane_state *state) -{ - __drm_atomic_helper_plane_destroy_state(state); - kfree(to_omap_plane_state(state)); -} - static void omap_plane_reset(struct drm_plane *plane) { struct omap_plane *omap_plane = to_omap_plane(plane); - struct omap_plane_state *omap_state; - if (plane->state) { - omap_plane_atomic_destroy_state(plane, plane->state); - plane->state = NULL; - } - - omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL); - if (omap_state == NULL) + drm_atomic_helper_plane_reset(plane); + if (!plane->state) return; /* - * Set defaults depending on whether we are a primary or overlay + * Set the zpos default depending on whether we are a primary or overlay * plane. */ - omap_state->zorder = plane->type == DRM_PLANE_TYPE_PRIMARY + plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id; - omap_state->base.rotation = DRM_MODE_ROTATE_0; - - plane->state = &omap_state->base; - plane->state->plane = plane; } static int omap_plane_atomic_set_property(struct drm_plane *plane, @@ -285,10 +206,9 @@ static int omap_plane_atomic_set_property(struct drm_plane *plane, uint64_t val) { struct omap_drm_private *priv = plane->dev->dev_private; - struct omap_plane_state *omap_state = to_omap_plane_state(state); if (property == priv->zorder_prop) - omap_state->zorder = val; + state->zpos = val; else return -EINVAL; @@ -301,11 +221,9 @@ static int omap_plane_atomic_get_property(struct drm_plane *plane, uint64_t *val) { struct omap_drm_private *priv = plane->dev->dev_private; - const struct omap_plane_state *omap_state = - container_of(state, const struct omap_plane_state, base); if (property == priv->zorder_prop) - *val = omap_state->zorder; + *val = state->zpos; else return -EINVAL; @@ -318,8 +236,8 @@ static const struct drm_plane_funcs omap_plane_funcs = { .reset = omap_plane_reset, .destroy = omap_plane_destroy, .set_property = drm_atomic_helper_plane_set_property, - .atomic_duplicate_state = omap_plane_atomic_duplicate_state, - .atomic_destroy_state = omap_plane_atomic_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, .atomic_set_property = omap_plane_atomic_set_property, .atomic_get_property = omap_plane_atomic_get_property, }; @@ -344,10 +262,13 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, u32 possible_crtcs) { struct omap_drm_private *priv = dev->dev_private; + unsigned int num_planes = priv->dispc_ops->get_num_ovls(); struct drm_plane *plane; struct omap_plane *omap_plane; enum omap_plane_id id; int ret; + u32 nformats; + const u32 *formats; if (WARN_ON(idx >= ARRAY_SIZE(plane_idx_to_id))) return ERR_PTR(-EINVAL); @@ -360,23 +281,24 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, if (!omap_plane) return ERR_PTR(-ENOMEM); - omap_plane->nformats = omap_framebuffer_get_formats( - omap_plane->formats, ARRAY_SIZE(omap_plane->formats), - priv->dispc_ops->ovl_get_color_modes(id)); + formats = priv->dispc_ops->ovl_get_color_modes(id); + for (nformats = 0; formats[nformats]; ++nformats) + ; omap_plane->id = id; omap_plane->name = plane_id_to_name[id]; plane = &omap_plane->base; ret = drm_universal_plane_init(dev, plane, possible_crtcs, - &omap_plane_funcs, omap_plane->formats, - omap_plane->nformats, type, NULL); + &omap_plane_funcs, formats, + nformats, type, NULL); if (ret < 0) goto error; drm_plane_helper_add(plane, &omap_plane_helper_funcs); omap_plane_install_properties(plane, &plane->base); + drm_plane_create_zpos_property(plane, 0, 0, num_planes - 1); return plane; diff --git a/drivers/gpu/drm/pl111/Kconfig b/drivers/gpu/drm/pl111/Kconfig index 309f4fd52de7..bbfba87cd1a8 100644 --- a/drivers/gpu/drm/pl111/Kconfig +++ b/drivers/gpu/drm/pl111/Kconfig @@ -6,6 +6,7 @@ config DRM_PL111 select DRM_KMS_HELPER select DRM_KMS_CMA_HELPER select DRM_GEM_CMA_HELPER + select DRM_PANEL select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE help Choose this option for DRM support for the PL111 CLCD controller. diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c index e96efad37d27..ac8771be70b0 100644 --- a/drivers/gpu/drm/pl111/pl111_drv.c +++ b/drivers/gpu/drm/pl111/pl111_drv.c @@ -179,7 +179,6 @@ static struct drm_driver pl111_drm_driver = { #endif }; -#ifdef CONFIG_ARM_AMBA static int pl111_amba_probe(struct amba_device *amba_dev, const struct amba_id *id) { @@ -252,7 +251,7 @@ static struct amba_id pl111_id_table[] = { {0, 0}, }; -static struct amba_driver pl111_amba_driver = { +static struct amba_driver pl111_amba_driver __maybe_unused = { .drv = { .name = "drm-clcd-pl111", }, @@ -261,8 +260,9 @@ static struct amba_driver pl111_amba_driver = { .id_table = pl111_id_table, }; +#ifdef CONFIG_ARM_AMBA module_amba_driver(pl111_amba_driver); -#endif /* CONFIG_ARM_AMBA */ +#endif MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR("ARM Ltd."); diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 40a5e6ef6f2c..9b3525a36969 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -1118,16 +1118,17 @@ static void vop_crtc_destroy_state(struct drm_crtc *crtc, #ifdef CONFIG_DRM_ANALOGIX_DP static struct drm_connector *vop_get_edp_connector(struct vop *vop) { - struct drm_crtc *crtc = &vop->crtc; struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; - mutex_lock(&crtc->dev->mode_config.mutex); - drm_for_each_connector(connector, crtc->dev) + drm_connector_list_iter_begin(vop->drm_dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) { - mutex_unlock(&crtc->dev->mode_config.mutex); + drm_connector_list_iter_end(&conn_iter); return connector; } - mutex_unlock(&crtc->dev->mode_config.mutex); + } + drm_connector_list_iter_end(&conn_iter); return NULL; } diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index 700cc0800e51..1b9483d4f2a4 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -1144,8 +1144,6 @@ void ltdc_unload(struct drm_device *ddev) DRM_DEBUG_DRIVER("\n"); - drm_vblank_cleanup(ddev); - if (ldev->panel) drm_panel_detach(ldev->panel); diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 8ddd72cd5873..c26d5888f8e1 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -138,7 +138,6 @@ finish_poll: sun4i_framebuffer_free(drm); cleanup_mode_config: drm_mode_config_cleanup(drm); - drm_vblank_cleanup(drm); free_mem_region: of_reserved_mem_device_release(dev); free_drm: @@ -154,7 +153,6 @@ static void sun4i_drv_unbind(struct device *dev) drm_kms_helper_poll_fini(drm); sun4i_framebuffer_free(drm); drm_mode_config_cleanup(drm); - drm_vblank_cleanup(drm); of_reserved_mem_device_release(dev); drm_dev_unref(drm); } diff --git a/drivers/gpu/drm/tinydrm/mipi-dbi.c b/drivers/gpu/drm/tinydrm/mipi-dbi.c index f4eb412f3604..c83eeb7a34b0 100644 --- a/drivers/gpu/drm/tinydrm/mipi-dbi.c +++ b/drivers/gpu/drm/tinydrm/mipi-dbi.c @@ -914,7 +914,7 @@ static int mipi_dbi_debugfs_command_show(struct seq_file *m, void *unused) { struct mipi_dbi *mipi = m->private; u8 cmd, val[4]; - size_t len, i; + size_t len; int ret; for (cmd = 0; cmd < 255; cmd++) { @@ -943,10 +943,7 @@ static int mipi_dbi_debugfs_command_show(struct seq_file *m, void *unused) seq_puts(m, "XX\n"); continue; } - - for (i = 0; i < len; i++) - seq_printf(m, "%02x", val[i]); - seq_puts(m, "\n"); + seq_printf(m, "%*phN\n", (int)len, val); } return 0; diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 0bfc4d88e4c2..403bbd5f99a9 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -345,12 +345,16 @@ static u32 vc4_get_fifo_full_level(u32 format) static struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc) { struct drm_connector *connector; + struct drm_connector_list_iter conn_iter; - drm_for_each_connector(connector, crtc->dev) { + drm_connector_list_iter_begin(crtc->dev, &conn_iter); + drm_for_each_connector_iter(connector, &conn_iter) { if (connector->state->crtc == crtc) { + drm_connector_list_iter_end(&conn_iter); return connector->encoder; } } + drm_connector_list_iter_end(&conn_iter); return NULL; } diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index a97556f7ccba..a5bf2e5e0b57 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -532,7 +532,7 @@ int vc4_queue_seqno_cb(struct drm_device *dev, extern struct platform_driver vc4_hdmi_driver; int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused); -/* vc4_hdmi.c */ +/* vc4_vec.c */ extern struct platform_driver vc4_vec_driver; int vc4_vec_debugfs_regs(struct seq_file *m, void *unused); diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index 5ae5518e605b..8c723da71f66 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -401,6 +401,7 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) return ret; } + pm_runtime_set_active(dev); pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, 40); /* a little over 2 frames. */ pm_runtime_enable(dev); diff --git a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c index 43e1d5916c6c..7df8d0c9026a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drm_bus.c +++ b/drivers/gpu/drm/virtio/virtgpu_drm_bus.c @@ -56,7 +56,6 @@ int drm_virtio_init(struct drm_driver *driver, struct virtio_device *vdev) dev = drm_dev_alloc(driver, &vdev->dev); if (IS_ERR(dev)) return PTR_ERR(dev); - dev->virtdev = vdev; vdev->priv = dev; if (strcmp(vdev->dev.parent->bus->name, "pci") == 0) { diff --git a/drivers/gpu/drm/virtio/virtgpu_kms.c b/drivers/gpu/drm/virtio/virtgpu_kms.c index 1e1c90b30d4a..6400506a06b0 100644 --- a/drivers/gpu/drm/virtio/virtgpu_kms.c +++ b/drivers/gpu/drm/virtio/virtgpu_kms.c @@ -138,7 +138,7 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags) u32 num_scanouts, num_capsets; int ret; - if (!virtio_has_feature(dev->virtdev, VIRTIO_F_VERSION_1)) + if (!virtio_has_feature(dev_to_virtio(dev->dev), VIRTIO_F_VERSION_1)) return -ENODEV; vgdev = kzalloc(sizeof(struct virtio_gpu_device), GFP_KERNEL); @@ -147,7 +147,7 @@ int virtio_gpu_driver_load(struct drm_device *dev, unsigned long flags) vgdev->ddev = dev; dev->dev_private = vgdev; - vgdev->vdev = dev->virtdev; + vgdev->vdev = dev_to_virtio(dev->dev); vgdev->dev = dev->dev; spin_lock_init(&vgdev->display_info_lock); diff --git a/drivers/video/fbdev/omap2/omapfb/dss/core.c b/drivers/video/fbdev/omap2/omapfb/dss/core.c index 29de4827589d..eecf695c16f4 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/core.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/core.c @@ -206,8 +206,6 @@ static int __init omap_dss_probe(struct platform_device *pdev) if (def_disp_name) core.default_display_name = def_disp_name; - else if (pdata->default_display_name) - core.default_display_name = pdata->default_display_name; register_pm_notifier(&omap_dss_pm_notif_block); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index b9b5566acfe6..39df16af7a4a 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -80,6 +80,9 @@ #include <drm/drm_debugfs.h> #include <drm/drm_ioctl.h> #include <drm/drm_sysfs.h> +#include <drm/drm_vblank.h> +#include <drm/drm_irq.h> + struct module; @@ -292,23 +295,6 @@ struct pci_controller; /* Format strings and argument splitters to simplify printing * various "complex" objects */ -#define DRM_MODE_FMT "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x" -#define DRM_MODE_ARG(m) \ - (m)->base.id, (m)->name, (m)->vrefresh, (m)->clock, \ - (m)->hdisplay, (m)->hsync_start, (m)->hsync_end, (m)->htotal, \ - (m)->vdisplay, (m)->vsync_start, (m)->vsync_end, (m)->vtotal, \ - (m)->type, (m)->flags - -#define DRM_RECT_FMT "%dx%d%+d%+d" -#define DRM_RECT_ARG(r) drm_rect_width(r), drm_rect_height(r), (r)->x1, (r)->y1 - -/* for rect's in fixed-point format: */ -#define DRM_RECT_FP_FMT "%d.%06ux%d.%06u%+d.%06u%+d.%06u" -#define DRM_RECT_FP_ARG(r) \ - drm_rect_width(r) >> 16, ((drm_rect_width(r) & 0xffff) * 15625) >> 10, \ - drm_rect_height(r) >> 16, ((drm_rect_height(r) & 0xffff) * 15625) >> 10, \ - (r)->x1 >> 16, (((r)->x1 & 0xffff) * 15625) >> 10, \ - (r)->y1 >> 16, (((r)->y1 & 0xffff) * 15625) >> 10 /*@}*/ @@ -391,8 +377,13 @@ struct drm_device { int last_context; /**< Last current context */ /*@} */ - /** \name VBLANK IRQ support */ - /*@{ */ + /** + * @irq_enabled: + * + * Indicates that interrupt handling is enabled, specifically vblank + * handling. Drivers which don't use drm_irq_install() need to set this + * to true manually. + */ bool irq_enabled; int irq; @@ -429,8 +420,6 @@ struct drm_device { struct pci_controller *hose; #endif - struct virtio_device *virtdev; - struct drm_sg_mem *sg; /**< Scatter gather memory */ unsigned int num_crtcs; /**< Number of CRTCs on this device */ @@ -466,8 +455,6 @@ static inline bool drm_drv_uses_atomic_modeset(struct drm_device *dev) return dev->mode_config.funcs->atomic_commit != NULL; } -#include <drm/drm_irq.h> - #define DRM_SWITCH_POWER_ON 0 #define DRM_SWITCH_POWER_OFF 1 #define DRM_SWITCH_POWER_CHANGING 2 diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 8645dcdef031..0196f264a418 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -520,7 +520,7 @@ __drm_atomic_get_current_plane_state(struct drm_atomic_state *state, int __must_check drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, - struct drm_display_mode *mode); + const struct drm_display_mode *mode); int __must_check drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state, struct drm_property_blob *blob); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 983054f2e86e..5b106eca6d57 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -253,6 +253,8 @@ int drm_bridge_attach(struct drm_encoder *encoder, struct drm_bridge *bridge, bool drm_bridge_mode_fixup(struct drm_bridge *bridge, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); +enum drm_mode_status drm_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_mode *mode); void drm_bridge_disable(struct drm_bridge *bridge); void drm_bridge_post_disable(struct drm_bridge *bridge); void drm_bridge_mode_set(struct drm_bridge *bridge, diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 2fe09c1ddfb8..d8bb25f38eba 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1010,21 +1010,6 @@ void drm_mode_put_tile_group(struct drm_device *dev, struct drm_tile_group *tg); /** - * drm_for_each_connector - iterate over all connectors - * @connector: the loop cursor - * @dev: the DRM device - * - * Iterate over all connectors of @dev. - * - * WARNING: - * - * This iterator is not safe against hotadd/removal of connectors and is - * deprecated. Use drm_for_each_connector_iter() instead. - */ -#define drm_for_each_connector(connector, dev) \ - list_for_each_entry(connector, &(dev)->mode_config.connector_list, head) - -/** * struct drm_connector_list_iter - connector_list iterator * * This iterator tracks state needed to be able to walk the connector_list diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index b6e3713bd7a9..629a5fe075b3 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -214,7 +214,9 @@ struct drm_crtc_state { * atomic commit. In that case the event can be send out any time * after the hardware has stopped scanning out the current * framebuffers. It should contain the timestamp and counter for the - * last vblank before the display pipeline was shut off. + * last vblank before the display pipeline was shut off. The simplest + * way to achieve that is calling drm_crtc_send_vblank_event() + * somewhen after drm_crtc_vblank_off() has been called. * * - For a CRTC which is enabled at the end of the commit (even when it * undergoes an full modeset) the vblank timestamp and counter must diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index e64e33b9dd26..18f3181674e8 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -327,11 +327,40 @@ struct drm_driver { struct timeval *vblank_time, bool in_vblank_irq); - /* these have to be filled in */ - + /** + * @irq_handler: + * + * Interrupt handler called when using drm_irq_install(). Not used by + * drivers which implement their own interrupt handling. + */ irqreturn_t(*irq_handler) (int irq, void *arg); + + /** + * @irq_preinstall: + * + * Optional callback used by drm_irq_install() which is called before + * the interrupt handler is registered. This should be used to clear out + * any pending interrupts (from e.g. firmware based drives) and reset + * the interrupt handling registers. + */ void (*irq_preinstall) (struct drm_device *dev); + + /** + * @irq_postinstall: + * + * Optional callback used by drm_irq_install() which is called after + * the interrupt handler is registered. This should be used to enable + * interrupt generation in the hardware. + */ int (*irq_postinstall) (struct drm_device *dev); + + /** + * @irq_uninstall: + * + * Optional callback used by drm_irq_uninstall() which is called before + * the interrupt handler is unregistered. This should be used to disable + * interrupt generation in the hardware. + */ void (*irq_uninstall) (struct drm_device *dev); /** diff --git a/include/drm/drm_file.h b/include/drm/drm_file.h index 5dd27ae5c47c..d66f7ee07fb5 100644 --- a/include/drm/drm_file.h +++ b/include/drm/drm_file.h @@ -40,6 +40,7 @@ struct dma_fence; struct drm_file; struct drm_device; +struct device; /* * FIXME: Not sure we want to have drm_minor here in the end, but to avoid diff --git a/include/drm/drm_irq.h b/include/drm/drm_irq.h index 569ca86d4e1f..d77f6e65b1c6 100644 --- a/include/drm/drm_irq.h +++ b/include/drm/drm_irq.h @@ -24,165 +24,9 @@ #ifndef _DRM_IRQ_H_ #define _DRM_IRQ_H_ -#include <linux/seqlock.h> - -/** - * struct drm_pending_vblank_event - pending vblank event tracking - */ -struct drm_pending_vblank_event { - /** - * @base: Base structure for tracking pending DRM events. - */ - struct drm_pending_event base; - /** - * @pipe: drm_crtc_index() of the &drm_crtc this event is for. - */ - unsigned int pipe; - /** - * @event: Actual event which will be sent to userspace. - */ - struct drm_event_vblank event; -}; - -/** - * struct drm_vblank_crtc - vblank tracking for a CRTC - * - * This structure tracks the vblank state for one CRTC. - * - * Note that for historical reasons - the vblank handling code is still shared - * with legacy/non-kms drivers - this is a free-standing structure not directly - * connected to &struct drm_crtc. But all public interface functions are taking - * a &struct drm_crtc to hide this implementation detail. - */ -struct drm_vblank_crtc { - /** - * @dev: Pointer to the &drm_device. - */ - struct drm_device *dev; - /** - * @queue: Wait queue for vblank waiters. - */ - wait_queue_head_t queue; /**< VBLANK wait queue */ - /** - * @disable_timer: Disable timer for the delayed vblank disabling - * hysteresis logic. Vblank disabling is controlled through the - * drm_vblank_offdelay module option and the setting of the - * &drm_device.max_vblank_count value. - */ - struct timer_list disable_timer; - - /** - * @seqlock: Protect vblank count and time. - */ - seqlock_t seqlock; /* protects vblank count and time */ - - /** - * @count: Current software vblank counter. - */ - u32 count; - /** - * @time: Vblank timestamp corresponding to @count. - */ - struct timeval time; - - /** - * @refcount: Number of users/waiters of the vblank interrupt. Only when - * this refcount reaches 0 can the hardware interrupt be disabled using - * @disable_timer. - */ - atomic_t refcount; /* number of users of vblank interruptsper crtc */ - /** - * @last: Protected by &drm_device.vbl_lock, used for wraparound handling. - */ - u32 last; - /** - * @inmodeset: Tracks whether the vblank is disabled due to a modeset. - * For legacy driver bit 2 additionally tracks whether an additional - * temporary vblank reference has been acquired to paper over the - * hardware counter resetting/jumping. KMS drivers should instead just - * call drm_crtc_vblank_off() and drm_crtc_vblank_on(), which explicitly - * save and restore the vblank count. - */ - unsigned int inmodeset; /* Display driver is setting mode */ - /** - * @pipe: drm_crtc_index() of the &drm_crtc corresponding to this - * structure. - */ - unsigned int pipe; - /** - * @framedur_ns: Frame/Field duration in ns, used by - * drm_calc_vbltimestamp_from_scanoutpos() and computed by - * drm_calc_timestamping_constants(). - */ - int framedur_ns; - /** - * @linedur_ns: Line duration in ns, used by - * drm_calc_vbltimestamp_from_scanoutpos() and computed by - * drm_calc_timestamping_constants(). - */ - int linedur_ns; - - /** - * @hwmode: - * - * Cache of the current hardware display mode. Only valid when @enabled - * is set. This is used by helpers like - * drm_calc_vbltimestamp_from_scanoutpos(). We can't just access the - * hardware mode by e.g. looking at &drm_crtc_state.adjusted_mode, - * because that one is really hard to get from interrupt context. - */ - struct drm_display_mode hwmode; - - /** - * @enabled: Tracks the enabling state of the corresponding &drm_crtc to - * avoid double-disabling and hence corrupting saved state. Needed by - * drivers not using atomic KMS, since those might go through their CRTC - * disabling functions multiple times. - */ - bool enabled; -}; +struct drm_device; int drm_irq_install(struct drm_device *dev, int irq); int drm_irq_uninstall(struct drm_device *dev); -int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs); -u32 drm_crtc_vblank_count(struct drm_crtc *crtc); -u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, - struct timeval *vblanktime); -void drm_crtc_send_vblank_event(struct drm_crtc *crtc, - struct drm_pending_vblank_event *e); -void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, - struct drm_pending_vblank_event *e); -bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe); -bool drm_crtc_handle_vblank(struct drm_crtc *crtc); -int drm_crtc_vblank_get(struct drm_crtc *crtc); -void drm_crtc_vblank_put(struct drm_crtc *crtc); -void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe); -void drm_crtc_wait_one_vblank(struct drm_crtc *crtc); -void drm_crtc_vblank_off(struct drm_crtc *crtc); -void drm_crtc_vblank_reset(struct drm_crtc *crtc); -void drm_crtc_vblank_on(struct drm_crtc *crtc); -void drm_vblank_cleanup(struct drm_device *dev); -u32 drm_accurate_vblank_count(struct drm_crtc *crtc); - -bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, - unsigned int pipe, int *max_error, - struct timeval *vblank_time, - bool in_vblank_irq); -void drm_calc_timestamping_constants(struct drm_crtc *crtc, - const struct drm_display_mode *mode); - -/** - * drm_crtc_vblank_waitqueue - get vblank waitqueue for the CRTC - * @crtc: which CRTC's vblank waitqueue to retrieve - * - * This function returns a pointer to the vblank waitqueue for the CRTC. - * Drivers can use this to implement vblank waits using wait_event() and related - * functions. - */ -static inline wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc) -{ - return &crtc->dev->vblank[drm_crtc_index(crtc)].queue; -} - #endif diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index 6dd34280e892..94ac771fe460 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -197,6 +197,8 @@ enum drm_mode_status { * there's the hardware timings, which are corrected for interlacing, * double-clocking and similar things. They are provided as a convenience, and * can be appropriately computed using drm_mode_set_crtcinfo(). + * + * For printing you can use %DRM_MODE_FMT and DRM_MODE_ARG(). */ struct drm_display_mode { /** @@ -407,6 +409,21 @@ struct drm_display_mode { enum hdmi_picture_aspect picture_aspect_ratio; }; +/** + * DRM_MODE_FMT - printf string for &struct drm_display_mode + */ +#define DRM_MODE_FMT "%d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x" + +/** + * DRM_MODE_ARG - printf arguments for &struct drm_display_mode + * @m: display mode + */ +#define DRM_MODE_ARG(m) \ + (m)->base.id, (m)->name, (m)->vrefresh, (m)->clock, \ + (m)->hdisplay, (m)->hsync_start, (m)->hsync_end, (m)->htotal, \ + (m)->vdisplay, (m)->vsync_start, (m)->vsync_end, (m)->vtotal, \ + (m)->type, (m)->flags + #define obj_to_mode(x) container_of(x, struct drm_display_mode, base) /** diff --git a/include/drm/drm_os_linux.h b/include/drm/drm_os_linux.h index 35e1482ba8a1..10122353b744 100644 --- a/include/drm/drm_os_linux.h +++ b/include/drm/drm_os_linux.h @@ -6,19 +6,7 @@ #include <linux/interrupt.h> /* For task queue support */ #include <linux/sched/signal.h> #include <linux/delay.h> - -#ifndef readq -static inline u64 readq(void __iomem *reg) -{ - return ((u64) readl(reg)) | (((u64) readl(reg + 4UL)) << 32); -} - -static inline void writeq(u64 val, void __iomem *reg) -{ - writel(val & 0xffffffff, reg); - writel(val >> 32, reg + 0x4UL); -} -#endif +#include <linux/io-64-nonatomic-lo-hi.h> /** Current process ID */ #define DRM_CURRENTPID task_pid_nr(current) diff --git a/include/drm/drm_prime.h b/include/drm/drm_prime.h index 59ccab402e85..9cd9e36f77b5 100644 --- a/include/drm/drm_prime.h +++ b/include/drm/drm_prime.h @@ -59,6 +59,8 @@ struct drm_device; struct drm_gem_object; struct drm_file; +struct device; + struct dma_buf *drm_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags); diff --git a/include/drm/drm_rect.h b/include/drm/drm_rect.h index 83bb156d4356..44bc122b9ee0 100644 --- a/include/drm/drm_rect.h +++ b/include/drm/drm_rect.h @@ -43,6 +43,33 @@ struct drm_rect { }; /** + * DRM_RECT_FMT - printf string for &struct drm_rect + */ +#define DRM_RECT_FMT "%dx%d%+d%+d" +/** + * DRM_RECT_ARG - printf arguments for &struct drm_rect + * @r: rectangle struct + */ +#define DRM_RECT_ARG(r) drm_rect_width(r), drm_rect_height(r), (r)->x1, (r)->y1 + +/** + * DRM_RECT_FP_FMT - printf string for &struct drm_rect in 16.16 fixed point + */ +#define DRM_RECT_FP_FMT "%d.%06ux%d.%06u%+d.%06u%+d.%06u" +/** + * DRM_RECT_FP_ARG - printf arguments for &struct drm_rect in 16.16 fixed point + * @r: rectangle struct + * + * This is useful for e.g. printing plane source rectangles, which are in 16.16 + * fixed point. + */ +#define DRM_RECT_FP_ARG(r) \ + drm_rect_width(r) >> 16, ((drm_rect_width(r) & 0xffff) * 15625) >> 10, \ + drm_rect_height(r) >> 16, ((drm_rect_height(r) & 0xffff) * 15625) >> 10, \ + (r)->x1 >> 16, (((r)->x1 & 0xffff) * 15625) >> 10, \ + (r)->y1 >> 16, (((r)->y1 & 0xffff) * 15625) >> 10 + +/** * drm_rect_adjust_size - adjust the size of the rectangle * @r: rectangle to be adjusted * @dw: horizontal adjustment diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h new file mode 100644 index 000000000000..4cde47332dfa --- /dev/null +++ b/include/drm/drm_vblank.h @@ -0,0 +1,181 @@ +/* + * Copyright 2016 Intel Corp. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DRM_VBLANK_H_ +#define _DRM_VBLANK_H_ + +#include <linux/seqlock.h> +#include <linux/idr.h> +#include <linux/poll.h> + +#include <drm/drm_file.h> +#include <drm/drm_modes.h> +#include <uapi/drm/drm.h> + +struct drm_device; +struct drm_crtc; + +/** + * struct drm_pending_vblank_event - pending vblank event tracking + */ +struct drm_pending_vblank_event { + /** + * @base: Base structure for tracking pending DRM events. + */ + struct drm_pending_event base; + /** + * @pipe: drm_crtc_index() of the &drm_crtc this event is for. + */ + unsigned int pipe; + /** + * @event: Actual event which will be sent to userspace. + */ + struct drm_event_vblank event; +}; + +/** + * struct drm_vblank_crtc - vblank tracking for a CRTC + * + * This structure tracks the vblank state for one CRTC. + * + * Note that for historical reasons - the vblank handling code is still shared + * with legacy/non-kms drivers - this is a free-standing structure not directly + * connected to &struct drm_crtc. But all public interface functions are taking + * a &struct drm_crtc to hide this implementation detail. + */ +struct drm_vblank_crtc { + /** + * @dev: Pointer to the &drm_device. + */ + struct drm_device *dev; + /** + * @queue: Wait queue for vblank waiters. + */ + wait_queue_head_t queue; /**< VBLANK wait queue */ + /** + * @disable_timer: Disable timer for the delayed vblank disabling + * hysteresis logic. Vblank disabling is controlled through the + * drm_vblank_offdelay module option and the setting of the + * &drm_device.max_vblank_count value. + */ + struct timer_list disable_timer; + + /** + * @seqlock: Protect vblank count and time. + */ + seqlock_t seqlock; /* protects vblank count and time */ + + /** + * @count: Current software vblank counter. + */ + u32 count; + /** + * @time: Vblank timestamp corresponding to @count. + */ + struct timeval time; + + /** + * @refcount: Number of users/waiters of the vblank interrupt. Only when + * this refcount reaches 0 can the hardware interrupt be disabled using + * @disable_timer. + */ + atomic_t refcount; /* number of users of vblank interruptsper crtc */ + /** + * @last: Protected by &drm_device.vbl_lock, used for wraparound handling. + */ + u32 last; + /** + * @inmodeset: Tracks whether the vblank is disabled due to a modeset. + * For legacy driver bit 2 additionally tracks whether an additional + * temporary vblank reference has been acquired to paper over the + * hardware counter resetting/jumping. KMS drivers should instead just + * call drm_crtc_vblank_off() and drm_crtc_vblank_on(), which explicitly + * save and restore the vblank count. + */ + unsigned int inmodeset; /* Display driver is setting mode */ + /** + * @pipe: drm_crtc_index() of the &drm_crtc corresponding to this + * structure. + */ + unsigned int pipe; + /** + * @framedur_ns: Frame/Field duration in ns, used by + * drm_calc_vbltimestamp_from_scanoutpos() and computed by + * drm_calc_timestamping_constants(). + */ + int framedur_ns; + /** + * @linedur_ns: Line duration in ns, used by + * drm_calc_vbltimestamp_from_scanoutpos() and computed by + * drm_calc_timestamping_constants(). + */ + int linedur_ns; + + /** + * @hwmode: + * + * Cache of the current hardware display mode. Only valid when @enabled + * is set. This is used by helpers like + * drm_calc_vbltimestamp_from_scanoutpos(). We can't just access the + * hardware mode by e.g. looking at &drm_crtc_state.adjusted_mode, + * because that one is really hard to get from interrupt context. + */ + struct drm_display_mode hwmode; + + /** + * @enabled: Tracks the enabling state of the corresponding &drm_crtc to + * avoid double-disabling and hence corrupting saved state. Needed by + * drivers not using atomic KMS, since those might go through their CRTC + * disabling functions multiple times. + */ + bool enabled; +}; + +int drm_vblank_init(struct drm_device *dev, unsigned int num_crtcs); +u32 drm_crtc_vblank_count(struct drm_crtc *crtc); +u32 drm_crtc_vblank_count_and_time(struct drm_crtc *crtc, + struct timeval *vblanktime); +void drm_crtc_send_vblank_event(struct drm_crtc *crtc, + struct drm_pending_vblank_event *e); +void drm_crtc_arm_vblank_event(struct drm_crtc *crtc, + struct drm_pending_vblank_event *e); +bool drm_handle_vblank(struct drm_device *dev, unsigned int pipe); +bool drm_crtc_handle_vblank(struct drm_crtc *crtc); +int drm_crtc_vblank_get(struct drm_crtc *crtc); +void drm_crtc_vblank_put(struct drm_crtc *crtc); +void drm_wait_one_vblank(struct drm_device *dev, unsigned int pipe); +void drm_crtc_wait_one_vblank(struct drm_crtc *crtc); +void drm_crtc_vblank_off(struct drm_crtc *crtc); +void drm_crtc_vblank_reset(struct drm_crtc *crtc); +void drm_crtc_vblank_on(struct drm_crtc *crtc); +void drm_vblank_cleanup(struct drm_device *dev); +u32 drm_accurate_vblank_count(struct drm_crtc *crtc); + +bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, + unsigned int pipe, int *max_error, + struct timeval *vblank_time, + bool in_vblank_irq); +void drm_calc_timestamping_constants(struct drm_crtc *crtc, + const struct drm_display_mode *mode); +wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc); +#endif diff --git a/include/linux/platform_data/omapdss.h b/include/linux/platform_data/omapdss.h index 679177929045..7feb011ed500 100644 --- a/include/linux/platform_data/omapdss.h +++ b/include/linux/platform_data/omapdss.h @@ -27,7 +27,6 @@ enum omapdss_version { /* Board specific data */ struct omap_dss_board_info { - const char *default_display_name; int (*dsi_enable_pads)(int dsi_id, unsigned int lane_mask); void (*dsi_disable_pads)(int dsi_id, unsigned int lane_mask); int (*set_min_bus_tput)(struct device *dev, unsigned long r); diff --git a/include/uapi/drm/omap_drm.h b/include/uapi/drm/omap_drm.h index 7fb97863c945..fd5e3ea53f2b 100644 --- a/include/uapi/drm/omap_drm.h +++ b/include/uapi/drm/omap_drm.h @@ -106,8 +106,8 @@ struct drm_omap_gem_info { #define DRM_OMAP_GET_PARAM 0x00 #define DRM_OMAP_SET_PARAM 0x01 #define DRM_OMAP_GEM_NEW 0x03 -#define DRM_OMAP_GEM_CPU_PREP 0x04 -#define DRM_OMAP_GEM_CPU_FINI 0x05 +#define DRM_OMAP_GEM_CPU_PREP 0x04 /* Deprecated, to be removed */ +#define DRM_OMAP_GEM_CPU_FINI 0x05 /* Deprecated, to be removed */ #define DRM_OMAP_GEM_INFO 0x06 #define DRM_OMAP_NUM_IOCTLS 0x07 |