drm: msm: dsi-staging: add support for DSI clock manager
Add support for DSI clock manager driver. This includes
refactor of the DSI power module, ULPS, PHY clamping,
DSI controller/PHY re-initialisation, and adds support
for enable/disable of DSI clocks during static screen.
The DSI clock manager maintains three state of the DSI
clocks namely ON/OFF/ECG.
CRs-Fixed: 2008002
Change-Id: I15bcf8531be6596c4a9dc52690c4971de46f387d
Signed-off-by: Padmanabhan Komanduru <pkomandu@codeaurora.org>
Signed-off-by: Shashank Babu Chinta Venkata <sbchin@codeaurora.org>
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 28c01c6..5c29e67 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -95,7 +95,7 @@
msm_drm-$(CONFIG_DRM_MSM_DSI_28NM_8960_PHY) += dsi/pll/dsi_pll_28nm_8960.o
endif
msm_drm-$(CONFIG_DRM_MSM_DSI_STAGING) += dsi-staging/dsi_phy.o \
- dsi-staging/dsi_clk_pwr.o \
+ dsi-staging/dsi_pwr.o \
dsi-staging/dsi_phy.o \
dsi-staging/dsi_phy_hw_v4_0.o \
dsi-staging/dsi_ctrl_hw_1_4.o \
@@ -104,6 +104,7 @@
dsi-staging/dsi_drm.o \
dsi-staging/dsi_display.o \
dsi-staging/dsi_panel.o \
+ dsi-staging/dsi_clk_manager.o \
dsi-staging/dsi_display_test.o
msm_drm-$(CONFIG_DRM_MSM_DSI_PLL) += dsi/pll/dsi_pll.o \
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
index 06027a9..07503f5 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -37,10 +37,10 @@
ctrl->ops.kickoff_fifo_command = dsi_ctrl_hw_14_kickoff_fifo_command;
ctrl->ops.reset_cmd_fifo = dsi_ctrl_hw_14_reset_cmd_fifo;
ctrl->ops.trigger_command_dma = dsi_ctrl_hw_14_trigger_command_dma;
- ctrl->ops.ulps_request = dsi_ctrl_hw_14_ulps_request;
- ctrl->ops.ulps_exit = dsi_ctrl_hw_14_ulps_exit;
- ctrl->ops.clear_ulps_request = dsi_ctrl_hw_14_clear_ulps_request;
- ctrl->ops.get_lanes_in_ulps = dsi_ctrl_hw_14_get_lanes_in_ulps;
+ ctrl->ops.ulps_ops.ulps_request = dsi_ctrl_hw_14_ulps_request;
+ ctrl->ops.ulps_ops.ulps_exit = dsi_ctrl_hw_14_ulps_exit;
+ ctrl->ops.ulps_ops.wait_for_lane_idle = dsi_ctrl_hw_wait_for_lane_idle;
+ ctrl->ops.ulps_ops.get_lanes_in_ulps = dsi_ctrl_hw_14_get_lanes_in_ulps;
ctrl->ops.clamp_enable = dsi_ctrl_hw_14_clamp_enable;
ctrl->ops.clamp_disable = dsi_ctrl_hw_14_clamp_disable;
ctrl->ops.get_interrupt_status = dsi_ctrl_hw_14_get_interrupt_status;
@@ -125,6 +125,8 @@
phy->ops.disable = dsi_phy_hw_v4_0_disable;
phy->ops.calculate_timing_params =
dsi_phy_hw_v4_0_calculate_timing_params;
+ phy->ops.phy_idle_on = dsi_phy_hw_v4_0_idle_on;
+ phy->ops.phy_idle_off = dsi_phy_hw_v4_0_idle_off;
}
/**
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
index 98bd9b0..03bf905 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_catalog.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -57,6 +57,8 @@
struct dsi_host_common_cfg *cfg,
struct dsi_phy_per_lane_cfgs
*timing);
+void dsi_phy_hw_v4_0_idle_on(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
+void dsi_phy_hw_v4_0_idle_off(struct dsi_phy_hw *phy);
/* Definitions for 1.4 controller hardware driver */
void dsi_ctrl_hw_14_host_setup(struct dsi_ctrl_hw *ctrl,
@@ -94,10 +96,9 @@
u32 flags);
void dsi_ctrl_hw_14_reset_cmd_fifo(struct dsi_ctrl_hw *ctrl);
void dsi_ctrl_hw_14_trigger_command_dma(struct dsi_ctrl_hw *ctrl);
-
+int dsi_ctrl_hw_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes);
void dsi_ctrl_hw_14_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes);
void dsi_ctrl_hw_14_ulps_exit(struct dsi_ctrl_hw *ctrl, u32 lanes);
-void dsi_ctrl_hw_14_clear_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes);
u32 dsi_ctrl_hw_14_get_lanes_in_ulps(struct dsi_ctrl_hw *ctrl);
void dsi_ctrl_hw_14_clamp_enable(struct dsi_ctrl_hw *ctrl,
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h b/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h
new file mode 100644
index 0000000..1ffd9ac
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_clk.h
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DSI_CLK_H_
+#define _DSI_CLK_H_
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/clk.h>
+
+#define MAX_STRING_LEN 32
+#define MAX_DSI_CTRL 2
+
+enum dsi_clk_state {
+ DSI_CLK_OFF,
+ DSI_CLK_ON,
+ DSI_CLK_EARLY_GATE,
+};
+
+enum clk_req_client {
+ DSI_CLK_REQ_MDP_CLIENT = 0,
+ DSI_CLK_REQ_DSI_CLIENT,
+};
+
+enum dsi_link_clk_type {
+ DSI_LINK_ESC_CLK,
+ DSI_LINK_BYTE_CLK,
+ DSI_LINK_PIX_CLK,
+ DSI_LINK_CLK_MAX,
+};
+
+enum dsi_clk_type {
+ DSI_CORE_CLK = BIT(0),
+ DSI_LINK_CLK = BIT(1),
+ DSI_ALL_CLKS = (BIT(0) | BIT(1)),
+ DSI_CLKS_MAX = BIT(2),
+};
+
+struct dsi_clk_ctrl_info {
+ enum dsi_clk_type clk_type;
+ enum dsi_clk_state clk_state;
+ enum clk_req_client client;
+};
+
+struct clk_ctrl_cb {
+ void *priv;
+ int (*dsi_clk_cb)(void *priv, struct dsi_clk_ctrl_info clk_ctrl_info);
+};
+
+/**
+ * struct dsi_core_clk_info - Core clock information for DSI hardware
+ * @mdp_core_clk: Handle to MDP core clock.
+ * @iface_clk: Handle to MDP interface clock.
+ * @core_mmss_clk: Handle to MMSS core clock.
+ * @bus_clk: Handle to bus clock.
+ */
+struct dsi_core_clk_info {
+ struct clk *mdp_core_clk;
+ struct clk *iface_clk;
+ struct clk *core_mmss_clk;
+ struct clk *bus_clk;
+};
+
+/**
+ * struct dsi_link_clk_info - Link clock information for DSI hardware.
+ * @byte_clk: Handle to DSI byte clock.
+ * @pixel_clk: Handle to DSI pixel clock.
+ * @esc_clk: Handle to DSI escape clock.
+ */
+struct dsi_link_clk_info {
+ struct clk *byte_clk;
+ struct clk *pixel_clk;
+ struct clk *esc_clk;
+};
+
+/**
+ * struct link_clk_freq - Clock frequency information for Link clocks
+ * @byte_clk_rate: Frequency of DSI byte clock in KHz.
+ * @pixel_clk_rate: Frequency of DSI pixel clock in KHz.
+ * @esc_clk_rate: Frequency of DSI escape clock in KHz.
+ */
+struct link_clk_freq {
+ u32 byte_clk_rate;
+ u32 pix_clk_rate;
+ u32 esc_clk_rate;
+};
+
+/**
+ * typedef *pre_clockoff_cb() - Callback before clock is turned off
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned off.
+ * @new_state: next state for the clock.
+ *
+ * @return: error code.
+ */
+typedef int (*pre_clockoff_cb)(void *priv,
+ enum dsi_clk_type clk_type,
+ enum dsi_clk_state new_state);
+
+/**
+ * typedef *post_clockoff_cb() - Callback after clock is turned off
+ * @priv: private data pointer.
+ * @clk_type: clock which was turned off.
+ * @curr_state: current state for the clock.
+ *
+ * @return: error code.
+ */
+typedef int (*post_clockoff_cb)(void *priv,
+ enum dsi_clk_type clk_type,
+ enum dsi_clk_state curr_state);
+
+/**
+ * typedef *post_clockon_cb() - Callback after clock is turned on
+ * @priv: private data pointer.
+ * @clk_type: clock which was turned on.
+ * @curr_state: current state for the clock.
+ *
+ * @return: error code.
+ */
+typedef int (*post_clockon_cb)(void *priv,
+ enum dsi_clk_type clk_type,
+ enum dsi_clk_state curr_state);
+
+/**
+ * typedef *pre_clockon_cb() - Callback before clock is turned on
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned on.
+ * @new_state: next state for the clock.
+ *
+ * @return: error code.
+ */
+typedef int (*pre_clockon_cb)(void *priv,
+ enum dsi_clk_type clk_type,
+ enum dsi_clk_state new_state);
+
+
+struct dsi_clk_info {
+ char name[MAX_STRING_LEN];
+ struct dsi_core_clk_info c_clks[MAX_DSI_CTRL];
+ struct dsi_link_clk_info l_clks[MAX_DSI_CTRL];
+ u32 bus_handle[MAX_DSI_CTRL];
+ pre_clockoff_cb pre_clkoff_cb;
+ post_clockoff_cb post_clkoff_cb;
+ post_clockon_cb post_clkon_cb;
+ pre_clockon_cb pre_clkon_cb;
+ void *priv_data;
+ u32 master_ndx;
+ u32 dsi_ctrl_count;
+};
+
+/**
+ * struct dsi_clk_link_set - Pair of clock handles to describe link clocks
+ * @byte_clk: Handle to DSi byte clock.
+ * @pixel_clk: Handle to DSI pixel clock.
+ */
+struct dsi_clk_link_set {
+ struct clk *byte_clk;
+ struct clk *pixel_clk;
+};
+
+/**
+ * dsi_display_clk_mgr_register() - Register DSI clock manager
+ * @info: Structure containing DSI clock information
+ */
+void *dsi_display_clk_mngr_register(struct dsi_clk_info *info);
+
+/**
+ * dsi_display_clk_mngr_deregister() - Deregister DSI clock manager
+ * @clk_mngr: DSI clock manager pointer
+ */
+int dsi_display_clk_mngr_deregister(void *clk_mngr);
+
+/**
+ * dsi_register_clk_handle() - Register clock handle with DSI clock manager
+ * @clk_mngr: DSI clock manager pointer
+ * @client: DSI clock client pointer.
+ */
+void *dsi_register_clk_handle(void *clk_mngr, char *client);
+
+/**
+ * dsi_deregister_clk_handle() - Deregister clock handle from DSI clock manager
+ * @client: DSI clock client pointer.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_deregister_clk_handle(void *client);
+
+/**
+ * dsi_display_clk_ctrl() - set frequencies for link clks
+ * @handle: Handle of desired DSI clock client.
+ * @clk_type: Clock which is being controlled.
+ * @clk_state: Desired state of clock
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_display_clk_ctrl(void *handle,
+ enum dsi_clk_type clk_type, enum dsi_clk_state clk_state);
+
+/**
+ * dsi_clk_set_link_frequencies() - set frequencies for link clks
+ * @client: DSI clock client pointer.
+ * @freq: Structure containing link clock frequencies.
+ * @index: Index of the DSI controller.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_link_frequencies(void *client, struct link_clk_freq freq,
+ u32 index);
+
+
+/**
+ * dsi_clk_set_pixel_clk_rate() - set frequency for pixel clock
+ * @client: DSI clock client pointer.
+ * @pixel_clk: Pixel clock rate in Hz.
+ * @index: Index of the DSI controller.
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_pixel_clk_rate(void *client, u64 pixel_clk, u32 index);
+
+
+/**
+ * dsi_clk_set_byte_clk_rate() - set frequency for byte clock
+ * @client: DSI clock client pointer.
+ * @byte_clk: Pixel clock rate in Hz.
+ * @index: Index of the DSI controller.
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index);
+
+/**
+ * dsi_clk_update_parent() - update parent clocks for specified clock
+ * @parent: link clock pair which are set as parent.
+ * @child: link clock pair whose parent has to be set.
+ */
+int dsi_clk_update_parent(struct dsi_clk_link_set *parent,
+ struct dsi_clk_link_set *child);
+#endif /* _DSI_CLK_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c
new file mode 100644
index 0000000..4614f08
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_manager.c
@@ -0,0 +1,1081 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/msm-bus.h>
+#include "dsi_clk.h"
+
+struct dsi_core_clks {
+ struct dsi_core_clk_info clks;
+ u32 bus_handle;
+};
+
+struct dsi_link_clks {
+ struct dsi_link_clk_info clks;
+ struct link_clk_freq freq;
+};
+
+struct dsi_clk_mngr {
+ char name[MAX_STRING_LEN];
+ struct mutex clk_mutex;
+ struct list_head client_list;
+
+ u32 dsi_ctrl_count;
+ u32 master_ndx;
+ struct dsi_core_clks core_clks[MAX_DSI_CTRL];
+ struct dsi_link_clks link_clks[MAX_DSI_CTRL];
+ u32 core_clk_state;
+ u32 link_clk_state;
+
+ pre_clockoff_cb pre_clkoff_cb;
+ post_clockoff_cb post_clkoff_cb;
+ post_clockon_cb post_clkon_cb;
+ pre_clockon_cb pre_clkon_cb;
+
+ void *priv_data;
+};
+
+struct dsi_clk_client_info {
+ char name[MAX_STRING_LEN];
+ u32 core_refcount;
+ u32 link_refcount;
+ u32 core_clk_state;
+ u32 link_clk_state;
+ struct list_head list;
+ struct dsi_clk_mngr *mngr;
+};
+
+/**
+ * dsi_clk_set_link_frequencies() - set frequencies for link clks
+ * @clks: Link clock information
+ * @pixel_clk: pixel clock frequency in KHz.
+ * @byte_clk: Byte clock frequency in KHz.
+ * @esc_clk: Escape clock frequency in KHz.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_link_frequencies(void *client, struct link_clk_freq freq,
+ u32 index)
+{
+ int rc = 0;
+ struct dsi_clk_client_info *c = client;
+ struct dsi_clk_mngr *mngr;
+
+ if (!client) {
+ pr_err("invalid params\n");
+ return -EINVAL;
+ }
+
+ mngr = c->mngr;
+ memcpy(&mngr->link_clks[index].freq, &freq,
+ sizeof(struct link_clk_freq));
+ return rc;
+}
+
+/**
+ * dsi_clk_set_pixel_clk_rate() - set frequency for pixel clock
+ * @clks: DSI link clock information.
+ * @pixel_clk: Pixel clock rate in KHz.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_pixel_clk_rate(void *client, u64 pixel_clk, u32 index)
+{
+ int rc = 0;
+ struct dsi_clk_client_info *c = client;
+ struct dsi_clk_mngr *mngr;
+
+ mngr = c->mngr;
+ rc = clk_set_rate(mngr->link_clks[index].clks.pixel_clk, pixel_clk);
+ if (rc)
+ pr_err("failed to set clk rate for pixel clk, rc=%d\n", rc);
+ else
+ mngr->link_clks[index].freq.pix_clk_rate = pixel_clk;
+
+ return rc;
+}
+
+/**
+ * dsi_clk_set_byte_clk_rate() - set frequency for byte clock
+ * @client: DSI clock client pointer.
+ * @byte_clk: Pixel clock rate in Hz.
+ * @index: Index of the DSI controller.
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_clk_set_byte_clk_rate(void *client, u64 byte_clk, u32 index)
+{
+ int rc = 0;
+ struct dsi_clk_client_info *c = client;
+ struct dsi_clk_mngr *mngr;
+
+ mngr = c->mngr;
+ rc = clk_set_rate(mngr->link_clks[index].clks.byte_clk, byte_clk);
+ if (rc)
+ pr_err("failed to set clk rate for byte clk, rc=%d\n", rc);
+ else
+ mngr->link_clks[index].freq.byte_clk_rate = byte_clk;
+
+ return rc;
+
+}
+
+/**
+ * dsi_clk_update_parent() - update parent clocks for specified clock
+ * @parent: link clock pair which are set as parent.
+ * @child: link clock pair whose parent has to be set.
+ */
+int dsi_clk_update_parent(struct dsi_clk_link_set *parent,
+ struct dsi_clk_link_set *child)
+{
+ int rc = 0;
+
+ rc = clk_set_parent(child->byte_clk, parent->byte_clk);
+ if (rc) {
+ pr_err("failed to set byte clk parent\n");
+ goto error;
+ }
+
+ rc = clk_set_parent(child->pixel_clk, parent->pixel_clk);
+ if (rc) {
+ pr_err("failed to set pixel clk parent\n");
+ goto error;
+ }
+error:
+ return rc;
+}
+
+int dsi_core_clk_start(struct dsi_core_clks *c_clks)
+{
+ int rc = 0;
+
+ rc = clk_prepare_enable(c_clks->clks.mdp_core_clk);
+ if (rc) {
+ pr_err("failed to enable mdp_core_clk, rc=%d\n", rc);
+ goto error;
+ }
+
+ rc = clk_prepare_enable(c_clks->clks.iface_clk);
+ if (rc) {
+ pr_err("failed to enable iface_clk, rc=%d\n", rc);
+ goto error_disable_core_clk;
+ }
+
+ rc = clk_prepare_enable(c_clks->clks.bus_clk);
+ if (rc) {
+ pr_err("failed to enable bus_clk, rc=%d\n", rc);
+ goto error_disable_iface_clk;
+ }
+
+ rc = clk_prepare_enable(c_clks->clks.core_mmss_clk);
+ if (rc) {
+ pr_err("failed to enable core_mmss_clk, rc=%d\n", rc);
+ goto error_disable_bus_clk;
+ }
+
+ rc = msm_bus_scale_client_update_request(c_clks->bus_handle, 1);
+ if (rc) {
+ pr_err("bus scale client enable failed, rc=%d\n", rc);
+ goto error_disable_mmss_clk;
+ }
+
+ return rc;
+
+error_disable_mmss_clk:
+ clk_disable_unprepare(c_clks->clks.core_mmss_clk);
+error_disable_bus_clk:
+ clk_disable_unprepare(c_clks->clks.bus_clk);
+error_disable_iface_clk:
+ clk_disable_unprepare(c_clks->clks.iface_clk);
+error_disable_core_clk:
+ clk_disable_unprepare(c_clks->clks.mdp_core_clk);
+error:
+ return rc;
+}
+
+int dsi_core_clk_stop(struct dsi_core_clks *c_clks)
+{
+ if (msm_bus_scale_client_update_request(c_clks->bus_handle, 0))
+ pr_err("bus scale client disable failed\n");
+ clk_disable_unprepare(c_clks->clks.core_mmss_clk);
+ clk_disable_unprepare(c_clks->clks.bus_clk);
+ clk_disable_unprepare(c_clks->clks.iface_clk);
+ clk_disable_unprepare(c_clks->clks.mdp_core_clk);
+
+ return 0;
+}
+
+static int dsi_link_clk_set_rate(struct dsi_link_clks *l_clks)
+{
+ int rc = 0;
+
+ rc = clk_set_rate(l_clks->clks.esc_clk, l_clks->freq.esc_clk_rate);
+ if (rc) {
+ pr_err("clk_set_rate failed for esc_clk rc = %d\n", rc);
+ goto error;
+ }
+
+ rc = clk_set_rate(l_clks->clks.byte_clk, l_clks->freq.byte_clk_rate);
+ if (rc) {
+ pr_err("clk_set_rate failed for byte_clk rc = %d\n", rc);
+ goto error;
+ }
+
+ rc = clk_set_rate(l_clks->clks.pixel_clk, l_clks->freq.pix_clk_rate);
+ if (rc) {
+ pr_err("clk_set_rate failed for pixel_clk rc = %d\n", rc);
+ goto error;
+ }
+error:
+ return rc;
+}
+
+static int dsi_link_clk_prepare(struct dsi_link_clks *l_clks)
+{
+ int rc = 0;
+
+ rc = clk_prepare(l_clks->clks.esc_clk);
+ if (rc) {
+ pr_err("Failed to prepare dsi esc clk, rc=%d\n", rc);
+ goto esc_clk_err;
+ }
+
+ rc = clk_prepare(l_clks->clks.byte_clk);
+ if (rc) {
+ pr_err("Failed to prepare dsi byte clk, rc=%d\n", rc);
+ goto byte_clk_err;
+ }
+
+ rc = clk_prepare(l_clks->clks.pixel_clk);
+ if (rc) {
+ pr_err("Failed to prepare dsi pixel clk, rc=%d\n", rc);
+ goto pixel_clk_err;
+ }
+
+ return rc;
+
+pixel_clk_err:
+ clk_unprepare(l_clks->clks.byte_clk);
+byte_clk_err:
+ clk_unprepare(l_clks->clks.esc_clk);
+esc_clk_err:
+ return rc;
+}
+
+static void dsi_link_clk_unprepare(struct dsi_link_clks *l_clks)
+{
+ clk_unprepare(l_clks->clks.pixel_clk);
+ clk_unprepare(l_clks->clks.byte_clk);
+ clk_unprepare(l_clks->clks.esc_clk);
+}
+
+static int dsi_link_clk_enable(struct dsi_link_clks *l_clks)
+{
+ int rc = 0;
+
+ rc = clk_enable(l_clks->clks.esc_clk);
+ if (rc) {
+ pr_err("Failed to enable dsi esc clk, rc=%d\n", rc);
+ goto esc_clk_err;
+ }
+
+ rc = clk_enable(l_clks->clks.byte_clk);
+ if (rc) {
+ pr_err("Failed to enable dsi byte clk, rc=%d\n", rc);
+ goto byte_clk_err;
+ }
+
+ rc = clk_enable(l_clks->clks.pixel_clk);
+ if (rc) {
+ pr_err("Failed to enable dsi pixel clk, rc=%d\n", rc);
+ goto pixel_clk_err;
+ }
+
+ return rc;
+
+pixel_clk_err:
+ clk_disable(l_clks->clks.byte_clk);
+byte_clk_err:
+ clk_disable(l_clks->clks.esc_clk);
+esc_clk_err:
+ return rc;
+}
+
+static void dsi_link_clk_disable(struct dsi_link_clks *l_clks)
+{
+ clk_disable(l_clks->clks.esc_clk);
+ clk_disable(l_clks->clks.pixel_clk);
+ clk_disable(l_clks->clks.byte_clk);
+}
+
+/**
+ * dsi_link_clk_start() - enable dsi link clocks
+ */
+int dsi_link_clk_start(struct dsi_link_clks *clks)
+{
+ int rc = 0;
+
+ rc = dsi_link_clk_set_rate(clks);
+ if (rc) {
+ pr_err("failed to set clk rates, rc = %d\n", rc);
+ goto error;
+ }
+
+ rc = dsi_link_clk_prepare(clks);
+ if (rc) {
+ pr_err("failed to prepare link clks, rc = %d\n", rc);
+ goto error;
+ }
+
+ rc = dsi_link_clk_enable(clks);
+ if (rc) {
+ pr_err("failed to enable link clks, rc = %d\n", rc);
+ goto error_unprepare;
+ }
+
+ pr_debug("Link clocks are enabled\n");
+ return rc;
+error_unprepare:
+ dsi_link_clk_unprepare(clks);
+error:
+ return rc;
+}
+
+/**
+ * dsi_link_clk_stop() - Stop DSI link clocks.
+ */
+int dsi_link_clk_stop(struct dsi_link_clks *clks)
+{
+ dsi_link_clk_disable(clks);
+ dsi_link_clk_unprepare(clks);
+
+ pr_debug("Link clocks disabled\n");
+
+ return 0;
+}
+
+static int dsi_display_core_clk_enable(struct dsi_core_clks *clks,
+ u32 ctrl_count, u32 master_ndx)
+{
+ int rc = 0;
+ int i;
+ struct dsi_core_clks *clk, *m_clks;
+
+ /*
+ * In case of split DSI usecases, the clock for master controller should
+ * be enabled before the other controller. Master controller in the
+ * clock context refers to the controller that sources the clock.
+ */
+
+ m_clks = &clks[master_ndx];
+
+ rc = dsi_core_clk_start(m_clks);
+ if (rc) {
+ pr_err("failed to turn on master clocks, rc=%d\n", rc);
+ goto error;
+ }
+
+ /* Turn on rest of the core clocks */
+ for (i = 0; i < ctrl_count; i++) {
+ clk = &clks[i];
+ if (!clk || (clk == m_clks))
+ continue;
+
+ rc = dsi_core_clk_start(clk);
+ if (rc) {
+ pr_err("failed to turn on clocks, rc=%d\n", rc);
+ goto error_disable_master;
+ }
+ }
+ return rc;
+error_disable_master:
+ (void)dsi_core_clk_stop(m_clks);
+error:
+ return rc;
+}
+
+static int dsi_display_link_clk_enable(struct dsi_link_clks *clks,
+ u32 ctrl_count, u32 master_ndx)
+{
+ int rc = 0;
+ int i;
+ struct dsi_link_clks *clk, *m_clks;
+
+ /*
+ * In case of split DSI usecases, the clock for master controller should
+ * be enabled before the other controller. Master controller in the
+ * clock context refers to the controller that sources the clock.
+ */
+
+ m_clks = &clks[master_ndx];
+
+ rc = dsi_link_clk_start(m_clks);
+ if (rc) {
+ pr_err("failed to turn on master clocks, rc=%d\n", rc);
+ goto error;
+ }
+
+ /* Turn on rest of the core clocks */
+ for (i = 0; i < ctrl_count; i++) {
+ clk = &clks[i];
+ if (!clk || (clk == m_clks))
+ continue;
+
+ rc = dsi_link_clk_start(clk);
+ if (rc) {
+ pr_err("failed to turn on clocks, rc=%d\n", rc);
+ goto error_disable_master;
+ }
+ }
+ return rc;
+error_disable_master:
+ (void)dsi_link_clk_stop(m_clks);
+error:
+ return rc;
+}
+
+static int dsi_display_core_clk_disable(struct dsi_core_clks *clks,
+ u32 ctrl_count, u32 master_ndx)
+{
+ int rc = 0;
+ int i;
+ struct dsi_core_clks *clk, *m_clks;
+
+ /*
+ * In case of split DSI usecases, clock for slave DSI controllers should
+ * be disabled first before disabling clock for master controller. Slave
+ * controllers in the clock context refer to controller which source
+ * clock from another controller.
+ */
+
+ m_clks = &clks[master_ndx];
+
+ /* Turn off non-master core clocks */
+ for (i = 0; i < ctrl_count; i++) {
+ clk = &clks[i];
+ if (!clk || (clk == m_clks))
+ continue;
+
+ rc = dsi_core_clk_stop(clk);
+ if (rc)
+ pr_err("failed to turn off clocks, rc=%d\n", rc);
+ }
+
+ rc = dsi_core_clk_stop(m_clks);
+ if (rc)
+ pr_err("failed to turn off master clocks, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int dsi_display_link_clk_disable(struct dsi_link_clks *clks,
+ u32 ctrl_count, u32 master_ndx)
+{
+ int rc = 0;
+ int i;
+ struct dsi_link_clks *clk, *m_clks;
+
+ /*
+ * In case of split DSI usecases, clock for slave DSI controllers should
+ * be disabled first before disabling clock for master controller. Slave
+ * controllers in the clock context refer to controller which source
+ * clock from another controller.
+ */
+
+ m_clks = &clks[master_ndx];
+
+ /* Turn off non-master link clocks */
+ for (i = 0; i < ctrl_count; i++) {
+ clk = &clks[i];
+ if (!clk || (clk == m_clks))
+ continue;
+
+ rc = dsi_link_clk_stop(clk);
+ if (rc)
+ pr_err("failed to turn off clocks, rc=%d\n", rc);
+ }
+
+ rc = dsi_link_clk_stop(m_clks);
+ if (rc)
+ pr_err("failed to turn off master clocks, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int dsi_update_clk_state(struct dsi_core_clks *c_clks, u32 c_state,
+ struct dsi_link_clks *l_clks, u32 l_state)
+{
+ int rc = 0;
+ struct dsi_clk_mngr *mngr;
+ bool l_c_on = false;
+
+ if (c_clks) {
+ mngr =
+ container_of(c_clks, struct dsi_clk_mngr, core_clks[0]);
+ } else if (l_clks) {
+ mngr =
+ container_of(l_clks, struct dsi_clk_mngr, link_clks[0]);
+ } else {
+ mngr = NULL;
+ }
+
+ if (!mngr)
+ return -EINVAL;
+
+ pr_debug("c_state = %d, l_state = %d\n",
+ c_clks ? c_state : -1, l_clks ? l_state : -1);
+ /*
+ * Below is the sequence to toggle DSI clocks:
+ * 1. For ON sequence, Core clocks before link clocks
+ * 2. For OFF sequence, Link clocks before core clocks.
+ */
+ if (c_clks && (c_state == DSI_CLK_ON)) {
+ if (mngr->core_clk_state == DSI_CLK_OFF) {
+ rc = mngr->pre_clkon_cb(mngr->priv_data,
+ DSI_CORE_CLK,
+ DSI_CLK_ON);
+ if (rc) {
+ pr_err("failed to turn on MDP FS rc= %d\n", rc);
+ goto error;
+ }
+ }
+ rc = dsi_display_core_clk_enable(c_clks, mngr->dsi_ctrl_count,
+ mngr->master_ndx);
+ if (rc) {
+ pr_err("failed to turn on core clks rc = %d\n", rc);
+ goto error;
+ }
+
+ if (mngr->post_clkon_cb) {
+ rc = mngr->post_clkon_cb(mngr->priv_data,
+ DSI_CORE_CLK,
+ DSI_CLK_ON);
+ if (rc)
+ pr_err("post clk on cb failed, rc = %d\n", rc);
+ }
+ mngr->core_clk_state = DSI_CLK_ON;
+ }
+
+ if (l_clks) {
+ if (l_state == DSI_CLK_ON) {
+ if (mngr->pre_clkon_cb) {
+ rc = mngr->pre_clkon_cb(mngr->priv_data,
+ DSI_LINK_CLK, l_state);
+ if (rc)
+ pr_err("pre link clk on cb failed\n");
+ }
+ rc = dsi_display_link_clk_enable(l_clks,
+ mngr->dsi_ctrl_count, mngr->master_ndx);
+ if (rc) {
+ pr_err("failed to start link clk rc= %d\n", rc);
+ goto error;
+ }
+ if (mngr->post_clkon_cb) {
+ rc = mngr->post_clkon_cb(mngr->priv_data,
+ DSI_LINK_CLK,
+ l_state);
+ if (rc)
+ pr_err("post link clk on cb failed\n");
+ }
+ } else {
+ /*
+ * Two conditions that need to be checked for Link
+ * clocks:
+ * 1. Link clocks need core clocks to be on when
+ * transitioning from EARLY_GATE to OFF state.
+ * 2. ULPS mode might have to be enabled in case of OFF
+ * state. For ULPS, Link clocks should be turned ON
+ * first before they are turned off again.
+ *
+ * If Link is going from EARLY_GATE to OFF state AND
+ * Core clock is already in EARLY_GATE or OFF state,
+ * turn on Core clocks and link clocks.
+ *
+ * ULPS state is managed as part of the pre_clkoff_cb.
+ */
+ if ((l_state == DSI_CLK_OFF) &&
+ (mngr->link_clk_state ==
+ DSI_CLK_EARLY_GATE) &&
+ (mngr->core_clk_state !=
+ DSI_CLK_ON)) {
+ rc = dsi_display_core_clk_enable(
+ mngr->core_clks, mngr->dsi_ctrl_count,
+ mngr->master_ndx);
+ if (rc) {
+ pr_err("core clks did not start\n");
+ goto error;
+ }
+
+ rc = dsi_display_link_clk_enable(l_clks,
+ mngr->dsi_ctrl_count, mngr->master_ndx);
+ if (rc) {
+ pr_err("Link clks did not start\n");
+ goto error;
+ }
+ l_c_on = true;
+ pr_debug("ECG: core and Link_on\n");
+ }
+
+ if (mngr->pre_clkoff_cb) {
+ rc = mngr->pre_clkoff_cb(mngr->priv_data,
+ DSI_LINK_CLK, l_state);
+ if (rc)
+ pr_err("pre link clk off cb failed\n");
+ }
+
+ rc = dsi_display_link_clk_disable(l_clks,
+ mngr->dsi_ctrl_count, mngr->master_ndx);
+ if (rc) {
+ pr_err("failed to stop link clk, rc = %d\n",
+ rc);
+ goto error;
+ }
+
+ if (mngr->post_clkoff_cb) {
+ rc = mngr->post_clkoff_cb(mngr->priv_data,
+ DSI_LINK_CLK, l_state);
+ if (rc)
+ pr_err("post link clk off cb failed\n");
+ }
+ /*
+ * This check is to save unnecessary clock state
+ * change when going from EARLY_GATE to OFF. In the
+ * case where the request happens for both Core and Link
+ * clocks in the same call, core clocks need to be
+ * turned on first before OFF state can be entered.
+ *
+ * Core clocks are turned on here for Link clocks to go
+ * to OFF state. If core clock request is also present,
+ * then core clocks can be turned off Core clocks are
+ * transitioned to OFF state.
+ */
+ if (l_c_on && (!(c_clks && (c_state == DSI_CLK_OFF)
+ && (mngr->core_clk_state ==
+ DSI_CLK_EARLY_GATE)))) {
+ rc = dsi_display_core_clk_disable(
+ mngr->core_clks, mngr->dsi_ctrl_count,
+ mngr->master_ndx);
+ if (rc) {
+ pr_err("core clks did not stop\n");
+ goto error;
+ }
+
+ l_c_on = false;
+ pr_debug("ECG: core off\n");
+ } else
+ pr_debug("ECG: core off skip\n");
+ }
+
+ mngr->link_clk_state = l_state;
+ }
+
+ if (c_clks && (c_state != DSI_CLK_ON)) {
+ /*
+ * When going to OFF state from EARLY GATE state, Core clocks
+ * should be turned on first so that the IOs can be clamped.
+ * l_c_on flag is set, then the core clocks were turned before
+ * to the Link clocks go to OFF state. So Core clocks are
+ * already ON and this step can be skipped.
+ *
+ * IOs are clamped in pre_clkoff_cb callback.
+ */
+ if ((c_state == DSI_CLK_OFF) &&
+ (mngr->core_clk_state ==
+ DSI_CLK_EARLY_GATE) && !l_c_on) {
+ rc = dsi_display_core_clk_enable(mngr->core_clks,
+ mngr->dsi_ctrl_count, mngr->master_ndx);
+ if (rc) {
+ pr_err("core clks did not start\n");
+ goto error;
+ }
+ pr_debug("ECG: core on\n");
+ } else
+ pr_debug("ECG: core on skip\n");
+
+ if (mngr->pre_clkoff_cb) {
+ rc = mngr->pre_clkoff_cb(mngr->priv_data,
+ DSI_CORE_CLK,
+ c_state);
+ if (rc)
+ pr_err("pre core clk off cb failed\n");
+ }
+
+ rc = dsi_display_core_clk_disable(c_clks, mngr->dsi_ctrl_count,
+ mngr->master_ndx);
+ if (rc) {
+ pr_err("failed to turn off core clks rc = %d\n", rc);
+ goto error;
+ }
+
+ if (c_state == DSI_CLK_OFF) {
+ if (mngr->post_clkoff_cb) {
+ rc = mngr->post_clkoff_cb(mngr->priv_data,
+ DSI_CORE_CLK,
+ DSI_CLK_OFF);
+ if (rc)
+ pr_err("post clkoff cb fail, rc = %d\n",
+ rc);
+ }
+ }
+ mngr->core_clk_state = c_state;
+ }
+
+error:
+ return rc;
+}
+
+static int dsi_recheck_clk_state(struct dsi_clk_mngr *mngr)
+{
+ int rc = 0;
+ struct list_head *pos = NULL;
+ struct dsi_clk_client_info *c;
+ u32 new_core_clk_state = DSI_CLK_OFF;
+ u32 new_link_clk_state = DSI_CLK_OFF;
+ u32 old_c_clk_state = DSI_CLK_OFF;
+ u32 old_l_clk_state = DSI_CLK_OFF;
+ struct dsi_core_clks *c_clks = NULL;
+ struct dsi_link_clks *l_clks = NULL;
+
+ /*
+ * Conditions to maintain DSI manager clock state based on
+ * clock states of various clients:
+ * 1. If any client has clock in ON state, DSI manager clock state
+ * should be ON.
+ * 2. If any client is in ECG state with rest of them turned OFF,
+ * go to Early gate state.
+ * 3. If all clients have clocks as OFF, then go to OFF state.
+ */
+ list_for_each(pos, &mngr->client_list) {
+ c = list_entry(pos, struct dsi_clk_client_info, list);
+ if (c->core_clk_state == DSI_CLK_ON) {
+ new_core_clk_state = DSI_CLK_ON;
+ break;
+ } else if (c->core_clk_state == DSI_CLK_EARLY_GATE) {
+ new_core_clk_state = DSI_CLK_EARLY_GATE;
+ }
+ }
+
+ list_for_each(pos, &mngr->client_list) {
+ c = list_entry(pos, struct dsi_clk_client_info, list);
+ if (c->link_clk_state == DSI_CLK_ON) {
+ new_link_clk_state = DSI_CLK_ON;
+ break;
+ } else if (c->link_clk_state == DSI_CLK_EARLY_GATE) {
+ new_link_clk_state = DSI_CLK_EARLY_GATE;
+ }
+ }
+
+ if (new_core_clk_state != mngr->core_clk_state)
+ c_clks = mngr->core_clks;
+
+ if (new_link_clk_state != mngr->link_clk_state)
+ l_clks = mngr->link_clks;
+
+ old_c_clk_state = mngr->core_clk_state;
+ old_l_clk_state = mngr->link_clk_state;
+
+ pr_debug("c_clk_state (%d -> %d)\n",
+ old_c_clk_state, new_core_clk_state);
+ pr_debug("l_clk_state (%d -> %d)\n",
+ old_l_clk_state, new_link_clk_state);
+
+ if (c_clks || l_clks) {
+ rc = dsi_update_clk_state(c_clks, new_core_clk_state,
+ l_clks, new_link_clk_state);
+ if (rc) {
+ pr_err("failed to update clock state, rc = %d\n", rc);
+ goto error;
+ }
+ }
+
+error:
+ return rc;
+}
+
+int dsi_clk_req_state(void *client, enum dsi_clk_type clk,
+ enum dsi_clk_state state)
+{
+ int rc = 0;
+ struct dsi_clk_client_info *c = client;
+ struct dsi_clk_mngr *mngr;
+ bool changed = false;
+
+ if (!client || !clk || clk > (DSI_CORE_CLK | DSI_LINK_CLK) ||
+ state > DSI_CLK_EARLY_GATE) {
+ pr_err("Invalid params, client = %pK, clk = 0x%x, state = %d\n",
+ client, clk, state);
+ return -EINVAL;
+ }
+
+ mngr = c->mngr;
+ mutex_lock(&mngr->clk_mutex);
+
+ pr_debug("[%s]%s: CLK=%d, new_state=%d, core=%d, linkl=%d\n",
+ mngr->name, c->name, clk, state, c->core_clk_state,
+ c->link_clk_state);
+
+ /*
+ * Clock refcount handling as below:
+ * i. Increment refcount whenever ON is called.
+ * ii. Decrement refcount when transitioning from ON state to
+ * either OFF or EARLY_GATE.
+ * iii. Do not decrement refcount when changing from
+ * EARLY_GATE to OFF.
+ */
+ if (state == DSI_CLK_ON) {
+ if (clk & DSI_CORE_CLK) {
+ c->core_refcount++;
+ if (c->core_clk_state != DSI_CLK_ON) {
+ c->core_clk_state = DSI_CLK_ON;
+ changed = true;
+ }
+ }
+ if (clk & DSI_LINK_CLK) {
+ c->link_refcount++;
+ if (c->link_clk_state != DSI_CLK_ON) {
+ c->link_clk_state = DSI_CLK_ON;
+ changed = true;
+ }
+ }
+ } else if ((state == DSI_CLK_EARLY_GATE) ||
+ (state == DSI_CLK_OFF)) {
+ if (clk & DSI_CORE_CLK) {
+ if (c->core_refcount == 0) {
+ if ((c->core_clk_state ==
+ DSI_CLK_EARLY_GATE) &&
+ (state == DSI_CLK_OFF)) {
+ changed = true;
+ c->core_clk_state = DSI_CLK_OFF;
+ } else {
+ pr_warn("Core refcount is zero for %s",
+ c->name);
+ }
+ } else {
+ c->core_refcount--;
+ if (c->core_refcount == 0) {
+ c->core_clk_state = state;
+ changed = true;
+ }
+ }
+ }
+ if (clk & DSI_LINK_CLK) {
+ if (c->link_refcount == 0) {
+ if ((c->link_clk_state ==
+ DSI_CLK_EARLY_GATE) &&
+ (state == DSI_CLK_OFF)) {
+ changed = true;
+ c->link_clk_state = DSI_CLK_OFF;
+ } else {
+ pr_warn("Link refcount is zero for %s",
+ c->name);
+ }
+ } else {
+ c->link_refcount--;
+ if (c->link_refcount == 0) {
+ c->link_clk_state = state;
+ changed = true;
+ }
+ }
+ }
+ }
+ pr_debug("[%s]%s: change=%d, Core (ref=%d, state=%d), Link (ref=%d, state=%d)\n",
+ mngr->name, c->name, changed, c->core_refcount,
+ c->core_clk_state, c->link_refcount, c->link_clk_state);
+
+ if (changed) {
+ rc = dsi_recheck_clk_state(mngr);
+ if (rc)
+ pr_err("Failed to adjust clock state rc = %d\n", rc);
+ }
+
+ mutex_unlock(&mngr->clk_mutex);
+ return rc;
+}
+
+DEFINE_MUTEX(dsi_mngr_clk_mutex);
+
+int dsi_display_clk_ctrl(void *handle,
+ enum dsi_clk_type clk_type, enum dsi_clk_state clk_state)
+{
+ int rc = 0;
+
+ if (!handle) {
+ pr_err("%s: Invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dsi_mngr_clk_mutex);
+ rc = dsi_clk_req_state(handle, clk_type, clk_state);
+ if (rc)
+ pr_err("%s: failed set clk state, rc = %d\n", __func__, rc);
+ mutex_unlock(&dsi_mngr_clk_mutex);
+
+ return rc;
+}
+
+void *dsi_register_clk_handle(void *clk_mngr, char *client)
+{
+ void *handle = NULL;
+ struct dsi_clk_mngr *mngr = clk_mngr;
+ struct dsi_clk_client_info *c;
+
+ if (!mngr) {
+ pr_err("bad params\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ mutex_lock(&mngr->clk_mutex);
+
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ handle = ERR_PTR(-ENOMEM);
+ goto error;
+ }
+
+ strlcpy(c->name, client, MAX_STRING_LEN);
+ c->mngr = mngr;
+
+ list_add(&c->list, &mngr->client_list);
+
+ pr_debug("[%s]: Added new client (%s)\n", mngr->name, c->name);
+ handle = c;
+error:
+ mutex_unlock(&mngr->clk_mutex);
+ return handle;
+}
+
+int dsi_deregister_clk_handle(void *client)
+{
+ int rc = 0;
+ struct dsi_clk_client_info *c = client;
+ struct dsi_clk_mngr *mngr;
+ struct list_head *pos = NULL;
+ struct list_head *tmp = NULL;
+ struct dsi_clk_client_info *node = NULL;
+
+ if (!client) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mngr = c->mngr;
+ pr_debug("%s: ENTER\n", mngr->name);
+ mutex_lock(&mngr->clk_mutex);
+ c->core_clk_state = DSI_CLK_OFF;
+ c->link_clk_state = DSI_CLK_OFF;
+
+ rc = dsi_recheck_clk_state(mngr);
+ if (rc) {
+ pr_err("clock state recheck failed rc = %d\n", rc);
+ goto error;
+ }
+
+ list_for_each_safe(pos, tmp, &mngr->client_list) {
+ node = list_entry(pos, struct dsi_clk_client_info,
+ list);
+ if (node == c) {
+ list_del(&node->list);
+ pr_debug("Removed device (%s)\n", node->name);
+ kfree(node);
+ break;
+ }
+ }
+
+error:
+ mutex_unlock(&mngr->clk_mutex);
+ pr_debug("%s: EXIT, rc = %d\n", mngr->name, rc);
+ return rc;
+}
+
+void *dsi_display_clk_mngr_register(struct dsi_clk_info *info)
+{
+ struct dsi_clk_mngr *mngr;
+ int i = 0;
+
+ if (!info) {
+ pr_err("Invalid params\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ mngr = kzalloc(sizeof(*mngr), GFP_KERNEL);
+ if (!mngr) {
+ mngr = ERR_PTR(-ENOMEM);
+ goto error;
+ }
+
+ mutex_init(&mngr->clk_mutex);
+ mngr->dsi_ctrl_count = info->dsi_ctrl_count;
+ mngr->master_ndx = info->master_ndx;
+
+ if (mngr->dsi_ctrl_count > MAX_DSI_CTRL) {
+ kfree(mngr);
+ return ERR_PTR(-EINVAL);
+ }
+
+ for (i = 0; i < mngr->dsi_ctrl_count; i++) {
+ memcpy(&mngr->core_clks[i].clks, &info->c_clks[i],
+ sizeof(struct dsi_core_clk_info));
+ memcpy(&mngr->link_clks[i].clks, &info->l_clks[i],
+ sizeof(struct dsi_link_clk_info));
+ mngr->core_clks[i].bus_handle = info->bus_handle[i];
+ }
+
+ INIT_LIST_HEAD(&mngr->client_list);
+ mngr->pre_clkon_cb = info->pre_clkon_cb;
+ mngr->post_clkon_cb = info->post_clkon_cb;
+ mngr->pre_clkoff_cb = info->pre_clkoff_cb;
+ mngr->post_clkoff_cb = info->post_clkoff_cb;
+ mngr->priv_data = info->priv_data;
+ memcpy(mngr->name, info->name, MAX_STRING_LEN);
+
+error:
+ pr_debug("EXIT, rc = %ld\n", PTR_ERR(mngr));
+ return mngr;
+}
+
+int dsi_display_clk_mngr_deregister(void *clk_mngr)
+{
+ int rc = 0;
+ struct dsi_clk_mngr *mngr = clk_mngr;
+ struct list_head *position = NULL;
+ struct list_head *tmp = NULL;
+ struct dsi_clk_client_info *node = NULL;
+
+ if (!mngr) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ pr_debug("%s: ENTER\n", mngr->name);
+ mutex_lock(&mngr->clk_mutex);
+
+ list_for_each_safe(position, tmp, &mngr->client_list) {
+ node = list_entry(position, struct dsi_clk_client_info,
+ list);
+ list_del(&node->list);
+ pr_debug("Removed device (%s)\n", node->name);
+ kfree(node);
+ }
+
+ rc = dsi_recheck_clk_state(mngr);
+ if (rc)
+ pr_err("failed to disable all clocks\n");
+
+ mutex_unlock(&mngr->clk_mutex);
+ pr_debug("%s: EXIT, rc = %d\n", mngr->name, rc);
+ kfree(mngr);
+ return rc;
+}
+
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.c b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.c
deleted file mode 100644
index 7def847..0000000
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.c
+++ /dev/null
@@ -1,727 +0,0 @@
-/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/of.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-
-#include "dsi_clk_pwr.h"
-
-#define INC_REFCOUNT(s, start_func) \
- ({ \
- int rc = 0; \
- if ((s)->refcount == 0) { \
- rc = start_func(s); \
- if (rc) \
- pr_err("failed to enable, rc = %d\n", rc); \
- } \
- (s)->refcount++; \
- rc; \
- })
-
-#define DEC_REFCOUNT(s, stop_func) \
- ({ \
- int rc = 0; \
- if ((s)->refcount == 0) { \
- pr_err("unbalanced refcount\n"); \
- } else { \
- (s)->refcount--; \
- if ((s)->refcount == 0) { \
- rc = stop_func(s); \
- if (rc) \
- pr_err("disable failed, rc=%d\n", rc); \
- } \
- } \
- rc; \
- })
-
-static int dsi_core_clk_start(struct dsi_core_clk_info *clks)
-{
- int rc = 0;
-
- rc = clk_prepare_enable(clks->mdp_core_clk);
- if (rc) {
- pr_err("failed to enable mdp_core_clk, rc=%d\n", rc);
- goto error;
- }
-
- rc = clk_prepare_enable(clks->iface_clk);
- if (rc) {
- pr_err("failed to enable iface_clk, rc=%d\n", rc);
- goto error_disable_core_clk;
- }
-
- rc = clk_prepare_enable(clks->bus_clk);
- if (rc) {
- pr_err("failed to enable bus_clk, rc=%d\n", rc);
- goto error_disable_iface_clk;
- }
-
- rc = clk_prepare_enable(clks->core_mmss_clk);
- if (rc) {
- pr_err("failed to enable core_mmss_clk, rc=%d\n", rc);
- goto error_disable_bus_clk;
- }
-
- return rc;
-
-error_disable_bus_clk:
- clk_disable_unprepare(clks->bus_clk);
-error_disable_iface_clk:
- clk_disable_unprepare(clks->iface_clk);
-error_disable_core_clk:
- clk_disable_unprepare(clks->mdp_core_clk);
-error:
- return rc;
-}
-
-static int dsi_core_clk_stop(struct dsi_core_clk_info *clks)
-{
- clk_disable_unprepare(clks->core_mmss_clk);
- clk_disable_unprepare(clks->bus_clk);
- clk_disable_unprepare(clks->iface_clk);
- clk_disable_unprepare(clks->mdp_core_clk);
-
- return 0;
-}
-
-static int dsi_link_clk_set_rate(struct dsi_link_clk_info *l_clks)
-{
- int rc = 0;
-
- rc = clk_set_rate(l_clks->esc_clk, l_clks->esc_clk_rate);
- if (rc) {
- pr_err("clk_set_rate failed for esc_clk rc = %d\n", rc);
- goto error;
- }
-
- rc = clk_set_rate(l_clks->byte_clk, l_clks->byte_clk_rate);
- if (rc) {
- pr_err("clk_set_rate failed for byte_clk rc = %d\n", rc);
- goto error;
- }
-
- rc = clk_set_rate(l_clks->pixel_clk, l_clks->pixel_clk_rate);
- if (rc) {
- pr_err("clk_set_rate failed for pixel_clk rc = %d\n", rc);
- goto error;
- }
-error:
- return rc;
-}
-
-static int dsi_link_clk_prepare(struct dsi_link_clk_info *l_clks)
-{
- int rc = 0;
-
- rc = clk_prepare(l_clks->esc_clk);
- if (rc) {
- pr_err("Failed to prepare dsi esc clk, rc=%d\n", rc);
- goto esc_clk_err;
- }
-
- rc = clk_prepare(l_clks->byte_clk);
- if (rc) {
- pr_err("Failed to prepare dsi byte clk, rc=%d\n", rc);
- goto byte_clk_err;
- }
-
- rc = clk_prepare(l_clks->pixel_clk);
- if (rc) {
- pr_err("Failed to prepare dsi pixel clk, rc=%d\n", rc);
- goto pixel_clk_err;
- }
-
- return rc;
-
-pixel_clk_err:
- clk_unprepare(l_clks->byte_clk);
-byte_clk_err:
- clk_unprepare(l_clks->esc_clk);
-esc_clk_err:
- return rc;
-}
-
-static void dsi_link_clk_unprepare(struct dsi_link_clk_info *l_clks)
-{
- clk_unprepare(l_clks->pixel_clk);
- clk_unprepare(l_clks->byte_clk);
- clk_unprepare(l_clks->esc_clk);
-}
-
-static int dsi_link_clk_enable(struct dsi_link_clk_info *l_clks)
-{
- int rc = 0;
-
- rc = clk_enable(l_clks->esc_clk);
- if (rc) {
- pr_err("Failed to enable dsi esc clk, rc=%d\n", rc);
- goto esc_clk_err;
- }
-
- rc = clk_enable(l_clks->byte_clk);
- if (rc) {
- pr_err("Failed to enable dsi byte clk, rc=%d\n", rc);
- goto byte_clk_err;
- }
-
- rc = clk_enable(l_clks->pixel_clk);
- if (rc) {
- pr_err("Failed to enable dsi pixel clk, rc=%d\n", rc);
- goto pixel_clk_err;
- }
-
- return rc;
-
-pixel_clk_err:
- clk_disable(l_clks->byte_clk);
-byte_clk_err:
- clk_disable(l_clks->esc_clk);
-esc_clk_err:
- return rc;
-}
-
-static void dsi_link_clk_disable(struct dsi_link_clk_info *l_clks)
-{
- clk_disable(l_clks->esc_clk);
- clk_disable(l_clks->pixel_clk);
- clk_disable(l_clks->byte_clk);
-}
-
-/**
- * dsi_link_clk_start() - enable dsi link clocks
- */
-static int dsi_link_clk_start(struct dsi_link_clk_info *clks)
-{
- int rc = 0;
-
- if (clks->set_new_rate) {
- rc = dsi_link_clk_set_rate(clks);
- if (rc) {
- pr_err("failed to set clk rates, rc = %d\n", rc);
- goto error;
- } else {
- clks->set_new_rate = false;
- }
- }
-
- rc = dsi_link_clk_prepare(clks);
- if (rc) {
- pr_err("failed to prepare link clks, rc = %d\n", rc);
- goto error;
- }
-
- rc = dsi_link_clk_enable(clks);
- if (rc) {
- pr_err("failed to enable link clks, rc = %d\n", rc);
- goto error_unprepare;
- }
-
- pr_debug("Link clocks are enabled\n");
- return rc;
-error_unprepare:
- dsi_link_clk_unprepare(clks);
-error:
- return rc;
-}
-
-/**
- * dsi_link_clk_stop() - Stop DSI link clocks.
- */
-static int dsi_link_clk_stop(struct dsi_link_clk_info *clks)
-{
- dsi_link_clk_disable(clks);
- dsi_link_clk_unprepare(clks);
-
- pr_debug("Link clocks disabled\n");
-
- return 0;
-}
-
-/*
- * dsi_pwr_parse_supply_node() - parse power supply node from root device node
- */
-static int dsi_pwr_parse_supply_node(struct device_node *root,
- struct dsi_regulator_info *regs)
-{
- int rc = 0;
- int i = 0;
- u32 tmp = 0;
- struct device_node *node = NULL;
-
- for_each_child_of_node(root, node) {
- const char *st = NULL;
-
- rc = of_property_read_string(node, "qcom,supply-name", &st);
- if (rc) {
- pr_err("failed to read name, rc = %d\n", rc);
- goto error;
- }
-
- snprintf(regs->vregs[i].vreg_name,
- ARRAY_SIZE(regs->vregs[i].vreg_name),
- "%s", st);
-
- rc = of_property_read_u32(node, "qcom,supply-min-voltage",
- &tmp);
- if (rc) {
- pr_err("failed to read min voltage, rc = %d\n", rc);
- goto error;
- }
- regs->vregs[i].min_voltage = tmp;
-
- rc = of_property_read_u32(node, "qcom,supply-max-voltage",
- &tmp);
- if (rc) {
- pr_err("failed to read max voltage, rc = %d\n", rc);
- goto error;
- }
- regs->vregs[i].max_voltage = tmp;
-
- rc = of_property_read_u32(node, "qcom,supply-enable-load",
- &tmp);
- if (rc) {
- pr_err("failed to read enable load, rc = %d\n", rc);
- goto error;
- }
- regs->vregs[i].enable_load = tmp;
-
- rc = of_property_read_u32(node, "qcom,supply-disable-load",
- &tmp);
- if (rc) {
- pr_err("failed to read disable load, rc = %d\n", rc);
- goto error;
- }
- regs->vregs[i].disable_load = tmp;
-
- /* Optional values */
- rc = of_property_read_u32(node, "qcom,supply-pre-on-sleep",
- &tmp);
- if (rc) {
- pr_debug("pre-on-sleep not specified\n");
- rc = 0;
- } else {
- regs->vregs[i].pre_on_sleep = tmp;
- }
-
- rc = of_property_read_u32(node, "qcom,supply-pre-off-sleep",
- &tmp);
- if (rc) {
- pr_debug("pre-off-sleep not specified\n");
- rc = 0;
- } else {
- regs->vregs[i].pre_off_sleep = tmp;
- }
-
- rc = of_property_read_u32(node, "qcom,supply-post-on-sleep",
- &tmp);
- if (rc) {
- pr_debug("post-on-sleep not specified\n");
- rc = 0;
- } else {
- regs->vregs[i].post_on_sleep = tmp;
- }
-
- rc = of_property_read_u32(node, "qcom,supply-post-off-sleep",
- &tmp);
- if (rc) {
- pr_debug("post-off-sleep not specified\n");
- rc = 0;
- } else {
- regs->vregs[i].post_off_sleep = tmp;
- }
-
- ++i;
- pr_debug("[%s] minv=%d maxv=%d, en_load=%d, dis_load=%d\n",
- regs->vregs[i].vreg_name,
- regs->vregs[i].min_voltage,
- regs->vregs[i].max_voltage,
- regs->vregs[i].enable_load,
- regs->vregs[i].disable_load);
- }
-
-error:
- return rc;
-}
-
-/**
- * dsi_pwr_enable_vregs() - enable/disable regulators
- */
-static int dsi_pwr_enable_vregs(struct dsi_regulator_info *regs, bool enable)
-{
- int rc = 0, i = 0;
- struct dsi_vreg *vreg;
- int num_of_v = 0;
-
- if (enable) {
- for (i = 0; i < regs->count; i++) {
- vreg = ®s->vregs[i];
- if (vreg->pre_on_sleep)
- msleep(vreg->pre_on_sleep);
-
- rc = regulator_set_load(vreg->vreg,
- vreg->enable_load);
- if (rc < 0) {
- pr_err("Setting optimum mode failed for %s\n",
- vreg->vreg_name);
- goto error;
- }
- num_of_v = regulator_count_voltages(vreg->vreg);
- if (num_of_v > 0) {
- rc = regulator_set_voltage(vreg->vreg,
- vreg->min_voltage,
- vreg->max_voltage);
- if (rc) {
- pr_err("Set voltage(%s) fail, rc=%d\n",
- vreg->vreg_name, rc);
- goto error_disable_opt_mode;
- }
- }
-
- rc = regulator_enable(vreg->vreg);
- if (rc) {
- pr_err("enable failed for %s, rc=%d\n",
- vreg->vreg_name, rc);
- goto error_disable_voltage;
- }
-
- if (vreg->post_on_sleep)
- msleep(vreg->post_on_sleep);
- }
- } else {
- for (i = (regs->count - 1); i >= 0; i--) {
- if (regs->vregs[i].pre_off_sleep)
- msleep(regs->vregs[i].pre_off_sleep);
-
- (void)regulator_set_load(regs->vregs[i].vreg,
- regs->vregs[i].disable_load);
- (void)regulator_disable(regs->vregs[i].vreg);
-
- if (regs->vregs[i].post_off_sleep)
- msleep(regs->vregs[i].post_off_sleep);
- }
- }
-
- return 0;
-error_disable_opt_mode:
- (void)regulator_set_load(regs->vregs[i].vreg,
- regs->vregs[i].disable_load);
-
-error_disable_voltage:
- if (num_of_v > 0)
- (void)regulator_set_voltage(regs->vregs[i].vreg,
- 0, regs->vregs[i].max_voltage);
-error:
- for (i--; i >= 0; i--) {
- if (regs->vregs[i].pre_off_sleep)
- msleep(regs->vregs[i].pre_off_sleep);
-
- (void)regulator_set_load(regs->vregs[i].vreg,
- regs->vregs[i].disable_load);
-
- num_of_v = regulator_count_voltages(regs->vregs[i].vreg);
- if (num_of_v > 0)
- (void)regulator_set_voltage(regs->vregs[i].vreg,
- 0, regs->vregs[i].max_voltage);
-
- (void)regulator_disable(regs->vregs[i].vreg);
-
- if (regs->vregs[i].post_off_sleep)
- msleep(regs->vregs[i].post_off_sleep);
- }
-
- return rc;
-}
-
-/**
-* dsi_clk_pwr_of_get_vreg_data - Parse regulator supply information
-* @of_node: Device of node to parse for supply information.
-* @regs: Pointer where regulator information will be copied to.
-* @supply_name: Name of the supply node.
-*
-* return: error code in case of failure or 0 for success.
-*/
-int dsi_clk_pwr_of_get_vreg_data(struct device_node *of_node,
- struct dsi_regulator_info *regs,
- char *supply_name)
-{
- int rc = 0;
- struct device_node *supply_root_node = NULL;
-
- if (!of_node || !regs) {
- pr_err("Bad params\n");
- return -EINVAL;
- }
-
- regs->count = 0;
- supply_root_node = of_get_child_by_name(of_node, supply_name);
- if (!supply_root_node) {
- supply_root_node = of_parse_phandle(of_node, supply_name, 0);
- if (!supply_root_node) {
- pr_err("No supply entry present for %s\n", supply_name);
- return -EINVAL;
- }
- }
-
- regs->count = of_get_available_child_count(supply_root_node);
- if (regs->count == 0) {
- pr_err("No vregs defined for %s\n", supply_name);
- return -EINVAL;
- }
-
- regs->vregs = kcalloc(regs->count, sizeof(*regs->vregs), GFP_KERNEL);
- if (!regs->vregs) {
- regs->count = 0;
- return -ENOMEM;
- }
-
- rc = dsi_pwr_parse_supply_node(supply_root_node, regs);
- if (rc) {
- pr_err("failed to parse supply node for %s, rc = %d\n",
- supply_name, rc);
-
- kfree(regs->vregs);
- regs->vregs = NULL;
- regs->count = 0;
- }
-
- return rc;
-}
-
-/**
- * dsi_clk_pwr_get_dt_vreg_data - parse regulator supply information
- * @dev: Device whose of_node needs to be parsed.
- * @regs: Pointer where regulator information will be copied to.
- * @supply_name: Name of the supply node.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_pwr_get_dt_vreg_data(struct device *dev,
- struct dsi_regulator_info *regs,
- char *supply_name)
-{
- int rc = 0;
- struct device_node *of_node = NULL;
- struct device_node *supply_node = NULL;
- struct device_node *supply_root_node = NULL;
-
- if (!dev || !regs) {
- pr_err("Bad params\n");
- return -EINVAL;
- }
-
- of_node = dev->of_node;
- regs->count = 0;
- supply_root_node = of_get_child_by_name(of_node, supply_name);
- if (!supply_root_node) {
- supply_root_node = of_parse_phandle(of_node, supply_name, 0);
- if (!supply_root_node) {
- pr_err("No supply entry present for %s\n", supply_name);
- return -EINVAL;
- }
- }
-
- for_each_child_of_node(supply_root_node, supply_node)
- regs->count++;
-
- if (regs->count == 0) {
- pr_err("No vregs defined for %s\n", supply_name);
- return -EINVAL;
- }
-
- regs->vregs = devm_kcalloc(dev, regs->count, sizeof(*regs->vregs),
- GFP_KERNEL);
- if (!regs->vregs) {
- regs->count = 0;
- return -ENOMEM;
- }
-
- rc = dsi_pwr_parse_supply_node(supply_root_node, regs);
- if (rc) {
- pr_err("failed to parse supply node for %s, rc = %d\n",
- supply_name, rc);
- devm_kfree(dev, regs->vregs);
- regs->vregs = NULL;
- regs->count = 0;
- }
-
- return rc;
-}
-
-/**
- * dsi_pwr_enable_regulator() - enable a set of regulators
- * @regs: Pointer to set of regulators to enable or disable.
- * @enable: Enable/Disable regulators.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_pwr_enable_regulator(struct dsi_regulator_info *regs, bool enable)
-{
- int rc = 0;
-
- if (enable) {
- if (regs->refcount == 0) {
- rc = dsi_pwr_enable_vregs(regs, true);
- if (rc)
- pr_err("failed to enable regulators\n");
- }
- regs->refcount++;
- } else {
- if (regs->refcount == 0) {
- pr_err("Unbalanced regulator off\n");
- } else {
- regs->refcount--;
- if (regs->refcount == 0) {
- rc = dsi_pwr_enable_vregs(regs, false);
- if (rc)
- pr_err("failed to disable vregs\n");
- }
- }
- }
-
- return rc;
-}
-
-/**
- * dsi_clk_enable_core_clks() - enable DSI core clocks
- * @clks: DSI core clock information.
- * @enable: enable/disable DSI core clocks.
- *
- * A ref count is maintained, so caller should make sure disable and enable
- * calls are balanced.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_enable_core_clks(struct dsi_core_clk_info *clks, bool enable)
-{
- int rc = 0;
-
- if (enable)
- rc = INC_REFCOUNT(clks, dsi_core_clk_start);
- else
- rc = DEC_REFCOUNT(clks, dsi_core_clk_stop);
-
- return rc;
-}
-
-/**
- * dsi_clk_enable_link_clks() - enable DSI link clocks
- * @clks: DSI link clock information.
- * @enable: enable/disable DSI link clocks.
- *
- * A ref count is maintained, so caller should make sure disable and enable
- * calls are balanced.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_enable_link_clks(struct dsi_link_clk_info *clks, bool enable)
-{
- int rc = 0;
-
- if (enable)
- rc = INC_REFCOUNT(clks, dsi_link_clk_start);
- else
- rc = DEC_REFCOUNT(clks, dsi_link_clk_stop);
-
- return rc;
-}
-
-/**
- * dsi_clk_set_link_frequencies() - set frequencies for link clks
- * @clks: Link clock information
- * @pixel_clk: pixel clock frequency in KHz.
- * @byte_clk: Byte clock frequency in KHz.
- * @esc_clk: Escape clock frequency in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_link_frequencies(struct dsi_link_clk_info *clks,
- u64 pixel_clk,
- u64 byte_clk,
- u64 esc_clk)
-{
- int rc = 0;
-
- clks->pixel_clk_rate = pixel_clk;
- clks->byte_clk_rate = byte_clk;
- clks->esc_clk_rate = esc_clk;
- clks->set_new_rate = true;
-
- return rc;
-}
-
-/**
- * dsi_clk_set_pixel_clk_rate() - set frequency for pixel clock
- * @clks: DSI link clock information.
- * @pixel_clk: Pixel clock rate in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_pixel_clk_rate(struct dsi_link_clk_info *clks, u64 pixel_clk)
-{
- int rc = 0;
-
- rc = clk_set_rate(clks->pixel_clk, pixel_clk);
- if (rc)
- pr_err("failed to set clk rate for pixel clk, rc=%d\n", rc);
- else
- clks->pixel_clk_rate = pixel_clk;
-
- return rc;
-}
-
-/**
- * dsi_clk_set_byte_clk_rate() - set frequency for byte clock
- * @clks: DSI link clock information.
- * @byte_clk: Byte clock rate in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_byte_clk_rate(struct dsi_link_clk_info *clks, u64 byte_clk)
-{
- int rc = 0;
-
- rc = clk_set_rate(clks->byte_clk, byte_clk);
- if (rc)
- pr_err("failed to set clk rate for byte clk, rc=%d\n", rc);
- else
- clks->byte_clk_rate = byte_clk;
-
- return rc;
-}
-
-/**
- * dsi_clk_update_parent() - update parent clocks for specified clock
- * @parent: link clock pair which are set as parent.
- * @child: link clock pair whose parent has to be set.
- */
-int dsi_clk_update_parent(struct dsi_clk_link_set *parent,
- struct dsi_clk_link_set *child)
-{
- int rc = 0;
-
- rc = clk_set_parent(child->byte_clk, parent->byte_clk);
- if (rc) {
- pr_err("failed to set byte clk parent\n");
- goto error;
- }
-
- rc = clk_set_parent(child->pixel_clk, parent->pixel_clk);
- if (rc) {
- pr_err("failed to set pixel clk parent\n");
- goto error;
- }
-error:
- return rc;
-}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.h b/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.h
deleted file mode 100644
index 223ca4e..0000000
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_clk_pwr.h
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef _DSI_CLK_PWR_H_
-#define _DSI_CLK_PWR_H_
-
-#include <linux/device.h>
-#include <linux/platform_device.h>
-#include <linux/types.h>
-#include <linux/regulator/consumer.h>
-#include <linux/clk.h>
-
-/**
- * struct dsi_vreg - regulator information for DSI regulators
- * @vreg: Handle to the regulator.
- * @vreg_name: Regulator name.
- * @min_voltage: Minimum voltage in uV.
- * @max_voltage: Maximum voltage in uV.
- * @enable_load: Load, in uA, when enabled.
- * @disable_load: Load, in uA, when disabled.
- * @pre_on_sleep: Sleep, in ms, before enabling the regulator.
- * @post_on_sleep: Sleep, in ms, after enabling the regulator.
- * @pre_off_sleep: Sleep, in ms, before disabling the regulator.
- * @post_off_sleep: Sleep, in ms, after disabling the regulator.
- */
-struct dsi_vreg {
- struct regulator *vreg;
- char vreg_name[32];
- u32 min_voltage;
- u32 max_voltage;
- u32 enable_load;
- u32 disable_load;
- u32 pre_on_sleep;
- u32 post_on_sleep;
- u32 pre_off_sleep;
- u32 post_off_sleep;
-};
-
-/**
- * struct dsi_regulator_info - set of vregs that are turned on/off together.
- * @vregs: Array of dsi_vreg structures.
- * @count: Number of vregs.
- * @refcount: Reference counting for enabling.
- */
-struct dsi_regulator_info {
- struct dsi_vreg *vregs;
- u32 count;
- u32 refcount;
-};
-
-/**
- * struct dsi_core_clk_info - Core clock information for DSI hardware
- * @mdp_core_clk: Handle to MDP core clock.
- * @iface_clk: Handle to MDP interface clock.
- * @core_mmss_clk: Handle to MMSS core clock.
- * @bus_clk: Handle to bus clock.
- * @refcount: Reference count for core clocks.
- * @clk_state: Current clock state.
- */
-struct dsi_core_clk_info {
- struct clk *mdp_core_clk;
- struct clk *iface_clk;
- struct clk *core_mmss_clk;
- struct clk *bus_clk;
-
- u32 refcount;
- u32 clk_state;
-};
-
-/**
- * struct dsi_link_clk_info - Link clock information for DSI hardware.
- * @byte_clk: Handle to DSI byte clock.
- * @byte_clk_rate: Frequency of DSI byte clock in KHz.
- * @pixel_clk: Handle to DSI pixel clock.
- * @pixel_clk_rate: Frequency of DSI pixel clock in KHz.
- * @esc_clk: Handle to DSI escape clock.
- * @esc_clk_rate: Frequency of DSI escape clock in KHz.
- * @refcount: Reference count for link clocks.
- * @clk_state: Current clock state.
- * @set_new_rate: private flag used by clock utility.
- */
-struct dsi_link_clk_info {
- struct clk *byte_clk;
- u64 byte_clk_rate;
-
- struct clk *pixel_clk;
- u64 pixel_clk_rate;
-
- struct clk *esc_clk;
- u64 esc_clk_rate;
-
- u32 refcount;
- u32 clk_state;
- bool set_new_rate;
-};
-
-/**
- * struct dsi_clk_link_set - Pair of clock handles to describe link clocks
- * @byte_clk: Handle to DSi byte clock.
- * @pixel_clk: Handle to DSI pixel clock.
- */
-struct dsi_clk_link_set {
- struct clk *byte_clk;
- struct clk *pixel_clk;
-};
-
-/**
- * dsi_clk_pwr_of_get_vreg_data - parse regulator supply information
- * @of_node: Device of node to parse for supply information.
- * @regs: Pointer where regulator information will be copied to.
- * @supply_name: Name of the supply node.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_pwr_of_get_vreg_data(struct device_node *of_node,
- struct dsi_regulator_info *regs,
- char *supply_name);
-
-/**
- * dsi_clk_pwr_get_dt_vreg_data - parse regulator supply information
- * @dev: Device whose of_node needs to be parsed.
- * @regs: Pointer where regulator information will be copied to.
- * @supply_name: Name of the supply node.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_pwr_get_dt_vreg_data(struct device *dev,
- struct dsi_regulator_info *regs,
- char *supply_name);
-
-/**
- * dsi_pwr_enable_regulator() - enable a set of regulators
- * @regs: Pointer to set of regulators to enable or disable.
- * @enable: Enable/Disable regulators.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_pwr_enable_regulator(struct dsi_regulator_info *regs, bool enable);
-
-/**
- * dsi_clk_enable_core_clks() - enable DSI core clocks
- * @clks: DSI core clock information.
- * @enable: enable/disable DSI core clocks.
- *
- * A ref count is maintained, so caller should make sure disable and enable
- * calls are balanced.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_enable_core_clks(struct dsi_core_clk_info *clks, bool enable);
-
-/**
- * dsi_clk_enable_link_clks() - enable DSI link clocks
- * @clks: DSI link clock information.
- * @enable: enable/disable DSI link clocks.
- *
- * A ref count is maintained, so caller should make sure disable and enable
- * calls are balanced.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_enable_link_clks(struct dsi_link_clk_info *clks, bool enable);
-
-/**
- * dsi_clk_set_link_frequencies() - set frequencies for link clks
- * @clks: Link clock information
- * @pixel_clk: pixel clock frequency in KHz.
- * @byte_clk: Byte clock frequency in KHz.
- * @esc_clk: Escape clock frequency in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_link_frequencies(struct dsi_link_clk_info *clks,
- u64 pixel_clk,
- u64 byte_clk,
- u64 esc_clk);
-
-/**
- * dsi_clk_set_pixel_clk_rate() - set frequency for pixel clock
- * @clks: DSI link clock information.
- * @pixel_clk: Pixel clock rate in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_pixel_clk_rate(struct dsi_link_clk_info *clks, u64 pixel_clk);
-
-/**
- * dsi_clk_set_byte_clk_rate() - set frequency for byte clock
- * @clks: DSI link clock information.
- * @byte_clk: Byte clock rate in KHz.
- *
- * return: error code in case of failure or 0 for success.
- */
-int dsi_clk_set_byte_clk_rate(struct dsi_link_clk_info *clks, u64 byte_clk);
-
-/**
- * dsi_clk_update_parent() - update parent clocks for specified clock
- * @parent: link clock pair which are set as parent.
- * @child: link clock pair whose parent has to be set.
- */
-int dsi_clk_update_parent(struct dsi_clk_link_set *parent,
- struct dsi_clk_link_set *child);
-#endif /* _DSI_CLK_PWR_H_ */
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
index b8520aa..3e0e9f6 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -27,7 +27,8 @@
#include "msm_gpu.h"
#include "dsi_ctrl.h"
#include "dsi_ctrl_hw.h"
-#include "dsi_clk_pwr.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
#include "dsi_catalog.h"
#define DSI_CTRL_DEFAULT_LABEL "MDSS DSI CTRL"
@@ -44,9 +45,6 @@
DSI_CTRL_OP_VID_ENGINE,
DSI_CTRL_OP_HOST_ENGINE,
DSI_CTRL_OP_CMD_TX,
- DSI_CTRL_OP_ULPS_TOGGLE,
- DSI_CTRL_OP_CLAMP_TOGGLE,
- DSI_CTRL_OP_SET_CLK_SOURCE,
DSI_CTRL_OP_HOST_INIT,
DSI_CTRL_OP_TPG,
DSI_CTRL_OP_PHY_SW_RESET,
@@ -99,16 +97,7 @@
/* Dump current state */
len += snprintf((buf + len), (SZ_4K - len), "Current State:\n");
len += snprintf((buf + len), (SZ_4K - len),
- "\tPOWER_STATUS = %s\n\tCORE_CLOCK = %s\n",
- TO_ON_OFF(dsi_ctrl->current_state.pwr_enabled),
- TO_ON_OFF(dsi_ctrl->current_state.core_clk_enabled));
- len += snprintf((buf + len), (SZ_4K - len),
- "\tLINK_CLOCK = %s\n\tULPS_STATUS = %s\n",
- TO_ON_OFF(dsi_ctrl->current_state.link_clk_enabled),
- TO_ON_OFF(dsi_ctrl->current_state.ulps_enabled));
- len += snprintf((buf + len), (SZ_4K - len),
- "\tCLAMP_STATUS = %s\n\tCTRL_ENGINE = %s\n",
- TO_ON_OFF(dsi_ctrl->current_state.clamp_enabled),
+ "\tCTRL_ENGINE = %s\n",
TO_ON_OFF(dsi_ctrl->current_state.controller_state));
len += snprintf((buf + len), (SZ_4K - len),
"\tVIDEO_ENGINE = %s\n\tCOMMAND_ENGINE = %s\n",
@@ -118,10 +107,10 @@
/* Dump clock information */
len += snprintf((buf + len), (SZ_4K - len), "\nClock Info:\n");
len += snprintf((buf + len), (SZ_4K - len),
- "\tBYTE_CLK = %llu, PIXEL_CLK = %llu, ESC_CLK = %llu\n",
- dsi_ctrl->clk_info.link_clks.byte_clk_rate,
- dsi_ctrl->clk_info.link_clks.pixel_clk_rate,
- dsi_ctrl->clk_info.link_clks.esc_clk_rate);
+ "\tBYTE_CLK = %u, PIXEL_CLK = %u, ESC_CLK = %u\n",
+ dsi_ctrl->clk_freq.byte_clk_rate,
+ dsi_ctrl->clk_freq.pix_clk_rate,
+ dsi_ctrl->clk_freq.esc_clk_rate);
/* TODO: make sure that this does not exceed 4K */
if (copy_to_user(buff, buf, len)) {
@@ -142,6 +131,8 @@
struct dsi_ctrl *dsi_ctrl = file->private_data;
char *buf;
u32 len = 0;
+ struct dsi_clk_ctrl_info clk_info;
+ int rc = 0;
if (!dsi_ctrl)
return -ENODEV;
@@ -153,15 +144,28 @@
if (!buf)
return -ENOMEM;
- if (dsi_ctrl->current_state.core_clk_enabled) {
- len = dsi_ctrl->hw.ops.reg_dump_to_buffer(&dsi_ctrl->hw,
- buf,
- SZ_4K);
- } else {
- len = snprintf((buf + len), (SZ_4K - len),
- "Core clocks are not turned on, cannot read\n");
+ clk_info.client = DSI_CLK_REQ_DSI_CLIENT;
+ clk_info.clk_type = DSI_CORE_CLK;
+ clk_info.clk_state = DSI_CLK_ON;
+
+ rc = dsi_ctrl->clk_cb.dsi_clk_cb(dsi_ctrl->clk_cb.priv, clk_info);
+ if (rc) {
+ pr_err("failed to enable DSI core clocks\n");
+ kfree(buf);
+ return rc;
}
+ len = dsi_ctrl->hw.ops.reg_dump_to_buffer(&dsi_ctrl->hw,
+ buf, SZ_4K);
+
+ clk_info.clk_state = DSI_CLK_OFF;
+ rc = dsi_ctrl->clk_cb.dsi_clk_cb(dsi_ctrl->clk_cb.priv, clk_info);
+ if (rc) {
+ pr_err("failed to disable DSI core clocks\n");
+ return rc;
+ }
+
+
/* TODO: make sure that this does not exceed 4K */
if (copy_to_user(buff, buf, len)) {
kfree(buf);
@@ -247,7 +251,7 @@
pr_debug("[%d] No change in state, pwr_state=%d\n",
dsi_ctrl->index, op_state);
rc = -EINVAL;
- } else if (state->power_state == DSI_CTRL_POWER_LINK_CLK_ON) {
+ } else if (state->power_state == DSI_CTRL_POWER_VREG_ON) {
if ((state->cmd_engine_state == DSI_CTRL_ENGINE_ON) ||
(state->vid_engine_state == DSI_CTRL_ENGINE_ON) ||
(state->controller_state == DSI_CTRL_ENGINE_ON)) {
@@ -266,7 +270,7 @@
pr_debug("[%d] No change in state, cmd_state=%d\n",
dsi_ctrl->index, op_state);
rc = -EINVAL;
- } else if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
+ } else if ((state->power_state != DSI_CTRL_POWER_VREG_ON) ||
(state->controller_state != DSI_CTRL_ENGINE_ON)) {
pr_debug("[%d]State error: op=%d: %d, %d\n",
dsi_ctrl->index,
@@ -281,7 +285,7 @@
pr_debug("[%d] No change in state, cmd_state=%d\n",
dsi_ctrl->index, op_state);
rc = -EINVAL;
- } else if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
+ } else if ((state->power_state != DSI_CTRL_POWER_VREG_ON) ||
(state->controller_state != DSI_CTRL_ENGINE_ON)) {
pr_debug("[%d]State error: op=%d: %d, %d\n",
dsi_ctrl->index,
@@ -296,7 +300,7 @@
pr_debug("[%d] No change in state, ctrl_state=%d\n",
dsi_ctrl->index, op_state);
rc = -EINVAL;
- } else if (state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) {
+ } else if (state->power_state != DSI_CTRL_POWER_VREG_ON) {
pr_debug("[%d]State error (link is off): op=%d:, %d\n",
dsi_ctrl->index,
op_state,
@@ -314,7 +318,7 @@
}
break;
case DSI_CTRL_OP_CMD_TX:
- if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
+ if ((state->power_state != DSI_CTRL_POWER_VREG_ON) ||
(state->host_initialized != true) ||
(state->cmd_engine_state != DSI_CTRL_ENGINE_ON)) {
pr_debug("[%d]State error: op=%d: %d, %d, %d\n",
@@ -331,57 +335,18 @@
pr_debug("[%d] No change in state, host_init=%d\n",
dsi_ctrl->index, op_state);
rc = -EINVAL;
- } else if (state->power_state != DSI_CTRL_POWER_CORE_CLK_ON) {
+ } else if (state->power_state != DSI_CTRL_POWER_VREG_ON) {
pr_debug("[%d]State error: op=%d: %d\n",
dsi_ctrl->index, op, state->power_state);
rc = -EINVAL;
}
break;
- case DSI_CTRL_OP_ULPS_TOGGLE:
- if (state->ulps_enabled == op_state) {
- pr_debug("[%d] No change in state, ulps_enabled=%d\n",
- dsi_ctrl->index, op_state);
- rc = -EINVAL;
- } else if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
- (state->controller_state != DSI_CTRL_ENGINE_ON)) {
- pr_debug("[%d]State error: op=%d: %d, %d\n",
- dsi_ctrl->index,
- op,
- state->power_state,
- state->controller_state);
- rc = -EINVAL;
- }
- break;
- case DSI_CTRL_OP_CLAMP_TOGGLE:
- if (state->clamp_enabled == op_state) {
- pr_debug("[%d] No change in state, clamp_enabled=%d\n",
- dsi_ctrl->index, op_state);
- rc = -EINVAL;
- } else if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
- (state->controller_state != DSI_CTRL_ENGINE_ON)) {
- pr_debug("[%d]State error: op=%d: %d, %d\n",
- dsi_ctrl->index,
- op,
- state->power_state,
- state->controller_state);
- rc = -EINVAL;
- }
- break;
- case DSI_CTRL_OP_SET_CLK_SOURCE:
- if (state->power_state == DSI_CTRL_POWER_LINK_CLK_ON) {
- pr_debug("[%d] State error: op=%d: %d\n",
- dsi_ctrl->index,
- op,
- state->power_state);
- rc = -EINVAL;
- }
- break;
case DSI_CTRL_OP_TPG:
if (state->tpg_enabled == op_state) {
pr_debug("[%d] No change in state, tpg_enabled=%d\n",
dsi_ctrl->index, op_state);
rc = -EINVAL;
- } else if ((state->power_state != DSI_CTRL_POWER_LINK_CLK_ON) ||
+ } else if ((state->power_state != DSI_CTRL_POWER_VREG_ON) ||
(state->controller_state != DSI_CTRL_ENGINE_ON)) {
pr_debug("[%d]State error: op=%d: %d, %d\n",
dsi_ctrl->index,
@@ -392,7 +357,7 @@
}
break;
case DSI_CTRL_OP_PHY_SW_RESET:
- if (state->power_state != DSI_CTRL_POWER_CORE_CLK_ON) {
+ if (state->power_state != DSI_CTRL_POWER_VREG_ON) {
pr_debug("[%d]State error: op=%d: %d\n",
dsi_ctrl->index, op, state->power_state);
rc = -EINVAL;
@@ -422,23 +387,6 @@
switch (op) {
case DSI_CTRL_OP_POWER_STATE_CHANGE:
state->power_state = op_state;
- if (op_state == DSI_CTRL_POWER_OFF) {
- state->pwr_enabled = false;
- state->core_clk_enabled = false;
- state->link_clk_enabled = false;
- } else if (op_state == DSI_CTRL_POWER_VREG_ON) {
- state->pwr_enabled = true;
- state->core_clk_enabled = false;
- state->link_clk_enabled = false;
- } else if (op_state == DSI_CTRL_POWER_CORE_CLK_ON) {
- state->pwr_enabled = true;
- state->core_clk_enabled = true;
- state->link_clk_enabled = false;
- } else if (op_state == DSI_CTRL_POWER_LINK_CLK_ON) {
- state->pwr_enabled = true;
- state->core_clk_enabled = true;
- state->link_clk_enabled = true;
- }
break;
case DSI_CTRL_OP_CMD_ENGINE:
state->cmd_engine_state = op_state;
@@ -449,15 +397,6 @@
case DSI_CTRL_OP_HOST_ENGINE:
state->controller_state = op_state;
break;
- case DSI_CTRL_OP_ULPS_TOGGLE:
- state->ulps_enabled = (op_state == 1) ? true : false;
- break;
- case DSI_CTRL_OP_CLAMP_TOGGLE:
- state->clamp_enabled = (op_state == 1) ? true : false;
- break;
- case DSI_CTRL_OP_SET_CLK_SOURCE:
- state->clk_source_set = (op_state == 1) ? true : false;
- break;
case DSI_CTRL_OP_HOST_INIT:
state->host_initialized = (op_state == 1) ? true : false;
break;
@@ -657,7 +596,7 @@
struct dsi_regulator_info *regs;
struct regulator *vreg = NULL;
- rc = dsi_clk_pwr_get_dt_vreg_data(&pdev->dev,
+ rc = dsi_pwr_get_dt_vreg_data(&pdev->dev,
&ctrl->pwr_info.digital,
"qcom,core-supply-entries");
if (rc) {
@@ -665,7 +604,7 @@
goto error;
}
- rc = dsi_clk_pwr_get_dt_vreg_data(&pdev->dev,
+ rc = dsi_pwr_get_dt_vreg_data(&pdev->dev,
&ctrl->pwr_info.host_pwr,
"qcom,ctrl-supply-entries");
if (rc) {
@@ -775,7 +714,7 @@
}
static int dsi_ctrl_update_link_freqs(struct dsi_ctrl *dsi_ctrl,
- struct dsi_host_config *config)
+ struct dsi_host_config *config, void *clk_handle)
{
int rc = 0;
u32 num_of_lanes = 0;
@@ -809,10 +748,12 @@
pr_debug("byte_clk_rate = %llu, pclk_rate = %llu\n",
byte_clk_rate, pclk_rate);
- rc = dsi_clk_set_link_frequencies(&dsi_ctrl->clk_info.link_clks,
- pclk_rate,
- byte_clk_rate,
- config->esc_clk_rate_hz);
+ dsi_ctrl->clk_freq.byte_clk_rate = byte_clk_rate;
+ dsi_ctrl->clk_freq.pix_clk_rate = pclk_rate;
+ dsi_ctrl->clk_freq.esc_clk_rate = config->esc_clk_rate_hz;
+
+ rc = dsi_clk_set_link_frequencies(clk_handle, dsi_ctrl->clk_freq,
+ dsi_ctrl->index);
if (rc)
pr_err("Failed to update link frequencies\n");
@@ -824,11 +765,13 @@
int rc = 0;
if (enable) {
- rc = dsi_pwr_enable_regulator(&dsi_ctrl->pwr_info.host_pwr,
- true);
- if (rc) {
- pr_err("failed to enable host power regs, rc=%d\n", rc);
- goto error;
+ if (!dsi_ctrl->current_state.host_initialized) {
+ rc = dsi_pwr_enable_regulator(
+ &dsi_ctrl->pwr_info.host_pwr, true);
+ if (rc) {
+ pr_err("failed to enable host power regs\n");
+ goto error;
+ }
}
rc = dsi_pwr_enable_regulator(&dsi_ctrl->pwr_info.digital,
@@ -849,50 +792,19 @@
goto error;
}
- rc = dsi_pwr_enable_regulator(&dsi_ctrl->pwr_info.host_pwr,
- false);
- if (rc) {
- pr_err("failed to disable host power regs, rc=%d\n",
- rc);
- goto error;
+ if (!dsi_ctrl->current_state.host_initialized) {
+ rc = dsi_pwr_enable_regulator(
+ &dsi_ctrl->pwr_info.host_pwr, false);
+ if (rc) {
+ pr_err("failed to disable host power regs\n");
+ goto error;
+ }
}
}
error:
return rc;
}
-static int dsi_ctrl_vote_for_bandwidth(struct dsi_ctrl *dsi_ctrl, bool on)
-{
- int rc = 0;
- bool changed = false;
- struct dsi_ctrl_bus_scale_info *axi_bus = &dsi_ctrl->axi_bus_info;
-
- if (on) {
- if (axi_bus->refcount == 0)
- changed = true;
-
- axi_bus->refcount++;
- } else {
- if (axi_bus->refcount != 0) {
- axi_bus->refcount--;
-
- if (axi_bus->refcount == 0)
- changed = true;
- } else {
- pr_err("bus bw votes are not balanced\n");
- }
- }
-
- if (changed) {
- rc = msm_bus_scale_client_update_request(axi_bus->bus_handle,
- on ? 1 : 0);
- if (rc)
- pr_err("bus scale client update failed, rc=%d\n", rc);
- }
-
- return rc;
-}
-
static int dsi_ctrl_copy_and_pad_cmd(struct dsi_ctrl *dsi_ctrl,
const struct mipi_dsi_packet *packet,
u8 **buffer,
@@ -1089,16 +1001,22 @@
static int dsi_enable_ulps(struct dsi_ctrl *dsi_ctrl)
{
int rc = 0;
- u32 lanes;
+ u32 lanes = 0;
u32 ulps_lanes;
if (dsi_ctrl->host_config.panel_mode == DSI_OP_CMD_MODE)
lanes = dsi_ctrl->host_config.common_config.data_lanes;
- lanes |= DSI_CLOCK_LANE;
- dsi_ctrl->hw.ops.ulps_request(&dsi_ctrl->hw, lanes);
+ rc = dsi_ctrl->hw.ops.ulps_ops.wait_for_lane_idle(&dsi_ctrl->hw, lanes);
+ if (rc) {
+ pr_err("lanes not entering idle, skip ULPS\n");
+ return rc;
+ }
- ulps_lanes = dsi_ctrl->hw.ops.get_lanes_in_ulps(&dsi_ctrl->hw);
+ lanes |= DSI_CLOCK_LANE;
+ dsi_ctrl->hw.ops.ulps_ops.ulps_request(&dsi_ctrl->hw, lanes);
+
+ ulps_lanes = dsi_ctrl->hw.ops.ulps_ops.get_lanes_in_ulps(&dsi_ctrl->hw);
if ((lanes & ulps_lanes) != lanes) {
pr_err("Failed to enter ULPS, request=0x%x, actual=0x%x\n",
@@ -1118,21 +1036,16 @@
lanes = dsi_ctrl->host_config.common_config.data_lanes;
lanes |= DSI_CLOCK_LANE;
- ulps_lanes = dsi_ctrl->hw.ops.get_lanes_in_ulps(&dsi_ctrl->hw);
+ ulps_lanes = dsi_ctrl->hw.ops.ulps_ops.get_lanes_in_ulps(&dsi_ctrl->hw);
if ((lanes & ulps_lanes) != lanes)
pr_err("Mismatch between lanes in ULPS\n");
lanes &= ulps_lanes;
- dsi_ctrl->hw.ops.ulps_exit(&dsi_ctrl->hw, lanes);
+ dsi_ctrl->hw.ops.ulps_ops.ulps_exit(&dsi_ctrl->hw, lanes);
- /* 1 ms delay is recommended by specification */
- udelay(1000);
-
- dsi_ctrl->hw.ops.clear_ulps_request(&dsi_ctrl->hw, lanes);
-
- ulps_lanes = dsi_ctrl->hw.ops.get_lanes_in_ulps(&dsi_ctrl->hw);
+ ulps_lanes = dsi_ctrl->hw.ops.ulps_ops.get_lanes_in_ulps(&dsi_ctrl->hw);
if (ulps_lanes & lanes) {
pr_err("Lanes (0x%x) stuck in ULPS\n", ulps_lanes);
rc = -EIO;
@@ -1148,15 +1061,9 @@
struct dsi_ctrl_state_info *state = &dsi_ctrl->current_state;
if (!splash_enabled) {
- state->power_state = DSI_CTRL_POWER_OFF;
+ state->power_state = DSI_CTRL_POWER_VREG_OFF;
state->cmd_engine_state = DSI_CTRL_ENGINE_OFF;
state->vid_engine_state = DSI_CTRL_ENGINE_OFF;
- state->pwr_enabled = false;
- state->core_clk_enabled = false;
- state->link_clk_enabled = false;
- state->ulps_enabled = false;
- state->clamp_enabled = false;
- state->clk_source_set = false;
}
return rc;
@@ -1218,9 +1125,9 @@
return rc;
}
-static int dsi_enable_io_clamp(struct dsi_ctrl *dsi_ctrl, bool enable)
+static int dsi_enable_io_clamp(struct dsi_ctrl *dsi_ctrl,
+ bool enable, bool ulps_enabled)
{
- bool en_ulps = dsi_ctrl->current_state.ulps_enabled;
u32 lanes = 0;
if (dsi_ctrl->host_config.panel_mode == DSI_OP_CMD_MODE)
@@ -1229,9 +1136,11 @@
lanes |= DSI_CLOCK_LANE;
if (enable)
- dsi_ctrl->hw.ops.clamp_enable(&dsi_ctrl->hw, lanes, en_ulps);
+ dsi_ctrl->hw.ops.clamp_enable(&dsi_ctrl->hw,
+ lanes, ulps_enabled);
else
- dsi_ctrl->hw.ops.clamp_disable(&dsi_ctrl->hw, lanes, en_ulps);
+ dsi_ctrl->hw.ops.clamp_disable(&dsi_ctrl->hw,
+ lanes, ulps_enabled);
return 0;
}
@@ -1508,6 +1417,19 @@
return rc;
}
+int dsi_ctrl_clk_cb_register(struct dsi_ctrl *dsi_ctrl,
+ struct clk_ctrl_cb *clk_cb)
+{
+ if (!dsi_ctrl || !clk_cb) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ dsi_ctrl->clk_cb.priv = clk_cb->priv;
+ dsi_ctrl->clk_cb.dsi_clk_cb = clk_cb->dsi_clk_cb;
+ return 0;
+}
+
/**
* dsi_ctrl_phy_sw_reset() - perform a PHY software reset
* @dsi_ctrl: DSI controller handle.
@@ -1588,6 +1510,63 @@
}
/**
+ * dsi_ctrl_setup() - Setup DSI host hardware while coming out of idle screen.
+ * @dsi_ctrl: DSI controller handle.
+ *
+ * Initializes DSI controller hardware with host configuration provided by
+ * dsi_ctrl_update_host_config(). Initialization can be performed only during
+ * DSI_CTRL_POWER_CORE_CLK_ON state and after the PHY SW reset has been
+ * performed.
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl)
+{
+ int rc = 0;
+
+ if (!dsi_ctrl) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&dsi_ctrl->ctrl_lock);
+
+ dsi_ctrl->hw.ops.setup_lane_map(&dsi_ctrl->hw,
+ &dsi_ctrl->host_config.lane_map);
+
+ dsi_ctrl->hw.ops.host_setup(&dsi_ctrl->hw,
+ &dsi_ctrl->host_config.common_config);
+
+ if (dsi_ctrl->host_config.panel_mode == DSI_OP_CMD_MODE) {
+ dsi_ctrl->hw.ops.cmd_engine_setup(&dsi_ctrl->hw,
+ &dsi_ctrl->host_config.common_config,
+ &dsi_ctrl->host_config.u.cmd_engine);
+
+ dsi_ctrl->hw.ops.setup_cmd_stream(&dsi_ctrl->hw,
+ dsi_ctrl->host_config.video_timing.h_active,
+ dsi_ctrl->host_config.video_timing.h_active * 3,
+ dsi_ctrl->host_config.video_timing.v_active,
+ 0x0);
+ dsi_ctrl->hw.ops.cmd_engine_en(&dsi_ctrl->hw, true);
+ } else {
+ dsi_ctrl->hw.ops.video_engine_setup(&dsi_ctrl->hw,
+ &dsi_ctrl->host_config.common_config,
+ &dsi_ctrl->host_config.u.video_engine);
+ dsi_ctrl->hw.ops.set_video_timing(&dsi_ctrl->hw,
+ &dsi_ctrl->host_config.video_timing);
+ dsi_ctrl->hw.ops.video_engine_en(&dsi_ctrl->hw, true);
+ }
+
+ dsi_ctrl->hw.ops.enable_status_interrupts(&dsi_ctrl->hw, 0x0);
+ dsi_ctrl->hw.ops.enable_error_interrupts(&dsi_ctrl->hw, 0x0);
+ dsi_ctrl->hw.ops.ctrl_en(&dsi_ctrl->hw, true);
+
+ mutex_unlock(&dsi_ctrl->ctrl_lock);
+ return rc;
+}
+
+
+/**
* dsi_ctrl_host_init() - Initialize DSI host hardware.
* @dsi_ctrl: DSI controller handle.
*
@@ -1702,7 +1681,7 @@
*/
int dsi_ctrl_update_host_config(struct dsi_ctrl *ctrl,
struct dsi_host_config *config,
- int flags)
+ int flags, void *clk_handle)
{
int rc = 0;
@@ -1720,7 +1699,7 @@
}
if (!(flags & DSI_MODE_FLAG_SEAMLESS)) {
- rc = dsi_ctrl_update_link_freqs(ctrl, config);
+ rc = dsi_ctrl_update_link_freqs(ctrl, config, clk_handle);
if (rc) {
pr_err("[%s] failed to update link frequencies, rc=%d\n",
ctrl->name, rc);
@@ -1795,12 +1774,6 @@
goto error;
}
- rc = dsi_ctrl_vote_for_bandwidth(dsi_ctrl, true);
- if (rc) {
- pr_err("bandwidth request failed, rc=%d\n", rc);
- goto error;
- }
-
if (flags & DSI_CTRL_CMD_READ) {
rc = dsi_message_rx(dsi_ctrl, msg, flags);
if (rc)
@@ -1813,7 +1786,6 @@
dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_CMD_TX, 0x0);
- (void)dsi_ctrl_vote_for_bandwidth(dsi_ctrl, false);
error:
mutex_unlock(&dsi_ctrl->ctrl_lock);
return rc;
@@ -1880,10 +1852,6 @@
enum dsi_power_state state)
{
int rc = 0;
- bool core_clk_enable = false;
- bool link_clk_enable = false;
- bool reg_enable = false;
- struct dsi_ctrl_state_info *drv_state;
if (!dsi_ctrl || (state >= DSI_CTRL_POWER_MAX)) {
pr_err("Invalid Params\n");
@@ -1900,57 +1868,14 @@
goto error;
}
- if (state == DSI_CTRL_POWER_LINK_CLK_ON)
- reg_enable = core_clk_enable = link_clk_enable = true;
- else if (state == DSI_CTRL_POWER_CORE_CLK_ON)
- reg_enable = core_clk_enable = true;
- else if (state == DSI_CTRL_POWER_VREG_ON)
- reg_enable = true;
-
- drv_state = &dsi_ctrl->current_state;
-
- if ((reg_enable) && (reg_enable != drv_state->pwr_enabled)) {
+ if (state == DSI_CTRL_POWER_VREG_ON) {
rc = dsi_ctrl_enable_supplies(dsi_ctrl, true);
if (rc) {
pr_err("[%d]failed to enable voltage supplies, rc=%d\n",
dsi_ctrl->index, rc);
goto error;
}
- }
-
- if ((core_clk_enable) &&
- (core_clk_enable != drv_state->core_clk_enabled)) {
- rc = dsi_clk_enable_core_clks(&dsi_ctrl->clk_info.core_clks,
- true);
- if (rc) {
- pr_err("[%d] failed to enable core clocks, rc=%d\n",
- dsi_ctrl->index, rc);
- goto error;
- }
- }
-
- if (link_clk_enable != drv_state->link_clk_enabled) {
- rc = dsi_clk_enable_link_clks(&dsi_ctrl->clk_info.link_clks,
- link_clk_enable);
- if (rc) {
- pr_err("[%d] failed to enable link clocks, rc=%d\n",
- dsi_ctrl->index, rc);
- goto error;
- }
- }
-
- if ((!core_clk_enable) &&
- (core_clk_enable != drv_state->core_clk_enabled)) {
- rc = dsi_clk_enable_core_clks(&dsi_ctrl->clk_info.core_clks,
- false);
- if (rc) {
- pr_err("[%d] failed to disable core clocks, rc=%d\n",
- dsi_ctrl->index, rc);
- goto error;
- }
- }
-
- if ((!reg_enable) && (reg_enable != drv_state->pwr_enabled)) {
+ } else if (state == DSI_CTRL_POWER_VREG_OFF) {
rc = dsi_ctrl_enable_supplies(dsi_ctrl, false);
if (rc) {
pr_err("[%d]failed to disable vreg supplies, rc=%d\n",
@@ -2147,14 +2072,14 @@
}
/**
- * dsi_ctrl_set_ulps() - set ULPS state for DSI lanes.
- * @dsi_ctrl: DSI controller handle.
- * @enable: enable/disable ULPS.
- *
- * ULPS can be enabled/disabled after DSI host engine is turned on.
- *
- * Return: error code.
- */
+ * dsi_ctrl_set_ulps() - set ULPS state for DSI lanes.
+ * @dsi_ctrl: DSI controller handle.
+ * @enable: enable/disable ULPS.
+ *
+ * ULPS can be enabled/disabled after DSI host engine is turned on.
+ *
+ * Return: error code.
+ */
int dsi_ctrl_set_ulps(struct dsi_ctrl *dsi_ctrl, bool enable)
{
int rc = 0;
@@ -2164,15 +2089,14 @@
return -EINVAL;
}
- mutex_lock(&dsi_ctrl->ctrl_lock);
-
- rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_ULPS_TOGGLE, enable);
- if (rc) {
- pr_err("[DSI_%d] Controller state check failed, rc=%d\n",
- dsi_ctrl->index, rc);
- goto error;
+ if (!dsi_ctrl->hw.ops.ulps_ops.ulps_request ||
+ !dsi_ctrl->hw.ops.ulps_ops.ulps_exit) {
+ pr_debug("DSI controller ULPS ops not present\n");
+ return 0;
}
+ mutex_lock(&dsi_ctrl->ctrl_lock);
+
if (enable)
rc = dsi_enable_ulps(dsi_ctrl);
else
@@ -2180,12 +2104,11 @@
if (rc) {
pr_err("[DSI_%d] Ulps state change(%d) failed, rc=%d\n",
- dsi_ctrl->index, enable, rc);
+ dsi_ctrl->index, enable, rc);
goto error;
}
-
pr_debug("[DSI_%d] ULPS state = %d\n", dsi_ctrl->index, enable);
- dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_ULPS_TOGGLE, enable);
+
error:
mutex_unlock(&dsi_ctrl->ctrl_lock);
return rc;
@@ -2200,7 +2123,8 @@
*
* Return: error code.
*/
-int dsi_ctrl_set_clamp_state(struct dsi_ctrl *dsi_ctrl, bool enable)
+int dsi_ctrl_set_clamp_state(struct dsi_ctrl *dsi_ctrl,
+ bool enable, bool ulps_enabled)
{
int rc = 0;
@@ -2211,21 +2135,13 @@
mutex_lock(&dsi_ctrl->ctrl_lock);
- rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_CLAMP_TOGGLE, enable);
- if (rc) {
- pr_err("[DSI_%d] Controller state check failed, rc=%d\n",
- dsi_ctrl->index, rc);
- goto error;
- }
-
- rc = dsi_enable_io_clamp(dsi_ctrl, enable);
+ rc = dsi_enable_io_clamp(dsi_ctrl, enable, ulps_enabled);
if (rc) {
pr_err("[DSI_%d] Failed to enable IO clamp\n", dsi_ctrl->index);
goto error;
}
pr_debug("[DSI_%d] Clamp state = %d\n", dsi_ctrl->index, enable);
- dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_CLAMP_TOGGLE, enable);
error:
mutex_unlock(&dsi_ctrl->ctrl_lock);
return rc;
@@ -2244,7 +2160,6 @@
struct dsi_clk_link_set *source_clks)
{
int rc = 0;
- u32 op_state = 0;
if (!dsi_ctrl || !source_clks) {
pr_err("Invalid params\n");
@@ -2253,17 +2168,6 @@
mutex_lock(&dsi_ctrl->ctrl_lock);
- if (source_clks->pixel_clk && source_clks->byte_clk)
- op_state = 1;
-
- rc = dsi_ctrl_check_state(dsi_ctrl, DSI_CTRL_OP_SET_CLK_SOURCE,
- op_state);
- if (rc) {
- pr_err("[DSI_%d] Controller state check failed, rc=%d\n",
- dsi_ctrl->index, rc);
- goto error;
- }
-
rc = dsi_clk_update_parent(source_clks, &dsi_ctrl->clk_info.rcg_clks);
if (rc) {
pr_err("[DSI_%d]Failed to update link clk parent, rc=%d\n",
@@ -2278,8 +2182,6 @@
pr_debug("[DSI_%d] Source clocks are updated\n", dsi_ctrl->index);
- dsi_ctrl_update_state(dsi_ctrl, DSI_CTRL_OP_SET_CLK_SOURCE, op_state);
-
error:
mutex_unlock(&dsi_ctrl->ctrl_lock);
return rc;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
index 993a35c..5b3e25c 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -19,7 +19,8 @@
#include "dsi_defs.h"
#include "dsi_ctrl_hw.h"
-#include "dsi_clk_pwr.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
#include "drm_mipi_dsi.h"
/*
@@ -41,18 +42,14 @@
/**
* enum dsi_power_state - defines power states for dsi controller.
- * @DSI_CTRL_POWER_OFF: DSI controller is powered down.
+ * @DSI_CTRL_POWER_VREG_OFF: Digital and analog supplies for DSI controller
+ turned off
* @DSI_CTRL_POWER_VREG_ON: Digital and analog supplies for DSI controller
- * are powered on.
- * @DSI_CTRL_POWER_CORE_CLK_ON: DSI core clocks for register access are enabled.
- * @DSI_CTRL_POWER_LINK_CLK_ON: DSI link clocks for link transfer are enabled.
* @DSI_CTRL_POWER_MAX: Maximum value.
*/
enum dsi_power_state {
- DSI_CTRL_POWER_OFF = 0,
+ DSI_CTRL_POWER_VREG_OFF = 0,
DSI_CTRL_POWER_VREG_ON,
- DSI_CTRL_POWER_CORE_CLK_ON,
- DSI_CTRL_POWER_LINK_CLK_ON,
DSI_CTRL_POWER_MAX,
};
@@ -112,38 +109,26 @@
* struct dsi_ctrl_bus_scale_info - Bus scale info for msm-bus bandwidth voting
* @bus_scale_table: Bus scale voting usecases.
* @bus_handle: Handle used for voting bandwidth.
- * @refcount: reference count.
*/
struct dsi_ctrl_bus_scale_info {
struct msm_bus_scale_pdata *bus_scale_table;
u32 bus_handle;
- u32 refcount;
};
/**
* struct dsi_ctrl_state_info - current driver state information
- * @power_state: Controller power state.
+ * @power_state: Status of power states on DSI controller.
* @cmd_engine_state: Status of DSI command engine.
* @vid_engine_state: Status of DSI video engine.
* @controller_state: Status of DSI Controller engine.
- * @pwr_enabled: Set to true, if voltage supplies are enabled.
- * @core_clk_enabled: Set to true, if core clocks are enabled.
- * @lin_clk_enabled: Set to true, if link clocks are enabled.
- * @ulps_enabled: Set to true, if lanes are in ULPS state.
- * @clamp_enabled: Set to true, if PHY output is clamped.
- * @clk_source_set: Set to true, if parent is set for DSI link clocks.
+ * @host_initialized: Boolean to indicate status of DSi host Initialization
+ * @tpg_enabled: Boolean to indicate whether tpg is enabled.
*/
struct dsi_ctrl_state_info {
enum dsi_power_state power_state;
enum dsi_engine_state cmd_engine_state;
enum dsi_engine_state vid_engine_state;
enum dsi_engine_state controller_state;
- bool pwr_enabled;
- bool core_clk_enabled;
- bool link_clk_enabled;
- bool ulps_enabled;
- bool clamp_enabled;
- bool clk_source_set;
bool host_initialized;
bool tpg_enabled;
};
@@ -189,9 +174,11 @@
* @drm_dev: Pointer to DRM device.
* @version: DSI controller version.
* @hw: DSI controller hardware object.
- * @current_state; Current driver and hardware state.
+ * @current_state: Current driver and hardware state.
+ * @clk_cb: Callback for DSI clock control.
* @int_info: Interrupt information.
* @clk_info: Clock information.
+ * @clk_freq: DSi Link clock frequency information.
* @pwr_info: Power information.
* @axi_bus_info: AXI bus information.
* @host_config: Current host configuration.
@@ -212,10 +199,12 @@
/* Current state */
struct dsi_ctrl_state_info current_state;
+ struct clk_ctrl_cb clk_cb;
struct dsi_ctrl_interrupts int_info;
/* Clock and power states */
struct dsi_ctrl_clk_info clk_info;
+ struct link_clk_freq clk_freq;
struct dsi_ctrl_power_info pwr_info;
struct dsi_ctrl_bus_scale_info axi_bus_info;
@@ -290,6 +279,7 @@
* @dsi_ctrl: DSI controller handle.
* @config: DSI host configuration.
* @flags: dsi_mode_flags modifying the behavior
+ * @clk_handle: Clock handle for DSI clocks
*
* Updates driver with new Host configuration to use for host initialization.
* This function call will only update the software context. The stored
@@ -299,7 +289,7 @@
*/
int dsi_ctrl_update_host_config(struct dsi_ctrl *dsi_ctrl,
struct dsi_host_config *config,
- int flags);
+ int flags, void *clk_handle);
/**
* dsi_ctrl_async_timing_update() - update only controller timing
@@ -353,6 +343,30 @@
int dsi_ctrl_host_deinit(struct dsi_ctrl *dsi_ctrl);
/**
+ * dsi_ctrl_set_ulps() - set ULPS state for DSI lanes.
+ * @dsi_ctrl: DSI controller handle.
+ * @enable: enable/disable ULPS.
+ *
+ * ULPS can be enabled/disabled after DSI host engine is turned on.
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_set_ulps(struct dsi_ctrl *dsi_ctrl, bool enable);
+
+/**
+ * dsi_ctrl_setup() - Setup DSI host hardware while coming out of idle screen.
+ * @dsi_ctrl: DSI controller handle.
+ *
+ * Initializes DSI controller hardware with host configuration provided by
+ * dsi_ctrl_update_host_config(). Initialization can be performed only during
+ * DSI_CTRL_POWER_CORE_CLK_ON state and after the PHY SW reset has been
+ * performed.
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_setup(struct dsi_ctrl *dsi_ctrl);
+
+/**
* dsi_ctrl_set_tpg_state() - enable/disable test pattern on the controller
* @dsi_ctrl: DSI controller handle.
* @on: enable/disable test pattern.
@@ -362,6 +376,7 @@
*
* Return: error code.
*/
+
int dsi_ctrl_set_tpg_state(struct dsi_ctrl *dsi_ctrl, bool on);
/**
@@ -454,15 +469,29 @@
int dsi_ctrl_set_ulps(struct dsi_ctrl *dsi_ctrl, bool enable);
/**
+ * dsi_ctrl_clk_cb_register() - Register DSI controller clk control callback
+ * @dsi_ctrl: DSI controller handle.
+ * @clk__cb: Structure containing callback for clock control.
+ *
+ * Register call for DSI clock control
+ *
+ * Return: error code.
+ */
+int dsi_ctrl_clk_cb_register(struct dsi_ctrl *dsi_ctrl,
+ struct clk_ctrl_cb *clk_cb);
+
+/**
* dsi_ctrl_set_clamp_state() - set clamp state for DSI phy
* @dsi_ctrl: DSI controller handle.
* @enable: enable/disable clamping.
+ * @ulps_enabled: ulps state.
*
* Clamps can be enabled/disabled while DSI contoller is still turned on.
*
* Return: error code.
*/
-int dsi_ctrl_set_clamp_state(struct dsi_ctrl *dsi_Ctrl, bool enable);
+int dsi_ctrl_set_clamp_state(struct dsi_ctrl *dsi_Ctrl,
+ bool enable, bool ulps_enabled);
/**
* dsi_ctrl_set_clock_source() - set clock source fpr dsi link clocks
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
index b81cdaf..a25c780 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -215,6 +215,50 @@
struct dsi_ctrl_hw;
+struct ulps_config_ops {
+ /**
+ * wait_for_lane_idle() - wait for DSI lanes to go to idle state
+ * @ctrl: Pointer to the controller host hardware.
+ * @lanes: ORed list of lanes (enum dsi_data_lanes) which need
+ * to be checked to be in idle state.
+ */
+ int (*wait_for_lane_idle)(struct dsi_ctrl_hw *ctrl, u32 lanes);
+
+ /**
+ * ulps_request() - request ulps entry for specified lanes
+ * @ctrl: Pointer to the controller host hardware.
+ * @lanes: ORed list of lanes (enum dsi_data_lanes) which need
+ * to enter ULPS.
+ *
+ * Caller should check if lanes are in ULPS mode by calling
+ * get_lanes_in_ulps() operation.
+ */
+ void (*ulps_request)(struct dsi_ctrl_hw *ctrl, u32 lanes);
+
+ /**
+ * ulps_exit() - exit ULPS on specified lanes
+ * @ctrl: Pointer to the controller host hardware.
+ * @lanes: ORed list of lanes (enum dsi_data_lanes) which need
+ * to exit ULPS.
+ *
+ * Caller should check if lanes are in active mode by calling
+ * get_lanes_in_ulps() operation.
+ */
+ void (*ulps_exit)(struct dsi_ctrl_hw *ctrl, u32 lanes);
+
+ /**
+ * get_lanes_in_ulps() - returns the list of lanes in ULPS mode
+ * @ctrl: Pointer to the controller host hardware.
+ *
+ * Returns an ORed list of lanes (enum dsi_data_lanes) that are in ULPS
+ * state. If 0 is returned, all the lanes are active.
+ *
+ * Return: List of lanes in ULPS state.
+ */
+ u32 (*get_lanes_in_ulps)(struct dsi_ctrl_hw *ctrl);
+
+};
+
/**
* struct dsi_ctrl_hw_ops - operations supported by dsi host hardware
*/
@@ -383,53 +427,13 @@
u8 *rd_buf,
u32 total_read_len);
- /**
- * ulps_request() - request ulps entry for specified lanes
- * @ctrl: Pointer to the controller host hardware.
- * @lanes: ORed list of lanes (enum dsi_data_lanes) which need
- * to enter ULPS.
- *
- * Caller should check if lanes are in ULPS mode by calling
- * get_lanes_in_ulps() operation.
- */
- void (*ulps_request)(struct dsi_ctrl_hw *ctrl, u32 lanes);
-
- /**
- * ulps_exit() - exit ULPS on specified lanes
- * @ctrl: Pointer to the controller host hardware.
- * @lanes: ORed list of lanes (enum dsi_data_lanes) which need
- * to exit ULPS.
- *
- * Caller should check if lanes are in active mode by calling
- * get_lanes_in_ulps() operation.
- */
- void (*ulps_exit)(struct dsi_ctrl_hw *ctrl, u32 lanes);
-
- /**
- * clear_ulps_request() - clear ulps request once all lanes are active
- * @ctrl: Pointer to controller host hardware.
- * @lanes: ORed list of lanes (enum dsi_data_lanes).
- *
- * ULPS request should be cleared after the lanes have exited ULPS.
- */
- void (*clear_ulps_request)(struct dsi_ctrl_hw *ctrl, u32 lanes);
-
- /**
- * get_lanes_in_ulps() - returns the list of lanes in ULPS mode
- * @ctrl: Pointer to the controller host hardware.
- *
- * Returns an ORed list of lanes (enum dsi_data_lanes) that are in ULPS
- * state. If 0 is returned, all the lanes are active.
- *
- * Return: List of lanes in ULPS state.
- */
- u32 (*get_lanes_in_ulps)(struct dsi_ctrl_hw *ctrl);
+ struct ulps_config_ops ulps_ops;
/**
* clamp_enable() - enable DSI clamps to keep PHY driving a stable link
- * @ctrl: Pointer to the controller host hardware.
- * @lanes: ORed list of lanes which need to be clamped.
- * @enable_ulps: TODO:??
+ * @ctrl: Pointer to the controller host hardware.
+ * @lanes: ORed list of lanes which need to have clamps released.
+ * @enable_ulps: TODO:??
*/
void (*clamp_enable)(struct dsi_ctrl_hw *ctrl,
u32 lanes,
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c
index ca04eed..e367db3 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl_hw_1_4.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -14,6 +14,7 @@
#define pr_fmt(fmt) "dsi-hw:" fmt
#include <linux/delay.h>
+#include <linux/iopoll.h>
#include "dsi_ctrl_hw.h"
#include "dsi_ctrl_reg_1_4.h"
@@ -591,6 +592,70 @@
pr_debug("[DSI_%d] Read %d bytes\n", ctrl->index, j);
return j;
}
+
+
+/**
+ * dsi_ctrl_hw_wait_for_lane_idle()
+ * This function waits for all the active DSI lanes to be idle by polling all
+ * the FIFO_EMPTY bits and polling he lane status to ensure that all the lanes
+ * are in stop state. This function assumes that the bus clocks required to
+ * access the registers are already turned on.
+ *
+ * @ctrl: Pointer to the controller host hardware.
+ * @lanes: ORed list of lanes (enum dsi_data_lanes) which need
+ * to be stopped.
+ *
+ * return: Error code.
+ */
+int dsi_ctrl_hw_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes)
+{
+ int rc = 0, val = 0;
+ u32 stop_state_mask = 0, fifo_empty_mask = 0;
+ u32 const sleep_us = 10;
+ u32 const timeout_us = 100;
+
+ if (lanes & DSI_DATA_LANE_0) {
+ stop_state_mask |= BIT(0);
+ fifo_empty_mask |= (BIT(12) | BIT(16));
+ }
+ if (lanes & DSI_DATA_LANE_1) {
+ stop_state_mask |= BIT(1);
+ fifo_empty_mask |= BIT(20);
+ }
+ if (lanes & DSI_DATA_LANE_2) {
+ stop_state_mask |= BIT(2);
+ fifo_empty_mask |= BIT(24);
+ }
+ if (lanes & DSI_DATA_LANE_3) {
+ stop_state_mask |= BIT(3);
+ fifo_empty_mask |= BIT(28);
+ }
+
+ pr_debug("%s: polling for fifo empty, mask=0x%08x\n", __func__,
+ fifo_empty_mask);
+ rc = readl_poll_timeout(ctrl->base + DSI_FIFO_STATUS, val,
+ (val & fifo_empty_mask), sleep_us, timeout_us);
+ if (rc) {
+ pr_err("%s: fifo not empty, FIFO_STATUS=0x%08x\n",
+ __func__, val);
+ goto error;
+ }
+
+ pr_debug("%s: polling for lanes to be in stop state, mask=0x%08x\n",
+ __func__, stop_state_mask);
+ rc = readl_poll_timeout(ctrl->base + DSI_LANE_STATUS, val,
+ (val & stop_state_mask), sleep_us, timeout_us);
+ if (rc) {
+ pr_err("%s: lanes not in stop state, LANE_STATUS=0x%08x\n",
+ __func__, val);
+ goto error;
+ }
+
+error:
+ return rc;
+
+}
+
/**
* ulps_request() - request ulps entry for specified lanes
* @ctrl: Pointer to the controller host hardware.
@@ -615,12 +680,28 @@
if (lanes & DSI_DATA_LANE_3)
reg |= BIT(3);
+ /*
+ * ULPS entry request. Wait for short time to make sure
+ * that the lanes enter ULPS. Recommended as per HPG.
+ */
DSI_W32(ctrl, DSI_LANE_CTRL, reg);
+ usleep_range(100, 110);
pr_debug("[DSI_%d] ULPS requested for lanes 0x%x\n", ctrl->index,
lanes);
}
+static void dsi_ctrl_hw_dln0_phy_err(struct dsi_ctrl_hw *ctrl)
+{
+ u32 status = 0;
+
+ status = DSI_R32(ctrl, DSI_DLN0_PHY_ERR);
+ if (status & 0x011111) {
+ DSI_W32(ctrl, DSI_DLN0_PHY_ERR, status);
+ pr_err("%s: phy_err_status = %x\n", __func__, status);
+ }
+}
+
/**
* ulps_exit() - exit ULPS on specified lanes
* @ctrl: Pointer to the controller host hardware.
@@ -634,9 +715,15 @@
{
u32 reg = 0;
- reg = DSI_R32(ctrl, DSI_LANE_CTRL);
+ /*
+ * Clear out any phy errors prior to exiting ULPS
+ * This fixes certain instances where phy does not exit
+ * ULPS cleanly. Also, do not print error during such cases.
+ */
+ dsi_ctrl_hw_dln0_phy_err(ctrl);
+
if (lanes & DSI_CLOCK_LANE)
- reg |= BIT(12);
+ reg = BIT(12);
if (lanes & DSI_DATA_LANE_0)
reg |= BIT(8);
if (lanes & DSI_DATA_LANE_1)
@@ -646,45 +733,26 @@
if (lanes & DSI_DATA_LANE_3)
reg |= BIT(11);
+ /*
+ * ULPS Exit Request
+ * Hardware requirement is to wait for at least 1ms
+ */
DSI_W32(ctrl, DSI_LANE_CTRL, reg);
+ usleep_range(1000, 1010);
+ /*
+ * Sometimes when exiting ULPS, it is possible that some DSI
+ * lanes are not in the stop state which could lead to DSI
+ * commands not going through. To avoid this, force the lanes
+ * to be in stop state.
+ */
+ DSI_W32(ctrl, DSI_LANE_CTRL, reg << 8);
+ DSI_W32(ctrl, DSI_LANE_CTRL, 0x0);
pr_debug("[DSI_%d] ULPS exit request for lanes=0x%x\n",
ctrl->index, lanes);
}
/**
- * clear_ulps_request() - clear ulps request once all lanes are active
- * @ctrl: Pointer to controller host hardware.
- * @lanes: ORed list of lanes (enum dsi_data_lanes).
- *
- * ULPS request should be cleared after the lanes have exited ULPS.
- */
-void dsi_ctrl_hw_14_clear_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes)
-{
- u32 reg = 0;
-
- reg = DSI_R32(ctrl, DSI_LANE_CTRL);
- reg &= ~BIT(4); /* clock lane */
- if (lanes & DSI_DATA_LANE_0)
- reg &= ~BIT(0);
- if (lanes & DSI_DATA_LANE_1)
- reg &= ~BIT(1);
- if (lanes & DSI_DATA_LANE_2)
- reg &= ~BIT(2);
- if (lanes & DSI_DATA_LANE_3)
- reg &= ~BIT(3);
-
- DSI_W32(ctrl, DSI_LANE_CTRL, reg);
- /*
- * HPG recommends separate writes for clearing ULPS_REQUEST and
- * ULPS_EXIT.
- */
- DSI_W32(ctrl, DSI_LANE_CTRL, 0x0);
-
- pr_debug("[DSI_%d] ULPS request cleared\n", ctrl->index);
-}
-
-/**
* get_lanes_in_ulps() - returns the list of lanes in ULPS mode
* @ctrl: Pointer to the controller host hardware.
*
@@ -718,7 +786,7 @@
* clamp_enable() - enable DSI clamps to keep PHY driving a stable link
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes which need to be clamped.
- * @enable_ulps: TODO:??
+ * @enable_ulps: Boolean to specify if ULPS is enabled in DSI controller
*/
void dsi_ctrl_hw_14_clamp_enable(struct dsi_ctrl_hw *ctrl,
u32 lanes,
@@ -761,15 +829,16 @@
clamp_reg |= BIT(0);
}
- clamp_reg |= BIT(15); /* Enable clamp */
-
reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
reg |= (clamp_reg << bit_shift);
DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
+ reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
+ reg |= (BIT(15) << bit_shift); /* Enable clamp */
+ DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
- reg |= BIT(30);
+ reg |= BIT(30); /* Disable PHY reset */
DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
pr_debug("[DSI_%d] Clamps enabled for lanes=0x%x\n", ctrl->index,
@@ -780,7 +849,7 @@
* clamp_disable() - disable DSI clamps
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes which need to have clamps released.
- * @disable_ulps: TODO:??
+ * @disable_ulps: Boolean to specify if ULPS is enabled in DSI controller
*/
void dsi_ctrl_hw_14_clamp_disable(struct dsi_ctrl_hw *ctrl,
u32 lanes,
@@ -826,7 +895,7 @@
clamp_reg |= BIT(15); /* Enable clamp */
clamp_reg <<= bit_shift;
- /* Disable PHY reset skip */
+ /* Clear disable PHY reset bit */
reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
reg &= ~BIT(30);
DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
index 5a166a4..06f9ccb 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -23,6 +23,8 @@
#include "dsi_ctrl.h"
#include "dsi_ctrl_hw.h"
#include "dsi_drm.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
#define to_dsi_display(x) container_of(x, struct dsi_display, host)
@@ -164,6 +166,273 @@
}
}
+static int dsi_display_is_ulps_req_valid(struct dsi_display *display,
+ bool enable)
+{
+ /* TODO: make checks based on cont. splash */
+ int splash_enabled = false;
+
+ pr_debug("checking ulps req validity\n");
+
+ if (!dsi_panel_ulps_feature_enabled(display->panel))
+ return false;
+
+ /* TODO: ULPS during suspend */
+ if (!dsi_panel_initialized(display->panel))
+ return false;
+
+ if (enable && display->ulps_enabled) {
+ pr_debug("ULPS already enabled\n");
+ return false;
+ } else if (!enable && !display->ulps_enabled) {
+ pr_debug("ULPS already disabled\n");
+ return false;
+ }
+
+ /*
+ * No need to enter ULPS when transitioning from splash screen to
+ * boot animation since it is expected that the clocks would be turned
+ * right back on.
+ */
+ if (enable && splash_enabled)
+ return false;
+
+ return true;
+}
+
+
+/**
+ * dsi_display_set_ulps() - set ULPS state for DSI lanes.
+ * @dsi_display: DSI display handle.
+ * @enable: enable/disable ULPS.
+ *
+ * ULPS can be enabled/disabled after DSI host engine is turned on.
+ *
+ * Return: error code.
+ */
+static int dsi_display_set_ulps(struct dsi_display *display, bool enable)
+{
+ int rc = 0;
+ int i = 0;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ if (!dsi_display_is_ulps_req_valid(display, enable)) {
+ pr_debug("%s: skipping ULPS config, enable=%d\n",
+ __func__, enable);
+ return 0;
+ }
+
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+
+ rc = dsi_ctrl_set_ulps(m_ctrl->ctrl, enable);
+ if (rc) {
+ pr_err("Ulps controller state change(%d) failed\n", enable);
+ return rc;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_set_ulps(ctrl->ctrl, enable);
+ if (rc) {
+ pr_err("Ulps controller state change(%d) failed\n",
+ enable);
+ return rc;
+ }
+ }
+ display->ulps_enabled = enable;
+ return 0;
+}
+
+/**
+ * dsi_display_set_clamp() - set clamp state for DSI IO.
+ * @dsi_display: DSI display handle.
+ * @enable: enable/disable clamping.
+ *
+ * Return: error code.
+ */
+static int dsi_display_set_clamp(struct dsi_display *display, bool enable)
+{
+ int rc = 0;
+ int i = 0;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+ bool ulps_enabled = false;
+
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+ ulps_enabled = display->ulps_enabled;
+
+ rc = dsi_ctrl_set_clamp_state(m_ctrl->ctrl, enable, ulps_enabled);
+ if (rc) {
+ pr_err("DSI Clamp state change(%d) failed\n", enable);
+ return rc;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_set_clamp_state(ctrl->ctrl, enable, ulps_enabled);
+ if (rc) {
+ pr_err("DSI Clamp state change(%d) failed\n", enable);
+ return rc;
+ }
+ }
+ display->clamp_enabled = enable;
+ return 0;
+}
+
+/**
+ * dsi_display_setup_ctrl() - setup DSI controller.
+ * @dsi_display: DSI display handle.
+ *
+ * Return: error code.
+ */
+static int dsi_display_ctrl_setup(struct dsi_display *display)
+{
+ int rc = 0;
+ int i = 0;
+ struct dsi_display_ctrl *ctrl, *m_ctrl;
+
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+ rc = dsi_ctrl_setup(m_ctrl->ctrl);
+ if (rc) {
+ pr_err("DSI controller setup failed\n");
+ return rc;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_ctrl_setup(ctrl->ctrl);
+ if (rc) {
+ pr_err("DSI controller setup failed\n");
+ return rc;
+ }
+ }
+ return 0;
+}
+
+static int dsi_display_phy_enable(struct dsi_display *display);
+
+/**
+ * dsi_display_phy_idle_on() - enable DSI PHY while coming out of idle screen.
+ * @dsi_display: DSI display handle.
+ * @enable: enable/disable DSI PHY.
+ *
+ * Return: error code.
+ */
+static int dsi_display_phy_idle_on(struct dsi_display *display,
+ bool mmss_clamp)
+{
+ int rc = 0;
+ int i = 0;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ if (mmss_clamp && !display->phy_idle_power_off) {
+ dsi_display_phy_enable(display);
+ return 0;
+ }
+
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+ rc = dsi_phy_idle_ctrl(m_ctrl->phy, true);
+ if (rc) {
+ pr_err("DSI controller setup failed\n");
+ return rc;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_phy_idle_ctrl(ctrl->phy, true);
+ if (rc) {
+ pr_err("DSI controller setup failed\n");
+ return rc;
+ }
+ }
+ display->phy_idle_power_off = false;
+ return 0;
+}
+
+/**
+ * dsi_display_phy_idle_off() - disable DSI PHY while going to idle screen.
+ * @dsi_display: DSI display handle.
+ * @enable: enable/disable DSI PHY.
+ *
+ * Return: error code.
+ */
+static int dsi_display_phy_idle_off(struct dsi_display *display)
+{
+ int rc = 0;
+ int i = 0;
+ struct dsi_display_ctrl *m_ctrl, *ctrl;
+
+ if (!display) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ if (!display->panel->allow_phy_power_off) {
+ pr_debug("panel doesn't support this feature\n");
+ return 0;
+ }
+
+ m_ctrl = &display->ctrl[display->cmd_master_idx];
+
+ rc = dsi_phy_idle_ctrl(m_ctrl->phy, false);
+ if (rc) {
+ pr_err("[%s] failed to enable cmd engine, rc=%d\n",
+ display->name, rc);
+ return rc;
+ }
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ ctrl = &display->ctrl[i];
+ if (!ctrl->ctrl || (ctrl == m_ctrl))
+ continue;
+
+ rc = dsi_phy_idle_ctrl(ctrl->phy, false);
+ if (rc) {
+ pr_err("DSI controller setup failed\n");
+ return rc;
+ }
+ }
+ display->phy_idle_power_off = true;
+ return 0;
+}
+
+
+
static int dsi_display_ctrl_power_on(struct dsi_display *display)
{
int rc = 0;
@@ -171,7 +440,6 @@
struct dsi_display_ctrl *ctrl;
/* Sequence does not matter for split dsi usecases */
-
for (i = 0; i < display->ctrl_count; i++) {
ctrl = &display->ctrl[i];
if (!ctrl->ctrl)
@@ -192,7 +460,8 @@
ctrl = &display->ctrl[i];
if (!ctrl->ctrl)
continue;
- (void)dsi_ctrl_set_power_state(ctrl->ctrl, DSI_CTRL_POWER_OFF);
+ (void)dsi_ctrl_set_power_state(ctrl->ctrl,
+ DSI_CTRL_POWER_VREG_OFF);
}
return rc;
}
@@ -204,13 +473,13 @@
struct dsi_display_ctrl *ctrl;
/* Sequence does not matter for split dsi usecases */
-
for (i = 0; i < display->ctrl_count; i++) {
ctrl = &display->ctrl[i];
if (!ctrl->ctrl)
continue;
- rc = dsi_ctrl_set_power_state(ctrl->ctrl, DSI_CTRL_POWER_OFF);
+ rc = dsi_ctrl_set_power_state(ctrl->ctrl,
+ DSI_CTRL_POWER_VREG_OFF);
if (rc) {
pr_err("[%s] Failed to power off, rc=%d\n",
ctrl->ctrl->name, rc);
@@ -228,7 +497,6 @@
struct dsi_display_ctrl *ctrl;
/* Sequence does not matter for split dsi usecases */
-
for (i = 0; i < display->ctrl_count; i++) {
ctrl = &display->ctrl[i];
if (!ctrl->ctrl)
@@ -260,7 +528,6 @@
struct dsi_display_ctrl *ctrl;
/* Sequence does not matter for split dsi usecases */
-
for (i = 0; i < display->ctrl_count; i++) {
ctrl = &display->ctrl[i];
if (!ctrl->phy)
@@ -277,7 +544,7 @@
return rc;
}
-static int dsi_display_ctrl_core_clk_on(struct dsi_display *display)
+static int dsi_display_set_clk_src(struct dsi_display *display)
{
int rc = 0;
int i;
@@ -288,64 +555,14 @@
* be enabled before the other controller. Master controller in the
* clock context refers to the controller that sources the clock.
*/
-
- m_ctrl = &display->ctrl[display->clk_master_idx];
-
- rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_CORE_CLK_ON);
- if (rc) {
- pr_err("[%s] failed to turn on clocks, rc=%d\n",
- display->name, rc);
- goto error;
- }
-
- /* Turn on rest of the controllers */
- for (i = 0; i < display->ctrl_count; i++) {
- ctrl = &display->ctrl[i];
- if (!ctrl->ctrl || (ctrl == m_ctrl))
- continue;
-
- rc = dsi_ctrl_set_power_state(ctrl->ctrl,
- DSI_CTRL_POWER_CORE_CLK_ON);
- if (rc) {
- pr_err("[%s] failed to turn on clock, rc=%d\n",
- display->name, rc);
- goto error_disable_master;
- }
- }
- return rc;
-error_disable_master:
- (void)dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_VREG_ON);
-error:
- return rc;
-}
-
-static int dsi_display_ctrl_link_clk_on(struct dsi_display *display)
-{
- int rc = 0;
- int i;
- struct dsi_display_ctrl *m_ctrl, *ctrl;
-
- /*
- * In case of split DSI usecases, the clock for master controller should
- * be enabled before the other controller. Master controller in the
- * clock context refers to the controller that sources the clock.
- */
-
m_ctrl = &display->ctrl[display->clk_master_idx];
rc = dsi_ctrl_set_clock_source(m_ctrl->ctrl,
- &display->clock_info.src_clks);
+ &display->clock_info.src_clks);
if (rc) {
pr_err("[%s] failed to set source clocks for master, rc=%d\n",
- display->name, rc);
- goto error;
- }
-
- rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_LINK_CLK_ON);
- if (rc) {
- pr_err("[%s] failed to turn on clocks, rc=%d\n",
- display->name, rc);
- goto error;
+ display->name, rc);
+ return rc;
}
/* Turn on rest of the controllers */
@@ -355,97 +572,14 @@
continue;
rc = dsi_ctrl_set_clock_source(ctrl->ctrl,
- &display->clock_info.src_clks);
+ &display->clock_info.src_clks);
if (rc) {
pr_err("[%s] failed to set source clocks, rc=%d\n",
- display->name, rc);
- goto error_disable_master;
- }
-
- rc = dsi_ctrl_set_power_state(ctrl->ctrl,
- DSI_CTRL_POWER_LINK_CLK_ON);
- if (rc) {
- pr_err("[%s] failed to turn on clock, rc=%d\n",
- display->name, rc);
- goto error_disable_master;
+ display->name, rc);
+ return rc;
}
}
- return rc;
-error_disable_master:
- (void)dsi_ctrl_set_power_state(m_ctrl->ctrl,
- DSI_CTRL_POWER_CORE_CLK_ON);
-error:
- return rc;
-}
-
-static int dsi_display_ctrl_core_clk_off(struct dsi_display *display)
-{
- int rc = 0;
- int i;
- struct dsi_display_ctrl *m_ctrl, *ctrl;
-
- /*
- * In case of split DSI usecases, clock for slave DSI controllers should
- * be disabled first before disabling clock for master controller. Slave
- * controllers in the clock context refer to controller which source
- * clock from another controller.
- */
-
- m_ctrl = &display->ctrl[display->clk_master_idx];
-
- for (i = 0; i < display->ctrl_count; i++) {
- ctrl = &display->ctrl[i];
- if (!ctrl->ctrl || (ctrl == m_ctrl))
- continue;
-
- rc = dsi_ctrl_set_power_state(ctrl->ctrl,
- DSI_CTRL_POWER_VREG_ON);
- if (rc) {
- pr_err("[%s] failed to turn off clock, rc=%d\n",
- display->name, rc);
- }
- }
-
- rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_VREG_ON);
- if (rc)
- pr_err("[%s] failed to turn off clocks, rc=%d\n",
- display->name, rc);
-
- return rc;
-}
-
-static int dsi_display_ctrl_link_clk_off(struct dsi_display *display)
-{
- int rc = 0;
- int i;
- struct dsi_display_ctrl *m_ctrl, *ctrl;
-
- /*
- * In case of split DSI usecases, clock for slave DSI controllers should
- * be disabled first before disabling clock for master controller. Slave
- * controllers in the clock context refer to controller which source
- * clock from another controller.
- */
-
- m_ctrl = &display->ctrl[display->clk_master_idx];
-
- for (i = 0; i < display->ctrl_count; i++) {
- ctrl = &display->ctrl[i];
- if (!ctrl->ctrl || (ctrl == m_ctrl))
- continue;
-
- rc = dsi_ctrl_set_power_state(ctrl->ctrl,
- DSI_CTRL_POWER_CORE_CLK_ON);
- if (rc) {
- pr_err("[%s] failed to turn off clock, rc=%d\n",
- display->name, rc);
- }
- }
- rc = dsi_ctrl_set_power_state(m_ctrl->ctrl, DSI_CTRL_POWER_CORE_CLK_ON);
- if (rc)
- pr_err("[%s] failed to turn off clocks, rc=%d\n",
- display->name, rc);
- return rc;
+ return 0;
}
static int dsi_display_ctrl_init(struct dsi_display *display)
@@ -893,18 +1027,26 @@
return 0;
}
+ rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_ALL_CLKS, DSI_CLK_ON);
+ if (rc) {
+ pr_err("[%s] failed to enable all DSI clocks, rc=%d\n",
+ display->name, rc);
+ goto error;
+ }
+
rc = dsi_display_wake_up(display);
if (rc) {
pr_err("[%s] failed to wake up display, rc=%d\n",
display->name, rc);
- goto error;
+ goto error_disable_clks;
}
rc = dsi_display_cmd_engine_enable(display);
if (rc) {
pr_err("[%s] failed to enable cmd engine, rc=%d\n",
display->name, rc);
- goto error;
+ goto error_disable_clks;
}
if (display->ctrl_count > 1) {
@@ -925,6 +1067,13 @@
}
error_disable_cmd_engine:
(void)dsi_display_cmd_engine_disable(display);
+error_disable_clks:
+ rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_ALL_CLKS, DSI_CLK_OFF);
+ if (rc) {
+ pr_err("[%s] failed to enable all DSI clocks, rc=%d\n",
+ display->name, rc);
+ }
error:
return rc;
}
@@ -1094,6 +1243,231 @@
return rc;
}
+static int dsi_display_clk_ctrl_cb(void *priv,
+ struct dsi_clk_ctrl_info clk_state_info)
+{
+ int rc = 0;
+ struct dsi_display *display = NULL;
+ void *clk_handle = NULL;
+
+ if (!priv) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ display = priv;
+
+ if (clk_state_info.client == DSI_CLK_REQ_MDP_CLIENT) {
+ clk_handle = display->mdp_clk_handle;
+ } else if (clk_state_info.client == DSI_CLK_REQ_DSI_CLIENT) {
+ clk_handle = display->dsi_clk_handle;
+ } else {
+ pr_err("invalid clk handle, return error\n");
+ return -EINVAL;
+ }
+
+ /*
+ * TODO: Wait for CMD_MDP_DONE interrupt if MDP client tries
+ * to turn off DSI clocks.
+ */
+ rc = dsi_display_clk_ctrl(clk_handle,
+ clk_state_info.clk_type, clk_state_info.clk_state);
+ if (rc) {
+ pr_err("[%s] failed to %d DSI %d clocks, rc=%d\n",
+ display->name, clk_state_info.clk_state,
+ clk_state_info.clk_type, rc);
+ return rc;
+ }
+ return 0;
+}
+
+int dsi_pre_clkoff_cb(void *priv,
+ enum dsi_clk_type clk,
+ enum dsi_clk_state new_state)
+{
+ int rc = 0;
+ struct dsi_display *display = priv;
+
+ if ((clk & DSI_LINK_CLK) && (new_state == DSI_CLK_OFF)) {
+ /*
+ * If ULPS feature is enabled, enter ULPS first.
+ */
+ if (dsi_panel_initialized(display->panel) &&
+ dsi_panel_ulps_feature_enabled(display->panel)) {
+ rc = dsi_display_set_ulps(display, true);
+ if (rc) {
+ pr_err("%s: failed enable ulps, rc = %d\n",
+ __func__, rc);
+ }
+ }
+ }
+
+ if ((clk & DSI_CORE_CLK) && (new_state == DSI_CLK_OFF)) {
+ /*
+ * Enable DSI clamps only if entering idle power collapse.
+ */
+ if (dsi_panel_initialized(display->panel) &&
+ dsi_panel_ulps_feature_enabled(display->panel)) {
+ dsi_display_phy_idle_off(display);
+ rc = dsi_display_set_clamp(display, true);
+ if (rc)
+ pr_err("%s: Failed to enable dsi clamps. rc=%d\n",
+ __func__, rc);
+ } else {
+ /* Make sure that controller is not in ULPS state when
+ * the DSI link is not active.
+ */
+ rc = dsi_display_set_ulps(display, false);
+ if (rc)
+ pr_err("%s: failed to disable ulps. rc=%d\n",
+ __func__, rc);
+ }
+ }
+
+ return rc;
+}
+
+int dsi_post_clkon_cb(void *priv,
+ enum dsi_clk_type clk,
+ enum dsi_clk_state curr_state)
+{
+ int rc = 0;
+ struct dsi_display *display = priv;
+ bool mmss_clamp = false;
+
+ if (clk & DSI_CORE_CLK) {
+ mmss_clamp = display->clamp_enabled;
+ /*
+ * controller setup is needed if coming out of idle
+ * power collapse with clamps enabled.
+ */
+ if (mmss_clamp)
+ dsi_display_ctrl_setup(display);
+
+ if (display->ulps_enabled) {
+ /*
+ * ULPS Entry Request. This is needed if the lanes were
+ * in ULPS prior to power collapse, since after
+ * power collapse and reset, the DSI controller resets
+ * back to idle state and not ULPS. This ulps entry
+ * request will transition the state of the DSI
+ * controller to ULPS which will match the state of the
+ * DSI phy. This needs to be done prior to disabling
+ * the DSI clamps.
+ *
+ * Also, reset the ulps flag so that ulps_config
+ * function would reconfigure the controller state to
+ * ULPS.
+ */
+ display->ulps_enabled = false;
+ rc = dsi_display_set_ulps(display, true);
+ if (rc) {
+ pr_err("%s: Failed to enter ULPS. rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+ }
+
+ rc = dsi_display_set_clamp(display, false);
+ if (rc) {
+ pr_err("%s: Failed to disable dsi clamps. rc=%d\n",
+ __func__, rc);
+ goto error;
+ }
+
+ /*
+ * Phy setup is needed if coming out of idle
+ * power collapse with clamps enabled.
+ */
+ if (display->phy_idle_power_off || mmss_clamp)
+ dsi_display_phy_idle_on(display, mmss_clamp);
+ }
+ if (clk & DSI_LINK_CLK) {
+ if (display->ulps_enabled) {
+ rc = dsi_display_set_ulps(display, false);
+ if (rc) {
+ pr_err("%s: failed to disable ulps, rc= %d\n",
+ __func__, rc);
+ goto error;
+ }
+ }
+ }
+error:
+ return rc;
+}
+
+int dsi_post_clkoff_cb(void *priv,
+ enum dsi_clk_type clk_type,
+ enum dsi_clk_state curr_state)
+{
+ int rc = 0;
+ struct dsi_display *display = priv;
+
+ if (!display) {
+ pr_err("%s: Invalid arg\n", __func__);
+ return -EINVAL;
+ }
+
+ if ((clk_type & DSI_CORE_CLK) &&
+ (curr_state == DSI_CLK_OFF)) {
+
+ rc = dsi_display_phy_power_off(display);
+ if (rc)
+ pr_err("[%s] failed to power off PHY, rc=%d\n",
+ display->name, rc);
+
+ rc = dsi_display_ctrl_power_off(display);
+ if (rc)
+ pr_err("[%s] failed to power DSI vregs, rc=%d\n",
+ display->name, rc);
+ }
+ return rc;
+}
+
+int dsi_pre_clkon_cb(void *priv,
+ enum dsi_clk_type clk_type,
+ enum dsi_clk_state new_state)
+{
+ int rc = 0;
+ struct dsi_display *display = priv;
+
+ if (!display) {
+ pr_err("%s: invalid input\n", __func__);
+ return -EINVAL;
+ }
+
+ if ((clk_type & DSI_CORE_CLK) && (new_state == DSI_CLK_ON)) {
+ /*
+ * Enable DSI core power
+ * 1.> PANEL_PM are controlled as part of
+ * panel_power_ctrl. Needed not be handled here.
+ * 2.> CORE_PM are controlled by dsi clk manager.
+ * 3.> CTRL_PM need to be enabled/disabled
+ * only during unblank/blank. Their state should
+ * not be changed during static screen.
+ */
+
+ rc = dsi_display_ctrl_power_on(display);
+ if (rc) {
+ pr_err("[%s] failed to power on dsi controllers, rc=%d\n",
+ display->name, rc);
+ return rc;
+ }
+
+ rc = dsi_display_phy_power_on(display);
+ if (rc) {
+ pr_err("[%s] failed to power on dsi phy, rc = %d\n",
+ display->name, rc);
+ return rc;
+ }
+
+ pr_debug("%s: Enable DSI core power\n", __func__);
+ }
+
+ return rc;
+}
+
+
static int dsi_display_parse_lane_map(struct dsi_display *display)
{
int rc = 0;
@@ -1509,7 +1883,7 @@
}
/* TODO: Remove this direct reference to the dsi_ctrl */
- clk_hz = m_ctrl->clk_info.link_clks.pixel_clk_rate;
+ clk_hz = m_ctrl->clk_freq.pix_clk_rate;
timing = &per_ctrl_mode.timing;
switch (dfps_caps.type) {
@@ -1598,7 +1972,7 @@
for (i = 0; i < display->ctrl_count; i++) {
ctrl = &display->ctrl[i];
rc = dsi_ctrl_update_host_config(ctrl->ctrl, &display->config,
- mode->flags);
+ mode->flags, display->dsi_clk_handle);
if (rc) {
pr_err("[%s] failed to update ctrl config, rc=%d\n",
display->name, rc);
@@ -1686,7 +2060,12 @@
struct dsi_display_ctrl *display_ctrl;
struct drm_device *drm;
struct dsi_display *display;
+ struct dsi_clk_info info;
+ struct clk_ctrl_cb clk_cb;
+ void *handle = NULL;
struct platform_device *pdev = to_platform_device(dev);
+ char *client1 = "dsi_clk_client";
+ char *client2 = "mdp_event_client";
int i, rc = 0;
if (!dev || !pdev || !master) {
@@ -1711,6 +2090,8 @@
goto error;
}
+ memset(&info, 0x0, sizeof(info));
+
for (i = 0; i < display->ctrl_count; i++) {
display_ctrl = &display->ctrl[i];
@@ -1728,6 +2109,72 @@
(void)dsi_ctrl_drv_deinit(display_ctrl->ctrl);
goto error_ctrl_deinit;
}
+
+ memcpy(&info.c_clks[i], &display_ctrl->ctrl->clk_info.core_clks,
+ sizeof(struct dsi_core_clk_info));
+ memcpy(&info.l_clks[i], &display_ctrl->ctrl->clk_info.link_clks,
+ sizeof(struct dsi_link_clk_info));
+ info.bus_handle[i] =
+ display_ctrl->ctrl->axi_bus_info.bus_handle;
+ }
+
+ info.pre_clkoff_cb = dsi_pre_clkoff_cb;
+ info.pre_clkon_cb = dsi_pre_clkon_cb;
+ info.post_clkoff_cb = dsi_post_clkoff_cb;
+ info.post_clkon_cb = dsi_post_clkon_cb;
+ info.priv_data = display;
+ info.master_ndx = display->clk_master_idx;
+ info.dsi_ctrl_count = display->ctrl_count;
+ snprintf(info.name, MAX_STRING_LEN,
+ "DSI_MNGR-%s", display->name);
+
+ display->clk_mngr = dsi_display_clk_mngr_register(&info);
+ if (IS_ERR_OR_NULL(display->clk_mngr)) {
+ rc = PTR_ERR(display->clk_mngr);
+ display->clk_mngr = NULL;
+ pr_err("dsi clock registration failed, rc = %d\n", rc);
+ goto error_ctrl_deinit;
+ }
+
+ handle = dsi_register_clk_handle(display->clk_mngr, client1);
+ if (IS_ERR_OR_NULL(handle)) {
+ rc = PTR_ERR(handle);
+ pr_err("failed to register %s client, rc = %d\n",
+ client1, rc);
+ goto error_clk_deinit;
+ } else {
+ display->dsi_clk_handle = handle;
+ }
+
+ handle = dsi_register_clk_handle(display->clk_mngr, client2);
+ if (IS_ERR_OR_NULL(handle)) {
+ rc = PTR_ERR(handle);
+ pr_err("failed to register %s client, rc = %d\n",
+ client2, rc);
+ goto error_clk_client_deinit;
+ } else {
+ display->mdp_clk_handle = handle;
+ }
+
+ clk_cb.priv = display;
+ clk_cb.dsi_clk_cb = dsi_display_clk_ctrl_cb;
+
+ for (i = 0; i < display->ctrl_count; i++) {
+ display_ctrl = &display->ctrl[i];
+
+ rc = dsi_ctrl_clk_cb_register(display_ctrl->ctrl, &clk_cb);
+ if (rc) {
+ pr_err("[%s] failed to register ctrl clk_cb[%d], rc=%d\n",
+ display->name, i, rc);
+ goto error_ctrl_deinit;
+ }
+
+ rc = dsi_phy_clk_cb_register(display_ctrl->phy, &clk_cb);
+ if (rc) {
+ pr_err("[%s] failed to register phy clk_cb[%d], rc=%d\n",
+ display->name, i, rc);
+ goto error_ctrl_deinit;
+ }
}
rc = dsi_display_mipi_host_init(display);
@@ -1759,6 +2206,10 @@
(void)dsi_panel_drv_deinit(display->panel);
error_host_deinit:
(void)dsi_display_mipi_host_deinit(display);
+error_clk_client_deinit:
+ (void)dsi_deregister_clk_handle(display->dsi_clk_handle);
+error_clk_deinit:
+ (void)dsi_display_clk_mngr_deregister(display->clk_mngr);
error_ctrl_deinit:
for (i = i - 1; i >= 0; i--) {
display_ctrl = &display->ctrl[i];
@@ -2294,25 +2745,12 @@
goto error;
}
- rc = dsi_display_ctrl_power_on(display);
- if (rc) {
- pr_err("[%s] failed to power on dsi controllers, rc=%d\n",
- display->name, rc);
- goto error_panel_post_unprep;
- }
-
- rc = dsi_display_phy_power_on(display);
- if (rc) {
- pr_err("[%s] failed to power on dsi phy, rc = %d\n",
- display->name, rc);
- goto error_ctrl_pwr_off;
- }
-
- rc = dsi_display_ctrl_core_clk_on(display);
+ rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_CORE_CLK, DSI_CLK_ON);
if (rc) {
pr_err("[%s] failed to enable DSI core clocks, rc=%d\n",
display->name, rc);
- goto error_phy_pwr_off;
+ goto error_panel_post_unprep;
}
rc = dsi_display_phy_sw_reset(display);
@@ -2335,7 +2773,15 @@
goto error_phy_disable;
}
- rc = dsi_display_ctrl_link_clk_on(display);
+ rc = dsi_display_set_clk_src(display);
+ if (rc) {
+ pr_err("[%s] failed to set DSI link clock source, rc=%d\n",
+ display->name, rc);
+ goto error_ctrl_deinit;
+ }
+
+ rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_LINK_CLK, DSI_CLK_ON);
if (rc) {
pr_err("[%s] failed to enable DSI link clocks, rc=%d\n",
display->name, rc);
@@ -2360,17 +2806,15 @@
error_host_engine_off:
(void)dsi_display_ctrl_host_disable(display);
error_ctrl_link_off:
- (void)dsi_display_ctrl_link_clk_off(display);
+ (void)dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_LINK_CLK, DSI_CLK_OFF);
error_ctrl_deinit:
(void)dsi_display_ctrl_deinit(display);
error_phy_disable:
(void)dsi_display_phy_disable(display);
error_ctrl_clk_off:
- (void)dsi_display_ctrl_core_clk_off(display);
-error_phy_pwr_off:
- (void)dsi_display_phy_power_off(display);
-error_ctrl_pwr_off:
- (void)dsi_display_ctrl_power_off(display);
+ (void)dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_CORE_CLK, DSI_CLK_OFF);
error_panel_post_unprep:
(void)dsi_panel_post_unprepare(display->panel);
error:
@@ -2531,7 +2975,8 @@
pr_err("[%s] failed to disable DSI host, rc=%d\n",
display->name, rc);
- rc = dsi_display_ctrl_link_clk_off(display);
+ rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_LINK_CLK, DSI_CLK_OFF);
if (rc)
pr_err("[%s] failed to disable Link clocks, rc=%d\n",
display->name, rc);
@@ -2546,21 +2991,12 @@
pr_err("[%s] failed to disable DSI PHY, rc=%d\n",
display->name, rc);
- rc = dsi_display_ctrl_core_clk_off(display);
+ rc = dsi_display_clk_ctrl(display->dsi_clk_handle,
+ DSI_CORE_CLK, DSI_CLK_OFF);
if (rc)
pr_err("[%s] failed to disable DSI clocks, rc=%d\n",
display->name, rc);
- rc = dsi_display_phy_power_off(display);
- if (rc)
- pr_err("[%s] failed to power off PHY, rc=%d\n",
- display->name, rc);
-
- rc = dsi_display_ctrl_power_off(display);
- if (rc)
- pr_err("[%s] failed to power DSI vregs, rc=%d\n",
- display->name, rc);
-
rc = dsi_panel_post_unprepare(display->panel);
if (rc)
pr_err("[%s] panel post-unprepare failed, rc=%d\n",
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
index b77bf26..1c1f4b4 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_display.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -110,14 +110,20 @@
* @cmd_master_idx: The master controller for sending DSI commands to panel.
* @video_master_idx: The master controller for enabling video engine.
* @clock_info: Clock sourcing for DSI display.
+ * @config: DSI host configuration information.
* @lane_map: Lane mapping between DSI host and Panel.
* @num_of_modes: Number of modes supported by display.
* @is_tpg_enabled: TPG state.
+ * @ulps_enabled: ulps state.
+ * @clamp_enabled: clamp state.
+ * @phy_idle_power_off: PHY power state.
* @host: DRM MIPI DSI Host.
- * @connector: Pointer to DRM connector object.
* @bridge: Pointer to DRM bridge object.
* @cmd_engine_refcount: Reference count enforcing single instance of cmd eng
- * @root: Debugfs root directory
+ * @clk_mngr: DSI clock manager.
+ * @dsi_clk_handle: DSI clock handle.
+ * @mdp_clk_handle: MDP clock handle.
+ * @root: Debugfs root directory
*/
struct dsi_display {
struct platform_device *pdev;
@@ -146,11 +152,18 @@
struct dsi_lane_mapping lane_map;
u32 num_of_modes;
bool is_tpg_enabled;
+ bool ulps_enabled;
+ bool clamp_enabled;
+ bool phy_idle_power_off;
struct mipi_dsi_host host;
struct dsi_bridge *bridge;
u32 cmd_engine_refcount;
+ void *clk_mngr;
+ void *dsi_clk_handle;
+ void *mdp_clk_handle;
+
/* DEBUG FS */
struct dentry *root;
};
@@ -317,6 +330,51 @@
int dsi_display_disable(struct dsi_display *display);
/**
+ * dsi_pre_clkoff_cb() - Callback before clock is turned off
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned on.
+ * @new_state: next state for the clock.
+ *
+ * @return: error code.
+ */
+int dsi_pre_clkoff_cb(void *priv, enum dsi_clk_type clk_type,
+ enum dsi_clk_state new_state);
+
+/**
+ * dsi_post_clkoff_cb() - Callback after clock is turned off
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned on.
+ * @curr_state: current state for the clock.
+ *
+ * @return: error code.
+ */
+int dsi_post_clkoff_cb(void *priv, enum dsi_clk_type clk_type,
+ enum dsi_clk_state curr_state);
+
+/**
+ * dsi_post_clkon_cb() - Callback after clock is turned on
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned on.
+ * @curr_state: current state for the clock.
+ *
+ * @return: error code.
+ */
+int dsi_post_clkon_cb(void *priv, enum dsi_clk_type clk_type,
+ enum dsi_clk_state curr_state);
+
+
+/**
+ * dsi_pre_clkon_cb() - Callback before clock is turned on
+ * @priv: private data pointer.
+ * @clk_type: clock which is being turned on.
+ * @new_state: next state for the clock.
+ *
+ * @return: error code.
+ */
+int dsi_pre_clkon_cb(void *priv, enum dsi_clk_type clk_type,
+ enum dsi_clk_state new_state);
+
+/**
* dsi_display_unprepare() - power off display hardware.
* @display: Handle to display.
*
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
index a7a39e6..b9052ec 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -1331,7 +1331,7 @@
{
int rc = 0;
- rc = dsi_clk_pwr_of_get_vreg_data(of_node,
+ rc = dsi_pwr_of_get_vreg_data(of_node,
&panel->power_info,
"qcom,panel-supply-entries");
if (rc) {
@@ -1871,6 +1871,7 @@
pr_err("[%s] failed to send DSI_CMD_SET_ON cmds, rc=%d\n",
panel->name, rc);
}
+ panel->panel_initialized = true;
mutex_unlock(&panel->panel_lock);
return rc;
}
@@ -1892,6 +1893,7 @@
panel->name, rc);
goto error;
}
+ panel->panel_initialized = false;
error:
mutex_unlock(&panel->panel_lock);
return rc;
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
index 4d21a4c..1bf8787 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_panel.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -25,7 +25,8 @@
#include "dsi_defs.h"
#include "dsi_ctrl_hw.h"
-#include "dsi_clk_pwr.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
#define MAX_BL_LEVEL 4096
@@ -159,8 +160,22 @@
struct dsi_pinctrl_info pinctrl;
bool lp11_init;
+ bool ulps_enabled;
+ bool allow_phy_power_off;
+
+ bool panel_initialized;
};
+static inline bool dsi_panel_ulps_feature_enabled(struct dsi_panel *panel)
+{
+ return panel->ulps_enabled;
+}
+
+static inline bool dsi_panel_initialized(struct dsi_panel *panel)
+{
+ return panel->panel_initialized;
+}
+
struct dsi_panel *dsi_panel_get(struct device *parent,
struct device_node *of_node);
void dsi_panel_put(struct dsi_panel *panel);
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
index 1ccbbe7..4249ac4 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -25,7 +25,8 @@
#include "msm_gpu.h"
#include "dsi_phy.h"
#include "dsi_phy_hw.h"
-#include "dsi_clk_pwr.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
#include "dsi_catalog.h"
#define DSI_PHY_DEFAULT_LABEL "MDSS PHY CTRL"
@@ -104,65 +105,6 @@
return 0;
}
-static int dsi_phy_clocks_deinit(struct msm_dsi_phy *phy)
-{
- int rc = 0;
- struct dsi_core_clk_info *core = &phy->clks.core_clks;
-
- if (core->mdp_core_clk)
- devm_clk_put(&phy->pdev->dev, core->mdp_core_clk);
- if (core->iface_clk)
- devm_clk_put(&phy->pdev->dev, core->iface_clk);
- if (core->core_mmss_clk)
- devm_clk_put(&phy->pdev->dev, core->core_mmss_clk);
- if (core->bus_clk)
- devm_clk_put(&phy->pdev->dev, core->bus_clk);
-
- memset(core, 0x0, sizeof(*core));
-
- return rc;
-}
-
-static int dsi_phy_clocks_init(struct platform_device *pdev,
- struct msm_dsi_phy *phy)
-{
- int rc = 0;
- struct dsi_core_clk_info *core = &phy->clks.core_clks;
-
- core->mdp_core_clk = devm_clk_get(&pdev->dev, "mdp_core_clk");
- if (IS_ERR(core->mdp_core_clk)) {
- rc = PTR_ERR(core->mdp_core_clk);
- pr_err("failed to get mdp_core_clk, rc=%d\n", rc);
- goto fail;
- }
-
- core->iface_clk = devm_clk_get(&pdev->dev, "iface_clk");
- if (IS_ERR(core->iface_clk)) {
- rc = PTR_ERR(core->iface_clk);
- pr_err("failed to get iface_clk, rc=%d\n", rc);
- goto fail;
- }
-
- core->core_mmss_clk = devm_clk_get(&pdev->dev, "core_mmss_clk");
- if (IS_ERR(core->core_mmss_clk)) {
- rc = PTR_ERR(core->core_mmss_clk);
- pr_err("failed to get core_mmss_clk, rc=%d\n", rc);
- goto fail;
- }
-
- core->bus_clk = devm_clk_get(&pdev->dev, "bus_clk");
- if (IS_ERR(core->bus_clk)) {
- rc = PTR_ERR(core->bus_clk);
- pr_err("failed to get bus_clk, rc=%d\n", rc);
- goto fail;
- }
-
- return rc;
-fail:
- dsi_phy_clocks_deinit(phy);
- return rc;
-}
-
static int dsi_phy_supplies_init(struct platform_device *pdev,
struct msm_dsi_phy *phy)
{
@@ -182,7 +124,7 @@
ARRAY_SIZE(regs->vregs[i].vreg_name),
"%s", "gdsc");
- rc = dsi_clk_pwr_get_dt_vreg_data(&pdev->dev,
+ rc = dsi_pwr_get_dt_vreg_data(&pdev->dev,
&phy->pwr_info.phy_pwr,
"qcom,phy-supply-entries");
if (rc) {
@@ -404,16 +346,10 @@
goto fail;
}
- rc = dsi_phy_clocks_init(pdev, dsi_phy);
- if (rc) {
- pr_err("failed to parse clock information, rc = %d\n", rc);
- goto fail_regmap;
- }
-
rc = dsi_phy_supplies_init(pdev, dsi_phy);
if (rc) {
pr_err("failed to parse voltage supplies, rc = %d\n", rc);
- goto fail_clks;
+ goto fail_regmap;
}
rc = dsi_catalog_phy_setup(&dsi_phy->hw, ver_info->version,
@@ -446,8 +382,6 @@
fail_supplies:
(void)dsi_phy_supplies_deinit(dsi_phy);
-fail_clks:
- (void)dsi_phy_clocks_deinit(dsi_phy);
fail_regmap:
(void)dsi_phy_regmap_deinit(dsi_phy);
fail:
@@ -489,10 +423,6 @@
if (rc)
pr_err("failed to deinitialize voltage supplies, rc=%d\n", rc);
- rc = dsi_phy_clocks_deinit(phy);
- if (rc)
- pr_err("failed to deinitialize clocks, rc=%d\n", rc);
-
rc = dsi_phy_regmap_deinit(phy);
if (rc)
pr_err("failed to deinitialize regmap, rc=%d\n", rc);
@@ -622,6 +552,19 @@
return 0;
}
+int dsi_phy_clk_cb_register(struct msm_dsi_phy *dsi_phy,
+ struct clk_ctrl_cb *clk_cb)
+{
+ if (!dsi_phy || !clk_cb) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
+ }
+
+ dsi_phy->clk_cb.priv = clk_cb->priv;
+ dsi_phy->clk_cb.dsi_clk_cb = clk_cb->dsi_clk_cb;
+ return 0;
+}
+
/**
* dsi_phy_validate_mode() - validate a display mode
* @dsi_phy: DSI PHY handle.
@@ -679,22 +622,27 @@
pr_err("failed to enable digital regulator\n");
goto error;
}
- rc = dsi_pwr_enable_regulator(&dsi_phy->pwr_info.phy_pwr, true);
- if (rc) {
- pr_err("failed to enable phy power\n");
- (void)dsi_pwr_enable_regulator(
- &dsi_phy->pwr_info.digital,
- false
- );
- goto error;
+
+ if (dsi_phy->dsi_phy_state == DSI_PHY_ENGINE_OFF) {
+ rc = dsi_pwr_enable_regulator(
+ &dsi_phy->pwr_info.phy_pwr, true);
+ if (rc) {
+ pr_err("failed to enable phy power\n");
+ (void)dsi_pwr_enable_regulator(
+ &dsi_phy->pwr_info.digital, false);
+ goto error;
+ }
}
} else {
- rc = dsi_pwr_enable_regulator(&dsi_phy->pwr_info.phy_pwr,
- false);
- if (rc) {
- pr_err("failed to enable digital regulator\n");
- goto error;
+ if (dsi_phy->dsi_phy_state == DSI_PHY_ENGINE_OFF) {
+ rc = dsi_pwr_enable_regulator(
+ &dsi_phy->pwr_info.phy_pwr, false);
+ if (rc) {
+ pr_err("failed to enable digital regulator\n");
+ goto error;
+ }
}
+
rc = dsi_pwr_enable_regulator(&dsi_phy->pwr_info.digital,
false);
if (rc) {
@@ -726,23 +674,28 @@
bool skip_validation)
{
int rc = 0;
+ struct dsi_clk_ctrl_info clk_info;
if (!phy || !config) {
pr_err("Invalid params\n");
return -EINVAL;
}
+ clk_info.client = DSI_CLK_REQ_DSI_CLIENT;
+ clk_info.clk_type = DSI_CORE_CLK;
+ clk_info.clk_state = DSI_CLK_ON;
+
+ rc = phy->clk_cb.dsi_clk_cb(phy->clk_cb.priv, clk_info);
+ if (rc) {
+ pr_err("failed to enable DSI core clocks\n");
+ return rc;
+ }
+
mutex_lock(&phy->phy_lock);
if (!skip_validation)
pr_debug("[PHY_%d] TODO: perform validation\n", phy->index);
- rc = dsi_clk_enable_core_clks(&phy->clks.core_clks, true);
- if (rc) {
- pr_err("failed to enable core clocks, rc=%d\n", rc);
- goto error;
- }
-
memcpy(&phy->mode, &config->video_timing, sizeof(phy->mode));
phy->data_lanes = config->common_config.data_lanes;
phy->dst_format = config->common_config.dst_format;
@@ -755,19 +708,19 @@
&phy->cfg.timing);
if (rc) {
pr_err("[%s] failed to set timing, rc=%d\n", phy->name, rc);
- goto error_disable_clks;
+ goto error;
}
dsi_phy_enable_hw(phy);
+ phy->dsi_phy_state = DSI_PHY_ENGINE_ON;
-error_disable_clks:
- rc = dsi_clk_enable_core_clks(&phy->clks.core_clks, false);
- if (rc) {
- pr_err("failed to disable clocks, skip phy disable\n");
- goto error;
- }
error:
mutex_unlock(&phy->phy_lock);
+
+ clk_info.clk_state = DSI_CLK_OFF;
+ rc = phy->clk_cb.dsi_clk_cb(phy->clk_cb.priv, clk_info);
+ if (rc)
+ pr_err("failed to disable DSI core clocks\n");
return rc;
}
@@ -780,31 +733,67 @@
int dsi_phy_disable(struct msm_dsi_phy *phy)
{
int rc = 0;
+ struct dsi_clk_ctrl_info clk_info;
if (!phy) {
pr_err("Invalid params\n");
return -EINVAL;
}
+ clk_info.client = DSI_CLK_REQ_DSI_CLIENT;
+ clk_info.clk_type = DSI_CORE_CLK;
+ clk_info.clk_state = DSI_CLK_ON;
+
+ rc = phy->clk_cb.dsi_clk_cb(phy->clk_cb.priv, clk_info);
+ if (rc) {
+ pr_err("failed to enable DSI core clocks\n");
+ return rc;
+ }
+
mutex_lock(&phy->phy_lock);
-
- rc = dsi_clk_enable_core_clks(&phy->clks.core_clks, true);
- if (rc) {
- pr_err("failed to enable core clocks, rc=%d\n", rc);
- goto error;
- }
-
dsi_phy_disable_hw(phy);
+ phy->dsi_phy_state = DSI_PHY_ENGINE_OFF;
+ mutex_unlock(&phy->phy_lock);
- rc = dsi_clk_enable_core_clks(&phy->clks.core_clks, false);
- if (rc) {
- pr_err("failed to disable core clocks, rc=%d\n", rc);
- goto error;
+ clk_info.clk_state = DSI_CLK_OFF;
+
+ rc = phy->clk_cb.dsi_clk_cb(phy->clk_cb.priv, clk_info);
+ if (rc)
+ pr_err("failed to disable DSI core clocks\n");
+
+ return rc;
+}
+
+/**
+ * dsi_phy_idle_ctrl() - enable/disable DSI PHY during idle screen
+ * @phy: DSI PHY handle
+ * @enable: boolean to specify PHY enable/disable.
+ *
+ * Return: error code.
+ */
+
+int dsi_phy_idle_ctrl(struct msm_dsi_phy *phy, bool enable)
+{
+ if (!phy) {
+ pr_err("Invalid params\n");
+ return -EINVAL;
}
-error:
+ mutex_lock(&phy->phy_lock);
+ if (enable) {
+ if (phy->hw.ops.phy_idle_on)
+ phy->hw.ops.phy_idle_on(&phy->hw, &phy->cfg);
+
+ if (phy->hw.ops.regulator_enable)
+ phy->hw.ops.regulator_enable(&phy->hw,
+ &phy->cfg.regulators);
+ } else {
+ if (phy->hw.ops.phy_idle_off)
+ phy->hw.ops.phy_idle_off(&phy->hw);
+ }
mutex_unlock(&phy->phy_lock);
- return rc;
+
+ return 0;
}
/**
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
index 6c31bfa..d8bfd15 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -15,7 +15,8 @@
#define _DSI_PHY_H_
#include "dsi_defs.h"
-#include "dsi_clk_pwr.h"
+#include "dsi_clk.h"
+#include "dsi_pwr.h"
#include "dsi_phy_hw.h"
struct dsi_ver_spec_info {
@@ -27,14 +28,6 @@
};
/**
- * struct dsi_phy_clk_info - clock information for DSI controller
- * @core_clks: Core clocks needed to access PHY registers.
- */
-struct dsi_phy_clk_info {
- struct dsi_core_clk_info core_clks;
-};
-
-/**
* struct dsi_phy_power_info - digital and analog power supplies for DSI PHY
* @digital: Digital power supply for DSI PHY.
* @phy_pwr: Analog power supplies for DSI PHY to work.
@@ -45,6 +38,19 @@
};
/**
+ * enum phy_engine_state - define engine status for dsi phy.
+ * @DSI_PHY_ENGINE_OFF: Engine is turned off.
+ * @DSI_PHY_ENGINE_ON: Engine is turned on.
+ * @DSI_PHY_ENGINE_MAX: Maximum value.
+ */
+enum phy_engine_state {
+ DSI_PHY_ENGINE_OFF = 0,
+ DSI_PHY_ENGINE_ON,
+ DSI_PHY_ENGINE_MAX,
+};
+
+
+/**
* struct msm_dsi_phy - DSI PHY object
* @pdev: Pointer to platform device.
* @index: Instance id.
@@ -53,8 +59,11 @@
* @phy_lock: Mutex for hardware and object access.
* @ver_info: Version specific phy parameters.
* @hw: DSI PHY hardware object.
+ * @pwr_info: Power information.
* @cfg: DSI phy configuration.
+ * @clk_cb: structure containing call backs for clock control
* @power_state: True if PHY is powered on.
+ * @dsi_phy_state: PHY state information.
* @mode: Current mode.
* @data_lanes: Number of data lanes used.
* @dst_format: Destination format.
@@ -70,11 +79,12 @@
const struct dsi_ver_spec_info *ver_info;
struct dsi_phy_hw hw;
- struct dsi_phy_clk_info clks;
struct dsi_phy_power_info pwr_info;
struct dsi_phy_cfg cfg;
+ struct clk_ctrl_cb clk_cb;
+ enum phy_engine_state dsi_phy_state;
bool power_state;
struct dsi_mode_info mode;
enum dsi_data_lanes data_lanes;
@@ -170,6 +180,25 @@
int dsi_phy_disable(struct msm_dsi_phy *phy);
/**
+ * dsi_phy_clk_cb_register() - Register PHY clock control callback
+ * @phy: DSI PHY handle
+ * @clk_cb: Structure containing call back for clock control
+ *
+ * Return: error code.
+ */
+int dsi_phy_clk_cb_register(struct msm_dsi_phy *phy,
+ struct clk_ctrl_cb *clk_cb);
+
+/**
+ * dsi_phy_idle_ctrl() - enable/disable DSI PHY during idle screen
+ * @phy: DSI PHY handle
+ * @enable: boolean to specify PHY enable/disable.
+ *
+ * Return: error code.
+ */
+int dsi_phy_idle_ctrl(struct msm_dsi_phy *phy, bool enable);
+
+/**
* dsi_phy_set_timing_params() - timing parameters for the panel
* @phy: DSI PHY handle
* @timing: array holding timing params.
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
index 5edfd5e..78c59e2 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -129,6 +129,20 @@
void (*disable)(struct dsi_phy_hw *phy);
/**
+ * phy_idle_on() - Enable PHY hardware when entering idle screen
+ * @phy: Pointer to DSI PHY hardware object.
+ * @cfg: Per lane configurations for timing, strength and lane
+ * configurations.
+ */
+ void (*phy_idle_on)(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg);
+
+ /**
+ * phy_idle_off() - Disable PHY hardware when exiting idle screen
+ * @phy: Pointer to DSI PHY hardware object.
+ */
+ void (*phy_idle_off)(struct dsi_phy_hw *phy);
+
+ /**
* calculate_timing_params() - calculates timing parameters.
* @phy: Pointer to DSI PHY hardware object.
* @mode: Mode information for which timing has to be calculated.
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v4_0.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v4_0.c
index 512352d..d0aaa6b 100644
--- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v4_0.c
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy_hw_v4_0.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -242,6 +242,44 @@
pr_debug("[DSI_%d]Phy disabled ", phy->index);
}
+/**
+ * dsi_phy_hw_v4_0_idle_on() - Enable DSI PHY hardware during idle screen
+ * @phy: Pointer to DSI PHY hardware object.
+ */
+void dsi_phy_hw_v4_0_idle_on(struct dsi_phy_hw *phy, struct dsi_phy_cfg *cfg)
+{
+ int i = 0;
+
+ for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++) {
+ DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_0(i),
+ cfg->strength.lane[i][0]);
+ DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_1(i),
+ cfg->strength.lane[i][1]);
+ }
+ wmb(); /* make sure write happens */
+ pr_debug("[DSI_%d]Phy enabled out of idle screen\n", phy->index);
+}
+
+
+/**
+ * dsi_phy_hw_v4_0_idle_off() - Disable DSI PHY hardware during idle screen
+ * @phy: Pointer to DSI PHY hardware object.
+ */
+void dsi_phy_hw_v4_0_idle_off(struct dsi_phy_hw *phy)
+{
+ int i = 0;
+
+ DSI_W32(phy, DSIPHY_CMN_CTRL_0, 0x7f);
+ for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++)
+ DSI_W32(phy, DSIPHY_DLNX_VREG_CNTRL(i), 0x1c);
+ DSI_W32(phy, DSIPHY_CMN_LDO_CNTRL, 0x1C);
+
+ for (i = DSI_LOGICAL_LANE_0; i < DSI_LANE_MAX; i++)
+ DSI_W32(phy, DSIPHY_DLNX_STRENGTH_CTRL_1(i), 0x0);
+ wmb(); /* make sure write happens */
+ pr_debug("[DSI_%d]Phy disabled during idle screen\n", phy->index);
+}
+
static const u32 bits_per_pixel[DSI_PIXEL_FORMAT_MAX] = {
16, 18, 18, 24, 3, 8, 12 };
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.c b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.c
new file mode 100644
index 0000000..609c5ff
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include "dsi_pwr.h"
+
+/*
+ * dsi_pwr_parse_supply_node() - parse power supply node from root device node
+ */
+static int dsi_pwr_parse_supply_node(struct device_node *root,
+ struct dsi_regulator_info *regs)
+{
+ int rc = 0;
+ int i = 0;
+ u32 tmp = 0;
+ struct device_node *node = NULL;
+
+ for_each_child_of_node(root, node) {
+ const char *st = NULL;
+
+ rc = of_property_read_string(node, "qcom,supply-name", &st);
+ if (rc) {
+ pr_err("failed to read name, rc = %d\n", rc);
+ goto error;
+ }
+
+ snprintf(regs->vregs[i].vreg_name,
+ ARRAY_SIZE(regs->vregs[i].vreg_name),
+ "%s", st);
+
+ rc = of_property_read_u32(node, "qcom,supply-min-voltage",
+ &tmp);
+ if (rc) {
+ pr_err("failed to read min voltage, rc = %d\n", rc);
+ goto error;
+ }
+ regs->vregs[i].min_voltage = tmp;
+
+ rc = of_property_read_u32(node, "qcom,supply-max-voltage",
+ &tmp);
+ if (rc) {
+ pr_err("failed to read max voltage, rc = %d\n", rc);
+ goto error;
+ }
+ regs->vregs[i].max_voltage = tmp;
+
+ rc = of_property_read_u32(node, "qcom,supply-enable-load",
+ &tmp);
+ if (rc) {
+ pr_err("failed to read enable load, rc = %d\n", rc);
+ goto error;
+ }
+ regs->vregs[i].enable_load = tmp;
+
+ rc = of_property_read_u32(node, "qcom,supply-disable-load",
+ &tmp);
+ if (rc) {
+ pr_err("failed to read disable load, rc = %d\n", rc);
+ goto error;
+ }
+ regs->vregs[i].disable_load = tmp;
+
+ /* Optional values */
+ rc = of_property_read_u32(node, "qcom,supply-pre-on-sleep",
+ &tmp);
+ if (rc) {
+ pr_debug("pre-on-sleep not specified\n");
+ rc = 0;
+ } else {
+ regs->vregs[i].pre_on_sleep = tmp;
+ }
+
+ rc = of_property_read_u32(node, "qcom,supply-pre-off-sleep",
+ &tmp);
+ if (rc) {
+ pr_debug("pre-off-sleep not specified\n");
+ rc = 0;
+ } else {
+ regs->vregs[i].pre_off_sleep = tmp;
+ }
+
+ rc = of_property_read_u32(node, "qcom,supply-post-on-sleep",
+ &tmp);
+ if (rc) {
+ pr_debug("post-on-sleep not specified\n");
+ rc = 0;
+ } else {
+ regs->vregs[i].post_on_sleep = tmp;
+ }
+
+ rc = of_property_read_u32(node, "qcom,supply-post-off-sleep",
+ &tmp);
+ if (rc) {
+ pr_debug("post-off-sleep not specified\n");
+ rc = 0;
+ } else {
+ regs->vregs[i].post_off_sleep = tmp;
+ }
+
+ ++i;
+ pr_debug("[%s] minv=%d maxv=%d, en_load=%d, dis_load=%d\n",
+ regs->vregs[i].vreg_name,
+ regs->vregs[i].min_voltage,
+ regs->vregs[i].max_voltage,
+ regs->vregs[i].enable_load,
+ regs->vregs[i].disable_load);
+ }
+
+error:
+ return rc;
+}
+
+/**
+ * dsi_pwr_enable_vregs() - enable/disable regulators
+ */
+static int dsi_pwr_enable_vregs(struct dsi_regulator_info *regs, bool enable)
+{
+ int rc = 0, i = 0;
+ struct dsi_vreg *vreg;
+ int num_of_v = 0;
+
+ if (enable) {
+ for (i = 0; i < regs->count; i++) {
+ vreg = ®s->vregs[i];
+ if (vreg->pre_on_sleep)
+ msleep(vreg->pre_on_sleep);
+
+ rc = regulator_set_load(vreg->vreg,
+ vreg->enable_load);
+ if (rc < 0) {
+ pr_err("Setting optimum mode failed for %s\n",
+ vreg->vreg_name);
+ goto error;
+ }
+ num_of_v = regulator_count_voltages(vreg->vreg);
+ if (num_of_v > 0) {
+ rc = regulator_set_voltage(vreg->vreg,
+ vreg->min_voltage,
+ vreg->max_voltage);
+ if (rc) {
+ pr_err("Set voltage(%s) fail, rc=%d\n",
+ vreg->vreg_name, rc);
+ goto error_disable_opt_mode;
+ }
+ }
+
+ rc = regulator_enable(vreg->vreg);
+ if (rc) {
+ pr_err("enable failed for %s, rc=%d\n",
+ vreg->vreg_name, rc);
+ goto error_disable_voltage;
+ }
+
+ if (vreg->post_on_sleep)
+ msleep(vreg->post_on_sleep);
+ }
+ } else {
+ for (i = (regs->count - 1); i >= 0; i--) {
+ if (regs->vregs[i].pre_off_sleep)
+ msleep(regs->vregs[i].pre_off_sleep);
+
+ (void)regulator_set_load(regs->vregs[i].vreg,
+ regs->vregs[i].disable_load);
+ (void)regulator_disable(regs->vregs[i].vreg);
+
+ if (regs->vregs[i].post_off_sleep)
+ msleep(regs->vregs[i].post_off_sleep);
+ }
+ }
+
+ return 0;
+error_disable_opt_mode:
+ (void)regulator_set_load(regs->vregs[i].vreg,
+ regs->vregs[i].disable_load);
+
+error_disable_voltage:
+ if (num_of_v > 0)
+ (void)regulator_set_voltage(regs->vregs[i].vreg,
+ 0, regs->vregs[i].max_voltage);
+error:
+ for (i--; i >= 0; i--) {
+ if (regs->vregs[i].pre_off_sleep)
+ msleep(regs->vregs[i].pre_off_sleep);
+
+ (void)regulator_set_load(regs->vregs[i].vreg,
+ regs->vregs[i].disable_load);
+
+ num_of_v = regulator_count_voltages(regs->vregs[i].vreg);
+ if (num_of_v > 0)
+ (void)regulator_set_voltage(regs->vregs[i].vreg,
+ 0, regs->vregs[i].max_voltage);
+
+ (void)regulator_disable(regs->vregs[i].vreg);
+
+ if (regs->vregs[i].post_off_sleep)
+ msleep(regs->vregs[i].post_off_sleep);
+ }
+
+ return rc;
+}
+
+/**
+* dsi_pwr_of_get_vreg_data - Parse regulator supply information
+* @of_node: Device of node to parse for supply information.
+* @regs: Pointer where regulator information will be copied to.
+* @supply_name: Name of the supply node.
+*
+* return: error code in case of failure or 0 for success.
+*/
+int dsi_pwr_of_get_vreg_data(struct device_node *of_node,
+ struct dsi_regulator_info *regs,
+ char *supply_name)
+{
+ int rc = 0;
+ struct device_node *supply_root_node = NULL;
+
+ if (!of_node || !regs) {
+ pr_err("Bad params\n");
+ return -EINVAL;
+ }
+
+ regs->count = 0;
+ supply_root_node = of_get_child_by_name(of_node, supply_name);
+ if (!supply_root_node) {
+ supply_root_node = of_parse_phandle(of_node, supply_name, 0);
+ if (!supply_root_node) {
+ pr_err("No supply entry present for %s\n", supply_name);
+ return -EINVAL;
+ }
+ }
+
+ regs->count = of_get_available_child_count(supply_root_node);
+ if (regs->count == 0) {
+ pr_err("No vregs defined for %s\n", supply_name);
+ return -EINVAL;
+ }
+
+ regs->vregs = kcalloc(regs->count, sizeof(*regs->vregs), GFP_KERNEL);
+ if (!regs->vregs) {
+ regs->count = 0;
+ return -ENOMEM;
+ }
+
+ rc = dsi_pwr_parse_supply_node(supply_root_node, regs);
+ if (rc) {
+ pr_err("failed to parse supply node for %s, rc = %d\n",
+ supply_name, rc);
+
+ kfree(regs->vregs);
+ regs->vregs = NULL;
+ regs->count = 0;
+ }
+
+ return rc;
+}
+
+/**
+ * dsi_pwr_get_dt_vreg_data - parse regulator supply information
+ * @dev: Device whose of_node needs to be parsed.
+ * @regs: Pointer where regulator information will be copied to.
+ * @supply_name: Name of the supply node.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_pwr_get_dt_vreg_data(struct device *dev,
+ struct dsi_regulator_info *regs,
+ char *supply_name)
+{
+ int rc = 0;
+ struct device_node *of_node = NULL;
+ struct device_node *supply_node = NULL;
+ struct device_node *supply_root_node = NULL;
+
+ if (!dev || !regs) {
+ pr_err("Bad params\n");
+ return -EINVAL;
+ }
+
+ of_node = dev->of_node;
+ regs->count = 0;
+ supply_root_node = of_get_child_by_name(of_node, supply_name);
+ if (!supply_root_node) {
+ supply_root_node = of_parse_phandle(of_node, supply_name, 0);
+ if (!supply_root_node) {
+ pr_err("No supply entry present for %s\n", supply_name);
+ return -EINVAL;
+ }
+ }
+
+ for_each_child_of_node(supply_root_node, supply_node)
+ regs->count++;
+
+ if (regs->count == 0) {
+ pr_err("No vregs defined for %s\n", supply_name);
+ return -EINVAL;
+ }
+
+ regs->vregs = devm_kcalloc(dev, regs->count, sizeof(*regs->vregs),
+ GFP_KERNEL);
+ if (!regs->vregs) {
+ regs->count = 0;
+ return -ENOMEM;
+ }
+
+ rc = dsi_pwr_parse_supply_node(supply_root_node, regs);
+ if (rc) {
+ pr_err("failed to parse supply node for %s, rc = %d\n",
+ supply_name, rc);
+ devm_kfree(dev, regs->vregs);
+ regs->vregs = NULL;
+ regs->count = 0;
+ }
+
+ return rc;
+}
+
+/**
+ * dsi_pwr_enable_regulator() - enable a set of regulators
+ * @regs: Pointer to set of regulators to enable or disable.
+ * @enable: Enable/Disable regulators.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_pwr_enable_regulator(struct dsi_regulator_info *regs, bool enable)
+{
+ int rc = 0;
+
+ if (enable) {
+ if (regs->refcount == 0) {
+ rc = dsi_pwr_enable_vregs(regs, true);
+ if (rc)
+ pr_err("failed to enable regulators\n");
+ }
+ regs->refcount++;
+ } else {
+ if (regs->refcount == 0) {
+ pr_err("Unbalanced regulator off\n");
+ } else {
+ regs->refcount--;
+ if (regs->refcount == 0) {
+ rc = dsi_pwr_enable_vregs(regs, false);
+ if (rc)
+ pr_err("failed to disable vregs\n");
+ }
+ }
+ }
+
+ return rc;
+}
diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.h b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.h
new file mode 100644
index 0000000..4d85244
--- /dev/null
+++ b/drivers/gpu/drm/msm/dsi-staging/dsi_pwr.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _DSI_PWR_H_
+#define _DSI_PWR_H_
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/regulator/consumer.h>
+
+/**
+ * struct dsi_vreg - regulator information for DSI regulators
+ * @vreg: Handle to the regulator.
+ * @vreg_name: Regulator name.
+ * @min_voltage: Minimum voltage in uV.
+ * @max_voltage: Maximum voltage in uV.
+ * @enable_load: Load, in uA, when enabled.
+ * @disable_load: Load, in uA, when disabled.
+ * @pre_on_sleep: Sleep, in ms, before enabling the regulator.
+ * @post_on_sleep: Sleep, in ms, after enabling the regulator.
+ * @pre_off_sleep: Sleep, in ms, before disabling the regulator.
+ * @post_off_sleep: Sleep, in ms, after disabling the regulator.
+ */
+struct dsi_vreg {
+ struct regulator *vreg;
+ char vreg_name[32];
+ u32 min_voltage;
+ u32 max_voltage;
+ u32 enable_load;
+ u32 disable_load;
+ u32 pre_on_sleep;
+ u32 post_on_sleep;
+ u32 pre_off_sleep;
+ u32 post_off_sleep;
+};
+
+/**
+ * struct dsi_regulator_info - set of vregs that are turned on/off together.
+ * @vregs: Array of dsi_vreg structures.
+ * @count: Number of vregs.
+ * @refcount: Reference counting for enabling.
+ */
+struct dsi_regulator_info {
+ struct dsi_vreg *vregs;
+ u32 count;
+ u32 refcount;
+};
+
+/**
+ * dsi_pwr_of_get_vreg_data - parse regulator supply information
+ * @of_node: Device of node to parse for supply information.
+ * @regs: Pointer where regulator information will be copied to.
+ * @supply_name: Name of the supply node.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_pwr_of_get_vreg_data(struct device_node *of_node,
+ struct dsi_regulator_info *regs,
+ char *supply_name);
+
+/**
+ * dsi_pwr_get_dt_vreg_data - parse regulator supply information
+ * @dev: Device whose of_node needs to be parsed.
+ * @regs: Pointer where regulator information will be copied to.
+ * @supply_name: Name of the supply node.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_pwr_get_dt_vreg_data(struct device *dev,
+ struct dsi_regulator_info *regs,
+ char *supply_name);
+
+/**
+ * dsi_pwr_enable_regulator() - enable a set of regulators
+ * @regs: Pointer to set of regulators to enable or disable.
+ * @enable: Enable/Disable regulators.
+ *
+ * return: error code in case of failure or 0 for success.
+ */
+int dsi_pwr_enable_regulator(struct dsi_regulator_info *regs, bool enable);
+#endif /* _DSI_PWR_H_ */