OSDN Git Service

drm/sde: bridge chip error and smmu fault handling for recovery
authorRahul Sharma <rahsha@codeaurora.org>
Mon, 9 Jul 2018 18:18:12 +0000 (23:48 +0530)
committerRahul Sharma <rahsha@codeaurora.org>
Thu, 19 Jul 2018 04:38:28 +0000 (10:08 +0530)
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 <rahsha@codeaurora.org>
drivers/gpu/drm/msm/dba_bridge.c
drivers/gpu/drm/msm/msm_mmu.h
drivers/gpu/drm/msm/msm_smmu.c
drivers/gpu/drm/msm/sde/sde_kms.c
drivers/gpu/drm/msm/sde/sde_recovery_manager.h
drivers/video/fbdev/msm/msm_dba/adv7533.c
include/video/msm_dba.h

index 62294dd..cfab8c7 100644 (file)
@@ -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);
index 74dea95..03f8a55 100644 (file)
@@ -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__ */
index aefbe09..eed3cfc 100644 (file)
@@ -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)
 {
index bd8f370..5247ec5 100644 (file)
@@ -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);
 
index 32fe17a..aeaecbd 100644 (file)
 
 #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
index 15fe77d..a21b6db 100644 (file)
@@ -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__);
index 3d20fd8..8ce2138 100644 (file)
@@ -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),
 };
 
 /**