@@ -32,6 +32,12 @@ msm-y := \
edp/v200/edp_v200_connector.o \
edp/v200/edp_v200_ctrl.o \
edp/v200/edp_v200_phy.o \
+ edp/v510/edp_v510.o \
+ edp/v510/edp_v510_aux.o \
+ edp/v510/edp_v510_bridge.o \
+ edp/v510/edp_v510_connector.o \
+ edp/v510/edp_v510_ctrl.o \
+ edp/v510/edp_v510_phy.o \
disp/mdp_format.o \
disp/mdp_kms.o \
disp/mdp4/mdp4_crtc.o \
@@ -8,11 +8,13 @@
void __init msm_edp_register(void)
{
msm_edp_v200_register();
+ msm_edp_v510_register();
}
void __exit msm_edp_unregister(void)
{
msm_edp_v200_unregister();
+ msm_edp_v510_unregister();
}
/* Second part of initialization, the drm/kms level modeset_init */
@@ -29,6 +31,8 @@ int msm_edp_modeset_init(struct msm_edp *edp, struct drm_device *dev,
if (edp->version == MSM_EDP_VERSION_200)
ret = msm_edp_v200_modeset_init(edp, dev, encoder);
+ else if (edp->version == MSM_EDP_VERSION_510)
+ ret = msm_edp_v510_modeset_init(edp, dev, encoder);
return ret;
}
@@ -19,6 +19,7 @@
#include "dpu_io_util.h"
#define MSM_EDP_VERSION_200 200
+#define MSM_EDP_VERSION_510 510
struct msm_edp {
struct drm_device *dev;
@@ -38,4 +39,9 @@ void __exit msm_edp_v200_unregister(void);
int msm_edp_v200_modeset_init(struct msm_edp *edp, struct drm_device *dev,
struct drm_encoder *encoder);
+void __init msm_edp_v510_register(void);
+void __exit msm_edp_v510_unregister(void);
+int msm_edp_v510_modeset_init(struct msm_edp *edp, struct drm_device *dev,
+ struct drm_encoder *encoder);
+
#endif /* __EDP_COMMON_CONNECTOR_H__ */
new file mode 100644
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/of_irq.h>
+#include "edp_v510.h"
+
+static irqreturn_t edp_irq(int irq, void *dev_id)
+{
+ struct msm_edp_v510 *edp = dev_id;
+
+ /* Process eDP irq */
+ return msm_edp_v510_ctrl_irq(edp->ctrl);
+}
+
+static void edp_destroy(struct platform_device *pdev)
+{
+ struct msm_edp_v510 *edp = platform_get_drvdata(pdev);
+
+ if (!edp)
+ return;
+
+ if (edp->ctrl) {
+ msm_edp_v510_ctrl_destroy(edp->ctrl);
+ edp->ctrl = NULL;
+ }
+
+ platform_set_drvdata(pdev, NULL);
+}
+
+/* construct eDP at bind/probe time, grab all the resources. */
+static struct msm_edp *edp_init(struct platform_device *pdev)
+{
+ struct msm_edp_v510 *edp = NULL;
+ int ret;
+
+ if (!pdev) {
+ DRM_ERROR("no eDP device\n");
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ edp = devm_kzalloc(&pdev->dev, sizeof(*edp), GFP_KERNEL);
+ if (!edp) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+ DRM_INFO("eDP v510 probed\n");
+ edp->base.version = MSM_EDP_VERSION_510;
+
+ edp->base.pdev = pdev;
+ platform_set_drvdata(pdev, edp);
+
+ ret = msm_edp_v510_ctrl_init(edp);
+ if (ret)
+ goto fail;
+
+ return &edp->base;
+
+fail:
+ if (edp)
+ edp_destroy(pdev);
+
+ return ERR_PTR(ret);
+}
+
+static int edp_bind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm = dev_get_drvdata(master);
+ struct msm_drm_private *priv = drm->dev_private;
+ struct msm_edp *edp;
+
+ edp = edp_init(to_platform_device(dev));
+ if (IS_ERR(edp))
+ return PTR_ERR(edp);
+ priv->edp = edp;
+
+ return 0;
+}
+
+static void edp_unbind(struct device *dev, struct device *master, void *data)
+{
+ struct drm_device *drm = dev_get_drvdata(master);
+ struct msm_drm_private *priv = drm->dev_private;
+
+ if (priv->edp) {
+ edp_destroy(to_platform_device(dev));
+ priv->edp = NULL;
+ }
+}
+
+static const struct component_ops edp_ops = {
+ .bind = edp_bind,
+ .unbind = edp_unbind,
+};
+
+static int edp_dev_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &edp_ops);
+}
+
+static int edp_dev_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &edp_ops);
+ return 0;
+}
+
+static const struct of_device_id dt_match[] = {
+ { .compatible = "qcom,mdss-edp-v510" },
+ {}
+};
+
+static struct platform_driver edp_driver = {
+ .probe = edp_dev_probe,
+ .remove = edp_dev_remove,
+ .driver = {
+ .name = "msm_edp_v510",
+ .of_match_table = dt_match,
+ },
+};
+
+void __init msm_edp_v510_register(void)
+{
+ platform_driver_register(&edp_driver);
+}
+
+void __exit msm_edp_v510_unregister(void)
+{
+ platform_driver_unregister(&edp_driver);
+}
+
+static void edp_display_set_encoder_mode(struct msm_edp_v510 *edp)
+{
+ struct msm_drm_private *priv = edp->base.dev->dev_private;
+ struct msm_kms *kms = priv->kms;
+
+ if (!edp->encoder_mode_set && edp->base.encoder &&
+ kms->funcs->set_encoder_mode) {
+ kms->funcs->set_encoder_mode(kms,
+ edp->base.encoder, true);
+
+ edp->encoder_mode_set = true;
+ }
+}
+
+/* Second part of initialization, the drm/kms level modeset_init */
+int msm_edp_v510_modeset_init(struct msm_edp *edp, struct drm_device *dev,
+ struct drm_encoder *encoder)
+{
+ struct platform_device *pdev = edp->pdev;
+ struct msm_drm_private *priv = dev->dev_private;
+ struct msm_edp_v510 *edp_ptr = NULL;
+ int ret;
+
+ if (WARN_ON(!encoder) || WARN_ON(!edp) || WARN_ON(!dev))
+ return -EINVAL;
+
+ edp->encoder = encoder;
+ edp->dev = dev;
+
+ edp_ptr = container_of(edp, struct msm_edp_v510, base);
+ if (IS_ERR(edp_ptr)) {
+ ret = PTR_ERR(edp_ptr);
+ DRM_DEV_ERROR(dev->dev, "failed to retrieve edp_v510 ptr: %d\n", ret);
+ goto fail;
+ }
+
+ edp_ptr->base.bridge = msm_edp_v510_bridge_init(edp_ptr);
+ if (IS_ERR(edp_ptr->base.bridge)) {
+ ret = PTR_ERR(edp_ptr->base.bridge);
+ DRM_DEV_ERROR(dev->dev, "failed to create eDP bridge: %d\n", ret);
+ edp_ptr->base.bridge = NULL;
+ goto fail;
+ }
+
+ edp_ptr->base.connector = msm_edp_v510_connector_init(edp_ptr);
+ if (IS_ERR(edp_ptr->base.connector)) {
+ ret = PTR_ERR(edp_ptr->base.connector);
+ DRM_DEV_ERROR(dev->dev, "failed to create eDP connector: %d\n", ret);
+ edp_ptr->base.connector = NULL;
+ goto fail;
+ }
+
+ edp_ptr->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+ if (edp_ptr->irq < 0) {
+ ret = edp_ptr->irq;
+ DRM_DEV_ERROR(dev->dev, "failed to get IRQ: %d\n", ret);
+ goto fail;
+ }
+
+ ret = devm_request_irq(&pdev->dev, edp_ptr->irq,
+ edp_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "edp_isr", edp_ptr);
+ if (ret < 0) {
+ DRM_DEV_ERROR(dev->dev, "failed to request IRQ%u: %d\n",
+ edp_ptr->irq, ret);
+ goto fail;
+ }
+
+ edp_display_set_encoder_mode(edp_ptr);
+
+ priv->bridges[priv->num_bridges++] = edp->bridge;
+ priv->connectors[priv->num_connectors++] = edp->connector;
+
+ return 0;
+
+fail:
+ /* bridge/connector are normally destroyed by drm */
+ if (edp->bridge) {
+ edp_v510_bridge_destroy(edp->bridge);
+ edp->bridge = NULL;
+ }
+ if (edp->connector) {
+ edp->connector->funcs->destroy(edp->connector);
+ edp->connector = NULL;
+ }
+
+ return ret;
+}
new file mode 100644
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __EDP_V510_CONNECTOR_H__
+#define __EDP_V510_CONNECTOR_H__
+
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_dp_helper.h>
+
+#include "msm_drv.h"
+#include "msm_kms.h"
+#include "dpu_io_util.h"
+#include "../edp_common.h"
+
+#define MSM_EDP_CONTROLLER_AHB_OFFSET 0x0000
+#define MSM_EDP_CONTROLLER_AHB_SIZE 0x0200
+#define MSM_EDP_CONTROLLER_AUX_OFFSET 0x0200
+#define MSM_EDP_CONTROLLER_AUX_SIZE 0x0200
+#define MSM_EDP_CONTROLLER_LINK_OFFSET 0x0400
+#define MSM_EDP_CONTROLLER_LINK_SIZE 0x0C00
+#define MSM_EDP_CONTROLLER_P0_OFFSET 0x1000
+#define MSM_EDP_CONTROLLER_P0_SIZE 0x0400
+
+static inline u32 edp_read_aux(void *base, u32 offset)
+{
+ offset += MSM_EDP_CONTROLLER_AUX_OFFSET;
+ return readl_relaxed(base + offset);
+}
+
+static inline void edp_write_aux(void *base, u32 offset, u32 data)
+{
+ offset += MSM_EDP_CONTROLLER_AUX_OFFSET;
+ /*
+ * To make sure aux reg writes happens before any other operation,
+ * this function uses writel() instread of writel_relaxed()
+ */
+ writel(data, base + offset);
+}
+
+static inline u32 edp_read_ahb(void *base, u32 offset)
+{
+ offset += MSM_EDP_CONTROLLER_AHB_OFFSET;
+ return readl_relaxed(base + offset);
+}
+
+static inline void edp_write_ahb(void *base, u32 offset, u32 data)
+{
+ offset += MSM_EDP_CONTROLLER_AHB_OFFSET;
+ /*
+ * To make sure ahb reg writes happens before any other operation,
+ * this function uses writel() instread of writel_relaxed()
+ */
+ writel(data, base + offset);
+}
+
+static inline void edp_write_p0(void *base, u32 offset, u32 data)
+{
+ offset += MSM_EDP_CONTROLLER_P0_OFFSET;
+ /*
+ * To make sure pclk reg writes happens before any other operation,
+ * this function uses writel() instread of writel_relaxed()
+ */
+ writel(data, base + offset);
+}
+
+static inline u32 edp_read_p0(void *base, u32 offset)
+{
+ offset += MSM_EDP_CONTROLLER_P0_OFFSET;
+ return readl_relaxed(base + offset);
+}
+
+static inline u32 edp_read_link(void *base, u32 offset)
+{
+ offset += MSM_EDP_CONTROLLER_LINK_OFFSET;
+ return readl_relaxed(base + offset);
+}
+
+static inline void edp_write_link(void *base, u32 offset, u32 data)
+{
+ offset += MSM_EDP_CONTROLLER_LINK_OFFSET;
+ /*
+ * To make sure link reg writes happens before any other operation,
+ * this function uses writel() instread of writel_relaxed()
+ */
+ writel(data, base + offset);
+}
+
+struct edp_ctrl;
+struct edp_aux;
+struct edp_phy;
+struct edp_phy_opts {
+ unsigned long link_rate;
+ int lanes;
+ int voltage[4];
+ int pre[4];
+};
+
+
+struct msm_edp_v510 {
+ struct msm_edp base;
+ struct edp_ctrl *ctrl;
+
+ int irq;
+ bool encoder_mode_set;
+};
+
+/* eDP bridge */
+struct drm_bridge *msm_edp_v510_bridge_init(struct msm_edp_v510 *edp);
+void edp_v510_bridge_destroy(struct drm_bridge *bridge);
+
+/* eDP connector */
+struct drm_connector *msm_edp_v510_connector_init(struct msm_edp_v510 *edp);
+
+/* AUX */
+void *msm_edp_v510_aux_init(struct device *dev, void __iomem *regbase,
+ struct drm_dp_aux **drm_aux);
+void msm_edp_v510_aux_destroy(struct device *dev, struct edp_aux *aux);
+irqreturn_t msm_edp_v510_aux_irq(struct edp_aux *aux, u32 isr);
+void msm_edp_v510_aux_ctrl(struct edp_aux *aux, int enable);
+
+/* Phy */
+int msm_edp_v510_phy_enable(struct edp_phy *edp_phy);
+void msm_edp_v510_phy_vm_pe_init(struct edp_phy *edp_phy,
+ struct edp_phy_opts *opts);
+void *msm_edp_v510_phy_init(struct device *dev, void __iomem *regbase,
+ struct edp_phy_opts *opts);
+int msm_edp_v510_phy_power_on(struct edp_phy *edp_phy);
+void msm_edp_v510_phy_config(struct edp_phy *edp_phy, u8 v_level, u8 p_level);
+
+/* Ctrl */
+irqreturn_t msm_edp_v510_ctrl_irq(struct edp_ctrl *ctrl);
+void msm_edp_v510_ctrl_power(struct edp_ctrl *ctrl, bool on);
+int msm_edp_v510_ctrl_init(struct msm_edp_v510 *edp);
+void msm_edp_v510_ctrl_destroy(struct edp_ctrl *ctrl);
+bool msm_edp_v510_ctrl_panel_connected(struct edp_ctrl *ctrl);
+int msm_edp_v510_ctrl_get_panel_info(struct edp_ctrl *ctrl,
+ struct drm_connector *connector, struct edid **edid);
+int msm_edp_v510_ctrl_mode_set(struct edp_ctrl *ctrl,
+ const struct drm_display_mode *mode,
+ const struct drm_display_info *info);
+/* @pixel_rate is in kHz */
+bool msm_edp_v510_ctrl_pixel_clock_valid(struct edp_ctrl *ctrl, u32 pixel_rate);
+
+#endif /* __EDP_V510_CONNECTOR_H__ */
new file mode 100644
@@ -0,0 +1,268 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include "edp_v510.h"
+#include "edp_v510_reg.h"
+
+#define AUX_CMD_FIFO_LEN 144
+#define AUX_CMD_NATIVE_MAX 16
+#define AUX_CMD_I2C_MAX 128
+
+#define EDP_INTR_AUX_I2C_ERR \
+ (EDP_INTR_WRONG_ADDR | EDP_INTR_TIMEOUT | \
+ EDP_INTR_NACK_DEFER | EDP_INTR_WRONG_DATA_CNT | \
+ EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER)
+
+#define EDP_INTR_TRANS_STATUS \
+ (EDP_INTR_AUX_I2C_DONE | EDP_INTR_AUX_I2C_ERR)
+
+struct edp_aux {
+ void __iomem *base;
+ bool msg_err;
+
+ struct completion msg_comp;
+
+ /* To prevent the message transaction routine from reentry. */
+ struct mutex msg_mutex;
+
+ struct drm_dp_aux drm_aux;
+};
+#define to_edp_aux(x) container_of(x, struct edp_aux, drm_aux)
+
+static int edp_msg_fifo_tx(struct edp_aux *aux, struct drm_dp_aux_msg *msg)
+{
+ u32 data[4];
+ u32 reg, len;
+ bool native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
+ bool read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+ u8 *msgdata = msg->buffer;
+ int i;
+
+ if (read)
+ len = 4;
+ else
+ len = msg->size + 4;
+
+ /*
+ * cmd fifo only has depth of 144 bytes
+ */
+ if (len > AUX_CMD_FIFO_LEN)
+ return -EINVAL;
+
+ /* Pack cmd and write to HW */
+ data[0] = (msg->address >> 16) & 0xf; /* addr[19:16] */
+ if (read)
+ data[0] |= BIT(4); /* R/W */
+
+ data[1] = (msg->address >> 8) & 0xff; /* addr[15:8] */
+ data[2] = msg->address & 0xff; /* addr[7:0] */
+ data[3] = (msg->size - 1) & 0xff; /* len[7:0] */
+
+ for (i = 0; i < len; i++) {
+ reg = (i < 4) ? data[i] : msgdata[i - 4];
+ reg = EDP_AUX_DATA_DATA(reg); /* index = 0, write */
+ if (i == 0)
+ reg |= EDP_AUX_DATA_INDEX_WRITE;
+ edp_write_aux(aux->base, REG_EDP_AUX_DATA, reg);
+ }
+
+ reg = 0; /* Transaction number is always 1 */
+ if (!native) /* i2c */
+ reg |= EDP_AUX_TRANS_CTRL_I2C;
+
+ reg |= EDP_AUX_TRANS_CTRL_GO;
+ edp_write_aux(aux->base, REG_EDP_AUX_TRANS_CTRL, reg);
+
+ return 0;
+}
+
+static int edp_msg_fifo_rx(struct edp_aux *aux, struct drm_dp_aux_msg *msg)
+{
+ u32 data;
+ u8 *dp;
+ int i;
+ u32 len = msg->size;
+
+ edp_write_aux(aux->base, REG_EDP_AUX_DATA,
+ EDP_AUX_DATA_INDEX_WRITE | EDP_AUX_DATA_READ); /* index = 0 */
+
+ dp = msg->buffer;
+
+ /* discard first byte */
+ data = edp_read_aux(aux->base, REG_EDP_AUX_DATA);
+ for (i = 0; i < len; i++) {
+ data = edp_read_aux(aux->base, REG_EDP_AUX_DATA);
+ dp[i] = (u8)((data >> 8) & 0xff);
+ }
+
+ return 0;
+}
+
+/*
+ * This function does the real job to process an AUX transaction.
+ * It will call msm_edp_aux_ctrl() function to reset the AUX channel,
+ * if the waiting is timeout.
+ * The caller who triggers the transaction should avoid the
+ * msm_edp_aux_ctrl() running concurrently in other threads, i.e.
+ * start transaction only when AUX channel is fully enabled.
+ */
+static ssize_t edp_aux_transfer(struct drm_dp_aux *drm_aux,
+ struct drm_dp_aux_msg *msg)
+{
+ struct edp_aux *aux = to_edp_aux(drm_aux);
+ ssize_t ret;
+ unsigned long time_left;
+ bool native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
+ bool read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
+
+ /* Ignore address only message */
+ if ((msg->size == 0) || (msg->buffer == NULL)) {
+ msg->reply = native ?
+ DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
+ return msg->size;
+ }
+
+ /* msg sanity check */
+ if ((native && (msg->size > AUX_CMD_NATIVE_MAX)) ||
+ (msg->size > AUX_CMD_I2C_MAX)) {
+ pr_err("%s: invalid msg: size(%zu), request(%x)\n",
+ __func__, msg->size, msg->request);
+ return -EINVAL;
+ }
+
+ mutex_lock(&aux->msg_mutex);
+
+ aux->msg_err = false;
+ reinit_completion(&aux->msg_comp);
+
+ ret = edp_msg_fifo_tx(aux, msg);
+ if (ret < 0)
+ goto unlock_exit;
+
+ DRM_DEBUG_DP("wait_for_completion");
+ time_left = wait_for_completion_timeout(&aux->msg_comp,
+ msecs_to_jiffies(300));
+ if (!time_left) {
+ /*
+ * Clear GO and reset AUX channel
+ * to cancel the current transaction.
+ */
+ edp_write_aux(aux->base, REG_EDP_AUX_TRANS_CTRL, 0);
+ msm_edp_v510_aux_ctrl(aux, 1);
+ pr_err("%s: aux timeout,\n", __func__);
+ ret = -ETIMEDOUT;
+ goto unlock_exit;
+ }
+ DRM_DEBUG_DP("completion");
+
+ if (!aux->msg_err) {
+ if (read) {
+ ret = edp_msg_fifo_rx(aux, msg);
+ if (ret < 0)
+ goto unlock_exit;
+ }
+
+ msg->reply = native ?
+ DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
+ } else {
+ /* Reply defer to retry */
+ msg->reply = native ?
+ DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
+ /*
+ * The sleep time in caller is not long enough to make sure
+ * our H/W completes transactions. Add more defer time here.
+ */
+ msleep(100);
+ }
+
+ /* Return requested size for success or retry */
+ ret = msg->size;
+
+unlock_exit:
+ mutex_unlock(&aux->msg_mutex);
+ return ret;
+}
+
+void *msm_edp_v510_aux_init(struct device *dev, void __iomem *regbase,
+ struct drm_dp_aux **drm_aux)
+{
+ struct edp_aux *aux = NULL;
+ int ret;
+
+ aux = devm_kzalloc(dev, sizeof(*aux), GFP_KERNEL);
+ if (!aux)
+ return NULL;
+
+ aux->base = regbase;
+ mutex_init(&aux->msg_mutex);
+ init_completion(&aux->msg_comp);
+
+ aux->drm_aux.name = "msm_edp_aux";
+ aux->drm_aux.dev = dev;
+ aux->drm_aux.transfer = edp_aux_transfer;
+ ret = drm_dp_aux_register(&aux->drm_aux);
+ if (ret) {
+ pr_err("%s: failed to register drm aux: %d\n", __func__, ret);
+ mutex_destroy(&aux->msg_mutex);
+ }
+
+ if (drm_aux && aux)
+ *drm_aux = &aux->drm_aux;
+
+ return aux;
+}
+
+void msm_edp_v510_aux_destroy(struct device *dev, struct edp_aux *aux)
+{
+ if (aux) {
+ drm_dp_aux_unregister(&aux->drm_aux);
+ mutex_destroy(&aux->msg_mutex);
+ }
+}
+
+irqreturn_t msm_edp_v510_aux_irq(struct edp_aux *aux, u32 isr)
+{
+ if (isr & EDP_INTR_TRANS_STATUS) {
+ DRM_DEBUG_DP("isr=%x", isr);
+ edp_write_aux(aux->base, REG_EDP_AUX_TRANS_CTRL, 0);
+
+ if (isr & EDP_INTR_AUX_I2C_ERR)
+ aux->msg_err = true;
+ else
+ aux->msg_err = false;
+
+ complete(&aux->msg_comp);
+ }
+
+ return IRQ_HANDLED;
+}
+
+void msm_edp_v510_aux_ctrl(struct edp_aux *aux, int enable)
+{
+ u32 data;
+
+ DRM_INFO("enable=%d", enable);
+ data = edp_read_aux(aux->base, REG_EDP_AUX_CTRL);
+
+ if (enable) {
+ data |= EDP_AUX_CTRL_RESET;
+ edp_write_aux(aux->base, REG_EDP_AUX_CTRL, data);
+ /* Make sure full reset */
+ wmb();
+ usleep_range(500, 1000);
+
+ data &= ~EDP_AUX_CTRL_RESET;
+ edp_write_aux(aux->base, REG_EDP_AUX_CTRL, data);
+
+ edp_write_aux(aux->base, REG_EDP_TIMEOUT_COUNT, 0xffff);
+ edp_write_aux(aux->base, REG_EDP_AUX_LIMITS, 0xffff);
+
+ data |= EDP_AUX_CTRL_ENABLE;
+ edp_write_aux(aux->base, REG_EDP_AUX_CTRL, data);
+ } else {
+ data &= ~EDP_AUX_CTRL_ENABLE;
+ edp_write_aux(aux->base, REG_EDP_AUX_CTRL, data);
+ }
+}
new file mode 100644
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include "edp_v510.h"
+
+struct edp_bridge {
+ struct drm_bridge base;
+ struct msm_edp_v510 *edp;
+};
+#define to_edp_bridge(x) container_of(x, struct edp_bridge, base)
+
+void edp_v510_bridge_destroy(struct drm_bridge *bridge)
+{
+}
+
+static void edp_bridge_pre_enable(struct drm_bridge *bridge)
+{
+ struct edp_bridge *edp_bridge = to_edp_bridge(bridge);
+ struct msm_edp_v510 *edp = edp_bridge->edp;
+
+ DBG("");
+ msm_edp_v510_ctrl_power(edp->ctrl, true);
+}
+
+static void edp_bridge_enable(struct drm_bridge *bridge)
+{
+ DBG("");
+}
+
+static void edp_bridge_disable(struct drm_bridge *bridge)
+{
+ DBG("");
+}
+
+static void edp_bridge_post_disable(struct drm_bridge *bridge)
+{
+ struct edp_bridge *edp_bridge = to_edp_bridge(bridge);
+ struct msm_edp_v510 *edp = edp_bridge->edp;
+
+ DBG("");
+ msm_edp_v510_ctrl_power(edp->ctrl, false);
+}
+
+static void edp_bridge_mode_set(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ const struct drm_display_mode *adjusted_mode)
+{
+ struct drm_device *dev = bridge->dev;
+ struct drm_connector *connector;
+ struct edp_bridge *edp_bridge = to_edp_bridge(bridge);
+ struct msm_edp_v510 *edp = edp_bridge->edp;
+
+ DRM_INFO("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode));
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ struct drm_encoder *encoder = connector->encoder;
+ struct drm_bridge *first_bridge;
+
+ if (!connector->encoder)
+ continue;
+
+ first_bridge = drm_bridge_chain_get_first_bridge(encoder);
+ if (bridge == first_bridge) {
+ msm_edp_v510_ctrl_mode_set(edp->ctrl,
+ adjusted_mode, &connector->display_info);
+ break;
+ }
+ }
+}
+
+static const struct drm_bridge_funcs edp_bridge_funcs = {
+ .pre_enable = edp_bridge_pre_enable,
+ .enable = edp_bridge_enable,
+ .disable = edp_bridge_disable,
+ .post_disable = edp_bridge_post_disable,
+ .mode_set = edp_bridge_mode_set,
+};
+
+/* initialize bridge */
+struct drm_bridge *msm_edp_v510_bridge_init(struct msm_edp_v510 *edp)
+{
+ struct drm_bridge *bridge = NULL;
+ struct edp_bridge *edp_bridge;
+ int ret;
+
+ edp_bridge = devm_kzalloc(edp->base.dev->dev,
+ sizeof(*edp_bridge), GFP_KERNEL);
+ if (!edp_bridge) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ edp_bridge->edp = edp;
+
+ bridge = &edp_bridge->base;
+ bridge->funcs = &edp_bridge_funcs;
+
+ ret = drm_bridge_attach(edp->base.encoder, bridge, NULL, 0);
+ if (ret)
+ goto fail;
+
+ return bridge;
+
+fail:
+ if (bridge)
+ edp_v510_bridge_destroy(bridge);
+
+ return ERR_PTR(ret);
+}
new file mode 100644
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include "drm/drm_edid.h"
+#include "msm_kms.h"
+#include "edp_v510.h"
+
+struct edp_connector {
+ struct drm_connector base;
+ struct msm_edp_v510 *edp;
+};
+#define to_edp_connector(x) container_of(x, struct edp_connector, base)
+
+static enum drm_connector_status edp_connector_detect(
+ struct drm_connector *connector, bool force)
+{
+ struct edp_connector *edp_connector = to_edp_connector(connector);
+ struct msm_edp_v510 *edp = edp_connector->edp;
+
+ DBG("");
+ return msm_edp_v510_ctrl_panel_connected(edp->ctrl) ?
+ connector_status_connected : connector_status_disconnected;
+}
+
+static void edp_connector_destroy(struct drm_connector *connector)
+{
+ struct edp_connector *edp_connector = to_edp_connector(connector);
+
+ DBG("");
+
+ drm_connector_cleanup(connector);
+
+ kfree(edp_connector);
+}
+
+static int edp_connector_get_modes(struct drm_connector *connector)
+{
+ struct edp_connector *edp_connector = to_edp_connector(connector);
+ struct msm_edp_v510 *edp = edp_connector->edp;
+
+ struct edid *drm_edid = NULL;
+ int ret = 0;
+
+ DBG("");
+ ret = msm_edp_v510_ctrl_get_panel_info(edp->ctrl, connector, &drm_edid);
+ if (ret)
+ return ret;
+
+ drm_connector_update_edid_property(connector, drm_edid);
+ if (drm_edid)
+ ret = drm_add_edid_modes(connector, drm_edid);
+
+ return ret;
+}
+
+static int edp_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct edp_connector *edp_connector = to_edp_connector(connector);
+ struct msm_edp_v510 *edp = edp_connector->edp;
+
+ if (!msm_edp_v510_ctrl_pixel_clock_valid(edp->ctrl, mode->clock))
+ return MODE_CLOCK_RANGE;
+
+ return MODE_OK;
+}
+
+static const struct drm_connector_funcs edp_connector_funcs = {
+ .detect = edp_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = edp_connector_destroy,
+ .reset = drm_atomic_helper_connector_reset,
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+};
+
+static const struct drm_connector_helper_funcs edp_connector_helper_funcs = {
+ .get_modes = edp_connector_get_modes,
+ .mode_valid = edp_connector_mode_valid,
+};
+
+/* initialize connector */
+struct drm_connector *msm_edp_v510_connector_init(struct msm_edp_v510 *edp)
+{
+ struct drm_connector *connector = NULL;
+ struct edp_connector *edp_connector;
+ int ret;
+
+ edp_connector = devm_kzalloc(edp->base.dev->dev, sizeof(*edp_connector),
+ GFP_KERNEL);
+ if (!edp_connector)
+ return ERR_PTR(-ENOMEM);
+
+ edp_connector->edp = edp;
+
+ connector = &edp_connector->base;
+
+ ret = drm_connector_init(edp->base.dev, connector, &edp_connector_funcs,
+ DRM_MODE_CONNECTOR_eDP);
+ if (ret)
+ return ERR_PTR(ret);
+
+ drm_connector_helper_add(connector, &edp_connector_helper_funcs);
+
+ /* We don't support HPD, so only poll status until connected. */
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT;
+
+ /* Display driver doesn't support interlace now. */
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+
+ drm_connector_attach_encoder(connector, edp->base.encoder);
+
+ return connector;
+}
new file mode 100644
@@ -0,0 +1,1583 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/rational.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_dp_helper.h>
+#include <drm/drm_edid.h>
+#include <linux/delay.h>
+
+#include "edp_v510.h"
+#include "edp_v510_reg.h"
+
+#define VDDA_UA_ON_LOAD 21800 /* uA units */
+#define VDDA_UA_OFF_LOAD 4 /* uA units */
+#define LVL_UA_ON_LOAD 36000 /* uA units */
+#define LVL_UA_OFF_LOAD 32 /* uA units */
+
+#define DPCD_LINK_VOLTAGE_MAX 4
+#define DPCD_LINK_PRE_EMPHASIS_MAX 4
+
+#define EDP_LINK_BW_MAX DP_LINK_BW_5_4
+
+/* Link training return value */
+#define EDP_TRAIN_FAIL -1
+#define EDP_TRAIN_SUCCESS 0
+#define EDP_TRAIN_RECONFIG 1
+
+#define EDP_CLK_MASK_AHB BIT(0)
+#define EDP_CLK_MASK_AUX BIT(1)
+#define EDP_CLK_MASK_LINK BIT(2)
+#define EDP_CLK_MASK_PIXEL BIT(3)
+#define EDP_CLK_MASK_MDP_CORE BIT(4)
+#define EDP_CLK_MASK_LINK_CHAN (EDP_CLK_MASK_LINK | EDP_CLK_MASK_PIXEL)
+#define EDP_CLK_MASK_AUX_CHAN \
+ (EDP_CLK_MASK_AHB | EDP_CLK_MASK_AUX | EDP_CLK_MASK_MDP_CORE)
+#define EDP_CLK_MASK_ALL (EDP_CLK_MASK_AUX_CHAN | EDP_CLK_MASK_LINK_CHAN)
+
+#define EDP_BACKLIGHT_MAX 255
+
+#define EDP_INTERRUPT_STATUS_ACK_SHIFT 1
+#define EDP_INTERRUPT_STATUS_MASK_SHIFT 2
+
+#define EDP_INTERRUPT_STATUS1 \
+ (EDP_INTR_AUX_I2C_DONE| \
+ EDP_INTR_WRONG_ADDR | EDP_INTR_TIMEOUT | \
+ EDP_INTR_NACK_DEFER | EDP_INTR_WRONG_DATA_CNT | \
+ EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER | \
+ EDP_INTR_PLL_UNLOCKED | EDP_INTR_AUX_ERROR)
+
+#define EDP_INTERRUPT_STATUS1_ACK \
+ (EDP_INTERRUPT_STATUS1 << EDP_INTERRUPT_STATUS_ACK_SHIFT)
+#define EDP_INTERRUPT_STATUS1_MASK \
+ (EDP_INTERRUPT_STATUS1 << EDP_INTERRUPT_STATUS_MASK_SHIFT)
+
+#define EDP_INTERRUPT_STATUS2 \
+ (EDP_INTR_READY_FOR_VIDEO | EDP_INTR_IDLE_PATTERN_SENT | \
+ EDP_INTR_FRAME_END | EDP_INTR_CRC_UPDATED | EDP_INTR_SST_FIFO_UNDERFLOW)
+
+#define EDP_INTERRUPT_STATUS2_ACK \
+ (EDP_INTERRUPT_STATUS2 << EDP_INTERRUPT_STATUS_ACK_SHIFT)
+#define EDP_INTERRUPT_STATUS2_MASK \
+ (EDP_INTERRUPT_STATUS2 << EDP_INTERRUPT_STATUS_MASK_SHIFT)
+
+enum edp_pm_type {
+ EDP_CORE_PM,
+ EDP_CTRL_PM,
+ EDP_STREAM_PM,
+ EDP_PHY_PM,
+ EDP_MAX_PM
+};
+
+struct edp_ctrl {
+ struct platform_device *pdev;
+
+ void __iomem *base;
+ void __iomem *phy_base;
+
+ /* regulators */
+ struct regulator *vdda_vreg; /* 1.8 V */
+ struct regulator *lvl_vreg;
+
+ /* clocks */
+ struct dss_module_power mp[EDP_MAX_PM];
+ bool core_clks_on;
+ bool link_clks_on;
+ bool stream_clks_on;
+
+ /* gpios */
+ struct gpio_desc *panel_en_gpio;
+ struct gpio_desc *panel_hpd_gpio;
+ struct gpio_desc *panel_bklt1_gpio;
+ struct gpio_desc *panel_bklt2_gpio;
+ struct gpio_desc *panel_pwm_gpio;
+
+ /* completion and mutex */
+ struct completion idle_comp;
+ struct mutex dev_mutex; /* To protect device power status */
+
+ /* work queue */
+ struct work_struct on_work;
+ struct work_struct off_work;
+ struct workqueue_struct *workqueue;
+
+ /* Interrupt register lock */
+ spinlock_t irq_lock;
+
+ bool edp_connected;
+ bool power_on;
+ bool core_initialized;
+
+ /* edid raw data */
+ struct edid *edid;
+
+ struct drm_dp_aux *drm_aux;
+
+ /* dpcd raw data */
+ u8 dpcd[DP_RECEIVER_CAP_SIZE];
+
+ /* Link status */
+ u8 link_rate;
+ u8 lane_cnt;
+ u8 v_level;
+ u8 p_level;
+ struct edp_phy_opts edp_opts;
+
+ /* Timing status */
+ u8 interlaced;
+ u32 pixel_rate; /* in kHz */
+ u32 color_depth;
+ struct drm_display_mode drm_mode;
+
+ struct edp_aux *aux;
+ struct edp_phy *phy;
+};
+
+struct edp_ctrl_tu {
+ u32 rate;
+ u32 edp_tu;
+ u32 valid_boundary;
+ u32 valid_boundary2;
+};
+
+#define MAX_TU_TABLE 1
+static const struct edp_ctrl_tu tu[MAX_TU_TABLE] = {
+ {285550, 0x20, 0x13001B, 0x920035}, /* 1920x1080@120Hz CVT RB1 */
+};
+
+static inline bool edp_check_prefix(const char *clk_prefix,
+ const char *clk_name)
+{
+ return !strncmp(clk_prefix, clk_name, strlen(clk_prefix));
+}
+
+static int edp_init_clk_data(struct edp_ctrl *ctrl)
+{
+ int num_clk, i, rc;
+ int core_clk_count = 0, ctrl_clk_count = 0, stream_clk_count = 0;
+ const char *clk_name;
+ struct device *dev = &ctrl->pdev->dev;
+ struct dss_module_power *core_power = &ctrl->mp[EDP_CORE_PM];
+ struct dss_module_power *ctrl_power = &ctrl->mp[EDP_CTRL_PM];
+ struct dss_module_power *stream_power = &ctrl->mp[EDP_STREAM_PM];
+
+ num_clk = of_property_count_strings(dev->of_node, "clock-names");
+ if (num_clk <= 0) {
+ DRM_ERROR("no clocks are defined\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < num_clk; i++) {
+ rc = of_property_read_string_index(dev->of_node,
+ "clock-names", i, &clk_name);
+ if (rc < 0)
+ return rc;
+
+ if (edp_check_prefix("core", clk_name))
+ core_clk_count++;
+
+ if (edp_check_prefix("ctrl", clk_name))
+ ctrl_clk_count++;
+
+ if (edp_check_prefix("stream", clk_name))
+ stream_clk_count++;
+ }
+
+ /* Initialize the CORE power module */
+ if (core_clk_count == 0) {
+ DRM_ERROR("no core clocks are defined\n");
+ return -EINVAL;
+ }
+
+ core_power->num_clk = core_clk_count;
+ core_power->clk_config = devm_kzalloc(dev,
+ sizeof(struct dss_clk) * core_power->num_clk,
+ GFP_KERNEL);
+ if (!core_power->clk_config)
+ return -EINVAL;
+
+ /* Initialize the CTRL power module */
+ if (ctrl_clk_count == 0) {
+ DRM_ERROR("no ctrl clocks are defined\n");
+ return -EINVAL;
+ }
+
+ ctrl_power->num_clk = ctrl_clk_count;
+ ctrl_power->clk_config = devm_kzalloc(dev,
+ sizeof(struct dss_clk) * ctrl_power->num_clk,
+ GFP_KERNEL);
+ if (!ctrl_power->clk_config) {
+ ctrl_power->num_clk = 0;
+ return -EINVAL;
+ }
+
+ /* Initialize the STREAM power module */
+ if (stream_clk_count == 0) {
+ DRM_ERROR("no stream (pixel) clocks are defined\n");
+ return -EINVAL;
+ }
+
+ stream_power->num_clk = stream_clk_count;
+ stream_power->clk_config = devm_kzalloc(dev,
+ sizeof(struct dss_clk) * stream_power->num_clk,
+ GFP_KERNEL);
+ if (!stream_power->clk_config) {
+ stream_power->num_clk = 0;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int edp_clk_init(struct edp_ctrl *ctrl)
+{
+ int rc = 0, i = 0;
+ int num_clk = 0;
+ int core_clk_index = 0, ctrl_clk_index = 0, stream_clk_index = 0;
+ int core_clk_count = 0, ctrl_clk_count = 0, stream_clk_count = 0;
+ const char *clk_name;
+ struct device *dev = &ctrl->pdev->dev;
+ struct dss_module_power *core_power = &ctrl->mp[EDP_CORE_PM];
+ struct dss_module_power *ctrl_power = &ctrl->mp[EDP_CTRL_PM];
+ struct dss_module_power *stream_power = &ctrl->mp[EDP_STREAM_PM];
+
+ rc = edp_init_clk_data(ctrl);
+ if (rc) {
+ DRM_ERROR("failed to initialize power data %d\n", rc);
+ return -EINVAL;
+ }
+
+ core_clk_count = core_power->num_clk;
+ ctrl_clk_count = ctrl_power->num_clk;
+ stream_clk_count = stream_power->num_clk;
+
+ num_clk = core_clk_count + ctrl_clk_count + stream_clk_count;
+
+ for (i = 0; i < num_clk; i++) {
+ rc = of_property_read_string_index(dev->of_node, "clock-names",
+ i, &clk_name);
+ if (rc) {
+ DRM_ERROR("error reading clock-names %d\n", rc);
+ return rc;
+ }
+ if (edp_check_prefix("core", clk_name) &&
+ core_clk_index < core_clk_count) {
+ struct dss_clk *clk =
+ &core_power->clk_config[core_clk_index];
+ strscpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
+ clk->type = DSS_CLK_AHB;
+ core_clk_index++;
+ } else if (edp_check_prefix("stream", clk_name) &&
+ stream_clk_index < stream_clk_count) {
+ struct dss_clk *clk =
+ &stream_power->clk_config[stream_clk_index];
+ strscpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
+ clk->type = DSS_CLK_PCLK;
+ stream_clk_index++;
+ } else if (edp_check_prefix("ctrl", clk_name) &&
+ ctrl_clk_index < ctrl_clk_count) {
+ struct dss_clk *clk =
+ &ctrl_power->clk_config[ctrl_clk_index];
+ strscpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
+ ctrl_clk_index++;
+ if (edp_check_prefix("ctrl_link", clk_name) ||
+ edp_check_prefix("stream_pixel", clk_name))
+ clk->type = DSS_CLK_PCLK;
+ else
+ clk->type = DSS_CLK_AHB;
+ }
+ }
+
+ DRM_DEBUG_DP("clock parsing successful\n");
+
+ rc = msm_dss_get_clk(dev, core_power->clk_config, core_power->num_clk);
+ if (rc) {
+ DRM_ERROR("failed to get core clk. err=%d\n", rc);
+ return rc;
+ }
+
+ rc = msm_dss_get_clk(dev, ctrl_power->clk_config, ctrl_power->num_clk);
+ if (rc) {
+ DRM_ERROR("failed to get ctrl clk. err=%d\n", rc);
+ msm_dss_put_clk(core_power->clk_config, core_power->num_clk);
+ return -ENODEV;
+ }
+
+ rc = msm_dss_get_clk(dev, stream_power->clk_config, stream_power->num_clk);
+ if (rc) {
+ DRM_ERROR("failed to get strem clk. err=%d\n", rc);
+ msm_dss_put_clk(core_power->clk_config, core_power->num_clk);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void edp_clk_deinit(struct edp_ctrl *ctrl)
+{
+ struct dss_module_power *core_power, *ctrl_power, *stream_power;
+
+ core_power = &ctrl->mp[EDP_CORE_PM];
+ ctrl_power = &ctrl->mp[EDP_CTRL_PM];
+ stream_power = &ctrl->mp[EDP_STREAM_PM];
+
+ if (!core_power || !ctrl_power || !stream_power)
+ DRM_ERROR("invalid power_data\n");
+
+ msm_dss_put_clk(ctrl_power->clk_config, ctrl_power->num_clk);
+ msm_dss_put_clk(core_power->clk_config, core_power->num_clk);
+ msm_dss_put_clk(stream_power->clk_config, stream_power->num_clk);
+}
+
+static int edp_clk_set_rate(struct edp_ctrl *ctrl,
+ enum edp_pm_type module, bool enable)
+{
+ int rc = 0;
+ struct dss_module_power *mp = &ctrl->mp[module];
+
+ if (enable) {
+ rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk);
+ if (rc) {
+ DRM_ERROR("failed to set clks rate.\n");
+ return rc;
+ }
+ }
+
+ rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable);
+ if (rc) {
+ DRM_ERROR("failed to %d clks, err: %d\n", enable, rc);
+ return rc;
+ }
+
+ return 0;
+}
+
+int edp_clk_enable(struct edp_ctrl *ctrl,
+ enum edp_pm_type pm_type, bool enable)
+{
+ int rc = 0;
+
+ if (pm_type != EDP_CORE_PM && pm_type != EDP_CTRL_PM &&
+ pm_type != EDP_STREAM_PM) {
+ DRM_ERROR("unsupported power module\n");
+ return -EINVAL;
+ }
+
+ if (enable) {
+ if (pm_type == EDP_CORE_PM && ctrl->core_clks_on) {
+ DRM_DEBUG_DP("core clks already enabled\n");
+ return 0;
+ }
+
+ if (pm_type == EDP_CTRL_PM && ctrl->link_clks_on) {
+ DRM_DEBUG_DP("links clks already enabled\n");
+ return 0;
+ }
+
+ if (pm_type == EDP_STREAM_PM && ctrl->stream_clks_on) {
+ DRM_DEBUG_DP("pixel clks already enabled\n");
+ return 0;
+ }
+
+ if ((pm_type == EDP_CTRL_PM) && (!ctrl->core_clks_on)) {
+ DRM_DEBUG_DP("Enable core clks before link clks\n");
+
+ rc = edp_clk_set_rate(ctrl, EDP_CORE_PM, enable);
+ if (rc) {
+ DRM_ERROR("fail to enable clks: core. err=%d\n",
+ rc);
+ return rc;
+ }
+ ctrl->core_clks_on = true;
+ }
+ }
+
+ rc = edp_clk_set_rate(ctrl, pm_type, enable);
+ if (rc) {
+ DRM_ERROR("failed to '%s' clks. err=%d\n",
+ enable ? "enable" : "disable", rc);
+ return rc;
+ }
+
+ if (pm_type == EDP_CORE_PM)
+ ctrl->core_clks_on = enable;
+ else if (pm_type == EDP_STREAM_PM)
+ ctrl->stream_clks_on = enable;
+ else
+ ctrl->link_clks_on = enable;
+
+ DRM_DEBUG_DP("stream_clks:%s link_clks:%s core_clks:%s\n",
+ ctrl->stream_clks_on ? "on" : "off",
+ ctrl->link_clks_on ? "on" : "off",
+ ctrl->core_clks_on ? "on" : "off");
+
+ return 0;
+}
+
+static void edp_ctrl_set_clock_rate(struct edp_ctrl *ctrl,
+ enum edp_pm_type module, char *name, unsigned long rate)
+{
+ u32 num = ctrl->mp[module].num_clk;
+ struct dss_clk *cfg = ctrl->mp[module].clk_config;
+
+ while (num && strcmp(cfg->clk_name, name)) {
+ num--;
+ cfg++;
+ }
+
+ DRM_DEBUG_DP("setting rate=%lu on clk=%s\n", rate, name);
+
+ if (num)
+ cfg->rate = rate;
+ else
+ DRM_ERROR("%s clock doesn't exit to set rate %lu\n",
+ name, rate);
+}
+
+static int edp_regulator_init(struct edp_ctrl *ctrl)
+{
+ struct device *dev = &ctrl->pdev->dev;
+ int ret;
+
+ ctrl->vdda_vreg = devm_regulator_get(dev, "vdda");
+ ret = PTR_ERR_OR_ZERO(ctrl->vdda_vreg);
+ if (ret) {
+ DRM_ERROR("%s: Could not get vdda reg, ret = %d\n", __func__,
+ ret);
+ ctrl->vdda_vreg = NULL;
+ return ret;
+ }
+ ctrl->lvl_vreg = devm_regulator_get(dev, "lvl-vdd");
+ ret = PTR_ERR_OR_ZERO(ctrl->lvl_vreg);
+ if (ret) {
+ DRM_ERROR("%s: Could not get lvl-vdd reg, ret = %d\n", __func__,
+ ret);
+ ctrl->lvl_vreg = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+
+static int edp_regulator_enable(struct edp_ctrl *ctrl)
+{
+ int ret;
+
+ ret = regulator_set_load(ctrl->vdda_vreg, VDDA_UA_ON_LOAD);
+ if (ret < 0) {
+ DRM_ERROR("%s: vdda_vreg set regulator mode failed.\n", __func__);
+ goto vdda_set_fail;
+ }
+
+ ret = regulator_enable(ctrl->vdda_vreg);
+ if (ret) {
+ DRM_ERROR("%s: Failed to enable vdda_vreg regulator.\n", __func__);
+ goto vdda_enable_fail;
+ }
+
+ ret = regulator_set_load(ctrl->lvl_vreg, LVL_UA_ON_LOAD);
+ if (ret < 0) {
+ DRM_ERROR("%s: vdda_vreg set regulator mode failed.\n", __func__);
+ goto vdda_set_fail;
+ }
+
+ ret = regulator_enable(ctrl->lvl_vreg);
+ if (ret) {
+ DRM_ERROR("Failed to enable lvl-vdd reg regulator, %d", ret);
+ goto lvl_enable_fail;
+ }
+
+ return 0;
+
+lvl_enable_fail:
+ regulator_disable(ctrl->vdda_vreg);
+vdda_enable_fail:
+ regulator_set_load(ctrl->vdda_vreg, VDDA_UA_OFF_LOAD);
+vdda_set_fail:
+ return ret;
+}
+
+static void edp_regulator_disable(struct edp_ctrl *ctrl)
+{
+ regulator_disable(ctrl->lvl_vreg);
+ regulator_set_load(ctrl->lvl_vreg, LVL_UA_OFF_LOAD);
+ regulator_disable(ctrl->vdda_vreg);
+ regulator_set_load(ctrl->vdda_vreg, VDDA_UA_OFF_LOAD);
+}
+
+static int edp_gpio_config(struct edp_ctrl *ctrl)
+{
+ struct device *dev = &ctrl->pdev->dev;
+ int ret;
+
+ ctrl->panel_hpd_gpio = devm_gpiod_get(dev, "panel-hpd", GPIOD_IN);
+ if (IS_ERR(ctrl->panel_hpd_gpio)) {
+ ret = PTR_ERR(ctrl->panel_hpd_gpio);
+ ctrl->panel_hpd_gpio = NULL;
+ DRM_ERROR("%s: cannot get panel-hpd-gpios, %d\n", __func__, ret);
+ return ret;
+ }
+
+ ctrl->panel_en_gpio = devm_gpiod_get(dev, "panel-en", GPIOD_OUT_HIGH);
+ if (IS_ERR(ctrl->panel_en_gpio)) {
+ ret = PTR_ERR(ctrl->panel_en_gpio);
+ ctrl->panel_en_gpio = NULL;
+ DRM_ERROR("%s: cannot get panel-en-gpios, %d\n", __func__, ret);
+ return ret;
+ }
+
+ ctrl->panel_bklt1_gpio = devm_gpiod_get(dev, "panel-bklt1",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ctrl->panel_bklt1_gpio)) {
+ ret = PTR_ERR(ctrl->panel_bklt1_gpio);
+ ctrl->panel_bklt1_gpio = NULL;
+ DRM_ERROR("%s: cannot get panel-bklt1-gpios, %d\n", __func__, ret);
+ return ret;
+ }
+
+ ctrl->panel_bklt2_gpio = devm_gpiod_get(dev, "panel-bklt2",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ctrl->panel_bklt2_gpio)) {
+ ret = PTR_ERR(ctrl->panel_bklt2_gpio);
+ ctrl->panel_bklt2_gpio = NULL;
+ DRM_ERROR("%s: cannot get panel-bklt2-gpios, %d\n", __func__, ret);
+ return ret;
+ }
+
+ ctrl->panel_pwm_gpio = devm_gpiod_get(dev, "panel-pwm", GPIOD_OUT_HIGH);
+ if (IS_ERR(ctrl->panel_pwm_gpio)) {
+ ret = PTR_ERR(ctrl->panel_pwm_gpio);
+ ctrl->panel_pwm_gpio = NULL;
+ DRM_ERROR("%s: cannot get panel-pwm-gpios, %d\n", __func__, ret);
+ return ret;
+ }
+
+ DRM_INFO("gpio on");
+
+ return 0;
+}
+
+static void edp_ctrl_irq_enable(struct edp_ctrl *ctrl, int enable)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctrl->irq_lock, flags);
+ if (enable) {
+ edp_write_ahb(ctrl->base, REG_EDP_INTR_STATUS,
+ EDP_INTERRUPT_STATUS1_MASK);
+ edp_write_ahb(ctrl->base, REG_EDP_INTR_STATUS2,
+ EDP_INTERRUPT_STATUS2_MASK);
+ } else {
+ edp_write_ahb(ctrl->base, REG_EDP_INTR_STATUS,
+ EDP_INTERRUPT_STATUS1_ACK);
+ edp_write_ahb(ctrl->base, REG_EDP_INTR_STATUS2,
+ EDP_INTERRUPT_STATUS2_ACK);
+ }
+ spin_unlock_irqrestore(&ctrl->irq_lock, flags);
+}
+
+static void edp_fill_link_cfg(struct edp_ctrl *ctrl)
+{
+ u32 prate;
+ u32 bpp;
+ u8 max_lane = drm_dp_max_lane_count(ctrl->dpcd);
+
+ prate = ctrl->pixel_rate;
+ bpp = ctrl->color_depth * 3;
+
+ /*
+ * By default, use the maximum link rate and minimum lane count,
+ * so that we can do rate down shift during link training.
+ */
+ ctrl->link_rate = ctrl->dpcd[DP_MAX_LINK_RATE];
+ ctrl->lane_cnt = max_lane;
+ DRM_INFO("rate=%d lane=%d", ctrl->link_rate, ctrl->lane_cnt);
+}
+
+static void edp_config_ctrl(struct edp_ctrl *ctrl)
+{
+ u32 config = 0, depth = 0;
+ u8 *dpcd = ctrl->dpcd;
+
+ /* Default-> LSCLK DIV: 1/4 LCLK */
+ config |= (2 << EDP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT);
+
+ /* Scrambler reset enable */
+ if (dpcd[DP_EDP_CONFIGURATION_CAP] & DP_ALTERNATE_SCRAMBLER_RESET_CAP)
+ config |= EDP_CONFIGURATION_CTRL_ASSR;
+
+ if (ctrl->color_depth == 8)
+ depth = EDP_8BIT;
+ else if (ctrl->color_depth == 10)
+ depth = EDP_10BIT;
+ else if (ctrl->color_depth == 12)
+ depth = EDP_12BIT;
+ else if (ctrl->color_depth == 16)
+ depth = EDP_16BIT;
+ config |= depth << EDP_CONFIGURATION_CTRL_BPC_SHIFT;
+
+ /* Num of Lanes */
+ config |= ((ctrl->lane_cnt - 1)
+ << EDP_CONFIGURATION_CTRL_NUM_OF_LANES_SHIFT);
+
+ if (drm_dp_enhanced_frame_cap(dpcd))
+ config |= EDP_CONFIGURATION_CTRL_ENHANCED_FRAMING;
+
+ config |= EDP_CONFIGURATION_CTRL_P_INTERLACED; /* progressive video */
+
+ /* sync clock & static Mvid */
+ config |= EDP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN;
+ config |= EDP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK;
+
+ edp_write_link(ctrl->base, REG_EDP_CONFIGURATION_CTRL, config);
+}
+
+static void edp_state_ctrl(struct edp_ctrl *ctrl, u32 state)
+{
+ edp_write_link(ctrl->base, REG_EDP_STATE_CTRL, state);
+ /* Make sure H/W status is set */
+ wmb();
+}
+
+static int edp_lane_set_write(struct edp_ctrl *ctrl,
+ u8 voltage_level, u8 pre_emphasis_level)
+{
+ int i;
+ u8 buf[4];
+
+ if (voltage_level >= DPCD_LINK_VOLTAGE_MAX)
+ voltage_level |= 0x04;
+
+ if (pre_emphasis_level >= DPCD_LINK_PRE_EMPHASIS_MAX)
+ pre_emphasis_level |= 0x04;
+
+ pre_emphasis_level <<= 3;
+
+ for (i = 0; i < 4; i++)
+ buf[i] = voltage_level | pre_emphasis_level;
+
+ DRM_INFO("%s: p|v=0x%x", __func__, voltage_level | pre_emphasis_level);
+ if (drm_dp_dpcd_write(ctrl->drm_aux, 0x103, buf, 4) < 4) {
+ DRM_ERROR("%s: Set sw/pe to panel failed\n", __func__);
+ return -ENOLINK;
+ }
+
+ return 0;
+}
+
+static int edp_train_pattern_set_write(struct edp_ctrl *ctrl, u8 pattern)
+{
+ u8 p = pattern;
+
+ DRM_DEBUG_DP("pattern=%x", p);
+ if (drm_dp_dpcd_write(ctrl->drm_aux,
+ DP_TRAINING_PATTERN_SET, &p, 1) < 1) {
+ DRM_ERROR("%s: Set training pattern to panel failed\n", __func__);
+ return -ENOLINK;
+ }
+
+ return 0;
+}
+
+static void edp_sink_train_set_adjust(struct edp_ctrl *ctrl,
+ const u8 *link_status)
+{
+ int i;
+ u8 max = 0;
+ u8 data;
+
+ /* use the max level across lanes */
+ for (i = 0; i < ctrl->lane_cnt; i++) {
+ data = drm_dp_get_adjust_request_voltage(link_status, i);
+ DRM_DEBUG_DP("lane=%d req_voltage_swing=0x%x", i, data);
+ if (max < data)
+ max = data;
+ }
+
+ ctrl->v_level = max >> DP_TRAIN_VOLTAGE_SWING_SHIFT;
+
+ /* use the max level across lanes */
+ max = 0;
+ for (i = 0; i < ctrl->lane_cnt; i++) {
+ data = drm_dp_get_adjust_request_pre_emphasis(link_status, i);
+ DRM_DEBUG_DP("lane=%d req_pre_emphasis=0x%x", i, data);
+ if (max < data)
+ max = data;
+ }
+
+ ctrl->p_level = max >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+ DRM_DEBUG_DP("v_level=%d, p_level=%d", ctrl->v_level, ctrl->p_level);
+}
+
+static void edp_host_train_set(struct edp_ctrl *ctrl, u32 train)
+{
+ int cnt = 10;
+ u32 data;
+ u32 shift = train - 1;
+
+ DRM_DEBUG_DP("train=%d", train);
+
+ edp_state_ctrl(ctrl, EDP_STATE_CTRL_LINK_TRAINING_PATTERN1 << shift);
+ while (--cnt) {
+ data = edp_read_link(ctrl->base, REG_EDP_MAINLINK_READY);
+ if (data & (EDP_MAINLINK_READY_TRAIN_PATTERN_1_READY << shift))
+ break;
+ }
+
+ if (cnt == 0)
+ DRM_DEBUG_DP("%s: set link_train=%d failed\n", __func__, train);
+}
+
+static int edp_voltage_pre_emphasis_set(struct edp_ctrl *ctrl)
+{
+ DRM_DEBUG_DP("v=%d p=%d", ctrl->v_level, ctrl->p_level);
+
+ msm_edp_v510_phy_config(ctrl->phy, ctrl->v_level, ctrl->p_level);
+ return edp_lane_set_write(ctrl, ctrl->v_level, ctrl->p_level);
+}
+
+static int edp_start_link_train_1(struct edp_ctrl *ctrl)
+{
+ u8 link_status[DP_LINK_STATUS_SIZE];
+ u8 old_v_level;
+ int tries;
+ int ret;
+ int rlen;
+
+ edp_host_train_set(ctrl, DP_TRAINING_PATTERN_1);
+ ret = edp_voltage_pre_emphasis_set(ctrl);
+ if (ret)
+ return ret;
+
+ ret = edp_train_pattern_set_write(ctrl,
+ DP_TRAINING_PATTERN_1 | DP_RECOVERED_CLOCK_OUT_EN);
+ if (ret)
+ return ret;
+
+ tries = 0;
+ old_v_level = ctrl->v_level;
+ while (1) {
+ drm_dp_link_train_clock_recovery_delay(ctrl->dpcd);
+
+ rlen = drm_dp_dpcd_read_link_status(ctrl->drm_aux, link_status);
+ if (rlen < DP_LINK_STATUS_SIZE) {
+ DRM_ERROR("%s: read link status failed\n", __func__);
+ return -ENOLINK;
+ }
+ if (drm_dp_clock_recovery_ok(link_status, ctrl->lane_cnt)) {
+ ret = 0;
+ break;
+ }
+
+ if (ctrl->v_level == DPCD_LINK_VOLTAGE_MAX) {
+ ret = -1;
+ break;
+ }
+
+ if (old_v_level == ctrl->v_level) {
+ tries++;
+ if (tries >= 5) {
+ ret = -1;
+ break;
+ }
+ } else {
+ tries = 0;
+ old_v_level = ctrl->v_level;
+ }
+
+ edp_sink_train_set_adjust(ctrl, link_status);
+ ret = edp_voltage_pre_emphasis_set(ctrl);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int edp_start_link_train_2(struct edp_ctrl *ctrl)
+{
+ u8 link_status[DP_LINK_STATUS_SIZE];
+ int tries = 0;
+ int ret;
+ int rlen;
+
+ edp_host_train_set(ctrl, DP_TRAINING_PATTERN_2);
+ ret = edp_voltage_pre_emphasis_set(ctrl);
+ if (ret)
+ return ret;
+
+ ret = edp_train_pattern_set_write(ctrl,
+ DP_TRAINING_PATTERN_2 | DP_RECOVERED_CLOCK_OUT_EN);
+ if (ret)
+ return ret;
+
+ while (1) {
+ drm_dp_link_train_channel_eq_delay(ctrl->dpcd);
+
+ rlen = drm_dp_dpcd_read_link_status(ctrl->drm_aux, link_status);
+ if (rlen < DP_LINK_STATUS_SIZE) {
+ DRM_ERROR("%s: read link status failed\n", __func__);
+ return -ENOLINK;
+ }
+ if (drm_dp_channel_eq_ok(link_status, ctrl->lane_cnt)) {
+ ret = 0;
+ break;
+ }
+
+ tries++;
+ if (tries > 10) {
+ ret = -1;
+ break;
+ }
+
+ edp_sink_train_set_adjust(ctrl, link_status);
+ ret = edp_voltage_pre_emphasis_set(ctrl);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static int edp_link_rate_down_shift(struct edp_ctrl *ctrl)
+{
+ u32 prate, lrate, bpp;
+ u8 rate, lane, max_lane;
+ int changed = 0;
+
+ rate = ctrl->link_rate;
+ lane = ctrl->lane_cnt;
+ max_lane = drm_dp_max_lane_count(ctrl->dpcd);
+
+ bpp = ctrl->color_depth * 3;
+ prate = ctrl->pixel_rate;
+ prate *= bpp;
+ prate /= 8; /* in kByte */
+
+ if (rate > DP_LINK_BW_1_62 && rate <= EDP_LINK_BW_MAX) {
+ rate -= 4; /* reduce rate */
+ changed++;
+ }
+
+ if (changed) {
+ if (lane >= 1 && lane < max_lane)
+ lane <<= 1; /* increase lane */
+
+ lrate = 270000; /* in kHz */
+ lrate *= rate;
+ lrate /= 10; /* kByte, 10 bits --> 8 bits */
+ lrate *= lane;
+
+ DRM_DEBUG_DP("new lrate=%u prate=%u(kHz) rate=%d lane=%d p=%u b=%d",
+ lrate, prate, rate, lane,
+ ctrl->pixel_rate,
+ bpp);
+
+ if (lrate > prate) {
+ ctrl->link_rate = rate;
+ ctrl->lane_cnt = lane;
+ DRM_DEBUG_DP("new rate=%d %d", rate, lane);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int edp_clear_training_pattern(struct edp_ctrl *ctrl)
+{
+ int ret;
+
+ ret = edp_train_pattern_set_write(ctrl, 0);
+
+ drm_dp_link_train_channel_eq_delay(ctrl->dpcd);
+
+ return ret;
+}
+
+static int edp_do_link_train(struct edp_ctrl *ctrl)
+{
+ u8 values[2], edp_config = 0;
+ int ret;
+
+ /*
+ * Set the current link rate and lane cnt to panel. They may have been
+ * adjusted and the values are different from them in DPCD CAP
+ */
+ values[0] = ctrl->lane_cnt;
+ values[1] = ctrl->link_rate;
+
+ if (drm_dp_enhanced_frame_cap(ctrl->dpcd))
+ values[0] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+
+ if (drm_dp_dpcd_write(ctrl->drm_aux, DP_LINK_BW_SET, &values[1], 1) < 0)
+ return EDP_TRAIN_FAIL;
+
+ drm_dp_dpcd_write(ctrl->drm_aux, DP_LANE_COUNT_SET, &values[0], 1);
+ ctrl->v_level = 0; /* start from default level */
+ ctrl->p_level = 0;
+
+ values[0] = DP_SPREAD_AMP_0_5;
+ values[1] = 1;
+ drm_dp_dpcd_write(ctrl->drm_aux, DP_DOWNSPREAD_CTRL, &values[0], 1);
+ drm_dp_dpcd_write(ctrl->drm_aux, DP_MAIN_LINK_CHANNEL_CODING_SET, &values[1], 1);
+
+ edp_state_ctrl(ctrl, 0);
+ if (edp_clear_training_pattern(ctrl))
+ return EDP_TRAIN_FAIL;
+
+ ret = edp_start_link_train_1(ctrl);
+ if (ret < 0) {
+ if (edp_link_rate_down_shift(ctrl) == 0) {
+ DRM_ERROR("link reconfig");
+ ret = EDP_TRAIN_RECONFIG;
+ goto clear;
+ } else {
+ DRM_ERROR("%s: Training 1 failed", __func__);
+ ret = EDP_TRAIN_FAIL;
+ goto clear;
+ }
+ }
+ DRM_INFO("Training 1 completed successfully");
+
+ edp_state_ctrl(ctrl, 0);
+ if (edp_clear_training_pattern(ctrl))
+ return EDP_TRAIN_FAIL;
+
+ ret = edp_start_link_train_2(ctrl);
+ if (ret < 0) {
+ if (edp_link_rate_down_shift(ctrl) == 0) {
+ DRM_ERROR("link reconfig");
+ ret = EDP_TRAIN_RECONFIG;
+ goto clear;
+ } else {
+ DRM_ERROR("%s: Training 2 failed", __func__);
+ ret = EDP_TRAIN_FAIL;
+ goto clear;
+ }
+ }
+ DRM_INFO("Training 2 completed successfully");
+
+ edp_config = DP_ALTERNATE_SCRAMBLER_RESET_ENABLE;
+ drm_dp_dpcd_write(ctrl->drm_aux, DP_EDP_CONFIGURATION_SET,
+ &edp_config, 1);
+
+ edp_state_ctrl(ctrl, EDP_STATE_CTRL_SEND_VIDEO);
+clear:
+ edp_clear_training_pattern(ctrl);
+
+ return ret;
+}
+
+static void edp_ctrl_config_misc(struct edp_ctrl *ctrl)
+{
+ u32 misc_val;
+ enum edp_color_depth depth = EDP_8BIT;
+
+ misc_val = edp_read_link(ctrl->base, REG_EDP_MISC1_MISC0);
+
+ if (ctrl->color_depth == 8)
+ depth = EDP_8BIT;
+ else if (ctrl->color_depth == 10)
+ depth = EDP_10BIT;
+ else if (ctrl->color_depth == 12)
+ depth = EDP_12BIT;
+ else if (ctrl->color_depth == 16)
+ depth = EDP_16BIT;
+
+ /* clear bpp bits */
+ misc_val &= ~(0x07 << EDP_MISC0_TEST_BITS_DEPTH_SHIFT);
+ misc_val |= depth << EDP_MISC0_TEST_BITS_DEPTH_SHIFT;
+
+ /* Configure clock to synchronous mode */
+ misc_val |= EDP_MISC0_SYNCHRONOUS_CLK;
+
+ DRM_DEBUG_DP("misc settings = 0x%x\n", misc_val);
+ edp_write_link(ctrl->base, REG_EDP_MISC1_MISC0, misc_val);
+}
+
+static void edp_ctrl_config_msa(struct edp_ctrl *ctrl)
+{
+ u32 pixel_m, pixel_n;
+ u32 mvid, nvid, pixel_div = 0, dispcc_input_rate;
+ unsigned long den, num;
+ u8 rate = ctrl->link_rate;
+ u32 stream_rate_khz = ctrl->pixel_rate;
+
+ if (rate == DP_LINK_BW_8_1)
+ pixel_div = 6;
+ else if (rate == DP_LINK_BW_1_62 || rate == DP_LINK_BW_2_7)
+ pixel_div = 2;
+ else if (rate == DP_LINK_BW_5_4)
+ pixel_div = 4;
+ else
+ DRM_ERROR("Invalid pixel mux divider\n");
+
+ dispcc_input_rate = (drm_dp_bw_code_to_link_rate(rate) * 10) / pixel_div;
+
+ rational_best_approximation(dispcc_input_rate, stream_rate_khz,
+ (unsigned long)(1 << 16) - 1,
+ (unsigned long)(1 << 16) - 1, &den, &num);
+
+ den = ~(den - num);
+ den = den & 0xFFFF;
+ pixel_m = num;
+ pixel_n = den;
+
+ mvid = (pixel_m & 0xFFFF) * 5;
+ nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);
+
+ if (rate == DP_LINK_BW_5_4)
+ nvid *= 2;
+
+ if (rate == DP_LINK_BW_8_1)
+ nvid *= 3;
+
+ DRM_DEBUG_DP("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
+ edp_write_link(ctrl->base, REG_EDP_SOFTWARE_MVID, mvid);
+ edp_write_link(ctrl->base, REG_EDP_SOFTWARE_NVID, nvid);
+ edp_write_p0(ctrl->base, REG_EDP_DSC_DTO, 0x0);
+}
+
+static void edp_ctrl_config_TU(struct edp_ctrl *ctrl)
+{
+ int i;
+
+ for (i = 0; i < MAX_TU_TABLE; i++) {
+ if (tu[i].rate == ctrl->pixel_rate)
+ break;
+ }
+
+ edp_write_link(ctrl->base, REG_EDP_VALID_BOUNDARY,
+ tu[i].valid_boundary);
+
+ edp_write_link(ctrl->base, REG_EDP_TU, tu[i].edp_tu);
+
+ edp_write_link(ctrl->base, REG_EDP_VALID_BOUNDARY_2,
+ tu[i].valid_boundary2);
+}
+
+static void edp_ctrl_timing_cfg(struct edp_ctrl *ctrl)
+{
+ struct drm_display_mode *mode = &ctrl->drm_mode;
+ u32 hstart_from_sync, vstart_from_sync;
+ u32 data;
+
+ /* Configure eDP timing to HW */
+ edp_write_link(ctrl->base, REG_EDP_TOTAL_HOR_VER,
+ EDP_TOTAL_HOR_VER_HORIZ(mode->htotal) |
+ EDP_TOTAL_HOR_VER_VERT(mode->vtotal));
+
+ vstart_from_sync = mode->vtotal - mode->vsync_start;
+ hstart_from_sync = mode->htotal - mode->hsync_start;
+ edp_write_link(ctrl->base, REG_EDP_START_HOR_VER_FROM_SYNC,
+ EDP_START_HOR_VER_FROM_SYNC_HORIZ(hstart_from_sync) |
+ EDP_START_HOR_VER_FROM_SYNC_VERT(vstart_from_sync));
+
+ data = EDP_HSYNC_VSYNC_WIDTH_POLARITY_VERT(
+ mode->vsync_end - mode->vsync_start);
+ data |= EDP_HSYNC_VSYNC_WIDTH_POLARITY_HORIZ(
+ mode->hsync_end - mode->hsync_start);
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ data |= EDP_HSYNC_VSYNC_WIDTH_POLARITY_NVSYNC;
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ data |= EDP_HSYNC_VSYNC_WIDTH_POLARITY_NHSYNC;
+ edp_write_link(ctrl->base, REG_EDP_HSYNC_VSYNC_WIDTH_POLARITY, data);
+
+ edp_write_link(ctrl->base, REG_EDP_ACTIVE_HOR_VER,
+ EDP_ACTIVE_HOR_VER_HORIZ(mode->hdisplay) |
+ EDP_ACTIVE_HOR_VER_VERT(mode->vdisplay));
+
+}
+
+static void edp_mainlink_ctrl(struct edp_ctrl *ctrl, int enable)
+{
+ u32 data = 0;
+
+ edp_write_link(ctrl->base, REG_EDP_MAINLINK_CTRL, EDP_MAINLINK_CTRL_RESET);
+ /* Make sure fully reset */
+ wmb();
+ usleep_range(500, 1000);
+
+ if (enable) {
+ data = (EDP_MAINLINK_CTRL_ENABLE |
+ EDP_MAINLINK_FB_BOUNDARY_SEL);
+ }
+
+ edp_write_link(ctrl->base, REG_EDP_MAINLINK_CTRL, data);
+}
+
+static void edp_ctrl_phy_enable(struct edp_ctrl *ctrl, int enable)
+{
+ if (enable) {
+ edp_write_ahb(ctrl->base, REG_EDP_PHY_CTRL,
+ EDP_PHY_CTRL_SW_RESET | EDP_PHY_CTRL_SW_RESET_PLL);
+ usleep_range(1000, 1100);
+ edp_write_ahb(ctrl->base, REG_EDP_PHY_CTRL, 0);
+
+ msm_edp_v510_phy_enable(ctrl->phy);
+ }
+}
+
+static void edp_ctrl_phy_aux_enable(struct edp_ctrl *ctrl, int enable)
+{
+ if (ctrl->core_initialized == enable)
+ return;
+
+ if (enable) {
+ pm_runtime_get_sync(&ctrl->pdev->dev);
+ edp_regulator_enable(ctrl);
+ edp_clk_enable(ctrl, EDP_CORE_PM, 1);
+ edp_ctrl_phy_enable(ctrl, 1);
+ msm_edp_v510_aux_ctrl(ctrl->aux, 1);
+ ctrl->core_initialized = true;
+ } else {
+ msm_edp_v510_aux_ctrl(ctrl->aux, 0);
+ edp_clk_enable(ctrl, EDP_CORE_PM, 0);
+ edp_regulator_disable(ctrl);
+ pm_runtime_put_sync(&ctrl->pdev->dev);
+ ctrl->core_initialized = false;
+ }
+}
+
+static void edp_ctrl_link_enable(struct edp_ctrl *ctrl, int enable)
+{
+ unsigned long link_rate;
+
+ link_rate = drm_dp_max_link_rate(ctrl->dpcd);
+ ctrl->edp_opts.link_rate = link_rate;
+ ctrl->edp_opts.lanes = drm_dp_max_lane_count(ctrl->dpcd);
+
+ if (enable) {
+ msm_edp_v510_phy_vm_pe_init(ctrl->phy, &ctrl->edp_opts);
+ msm_edp_v510_phy_power_on(ctrl->phy);
+
+ /* Enable link channel clocks */
+ edp_ctrl_set_clock_rate(ctrl, EDP_CTRL_PM, "ctrl_link",
+ link_rate * 1000);
+ edp_clk_enable(ctrl, EDP_CTRL_PM, 1);
+
+ edp_ctrl_set_clock_rate(ctrl, EDP_STREAM_PM, "stream_pixel",
+ ctrl->pixel_rate * 1000);
+ edp_clk_enable(ctrl, EDP_STREAM_PM, 1);
+
+ edp_mainlink_ctrl(ctrl, 1);
+ edp_config_ctrl(ctrl);
+ edp_ctrl_config_misc(ctrl);
+ edp_ctrl_timing_cfg(ctrl);
+ edp_ctrl_config_msa(ctrl);
+ edp_ctrl_config_TU(ctrl);
+
+ } else {
+ edp_mainlink_ctrl(ctrl, 0);
+ edp_clk_enable(ctrl, EDP_STREAM_PM, 0);
+ edp_clk_enable(ctrl, EDP_CTRL_PM, 0);
+ }
+}
+
+static int edp_ctrl_training(struct edp_ctrl *ctrl)
+{
+ int ret;
+
+ /* Do link training only when power is on */
+ if (!ctrl->power_on)
+ return -EINVAL;
+
+train_start:
+ ret = edp_do_link_train(ctrl);
+ if (ret == EDP_TRAIN_RECONFIG) {
+ /* Re-configure main link */
+ edp_ctrl_irq_enable(ctrl, 0);
+ edp_ctrl_link_enable(ctrl, 0);
+
+ /* Make sure link is fully disabled */
+ wmb();
+ usleep_range(500, 1000);
+
+ edp_ctrl_phy_enable(ctrl, 1);
+ edp_ctrl_irq_enable(ctrl, 1);
+ edp_ctrl_link_enable(ctrl, 1);
+ goto train_start;
+ }
+
+ return ret;
+}
+
+static void edp_ctrl_on_worker(struct work_struct *work)
+{
+ struct edp_ctrl *ctrl = container_of(
+ work, struct edp_ctrl, on_work);
+ u8 value;
+ int ret;
+
+ mutex_lock(&ctrl->dev_mutex);
+
+ if (ctrl->power_on) {
+ DRM_INFO("already on");
+ goto unlock_ret;
+ }
+
+ edp_ctrl_phy_aux_enable(ctrl, 1);
+ edp_ctrl_irq_enable(ctrl, 1);
+ edp_ctrl_link_enable(ctrl, 1);
+
+
+ /* DP_SET_POWER register is only available on DPCD v1.1 and later */
+ if (ctrl->dpcd[DP_DPCD_REV] >= 0x11) {
+ ret = drm_dp_dpcd_readb(ctrl->drm_aux, DP_SET_POWER, &value);
+ if (ret < 0)
+ goto fail;
+
+ value &= ~DP_SET_POWER_MASK;
+ value |= DP_SET_POWER_D0;
+
+ ret = drm_dp_dpcd_writeb(ctrl->drm_aux, DP_SET_POWER, value);
+ if (ret < 0)
+ goto fail;
+
+ /*
+ * According to the DP 1.1 specification, a "Sink Device must
+ * exit the power saving state within 1 ms" (Section 2.5.3.1,
+ * Table 5-52, "Sink Control Field" (register 0x600).
+ */
+ usleep_range(1000, 2000);
+ }
+
+ ctrl->power_on = true;
+
+ /* Start link training */
+ ret = edp_ctrl_training(ctrl);
+ if (ret != EDP_TRAIN_SUCCESS)
+ goto fail;
+
+ DRM_INFO("DONE");
+ goto unlock_ret;
+
+fail:
+ edp_ctrl_irq_enable(ctrl, 0);
+ edp_ctrl_link_enable(ctrl, 0);
+ edp_ctrl_phy_aux_enable(ctrl, 0);
+ ctrl->power_on = false;
+unlock_ret:
+ mutex_unlock(&ctrl->dev_mutex);
+}
+
+static void edp_ctrl_off_worker(struct work_struct *work)
+{
+ struct edp_ctrl *ctrl = container_of(
+ work, struct edp_ctrl, off_work);
+ unsigned long time_left;
+
+ mutex_lock(&ctrl->dev_mutex);
+
+ if (!ctrl->power_on) {
+ DRM_INFO("already off");
+ goto unlock_ret;
+ }
+
+ reinit_completion(&ctrl->idle_comp);
+
+ edp_state_ctrl(ctrl, EDP_STATE_CTRL_PUSH_IDLE);
+
+ time_left = wait_for_completion_timeout(&ctrl->idle_comp,
+ msecs_to_jiffies(500));
+ if (!time_left)
+ DRM_ERROR("%s: idle pattern timedout\n", __func__);
+
+ edp_state_ctrl(ctrl, 0);
+
+ /* DP_SET_POWER register is only available on DPCD v1.1 and later */
+ if (ctrl->dpcd[DP_DPCD_REV] >= 0x11) {
+ u8 value;
+ int ret;
+
+ ret = drm_dp_dpcd_readb(ctrl->drm_aux, DP_SET_POWER, &value);
+ if (ret > 0) {
+ value &= ~DP_SET_POWER_MASK;
+ value |= DP_SET_POWER_D3;
+
+ drm_dp_dpcd_writeb(ctrl->drm_aux, DP_SET_POWER, value);
+ }
+ }
+
+ edp_ctrl_irq_enable(ctrl, 0);
+
+ edp_ctrl_link_enable(ctrl, 0);
+
+ edp_ctrl_phy_aux_enable(ctrl, 0);
+
+ ctrl->power_on = false;
+
+unlock_ret:
+ mutex_unlock(&ctrl->dev_mutex);
+}
+
+irqreturn_t msm_edp_v510_ctrl_irq(struct edp_ctrl *ctrl)
+{
+ u32 isr1, isr2, mask1, mask2;
+ u32 ack;
+
+ spin_lock(&ctrl->irq_lock);
+ isr1 = edp_read_ahb(ctrl->base, REG_EDP_INTR_STATUS);
+ isr2 = edp_read_ahb(ctrl->base, REG_EDP_INTR_STATUS2);
+
+ mask1 = isr1 & EDP_INTERRUPT_STATUS1_MASK;
+ mask2 = isr2 & EDP_INTERRUPT_STATUS2_MASK;
+
+ isr1 &= ~mask1; /* remove masks bit */
+ isr2 &= ~mask2;
+
+ DRM_DEBUG_DP("isr=%x mask=%x isr2=%x mask2=%x",
+ isr1, mask1, isr2, mask2);
+
+ ack = isr1 & EDP_INTERRUPT_STATUS1;
+ ack <<= 1; /* ack bits */
+ ack |= mask1;
+ edp_write_ahb(ctrl->base, REG_EDP_INTR_STATUS, ack);
+
+ ack = isr2 & EDP_INTERRUPT_STATUS2;
+ ack <<= 1; /* ack bits */
+ ack |= mask2;
+ edp_write_ahb(ctrl->base, REG_EDP_INTR_STATUS2, ack);
+ spin_unlock(&ctrl->irq_lock);
+
+ if (isr2 & EDP_INTR_READY_FOR_VIDEO)
+ DRM_INFO("edp_video_ready");
+
+ if (isr2 & EDP_INTR_IDLE_PATTERN_SENT) {
+ DRM_INFO("idle_patterns_sent");
+ complete(&ctrl->idle_comp);
+ }
+
+ msm_edp_v510_aux_irq(ctrl->aux, isr1);
+
+ return IRQ_HANDLED;
+}
+
+void msm_edp_v510_ctrl_power(struct edp_ctrl *ctrl, bool on)
+{
+ if (on)
+ queue_work(ctrl->workqueue, &ctrl->on_work);
+ else
+ queue_work(ctrl->workqueue, &ctrl->off_work);
+}
+
+int msm_edp_v510_ctrl_init(struct msm_edp_v510 *edp)
+{
+ struct edp_ctrl *ctrl = NULL;
+ struct device *dev = &edp->base.pdev->dev;
+ int ret;
+
+ if (!edp) {
+ DRM_ERROR("%s: edp is NULL!\n", __func__);
+ return -EINVAL;
+ }
+
+ ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+ if (!ctrl)
+ return -ENOMEM;
+
+ edp->ctrl = ctrl;
+ ctrl->pdev = edp->base.pdev;
+
+ ctrl->base = msm_ioremap(ctrl->pdev, "edp_ctrl", "eDP_CTRL");
+ if (IS_ERR(ctrl->base))
+ return PTR_ERR(ctrl->base);
+
+ ctrl->phy_base = msm_ioremap(ctrl->pdev, "edp_phy", "eDP_PHY");
+ if (IS_ERR(ctrl->phy_base))
+ return PTR_ERR(ctrl->phy_base);
+
+ /* Get regulator, clock, gpio, pwm */
+ ret = edp_regulator_init(ctrl);
+ if (ret) {
+ DRM_ERROR("%s:regulator init fail\n", __func__);
+ return ret;
+ }
+ ret = edp_clk_init(ctrl);
+ if (ret) {
+ DRM_ERROR("%s:clk init fail\n", __func__);
+ return ret;
+ }
+ ret = edp_gpio_config(ctrl);
+ if (ret) {
+ DRM_ERROR("%s:failed to configure GPIOs: %d", __func__, ret);
+ return ret;
+ }
+
+ /* Init aux and phy */
+ ctrl->aux = msm_edp_v510_aux_init(dev, ctrl->base, &ctrl->drm_aux);
+ if (!ctrl->aux || !ctrl->drm_aux) {
+ DRM_ERROR("%s:failed to init aux\n", __func__);
+ return -ENOMEM;
+ }
+
+ ctrl->phy = msm_edp_v510_phy_init(dev, ctrl->phy_base, &ctrl->edp_opts);
+ if (!ctrl->phy) {
+ DRM_ERROR("%s:failed to init phy\n", __func__);
+ ret = -ENOMEM;
+ goto err_destory_aux;
+ }
+
+ pm_runtime_enable(dev);
+ spin_lock_init(&ctrl->irq_lock);
+ mutex_init(&ctrl->dev_mutex);
+ init_completion(&ctrl->idle_comp);
+
+ /* setup workqueue */
+ ctrl->workqueue = alloc_ordered_workqueue("edp_drm_work", 0);
+ INIT_WORK(&ctrl->on_work, edp_ctrl_on_worker);
+ INIT_WORK(&ctrl->off_work, edp_ctrl_off_worker);
+
+ return 0;
+
+err_destory_aux:
+ msm_edp_v510_aux_destroy(dev, ctrl->aux);
+ ctrl->aux = NULL;
+ return ret;
+}
+
+void msm_edp_v510_ctrl_destroy(struct edp_ctrl *ctrl)
+{
+ if (!ctrl)
+ return;
+
+ if (ctrl->workqueue) {
+ flush_workqueue(ctrl->workqueue);
+ destroy_workqueue(ctrl->workqueue);
+ ctrl->workqueue = NULL;
+ }
+
+ if (ctrl->aux) {
+ msm_edp_v510_aux_destroy(&ctrl->pdev->dev, ctrl->aux);
+ ctrl->aux = NULL;
+ }
+
+ edp_clk_deinit(ctrl);
+
+ kfree(ctrl->edid);
+ ctrl->edid = NULL;
+
+ mutex_destroy(&ctrl->dev_mutex);
+}
+
+bool msm_edp_v510_ctrl_panel_connected(struct edp_ctrl *ctrl)
+{
+ mutex_lock(&ctrl->dev_mutex);
+ if (ctrl->edp_connected) {
+ mutex_unlock(&ctrl->dev_mutex);
+ return true;
+ }
+
+ if (!ctrl->power_on) {
+ edp_ctrl_phy_aux_enable(ctrl, 1);
+ edp_ctrl_irq_enable(ctrl, 1);
+ }
+
+ if (drm_dp_dpcd_read(ctrl->drm_aux, DP_DPCD_REV, ctrl->dpcd,
+ DP_RECEIVER_CAP_SIZE) < DP_RECEIVER_CAP_SIZE) {
+ DRM_ERROR("%s: AUX channel is NOT ready\n", __func__);
+ memset(ctrl->dpcd, 0, DP_RECEIVER_CAP_SIZE);
+
+ if (!ctrl->power_on) {
+ edp_ctrl_irq_enable(ctrl, 0);
+ edp_ctrl_phy_aux_enable(ctrl, 0);
+ }
+
+ } else {
+ ctrl->edp_connected = true;
+ }
+
+
+ DRM_INFO("connect status=%d", ctrl->edp_connected);
+
+ mutex_unlock(&ctrl->dev_mutex);
+
+ return ctrl->edp_connected;
+}
+
+int msm_edp_v510_ctrl_get_panel_info(struct edp_ctrl *ctrl,
+ struct drm_connector *connector, struct edid **edid)
+{
+ int ret = 0;
+
+ mutex_lock(&ctrl->dev_mutex);
+
+ if (ctrl->edid) {
+ if (edid) {
+ DRM_DEBUG_DP("Just return edid buffer");
+ *edid = ctrl->edid;
+ }
+ goto unlock_ret;
+ }
+
+ if (!ctrl->power_on && !ctrl->edp_connected) {
+ edp_ctrl_phy_aux_enable(ctrl, 1);
+ edp_ctrl_irq_enable(ctrl, 1);
+ }
+
+ /* Initialize link rate as panel max link rate */
+ ctrl->link_rate = ctrl->dpcd[DP_MAX_LINK_RATE];
+
+
+ ctrl->edid = drm_get_edid(connector, &ctrl->drm_aux->ddc);
+ if (!ctrl->edid) {
+ DRM_ERROR("%s: edid read fail\n", __func__);
+ if (!ctrl->power_on) {
+ edp_ctrl_irq_enable(ctrl, 0);
+ edp_ctrl_phy_aux_enable(ctrl, 0);
+ }
+ goto unlock_ret;
+ }
+
+ if (edid)
+ *edid = ctrl->edid;
+
+unlock_ret:
+ mutex_unlock(&ctrl->dev_mutex);
+ return ret;
+}
+
+int msm_edp_v510_ctrl_mode_set(struct edp_ctrl *ctrl,
+ const struct drm_display_mode *mode,
+ const struct drm_display_info *info)
+{
+ /*
+ * Need to keep color depth, pixel rate and
+ * interlaced information in ctrl context
+ */
+ ctrl->color_depth = info->bpc;
+ ctrl->pixel_rate = mode->clock;
+
+ memcpy(&ctrl->drm_mode, mode, sizeof(*mode));
+
+ ctrl->interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+
+ /* Fill initial link config based on passed in timing */
+ edp_fill_link_cfg(ctrl);
+
+ return 0;
+}
+
+
+bool msm_edp_v510_ctrl_pixel_clock_valid(struct edp_ctrl *ctrl, u32 pixel_rate)
+{
+ u32 link_clock = 0;
+ unsigned long link_bw = 0, stream_bw = 0;
+
+ link_clock = drm_dp_bw_code_to_link_rate(ctrl->link_rate);
+ link_bw = link_clock * ctrl->lane_cnt;
+ stream_bw = pixel_rate * ctrl->color_depth * 3 / 8;
+
+ if (stream_bw > link_bw) {
+ DRM_ERROR("pixel clock %d(kHz) not supported", pixel_rate);
+ return false;
+ }
+
+ return true;
+}
new file mode 100644
@@ -0,0 +1,641 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+#include "edp_v510.h"
+#include "edp_v510_reg.h"
+
+#define MSM_EDP_PLL_OFFSET 0x0000
+#define MSM_EDP_TX0_OFFSET 0x0200
+#define MSM_EDP_TX1_OFFSET 0x0600
+#define MSM_EDP_PHY_OFFSET 0x0a00
+
+struct edp_phy_clks {
+ struct edp_phy *edp_phy;
+ struct clk_hw edp_link_hw;
+ struct clk_hw edp_pixel_hw;
+};
+
+struct edp_phy {
+ void __iomem *base;
+ struct edp_phy_opts *edp_opts;
+ struct edp_phy_clks *edp_clks;
+};
+
+static inline u32 edp_pll_read(struct edp_phy *phy, u32 offset)
+{
+ offset += MSM_EDP_PLL_OFFSET;
+ return readl_relaxed(phy->base + offset);
+}
+
+static inline u32 edp_tx0_read(struct edp_phy *phy, u32 offset)
+{
+ offset += MSM_EDP_TX0_OFFSET;
+ return readl_relaxed(phy->base + offset);
+}
+
+static inline u32 edp_tx1_read(struct edp_phy *phy, u32 offset)
+{
+ offset += MSM_EDP_TX1_OFFSET;
+ return readl_relaxed(phy->base + offset);
+}
+
+static inline u32 edp_phy_read(struct edp_phy *phy, u32 offset)
+{
+ offset += MSM_EDP_PHY_OFFSET;
+ return readl_relaxed(phy->base + offset);
+}
+
+static inline void edp_pll_write(struct edp_phy *phy, u32 offset, u32 data)
+{
+ offset += MSM_EDP_PLL_OFFSET;
+ writel(data, phy->base + offset);
+}
+
+static inline void edp_tx0_write(struct edp_phy *phy, u32 offset, u32 data)
+{
+ offset += MSM_EDP_TX0_OFFSET;
+ writel(data, phy->base + offset);
+}
+
+static inline void edp_tx1_write(struct edp_phy *phy, u32 offset, u32 data)
+{
+ offset += MSM_EDP_TX1_OFFSET;
+ writel(data, phy->base + offset);
+}
+
+static inline void edp_phy_write(struct edp_phy *phy, u32 offset, u32 data)
+{
+ offset += MSM_EDP_PHY_OFFSET;
+ writel(data, phy->base + offset);
+}
+
+static int edp_pixel_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ switch (req->rate) {
+ case 1620000000UL / 2:
+ case 2160000000UL / 2:
+ case 2430000000UL / 2:
+ case 2700000000UL / 2:
+ case 5940000000UL / 6:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static unsigned long
+edp_pixel_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct edp_phy_clks *edp_clks;
+ struct edp_phy *edp_phy;
+ struct edp_phy_opts *opts;
+
+ edp_clks = container_of(hw, struct edp_phy_clks, edp_pixel_hw);
+ edp_phy = edp_clks->edp_phy;
+ opts = edp_phy->edp_opts;
+
+ switch (opts->link_rate) {
+ case 162000:
+ return 1620000000UL / 2;
+ break;
+ case 216000:
+ return 2160000000UL / 2;
+ break;
+ case 243000:
+ return 2430000000UL / 2;
+ break;
+ case 270000:
+ return 2700000000UL / 2;
+ break;
+ case 324000:
+ return 3240000000UL / 4;
+ break;
+ case 432000:
+ return 4320000000UL / 4;
+ break;
+ case 540000:
+ return 5400000000UL / 4;
+ break;
+ case 594000:
+ return 5940000000UL / 6;
+ case 810000:
+ return 8100000000UL / 6;
+ default:
+ return 0;
+ }
+}
+
+static const struct clk_ops edp_pixel_clk_ops = {
+ .determine_rate = edp_pixel_clk_determine_rate,
+ .recalc_rate = edp_pixel_clk_recalc_rate,
+};
+
+static int edp_link_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
+{
+ switch (req->rate) {
+ case 162000000:
+ case 216000000:
+ case 243000000:
+ case 270000000:
+ case 324000000:
+ case 432000000:
+ case 540000000:
+ case 594000000:
+ case 810000000:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static unsigned long
+edp_link_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct edp_phy_clks *edp_clks;
+ struct edp_phy *edp_phy;
+ struct edp_phy_opts *opts;
+
+ edp_clks = container_of(hw, struct edp_phy_clks, edp_link_hw);
+ edp_phy = edp_clks->edp_phy;
+ opts = edp_phy->edp_opts;
+
+ switch (opts->link_rate) {
+ case 162000:
+ case 216000:
+ case 243000:
+ case 270000:
+ case 324000:
+ case 432000:
+ case 540000:
+ case 594000:
+ case 810000:
+ return opts->link_rate * 1000;
+ default:
+ return 0;
+ }
+}
+
+static const struct clk_ops edp_link_clk_ops = {
+ .determine_rate = edp_link_clk_determine_rate,
+ .recalc_rate = edp_link_clk_recalc_rate,
+};
+
+static struct clk_hw *
+edp_clks_hw_get(struct of_phandle_args *clkspec, void *data)
+{
+ struct edp_phy_clks *edp_clks = data;
+ unsigned int idx = clkspec->args[0];
+
+ if (idx >= 2) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ if (idx == 0)
+ return &edp_clks->edp_link_hw;
+
+ return &edp_clks->edp_pixel_hw;
+}
+
+static void edp_clk_release_provider(void *res)
+{
+ of_clk_del_provider(res);
+}
+
+static int edp_phy_clks_register(struct device *dev, struct edp_phy *edp_phy)
+{
+ struct clk_init_data init = { };
+ struct edp_phy_clks *edp_clks;
+ int ret;
+
+ edp_clks = devm_kzalloc(dev, sizeof(*edp_clks), GFP_KERNEL);
+ if (!edp_clks)
+ return -ENOMEM;
+
+ edp_clks->edp_phy = edp_phy;
+ edp_phy->edp_clks = edp_clks;
+
+ init.ops = &edp_link_clk_ops;
+ init.name = "edp_phy_pll_link_clk";
+ edp_clks->edp_link_hw.init = &init;
+ ret = devm_clk_hw_register(dev, &edp_clks->edp_link_hw);
+ if (ret)
+ return ret;
+
+ init.ops = &edp_pixel_clk_ops;
+ init.name = "edp_phy_pll_vco_div_clk";
+ edp_clks->edp_pixel_hw.init = &init;
+ ret = devm_clk_hw_register(dev, &edp_clks->edp_pixel_hw);
+ if (ret)
+ return ret;
+
+ ret = of_clk_add_hw_provider(dev->of_node, edp_clks_hw_get, edp_clks);
+ if (ret)
+ return ret;
+
+ /*
+ * Roll a devm action because the clock provider is the child node, but
+ * the child node is not actually a device.
+ */
+ ret = devm_add_action(dev, edp_clk_release_provider, dev->of_node);
+ if (ret)
+ edp_clk_release_provider(dev->of_node);
+
+ return ret;
+}
+
+static void edp_phy_ssc_en(struct edp_phy *edp_phy, bool en)
+{
+ if (en) {
+ edp_pll_write(edp_phy, 0x10, 0x01);
+ edp_pll_write(edp_phy, 0x14, 0x00);
+ edp_pll_write(edp_phy, 0x1c, 0x36);
+ edp_pll_write(edp_phy, 0x20, 0x01);
+ edp_pll_write(edp_phy, 0x24, 0x5c);
+ edp_pll_write(edp_phy, 0x28, 0x08);
+ } else {
+ edp_pll_write(edp_phy, 0x10, 0x00);
+ }
+}
+
+int msm_edp_v510_phy_enable(struct edp_phy *edp_phy)
+{
+ u32 status;
+
+ edp_phy_write(edp_phy, EDP_PHY_PD_CTL, 0x7D);
+ edp_pll_write(edp_phy, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x17);
+ edp_phy_write(edp_phy, EDP_PHY_AUX_CFG1, 0x13);
+ edp_phy_write(edp_phy, EDP_PHY_AUX_CFG2, 0x24);
+ edp_phy_write(edp_phy, EDP_PHY_AUX_CFG3, 0x00);
+ edp_phy_write(edp_phy, EDP_PHY_AUX_CFG4, 0x0a);
+ edp_phy_write(edp_phy, EDP_PHY_AUX_CFG5, 0x26);
+ edp_phy_write(edp_phy, EDP_PHY_AUX_CFG6, 0x0a);
+ edp_phy_write(edp_phy, EDP_PHY_AUX_CFG7, 0x03);
+ edp_phy_write(edp_phy, EDP_PHY_AUX_CFG8, 0xB7);
+ edp_phy_write(edp_phy, EDP_PHY_AUX_CFG9, 0x03);
+ edp_phy_write(edp_phy, EDP_PHY_AUX_INTERRUPT_MASK, 0x1f);
+
+ edp_phy_write(edp_phy, EDP_PHY_MODE, 0xFC);
+
+ if (readl_poll_timeout_atomic((edp_phy->base +
+ MSM_EDP_PLL_OFFSET + QSERDES_COM_CMN_STATUS),
+ status, ((status & BIT(7)) > 0), 5, 100))
+ DRM_ERROR("%s: refgen not ready. Status=0x%x\n", __func__, status);
+
+ edp_tx0_write(edp_phy, TXn_LDO_CONFIG, 0x01);
+ edp_tx1_write(edp_phy, TXn_LDO_CONFIG, 0x01);
+ edp_tx0_write(edp_phy, TXn_LANE_MODE_1, 0x00);
+ edp_tx1_write(edp_phy, TXn_LANE_MODE_1, 0x00);
+
+ return 0;
+}
+
+static const u8 edp_hbr2_pre_emphasis[4][4] = {
+ {0x08, 0x11, 0x17, 0x1B}, /* pe0, 0 db */
+ {0x00, 0x0C, 0x13, 0xFF}, /* pe1, 3.5 db */
+ {0x05, 0x10, 0xFF, 0xFF}, /* pe2, 6.0 db */
+ {0x00, 0xFF, 0xFF, 0xFF} /* pe3, 9.5 db */
+};
+
+static const u8 edp_hbr2_voltage_swing[4][4] = {
+ {0x0A, 0x11, 0x17, 0x1F}, /* sw0, 0.4v */
+ {0x0C, 0x14, 0x1D, 0xFF}, /* sw1, 0.6 v */
+ {0x15, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */
+ {0x17, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */
+};
+
+void msm_edp_v510_phy_vm_pe_init(struct edp_phy *edp_phy, struct edp_phy_opts *opts)
+{
+
+ edp_phy->edp_opts = opts;
+
+ edp_tx0_write(edp_phy, TXn_TX_DRV_LVL, edp_hbr2_voltage_swing[0][0]);
+ edp_tx0_write(edp_phy, TXn_TX_EMP_POST1_LVL,
+ edp_hbr2_pre_emphasis[0][0]);
+ edp_tx1_write(edp_phy, TXn_TX_DRV_LVL, edp_hbr2_voltage_swing[0][0]);
+ edp_tx1_write(edp_phy, TXn_TX_EMP_POST1_LVL,
+ edp_hbr2_pre_emphasis[0][0]);
+
+ edp_tx0_write(edp_phy, TXn_HIGHZ_DRVR_EN, 4);
+ edp_tx0_write(edp_phy, TXn_TRANSCEIVER_BIAS_EN, 3);
+ edp_tx1_write(edp_phy, TXn_HIGHZ_DRVR_EN, 7);
+ edp_tx1_write(edp_phy, TXn_TRANSCEIVER_BIAS_EN, 0);
+ edp_phy_write(edp_phy, EDP_PHY_CFG_1, 3);
+
+}
+
+void msm_edp_v510_phy_config(struct edp_phy *edp_phy, u8 v_level, u8 p_level)
+{
+ edp_tx0_write(edp_phy, TXn_TX_DRV_LVL,
+ edp_hbr2_voltage_swing[v_level][p_level]);
+ edp_tx0_write(edp_phy, TXn_TX_EMP_POST1_LVL,
+ edp_hbr2_pre_emphasis[v_level][p_level]);
+
+ edp_tx1_write(edp_phy, TXn_TX_DRV_LVL,
+ edp_hbr2_voltage_swing[v_level][p_level]);
+ edp_tx1_write(edp_phy, TXn_TX_EMP_POST1_LVL,
+ edp_hbr2_pre_emphasis[v_level][p_level]);
+}
+
+static void edp_pll_vco_init(struct edp_phy *edp_phy)
+{
+ edp_phy_ssc_en(edp_phy, true);
+ edp_pll_write(edp_phy, QSERDES_COM_SVS_MODE_CLK_SEL, 0x01);
+ edp_pll_write(edp_phy, QSERDES_COM_SYSCLK_EN_SEL, 0x0b);
+ edp_pll_write(edp_phy, QSERDES_COM_SYS_CLK_CTRL, 0x02);
+ edp_pll_write(edp_phy, QSERDES_COM_CLK_ENABLE1, 0x0c);
+ edp_pll_write(edp_phy, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x06);
+ edp_pll_write(edp_phy, QSERDES_COM_CLK_SEL, 0x30);
+ edp_pll_write(edp_phy, QSERDES_COM_PLL_IVCO, 0x07);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP_EN, 0x04);
+ edp_pll_write(edp_phy, QSERDES_COM_PLL_CCTRL_MODE0, 0x36);
+ edp_pll_write(edp_phy, QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
+ edp_pll_write(edp_phy, QSERDES_COM_CP_CTRL_MODE0, 0x06);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00);
+ edp_pll_write(edp_phy, QSERDES_COM_CMN_CONFIG, 0x02);
+ edp_pll_write(edp_phy, QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x3f);
+ edp_pll_write(edp_phy, QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE_MAP, 0x00);
+ edp_pll_write(edp_phy, QSERDES_COM_BG_TIMER, 0x0a);
+ edp_pll_write(edp_phy, QSERDES_COM_CORECLK_DIV_MODE0, 0x14);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE_CTRL, 0x00);
+ edp_pll_write(edp_phy, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x17);
+ edp_pll_write(edp_phy, QSERDES_COM_CORE_CLK_EN, 0x0f);
+
+ switch (edp_phy->edp_opts->link_rate) {
+ case 162000:
+ edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x05);
+ edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x69);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x80);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x07);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x6f);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x08);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0xa0);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x03);
+ break;
+ case 216000:
+ edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x04);
+ edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x70);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x08);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x3f);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x0b);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0x34);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x03);
+ break;
+ case 243000:
+ edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x04);
+ edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x7e);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x09);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0xa7);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x0c);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0x5c);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x02);
+ break;
+ case 270000:
+ edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x03);
+ edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x69);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x80);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x07);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x0f);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x0e);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0xa0);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x03);
+ break;
+ case 324000:
+ edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x03);
+ edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x7e);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x09);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0xdf);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x10);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0x5c);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x02);
+ break;
+ case 432000:
+ edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x01);
+ edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x70);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x08);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x7f);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x16);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0x34);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x03);
+ break;
+ case 540000:
+ edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x01);
+ edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x8c);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x0a);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x1f);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x1c);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0x84);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x01);
+ break;
+ case 594000:
+ edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x01);
+ edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x9a);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x0b);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0xef);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x1e);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0xac);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x00);
+ break;
+ case 810000:
+ edp_pll_write(edp_phy, QSERDES_COM_HSCLK_SEL, 0x00);
+ edp_pll_write(edp_phy, QSERDES_COM_DEC_START_MODE0, 0x69);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x80);
+ edp_pll_write(edp_phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x07);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x2f);
+ edp_pll_write(edp_phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x2a);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE1_MODE0, 0xa0);
+ edp_pll_write(edp_phy, QSERDES_COM_VCO_TUNE2_MODE0, 0x03);
+ break;
+ default:
+ DRM_ERROR("%s: Invalid link rate. rate=%lu\n", __func__,
+ edp_phy->edp_opts->link_rate);
+ break;
+ }
+}
+
+static void edp_lanes_init(struct edp_phy *edp_phy)
+{
+ edp_tx0_write(edp_phy, TXn_TRANSCEIVER_BIAS_EN, 0x03);
+ edp_tx0_write(edp_phy, TXn_CLKBUF_ENABLE, 0x0f);
+ edp_tx0_write(edp_phy, TXn_RESET_TSYNC_EN, 0x03);
+ edp_tx0_write(edp_phy, TXn_TRAN_DRVR_EMP_EN, 0x01);
+ edp_tx0_write(edp_phy, TXn_TX_BAND, 0x4);
+
+ edp_tx1_write(edp_phy, TXn_TRANSCEIVER_BIAS_EN, 0x03);
+ edp_tx1_write(edp_phy, TXn_CLKBUF_ENABLE, 0x0f);
+ edp_tx1_write(edp_phy, TXn_RESET_TSYNC_EN, 0x03);
+ edp_tx1_write(edp_phy, TXn_TRAN_DRVR_EMP_EN, 0x01);
+ edp_tx1_write(edp_phy, TXn_TX_BAND, 0x4);
+}
+
+static void edp_lanes_configure(struct edp_phy *edp_phy)
+{
+ edp_tx0_write(edp_phy, TXn_HIGHZ_DRVR_EN, 0x1f);
+ edp_tx0_write(edp_phy, TXn_HIGHZ_DRVR_EN, 0x04);
+ edp_tx0_write(edp_phy, TXn_TX_POL_INV, 0x00);
+
+ edp_tx1_write(edp_phy, TXn_HIGHZ_DRVR_EN, 0x1f);
+ edp_tx1_write(edp_phy, TXn_HIGHZ_DRVR_EN, 0x04);
+ edp_tx1_write(edp_phy, TXn_TX_POL_INV, 0x00);
+
+ edp_tx1_write(edp_phy, TXn_HIGHZ_DRVR_EN, 0x04);
+ edp_tx1_write(edp_phy, TXn_TX_POL_INV, 0x00);
+
+ edp_tx0_write(edp_phy, TXn_TX_DRV_LVL_OFFSET, 0x10);
+ edp_tx1_write(edp_phy, TXn_TX_DRV_LVL_OFFSET, 0x10);
+
+ edp_tx0_write(edp_phy, TXn_RES_CODE_LANE_OFFSET_TX0, 0x11);
+ edp_tx0_write(edp_phy, TXn_RES_CODE_LANE_OFFSET_TX1, 0x11);
+
+ edp_tx1_write(edp_phy, TXn_RES_CODE_LANE_OFFSET_TX0, 0x11);
+ edp_tx1_write(edp_phy, TXn_RES_CODE_LANE_OFFSET_TX1, 0x11);
+
+ edp_tx0_write(edp_phy, TXn_TX_EMP_POST1_LVL, 0x00);
+ edp_tx0_write(edp_phy, TXn_TX_DRV_LVL, 0x18);
+ edp_tx1_write(edp_phy, TXn_TX_EMP_POST1_LVL, 0x00);
+ edp_tx1_write(edp_phy, TXn_TX_DRV_LVL, 0x18);
+}
+
+static int edp_pll_vco_configure(struct edp_phy *edp_phy)
+{
+ struct edp_phy_clks *edp_clks = edp_phy->edp_clks;
+ u32 phy_vco_div = 0, status;
+ unsigned long pixel_freq = 0;
+
+ switch (edp_phy->edp_opts->link_rate) {
+ case 162000:
+ phy_vco_div = 2;
+ pixel_freq = 1620000000UL / 2;
+ break;
+ case 216000:
+ phy_vco_div = 1;
+ pixel_freq = 2160000000UL / 2;
+ break;
+ case 243000:
+ phy_vco_div = 1;
+ pixel_freq = 2430000000UL / 2;
+ break;
+ case 270000:
+ phy_vco_div = 1;
+ pixel_freq = 2700000000UL / 2;
+ break;
+ case 324000:
+ phy_vco_div = 2;
+ pixel_freq = 3240000000UL / 4;
+ break;
+ case 432000:
+ phy_vco_div = 2;
+ pixel_freq = 4320000000UL / 4;
+ break;
+ case 540000:
+ phy_vco_div = 2;
+ pixel_freq = 5400000000UL / 4;
+ break;
+ case 594000:
+ phy_vco_div = 0;
+ pixel_freq = 5940000000UL / 6;
+ break;
+ case 810000:
+ phy_vco_div = 0;
+ pixel_freq = 8100000000UL / 6;
+ break;
+ default:
+ DRM_ERROR("%s: Invalid link rate. rate=%lu\n", __func__,
+ edp_phy->edp_opts->link_rate);
+ break;
+ }
+
+ edp_phy_write(edp_phy, EDP_PHY_VCO_DIV, phy_vco_div);
+
+ clk_set_rate(edp_clks->edp_link_hw.clk,
+ edp_phy->edp_opts->link_rate * 1000);
+ clk_set_rate(edp_clks->edp_pixel_hw.clk, pixel_freq);
+
+ edp_phy_write(edp_phy, EDP_PHY_CFG, 0x01);
+ edp_phy_write(edp_phy, EDP_PHY_CFG, 0x05);
+ edp_phy_write(edp_phy, EDP_PHY_CFG, 0x01);
+ edp_phy_write(edp_phy, EDP_PHY_CFG, 0x09);
+
+ edp_pll_write(edp_phy, QSERDES_COM_RESETSM_CNTRL, 0x20);
+
+ if (readl_poll_timeout_atomic((edp_phy->base +
+ MSM_EDP_PLL_OFFSET + QSERDES_COM_C_READY_STATUS),
+ status, ((status & BIT(0)) > 0), 500, 10000)) {
+ DRM_ERROR("%s: PLL not locked. Status=0x%x\n", __func__, status);
+ return -ETIMEDOUT;
+ }
+
+ edp_phy_write(edp_phy, EDP_PHY_CFG, 0x19);
+ edp_lanes_configure(edp_phy);
+ edp_phy_write(edp_phy, EDP_PHY_CFG_1, 0x03);
+
+ if (readl_poll_timeout_atomic((edp_phy->base +
+ MSM_EDP_PHY_OFFSET + EDP_PHY_STATUS),
+ status, ((status & BIT(1)) > 0), 500, 10000)) {
+ DRM_ERROR("%s: PHY not ready. Status=0x%x\n", __func__, status);
+ return -ETIMEDOUT;
+ }
+
+ edp_phy_write(edp_phy, EDP_PHY_CFG, 0x18);
+ udelay(2000);
+ edp_phy_write(edp_phy, EDP_PHY_CFG, 0x19);
+
+ return readl_poll_timeout_atomic((edp_phy->base +
+ MSM_EDP_PLL_OFFSET + QSERDES_COM_C_READY_STATUS),
+ status, ((status & BIT(0)) > 0), 500, 10000);
+
+}
+
+int msm_edp_v510_phy_power_on(struct edp_phy *edp_phy)
+{
+ int ret = 0;
+
+ edp_pll_vco_init(edp_phy);
+
+ edp_phy_write(edp_phy, EDP_PHY_TX0_TX1_LANE_CTL, 0x05);
+ edp_phy_write(edp_phy, EDP_PHY_TX2_TX3_LANE_CTL, 0x05);
+
+ edp_lanes_init(edp_phy);
+
+ ret = edp_pll_vco_configure(edp_phy);
+
+ return ret;
+}
+
+void *msm_edp_v510_phy_init(struct device *dev, void __iomem *regbase,
+ struct edp_phy_opts *opts)
+{
+ struct edp_phy *phy = NULL;
+
+ phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy)
+ return NULL;
+
+ phy->base = regbase;
+ phy->edp_opts = opts;
+ edp_phy_clks_register(dev, phy);
+
+ return phy;
+}
new file mode 100644
@@ -0,0 +1,339 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2014-2021, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef EDP_V510_REG
+#define EDP_V510_REG
+
+enum edp_color_depth {
+ EDP_6BIT = 0,
+ EDP_8BIT = 1,
+ EDP_10BIT = 2,
+ EDP_12BIT = 3,
+ EDP_16BIT = 4,
+};
+
+enum edp_component_format {
+ EDP_RGB = 0,
+ EDP_YUV422 = 1,
+ EDP_YUV444 = 2,
+};
+
+/* DP_TX Registers */
+#define REG_EDP_HW_VERSION (0x00000000)
+
+#define REG_EDP_SW_RESET (0x00000010)
+#define DP_SW_RESET (0x00000001)
+
+#define REG_EDP_PHY_CTRL (0x00000014)
+#define EDP_PHY_CTRL_SW_RESET_PLL (0x00000001)
+#define EDP_PHY_CTRL_SW_RESET (0x00000004)
+
+#define REG_EDP_CLK_CTRL (0x00000018)
+#define REG_EDP_CLK_ACTIVE (0x0000001C)
+
+#define REG_EDP_INTR_STATUS (0x00000020)
+#define EDP_INTR_AUX_I2C_DONE BIT(3)
+#define EDP_INTR_WRONG_ADDR BIT(6)
+#define EDP_INTR_TIMEOUT BIT(9)
+#define EDP_INTR_NACK_DEFER BIT(12)
+#define EDP_INTR_WRONG_DATA_CNT BIT(15)
+#define EDP_INTR_I2C_NACK BIT(18)
+#define EDP_INTR_I2C_DEFER BIT(21)
+#define EDP_INTR_PLL_UNLOCKED BIT(24)
+#define EDP_INTR_AUX_ERROR BIT(27)
+
+#define REG_EDP_INTR_STATUS2 (0x00000024)
+#define EDP_INTR_READY_FOR_VIDEO BIT(0)
+#define EDP_INTR_IDLE_PATTERN_SENT BIT(3)
+#define EDP_INTR_FRAME_END BIT(6)
+#define EDP_INTR_CRC_UPDATED BIT(9)
+#define EDP_INTR_SST_FIFO_UNDERFLOW BIT(28)
+
+
+#define REG_EDP_DP_HPD_CTRL (0x00000000)
+#define EDP_DP_HPD_CTRL_HPD_EN (0x00000001)
+
+#define REG_EDP_DP_HPD_INT_STATUS (0x00000004)
+
+#define REG_EDP_DP_HPD_INT_ACK (0x00000008)
+#define EDP_DP_HPD_PLUG_INT_ACK (0x00000001)
+#define EDP_DP_IRQ_HPD_INT_ACK (0x00000002)
+#define EDP_DP_HPD_REPLUG_INT_ACK (0x00000004)
+#define EDP_DP_HPD_UNPLUG_INT_ACK (0x00000008)
+#define EDP_DP_HPD_STATE_STATUS_BITS_MASK (0x0000000F)
+#define EDP_DP_HPD_STATE_STATUS_BITS_SHIFT (0x1C)
+
+#define REG_EDP_DP_HPD_INT_MASK (0x0000000C)
+#define EDP_DP_HPD_PLUG_INT_MASK (0x00000001)
+#define EDP_DP_IRQ_HPD_INT_MASK (0x00000002)
+#define EDP_DP_HPD_REPLUG_INT_MASK (0x00000004)
+#define EDP_DP_HPD_UNPLUG_INT_MASK (0x00000008)
+#define EDP_DP_HPD_INT_MASK (EDP_DP_HPD_PLUG_INT_MASK | \
+ EDP_DP_IRQ_HPD_INT_MASK | \
+ EDP_DP_HPD_REPLUG_INT_MASK | \
+ EDP_DP_HPD_UNPLUG_INT_MASK)
+#define EDP_DP_HPD_STATE_STATUS_CONNECTED (0x40000000)
+#define EDP_DP_HPD_STATE_STATUS_PENDING (0x20000000)
+#define EDP_DP_HPD_STATE_STATUS_DISCONNECTED (0x00000000)
+#define EDP_DP_HPD_STATE_STATUS_MASK (0xE0000000)
+
+#define REG_EDP_DP_HPD_REFTIMER (0x00000018)
+#define EDP_DP_HPD_REFTIMER_ENABLE (1 << 16)
+
+#define REG_EDP_DP_HPD_EVENT_TIME_0 (0x0000001C)
+#define REG_EDP_DP_HPD_EVENT_TIME_1 (0x00000020)
+#define EDP_DP_HPD_EVENT_TIME_0_VAL (0x3E800FA)
+#define EDP_DP_HPD_EVENT_TIME_1_VAL (0x1F407D0)
+
+#define REG_EDP_AUX_CTRL (0x00000030)
+#define EDP_AUX_CTRL_ENABLE (0x00000001)
+#define EDP_AUX_CTRL_RESET (0x00000002)
+
+#define REG_EDP_AUX_DATA (0x00000034)
+#define EDP_AUX_DATA_READ (0x00000001)
+#define EDP_AUX_DATA_DATA__MASK (0x0000ff00)
+#define EDP_AUX_DATA_DATA__SHIFT (8)
+static inline uint32_t EDP_AUX_DATA_DATA(uint32_t val)
+{
+ return ((val) << EDP_AUX_DATA_DATA__SHIFT) & EDP_AUX_DATA_DATA__MASK;
+}
+#define EDP_AUX_DATA_INDEX__MASK (0x00ff0000)
+#define EDP_AUX_DATA_INDEX__SHIFT (16)
+static inline uint32_t EDP_AUX_DATA_INDEX(uint32_t val)
+{
+ return ((val) << EDP_AUX_DATA_INDEX__SHIFT) & EDP_AUX_DATA_INDEX__MASK;
+}
+#define EDP_AUX_DATA_INDEX_WRITE (0x80000000)
+
+#define REG_EDP_AUX_TRANS_CTRL (0x00000038)
+#define EDP_AUX_TRANS_CTRL_I2C (0x00000100)
+#define EDP_AUX_TRANS_CTRL_GO (0x00000200)
+#define EDP_AUX_TRANS_CTRL_NO_SEND_ADDR (0x00000400)
+#define EDP_AUX_TRANS_CTRL_NO_SEND_STOP (0x00000800)
+
+#define REG_EDP_TIMEOUT_COUNT (0x0000003C)
+#define REG_EDP_AUX_LIMITS (0x00000040)
+#define REG_EDP_AUX_STATUS (0x00000044)
+
+#define EDP_INTERRUPT_TRANS_NUM (0x000000A0)
+
+#define REG_EDP_MAINLINK_CTRL (0x00000000)
+#define EDP_MAINLINK_CTRL_ENABLE (0x00000001)
+#define EDP_MAINLINK_CTRL_RESET (0x00000002)
+#define EDP_MAINLINK_CTRL_SW_BYPASS_SCRAMBLER (0x00000010)
+#define EDP_MAINLINK_FB_BOUNDARY_SEL (0x02000000)
+
+#define REG_EDP_STATE_CTRL (0x00000004)
+#define EDP_STATE_CTRL_LINK_TRAINING_PATTERN1 (0x00000001)
+#define EDP_STATE_CTRL_LINK_TRAINING_PATTERN2 (0x00000002)
+#define EDP_STATE_CTRL_LINK_TRAINING_PATTERN3 (0x00000004)
+#define EDP_STATE_CTRL_LINK_TRAINING_PATTERN4 (0x00000008)
+#define EDP_STATE_CTRL_LINK_SYMBOL_ERR_MEASURE (0x00000010)
+#define EDP_STATE_CTRL_LINK_PRBS7 (0x00000020)
+#define EDP_STATE_CTRL_LINK_TEST_CUSTOM_PATTERN (0x00000040)
+#define EDP_STATE_CTRL_SEND_VIDEO (0x00000080)
+#define EDP_STATE_CTRL_PUSH_IDLE (0x00000100)
+
+#define REG_EDP_CONFIGURATION_CTRL (0x00000008)
+#define EDP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK (0x00000001)
+#define EDP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN (0x00000002)
+#define EDP_CONFIGURATION_CTRL_P_INTERLACED (0x00000004)
+#define EDP_CONFIGURATION_CTRL_INTERLACED_BTF (0x00000008)
+#define EDP_CONFIGURATION_CTRL_NUM_OF_LANES (0x00000010)
+#define EDP_CONFIGURATION_CTRL_ENHANCED_FRAMING (0x00000040)
+#define EDP_CONFIGURATION_CTRL_SEND_VSC (0x00000080)
+#define EDP_CONFIGURATION_CTRL_BPC (0x00000100)
+#define EDP_CONFIGURATION_CTRL_ASSR (0x00000400)
+#define EDP_CONFIGURATION_CTRL_RGB_YUV (0x00000800)
+#define EDP_CONFIGURATION_CTRL_LSCLK_DIV (0x00002000)
+#define EDP_CONFIGURATION_CTRL_NUM_OF_LANES_SHIFT (0x04)
+#define EDP_CONFIGURATION_CTRL_BPC_SHIFT (0x08)
+#define EDP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT (0x0D)
+
+#define REG_EDP_SOFTWARE_MVID (0x00000010)
+#define REG_EDP_SOFTWARE_NVID (0x00000018)
+
+#define REG_EDP_TOTAL_HOR_VER (0x0000001C)
+#define EDP_TOTAL_HOR_VER_HORIZ__MASK (0x0000FFFF)
+#define EDP_TOTAL_HOR_VER_HORIZ__SHIFT (0)
+static inline uint32_t EDP_TOTAL_HOR_VER_HORIZ(uint32_t val)
+{
+ return ((val) << EDP_TOTAL_HOR_VER_HORIZ__SHIFT) & EDP_TOTAL_HOR_VER_HORIZ__MASK;
+}
+#define EDP_TOTAL_HOR_VER_VERT__MASK (0xffff0000)
+#define EDP_TOTAL_HOR_VER_VERT__SHIFT (16)
+static inline uint32_t EDP_TOTAL_HOR_VER_VERT(uint32_t val)
+{
+ return ((val) << EDP_TOTAL_HOR_VER_VERT__SHIFT) & EDP_TOTAL_HOR_VER_VERT__MASK;
+}
+
+#define REG_EDP_START_HOR_VER_FROM_SYNC (0x00000020)
+#define EDP_START_HOR_VER_FROM_SYNC_HORIZ__MASK (0x0000ffff)
+#define EDP_START_HOR_VER_FROM_SYNC_HORIZ__SHIFT (0)
+static inline uint32_t EDP_START_HOR_VER_FROM_SYNC_HORIZ(uint32_t val)
+{
+ return ((val) << EDP_START_HOR_VER_FROM_SYNC_HORIZ__SHIFT) &
+ EDP_START_HOR_VER_FROM_SYNC_HORIZ__MASK;
+}
+#define EDP_START_HOR_VER_FROM_SYNC_VERT__MASK (0xffff0000)
+#define EDP_START_HOR_VER_FROM_SYNC_VERT__SHIFT (16)
+static inline uint32_t EDP_START_HOR_VER_FROM_SYNC_VERT(uint32_t val)
+{
+ return ((val) << EDP_START_HOR_VER_FROM_SYNC_VERT__SHIFT) &
+ EDP_START_HOR_VER_FROM_SYNC_VERT__MASK;
+}
+
+#define REG_EDP_HSYNC_VSYNC_WIDTH_POLARITY (0x00000024)
+#define EDP_HSYNC_VSYNC_WIDTH_POLARITY_HORIZ__MASK (0x00007fff)
+#define EDP_HSYNC_VSYNC_WIDTH_POLARITY_HORIZ__SHIFT (0)
+static inline uint32_t EDP_HSYNC_VSYNC_WIDTH_POLARITY_HORIZ(uint32_t val)
+{
+ return ((val) << EDP_HSYNC_VSYNC_WIDTH_POLARITY_HORIZ__SHIFT) &
+ EDP_HSYNC_VSYNC_WIDTH_POLARITY_HORIZ__MASK;
+}
+#define EDP_HSYNC_VSYNC_WIDTH_POLARITY_NHSYNC (0x00008000)
+#define EDP_HSYNC_VSYNC_WIDTH_POLARITY_VERT__MASK (0x7fff0000)
+#define EDP_HSYNC_VSYNC_WIDTH_POLARITY_VERT__SHIFT (16)
+static inline uint32_t EDP_HSYNC_VSYNC_WIDTH_POLARITY_VERT(uint32_t val)
+{
+ return ((val) << EDP_HSYNC_VSYNC_WIDTH_POLARITY_VERT__SHIFT) &
+ EDP_HSYNC_VSYNC_WIDTH_POLARITY_VERT__MASK;
+}
+#define EDP_HSYNC_VSYNC_WIDTH_POLARITY_NVSYNC (0x80000000)
+
+#define REG_EDP_ACTIVE_HOR_VER (0x00000028)
+#define EDP_ACTIVE_HOR_VER_HORIZ__MASK (0x0000ffff)
+#define EDP_ACTIVE_HOR_VER_HORIZ__SHIFT (0)
+static inline uint32_t EDP_ACTIVE_HOR_VER_HORIZ(uint32_t val)
+{
+ return ((val) << EDP_ACTIVE_HOR_VER_HORIZ__SHIFT) & EDP_ACTIVE_HOR_VER_HORIZ__MASK;
+}
+#define EDP_ACTIVE_HOR_VER_VERT__MASK (0xffff0000)
+#define EDP_ACTIVE_HOR_VER_VERT__SHIFT (16)
+static inline uint32_t EDP_ACTIVE_HOR_VER_VERT(uint32_t val)
+{
+ return ((val) << EDP_ACTIVE_HOR_VER_VERT__SHIFT) & EDP_ACTIVE_HOR_VER_VERT__MASK;
+}
+
+
+#define REG_EDP_MISC1_MISC0 (0x0000002C)
+#define EDP_MISC0_SYNCHRONOUS_CLK (0x00000001)
+#define EDP_MISC0_COLORIMETRY_CFG_SHIFT (0x00000001)
+#define EDP_MISC0_TEST_BITS_DEPTH_SHIFT (0x00000005)
+
+#define REG_EDP_VALID_BOUNDARY (0x00000030)
+#define REG_EDP_VALID_BOUNDARY_2 (0x00000034)
+
+#define REG_EDP_LOGICAL2PHYSICAL_LANE_MAPPING (0x00000038)
+#define LANE0_MAPPING_SHIFT (0x00000000)
+#define LANE1_MAPPING_SHIFT (0x00000002)
+#define LANE2_MAPPING_SHIFT (0x00000004)
+#define LANE3_MAPPING_SHIFT (0x00000006)
+
+#define REG_EDP_MAINLINK_READY (0x00000040)
+#define EDP_MAINLINK_READY_FOR_VIDEO (0x00000001)
+#define EDP_MAINLINK_READY_TRAIN_PATTERN_1_READY (0x00000008)
+
+#define REG_EDP_MAINLINK_LEVELS (0x00000044)
+#define EDP_MAINLINK_SAFE_TO_EXIT_LEVEL_2 (0x00000002)
+
+#define REG_EDP_TU (0x0000004C)
+
+/* PCLK registers */
+#define REG_EDP_DSC_DTO (0x0000007C)
+
+/* PHY registers */
+#define EDP_PHY_CFG (0x00000010)
+#define EDP_PHY_CFG_1 (0x00000014)
+#define EDP_PHY_PD_CTL (0x0000001C)
+#define EDP_PHY_MODE (0x00000020)
+#define EDP_PHY_AUX_CFG0 (0x00000024)
+#define EDP_PHY_AUX_CFG1 (0x00000028)
+#define EDP_PHY_AUX_CFG2 (0x0000002C)
+#define EDP_PHY_AUX_CFG3 (0x00000030)
+#define EDP_PHY_AUX_CFG4 (0x00000034)
+#define EDP_PHY_AUX_CFG5 (0x00000038)
+#define EDP_PHY_AUX_CFG6 (0x0000003C)
+#define EDP_PHY_AUX_CFG7 (0x00000040)
+#define EDP_PHY_AUX_CFG8 (0x00000044)
+#define EDP_PHY_AUX_CFG9 (0x00000048)
+#define EDP_PHY_AUX_INTERRUPT_MASK (0x00000058)
+#define EDP_PHY_VCO_DIV (0x00000074)
+#define EDP_PHY_TX0_TX1_LANE_CTL (0x0000007C)
+#define EDP_PHY_TX2_TX3_LANE_CTL (0x000000A0)
+#define EDP_PHY_SPARE0 (0x000000CC)
+#define EDP_PHY_STATUS (0x000000E0)
+
+
+/* Tx registers */
+#define TXn_CLKBUF_ENABLE (0x00000000)
+#define TXn_TX_EMP_POST1_LVL (0x00000004)
+#define TXn_TX_DRV_LVL (0x00000014)
+#define TXn_TX_DRV_LVL_OFFSET (0x00000018)
+#define TXn_RESET_TSYNC_EN (0x0000001C)
+#define TXn_TX_BAND (0x00000028)
+#define TXn_RES_CODE_LANE_OFFSET_TX0 (0x00000044)
+#define TXn_RES_CODE_LANE_OFFSET_TX1 (0x00000048)
+#define TXn_TRANSCEIVER_BIAS_EN (0x00000054)
+#define TXn_HIGHZ_DRVR_EN (0x00000058)
+#define TXn_TX_POL_INV (0x0000005C)
+#define TXn_LANE_MODE_1 (0x00000064)
+#define TXn_TRAN_DRVR_EMP_EN (0x00000078)
+#define TXn_LDO_CONFIG (0x00000084)
+
+
+/* PLL registers */
+#define QSERDES_COM_BG_TIMER (0x0000000C)
+#define QSERDES_COM_SSC_EN_CENTER (0x00000010)
+#define QSERDES_COM_SSC_ADJ_PER1 (0x00000014)
+#define QSERDES_COM_SSC_ADJ_PER2 (0x00000018)
+#define QSERDES_COM_SSC_PER1 (0x0000001C)
+#define QSERDES_COM_SSC_PER2 (0x00000020)
+#define QSERDES_COM_SSC_STEP_SIZE1_MODE0 (0x00000024)
+#define QSERDES_COM_SSC_STEP_SIZE2_MODE0 (0x00000028)
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN (0x00000044)
+#define QSERDES_COM_CLK_ENABLE1 (0x00000048)
+#define QSERDES_COM_SYS_CLK_CTRL (0x0000004C)
+#define QSERDES_COM_SYSCLK_BUF_ENABLE (0x00000050)
+#define QSERDES_COM_PLL_IVCO (0x00000058)
+#define QSERDES_COM_CP_CTRL_MODE0 (0x00000074)
+#define QSERDES_COM_PLL_RCTRL_MODE0 (0x0000007C)
+#define QSERDES_COM_PLL_CCTRL_MODE0 (0x00000084)
+#define QSERDES_COM_SYSCLK_EN_SEL (0x00000094)
+#define QSERDES_COM_RESETSM_CNTRL (0x0000009C)
+#define QSERDES_COM_LOCK_CMP_EN (0x000000A4)
+#define QSERDES_COM_LOCK_CMP1_MODE0 (0x000000AC)
+#define QSERDES_COM_LOCK_CMP2_MODE0 (0x000000B0)
+#define QSERDES_COM_DEC_START_MODE0 (0x000000BC)
+#define QSERDES_COM_DIV_FRAC_START1_MODE0 (0x000000CC)
+#define QSERDES_COM_DIV_FRAC_START2_MODE0 (0x000000D0)
+#define QSERDES_COM_DIV_FRAC_START3_MODE0 (0x000000D4)
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 (0x000000EC)
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 (0x000000F0)
+#define QSERDES_COM_VCO_TUNE_CTRL (0x00000108)
+#define QSERDES_COM_VCO_TUNE_MAP (0x0000010C)
+#define QSERDES_COM_VCO_TUNE1_MODE0 (0x00000110)
+#define QSERDES_COM_VCO_TUNE2_MODE0 (0x00000114)
+#define QSERDES_COM_CMN_STATUS (0x00000140)
+#define QSERDES_COM_CLK_SEL (0x00000154)
+#define QSERDES_COM_HSCLK_SEL (0x00000158)
+#define QSERDES_COM_CORECLK_DIV_MODE0 (0x00000168)
+#define QSERDES_COM_CORE_CLK_EN (0x00000174)
+#define QSERDES_COM_C_READY_STATUS (0x00000178)
+#define QSERDES_COM_CMN_CONFIG (0x0000017C)
+#define QSERDES_COM_SVS_MODE_CLK_SEL (0x00000184)
+
+
+#define DP_PHY_PLL_POLL_SLEEP_US (500)
+#define DP_PHY_PLL_POLL_TIMEOUT_US (10000)
+
+
+#define EDP_VCO_RATE_8100MHZDIV1000 (8100000UL)
+#define EDP_VCO_RATE_8640MHZDIV1000 (8640000UL)
+#define EDP_VCO_RATE_9720MHZDIV1000 (9720000UL)
+#define EDP_VCO_RATE_10800MHZDIV1000 (10800000UL)
+#define EDP_VCO_RATE_11880MHZDIV1000 (11880000UL)
+
+#endif /* EDP_V510_REG */
The eDP controller and phy hardware for the next generation snapdragon platform has a different desgin compared to their earlier generations. So, there is a need to implement new sequences in driver software for the new hardware. This change will implement the required seuqences to enable eDP interface on the next generation snapdragon platforms. Signed-off-by: Sankeerth Billakanti <sbillaka@codeaurora.org> --- drivers/gpu/drm/msm/Makefile | 6 + drivers/gpu/drm/msm/edp/edp_common.c | 4 + drivers/gpu/drm/msm/edp/edp_common.h | 6 + drivers/gpu/drm/msm/edp/v510/edp_v510.c | 220 +++ drivers/gpu/drm/msm/edp/v510/edp_v510.h | 151 ++ drivers/gpu/drm/msm/edp/v510/edp_v510_aux.c | 268 ++++ drivers/gpu/drm/msm/edp/v510/edp_v510_bridge.c | 111 ++ drivers/gpu/drm/msm/edp/v510/edp_v510_connector.c | 117 ++ drivers/gpu/drm/msm/edp/v510/edp_v510_ctrl.c | 1583 +++++++++++++++++++++ drivers/gpu/drm/msm/edp/v510/edp_v510_phy.c | 641 +++++++++ drivers/gpu/drm/msm/edp/v510/edp_v510_reg.h | 339 +++++ 11 files changed, 3446 insertions(+) create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510.c create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510.h create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510_aux.c create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510_bridge.c create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510_connector.c create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510_ctrl.c create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510_phy.c create mode 100644 drivers/gpu/drm/msm/edp/v510/edp_v510_reg.h