diff options
author | Dave Airlie <airlied@redhat.com> | 2020-06-23 10:58:28 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2020-06-24 15:45:51 +1000 |
commit | 0a19b068acc47d05212f03e494381926dc0381e2 (patch) | |
tree | 00490aed65afc870cd61e5551d63d5c032048806 /drivers/gpu/drm/drm_dp_mst_topology.c | |
parent | 48778464bb7d346b47157d21ffde2af6b2d39110 (diff) | |
parent | 114427b8927a4def2942b2b886f7e4aeae289ccb (diff) |
Merge tag 'drm-misc-next-2020-06-19' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for v5.9:
UAPI Changes:
- Add DRM_MODE_TYPE_USERDEF for video modes specified in cmdline.
Cross-subsystem Changes:
- Assorted devicetree binding updates.
- Add might_sleep() to dma_fence_wait().
- Fix fbdev's get_user_pages_fast() handling, and use pin_user_pages.
- Small cleanup with IS_BUILTIN in video/fbdev drivers.
- Fix video/hdmi coding style for infoframe size.
Core Changes:
- Silence vblank output during init.
- Fix DP-MST corruption during send msg timeout.
- Clear leak in drm_gem_objecs_lookup().
- Make newlines work with force connector attribute.
- Fix module refcounting error in drm_encoder_slave, and use new i2c api.
- Header fix for drm_managed.c
- More struct_mutex removal for !legacy drivers:
- Remove gem_free_object()
- Removal of drm_gem_object_put_unlocked().
- Show current->comm alongside pid in debug printfs.
- Add drm_client_modeset_check() + drm_client_framebuffer_flush().
- Replace drm_fb_swab16 with drm_fb_swap that also supports 32-bits.
- Remove mode->vrefresh, and compactify drm_display_mode.
- Use drm_* macros for logging and warnings.
- Add WARN when drm_gem_get_pages is used on a private obj.
- Handle importing and imported dmabuf better in shmem helpers.
- Small fix for drm/mm hole size comparison, and remove invalid entry optimization.
- Add a drm/mm selftest.
- Set DSI connector type for DSI panels.
- Assorted small fixes and documentation updates.
- Fix DDI I2C device registration for MST ports, and flushing on destroy.
- Fix master_set return type, used by vmwgfx.
- Make the drm_set/drop_master ioctl symmetrical.
Driver Changes:
Allow iommu in the sun4i driver and use it for sun8i.
- Simplify backlight lookup for omap, amba-clcd and tilcdc.
- Hold reg_lock for rockchip.
- Add support for bridge gpio and lane reordering + polarity to ti-sn65dsi86, and fix clock choice.
- Small assorted fixes to tilcdc, vc4, i915, omap, fbdev/sm712fb, fbdev/pxafb, console/newport_con, msm, virtio, udl, malidp, hdlcd, bridge/ti-sn65dsi86, panfrost.
- Remove hw cursor support for mgag200, and use simple kms helper + shmem helpers.
- Add support for KOE Allow iommu in the sun4i driver and use it for sun8i.
- Simplify backlight lookup for omap, amba-clcd and tilcdc.
- Hold reg_lock for rockchip.
- Add support for bridge gpio and lane reordering + polarity to ti-sn65dsi86, and fix clock choice.
- Small assorted fixes to tilcdc, vc4 (multiple), i915.
- Remove hw cursor support for mgag200, and use simple kms helper + shmem helpers.
- Add support for KOE TX26D202VM0BWA panel.
- Use GEM CMA functions in arc, arm, atmel-hlcdc, fsi-dcu, hisilicon, imx, ingenic, komeda, malidp, mcde, meson, msxfb, rcar-du, shmobile, stm, sti, tilcdc, tve200, zte.
- Remove gem_print_info.
- Improve gem_create_object_helper so udl can use shmem helpers.
- Convert vc4 dt bindings to schemas, and add clock properties.
- Device initialization cleanups for mgag200.
- Add a workaround to fix DP-MST short pulses handling on broken hardware in i915.
- Allow build test compiling arm drivers.
- Use managed pci functions in mgag200 and ast.
- Use dev_groups in malidp.
- Add per pixel alpha support for PX30 VOP in rockchip.
- Silence deferred probe logs in panfrost.
Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/001cd9a6-405d-4e29-43d8-354f53ae4e8b@linux.intel.com
Diffstat (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c')
-rw-r--r-- | drivers/gpu/drm/drm_dp_mst_topology.c | 94 |
1 files changed, 70 insertions, 24 deletions
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 1e26b89628f9..b2f5a84b4cfb 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -88,8 +88,8 @@ static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr, static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr, u8 *guid); -static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux); -static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux); +static int drm_dp_mst_register_i2c_bus(struct drm_dp_mst_port *port); +static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_mst_port *port); static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr); #define DBG_PREFIX "[dp_mst]" @@ -1178,12 +1178,38 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, struct drm_dp_sideband_msg_tx *txmsg) { struct drm_dp_mst_topology_mgr *mgr = mstb->mgr; + unsigned long wait_timeout = msecs_to_jiffies(4000); + unsigned long wait_expires = jiffies + wait_timeout; int ret; - ret = wait_event_timeout(mgr->tx_waitq, - check_txmsg_state(mgr, txmsg), - (4 * HZ)); - mutex_lock(&mstb->mgr->qlock); + for (;;) { + /* + * If the driver provides a way for this, change to + * poll-waiting for the MST reply interrupt if we didn't receive + * it for 50 msec. This would cater for cases where the HPD + * pulse signal got lost somewhere, even though the sink raised + * the corresponding MST interrupt correctly. One example is the + * Club 3D CAC-1557 TypeC -> DP adapter which for some reason + * filters out short pulses with a duration less than ~540 usec. + * + * The poll period is 50 msec to avoid missing an interrupt + * after the sink has cleared it (after a 110msec timeout + * since it raised the interrupt). + */ + ret = wait_event_timeout(mgr->tx_waitq, + check_txmsg_state(mgr, txmsg), + mgr->cbs->poll_hpd_irq ? + msecs_to_jiffies(50) : + wait_timeout); + + if (ret || !mgr->cbs->poll_hpd_irq || + time_after(jiffies, wait_expires)) + break; + + mgr->cbs->poll_hpd_irq(mgr); + } + + mutex_lock(&mgr->qlock); if (ret > 0) { if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) { ret = -EIO; @@ -1197,7 +1223,8 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb, /* remove from q */ if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED || - txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND) + txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND || + txmsg->state == DRM_DP_SIDEBAND_TX_SENT) list_del(&txmsg->next); } out: @@ -1603,7 +1630,7 @@ static void drm_dp_destroy_mst_branch_device(struct kref *kref) mutex_lock(&mgr->delayed_destroy_lock); list_add(&mstb->destroy_next, &mgr->destroy_branch_device_list); mutex_unlock(&mgr->delayed_destroy_lock); - schedule_work(&mgr->delayed_destroy_work); + queue_work(mgr->delayed_destroy_wq, &mgr->delayed_destroy_work); } /** @@ -1720,7 +1747,7 @@ static void drm_dp_destroy_port(struct kref *kref) mutex_lock(&mgr->delayed_destroy_lock); list_add(&port->next, &mgr->destroy_port_list); mutex_unlock(&mgr->delayed_destroy_lock); - schedule_work(&mgr->delayed_destroy_work); + queue_work(mgr->delayed_destroy_wq, &mgr->delayed_destroy_work); } /** @@ -1966,7 +1993,7 @@ drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt, } /* remove i2c over sideband */ - drm_dp_mst_unregister_i2c_bus(&port->aux); + drm_dp_mst_unregister_i2c_bus(port); } else { mutex_lock(&mgr->lock); drm_dp_mst_topology_put_mstb(port->mstb); @@ -1981,7 +2008,7 @@ drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt, if (port->pdt != DP_PEER_DEVICE_NONE) { if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) { /* add i2c over sideband */ - ret = drm_dp_mst_register_i2c_bus(&port->aux); + ret = drm_dp_mst_register_i2c_bus(port); } else { lct = drm_dp_calculate_rad(port, rad); mstb = drm_dp_add_mst_branch_device(lct, rad); @@ -2894,8 +2921,9 @@ out: return ret < 0 ? ret : changed; } -void drm_dp_send_clear_payload_id_table(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_branch *mstb) +static void +drm_dp_send_clear_payload_id_table(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_branch *mstb) { struct drm_dp_sideband_msg_tx *txmsg; int ret; @@ -4641,12 +4669,13 @@ static void drm_dp_tx_work(struct work_struct *work) static inline void drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port) { + drm_dp_port_set_pdt(port, DP_PEER_DEVICE_NONE, port->mcs); + if (port->connector) { drm_connector_unregister(port->connector); drm_connector_put(port->connector); } - drm_dp_port_set_pdt(port, DP_PEER_DEVICE_NONE, port->mcs); drm_dp_mst_put_port_malloc(port); } @@ -5179,6 +5208,15 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr, INIT_LIST_HEAD(&mgr->destroy_port_list); INIT_LIST_HEAD(&mgr->destroy_branch_device_list); INIT_LIST_HEAD(&mgr->up_req_list); + + /* + * delayed_destroy_work will be queued on a dedicated WQ, so that any + * requeuing will be also flushed when deiniting the topology manager. + */ + mgr->delayed_destroy_wq = alloc_ordered_workqueue("drm_dp_mst_wq", 0); + if (mgr->delayed_destroy_wq == NULL) + return -ENOMEM; + INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work); INIT_WORK(&mgr->tx_work, drm_dp_tx_work); INIT_WORK(&mgr->delayed_destroy_work, drm_dp_delayed_destroy_work); @@ -5223,7 +5261,11 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr) { drm_dp_mst_topology_mgr_set_mst(mgr, false); flush_work(&mgr->work); - cancel_work_sync(&mgr->delayed_destroy_work); + /* The following will also drain any requeued work on the WQ. */ + if (mgr->delayed_destroy_wq) { + destroy_workqueue(mgr->delayed_destroy_wq); + mgr->delayed_destroy_wq = NULL; + } mutex_lock(&mgr->payload_lock); kfree(mgr->payloads); mgr->payloads = NULL; @@ -5346,22 +5388,26 @@ static const struct i2c_algorithm drm_dp_mst_i2c_algo = { /** * drm_dp_mst_register_i2c_bus() - register an I2C adapter for I2C-over-AUX - * @aux: DisplayPort AUX channel + * @port: The port to add the I2C bus on * * Returns 0 on success or a negative error code on failure. */ -static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux) +static int drm_dp_mst_register_i2c_bus(struct drm_dp_mst_port *port) { + struct drm_dp_aux *aux = &port->aux; + struct device *parent_dev = port->mgr->dev->dev; + aux->ddc.algo = &drm_dp_mst_i2c_algo; aux->ddc.algo_data = aux; aux->ddc.retries = 3; aux->ddc.class = I2C_CLASS_DDC; aux->ddc.owner = THIS_MODULE; - aux->ddc.dev.parent = aux->dev; - aux->ddc.dev.of_node = aux->dev->of_node; + /* FIXME: set the kdev of the port's connector as parent */ + aux->ddc.dev.parent = parent_dev; + aux->ddc.dev.of_node = parent_dev->of_node; - strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev), + strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(parent_dev), sizeof(aux->ddc.name)); return i2c_add_adapter(&aux->ddc); @@ -5369,11 +5415,11 @@ static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux) /** * drm_dp_mst_unregister_i2c_bus() - unregister an I2C-over-AUX adapter - * @aux: DisplayPort AUX channel + * @port: The port to remove the I2C bus from */ -static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux) +static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_mst_port *port) { - i2c_del_adapter(&aux->ddc); + i2c_del_adapter(&port->aux.ddc); } /** @@ -5447,7 +5493,7 @@ struct drm_dp_aux *drm_dp_mst_dsc_aux_for_port(struct drm_dp_mst_port *port) { struct drm_dp_mst_port *immediate_upstream_port; struct drm_dp_mst_port *fec_port; - struct drm_dp_desc desc = { }; + struct drm_dp_desc desc = {}; u8 endpoint_fec; u8 endpoint_dsc; |