#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__
* @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;
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");
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;
{
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) {
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);
}
}
+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)
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);
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__ */
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)
{
.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,
};
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;
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.
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);
#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
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__);
/*
- * 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
* @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
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),
};
/**