From: Rahul Sharma Date: Mon, 9 Jul 2018 18:18:12 +0000 (+0530) Subject: drm/sde: bridge chip error and smmu fault handling for recovery X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=faa4206fb7241a3ff1ceba48d6cd29e92d312f7f;p=sagit-ice-cold%2Fkernel_xiaomi_msm8998.git drm/sde: bridge chip error and smmu fault handling for recovery This implementation adds the following support: 1. handle bridge chip error reported by driver to DBA. error reported to DBA will be send to recovery manager to recover the faulty bridge chip by performing a power cycle. 2. smmu fault error notification. 3. detection on errors in bridge chip driver. Change-Id: I86cc491e3cd25fdcf9b998a0d687baba0a06d836 Signed-off-by: Rahul Sharma --- diff --git a/drivers/gpu/drm/msm/dba_bridge.c b/drivers/gpu/drm/msm/dba_bridge.c index 62294ddf8034..cfab8c73a1ad 100644 --- a/drivers/gpu/drm/msm/dba_bridge.c +++ b/drivers/gpu/drm/msm/dba_bridge.c @@ -16,6 +16,7 @@ #include "drm_edid.h" #include "sde_kms.h" #include "dba_bridge.h" +#include "sde/sde_recovery_manager.h" #undef pr_fmt #define pr_fmt(fmt) "dba_bridge:[%s] " fmt, __func__ @@ -36,6 +37,7 @@ * @num_of_input_lanes: Number of input lanes in case of DSI/LVDS * @pluggable: If it's pluggable * @panel_count: Number of panels attached to this display + * @client_info: bridge chip specific information for recovery manager */ struct dba_bridge { struct drm_bridge base; @@ -52,12 +54,17 @@ struct dba_bridge { bool pluggable; u32 panel_count; bool cont_splash_enabled; + struct recovery_client_info client_info; }; #define to_dba_bridge(x) container_of((x), struct dba_bridge, base) +static int _dba_bridge_recovery_callback(int err_code, + struct recovery_client_info *client_info); + static void _dba_bridge_cb(void *data, enum msm_dba_callback_event event) { struct dba_bridge *d_bridge = data; + int chip_err; if (!d_bridge) { SDE_ERROR("Invalid data\n"); @@ -73,6 +80,12 @@ static void _dba_bridge_cb(void *data, enum msm_dba_callback_event event) case MSM_DBA_CB_HPD_DISCONNECT: DRM_DEBUG("HPD DISCONNECT\n"); break; + case MSM_DBA_CB_DDC_I2C_ERROR: + case MSM_DBA_CB_DDC_TIMEOUT: + DRM_DEBUG("DDC FAILURE\n"); + chip_err = DBA_BRIDGE_CRITICAL_ERR + d_bridge->id; + sde_recovery_set_events(chip_err); + break; default: DRM_DEBUG("event:%d is not supported\n", event); break; @@ -83,6 +96,7 @@ static int _dba_bridge_attach(struct drm_bridge *bridge) { struct dba_bridge *d_bridge = to_dba_bridge(bridge); struct msm_dba_reg_info info; + struct recovery_client_info *client_info = &d_bridge->client_info; int ret = 0; if (!bridge) { @@ -115,6 +129,25 @@ static int _dba_bridge_attach(struct drm_bridge *bridge) goto error; } + snprintf(client_info->name, MAX_REC_NAME_LEN, "%s_%d", + d_bridge->chip_name, d_bridge->id); + + client_info->recovery_cb = _dba_bridge_recovery_callback; + + /* Identify individual chip by different error codes */ + client_info->err_supported[0].reported_err_code = + DBA_BRIDGE_CRITICAL_ERR + d_bridge->id; + client_info->err_supported[0].pre_err_code = 0; + client_info->err_supported[0].post_err_code = 0; + client_info->no_of_err = 1; + /* bridge chip context */ + client_info->pdata = d_bridge; + + ret = sde_recovery_client_register(client_info); + if (ret) + SDE_ERROR("%s recovery mgr register failed %d\n", + __func__, ret); + DRM_INFO("client:%s bridge:[%s:%d] attached\n", d_bridge->client_name, d_bridge->chip_name, d_bridge->id); @@ -239,6 +272,44 @@ static void _dba_bridge_post_disable(struct drm_bridge *bridge) } } +static int _dba_bridge_recovery_callback(int err_code, + struct recovery_client_info *client_info) +{ + int rc = 0; + struct dba_bridge *d_bridge; + + if (!client_info) { + SDE_ERROR("Invalid client info\n"); + rc = -EINVAL; + return rc; + } + + d_bridge = client_info->pdata; + + err_code = err_code - d_bridge->id; + + switch (err_code) { + case DBA_BRIDGE_CRITICAL_ERR: + SDE_DEBUG("%s critical bridge chip error\n", __func__); + + /* Power OFF */ + _dba_bridge_disable(&d_bridge->base); + _dba_bridge_post_disable(&d_bridge->base); + + /* settle power rails */ + msleep(100); + + /* Power On */ + _dba_bridge_pre_enable(&d_bridge->base); + _dba_bridge_enable(&d_bridge->base); + + break; + default: + SDE_ERROR("%s error %d undefined\n", __func__, err_code); + } + return rc; +} + static void _dba_bridge_mode_set(struct drm_bridge *bridge, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -369,6 +440,9 @@ void dba_bridge_cleanup(struct drm_bridge *bridge) if (!bridge) return; + sde_recovery_client_unregister(d_bridge->client_info.handle); + d_bridge->client_info.handle = NULL; + if (IS_ENABLED(CONFIG_MSM_DBA)) { if (!IS_ERR_OR_NULL(d_bridge->dba_ctx)) msm_dba_deregister_client(d_bridge->dba_ctx); diff --git a/drivers/gpu/drm/msm/msm_mmu.h b/drivers/gpu/drm/msm/msm_mmu.h index 74dea95d90de..03f8a55255c9 100644 --- a/drivers/gpu/drm/msm/msm_mmu.h +++ b/drivers/gpu/drm/msm/msm_mmu.h @@ -93,4 +93,8 @@ static inline void msm_mmu_disable(struct msm_mmu *mmu) int __init msm_smmu_driver_init(void); void __exit msm_smmu_driver_cleanup(void); +/* register custom fault handler for a specific domain */ +void msm_smmu_register_fault_handler(struct msm_mmu *mmu, + iommu_fault_handler_t handler); + #endif /* __MSM_MMU_H__ */ diff --git a/drivers/gpu/drm/msm/msm_smmu.c b/drivers/gpu/drm/msm/msm_smmu.c index aefbe0988fe5..eed3cfcb99ee 100644 --- a/drivers/gpu/drm/msm/msm_smmu.c +++ b/drivers/gpu/drm/msm/msm_smmu.c @@ -335,6 +335,18 @@ static struct device *msm_smmu_device_create(struct device *dev, return &pdev->dev; } +void msm_smmu_register_fault_handler(struct msm_mmu *mmu, + iommu_fault_handler_t handler) +{ + struct msm_smmu *smmu = to_msm_smmu(mmu); + struct msm_smmu_client *client = msm_smmu_to_client(smmu); + + if (client) + iommu_set_fault_handler(client->mmu_mapping->domain, + handler, client->dev); + +} + struct msm_mmu *msm_smmu_new(struct device *dev, enum msm_mmu_domain_type domain) { diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index bd8f3701a282..5247ec51cabf 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -67,7 +67,8 @@ static struct recovery_client_info info = { .recovery_cb = sde_kms_recovery_callback, .err_supported[0] = {SDE_UNDERRUN, 0, 0}, .err_supported[1] = {SDE_VSYNC_MISS, 0, 0}, - .no_of_err = 2, + .err_supported[2] = {SDE_SMMU_FAULT, 0, 0}, + .no_of_err = 3, .handle = NULL, .pdata = NULL, }; @@ -1140,6 +1141,18 @@ static int _sde_kms_mmu_destroy(struct sde_kms *sde_kms) return 0; } +static int sde_smmu_fault_handler(struct iommu_domain *iommu, + struct device *dev, unsigned long iova, int flags, void *arg) +{ + + dev_info(dev, "%s: iova=0x%08lx, flags=0x%x, iommu=%pK\n", __func__, + iova, flags, iommu); + + sde_recovery_set_events(SDE_SMMU_FAULT); + + return 0; +} + static int _sde_kms_mmu_init(struct sde_kms *sde_kms) { struct msm_mmu *mmu; @@ -1158,6 +1171,8 @@ static int _sde_kms_mmu_init(struct sde_kms *sde_kms) continue; } + msm_smmu_register_fault_handler(mmu, sde_smmu_fault_handler); + /* Attaching smmu means IOMMU HW starts to work immediately. * However, display HW in LK is still accessing memory * while the memory map is not done yet. @@ -1523,6 +1538,10 @@ static int sde_kms_recovery_callback(int err_code, pr_debug("%s [SDE_VSYNC_MISS] trigger soft reset\n", __func__); break; + case SDE_SMMU_FAULT: + pr_debug("%s [SDE_SMMU_FAULT] trigger soft reset\n", __func__); + break; + default: pr_err("%s error %d undefined\n", __func__, err_code); diff --git a/drivers/gpu/drm/msm/sde/sde_recovery_manager.h b/drivers/gpu/drm/msm/sde/sde_recovery_manager.h index 32fe17a187a0..aeaecbd194f4 100644 --- a/drivers/gpu/drm/msm/sde/sde_recovery_manager.h +++ b/drivers/gpu/drm/msm/sde/sde_recovery_manager.h @@ -29,11 +29,17 @@ #define MAX_REC_NAME_LEN (16) #define MAX_REC_UEVENT_LEN (64) -#define MAX_REC_ERR_SUPPORT (2) +#define MAX_REC_ERR_SUPPORT (3) /* MSM Recovery Manager Error Code */ +#define SDE_SMMU_FAULT 111 #define SDE_UNDERRUN 222 #define SDE_VSYNC_MISS 333 +/* + * instance id of bridge chip is added to make error code + * unique to individual bridge chip instance + */ +#define DBA_BRIDGE_CRITICAL_ERR 444 /** * struct recovery_mgr_info - Recovery manager information diff --git a/drivers/video/fbdev/msm/msm_dba/adv7533.c b/drivers/video/fbdev/msm/msm_dba/adv7533.c index 15fe77d05091..a21b6db85ed8 100644 --- a/drivers/video/fbdev/msm/msm_dba/adv7533.c +++ b/drivers/video/fbdev/msm/msm_dba/adv7533.c @@ -885,9 +885,13 @@ static void adv7533_handle_hdcp_intr(struct adv7533 *pdata, u8 hdcp_status) break; case 4: pr_err("%s: DDC: I2C ERROR\n", __func__); + adv7533_notify_clients(&pdata->dev_info, + MSM_DBA_CB_DDC_I2C_ERROR); break; case 5: pr_err("%s: DDC: TIMED OUT DS DONE\n", __func__); + adv7533_notify_clients(&pdata->dev_info, + MSM_DBA_CB_DDC_TIMEOUT); break; case 6: pr_err("%s: DDC: MAX CAS EXC\n", __func__); diff --git a/include/video/msm_dba.h b/include/video/msm_dba.h index 3d20fd8d65eb..8ce2138044c3 100644 --- a/include/video/msm_dba.h +++ b/include/video/msm_dba.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2015,2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -53,6 +53,9 @@ * @MSM_DBA_CB_POST_RESET: This callback is called after device reset is * complete and the driver has applied back all the * properties. + * @MSM_DBA_CB_DDC_I2C_ERROR: Detected a failure in DDC block for i2c error. + * @MSM_DBA_CB_DDC_TIMEOUT: Detected a failure in DDC block for timed out + * waiting for downstream receiver. * * Clients for this driver can register for receiving callbacks for specific * events. This enum defines the type of events supported by the driver. An @@ -71,6 +74,8 @@ enum msm_dba_callback_event { MSM_DBA_CB_CEC_READ_PENDING = BIT(9), MSM_DBA_CB_PRE_RESET = BIT(10), MSM_DBA_CB_POST_RESET = BIT(11), + MSM_DBA_CB_DDC_I2C_ERROR = BIT(12), + MSM_DBA_CB_DDC_TIMEOUT = BIT(13), }; /**