@@ -19,6 +19,8 @@
#include <drm/drm_crtc_helper.h>
#include <drm/drm_plane_helper.h>
+#include <linux/devfreq.h>
+#include <linux/devfreq-event.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
@@ -118,6 +120,13 @@ struct vop {
const struct vop_data *data;
+ struct devfreq *devfreq;
+ struct devfreq_event_dev *devfreq_event_dev;
+ struct notifier_block dmc_nb;
+ int dmc_in_process;
+ int vop_switch_status;
+ wait_queue_head_t wait_dmc_queue;
+ wait_queue_head_t wait_vop_switch_queue;
uint32_t *regsbak;
void __iomem *regs;
@@ -428,11 +437,47 @@ static void vop_dsp_hold_valid_irq_disable(struct vop *vop)
spin_unlock_irqrestore(&vop->irq_lock, flags);
}
+static int dmc_notify(struct notifier_block *nb, unsigned long event,
+ void *data)
+{
+ struct vop *vop = container_of(nb, struct vop, dmc_nb);
+
+ if (event == DEVFREQ_PRECHANGE) {
+ /*
+ * check if vop in enable or disable process,
+ * if yes, wait until it finishes, use 200ms as
+ * timeout.
+ */
+ if (!wait_event_timeout(vop->wait_vop_switch_queue,
+ !vop->vop_switch_status, HZ / 5))
+ dev_warn(vop->dev,
+ "Timeout waiting for vop swtich status\n");
+ vop->dmc_in_process = 1;
+ } else if (event == DEVFREQ_POSTCHANGE) {
+ vop->dmc_in_process = 0;
+ wake_up(&vop->wait_dmc_queue);
+ }
+
+ return NOTIFY_OK;
+}
+
static int vop_enable(struct drm_crtc *crtc)
{
struct vop *vop = to_vop(crtc);
+ int num_enabled_crtc = 0;
int ret;
+ /*
+ * if in dmc scaling frequency process, wait until it finishes
+ * use 200ms as timeout time.
+ */
+ if (!wait_event_timeout(vop->wait_dmc_queue,
+ !vop->dmc_in_process, HZ / 5))
+ dev_warn(vop->dev,
+ "Timeout waiting for dmc when vop enable\n");
+
+ vop->vop_switch_status = 1;
+
ret = pm_runtime_get_sync(vop->dev);
if (ret < 0) {
dev_err(vop->dev, "failed to get pm runtime: %d\n", ret);
@@ -479,6 +524,21 @@ static int vop_enable(struct drm_crtc *crtc)
drm_crtc_vblank_on(crtc);
+ vop->vop_switch_status = 0;
+ wake_up(&vop->wait_vop_switch_queue);
+
+ /* check how many VOPs in use now */
+ drm_for_each_crtc(crtc, vop->drm_dev) {
+ if (crtc->state->enable)
+ num_enabled_crtc++;
+ }
+
+ /* if enable two vop, need to disable dmc */
+ if ((num_enabled_crtc > 1) && vop->devfreq) {
+ if (vop->devfreq_event_dev)
+ devfreq_event_disable_edev(vop->devfreq_event_dev);
+ devfreq_suspend_device(vop->devfreq);
+ }
return 0;
err_disable_aclk:
@@ -489,17 +549,31 @@ err_disable_hclk:
clk_disable(vop->hclk);
err_put_pm_runtime:
pm_runtime_put_sync(vop->dev);
+ vop->vop_switch_status = 0;
+ wake_up(&vop->wait_vop_switch_queue);
return ret;
}
static void vop_crtc_disable(struct drm_crtc *crtc)
{
struct vop *vop = to_vop(crtc);
+ int num_enabled_crtc = 0;
int i;
WARN_ON(vop->event);
/*
+ * if in dmc scaling frequency process, wait until it finish
+ * use 200ms as timeout time.
+ */
+ if (!wait_event_timeout(vop->wait_dmc_queue,
+ !vop->dmc_in_process, HZ / 5))
+ dev_warn(vop->dev,
+ "Timeout waiting for dmc when vop disable\n");
+
+ vop->vop_switch_status = 1;
+
+ /*
* We need to make sure that all windows are disabled before we
* disable that crtc. Otherwise we might try to scan from a destroyed
* buffer later.
@@ -555,6 +629,24 @@ static void vop_crtc_disable(struct drm_crtc *crtc)
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
+
+ vop->vop_switch_status = 0;
+ wake_up(&vop->wait_vop_switch_queue);
+
+ /* check how many VOPs in use now */
+ drm_for_each_crtc(crtc, vop->drm_dev) {
+ if (crtc->state->enable)
+ num_enabled_crtc++;
+ }
+
+ /*
+ * if num_enabled_crtc = 1 now, it means 2 vop enabled
+ * change to 1 vop enabled need to enable dmc again.
+ */
+ if ((num_enabled_crtc == 1) && vop->devfreq) {
+ if (vop->devfreq_event_dev)
+ devfreq_event_enable_edev(vop->devfreq_event_dev);
+ devfreq_resume_device(vop->devfreq);
}
}
@@ -1413,6 +1505,8 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
struct drm_device *drm_dev = data;
struct vop *vop;
struct resource *res;
+ struct devfreq *devfreq;
+ struct devfreq_event_dev *event_dev;
size_t alloc_size;
int ret, irq;
@@ -1474,6 +1568,28 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
return ret;
pm_runtime_enable(&pdev->dev);
+
+ init_waitqueue_head(&vop->wait_vop_switch_queue);
+ vop->vop_switch_status = 0;
+ init_waitqueue_head(&vop->wait_dmc_queue);
+ vop->dmc_in_process = 0;
+
+ devfreq = devfreq_get_devfreq_by_phandle(dev, 0);
+ if (IS_ERR(devfreq))
+ goto out;
+
+ vop->devfreq = devfreq;
+ vop->dmc_nb.notifier_call = dmc_notify;
+ devfreq_register_notifier(vop->devfreq, &vop->dmc_nb,
+ DEVFREQ_TRANSITION_NOTIFIER);
+
+ event_dev = devfreq_event_get_edev_by_phandle(vop->devfreq->dev.parent,
+ 0);
+ if (IS_ERR(event_dev))
+ goto out;
+
+ vop->devfreq_event_dev = event_dev;
+out:
return 0;
}