diff --git a/Documentation/devicetree/bindings/display/brcm,bcm2835-v3d.yaml b/Documentation/devicetree/bindings/display/brcm,bcm2835-v3d.yaml index c55a8217de25..1063e686647c 100644 --- a/Documentation/devicetree/bindings/display/brcm,bcm2835-v3d.yaml +++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-v3d.yaml @@ -27,6 +27,9 @@ properties: power-domains: maxItems: 1 + resets: + maxItems: 1 + required: - compatible - reg diff --git a/MAINTAINERS b/MAINTAINERS index b7c12320e1e3..4d108339f44e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9156,10 +9156,11 @@ F: drivers/gpu/drm/drm_ras* F: include/drm/drm_ras* DRM TTM SUBSYSTEM -M: Christian Koenig -M: Huang Rui +M: Natalie Vock +M: Arun Pravin R: Matthew Auld R: Matthew Brost +R: Thomas Hellström L: dri-devel@lists.freedesktop.org S: Maintained T: git https://gitlab.freedesktop.org/drm/misc/kernel.git diff --git a/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi b/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi index 1e76b290510d..39eea8ce1058 100644 --- a/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm2835-common.dtsi @@ -139,6 +139,7 @@ compatible = "brcm,bcm2835-v3d"; reg = <0x7ec00000 0x1000>; interrupts = <1 10>; + resets = <&pm BCM2835_RESET_V3D>; }; }; diff --git a/drivers/accel/amdxdna/aie2_message.c b/drivers/accel/amdxdna/aie2_message.c index b4c49259a1a2..f658760c3d48 100644 --- a/drivers/accel/amdxdna/aie2_message.c +++ b/drivers/accel/amdxdna/aie2_message.c @@ -277,6 +277,7 @@ int aie2_create_context(struct amdxdna_dev_hdl *ndev, struct amdxdna_hwctx *hwct free_channel: xdna_mailbox_free_channel(hwctx->priv->mbox_chann); + hwctx->priv->mbox_chann = NULL; del_ctx_req: aie2_destroy_context_req(ndev, hwctx->fw_ctx_id); return ret; diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 5a1a2ee261b7..f4832337ec31 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -164,30 +164,6 @@ void amdxdna_gem_heap_free(struct amdxdna_client *client, struct amdxdna_gem_obj mutex_unlock(&client->mm_lock); } -static struct amdxdna_gem_obj * -amdxdna_gem_create_obj(struct drm_device *dev, size_t size) -{ - struct amdxdna_gem_obj *abo; - - abo = kzalloc_obj(*abo); - if (!abo) - return ERR_PTR(-ENOMEM); - - abo->pinned = false; - abo->assigned_hwctx = AMDXDNA_INVALID_CTX_HANDLE; - mutex_init(&abo->lock); - - abo->mem.dma_addr = AMDXDNA_INVALID_ADDR; - abo->mem.uva = AMDXDNA_INVALID_ADDR; - abo->mem.size = size; - abo->open_ref = 0; - abo->internal = false; - INIT_LIST_HEAD(&abo->mem.umap_list); - xa_init_flags(&abo->heap_xa, XA_FLAGS_ALLOC); - - return abo; -} - static void amdxdna_gem_destroy_obj(struct amdxdna_gem_obj *abo) { @@ -278,10 +254,8 @@ static bool amdxdna_hmm_invalidate(struct mmu_interval_notifier *mni, if (range->event == MMU_NOTIFY_UNMAP) { down_write(&xdna->notifier_lock); - if (!mapp->unmapped) { - queue_work(xdna->notifier_wq, &mapp->hmm_unreg_work); - mapp->unmapped = true; - } + mapp->unmapped = true; + queue_work(xdna->notifier_wq, &abo->hmm_unreg_work); up_write(&xdna->notifier_lock); } @@ -311,13 +285,13 @@ static void amdxdna_hmm_unregister(struct amdxdna_gem_obj *abo, if (!compare_range(mapp, vma->vm_mm, vma->vm_start, vma->vm_end)) continue; - queue_work(xdna->notifier_wq, &mapp->hmm_unreg_work); mapp->unmapped = true; + queue_work(xdna->notifier_wq, &abo->hmm_unreg_work); } up_write(&xdna->notifier_lock); } -static void amdxdna_hmm_unregister_all(struct amdxdna_gem_obj *abo) +static void amdxdna_hmm_unreg_umaps(struct amdxdna_gem_obj *abo, bool force) { struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev); struct amdxdna_umap *mapp, *tmp; @@ -325,16 +299,18 @@ static void amdxdna_hmm_unregister_all(struct amdxdna_gem_obj *abo) down_write(&xdna->notifier_lock); list_for_each_entry_safe(mapp, tmp, &abo->mem.umap_list, node) { + if (!force && !mapp->unmapped) + continue; + mapp->unmapped = true; - mapp->cleanup = true; list_move(&mapp->node, &dead); } + if (list_empty(&abo->mem.umap_list)) + abo->mem.uva = AMDXDNA_INVALID_ADDR; up_write(&xdna->notifier_lock); - list_for_each_entry_safe(mapp, tmp, &dead, node) { - cancel_work_sync(&mapp->hmm_unreg_work); + list_for_each_entry_safe(mapp, tmp, &dead, node) amdxdna_umap_put(mapp); - } } static void amdxdna_umap_release(struct kref *ref) @@ -353,24 +329,10 @@ void amdxdna_umap_put(struct amdxdna_umap *mapp) static void amdxdna_hmm_unreg_work(struct work_struct *work) { - struct amdxdna_umap *mapp = container_of(work, struct amdxdna_umap, - hmm_unreg_work); - struct amdxdna_gem_obj *abo = mapp->abo; - struct amdxdna_dev *xdna; + struct amdxdna_gem_obj *abo = container_of(work, struct amdxdna_gem_obj, + hmm_unreg_work); - xdna = to_xdna_dev(to_gobj(mapp->abo)->dev); - down_write(&xdna->notifier_lock); - if (mapp->cleanup) { - up_write(&xdna->notifier_lock); - return; - } - - list_del(&mapp->node); - if (list_empty(&abo->mem.umap_list)) - abo->mem.uva = AMDXDNA_INVALID_ADDR; - up_write(&xdna->notifier_lock); - - amdxdna_umap_put(mapp); + amdxdna_hmm_unreg_umaps(abo, false); } static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, @@ -415,6 +377,21 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, goto free_map; } + mapp->range.notifier = &mapp->notifier; + mapp->range.start = vma->vm_start; + mapp->range.end = vma->vm_end; + /* + * Access permissions are fixed at mmap() time. Changing them later + * with mprotect() is not supported: the range keeps requesting the + * original permissions, so the application may see a fault failure + * or an IOMMU fault. + */ + mapp->range.default_flags = HMM_PFN_REQ_FAULT; + if (vma->vm_flags & VM_WRITE) + mapp->range.default_flags |= HMM_PFN_REQ_WRITE; + mapp->abo = abo; + kref_init(&mapp->refcnt); + ret = mmu_interval_notifier_insert_locked(&mapp->notifier, current->mm, addr, @@ -425,15 +402,6 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, goto free_pfns; } - mapp->range.notifier = &mapp->notifier; - mapp->range.start = vma->vm_start; - mapp->range.end = vma->vm_end; - mapp->range.default_flags = HMM_PFN_REQ_FAULT; - mapp->abo = abo; - kref_init(&mapp->refcnt); - - INIT_WORK(&mapp->hmm_unreg_work, amdxdna_hmm_unreg_work); - down_write(&xdna->notifier_lock); if (list_empty(&abo->mem.umap_list)) abo->mem.uva = addr; @@ -449,6 +417,31 @@ free_map: return ret; } +static struct amdxdna_gem_obj * +amdxdna_gem_create_obj(struct drm_device *dev, size_t size) +{ + struct amdxdna_gem_obj *abo; + + abo = kzalloc_obj(*abo); + if (!abo) + return ERR_PTR(-ENOMEM); + + abo->pinned = false; + abo->assigned_hwctx = AMDXDNA_INVALID_CTX_HANDLE; + mutex_init(&abo->lock); + + abo->mem.dma_addr = AMDXDNA_INVALID_ADDR; + abo->mem.uva = AMDXDNA_INVALID_ADDR; + abo->mem.size = size; + abo->open_ref = 0; + abo->internal = false; + INIT_LIST_HEAD(&abo->mem.umap_list); + xa_init_flags(&abo->heap_xa, XA_FLAGS_ALLOC); + INIT_WORK(&abo->hmm_unreg_work, amdxdna_hmm_unreg_work); + + return abo; +} + static void amdxdna_gem_dev_obj_free(struct drm_gem_object *gobj) { struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev); @@ -756,7 +749,9 @@ static void amdxdna_gem_obj_free(struct drm_gem_object *gobj) struct amdxdna_dev *xdna = to_xdna_dev(gobj->dev); struct amdxdna_gem_obj *abo = to_xdna_obj(gobj); - amdxdna_hmm_unregister_all(abo); + /* No notifier survives this, so no new work can be queued. */ + amdxdna_hmm_unreg_umaps(abo, true); + cancel_work_sync(&abo->hmm_unreg_work); if (abo->pinned) amdxdna_gem_unpin(abo); @@ -1508,7 +1503,7 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev, amdxdna_gem_unpin(abo); if (ret) { - drm_WARN(&xdna->ddev, 1, "Can not get flush memory"); + XDNA_DBG(xdna, "Flush BO %d failed, ret %d", args->handle, ret); goto put_obj; } } @@ -1516,7 +1511,8 @@ int amdxdna_drm_sync_bo_ioctl(struct drm_device *dev, XDNA_DBG(xdna, "Sync bo %d offset 0x%llx, size 0x%llx\n", args->handle, args->offset, args->size); - if (args->direction == SYNC_DIRECT_FROM_DEVICE) + if (abo->assigned_hwctx != AMDXDNA_INVALID_CTX_HANDLE && + args->direction == SYNC_DIRECT_FROM_DEVICE) ret = amdxdna_hwctx_sync_debug_bo(client, args->handle); put_obj: diff --git a/drivers/accel/amdxdna/amdxdna_gem.h b/drivers/accel/amdxdna/amdxdna_gem.h index 5dfefdcf1356..9b4aa21a37c9 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.h +++ b/drivers/accel/amdxdna/amdxdna_gem.h @@ -14,13 +14,11 @@ struct amdxdna_umap { struct mmu_interval_notifier notifier; struct hmm_range range; - struct work_struct hmm_unreg_work; struct amdxdna_gem_obj *abo; struct list_head node; struct kref refcnt; bool invalid; bool unmapped; - bool cleanup; }; struct amdxdna_mem { @@ -44,6 +42,7 @@ struct amdxdna_gem_obj { struct mutex lock; /* Protects: pinned, mem.kva, open_ref */ struct amdxdna_mem mem; int open_ref; + struct work_struct hmm_unreg_work; /* Below members are initialized when needed */ struct drm_mm_node mm_node; /* For AMDXDNA_BO_DEV */ diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c index 157ff148efe7..ec7b9db8b0b4 100644 --- a/drivers/gpu/drm/arm/malidp_crtc.c +++ b/drivers/gpu/drm/arm/malidp_crtc.c @@ -56,6 +56,7 @@ static void malidp_crtc_atomic_enable(struct drm_crtc *crtc, if (err < 0) { DRM_DEBUG_DRIVER("Failed to enable runtime power management: %d\n", err); + pm_runtime_put_noidle(crtc->dev->dev); return; } diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c index 00feb73e63d7..707d7143e475 100644 --- a/drivers/gpu/drm/display/drm_bridge_connector.c +++ b/drivers/gpu/drm/display/drm_bridge_connector.c @@ -208,49 +208,6 @@ static void drm_bridge_connector_disable_hpd(struct drm_connector *connector) * Bridge Connector Functions */ -static enum drm_connector_status -drm_bridge_connector_detect(struct drm_connector *connector, bool force) -{ - struct drm_bridge_connector *bridge_connector = - to_drm_bridge_connector(connector); - struct drm_bridge *detect = bridge_connector->bridge_detect; - struct drm_bridge *hdmi = bridge_connector->bridge_hdmi; - enum drm_connector_status status; - - if (detect) { - status = detect->funcs->detect(detect, connector); - - if (hdmi) - drm_atomic_helper_connector_hdmi_hotplug(connector, status); - - drm_bridge_connector_hpd_notify(connector, status); - } else { - switch (connector->connector_type) { - case DRM_MODE_CONNECTOR_DPI: - case DRM_MODE_CONNECTOR_LVDS: - case DRM_MODE_CONNECTOR_DSI: - case DRM_MODE_CONNECTOR_eDP: - status = connector_status_connected; - break; - default: - status = connector_status_unknown; - break; - } - } - - return status; -} - -static void drm_bridge_connector_force(struct drm_connector *connector) -{ - struct drm_bridge_connector *bridge_connector = - to_drm_bridge_connector(connector); - struct drm_bridge *hdmi = bridge_connector->bridge_hdmi; - - if (hdmi) - drm_atomic_helper_connector_hdmi_force(connector); -} - static void drm_bridge_connector_debugfs_init(struct drm_connector *connector, struct dentry *root) { @@ -310,8 +267,6 @@ drm_bridge_connector_color_format(const struct drm_connector_state *conn_state) } static const struct drm_connector_funcs drm_bridge_connector_funcs = { - .detect = drm_bridge_connector_detect, - .force = drm_bridge_connector_force, .fill_modes = drm_helper_probe_single_connector_modes, .atomic_create_state = drm_bridge_connector_create_state, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, @@ -325,15 +280,64 @@ static const struct drm_connector_funcs drm_bridge_connector_funcs = { * Bridge Connector Helper Functions */ +static int drm_bridge_connector_detect_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *detect = bridge_connector->bridge_detect; + struct drm_bridge *hdmi = bridge_connector->bridge_hdmi; + enum drm_connector_status status; + int ret; + + if (detect) { + status = detect->funcs->detect(detect, connector); + + if (hdmi) { + ret = drm_atomic_helper_connector_hdmi_hotplug(connector, ctx, status); + if (ret == -EDEADLK) + return ret; + } + + drm_bridge_connector_hpd_notify(connector, status); + } else { + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_DPI: + case DRM_MODE_CONNECTOR_LVDS: + case DRM_MODE_CONNECTOR_DSI: + case DRM_MODE_CONNECTOR_eDP: + status = connector_status_connected; + break; + default: + status = connector_status_unknown; + break; + } + } + + return status; +} + +static int drm_bridge_connector_force_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *hdmi = bridge_connector->bridge_hdmi; + + if (hdmi) + return drm_atomic_helper_connector_hdmi_force_ctx(connector, ctx); + + return 0; +} + static int drm_bridge_connector_get_modes_edid(struct drm_connector *connector, struct drm_bridge *bridge) { - enum drm_connector_status status; const struct drm_edid *drm_edid; int n; - status = drm_bridge_connector_detect(connector, false); - if (status != connector_status_connected) + if (connector->status != connector_status_connected) goto no_edid; drm_edid = drm_bridge_edid_read(bridge, connector); @@ -418,6 +422,8 @@ static int drm_bridge_connector_atomic_check(struct drm_connector *connector, static const struct drm_connector_helper_funcs drm_bridge_connector_helper_funcs = { .get_modes = drm_bridge_connector_get_modes, + .detect_ctx = drm_bridge_connector_detect_ctx, + .force_ctx = drm_bridge_connector_force_ctx, .mode_valid = drm_bridge_connector_mode_valid, .enable_hpd = drm_bridge_connector_enable_hpd, .disable_hpd = drm_bridge_connector_disable_hpd, @@ -578,6 +584,32 @@ static int drm_bridge_connector_write_spd_infoframe(struct drm_connector *connec return bridge->funcs->hdmi_write_spd_infoframe(bridge, buffer, len); } +static int drm_bridge_connector_scrambler_enable(struct drm_connector *connector) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi; + if (!bridge) + return -EINVAL; + + return bridge->funcs->hdmi_scrambler_enable(bridge); +} + +static int drm_bridge_connector_scrambler_disable(struct drm_connector *connector) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_bridge *bridge; + + bridge = bridge_connector->bridge_hdmi; + if (!bridge) + return -EINVAL; + + return bridge->funcs->hdmi_scrambler_disable(bridge); +} + static const struct drm_edid * drm_bridge_connector_read_edid(struct drm_connector *connector) { @@ -593,6 +625,8 @@ drm_bridge_connector_read_edid(struct drm_connector *connector) } static const struct drm_connector_hdmi_funcs drm_bridge_connector_hdmi_funcs = { + .supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444), + .max_bpc = 8, .tmds_char_rate_valid = drm_bridge_connector_tmds_char_rate_valid, .read_edid = drm_bridge_connector_read_edid, .avi = { @@ -603,7 +637,7 @@ static const struct drm_connector_hdmi_funcs drm_bridge_connector_hdmi_funcs = { .clear_infoframe = drm_bridge_connector_clear_hdmi_infoframe, .write_infoframe = drm_bridge_connector_write_hdmi_infoframe, }, - /* audio, hdr_drm and spd are set dynamically during init */ + /* scrambler, audio, hdr_drm and spd are set dynamically during init */ }; static const struct drm_connector_infoframe_funcs drm_bridge_connector_hdmi_audio_infoframe = { @@ -826,8 +860,6 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, struct drm_connector *connector; struct i2c_adapter *ddc = NULL; struct drm_bridge *panel_bridge __free(drm_bridge_put) = NULL; - unsigned int supported_formats = BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444); - unsigned int max_bpc = 8; bool support_hdcp = false; int connector_type; int ret; @@ -909,12 +941,12 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, !bridge->funcs->hdmi_clear_spd_infoframe)) return ERR_PTR(-EINVAL); - bridge_connector->bridge_hdmi = drm_bridge_get(bridge); + if (bridge->supported_hdmi_ver >= HDMI_VERSION_2_0 && + (!bridge->funcs->hdmi_scrambler_enable || + !bridge->funcs->hdmi_scrambler_disable)) + return ERR_PTR(-EINVAL); - if (bridge->supported_formats) - supported_formats = bridge->supported_formats; - if (bridge->max_bpc) - max_bpc = bridge->max_bpc; + bridge_connector->bridge_hdmi = drm_bridge_get(bridge); } if (bridge->ops & DRM_BRIDGE_OP_HDMI_AUDIO) { @@ -996,11 +1028,31 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, return ERR_PTR(-EINVAL); if (bridge_connector->bridge_hdmi) { - if (!connector->ycbcr_420_allowed) - supported_formats &= ~BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420); - bridge_connector->hdmi_funcs = drm_bridge_connector_hdmi_funcs; + bridge_connector->hdmi_funcs.vendor = bridge_connector->bridge_hdmi->vendor; + bridge_connector->hdmi_funcs.product = bridge_connector->bridge_hdmi->product; + + if (bridge_connector->bridge_hdmi->supported_hdmi_ver) + bridge_connector->hdmi_funcs.supported_hdmi_ver = + bridge_connector->bridge_hdmi->supported_hdmi_ver; + + if (bridge_connector->bridge_hdmi->supported_formats) + bridge_connector->hdmi_funcs.supported_formats = + bridge_connector->bridge_hdmi->supported_formats; + + if (!connector->ycbcr_420_allowed) + bridge_connector->hdmi_funcs.supported_formats &= + ~BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420); + + if (bridge_connector->bridge_hdmi->max_tmds_char_rate) + bridge_connector->hdmi_funcs.supported_tmds_char_rate = + bridge_connector->bridge_hdmi->max_tmds_char_rate; + + if (bridge_connector->bridge_hdmi->max_bpc) + bridge_connector->hdmi_funcs.max_bpc = + bridge_connector->bridge_hdmi->max_bpc; + if (bridge_connector->bridge_hdmi->ops & DRM_BRIDGE_OP_HDMI_AUDIO) bridge_connector->hdmi_funcs.audio = drm_bridge_connector_hdmi_audio_infoframe; @@ -1013,14 +1065,17 @@ struct drm_connector *drm_bridge_connector_init(struct drm_device *drm, bridge_connector->hdmi_funcs.spd = drm_bridge_connector_hdmi_spd_infoframe; + if (bridge_connector->bridge_hdmi->supported_hdmi_ver >= HDMI_VERSION_2_0) { + bridge_connector->hdmi_funcs.scrambler_enable = + drm_bridge_connector_scrambler_enable; + bridge_connector->hdmi_funcs.scrambler_disable = + drm_bridge_connector_scrambler_disable; + } + ret = drmm_connector_hdmi_init(drm, connector, - bridge_connector->bridge_hdmi->vendor, - bridge_connector->bridge_hdmi->product, &drm_bridge_connector_funcs, &bridge_connector->hdmi_funcs, - connector_type, ddc, - supported_formats, - max_bpc); + connector_type, ddc); if (ret) return ERR_PTR(ret); } else { diff --git a/drivers/gpu/drm/display/drm_hdmi_helper.c b/drivers/gpu/drm/display/drm_hdmi_helper.c index 5cb0b033b171..f85cce3d6254 100644 --- a/drivers/gpu/drm/display/drm_hdmi_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_helper.c @@ -4,12 +4,21 @@ #include #include +#include + +#include +#include #include +#include #include #include +#include #include #include +#define DRM_HDMI_SCDC_SOURCE_VERSION 1 +#define DRM_HDMI_SCDC_POLL_DELAY_MS 1000 + static inline bool is_eotf_supported(u8 output_eotf, u8 sink_eotf) { return sink_eotf & BIT(output_eotf); @@ -426,3 +435,298 @@ drm_hdmi_acr_get_n_cts(unsigned long long tmds_char_rate, *out_cts = cts; } EXPORT_SYMBOL(drm_hdmi_acr_get_n_cts); + +/** + * drm_hdmi_mode_needs_scrambling() - Check if an HDMI mode requires scrambling + * @mode: Display mode to check + * @bpc: Bits per color component + * @fmt: Output pixel format + * + * Computes the TMDS character rate for the given mode, bits per component and + * output format, and checks whether it exceeds the HDMI 1.3 maximum TMDS + * character rate of 340 MHz. + * + * Returns: + * true if the mode requires scrambling, false otherwise. + */ +bool drm_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode, + unsigned int bpc, + enum drm_output_color_format fmt) +{ + unsigned long long tmds_char_rate; + + tmds_char_rate = drm_hdmi_compute_mode_clock(mode, bpc, fmt); + + return tmds_char_rate > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ; +} +EXPORT_SYMBOL(drm_hdmi_mode_needs_scrambling); + +static bool drm_scdc_sink_supports_scrambling(struct drm_connector *connector) +{ + const struct drm_display_info *info = &connector->display_info; + + return info->is_hdmi && + info->hdmi.scdc.supported && + info->hdmi.scdc.scrambling.supported; +} + +static int drm_connector_hdmi_try_scrambling_setup(struct drm_connector *connector) +{ + bool done; + + done = drm_scdc_set_high_tmds_clock_ratio(connector, true); + if (!done) + return -EIO; + + done = drm_scdc_set_scrambling(connector, true); + if (!done) + return -EIO; + + if (READ_ONCE(connector->hdmi.scrambler_enabled)) + schedule_delayed_work(&connector->hdmi.scdc_work, + msecs_to_jiffies(DRM_HDMI_SCDC_POLL_DELAY_MS)); + + return 0; +} + +static void drm_connector_hdmi_scdc_work(struct work_struct *work) +{ + struct drm_connector *connector; + struct drm_connector_hdmi *hdmi; + + hdmi = container_of(to_delayed_work(work), struct drm_connector_hdmi, + scdc_work); + connector = container_of(hdmi, struct drm_connector, hdmi); + + /* + * The connector, and hence this work item, may outlive the source + * driver, e.g. when userspace keeps a DRM file descriptor open past + * unbind. Never poke the DDC bus after the connector got unregistered, + * as the underlying I2C adapter may already be gone. + */ + if (drm_connector_is_unregistered(connector)) + return; + + if (READ_ONCE(connector->hdmi.scrambler_enabled) && + !drm_scdc_get_scrambling_status(connector)) + drm_connector_hdmi_try_scrambling_setup(connector); +} + +/** + * drm_connector_hdmi_enable_scrambling() - enable scrambling and monitor SCDC status + * @connector: connector + * @conn_state: connector state + * + * Enables scrambling and high TMDS clock ratio on both source and sink sides. + * Additionally, use a delayed work item to monitor the scrambling status on + * the sink side and retry the operation, as some displays refuse to set the + * scrambling bit right away. + * + * The work item is cancelled by drm_connector_hdmi_disable_scrambling(), hence + * source drivers must ensure the display pipeline is shut down on unbind, e.g. + * via drm_atomic_helper_shutdown(). Otherwise the work may outlive the driver's + * devres-managed resources, since the connector itself is only cleaned up when + * the last reference to the DRM device is dropped. + * + * Returns: + * Zero if scrambling is set successfully, an error code otherwise. + */ +int drm_connector_hdmi_enable_scrambling(struct drm_connector *connector, + const struct drm_connector_state *conn_state) +{ + struct drm_connector_hdmi *hdmi = &connector->hdmi; + struct drm_device *dev = connector->dev; + int ret; + + if (!conn_state) + return -EINVAL; + + if (!conn_state->hdmi.scrambler_needed) + return 0; + + if (!drm_connector_hdmi_scrambler_supported(connector)) { + drm_dbg_kms(dev, "Source doesn't support scrambling.\n"); + return -EINVAL; + } + + if (!drm_scdc_sink_supports_scrambling(connector)) { + drm_dbg_kms(dev, "Sink doesn't support scrambling.\n"); + return -EINVAL; + } + + if (!connector->ddc) + return -EINVAL; + + /* + * Advertise our SCDC source version. This is purely informational and + * does not gate scrambling, so failures are non-fatal. + */ + drm_scdc_set_source_version(connector, DRM_HDMI_SCDC_SOURCE_VERSION); + + drm_dbg_kms(dev, "Enabling scrambling\n"); + + if (!hdmi->scdc_work_initialized) { + INIT_DELAYED_WORK(&hdmi->scdc_work, + drm_connector_hdmi_scdc_work); + hdmi->scdc_work_initialized = true; + } + + WRITE_ONCE(hdmi->scrambler_enabled, true); + + ret = drm_connector_hdmi_try_scrambling_setup(connector); + if (!ret) + ret = hdmi->funcs->scrambler_enable(connector); + + if (ret) { + WRITE_ONCE(hdmi->scrambler_enabled, false); + cancel_delayed_work_sync(&hdmi->scdc_work); + + drm_scdc_set_scrambling(connector, false); + drm_scdc_set_high_tmds_clock_ratio(connector, false); + } + + return ret; +} +EXPORT_SYMBOL(drm_connector_hdmi_enable_scrambling); + +/** + * drm_connector_hdmi_disable_scrambling() - disable scrambling and SCDC monitoring + * @connector: connector + * + * Disables scrambling and high TMDS clock ratio on both source and sink sides. + * Also cancels the SCDC status monitoring work item, if it is still pending. + * + * Returns: + * Zero if scrambling is reset successfully, an error code otherwise. + */ +int drm_connector_hdmi_disable_scrambling(struct drm_connector *connector) +{ + struct drm_connector_hdmi *hdmi = &connector->hdmi; + struct drm_device *dev = connector->dev; + + if (!READ_ONCE(hdmi->scrambler_enabled)) + return 0; + + drm_dbg_kms(dev, "Disabling scrambling\n"); + + WRITE_ONCE(hdmi->scrambler_enabled, false); + + /* + * A driver may force scrambler_enabled at init to trigger a disable + * at boot (e.g. vc4), bypassing the enable path that sets up the work + * item. Only cancel it once it has actually been initialized. + */ + if (hdmi->scdc_work_initialized) + cancel_delayed_work_sync(&hdmi->scdc_work); + + if (connector->ddc && + connector->status == connector_status_connected && + drm_scdc_sink_supports_scrambling(connector)) { + drm_scdc_set_scrambling(connector, false); + drm_scdc_set_high_tmds_clock_ratio(connector, false); + } + + return hdmi->funcs->scrambler_disable(connector); +} +EXPORT_SYMBOL(drm_connector_hdmi_disable_scrambling); + +/** + * drm_connector_hdmi_sync_scdc() - resync the sink-side SCDC upon reconnect + * @connector: connector + * @plugged: connector plugged status event + * @ctx: initialized lock acquisition context + * + * When receiving hotplug disconnect/reconnect event, while the display is + * still active (CRTC enabled), the SCDC status on the sink side is reset + * and must be explicitly restored. + * + * The typical solution for this is to trigger an empty modeset in + * drm_connector_helper_funcs.detect_ctx(), which is what this helper does + * by triggering a CRTC reset on reconnection. + * + * When making use of the HDMI connector framework, this is automatically + * triggered via drm_atomic_helper_connector_hdmi_hotplug(). + * + * Returns: + * Zero on success, an error code otherwise, including -EDEADLK. + */ +int drm_connector_hdmi_sync_scdc(struct drm_connector *connector, bool plugged, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_connector_hdmi *hdmi = &connector->hdmi; + struct drm_device *dev = connector->dev; + struct drm_connector_state *conn_state; + struct drm_crtc *crtc; + u8 config; + int ret; + + if (!connector || !plugged || !ctx) + return 0; + + /* + * This is normally part of .detect_ctx() call path, which already holds + * connection_mutex through @ctx. However, re-acquiring it with the + * same context is a no-op and makes the helper safe under any caller. + */ + ret = drm_modeset_lock(&connector->dev->mode_config.connection_mutex, ctx); + if (ret) + return ret; + + /* TODO: Also handle HDMI 2.1 FRL link training */ + + if (!drm_connector_hdmi_scrambler_supported(connector) || + !READ_ONCE(hdmi->scrambler_enabled)) + return 0; + + conn_state = connector->state; + if (!conn_state) + return 0; + + crtc = conn_state->crtc; + if (!crtc) + return 0; + + ret = drm_modeset_lock(&crtc->mutex, ctx); + if (ret) + return ret; + + if (!crtc->state || !crtc->state->active) + return 0; + + if (!connector->ddc || !drm_scdc_sink_supports_scrambling(connector)) + return 0; + + if (conn_state->commit && + !try_wait_for_completion(&conn_state->commit->hw_done)) + return 0; + + ret = drm_scdc_readb(connector->ddc, SCDC_TMDS_CONFIG, &config); + if (ret) { + drm_dbg_kms(dev, "Failed to read TMDS config: %d\n", ret); + return ret; + } + + if (config & SCDC_SCRAMBLING_ENABLE) + return 0; + + /* + * HDMI 2.0 requires that one should not send scrambled data prior + * to configuring the sink scrambling, and that TMDS clock/data + * transmission should be suspended when changing the TMDS clock rate + * in the sink. So let's just do a full modeset here, even though some + * sinks would be perfectly happy if were to just reconfigure the SCDC + * settings on the fly. + * + * The disable/re-enable cycle triggered by the reset should call + * drm_connector_hdmi_enable_scrambling() during re-enable, thus + * properly configuring the sink before data transmission resumes. + */ + drm_dbg_kms(dev, "Resetting CRTC to restore SCDC status\n"); + + ret = drm_atomic_helper_reset_crtc(crtc, ctx); + if (ret && ret != -EDEADLK) + drm_dbg_kms(dev, "Failed to reset CRTC: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL(drm_connector_hdmi_sync_scdc); diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c index bccc5d7c8eed..7cdb7ca3dc12 100644 --- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c +++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c @@ -562,6 +562,10 @@ hdmi_clock_valid(const struct drm_connector *connector, if (info->max_tmds_clock && clock > info->max_tmds_clock * 1000) return MODE_CLOCK_HIGH; + if (connector->hdmi.max_tmds_char_rate && + clock > connector->hdmi.max_tmds_char_rate) + return MODE_CLOCK_HIGH; + if (funcs && funcs->tmds_char_rate_valid) { enum drm_mode_status status; @@ -592,6 +596,9 @@ hdmi_compute_clock(const struct drm_connector *connector, conn_state->hdmi.tmds_char_rate = clock; + /* TODO: also check drm_display_info.hdmi.scdc.scrambling.low_rates */ + conn_state->hdmi.scrambler_needed = (clock > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ); + return 0; } @@ -1352,18 +1359,36 @@ drm_atomic_helper_connector_hdmi_clear_audio_infoframe(struct drm_connector *con } EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_clear_audio_infoframe); -static void +static int drm_atomic_helper_connector_hdmi_update(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, enum drm_connector_status status) { const struct drm_edid *drm_edid; + int ret; if (status == connector_status_disconnected) { - // TODO: also handle scramber, HDMI sink disconnected. + /* + * .detect_ctx() can only ever return a drm_connector_status or + * -EDEADLK. Handle deadlocks, and report any !EDEADLK error. + */ + ret = drm_connector_hdmi_sync_scdc(connector, false, ctx); + if (ret) { + if (ret == -EDEADLK) + return ret; + + drm_warn(connector->dev, "Ignored SCDC sync error: %d\n", ret); + } + drm_connector_hdmi_audio_plugged_notify(connector, false); - drm_edid_connector_update(connector, NULL); + + ret = drm_edid_connector_update(connector, NULL); + if (ret) + drm_warn(connector->dev, "Ignored EDID update error: %d\n", ret); + drm_connector_cec_phys_addr_invalidate(connector); - return; + + return 0; } if (connector->hdmi.funcs->read_edid) @@ -1376,24 +1401,43 @@ drm_atomic_helper_connector_hdmi_update(struct drm_connector *connector, drm_edid_free(drm_edid); if (status == connector_status_connected) { - // TODO: also handle scramber, HDMI sink is now connected. + /* + * .detect_ctx() can only ever return a drm_connector_status or + * -EDEADLK. Handle deadlocks, and report any !EDEADLK error. + */ + ret = drm_connector_hdmi_sync_scdc(connector, true, ctx); + if (ret) { + if (ret == -EDEADLK) + return ret; + + drm_warn(connector->dev, "Ignored SCDC sync error: %d\n", ret); + } + drm_connector_hdmi_audio_plugged_notify(connector, true); drm_connector_cec_phys_addr_set(connector); } + + return 0; } /** * drm_atomic_helper_connector_hdmi_hotplug - Handle the hotplug event for the HDMI connector * @connector: A pointer to the HDMI connector + * @ctx: Lock acquisition context to be used for resetting CRTC * @status: Connection status * * This function should be called as a part of the .detect() / .detect_ctx() * callbacks for all status changes. + * + * Returns: + * Zero on success, error code on failure. + * If @ctx is set, it might also return -EDEADLK. */ -void drm_atomic_helper_connector_hdmi_hotplug(struct drm_connector *connector, - enum drm_connector_status status) +int drm_atomic_helper_connector_hdmi_hotplug(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + enum drm_connector_status status) { - drm_atomic_helper_connector_hdmi_update(connector, status); + return drm_atomic_helper_connector_hdmi_update(connector, ctx, status); } EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_hotplug); @@ -1408,6 +1452,27 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_hotplug); */ void drm_atomic_helper_connector_hdmi_force(struct drm_connector *connector) { - drm_atomic_helper_connector_hdmi_update(connector, connector->status); + drm_atomic_helper_connector_hdmi_update(connector, NULL, connector->status); } EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_force); + +/** + * drm_atomic_helper_connector_hdmi_force_ctx - HDMI Connector implementation + * of the force_ctx callback + * @connector: A pointer to the HDMI connector + * @ctx: Lock acquisition context to be used for resetting CRTC + * + * This function implements the .force_ctx() callback for the HDMI connectors. + * It can either be used directly as the callback or should be called from + * within the .force_ctx() callback implementation to maintain the HDMI-specific + * connector's data. + * + * Returns: + * Zero on success, error code on failure, including -EDEADLK. + */ +int drm_atomic_helper_connector_hdmi_force_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx) +{ + return drm_atomic_helper_connector_hdmi_update(connector, ctx, connector->status); +} +EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_force_ctx); diff --git a/drivers/gpu/drm/display/drm_scdc_helper.c b/drivers/gpu/drm/display/drm_scdc_helper.c index d00da20862fc..74388481fc55 100644 --- a/drivers/gpu/drm/display/drm_scdc_helper.c +++ b/drivers/gpu/drm/display/drm_scdc_helper.c @@ -22,12 +22,13 @@ */ #include -#include -#include -#include #include #include +#include +#include +#include #include +#include #include #include @@ -58,6 +59,10 @@ #define SCDC_I2C_SLAVE_ADDRESS 0x54 +#define drm_scdc_dbg(connector, fmt, ...) \ + drm_dbg_kms((connector)->dev, "[CONNECTOR:%d:%s] " fmt, \ + (connector)->base.id, (connector)->name, ##__VA_ARGS__) + static const char *drm_scdc_frl_rate_str(enum drm_scdc_frl_rate rate) { switch (rate) { @@ -193,9 +198,7 @@ bool drm_scdc_get_scrambling_status(struct drm_connector *connector) ret = drm_scdc_readb(connector->ddc, SCDC_SCRAMBLER_STATUS, &status); if (ret < 0) { - drm_dbg_kms(connector->dev, - "[CONNECTOR:%d:%s] Failed to read scrambling status: %d\n", - connector->base.id, connector->name, ret); + drm_scdc_dbg(connector, "Failed to read scrambling status: %d\n", ret); return false; } @@ -223,9 +226,7 @@ bool drm_scdc_set_scrambling(struct drm_connector *connector, ret = drm_scdc_readb(connector->ddc, SCDC_TMDS_CONFIG, &config); if (ret < 0) { - drm_dbg_kms(connector->dev, - "[CONNECTOR:%d:%s] Failed to read TMDS config: %d\n", - connector->base.id, connector->name, ret); + drm_scdc_dbg(connector, "Failed to read TMDS config: %d\n", ret); return false; } @@ -236,9 +237,7 @@ bool drm_scdc_set_scrambling(struct drm_connector *connector, ret = drm_scdc_writeb(connector->ddc, SCDC_TMDS_CONFIG, config); if (ret < 0) { - drm_dbg_kms(connector->dev, - "[CONNECTOR:%d:%s] Failed to enable scrambling: %d\n", - connector->base.id, connector->name, ret); + drm_scdc_dbg(connector, "Failed to enable scrambling: %d\n", ret); return false; } @@ -283,9 +282,7 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct drm_connector *connector, ret = drm_scdc_readb(connector->ddc, SCDC_TMDS_CONFIG, &config); if (ret < 0) { - drm_dbg_kms(connector->dev, - "[CONNECTOR:%d:%s] Failed to read TMDS config: %d\n", - connector->base.id, connector->name, ret); + drm_scdc_dbg(connector, "Failed to read TMDS config: %d\n", ret); return false; } @@ -296,9 +293,7 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct drm_connector *connector, ret = drm_scdc_writeb(connector->ddc, SCDC_TMDS_CONFIG, config); if (ret < 0) { - drm_dbg_kms(connector->dev, - "[CONNECTOR:%d:%s] Failed to set TMDS clock ratio: %d\n", - connector->base.id, connector->name, ret); + drm_scdc_dbg(connector, "Failed to set TMDS clock ratio: %d\n", ret); return false; } @@ -312,6 +307,49 @@ bool drm_scdc_set_high_tmds_clock_ratio(struct drm_connector *connector, } EXPORT_SYMBOL(drm_scdc_set_high_tmds_clock_ratio); +/** + * drm_scdc_set_source_version - set SCDC source version on the sink + * @connector: connector + * @ver: source version to advertise (per spec, 1) + * + * Reads the sink version for diagnostics and as a guard, then writes + * the source version unless the sink reports version 0. + * + * Returns: + * 0 on success or when skipped; a negative error code when either + * the read or the write failed. + */ +int drm_scdc_set_source_version(struct drm_connector *connector, u8 ver) +{ + u8 sink_ver; + int ret; + + ret = drm_scdc_readb(connector->ddc, SCDC_SINK_VERSION, &sink_ver); + if (ret) { + drm_scdc_dbg(connector, "Failed to read SCDC_SINK_VERSION: %d\n", ret); + return ret; + } + + drm_scdc_dbg(connector, "Sink reported SCDC ver. %u\n", sink_ver); + + /* + * Only advertise our source version to sinks that report a + * non-zero sink version. A sink reporting version 0 is either + * not SCDC-version-aware or non-conformant; writing the source + * version gains nothing and may upset broken hardware. + */ + if (sink_ver) { + ret = drm_scdc_writeb(connector->ddc, SCDC_SOURCE_VERSION, + min_t(u8, sink_ver, ver)); + if (ret) + drm_scdc_dbg(connector, + "Failed to write SCDC_SOURCE_VERSION: %d\n", ret); + } + + return ret; +} +EXPORT_SYMBOL(drm_scdc_set_source_version); + static void drm_scdc_parse_status0_flags(u8 val, struct drm_scdc_status_flags *flags) { diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 649f6b6ab5c8..4571ae1f6f81 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -460,9 +460,13 @@ void drm_bridge_add(struct drm_bridge *bridge) mutex_init(&bridge->hpd_state_mutex); mutex_init(&bridge->hpd_mutex); - if (bridge->ops & DRM_BRIDGE_OP_HDMI) + if (bridge->ops & DRM_BRIDGE_OP_HDMI) { + if (bridge->supported_hdmi_ver == HDMI_VERSION_UNKNOWN) + DRM_WARN("HDMI bridge misses supported HDMI version\n"); + bridge->ycbcr_420_allowed = !!(bridge->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420)); + } mutex_lock(&bridge_lock); list_add_tail(&bridge->list, &bridge_list); diff --git a/drivers/gpu/drm/drm_bridge_helper.c b/drivers/gpu/drm/drm_bridge_helper.c index 420f29cf3e54..0a3c8fee66b3 100644 --- a/drivers/gpu/drm/drm_bridge_helper.c +++ b/drivers/gpu/drm/drm_bridge_helper.c @@ -50,8 +50,6 @@ int drm_bridge_helper_reset_crtc(struct drm_bridge *bridge, crtc = connector->state->crtc; ret = drm_atomic_helper_reset_crtc(crtc, ctx); - if (ret) - goto out; out: drm_modeset_unlock(&dev->mode_config.connection_mutex); diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 8b4baed060f3..d94c86bfed86 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -546,6 +546,150 @@ EXPORT_SYMBOL(drmm_connector_init); * drmm_connector_hdmi_init - Init a preallocated HDMI connector * @dev: DRM device * @connector: A pointer to the HDMI connector to init + * @funcs: callbacks for this connector + * @hdmi_funcs: HDMI-related callbacks and capabilities for this connector + * @connector_type: user visible type of the connector + * @ddc: optional pointer to the associated ddc adapter + * + * Initialises a preallocated HDMI connector. Connectors can be + * subclassed as part of driver connector objects. + * + * Cleanup is automatically handled with a call to + * drm_connector_cleanup() in a DRM-managed action. + * + * The connector structure should be allocated with drmm_kzalloc(). + * + * The @drm_connector_funcs.destroy hook must be NULL. + * + * Returns: + * Zero on success, error code on failure. + */ +int drmm_connector_hdmi_init(struct drm_device *dev, + struct drm_connector *connector, + const struct drm_connector_funcs *funcs, + const struct drm_connector_hdmi_funcs *hdmi_funcs, + int connector_type, + struct i2c_adapter *ddc) +{ + int ret; + + if (!hdmi_funcs) + return -EINVAL; + + if (!hdmi_funcs->vendor || !hdmi_funcs->product) + return -EINVAL; + + if ((strlen(hdmi_funcs->vendor) > DRM_CONNECTOR_HDMI_VENDOR_LEN) || + (strlen(hdmi_funcs->product) > DRM_CONNECTOR_HDMI_PRODUCT_LEN)) + return -EINVAL; + + if (!hdmi_funcs->supported_formats || + !(hdmi_funcs->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_RGB444))) + return -EINVAL; + + if (connector->ycbcr_420_allowed != + !!(hdmi_funcs->supported_formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420))) + return -EINVAL; + + if (!(hdmi_funcs->max_bpc == 8 || + hdmi_funcs->max_bpc == 10 || + hdmi_funcs->max_bpc == 12)) + return -EINVAL; + + if (!hdmi_funcs->avi.clear_infoframe || + !hdmi_funcs->avi.write_infoframe || + !hdmi_funcs->hdmi.clear_infoframe || + !hdmi_funcs->hdmi.write_infoframe) + return -EINVAL; + + if (hdmi_funcs->supported_hdmi_ver >= HDMI_VERSION_2_0 && + (!hdmi_funcs->scrambler_enable || + !hdmi_funcs->scrambler_disable)) { + drm_err(dev, "Scrambler callbacks missing for HDMI 2.x\n"); + return -EINVAL; + } + + if (!(connector_type == DRM_MODE_CONNECTOR_HDMIA || + connector_type == DRM_MODE_CONNECTOR_HDMIB)) + return -EINVAL; + + ret = drmm_connector_init(dev, connector, funcs, connector_type, ddc); + if (ret) + return ret; + + /* TODO: remove after conversion to new drmm_connector_hdmi_init() */ + connector->hdmi.supported_formats = hdmi_funcs->supported_formats; + + /* + * The supported HDMI version can be used to determinate the maximum + * TMDS character rate allowed by the specification. Some controllers, + * however, may support a lower rate than that version would imply. + * + * A non-zero caps->max_tmds_char_rate lets drivers override this + * default with the actual controller capability. A value of zero keeps + * the limit inferred from supported_hdmi_ver. + */ + if (hdmi_funcs->supported_hdmi_ver >= HDMI_VERSION_2_0) + connector->hdmi.max_tmds_char_rate = HDMI_2_0_TMDS_CHAR_RATE_MAX_HZ; + else if (hdmi_funcs->supported_hdmi_ver >= HDMI_VERSION_1_3) + connector->hdmi.max_tmds_char_rate = HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ; + else if (hdmi_funcs->supported_hdmi_ver >= HDMI_VERSION_1_0) + connector->hdmi.max_tmds_char_rate = HDMI_1_0_TMDS_CHAR_RATE_MAX_HZ; + + if (hdmi_funcs->supported_tmds_char_rate) { + if (hdmi_funcs->supported_tmds_char_rate > connector->hdmi.max_tmds_char_rate) { + drm_err(dev, "Enforced max_tmds_char_rate exceeds %llu spec limit\n", + connector->hdmi.max_tmds_char_rate); + return -EINVAL; + } + + connector->hdmi.max_tmds_char_rate = hdmi_funcs->supported_tmds_char_rate; + } + + /* TODO: remove after conversion to new drmm_connector_hdmi_init() */ + strtomem_pad(connector->hdmi.vendor, hdmi_funcs->vendor, 0); + strtomem_pad(connector->hdmi.product, hdmi_funcs->product, 0); + + /* + * drm_connector_attach_max_bpc_property() requires the + * connector to have a state. + */ + if (connector->funcs->atomic_create_state) { + struct drm_connector_state *state; + + state = connector->funcs->atomic_create_state(connector); + if (IS_ERR(state)) + return PTR_ERR(state); + + connector->state = state; + } else if (connector->funcs->reset) { + connector->funcs->reset(connector); + if (!connector->state) + return -ENOMEM; + } + + drm_connector_attach_max_bpc_property(connector, 8, hdmi_funcs->max_bpc); + /* TODO: remove after conversion to new drmm_connector_hdmi_init() */ + connector->max_bpc = hdmi_funcs->max_bpc; + + if (hdmi_funcs->max_bpc > 8) + drm_connector_attach_hdr_output_metadata_property(connector); + + ret = drm_connector_attach_color_format_property(connector, + hdmi_funcs->supported_formats); + if (ret) + return ret; + + connector->hdmi.funcs = hdmi_funcs; + + return 0; +} +EXPORT_SYMBOL(drmm_connector_hdmi_init); + +/** + * drmm_connector_hdmi_ini2 - Init a preallocated HDMI connector + * @dev: DRM device + * @connector: A pointer to the HDMI connector to init * @vendor: HDMI Controller Vendor name * @product: HDMI Controller Product name * @funcs: callbacks for this connector @@ -568,7 +712,7 @@ EXPORT_SYMBOL(drmm_connector_init); * Returns: * Zero on success, error code on failure. */ -int drmm_connector_hdmi_init(struct drm_device *dev, +int drmm_connector_hdmi_ini2(struct drm_device *dev, struct drm_connector *connector, const char *vendor, const char *product, const struct drm_connector_funcs *funcs, @@ -628,6 +772,8 @@ int drmm_connector_hdmi_init(struct drm_device *dev, connector->state = state; } else if (connector->funcs->reset) { connector->funcs->reset(connector); + if (!connector->state) + return -ENOMEM; } drm_connector_attach_max_bpc_property(connector, 8, max_bpc); @@ -644,7 +790,7 @@ int drmm_connector_hdmi_init(struct drm_device *dev, return 0; } -EXPORT_SYMBOL(drmm_connector_hdmi_init); +EXPORT_SYMBOL(drmm_connector_hdmi_ini2); /** * drm_connector_attach_edid_property - attach edid property. @@ -780,6 +926,9 @@ void drm_connector_cleanup(struct drm_connector *connector) DRM_CONNECTOR_REGISTERED)) drm_connector_unregister(connector); + if (connector->hdmi.scdc_work_initialized) + cancel_delayed_work_sync(&connector->hdmi.scdc_work); + platform_device_unregister(connector->hdmi_audio.codec_pdev); if (connector->privacy_screen) { diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index e26ead5b5e58..856b40fdaac4 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -559,6 +559,7 @@ static int __drm_helper_update_and_validate(struct drm_connector *connector, int drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY) { + const struct drm_connector_helper_funcs *funcs = connector->helper_private; struct drm_device *dev = connector->dev; struct drm_display_mode *mode; int count = 0, ret; @@ -592,8 +593,20 @@ retry: connector->status = connector_status_connected; else connector->status = connector_status_disconnected; - if (connector->funcs->force) + + if (funcs && funcs->force_ctx) { + ret = funcs->force_ctx(connector, &ctx); + if (ret == -EDEADLK) { + connector->status = old_status; + drm_modeset_backoff(&ctx); + goto retry; + } else if (ret < 0) { + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] force_ctx failed: %d\n", + connector->base.id, connector->name, ret); + } + } else if (connector->funcs->force) { connector->funcs->force(connector); + } } else { ret = drm_helper_probe_detect(connector, &ctx, true); diff --git a/drivers/gpu/drm/imagination/pvr_trace.h b/drivers/gpu/drm/imagination/pvr_trace.h index 943d6b2418cb..4be7d4b8a357 100644 --- a/drivers/gpu/drm/imagination/pvr_trace.h +++ b/drivers/gpu/drm/imagination/pvr_trace.h @@ -90,21 +90,25 @@ TRACE_EVENT(pvr_job_create, __entry->sync_op_count) ); -#undef PVR_JOB_TYPE_TO_STR #undef PVR_JOB_GET_HWRT_FW_ADDR TRACE_EVENT(pvr_job_submit_fw, TP_PROTO(struct pvr_job *job), TP_ARGS(job), TP_STRUCT__entry(__field(struct pvr_job *, job) - __field(u32, done_seqno)), + __field(u32, done_seqno) + __field(enum drm_pvr_job_type, job_type)), TP_fast_assign(__entry->job = job; - __entry->done_seqno = job->done_fence->seqno;), - TP_printk("job=%p done_seqno=%u", + __entry->done_seqno = job->done_fence->seqno; + __entry->job_type = job->type;), + TP_printk("job=%p done_seqno=%u job_type=%s", __entry->job, - __entry->done_seqno) + __entry->done_seqno, + PVR_JOB_TYPE_TO_STR(__entry->job_type)) ); +#undef PVR_JOB_TYPE_TO_STR + TRACE_EVENT(pvr_job_done, TP_PROTO(struct pvr_job *job), TP_ARGS(job), diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 38032bb95826..10cd606d01b9 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -323,7 +323,7 @@ get_fp_strap(struct drm_device *dev, struct nvbios *bios) /* * The fp strap is normally dictated by the "User Strap" in * PEXTDEV_BOOT_0[20:16], but on BMP cards when bit 2 of the - * Internal_Flags struct at 0x48 is set, the user strap gets overriden + * Internal_Flags struct at 0x48 is set, the user strap gets overridden * by the PCI subsystem ID during POST, but not before the previous user * strap has been committed to CR58 for CR57=0xf on head A, which may be * read and used instead diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 738802358d85..019af307c669 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -533,7 +533,7 @@ nv50_dp_mode_valid(struct nouveau_encoder *outp, const unsigned int min_clock = 25000; unsigned int max_rate, mode_rate, ds_max_dotclock, clock = mode->clock; /* Check with the minmum bpc always, so we can advertise better modes. - * In particlar not doing this causes modes to be dropped on HDR + * In particular not doing this causes modes to be dropped on HDR * displays as we might check with a bpc of 16 even. */ const u8 bpp = 6 * 3; diff --git a/drivers/gpu/drm/nouveau/nouveau_exec.c b/drivers/gpu/drm/nouveau/nouveau_exec.c index a08ab1cfea9b..eea6619cffaa 100644 --- a/drivers/gpu/drm/nouveau/nouveau_exec.c +++ b/drivers/gpu/drm/nouveau/nouveau_exec.c @@ -23,7 +23,7 @@ * should be managed by the kernel and which by the UMD. * * The DRM_NOUVEAU_VM_BIND ioctl provides clients an interface to manage the - * userspace-managable portion of the VA space. It provides operations to map + * userspace-manageable portion of the VA space. It provides operations to map * and unmap memory. Mappings may be flagged as sparse. Sparse mappings are not * backed by a GEM object and the kernel will ignore GEM handles provided * alongside a sparse mapping. diff --git a/drivers/gpu/drm/nouveau/nouveau_led.c b/drivers/gpu/drm/nouveau/nouveau_led.c index 19e2110904d1..67088a71f6d1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_led.c +++ b/drivers/gpu/drm/nouveau/nouveau_led.c @@ -65,7 +65,7 @@ nouveau_led_set_brightness(struct led_classdev *led, enum led_brightness value) duty = value * div / LED_FULL; /* for now, this is safe to directly poke those registers because: - * - A: nvidia never puts the logo led to any other PWM controler + * - A: nvidia never puts the logo led to any other PWM controller * than PDISPLAY.SOR[1].PWM. * - B: nouveau does not touch these registers anywhere else */ diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c index b54f044c4483..3f888fb97910 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c @@ -403,7 +403,7 @@ init_table_(struct nvbios_init *init, u16 offset, const char *name) #define init_io_condition_table(b) init_table_((b), 0x08, "io condition table") #define init_io_flag_condition_table(b) init_table_((b), 0x0a, "io flag condition table") #define init_function_table(b) init_table_((b), 0x0c, "function table") -#define init_xlat_table(b) init_table_((b), 0x10, "xlat table"); +#define init_xlat_table(b) init_table_((b), 0x10, "xlat table") static u16 init_script(struct nvkm_bios *bios, int index) diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index 6723c3d422c9..838b34ea7e31 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -338,7 +338,7 @@ static void dsi_handle_irq_errors(struct dsi_data *dsi, u32 irqstatus, int i; if (irqstatus & DSI_IRQ_ERROR_MASK) { - DSSERR("DSI error, irqstatus %x\n", irqstatus); + dev_err(dsi->dev, "DSI error, irqstatus %x\n", irqstatus); print_irq_status(irqstatus); spin_lock(&dsi->errors_lock); dsi->errors |= irqstatus & DSI_IRQ_ERROR_MASK; @@ -349,8 +349,8 @@ static void dsi_handle_irq_errors(struct dsi_data *dsi, u32 irqstatus, for (i = 0; i < 4; ++i) { if (vcstatus[i] & DSI_VC_IRQ_ERROR_MASK) { - DSSERR("DSI VC(%d) error, vc irqstatus %x\n", - i, vcstatus[i]); + dev_err(dsi->dev, "DSI VC(%d) error, vc irqstatus %x\n", + i, vcstatus[i]); print_irq_status_vc(i, vcstatus[i]); } else if (debug_irq) { print_irq_status_vc(i, vcstatus[i]); @@ -358,7 +358,7 @@ static void dsi_handle_irq_errors(struct dsi_data *dsi, u32 irqstatus, } if (ciostatus & DSI_CIO_IRQ_ERROR_MASK) { - DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus); + dev_err(dsi->dev, "DSI CIO error, cio irqstatus %x\n", ciostatus); print_irq_status_cio(ciostatus); } else if (debug_irq) { print_irq_status_cio(ciostatus); @@ -694,8 +694,6 @@ static int dsi_runtime_get(struct dsi_data *dsi) { int r; - DSSDBG("dsi_runtime_get\n"); - r = pm_runtime_get_sync(dsi->dev); if (WARN_ON(r < 0)) { pm_runtime_put_noidle(dsi->dev); @@ -708,8 +706,6 @@ static void dsi_runtime_put(struct dsi_data *dsi) { int r; - DSSDBG("dsi_runtime_put\n"); - r = pm_runtime_put_sync(dsi->dev); WARN_ON(r < 0 && r != -ENOSYS); } @@ -751,13 +747,13 @@ static void _dsi_print_reset_status(struct dsi_data *dsi) static inline int dsi_if_enable(struct dsi_data *dsi, bool enable) { - DSSDBG("dsi_if_enable(%d)\n", enable); + dev_dbg(dsi->dev, "dsi_if_enable(%d)\n", enable); enable = enable ? 1 : 0; REG_FLD_MOD(dsi, DSI_CTRL, enable, 0, 0); /* IF_EN */ if (!wait_for_bit_change(dsi, DSI_CTRL, 0, enable)) { - DSSERR("Failed to set dsi_if_enable to %d\n", enable); + dev_err(dsi->dev, "Failed to set dsi_if_enable to %d\n", enable); return -EIO; } @@ -832,7 +828,7 @@ static int dsi_set_lp_clk_divisor(struct dsi_data *dsi) lp_clk = dsi_fclk / 2 / lp_clk_div; - DSSDBG("LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk); + dev_dbg(dsi->dev, "LP_CLK_DIV %u, LP_CLK %lu\n", lp_clk_div, lp_clk); dsi->current_lp_cinfo.lp_clk = lp_clk; dsi->current_lp_cinfo.lp_clk_div = lp_clk_div; @@ -880,8 +876,8 @@ static int dsi_pll_power(struct dsi_data *dsi, enum dsi_pll_power_state state) /* PLL_PWR_STATUS */ while (FLD_GET(dsi_read_reg(dsi, DSI_CLK_CTRL), 29, 28) != state) { if (++t > 1000) { - DSSERR("Failed to set DSI PLL power mode to %d\n", - state); + dev_err(dsi->dev, "Failed to set DSI PLL power mode to %d\n", + state); return -ENODEV; } udelay(1); @@ -907,7 +903,7 @@ static int dsi_pll_enable(struct dss_pll *pll) struct dsi_data *dsi = container_of(pll, struct dsi_data, pll); int r = 0; - DSSDBG("PLL init\n"); + dev_dbg(dsi->dev, "PLL init\n"); r = dsi_runtime_get(dsi); if (r) @@ -926,7 +922,7 @@ static int dsi_pll_enable(struct dss_pll *pll) dispc_pck_free_enable(dsi->dss->dispc, 1); if (!wait_for_bit_change(dsi, DSI_PLL_STATUS, 0, 1)) { - DSSERR("PLL not coming out of reset.\n"); + dev_err(dsi->dev, "PLL not coming out of reset.\n"); r = -ENODEV; dispc_pck_free_enable(dsi->dss->dispc, 0); goto err1; @@ -941,7 +937,7 @@ static int dsi_pll_enable(struct dss_pll *pll) if (r) goto err1; - DSSDBG("PLL init done\n"); + dev_dbg(dsi->dev, "PLL init done\n"); return 0; err1: @@ -963,7 +959,7 @@ static void dsi_pll_disable(struct dss_pll *pll) dsi_disable_scp_clk(dsi); dsi_runtime_put(dsi); - DSSDBG("PLL disable done\n"); + dev_dbg(dsi->dev, "PLL disable done\n"); } static int dsi_dump_dsi_clocks(struct seq_file *s, void *p) @@ -1227,7 +1223,7 @@ static int dsi_cio_power(struct dsi_data *dsi, enum dsi_cio_power_state state) while (FLD_GET(dsi_read_reg(dsi, DSI_COMPLEXIO_CFG1), 26, 25) != state) { if (++t > 1000) { - DSSERR("failed to set complexio power state to " + dev_err(dsi->dev, "failed to set complexio power state to " "%d\n", state); return -ENODEV; } @@ -1368,20 +1364,19 @@ static void dsi_cio_timings(struct dsi_data *dsi) /* min tclk-prepare + tclk-zero = 300ns */ tclk_zero = ns2ddr(dsi, 260); - DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n", + dev_dbg(dsi->dev, "ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n", ths_prepare, ddr2ns(dsi, ths_prepare), ths_prepare_ths_zero, ddr2ns(dsi, ths_prepare_ths_zero)); - DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n", - ths_trail, ddr2ns(dsi, ths_trail), - ths_exit, ddr2ns(dsi, ths_exit)); + dev_dbg(dsi->dev, "ths_trail %u (%uns), ths_exit %u (%uns)\n", + ths_trail, ddr2ns(dsi, ths_trail), + ths_exit, ddr2ns(dsi, ths_exit)); - DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), " - "tclk_zero %u (%uns)\n", - tlpx_half, ddr2ns(dsi, tlpx_half), - tclk_trail, ddr2ns(dsi, tclk_trail), - tclk_zero, ddr2ns(dsi, tclk_zero)); - DSSDBG("tclk_prepare %u (%uns)\n", - tclk_prepare, ddr2ns(dsi, tclk_prepare)); + dev_dbg(dsi->dev, "tlpx_half %u (%uns), tclk_trail %u (%uns), tclk_zero %u (%uns)\n", + tlpx_half, ddr2ns(dsi, tlpx_half), + tclk_trail, ddr2ns(dsi, tclk_trail), + tclk_zero, ddr2ns(dsi, tclk_zero)); + dev_dbg(dsi->dev, "tclk_prepare %u (%uns)\n", + tclk_prepare, ddr2ns(dsi, tclk_prepare)); /* program timings */ @@ -1447,8 +1442,8 @@ static int dsi_cio_wait_tx_clk_esc_reset(struct dsi_data *dsi) if (!in_use[i] || (l & (1 << offsets[i]))) continue; - DSSERR("CIO TXCLKESC%d domain not coming " \ - "out of reset\n", i); + dev_err(dsi->dev, + "CIO TXCLKESC%d domain not coming out of reset\n", i); } return -EIO; } @@ -1553,7 +1548,7 @@ static int dsi_cio_init(struct dsi_data *dsi) int r; u32 l; - DSSDBG("DSI CIO init starts"); + dev_dbg(dsi->dev, "DSI CIO init starts"); r = dsi_enable_pads(dsi, dsi_get_lane_mask(dsi)); if (r) @@ -1567,7 +1562,7 @@ static int dsi_cio_init(struct dsi_data *dsi) dsi_read_reg(dsi, DSI_DSIPHY_CFG5); if (!wait_for_bit_change(dsi, DSI_DSIPHY_CFG5, 30, 1)) { - DSSERR("CIO SCP Clock domain not coming out of reset.\n"); + dev_err(dsi->dev, "CIO SCP Clock domain not coming out of reset.\n"); r = -EIO; goto err_scp_clk_dom; } @@ -1589,7 +1584,7 @@ static int dsi_cio_init(struct dsi_data *dsi) goto err_cio_pwr; if (!wait_for_bit_change(dsi, DSI_COMPLEXIO_CFG1, 29, 1)) { - DSSERR("CIO PWR clock domain not coming out of reset.\n"); + dev_err(dsi->dev, "CIO PWR clock domain not coming out of reset.\n"); r = -ENODEV; goto err_cio_pwr_dom; } @@ -1612,7 +1607,7 @@ static int dsi_cio_init(struct dsi_data *dsi) !(dsi->dsidev->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS), 13, 13); - DSSDBG("CIO init done\n"); + dev_dbg(dsi->dev, "CIO init done\n"); return 0; @@ -1655,7 +1650,7 @@ static void dsi_config_tx_fifo(struct dsi_data *dsi, int size = dsi->vc[i].tx_fifo_size; if (add + size > 4) { - DSSERR("Illegal FIFO configuration\n"); + dev_err(dsi->dev, "Illegal FIFO configuration\n"); BUG(); return; } @@ -1687,7 +1682,7 @@ static void dsi_config_rx_fifo(struct dsi_data *dsi, int size = dsi->vc[i].rx_fifo_size; if (add + size > 4) { - DSSERR("Illegal FIFO configuration\n"); + dev_err(dsi->dev, "Illegal FIFO configuration\n"); BUG(); return; } @@ -1710,7 +1705,7 @@ static int dsi_force_tx_stop_mode_io(struct dsi_data *dsi) dsi_write_reg(dsi, DSI_TIMING1, r); if (!wait_for_bit_change(dsi, DSI_TIMING1, 15, 0)) { - DSSERR("TX_STOP bit not going down\n"); + dev_err(dsi->dev, "TX_STOP bit not going down\n"); return -EIO; } @@ -1755,7 +1750,7 @@ static int dsi_sync_vc_vp(struct dsi_data *dsi, int vc) if (REG_GET(dsi, DSI_VC_TE(vc), bit, bit)) { if (wait_for_completion_timeout(&completion, msecs_to_jiffies(10)) == 0) { - DSSERR("Failed to complete previous frame transfer\n"); + dev_err(dsi->dev, "Failed to complete previous frame transfer\n"); r = -EIO; goto err1; } @@ -1801,7 +1796,7 @@ static int dsi_sync_vc_l4(struct dsi_data *dsi, int vc) if (REG_GET(dsi, DSI_VC_CTRL(vc), 5, 5)) { if (wait_for_completion_timeout(&completion, msecs_to_jiffies(10)) == 0) { - DSSERR("Failed to complete previous l4 transfer\n"); + dev_err(dsi->dev, "Failed to complete previous l4 transfer\n"); r = -EIO; goto err1; } @@ -1840,15 +1835,15 @@ static int dsi_sync_vc(struct dsi_data *dsi, int vc) static int dsi_vc_enable(struct dsi_data *dsi, int vc, bool enable) { - DSSDBG("dsi_vc_enable vc %d, enable %d\n", - vc, enable); + dev_dbg(dsi->dev, "dsi_vc_enable vc %d, enable %d\n", + vc, enable); enable = enable ? 1 : 0; REG_FLD_MOD(dsi, DSI_VC_CTRL(vc), enable, 0, 0); if (!wait_for_bit_change(dsi, DSI_VC_CTRL(vc), 0, enable)) { - DSSERR("Failed to set dsi_vc_enable to %d\n", enable); + dev_err(dsi->dev, "Failed to set VC_EN to %d\n", enable); return -EIO; } @@ -1859,13 +1854,13 @@ static void dsi_vc_initial_config(struct dsi_data *dsi, int vc) { u32 r; - DSSDBG("Initial config of VC %d", vc); + dev_dbg(dsi->dev, "Initial config of VC %d", vc); r = dsi_read_reg(dsi, DSI_VC_CTRL(vc)); if (FLD_GET(r, 15, 15)) /* VC_BUSY */ - DSSERR("VC(%d) busy when trying to configure it!\n", - vc); + dev_err(dsi->dev, "VC(%d) busy when trying to configure it!\n", + vc); r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */ r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */ @@ -1890,7 +1885,7 @@ static void dsi_vc_enable_hs(struct omap_dss_device *dssdev, int vc, { struct dsi_data *dsi = to_dsi_data(dssdev); - DSSDBG("dsi_vc_enable_hs(%d, %d)\n", vc, enable); + dev_dbg(dsi->dev, "%s(%d, %d)\n", __func__, vc, enable); if (REG_GET(dsi, DSI_VC_CTRL(vc), 9, 9) == enable) return; @@ -1913,49 +1908,49 @@ static void dsi_vc_flush_long_data(struct dsi_data *dsi, int vc) while (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20)) { u32 val; val = dsi_read_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc)); - DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n", - (val >> 0) & 0xff, - (val >> 8) & 0xff, - (val >> 16) & 0xff, - (val >> 24) & 0xff); + dev_dbg(dsi->dev, "\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n", + (val >> 0) & 0xff, + (val >> 8) & 0xff, + (val >> 16) & 0xff, + (val >> 24) & 0xff); } } -static void dsi_show_rx_ack_with_err(u16 err) +static void dsi_show_rx_ack_with_err(struct dsi_data *dsi, u16 err) { - DSSERR("\tACK with ERROR (%#x):\n", err); + dev_err(dsi->dev, "\tACK with ERROR (%#x):\n", err); if (err & (1 << 0)) - DSSERR("\t\tSoT Error\n"); + dev_err(dsi->dev, "\t\tSoT Error\n"); if (err & (1 << 1)) - DSSERR("\t\tSoT Sync Error\n"); + dev_err(dsi->dev, "\t\tSoT Sync Error\n"); if (err & (1 << 2)) - DSSERR("\t\tEoT Sync Error\n"); + dev_err(dsi->dev, "\t\tEoT Sync Error\n"); if (err & (1 << 3)) - DSSERR("\t\tEscape Mode Entry Command Error\n"); + dev_err(dsi->dev, "\t\tEscape Mode Entry Command Error\n"); if (err & (1 << 4)) - DSSERR("\t\tLP Transmit Sync Error\n"); + dev_err(dsi->dev, "\t\tLP Transmit Sync Error\n"); if (err & (1 << 5)) - DSSERR("\t\tHS Receive Timeout Error\n"); + dev_err(dsi->dev, "\t\tHS Receive Timeout Error\n"); if (err & (1 << 6)) - DSSERR("\t\tFalse Control Error\n"); + dev_err(dsi->dev, "\t\tFalse Control Error\n"); if (err & (1 << 7)) - DSSERR("\t\t(reserved7)\n"); + dev_err(dsi->dev, "\t\t(reserved7)\n"); if (err & (1 << 8)) - DSSERR("\t\tECC Error, single-bit (corrected)\n"); + dev_err(dsi->dev, "\t\tECC Error, single-bit (corrected)\n"); if (err & (1 << 9)) - DSSERR("\t\tECC Error, multi-bit (not corrected)\n"); + dev_err(dsi->dev, "\t\tECC Error, multi-bit (not corrected)\n"); if (err & (1 << 10)) - DSSERR("\t\tChecksum Error\n"); + dev_err(dsi->dev, "\t\tChecksum Error\n"); if (err & (1 << 11)) - DSSERR("\t\tData type not recognized\n"); + dev_err(dsi->dev, "\t\tData type not recognized\n"); if (err & (1 << 12)) - DSSERR("\t\tInvalid VC ID\n"); + dev_err(dsi->dev, "\t\tInvalid VC ID\n"); if (err & (1 << 13)) - DSSERR("\t\tInvalid Transmission Length\n"); + dev_err(dsi->dev, "\t\tInvalid Transmission Length\n"); if (err & (1 << 14)) - DSSERR("\t\t(reserved14)\n"); + dev_err(dsi->dev, "\t\t(reserved14)\n"); if (err & (1 << 15)) - DSSERR("\t\tDSI Protocol Violation\n"); + dev_err(dsi->dev, "\t\tDSI Protocol Violation\n"); } static u16 dsi_vc_flush_receive_data(struct dsi_data *dsi, int vc) @@ -1965,23 +1960,23 @@ static u16 dsi_vc_flush_receive_data(struct dsi_data *dsi, int vc) u32 val; u8 dt; val = dsi_read_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc)); - DSSERR("\trawval %#08x\n", val); + dev_err(dsi->dev, "\trawval %#08x\n", val); dt = FLD_GET(val, 5, 0); if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) { u16 err = FLD_GET(val, 23, 8); - dsi_show_rx_ack_with_err(err); + dsi_show_rx_ack_with_err(dsi, err); } else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE) { - DSSERR("\tDCS short response, 1 byte: %#x\n", - FLD_GET(val, 23, 8)); + dev_err(dsi->dev, "\tDCS short response, 1 byte: %#x\n", + FLD_GET(val, 23, 8)); } else if (dt == MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE) { - DSSERR("\tDCS short response, 2 byte: %#x\n", - FLD_GET(val, 23, 8)); + dev_err(dsi->dev, "\tDCS short response, 2 byte: %#x\n", + FLD_GET(val, 23, 8)); } else if (dt == MIPI_DSI_RX_DCS_LONG_READ_RESPONSE) { - DSSERR("\tDCS long response, len %d\n", - FLD_GET(val, 23, 8)); + dev_err(dsi->dev, "\tDCS long response, len %d\n", + FLD_GET(val, 23, 8)); dsi_vc_flush_long_data(dsi, vc); } else { - DSSERR("\tunknown datatype 0x%02x\n", dt); + dev_err(dsi->dev, "\tunknown datatype 0x%02x\n", dt); } } return 0; @@ -1990,13 +1985,13 @@ static u16 dsi_vc_flush_receive_data(struct dsi_data *dsi, int vc) static int dsi_vc_send_bta(struct dsi_data *dsi, int vc) { if (dsi->debug_write || dsi->debug_read) - DSSDBG("dsi_vc_send_bta %d\n", vc); + dev_dbg(dsi->dev, "dsi_vc_send_bta %d\n", vc); WARN_ON(!dsi_bus_is_locked(dsi)); /* RX_FIFO_NOT_EMPTY */ if (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20)) { - DSSERR("rx fifo not empty when sending BTA, dumping data:\n"); + dev_err(dsi->dev, "rx fifo not empty when sending BTA, dumping data:\n"); dsi_vc_flush_receive_data(dsi, vc); } @@ -2031,14 +2026,14 @@ static int dsi_vc_send_bta_sync(struct omap_dss_device *dssdev, int vc) if (wait_for_completion_timeout(&completion, msecs_to_jiffies(500)) == 0) { - DSSERR("Failed to receive BTA\n"); + dev_err(dsi->dev, "Failed to receive BTA\n"); r = -EIO; goto err2; } err = dsi_get_errors(dsi); if (err) { - DSSERR("Error while sending BTA: %x\n", err); + dev_err(dsi->dev, "Error while sending BTA: %x\n", err); r = -EIO; goto err2; } @@ -2092,11 +2087,11 @@ static int dsi_vc_send_long(struct dsi_data *dsi, int vc, u8 b1, b2, b3, b4; if (dsi->debug_write) - DSSDBG("dsi_vc_send_long, %zu bytes\n", msg->tx_len); + dev_dbg(dsi->dev, "%s, %zu bytes\n", __func__, msg->tx_len); /* len + header */ if (dsi->vc[vc].tx_fifo_size * 32 * 4 < msg->tx_len + 4) { - DSSERR("unable to send long packet: packet too long.\n"); + dev_err(dsi->dev, "unable to send long packet: packet too long.\n"); return -EINVAL; } @@ -2105,7 +2100,7 @@ static int dsi_vc_send_long(struct dsi_data *dsi, int vc, p = msg->tx_buf; for (i = 0; i < msg->tx_len >> 2; i++) { if (dsi->debug_write) - DSSDBG("\tsending full packet %d\n", i); + dev_dbg(dsi->dev, "\tsending full packet %d\n", i); b1 = *p++; b2 = *p++; @@ -2120,7 +2115,7 @@ static int dsi_vc_send_long(struct dsi_data *dsi, int vc, b1 = 0; b2 = 0; b3 = 0; if (dsi->debug_write) - DSSDBG("\tsending remainder bytes %d\n", i); + dev_dbg(dsi->dev, "\tsending remainder bytes %d\n", i); switch (i) { case 3: @@ -2157,11 +2152,11 @@ static int dsi_vc_send_short(struct dsi_data *dsi, int vc, WARN_ON(!dsi_bus_is_locked(dsi)); if (dsi->debug_write) - DSSDBG("dsi_vc_send_short(vc%d, dt %#x, b1 %#x, b2 %#x)\n", - vc, msg->type, pkt.header[1], pkt.header[2]); + dev_dbg(dsi->dev, "%s(vc%d, dt %#x, b1 %#x, b2 %#x)\n", + __func__, vc, msg->type, pkt.header[1], pkt.header[2]); if (FLD_GET(dsi_read_reg(dsi, DSI_VC_CTRL(vc)), 16, 16)) { - DSSERR("ERROR FIFO FULL, aborting transfer\n"); + dev_err(dsi->dev, "ERROR FIFO FULL, aborting transfer\n"); return -EINVAL; } @@ -2216,20 +2211,20 @@ static int dsi_vc_write_common(struct omap_dss_device *dssdev, int vc, if (msg->flags & MIPI_DSI_MSG_REQ_ACK) { r = dsi_vc_send_bta_sync(dssdev, vc); if (r) { - DSSERR("bta sync failed\n"); + dev_err(dsi->dev, "bta sync failed\n"); return r; } } else { err = dsi_get_errors(dsi); if (err) { - DSSERR("Error while sending: %x\n", err); + dev_err(dsi->dev, "Error while sending: %x\n", err); return -EIO; } } /* RX_FIFO_NOT_EMPTY */ if (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20)) { - DSSERR("rx fifo not empty after write, dumping data:\n"); + dev_err(dsi->dev, "rx fifo not empty after write, dumping data:\n"); dsi_vc_flush_receive_data(dsi, vc); return -EIO; } @@ -2246,18 +2241,18 @@ static int dsi_vc_read_rx_fifo(struct dsi_data *dsi, int vc, u8 *buf, /* RX_FIFO_NOT_EMPTY */ if (REG_GET(dsi, DSI_VC_CTRL(vc), 20, 20) == 0) { - DSSERR("RX fifo empty when trying to read.\n"); + dev_err(dsi->dev, "RX fifo empty when trying to read.\n"); r = -EIO; goto err; } val = dsi_read_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc)); if (dsi->debug_read) - DSSDBG("\theader: %08x\n", val); + dev_dbg(dsi->dev, "\theader: %08x\n", val); dt = FLD_GET(val, 5, 0); if (dt == MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT) { u16 err = FLD_GET(val, 23, 8); - dsi_show_rx_ack_with_err(err); + dsi_show_rx_ack_with_err(dsi, err); r = -EIO; goto err; @@ -2266,7 +2261,7 @@ static int dsi_vc_read_rx_fifo(struct dsi_data *dsi, int vc, u8 *buf, MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE)) { u8 data = FLD_GET(val, 15, 8); if (dsi->debug_read) - DSSDBG("\t%s short response, 1 byte: %02x\n", + dev_dbg(dsi->dev, "\t%s short response, 1 byte: %02x\n", type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS", data); @@ -2283,7 +2278,7 @@ static int dsi_vc_read_rx_fifo(struct dsi_data *dsi, int vc, u8 *buf, MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE)) { u16 data = FLD_GET(val, 23, 8); if (dsi->debug_read) - DSSDBG("\t%s short response, 2 byte: %04x\n", + dev_dbg(dsi->dev, "\t%s short response, 2 byte: %04x\n", type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS", data); @@ -2302,7 +2297,7 @@ static int dsi_vc_read_rx_fifo(struct dsi_data *dsi, int vc, u8 *buf, int w; int len = FLD_GET(val, 23, 8); if (dsi->debug_read) - DSSDBG("\t%s long response, len %d\n", + dev_dbg(dsi->dev, "\t%s long response, len %d\n", type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS", len); @@ -2317,11 +2312,11 @@ static int dsi_vc_read_rx_fifo(struct dsi_data *dsi, int vc, u8 *buf, val = dsi_read_reg(dsi, DSI_VC_SHORT_PACKET_HEADER(vc)); if (dsi->debug_read) - DSSDBG("\t\t%02x %02x %02x %02x\n", - (val >> 0) & 0xff, - (val >> 8) & 0xff, - (val >> 16) & 0xff, - (val >> 24) & 0xff); + dev_dbg(dsi->dev, "\t\t%02x %02x %02x %02x\n", + (val >> 0) & 0xff, + (val >> 8) & 0xff, + (val >> 16) & 0xff, + (val >> 24) & 0xff); for (b = 0; b < 4; ++b) { if (w < len) @@ -2333,13 +2328,13 @@ static int dsi_vc_read_rx_fifo(struct dsi_data *dsi, int vc, u8 *buf, return len; } else { - DSSERR("\tunknown datatype 0x%02x\n", dt); + dev_err(dsi->dev, "\tunknown datatype 0x%02x\n", dt); r = -EIO; goto err; } err: - DSSERR("dsi_vc_read_rx_fifo(vc %d type %s) failed\n", vc, + dev_err(dsi->dev, "%s(vc %d type %s) failed\n", __func__, vc, type == DSS_DSI_CONTENT_GENERIC ? "GENERIC" : "DCS"); return r; @@ -2353,7 +2348,7 @@ static int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int vc, int r; if (dsi->debug_read) - DSSDBG("%s(vc %d, cmd %x)\n", __func__, vc, cmd); + dev_dbg(dsi->dev, "%s(vc %d, cmd %x)\n", __func__, vc, cmd); r = dsi_vc_send_short(dsi, vc, msg); if (r) @@ -2375,7 +2370,7 @@ static int dsi_vc_dcs_read(struct omap_dss_device *dssdev, int vc, return 0; err: - DSSERR("%s(vc %d, cmd 0x%02x) failed\n", __func__, vc, cmd); + dev_err(dsi->dev, "%s(vc %d, cmd 0x%02x) failed\n", __func__, vc, cmd); return r; } @@ -2405,7 +2400,7 @@ static int dsi_vc_generic_read(struct omap_dss_device *dssdev, int vc, return 0; err: - DSSERR("%s(vc %d, reqlen %zu) failed\n", __func__, vc, msg->tx_len); + dev_err(dsi->dev, "%s(vc %d, reqlen %zu) failed\n", __func__, vc, msg->tx_len); return r; } @@ -2430,10 +2425,10 @@ static void dsi_set_lp_rx_timeout(struct dsi_data *dsi, unsigned int ticks, total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); - DSSDBG("LP_RX_TO %lu ticks (%#x%s%s) = %lu ns\n", - total_ticks, - ticks, x4 ? " x4" : "", x16 ? " x16" : "", - (total_ticks * 1000) / (fck / 1000 / 1000)); + dev_dbg(dsi->dev, "LP_RX_TO %lu ticks (%#x%s%s) = %lu ns\n", + total_ticks, + ticks, x4 ? " x4" : "", x16 ? " x16" : "", + (total_ticks * 1000) / (fck / 1000 / 1000)); } static void dsi_set_ta_timeout(struct dsi_data *dsi, unsigned int ticks, @@ -2457,10 +2452,10 @@ static void dsi_set_ta_timeout(struct dsi_data *dsi, unsigned int ticks, total_ticks = ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1); - DSSDBG("TA_TO %lu ticks (%#x%s%s) = %lu ns\n", - total_ticks, - ticks, x8 ? " x8" : "", x16 ? " x16" : "", - (total_ticks * 1000) / (fck / 1000 / 1000)); + dev_dbg(dsi->dev, "TA_TO %lu ticks (%#x%s%s) = %lu ns\n", + total_ticks, + ticks, x8 ? " x8" : "", x16 ? " x16" : "", + (total_ticks * 1000) / (fck / 1000 / 1000)); } static void dsi_set_stop_state_counter(struct dsi_data *dsi, unsigned int ticks, @@ -2484,10 +2479,10 @@ static void dsi_set_stop_state_counter(struct dsi_data *dsi, unsigned int ticks, total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); - DSSDBG("STOP_STATE_COUNTER %lu ticks (%#x%s%s) = %lu ns\n", - total_ticks, - ticks, x4 ? " x4" : "", x16 ? " x16" : "", - (total_ticks * 1000) / (fck / 1000 / 1000)); + dev_dbg(dsi->dev, "STOP_STATE_COUNTER %lu ticks (%#x%s%s) = %lu ns\n", + total_ticks, + ticks, x4 ? " x4" : "", x16 ? " x16" : "", + (total_ticks * 1000) / (fck / 1000 / 1000)); } static void dsi_set_hs_tx_timeout(struct dsi_data *dsi, unsigned int ticks, @@ -2511,10 +2506,10 @@ static void dsi_set_hs_tx_timeout(struct dsi_data *dsi, unsigned int ticks, total_ticks = ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1); - DSSDBG("HS_TX_TO %lu ticks (%#x%s%s) = %lu ns\n", - total_ticks, - ticks, x4 ? " x4" : "", x16 ? " x16" : "", - (total_ticks * 1000) / (fck / 1000 / 1000)); + dev_dbg(dsi->dev, "HS_TX_TO %lu ticks (%#x%s%s) = %lu ns\n", + total_ticks, + ticks, x4 ? " x4" : "", x16 ? " x16" : "", + (total_ticks * 1000) / (fck / 1000 / 1000)); } static void dsi_config_vp_num_line_buffers(struct dsi_data *dsi) @@ -2734,11 +2729,11 @@ static void dsi_config_cmd_mode_interleaving(struct dsi_data *dsi) lp_clk_div, dsi_fclk_hsdiv); } - DSSDBG("DSI HS interleaving(TXBYTECLKHS) HSA %d, HFP %d, HBP %d, BLLP %d\n", + dev_dbg(dsi->dev, "DSI HS interleaving(TXBYTECLKHS) HSA %d, HFP %d, HBP %d, BLLP %d\n", hsa_interleave_hs, hfp_interleave_hs, hbp_interleave_hs, bl_interleave_hs); - DSSDBG("DSI LP interleaving(bytes) HSA %d, HFP %d, HBP %d, BLLP %d\n", + dev_dbg(dsi->dev, "DSI LP interleaving(bytes) HSA %d, HFP %d, HBP %d, BLLP %d\n", hsa_interleave_lp, hfp_interleave_lp, hbp_interleave_lp, bl_interleave_lp); @@ -2874,9 +2869,9 @@ static void dsi_proto_timings(struct dsi_data *dsi) r = FLD_MOD(r, ddr_clk_post, 7, 0); dsi_write_reg(dsi, DSI_CLK_TIMING, r); - DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n", - ddr_clk_pre, - ddr_clk_post); + dev_dbg(dsi->dev, "ddr_clk_pre %u, ddr_clk_post %u\n", + ddr_clk_pre, + ddr_clk_post); enter_hs_mode_lat = 1 + DIV_ROUND_UP(tlpx, 4) + DIV_ROUND_UP(ths_prepare, 4) + @@ -2888,8 +2883,8 @@ static void dsi_proto_timings(struct dsi_data *dsi) FLD_VAL(exit_hs_mode_lat, 15, 0); dsi_write_reg(dsi, DSI_VM_TIMING7, r); - DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n", - enter_hs_mode_lat, exit_hs_mode_lat); + dev_dbg(dsi->dev, "enter_hs_mode_lat %u, exit_hs_mode_lat %u\n", + enter_hs_mode_lat, exit_hs_mode_lat); if (dsi->mode == OMAP_DSS_DSI_VIDEO_MODE) { /* TODO: Implement a video mode check_timings function */ @@ -2915,9 +2910,9 @@ static void dsi_proto_timings(struct dsi_data *dsi) tl = DIV_ROUND_UP(4, ndl) + (hsync_end ? hsa : 0) + t_he + hfp + DIV_ROUND_UP(width_bytes + 6, ndl) + hbp; - DSSDBG("HBP: %d, HFP: %d, HSA: %d, TL: %d TXBYTECLKHS\n", hbp, + dev_dbg(dsi->dev, "HBP: %d, HFP: %d, HSA: %d, TL: %d TXBYTECLKHS\n", hbp, hfp, hsync_end ? hsa : 0, tl); - DSSDBG("VBP: %d, VFP: %d, VSA: %d, VACT: %d lines\n", vbp, vfp, + dev_dbg(dsi->dev, "VBP: %d, VFP: %d, VSA: %d, VACT: %d lines\n", vbp, vfp, vsa, vm->vactive); r = dsi_read_reg(dsi, DSI_VM_TIMING1); @@ -3113,7 +3108,7 @@ static void dsi_update_screen_dispc(struct dsi_data *dsi) u16 w = dsi->vm.hactive; u16 h = dsi->vm.vactive; - DSSDBG("dsi_update_screen_dispc(%dx%d)\n", w, h); + dev_dbg(dsi->dev, "%s(%dx%d)\n", __func__, w, h); bytespp = mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt) / 8; bytespl = w * bytespp; @@ -3208,7 +3203,7 @@ static void dsi_framedone_timeout_work_callback(struct work_struct *work) * on the HW is buggy, and would probably require resetting the whole * DSI */ - DSSERR("Framedone not received for 250ms!\n"); + dev_err(dsi->dev, "Framedone not received for 250ms!\n"); dsi_handle_framedone(dsi, -ETIMEDOUT); } @@ -3224,7 +3219,7 @@ static void dsi_framedone_irq_callback(void *data) cancel_delayed_work(&dsi->framedone_timeout_work); - DSSDBG("Framedone received!\n"); + dev_dbg(dsi->dev, "Framedone received!\n"); dsi_handle_framedone(dsi, 0); } @@ -3259,7 +3254,7 @@ static int dsi_update_channel(struct omap_dss_device *dssdev, int vc) goto err; } - DSSDBG("dsi_update_channel: %d", vc); + dev_dbg(dsi->dev, "%s: %d", __func__, vc); /* * Transition to LP here. If we don't send something here, the @@ -3309,7 +3304,7 @@ static int dsi_configure_dispc_clocks(struct dsi_data *dsi) r = dispc_calc_clock_rates(dsi->dss->dispc, fck, &dispc_cinfo); if (r) { - DSSERR("Failed to calc dispc clocks\n"); + dev_err(dsi->dev, "Failed to calc dispc clocks\n"); return r; } @@ -3331,7 +3326,7 @@ static int dsi_init_dispc(struct dsi_data *dsi) r = dss_mgr_register_framedone_handler(&dsi->output, dsi_framedone_irq_callback, dsi); if (r) { - DSSERR("can't register FRAMEDONE handler\n"); + dev_err(dsi->dev, "can't register FRAMEDONE handler\n"); goto err; } @@ -3383,7 +3378,7 @@ static int dsi_configure_dsi_clocks(struct dsi_data *dsi) r = dss_pll_set_config(&dsi->pll, &cinfo); if (r) { - DSSERR("Failed to set dsi clocks\n"); + dev_err(dsi->dev, "Failed to set dsi clocks\n"); return r; } @@ -3436,7 +3431,7 @@ static int dsi_init_dsi(struct dsi_data *dsi) dsi->module_id == 0 ? DSS_CLK_SRC_PLL1_2 : DSS_CLK_SRC_PLL2_2); - DSSDBG("PLL OK\n"); + dev_dbg(dsi->dev, "PLL OK\n"); if (!dsi->vdds_dsi_enabled) { r = regulator_enable(dsi->vdds_dsi_reg); @@ -3526,7 +3521,7 @@ err_init_dsi: dsi_runtime_put(dsi); err_get_dsi: mutex_unlock(&dsi->lock); - DSSDBG("dsi_enable FAILED\n"); + dev_dbg(dsi->dev, "%s FAILED\n", __func__); } static void dsi_disable(struct dsi_data *dsi) @@ -4095,7 +4090,7 @@ static int dsi_set_config(struct omap_dss_device *dssdev, r = __dsi_calc_config(dsi, mode, &ctx); if (r) { - DSSERR("failed to find suitable DSI clock settings\n"); + dev_err(dsi->dev, "failed to find suitable DSI clock settings\n"); goto err; } @@ -4255,7 +4250,7 @@ static int dsi_get_clocks(struct dsi_data *dsi) clk = devm_clk_get(dsi->dev, "fck"); if (IS_ERR(clk)) { - DSSERR("can't get fck\n"); + dev_err(dsi->dev, "can't get fck\n"); return PTR_ERR(clk); } @@ -4361,12 +4356,12 @@ static int omap_dsi_host_attach(struct mipi_dsi_host *host, int r; if (dsi->dsidev) { - DSSERR("dsi client already attached\n"); + dev_err(dsi->dev, "dsi client already attached\n"); return -EBUSY; } if (mipi_dsi_pixel_format_to_bpp(client->format) < 0) { - DSSERR("invalid pixel format\n"); + dev_err(dsi->dev, "invalid pixel format\n"); return -EINVAL; } @@ -4527,7 +4522,7 @@ static int dsi_init_pll_data(struct dss_device *dss, struct dsi_data *dsi) clk = devm_clk_get(dsi->dev, "sys_clk"); if (IS_ERR(clk)) { - DSSERR("can't get sys_clk\n"); + dev_err(dsi->dev, "can't get sys_clk\n"); return PTR_ERR(clk); } @@ -4935,21 +4930,21 @@ static int dsi_probe(struct platform_device *pdev) dsi->irq = platform_get_irq(pdev, 0); if (dsi->irq < 0) { - DSSERR("platform_get_irq failed\n"); + dev_err(dsi->dev, "platform_get_irq failed\n"); return -ENODEV; } r = devm_request_irq(dev, dsi->irq, omap_dsi_irq_handler, IRQF_SHARED, dev_name(dev), dsi); if (r < 0) { - DSSERR("request_irq failed\n"); + dev_err(dsi->dev, "request_irq failed\n"); return r; } dsi->vdds_dsi_reg = devm_regulator_get(dev, "vdd"); if (IS_ERR(dsi->vdds_dsi_reg)) { if (PTR_ERR(dsi->vdds_dsi_reg) != -EPROBE_DEFER) - DSSERR("can't get DSI VDD regulator\n"); + dev_err(dsi->dev, "can't get DSI VDD regulator\n"); return PTR_ERR(dsi->vdds_dsi_reg); } @@ -4964,7 +4959,7 @@ static int dsi_probe(struct platform_device *pdev) d++; if (d->address == 0) { - DSSERR("unsupported DSI module\n"); + dev_err(dsi->dev, "unsupported DSI module\n"); return -ENODEV; } @@ -5014,7 +5009,7 @@ static int dsi_probe(struct platform_device *pdev) r = dsi_probe_of(dsi); if (r) { - DSSERR("Invalid DSI DT data\n"); + dev_err(dsi->dev, "Invalid DSI DT data\n"); goto err_pm_disable; } diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c index b66751df502c..29f250e6e64d 100644 --- a/drivers/gpu/drm/panel/panel-edp.c +++ b/drivers/gpu/drm/panel/panel-edp.c @@ -2141,6 +2141,7 @@ static const struct edp_panel_entry edp_panels[] = { EDP_PANEL_ENTRY('C', 'S', 'W', 0x144b, &delay_200_500_e80, "MNE001BS1-4"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x1457, &delay_80_500_e80_p2e200, "MNE007QS3-8"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x1462, &delay_200_500_e50, "MNE007QS5-2"), + EDP_PANEL_ENTRY('C', 'S', 'W', 0x1465, &delay_200_500_e50, "MNE007QB1-1"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x1468, &delay_200_500_e50, "MNE007QB2-2"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x146e, &delay_80_500_e50_d50, "MNE007QB3-1"), EDP_PANEL_ENTRY('C', 'S', 'W', 0x147c, &delay_200_500_e50_d100, "MNE007QB3-1"), diff --git a/drivers/gpu/drm/panthor/panthor_drv.c b/drivers/gpu/drm/panthor/panthor_drv.c index 46a3080b0b20..8cdba0c1a14b 100644 --- a/drivers/gpu/drm/panthor/panthor_drv.c +++ b/drivers/gpu/drm/panthor/panthor_drv.c @@ -1769,6 +1769,7 @@ static void panthor_debugfs_init(struct drm_minor *minor) { panthor_mmu_debugfs_init(minor); panthor_gem_debugfs_init(minor); + panthor_sched_debugfs_init(minor); } #endif diff --git a/drivers/gpu/drm/panthor/panthor_sched.c b/drivers/gpu/drm/panthor/panthor_sched.c index 60b2417deb81..ac6aee554820 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.c +++ b/drivers/gpu/drm/panthor/panthor_sched.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 or MIT /* Copyright 2023 Collabora ltd. */ +#include #include #include #include @@ -4175,3 +4176,75 @@ int panthor_sched_init(struct panthor_device *ptdev) ptdev->scheduler = sched; return 0; } + +#ifdef CONFIG_DEBUG_FS + +static const char * +panthor_sched_prio_str(enum panthor_csg_priority prio) +{ + switch (prio) { + case PANTHOR_CSG_PRIORITY_LOW: + return "LOW"; + case PANTHOR_CSG_PRIORITY_MEDIUM: + return "MEDIUM"; + case PANTHOR_CSG_PRIORITY_HIGH: + return "HIGH"; + case PANTHOR_CSG_PRIORITY_RT: + return "REAL-TIME"; + default: + return "UNKNOWN"; + } +} + +static int show_file_groups(struct drm_file *file, struct seq_file *m) +{ + struct panthor_file *pfile = file->driver_priv; + struct panthor_group *group; + unsigned long i; + + if (IS_ERR_OR_NULL(pfile->groups)) + return -ENOENT; + + xa_lock(&pfile->groups->xa); + xa_for_each_marked(&pfile->groups->xa, i, group, GROUP_REGISTERED) { + seq_printf(m, "client_id %8llu pid %8d command %s: group %2lu priority %s queue count %u\n", + file->client_id, group->task_info.pid, group->task_info.comm, i, + panthor_sched_prio_str(group->priority), group->queue_count); + } + xa_unlock(&pfile->groups->xa); + + return 0; +} + +static int show_each_file(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *ddev = node->minor->dev; + int (*show)(struct drm_file *, struct seq_file *) = + node->info_ent->data; + struct drm_file *file; + int ret; + + scoped_cond_guard(mutex_intr, return -EINTR, &ddev->filelist_mutex) { + list_for_each_entry(file, &ddev->filelist, lhead) { + ret = show(file, m); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static struct drm_info_list panthor_sched_debugfs_list[] = { + { "sched_groups", show_each_file, 0, show_file_groups }, +}; + +void panthor_sched_debugfs_init(struct drm_minor *minor) +{ + drm_debugfs_create_files(panthor_sched_debugfs_list, + ARRAY_SIZE(panthor_sched_debugfs_list), + minor->debugfs_root, minor); +} + +#endif /* CONFIG_DEBUG_FS */ diff --git a/drivers/gpu/drm/panthor/panthor_sched.h b/drivers/gpu/drm/panthor/panthor_sched.h index 9a8692de8ade..d87cbdba4d40 100644 --- a/drivers/gpu/drm/panthor/panthor_sched.h +++ b/drivers/gpu/drm/panthor/panthor_sched.h @@ -10,6 +10,7 @@ struct drm_file; struct drm_gem_object; struct drm_sched_job; struct drm_memory_stats; +struct drm_minor; struct drm_panthor_group_create; struct drm_panthor_queue_create; struct drm_panthor_group_get_state; @@ -55,4 +56,8 @@ void panthor_sched_report_fw_events(struct panthor_device *ptdev, u32 events); void panthor_fdinfo_gather_group_samples(struct panthor_file *pfile); +#ifdef CONFIG_DEBUG_FS +void panthor_sched_debugfs_init(struct drm_minor *minor); +#endif + #endif diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c index e4835b99ca60..aba5545033ac 100644 --- a/drivers/gpu/drm/scheduler/sched_main.c +++ b/drivers/gpu/drm/scheduler/sched_main.c @@ -1210,9 +1210,6 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched) drm_sched_wqueue_stop(sched); - for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++) - kfree(sched->sched_rq[i]); - /* Wakeup everyone stuck in drm_sched_entity_flush for this scheduler */ wake_up_all(&sched->job_scheduled); @@ -1226,6 +1223,9 @@ void drm_sched_fini(struct drm_gpu_scheduler *sched) if (sched->own_submit_wq) destroy_workqueue(sched->submit_wq); sched->ready = false; + + for (i = DRM_SCHED_PRIORITY_KERNEL; i < sched->num_rqs; i++) + kfree(sched->sched_rq[i]); kfree(sched->sched_rq); sched->sched_rq = NULL; diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c index 91844a396a2b..d98b6f12a7bc 100644 --- a/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c +++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c @@ -654,7 +654,7 @@ static int sun4i_hdmi_bind(struct device *dev, struct device *master, drm_connector_helper_add(&hdmi->connector, &sun4i_hdmi_connector_helper_funcs); - ret = drmm_connector_hdmi_init(drm, &hdmi->connector, + ret = drmm_connector_hdmi_ini2(drm, &hdmi->connector, /* * NOTE: Those are likely to be * wrong, but I couldn't find the diff --git a/drivers/gpu/drm/tests/drm_connector_test.c b/drivers/gpu/drm/tests/drm_connector_test.c index beb1d50a6646..3368aedd4e66 100644 --- a/drivers/gpu/drm/tests/drm_connector_test.c +++ b/drivers/gpu/drm/tests/drm_connector_test.c @@ -669,7 +669,7 @@ static void drm_test_connector_hdmi_init_valid(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; int ret; - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -689,7 +689,7 @@ static void drm_test_connector_hdmi_init_null_ddc(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; int ret; - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -709,7 +709,7 @@ static void drm_test_connector_hdmi_init_null_vendor(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; int ret; - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, NULL, "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -729,7 +729,7 @@ static void drm_test_connector_hdmi_init_null_product(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; int ret; - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, "Vendor", NULL, &dummy_funcs, &dummy_hdmi_funcs, @@ -755,7 +755,7 @@ static void drm_test_connector_hdmi_init_product_valid(struct kunit *test) KUNIT_ASSERT_LT(test, strlen(product_name), DRM_CONNECTOR_HDMI_PRODUCT_LEN); - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, "Vendor", product_name, &dummy_funcs, &dummy_hdmi_funcs, @@ -788,7 +788,7 @@ static void drm_test_connector_hdmi_init_product_length_exact(struct kunit *test KUNIT_ASSERT_EQ(test, strlen(product_name), DRM_CONNECTOR_HDMI_PRODUCT_LEN); - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, "Vendor", product_name, &dummy_funcs, &dummy_hdmi_funcs, @@ -815,7 +815,7 @@ static void drm_test_connector_hdmi_init_product_length_too_long(struct kunit *t KUNIT_ASSERT_GT(test, strlen(product_name), DRM_CONNECTOR_HDMI_PRODUCT_LEN); - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, "Vendor", product_name, &dummy_funcs, &dummy_hdmi_funcs, @@ -841,7 +841,7 @@ static void drm_test_connector_hdmi_init_vendor_valid(struct kunit *test) KUNIT_ASSERT_LT(test, strlen(vendor_name), DRM_CONNECTOR_HDMI_VENDOR_LEN); - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, vendor_name, "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -873,7 +873,7 @@ static void drm_test_connector_hdmi_init_vendor_length_exact(struct kunit *test) KUNIT_ASSERT_EQ(test, strlen(vendor_name), DRM_CONNECTOR_HDMI_VENDOR_LEN); - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, vendor_name, "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -900,7 +900,7 @@ static void drm_test_connector_hdmi_init_vendor_length_too_long(struct kunit *te KUNIT_ASSERT_GT(test, strlen(vendor_name), DRM_CONNECTOR_HDMI_VENDOR_LEN); - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, vendor_name, "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -920,7 +920,7 @@ static void drm_test_connector_hdmi_init_bpc_invalid(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; int ret; - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -940,7 +940,7 @@ static void drm_test_connector_hdmi_init_bpc_null(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; int ret; - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -965,7 +965,7 @@ static void drm_test_connector_hdmi_init_bpc_8(struct kunit *test) uint64_t val; int ret; - ret = drmm_connector_hdmi_init(&priv->drm, connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, connector, "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -1006,7 +1006,7 @@ static void drm_test_connector_hdmi_init_bpc_10(struct kunit *test) uint64_t val; int ret; - ret = drmm_connector_hdmi_init(&priv->drm, connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, connector, "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -1047,7 +1047,7 @@ static void drm_test_connector_hdmi_init_bpc_12(struct kunit *test) uint64_t val; int ret; - ret = drmm_connector_hdmi_init(&priv->drm, connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, connector, "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -1083,7 +1083,7 @@ static void drm_test_connector_hdmi_init_formats_empty(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; int ret; - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -1103,7 +1103,7 @@ static void drm_test_connector_hdmi_init_formats_no_rgb(struct kunit *test) struct drm_connector_init_priv *priv = test->priv; int ret; - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -1161,7 +1161,7 @@ static void drm_test_connector_hdmi_init_formats_yuv420_allowed(struct kunit *te params = test->param_value; priv->connector.ycbcr_420_allowed = params->yuv420_allowed; - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -1182,7 +1182,7 @@ static void drm_test_connector_hdmi_init_type_valid(struct kunit *test) unsigned int connector_type = *(unsigned int *)test->param_value; int ret; - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -1217,7 +1217,7 @@ static void drm_test_connector_hdmi_init_type_invalid(struct kunit *test) unsigned int connector_type = *(unsigned int *)test->param_value; int ret; - ret = drmm_connector_hdmi_init(&priv->drm, &priv->connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, &priv->connector, "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, @@ -1494,7 +1494,7 @@ static void drm_test_drm_connector_attach_broadcast_rgb_property_hdmi_connector( struct drm_property *prop; int ret; - ret = drmm_connector_hdmi_init(&priv->drm, connector, + ret = drmm_connector_hdmi_ini2(&priv->drm, connector, "Vendor", "Product", &dummy_funcs, &dummy_hdmi_funcs, diff --git a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c index f8ae1a0bd5c0..b2e347863a77 100644 --- a/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c +++ b/drivers/gpu/drm/tests/drm_hdmi_state_helper_test.c @@ -275,7 +275,7 @@ __connector_hdmi_init(struct kunit *test, conn = &priv->connector; conn->ycbcr_420_allowed = !!(formats & BIT(DRM_OUTPUT_COLOR_FORMAT_YCBCR420)); - ret = drmm_connector_hdmi_init(drm, conn, + ret = drmm_connector_hdmi_ini2(drm, conn, "Vendor", "Product", &dummy_connector_funcs, hdmi_funcs, diff --git a/drivers/gpu/drm/v3d/v3d_drv.h b/drivers/gpu/drm/v3d/v3d_drv.h index 79ef20c89ff2..d40610a34db9 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.h +++ b/drivers/gpu/drm/v3d/v3d_drv.h @@ -84,6 +84,8 @@ struct v3d_queue_state { * This way, only events related to a specific submission will be counted. */ struct v3d_perfmon { + struct v3d_dev *v3d; + /* Tracks the number of users of the perfmon, when this counter reaches * zero the perfmon is destroyed. */ @@ -184,6 +186,11 @@ struct v3d_dev { /* Perfmon currently programmed in HW (or NULL if none). */ struct v3d_perfmon *active; + /* Number of perfmons alive on this device. Jobs are not + * serialized if the number is zero. + */ + atomic_t nperfmons; + /* Finished fence of the most recently submitted job that * opened a serialization window (i.e. a job with a non-global * perfmon attached). @@ -191,8 +198,11 @@ struct v3d_dev { struct dma_fence *fence; /* Finished fence of the most recently submitted job on each HW - * queue. Used so that a new perfmon-carrying job can depend on - * every job currently in-flight across all queues. + * queue, which is used so that a new perfmon-carrying job can + * depend on every job currently in-flight across all queues. + * + * Finished fences are only tracked if @nperfmons > 0 and no + * global perfmon is set. */ struct dma_fence *last_hw_fence[V3D_MAX_QUEUES]; } perfmon_state; @@ -697,8 +707,6 @@ void v3d_perfmon_put(struct v3d_perfmon *perfmon); void v3d_perfmon_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon); void v3d_perfmon_stop(struct v3d_dev *v3d, struct v3d_perfmon *perfmon, bool capture); -void v3d_perfmon_stop_locked(struct v3d_dev *v3d, struct v3d_perfmon *perfmon, - bool capture); void v3d_perfmon_suspend(struct v3d_dev *v3d); void v3d_perfmon_resume(struct v3d_dev *v3d); struct v3d_perfmon *v3d_perfmon_find(struct v3d_file_priv *v3d_priv, int id); diff --git a/drivers/gpu/drm/v3d/v3d_perfmon.c b/drivers/gpu/drm/v3d/v3d_perfmon.c index 07dab7fb3060..c00f18d4a425 100644 --- a/drivers/gpu/drm/v3d/v3d_perfmon.c +++ b/drivers/gpu/drm/v3d/v3d_perfmon.c @@ -217,8 +217,10 @@ void v3d_perfmon_get(struct v3d_perfmon *perfmon) void v3d_perfmon_put(struct v3d_perfmon *perfmon) { - if (perfmon && refcount_dec_and_test(&perfmon->refcnt)) + if (perfmon && refcount_dec_and_test(&perfmon->refcnt)) { + atomic_dec(&perfmon->v3d->perfmon_state.nperfmons); kfree(perfmon); + } } static void v3d_perfmon_hw_start(struct v3d_dev *v3d, struct v3d_perfmon *perfmon) @@ -305,8 +307,9 @@ static void v3d_perfmon_capture_locked(struct v3d_dev *v3d, v3d_pm_runtime_put(v3d); } -void v3d_perfmon_stop_locked(struct v3d_dev *v3d, struct v3d_perfmon *perfmon, - bool capture) +static void +v3d_perfmon_stop_locked(struct v3d_dev *v3d, struct v3d_perfmon *perfmon, + bool capture) { lockdep_assert_held(&v3d->perfmon_state.lock); @@ -434,13 +437,15 @@ int v3d_perfmon_create_ioctl(struct drm_device *dev, void *data, perfmon->counters[i] = req->counters[i]; perfmon->ncounters = req->ncounters; + perfmon->v3d = v3d; refcount_set(&perfmon->refcnt, 1); + atomic_inc(&v3d->perfmon_state.nperfmons); ret = xa_alloc(&v3d_priv->perfmons, &id, perfmon, xa_limit_32b, GFP_KERNEL); if (ret < 0) { - kfree(perfmon); + v3d_perfmon_put(perfmon); return ret; } diff --git a/drivers/gpu/drm/v3d/v3d_submit.c b/drivers/gpu/drm/v3d/v3d_submit.c index 834d52030979..7548faab67ba 100644 --- a/drivers/gpu/drm/v3d/v3d_submit.c +++ b/drivers/gpu/drm/v3d/v3d_submit.c @@ -357,22 +357,28 @@ v3d_attach_perfmon_to_jobs(struct v3d_submit *submit, u32 perfmon_id) * * We don't serialize the jobs when using a global perfmon as it's expected to * track concurrent activity from all jobs. + * + * Keeping track of the in-flight jobs costs a fence merge per job, so it is + * only done while at least one perfmon is alive, and is skipped for the jobs + * carrying no perfmon while a global perfmon is set. Jobs submitted while + * tracking is off go untracked and may overlap the first measured job. */ static int v3d_serialize_for_perfmon(struct v3d_job *job) { struct v3d_dev *v3d = job->v3d; struct dma_fence *merged; - bool is_global_perfmon; int ret; lockdep_assert_held(&v3d->sched_lock); - scoped_guard(spinlock_irqsave, &v3d->perfmon_state.lock) - is_global_perfmon = !!v3d->global_perfmon; + if (!atomic_read(&v3d->perfmon_state.nperfmons)) + return 0; - if (is_global_perfmon) - goto publish; + scoped_guard(spinlock_irqsave, &v3d->perfmon_state.lock) { + if (!job->perfmon && v3d->global_perfmon) + return 0; + } if (job->perfmon) { for (enum v3d_queue q = 0; q < V3D_MAX_QUEUES; q++) { @@ -393,7 +399,6 @@ v3d_serialize_for_perfmon(struct v3d_job *job) return ret; } -publish: /* * Accumulate every in-flight job on this queue into one merged fence. * A HW queue is fed by several scheduler entities (one per-fd), so jobs @@ -407,7 +412,7 @@ publish: dma_fence_put(v3d->perfmon_state.last_hw_fence[job->queue]); v3d->perfmon_state.last_hw_fence[job->queue] = merged; - if (job->perfmon && !is_global_perfmon) { + if (job->perfmon) { dma_fence_put(v3d->perfmon_state.fence); v3d->perfmon_state.fence = dma_fence_get(job->done_fence); } diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 616caf9d9915..eb859c65a453 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -279,7 +279,6 @@ static const struct of_device_id vc4_dma_range_matches[] = { { .compatible = "brcm,bcm2835-hvs" }, { .compatible = "brcm,bcm2835-v3d" }, { .compatible = "brcm,cygnus-v3d" }, - { .compatible = "brcm,vc4-v3d" }, {} }; diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index 649032174dbd..695bc6a29d30 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -212,14 +212,9 @@ struct vc4_dev { struct work_struct overflow_mem_work; - int power_refcount; - /* Set to true when the load tracker is active. */ bool load_tracker_enabled; - /* Mutex controlling the power refcount. */ - struct mutex power_lock; - struct { struct timer_list timer; struct work_struct reset_work; @@ -294,6 +289,13 @@ struct vc4_v3d { struct platform_device *pdev; void __iomem *regs; struct clk *clk; + + /* Reset line for the V3D block, used to recover from a GPU hang. + * NULL if the device tree does not describe one, in which case the + * GPU cannot be reset. + */ + struct reset_control *reset; + struct debugfs_regset32 regset; }; @@ -1056,6 +1058,7 @@ int vc4_v3d_bin_bo_get(struct vc4_dev *vc4, bool *used); void vc4_v3d_bin_bo_put(struct vc4_dev *vc4); int vc4_v3d_pm_get(struct vc4_dev *vc4); void vc4_v3d_pm_put(struct vc4_dev *vc4); +void vc4_v3d_init_hw(struct drm_device *dev); int vc4_v3d_debugfs_init(struct drm_minor *minor); /* vc4_validate.c */ diff --git a/drivers/gpu/drm/vc4/vc4_gem.c b/drivers/gpu/drm/vc4/vc4_gem.c index e231c906709c..3212b9167620 100644 --- a/drivers/gpu/drm/vc4/vc4_gem.c +++ b/drivers/gpu/drm/vc4/vc4_gem.c @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include #include @@ -292,19 +292,22 @@ err_free_state: static void vc4_reset(struct drm_device *dev) { - struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_v3d *v3d = to_vc4_dev(dev)->v3d; + int ret; - DRM_INFO("Resetting GPU.\n"); + vc4_irq_disable(dev); - mutex_lock(&vc4->power_lock); - if (vc4->power_refcount) { - /* Power the device off and back on the by dropping the - * reference on runtime PM. - */ - pm_runtime_put_sync_suspend(&vc4->v3d->pdev->dev); - pm_runtime_get_sync(&vc4->v3d->pdev->dev); + if (v3d->reset) { + drm_info(dev, "Resetting GPU.\n"); + + ret = reset_control_reset(v3d->reset); + if (ret) + drm_err(dev, "Failed to reset the GPU: %d\n", ret); + + vc4_v3d_init_hw(dev); + } else { + drm_info_once(dev, "No reset line; GPU state is not reset.\n"); } - mutex_unlock(&vc4->power_lock); vc4_irq_reset(dev); @@ -320,10 +323,19 @@ vc4_reset_work(struct work_struct *work) { struct vc4_dev *vc4 = container_of(work, struct vc4_dev, hangcheck.reset_work); + int ret; + + /* Make sure the device is not suspended during the reset. */ + ret = vc4_v3d_pm_get(vc4); + if (ret) { + drm_err(&vc4->base, "Failed to resume V3D for GPU reset: %d\n", ret); + return; + } vc4_save_hang_state(&vc4->base); - vc4_reset(&vc4->base); + + vc4_v3d_pm_put(vc4); } static void @@ -1177,10 +1189,6 @@ int vc4_gem_init(struct drm_device *dev) INIT_WORK(&vc4->job_done_work, vc4_job_done_work); - ret = drmm_mutex_init(dev, &vc4->power_lock); - if (ret) - return ret; - INIT_LIST_HEAD(&vc4->purgeable.list); ret = drmm_mutex_init(dev, &vc4->purgeable.lock); diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c index ea1f2013b20e..3a90dbb61f4e 100644 --- a/drivers/gpu/drm/vc4/vc4_hdmi.c +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c @@ -375,7 +375,7 @@ static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi, * the lock for now. */ - drm_atomic_helper_connector_hdmi_hotplug(connector, status); + drm_atomic_helper_connector_hdmi_hotplug(connector, ctx, status); if (status != connector_status_connected) return; @@ -542,7 +542,7 @@ static int vc4_hdmi_connector_init(struct drm_device *dev, if (vc4_hdmi->variant->supports_hdr) max_bpc = 12; - ret = drmm_connector_hdmi_init(dev, connector, + ret = drmm_connector_hdmi_ini2(dev, connector, "Broadcom", "Videocore", &vc4_hdmi_connector_funcs, &vc4_hdmi_hdmi_connector_funcs, diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index 7877d493d80e..3a3ea1e62dcb 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -336,10 +336,9 @@ void vc4_irq_reset(struct drm_device *dev) V3D_WRITE(V3D_INTCTL, V3D_DRIVER_IRQS); /* - * Turn all our interrupts on. Binner out of memory is the - * only one we expect to trigger at this point, since we've - * just come from poweron and haven't supplied any overflow - * memory yet. + * Turn all our interrupts on. Binner out of memory is the only + * one we expect to trigger at this point, since the reset cleared + * the overflow memory address and none has been supplied yet. */ V3D_WRITE(V3D_INTENA, V3D_DRIVER_IRQS); diff --git a/drivers/gpu/drm/vc4/vc4_v3d.c b/drivers/gpu/drm/vc4/vc4_v3d.c index f32410420d3e..b40d98c9d1d2 100644 --- a/drivers/gpu/drm/vc4/vc4_v3d.c +++ b/drivers/gpu/drm/vc4/vc4_v3d.c @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -122,29 +123,13 @@ static int vc4_v3d_debugfs_ident(struct seq_file *m, void *unused) return 0; } -/* - * Wraps pm_runtime_get_sync() in a refcount, so that we can reliably - * get the pm_runtime refcount to 0 in vc4_reset(). - */ int vc4_v3d_pm_get(struct vc4_dev *vc4) { if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4)) return -ENODEV; - mutex_lock(&vc4->power_lock); - if (vc4->power_refcount++ == 0) { - int ret = pm_runtime_get_sync(&vc4->v3d->pdev->dev); - - if (ret < 0) { - vc4->power_refcount--; - mutex_unlock(&vc4->power_lock); - return ret; - } - } - mutex_unlock(&vc4->power_lock); - - return 0; + return pm_runtime_resume_and_get(&vc4->v3d->pdev->dev); } void @@ -153,15 +138,10 @@ vc4_v3d_pm_put(struct vc4_dev *vc4) if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4)) return; - mutex_lock(&vc4->power_lock); - if (--vc4->power_refcount == 0) { - pm_runtime_mark_last_busy(&vc4->v3d->pdev->dev); - pm_runtime_put_autosuspend(&vc4->v3d->pdev->dev); - } - mutex_unlock(&vc4->power_lock); + pm_runtime_put_autosuspend(&vc4->v3d->pdev->dev); } -static void vc4_v3d_init_hw(struct drm_device *dev) +void vc4_v3d_init_hw(struct drm_device *dev) { struct vc4_dev *vc4 = to_vc4_dev(dev); @@ -447,6 +427,14 @@ static int vc4_v3d_bind(struct device *dev, struct device *master, void *data) if (IS_ERR(v3d->clk)) return dev_err_probe(dev, PTR_ERR(v3d->clk), "Failed to get V3D clock\n"); + v3d->reset = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(v3d->reset)) + return dev_err_probe(dev, PTR_ERR(v3d->reset), + "Failed to get reset control\n"); + + if (!v3d->reset) + drm_warn(drm, "No V3D reset line in the device tree"); + ret = platform_get_irq(pdev, 0); if (ret < 0) return ret; @@ -531,7 +519,6 @@ static void vc4_v3d_dev_remove(struct platform_device *pdev) const struct of_device_id vc4_v3d_dt_match[] = { { .compatible = "brcm,bcm2835-v3d" }, { .compatible = "brcm,cygnus-v3d" }, - { .compatible = "brcm,vc4-v3d" }, {} }; diff --git a/drivers/gpu/drm/virtio/virtgpu_plane.c b/drivers/gpu/drm/virtio/virtgpu_plane.c index 9f442b6afa9c..62955acd1f8e 100644 --- a/drivers/gpu/drm/virtio/virtgpu_plane.c +++ b/drivers/gpu/drm/virtio/virtgpu_plane.c @@ -106,6 +106,7 @@ static int virtio_gpu_plane_atomic_check(struct drm_plane *plane, plane); struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); + struct virtio_gpu_object *bo; bool is_cursor = plane->type == DRM_PLANE_TYPE_CURSOR; struct drm_crtc_state *crtc_state; int ret; @@ -116,10 +117,13 @@ static int virtio_gpu_plane_atomic_check(struct drm_plane *plane, /* * Ignore damage clips if the framebuffer attached to the plane's state * has changed since the last plane update (page-flip). In this case, a - * full plane update should happen because uploads are done per-buffer. + * full plane update should happen for dumb buffers because uploads are + * done per-buffer. Rendered resources are already coherent on the host, + * so preserve userspace's accumulated per-buffer damage for those. */ - if (old_plane_state->fb != new_plane_state->fb) - new_plane_state->ignore_damage_clips = true; + bo = gem_to_virtio_gpu_obj(new_plane_state->fb->obj[0]); + new_plane_state->ignore_damage_clips = + old_plane_state->fb != new_plane_state->fb && bo->dumb; crtc_state = drm_atomic_get_crtc_state(state, new_plane_state->crtc); diff --git a/include/drm/display/drm_hdmi_helper.h b/include/drm/display/drm_hdmi_helper.h index 9c31ed90516b..2cdd6e3bc090 100644 --- a/include/drm/display/drm_hdmi_helper.h +++ b/include/drm/display/drm_hdmi_helper.h @@ -8,6 +8,7 @@ struct drm_connector; struct drm_connector_state; struct drm_display_mode; +struct drm_modeset_acquire_ctx; enum drm_output_color_format; void @@ -35,4 +36,18 @@ drm_hdmi_acr_get_n_cts(unsigned long long tmds_char_rate, unsigned int *out_n, unsigned int *out_cts); +bool +drm_hdmi_mode_needs_scrambling(const struct drm_display_mode *mode, + unsigned int bpc, enum drm_output_color_format fmt); + +int +drm_connector_hdmi_enable_scrambling(struct drm_connector *connector, + const struct drm_connector_state *conn_state); +int +drm_connector_hdmi_disable_scrambling(struct drm_connector *connector); + +int +drm_connector_hdmi_sync_scdc(struct drm_connector *connector, bool plugged, + struct drm_modeset_acquire_ctx *ctx); + #endif diff --git a/include/drm/display/drm_hdmi_state_helper.h b/include/drm/display/drm_hdmi_state_helper.h index 596c57d9d250..965342f6bbc9 100644 --- a/include/drm/display/drm_hdmi_state_helper.h +++ b/include/drm/display/drm_hdmi_state_helper.h @@ -8,6 +8,7 @@ struct drm_atomic_commit; struct drm_connector; struct drm_connector_state; struct drm_display_mode; +struct drm_modeset_acquire_ctx; struct hdmi_audio_infoframe; enum drm_connector_status; @@ -23,9 +24,12 @@ int drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector int drm_atomic_helper_connector_hdmi_clear_audio_infoframe(struct drm_connector *connector); int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *connector, struct drm_atomic_commit *state); -void drm_atomic_helper_connector_hdmi_hotplug(struct drm_connector *connector, - enum drm_connector_status status); +int drm_atomic_helper_connector_hdmi_hotplug(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, + enum drm_connector_status status); void drm_atomic_helper_connector_hdmi_force(struct drm_connector *connector); +int drm_atomic_helper_connector_hdmi_force_ctx(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx); enum drm_mode_status drm_hdmi_connector_mode_valid(struct drm_connector *connector, diff --git a/include/drm/display/drm_scdc_helper.h b/include/drm/display/drm_scdc_helper.h index a3b20adaac7e..6a8be1f16ce0 100644 --- a/include/drm/display/drm_scdc_helper.h +++ b/include/drm/display/drm_scdc_helper.h @@ -168,6 +168,8 @@ bool drm_scdc_get_scrambling_status(struct drm_connector *connector); bool drm_scdc_set_scrambling(struct drm_connector *connector, bool enable); bool drm_scdc_set_high_tmds_clock_ratio(struct drm_connector *connector, bool set); +int drm_scdc_set_source_version(struct drm_connector *connector, u8 ver); + int drm_scdc_read_state(struct drm_connector *connector, struct drm_scdc_state *state); void drm_scdc_debugfs_init(struct drm_connector *connector, struct dentry *root); diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 58fff047f43b..77b3ffe69230 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -561,6 +561,41 @@ struct drm_bridge_funcs { const struct drm_display_mode *mode, unsigned long long tmds_rate); + /** + * @hdmi_scrambler_enable: + * + * Enable source-side scrambling and the high TMDS clock ratio for the + * mode currently being applied. + * + * This only programs the source side. Sink-side SCDC configuration is + * handled by the HDMI connector helpers. + * + * Mandatory for bridges that set the DRM_BRIDGE_OP_HDMI flag in their + * &drm_bridge->ops and advertise HDMI_VERSION_2_0 or later via + * &drm_bridge->supported_hdmi_ver; unused otherwise. + * + * Returns: + * 0 on success, a negative error code otherwise. + */ + int (*hdmi_scrambler_enable)(struct drm_bridge *bridge); + + /** + * @hdmi_scrambler_disable: + * + * Disable source-side scrambling and the high TMDS clock ratio. + * + * This only programs the source side. Sink-side SCDC configuration is + * handled by the HDMI connector helpers. + * + * Mandatory for bridges that set the DRM_BRIDGE_OP_HDMI flag in their + * &drm_bridge->ops and advertise HDMI_VERSION_2_0 or later via + * &drm_bridge->supported_hdmi_ver; unused otherwise. + * + * Returns: + * 0 on success, a negative error code otherwise. + */ + int (*hdmi_scrambler_disable)(struct drm_bridge *bridge); + /** * @hdmi_clear_avi_infoframe: * @@ -1081,6 +1116,12 @@ struct drm_bridge { */ const char *product; + /** + * @supported_hdmi_ver: HDMI version the bridge is conformant with. + * This is only relevant if @DRM_BRIDGE_OP_HDMI is set. + */ + enum hdmi_version supported_hdmi_ver; + /** * @supported_formats: Bitmask of @drm_output_color_format listing * supported output formats. This is only relevant if @@ -1088,6 +1129,14 @@ struct drm_bridge { */ unsigned int supported_formats; + /** + * @max_tmds_char_rate: Maximum TMDS character rate, in Hz, the HDMI + * bridge supports. A value of 0 means the core should use the default + * limit implied by @supported_hdmi_ver. This is only relevant if + * @DRM_BRIDGE_OP_HDMI is set. + */ + unsigned long long max_tmds_char_rate; + /** * @max_bpc: Maximum bits per char the HDMI bridge supports. Allowed * values are 8, 10 and 12. This is only relevant if diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index c61345e6fb08..2ee7c59329aa 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -1198,6 +1199,17 @@ struct drm_connector_hdmi_state { * @tmds_char_rate: TMDS Character Rate, in Hz. */ unsigned long long tmds_char_rate; + + /** + * @scrambler_needed: Whether HDMI 2.0 SCDC scrambling is required + * for the negotiated mode/bpc/format. + * + * Computed by drm_atomic_helper_connector_hdmi_check() according to + * the HDMI 2.0 specification: scrambling is mandatory above a 340 MHz + * TMDS character rate. Optional scrambling at lower rates is + * deliberately not requested by the helper. + */ + bool scrambler_needed; }; /** @@ -1470,8 +1482,50 @@ struct drm_connector_infoframe_funcs { /** * struct drm_connector_hdmi_funcs - drm_hdmi_connector control functions + * and controller capabilities */ struct drm_connector_hdmi_funcs { + /** + * @vendor: HDMI Controller Vendor name. + */ + const char *vendor; + + /** + * @product: HDMI Controller Product name + */ + const char *product; + + /** + * @supported_hdmi_ver: + * + * Maximum HDMI specification version supported by the controller side + * of this connector. This describes the controller capability only; + * the effective link capabilities may be further restricted by the + * sink, bridge, or mode validation. + */ + enum hdmi_version supported_hdmi_ver; + + /** + * @supported_tmds_char_rate: + * + * Maximum TMDS character rate supported by the controller, in Hz. + * A value of 0 means the core should use the default limit implied by + * @supported_hdmi_ver. + */ + unsigned long long supported_tmds_char_rate; + + /** + * @supported_formats: + * + * Bitmask of @drm_output_color_format listing supported output formats. + */ + unsigned long supported_formats; + + /** + * @max_bpc: Maximum bits per char the HDMI connector supports. + */ + unsigned int max_bpc; + /** * @tmds_char_rate_valid: * @@ -1506,6 +1560,36 @@ struct drm_connector_hdmi_funcs { */ const struct drm_edid *(*read_edid)(struct drm_connector *connector); + /** + * @scrambler_enable: + * + * The callback is invoked via @drm_connector_hdmi_enable_scrambling + * during commit to setup SCDC scrambling and high TMDS clock ratio on + * the source side. + * + * The @scrambler_enable callback is mandatory if HDMI 2.0 is to be + * supported. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*scrambler_enable)(struct drm_connector *connector); + + /** + * @scrambler_disable: + * + * The callback is invoked via @drm_connector_hdmi_disable_scrambling + * during commit to tear down SCDC scrambling and high TMDS clock ratio + * on the source side. + * + * The @scrambler_disable callback is mandatory if HDMI 2.0 is to be + * supported. + * + * Returns: + * 0 on success, a negative error code otherwise + */ + int (*scrambler_disable)(struct drm_connector *connector); + /** * @avi: * @@ -1628,6 +1712,12 @@ struct drm_connector_funcs { * the sysfs interfaces or on the kernel cmdline. In that case the * @detect callback isn't called. * + * New drivers should implement + * &drm_connector_helper_funcs.force_ctx instead, which gets passed + * the modeset acquire context and thus allows taking additional + * locks. It takes precedence over this callback when both are + * implemented. + * * FIXME: * * Note that this hook is only called by the probe helper. It's not in @@ -2119,7 +2209,38 @@ struct drm_connector_hdmi { unsigned long supported_formats; /** - * @funcs: HDMI connector Control Functions + * @max_tmds_char_rate: Maximum TMDS character rate, in Hz, + * supported by the controller. + * + * This is inferred from &drm_connector_hdmi_funcs.supported_hdmi_ver, + * by default. However, controllers may support a lower rate than that + * specification version would imply. If that is the case, drivers are + * expected to set &drm_connector_hdmi_funcs.supported_tmds_char_rate + * to a non-zero value indicating the actual limit. + */ + unsigned long long max_tmds_char_rate; + + /** + * @scrambler_enabled: Tracks whether HDMI 2.0 scrambler is currently enabled. + */ + bool scrambler_enabled; + + /** + * @scdc_work: Work item currently used to monitor sink-side scrambling + * status and retry setup if the sink resets it. + */ + struct delayed_work scdc_work; + + /** + * @scdc_work_initialized: Tracks whether @scdc_work has been set up via + * INIT_DELAYED_WORK(). The work item is initialized lazily on the first + * scrambling enable, so this guards the teardown paths against touching + * an uninitialized work item. + */ + bool scdc_work_initialized; + + /** + * @funcs: HDMI connector Control Functions and controller capabilities */ const struct drm_connector_hdmi_funcs *funcs; @@ -2617,6 +2738,12 @@ int drmm_connector_init(struct drm_device *dev, int connector_type, struct i2c_adapter *ddc); int drmm_connector_hdmi_init(struct drm_device *dev, + struct drm_connector *connector, + const struct drm_connector_funcs *funcs, + const struct drm_connector_hdmi_funcs *hdmi_funcs, + int connector_type, + struct i2c_adapter *ddc); +int drmm_connector_hdmi_ini2(struct drm_device *dev, struct drm_connector *connector, const char *vendor, const char *product, const struct drm_connector_funcs *funcs, @@ -2703,6 +2830,24 @@ drm_connector_is_unregistered(struct drm_connector *connector) DRM_CONNECTOR_UNREGISTERED; } +/** + * drm_connector_hdmi_scrambler_supported - does connector support HDMI + * scrambling? + * @connector: DRM connector + * + * Checks whether or not @connector driver supports HDMI scrambling, based on + * the HDMI version advertised by the controller. + * + * Returns: + * True if the connector supports scrambling, false otherwise. + */ +static inline bool +drm_connector_hdmi_scrambler_supported(const struct drm_connector *connector) +{ + return connector->hdmi.funcs && + connector->hdmi.funcs->supported_hdmi_ver >= HDMI_VERSION_2_0; +} + void drm_connector_oob_hotplug_event(struct fwnode_handle *connector_fwnode, enum drm_connector_status status); enum drm_connector_color_format diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index dc4c5e8733c3..60b30cc26940 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -935,6 +935,34 @@ struct drm_connector_helper_funcs { struct drm_modeset_acquire_ctx *ctx, bool force); + /** + * @force_ctx: + * + * This function is called to update internal encoder state when the + * connector is forced to a certain state by userspace, either through + * the sysfs interfaces or on the kernel cmdline. In that case the + * @detect_ctx callback isn't called. + * + * This is the atomic version of &drm_connector_funcs.force. When + * both are implemented, this one takes precedence and + * &drm_connector_funcs.force is not called. + * + * To avoid races against concurrent connector state updates, the + * helper libraries always call this with ctx set to a valid context, + * and &drm_mode_config.connection_mutex will always be locked with + * the ctx parameter set to this ctx. This allows taking additional + * locks as required. + * + * RETURNS: + * + * Zero on success, or a negative error code otherwise. This includes + * -EDEADLK when a lock acquisition needs to be restarted, in which + * case the helpers take care of backing off and calling this function + * again. + */ + int (*force_ctx)(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx); + /** * @mode_valid: * diff --git a/include/linux/hdmi.h b/include/linux/hdmi.h index 8dab78e1f61b..b80a5ee63bb2 100644 --- a/include/linux/hdmi.h +++ b/include/linux/hdmi.h @@ -27,6 +27,18 @@ #include #include +enum hdmi_version { + HDMI_VERSION_UNKNOWN, + HDMI_VERSION_1_0, + HDMI_VERSION_1_1, + HDMI_VERSION_1_2, + HDMI_VERSION_1_3, + HDMI_VERSION_1_4, + HDMI_VERSION_2_0, + HDMI_VERSION_2_1, + HDMI_VERSION_2_2, +}; + enum hdmi_packet_type { HDMI_PACKET_TYPE_NULL = 0x00, HDMI_PACKET_TYPE_AUDIO_CLOCK_REGEN = 0x01,