@@ -19,6 +19,8 @@
#include <drm/drm_fb_helper.h>
#include <drm/drm_gem_cma_helper.h>
#include <drm/drm_of.h>
+#include <linux/devfreq.h>
+#include <linux/devfreq-event.h>
#include <linux/dma-mapping.h>
#include <linux/dma-iommu.h>
#include <linux/pm_runtime.h>
@@ -77,6 +79,46 @@ void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
iommu_detach_device(domain, dev);
}
+#if IS_ENABLED(CONFIG_ARM_RK3399_DMC_DEVFREQ)
+static int rockchip_drm_init_devfreq(struct device *dev,
+ struct rockchip_drm_private *priv)
+{
+ struct devfreq *devfreq;
+ struct devfreq_event_dev *edev;
+ int ret;
+
+ devfreq = devfreq_get_devfreq_by_phandle(dev, 0);
+ if (IS_ERR(devfreq)) {
+ ret = PTR_ERR(devfreq);
+ if (ret == -ENODEV) {
+ DRM_DEV_INFO(dev, "devfreq missing, skip\n");
+ return 0;
+ }
+ return ret;
+ }
+
+ edev = devfreq_event_get_edev_by_phandle(devfreq->dev.parent, 0);
+ if (IS_ERR(edev)) {
+ ret = PTR_ERR(edev);
+ if (ret == -ENODEV) {
+ DRM_DEV_INFO(dev, "devfreq edev missing, skip\n");
+ return 0;
+ }
+ return ret;
+ }
+
+ priv->devfreq = devfreq;
+ priv->devfreq_event_dev = edev;
+ return 0;
+}
+#else
+static int rockchip_drm_init_devfreq(struct device *dev,
+ struct rockchip_drm_private *priv)
+{
+ return 0;
+}
+#endif
+
static int rockchip_drm_init_iommu(struct drm_device *drm_dev)
{
struct rockchip_drm_private *private = drm_dev->dev_private;
@@ -136,6 +178,10 @@ static int rockchip_drm_bind(struct device *dev)
INIT_LIST_HEAD(&private->psr_list);
mutex_init(&private->psr_list_lock);
+ ret = rockchip_drm_init_devfreq(dev, private);
+ if (ret)
+ goto err_free;
+
ret = rockchip_drm_init_iommu(drm_dev);
if (ret)
goto err_free;
@@ -57,6 +57,10 @@ struct rockchip_drm_private {
struct drm_mm mm;
struct list_head psr_list;
struct mutex psr_list_lock;
+
+ struct devfreq *devfreq;
+ struct devfreq_event_dev *devfreq_event_dev;
+ bool dmc_disable_flag;
};
int rockchip_drm_dma_attach_device(struct drm_device *drm_dev,
@@ -65,6 +69,11 @@ void rockchip_drm_dma_detach_device(struct drm_device *drm_dev,
struct device *dev);
int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout);
+uint32_t rockchip_drm_get_vblank_ns(struct drm_display_mode *mode);
+
+void rockchip_drm_enable_dmc(struct rockchip_drm_private *priv);
+void rockchip_drm_disable_dmc(struct rockchip_drm_private *priv);
+
extern struct platform_driver cdn_dp_driver;
extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
extern struct platform_driver dw_mipi_dsi_driver;
@@ -18,6 +18,7 @@
#include <drm/drm_atomic.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_crtc_helper.h>
+#include <soc/rockchip/rk3399_dmc.h>
#include "rockchip_drm_drv.h"
#include "rockchip_drm_fb.h"
@@ -185,6 +186,16 @@ rockchip_drm_psr_inhibit_get_state(struct drm_atomic_state *state)
rockchip_drm_psr_inhibit_get(encoder);
}
+uint32_t rockchip_drm_get_vblank_ns(struct drm_display_mode *mode)
+{
+ uint64_t vblank_time = mode->vtotal - mode->vdisplay;
+
+ vblank_time *= (uint64_t)NSEC_PER_SEC * mode->htotal;
+ do_div(vblank_time, mode->clock * 1000);
+
+ return vblank_time;
+}
+
static void
rockchip_drm_psr_inhibit_put_state(struct drm_atomic_state *state)
{
@@ -207,9 +218,28 @@ static void
rockchip_atomic_helper_commit_tail_rpm(struct drm_atomic_state *old_state)
{
struct drm_device *dev = old_state->dev;
+ struct rockchip_drm_private *priv = dev->dev_private;
+ struct drm_crtc *crtc;
+ struct drm_display_mode *mode;
+ bool force_dmc_off = false;
+
+ drm_for_each_crtc(crtc, dev) {
+ if (crtc->state->active) {
+ mode = &crtc->state->adjusted_mode;
+ if (rockchip_drm_get_vblank_ns(mode) <
+ DMC_MIN_VBLANK_NS)
+ force_dmc_off = true;
+ }
+ }
rockchip_drm_psr_inhibit_get_state(old_state);
+ /* If disabling dmc, disable it before committing mode set changes. */
+ if (force_dmc_off && !priv->dmc_disable_flag) {
+ rockchip_dmcfreq_block(priv->devfreq);
+ priv->dmc_disable_flag = true;
+ }
+
drm_atomic_helper_commit_modeset_disables(dev, old_state);
drm_atomic_helper_commit_modeset_enables(dev, old_state);
@@ -224,6 +254,11 @@ rockchip_atomic_helper_commit_tail_rpm(struct drm_atomic_state *old_state)
drm_atomic_helper_wait_for_vblanks(dev, old_state);
drm_atomic_helper_cleanup_planes(dev, old_state);
+
+ if (!force_dmc_off && priv->dmc_disable_flag) {
+ rockchip_dmcfreq_unblock(priv->devfreq);
+ priv->dmc_disable_flag = false;
+ }
}
static const struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = {
@@ -36,6 +36,8 @@
#include <linux/reset.h>
#include <linux/delay.h>
+#include <soc/rockchip/rk3399_dmc.h>
+
#include "rockchip_drm_drv.h"
#include "rockchip_drm_gem.h"
#include "rockchip_drm_fb.h"
@@ -103,7 +105,9 @@ struct vop {
struct drm_flip_work fb_unref_work;
unsigned long pending;
+ ktime_t line_flag_timestamp;
struct completion line_flag_completion;
+ struct notifier_block dmc_nb;
const struct vop_data *data;
@@ -569,10 +573,12 @@ static int vop_enable(struct drm_crtc *crtc)
static void vop_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_crtc_state *old_state)
{
+ struct rockchip_drm_private *priv = crtc->dev->dev_private;
struct vop *vop = to_vop(crtc);
WARN_ON(vop->event);
+ rockchip_dmcfreq_unregister_clk_sync_nb(priv->devfreq, &vop->dmc_nb);
mutex_lock(&vop->vop_lock);
drm_crtc_vblank_off(crtc);
@@ -861,6 +867,7 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
const struct vop_data *vop_data = vop->data;
struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state);
struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode;
+ struct rockchip_drm_private *priv = crtc->dev->dev_private;
u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
u16 hdisplay = adjusted_mode->hdisplay;
u16 htotal = adjusted_mode->htotal;
@@ -951,6 +958,31 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc,
VOP_REG_SET(vop, common, standby, 0);
mutex_unlock(&vop->vop_lock);
+ rockchip_dmcfreq_register_clk_sync_nb(priv->devfreq, &vop->dmc_nb);
+}
+
+static int dmc_notify(struct notifier_block *nb,
+ unsigned long action,
+ void *data)
+{
+ struct vop *vop = container_of(nb, struct vop, dmc_nb);
+ struct drm_crtc *crtc = &vop->crtc;
+ ktime_t *timeout = data;
+ int ret;
+
+ if (WARN_ON(!vop->is_enabled))
+ return NOTIFY_BAD;
+
+ ret = rockchip_drm_wait_vact_end(crtc, 100);
+ *timeout = ktime_add_ns(vop->line_flag_timestamp,
+ rockchip_drm_get_vblank_ns(&crtc->mode));
+ if (ret) {
+ dev_err(vop->dev, "%s: Line flag interrupt did not arrive\n",
+ __func__);
+ return NOTIFY_BAD;
+ }
+
+ return NOTIFY_STOP;
}
static bool vop_fs_irq_is_pending(struct vop *vop)
@@ -1190,6 +1222,7 @@ static irqreturn_t vop_isr(int irq, void *data)
}
if (active_irqs & LINE_FLAG_INTR) {
+ vop->line_flag_timestamp = ktime_get();
complete(&vop->line_flag_completion);
active_irqs &= ~LINE_FLAG_INTR;
ret = IRQ_HANDLED;
@@ -1566,6 +1599,7 @@ static int vop_bind(struct device *dev, struct device *master, void *data)
spin_lock_init(&vop->reg_lock);
spin_lock_init(&vop->irq_lock);
mutex_init(&vop->vop_lock);
+ vop->dmc_nb.notifier_call = dmc_notify;
ret = vop_create_crtc(vop);
if (ret)