From patchwork Tue Apr 30 18:05:21 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Ga=C3=ABl_PORTAY?= X-Patchwork-Id: 10924669 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id ACA8B1390 for ; Wed, 1 May 2019 07:19:16 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 94F1F28ABC for ; Wed, 1 May 2019 07:19:16 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 88C0428B3E; Wed, 1 May 2019 07:19:16 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id DCDF128ABC for ; Wed, 1 May 2019 07:19:15 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 86BAF89388; Wed, 1 May 2019 07:19:09 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [46.235.227.227]) by gabe.freedesktop.org (Postfix) with ESMTPS id B026889150 for ; Tue, 30 Apr 2019 18:05:41 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) (Authenticated sender: gportay) with ESMTPSA id 23F4A282FE4 From: =?utf-8?q?Ga=C3=ABl_PORTAY?= To: Rob Herring , Mark Rutland , Heiko Stuebner , Michael Turquette , Stephen Boyd , MyungJoo Ham , Kyungmin Park , Chanwoo Choi , Sandy Huang , David Airlie , Daniel Vetter , =?utf-8?q?Ga=C3=ABl_PORTAY?= , devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-pm@vger.kernel.org, dri-devel@lists.freedesktop.org Subject: [RFC 1/4] PM / devfreq: add devfreq_lock/unlock() functions Date: Tue, 30 Apr 2019 14:05:21 -0400 Message-Id: <20190430180524.22710-2-gael.portay@collabora.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190430180524.22710-1-gael.portay@collabora.com> References: <20190430180524.22710-1-gael.portay@collabora.com> MIME-Version: 1.0 X-Mailman-Approved-At: Wed, 01 May 2019 07:18:43 +0000 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Lin Huang , Derek Basehore , Douglas Anderson , Matthias Kaehlcke , Boris Brezillon , Enric Balletbo i Serra , kernel@collabora.com Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP This patch adds the implementation for lock/unlock functions in the devfreq framework. In some situations, changing the clock rate affect other devices, and the devfreq framework needs a mean to synchronize all the drivers together. This locking API allows third-party drivers that use a devfreq device to have control on whether it can change the rate or not. Those drivers can lock the devfreq device to prevent it from changing the rate, and then can release the lock to let it change the rate again. When a change rate is triggered, and the devfreq device is locked, the device informs the drivers that hold a lock through a callback that a change rate is wanted. This change is not applied. It is marked as pending. When a pending rate change exists, and all the locks are released, the devfreq device resumes the rate change that is pending after the last lock is released. Signed-off-by: Gaël PORTAY --- drivers/devfreq/devfreq.c | 200 +++++++++++++++++++++++++++++++++++++- include/linux/devfreq.h | 64 ++++++++++++ 2 files changed, 263 insertions(+), 1 deletion(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 0ae3de76833b..a655e14a28f6 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -323,6 +323,60 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq, return err; } +static void devfreq_set_target_work(struct work_struct *work) +{ + struct devfreq *devfreq = container_of(work, struct devfreq, + set_target_work); + struct devfreq_dev_userlock *userlock, *userlock_safe; + unsigned long freq, lock_flags; + bool deadline = false; + ktime_t now; + bool locked; + u32 flags; + int err; + + /* Suspended, skip. */ + if (atomic_read(&devfreq->suspend_count) > 0) + return; + + spin_lock_irqsave(&devfreq->lockers_spinlock, lock_flags); + locked = !list_empty(&devfreq->userlocks.locked); + /* Not locked, check if deadline of unlockers was not reached. */ + if (!locked) { + now = ktime_get(); + freq = devfreq->new_freq; + flags = devfreq->new_flags; + + list_for_each_entry_safe(userlock, userlock_safe, + &devfreq->userlocks.unlocked, + node) { + if (ktime_after(userlock->deadline, now)) + continue; + + if (!userlock->deadline) + continue; + + deadline = true; + list_move_tail(&userlock->node, + &devfreq->userlocks.locked); + } + + if (!deadline) + devfreq->set_target_pending = false; + } + spin_unlock_irqrestore(&devfreq->lockers_spinlock, lock_flags); + + if (locked || deadline) + return; + + mutex_lock(&devfreq->lock); + err = devfreq_set_target(devfreq, freq, flags); + if (err) + dev_err(&devfreq->dev, "failed to set target with (%d) error\n", + err); + mutex_unlock(&devfreq->lock); +} + /* Load monitoring helper functions for governors use */ /** @@ -334,7 +388,9 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq, */ int update_devfreq(struct devfreq *devfreq) { - unsigned long freq, min_freq, max_freq; + unsigned long freq, min_freq, max_freq, lock_flags; + struct devfreq_dev_userlock *userlock; + bool locked = false; int err = 0; u32 flags = 0; @@ -370,6 +426,28 @@ int update_devfreq(struct devfreq *devfreq) flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */ } + spin_lock_irqsave(&devfreq->lockers_spinlock, lock_flags); + locked = !list_empty(&devfreq->userlocks.locked); + /* Device is locked, tell lockers that a change rate is wanted */ + if (locked) { + devfreq->set_target_pending = true; + devfreq->new_freq = freq; + devfreq->new_flags = flags; + + list_for_each_entry(userlock, &devfreq->userlocks.locked, + node) { + if (userlock->want_to_change_rate) + userlock->want_to_change_rate(userlock, + devfreq); + } + } + spin_unlock_irqrestore(&devfreq->lockers_spinlock, lock_flags); + + if (locked) { + dev_warn(&devfreq->dev, "Locked!\n"); + return 0; + } + return devfreq_set_target(devfreq, freq, flags); } @@ -646,6 +724,9 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->last_status.current_frequency = profile->initial_freq; devfreq->data = data; devfreq->nb.notifier_call = devfreq_notifier_call; + INIT_WORK(&devfreq->set_target_work, devfreq_set_target_work); + INIT_LIST_HEAD(&devfreq->userlocks.locked); + INIT_LIST_HEAD(&devfreq->userlocks.unlocked); if (!devfreq->profile->max_state && !devfreq->profile->freq_table) { mutex_unlock(&devfreq->lock); @@ -1682,3 +1763,120 @@ void devm_devfreq_unregister_notifier(struct device *dev, devm_devfreq_dev_match, devfreq)); } EXPORT_SYMBOL(devm_devfreq_unregister_notifier); + +/** + * devfreq_register_dev_user_lock() - Helper function to register a user lock + * to devfreq + * @userlock: The devfreq dev user lock object. + * @devfreq: The devfreq object. + * + * Helper function to register the @userlock to the @devfreq device. + * + * The function moves the @userlock to the list of unlockers. + * + * Return: 0 on success or -EINVAL if arguments are invalid. + */ +int devfreq_register_dev_user_lock(struct devfreq_dev_userlock *userlock, + struct devfreq *devfreq) +{ + unsigned long flags; + + if (!devfreq || !userlock) + return -EINVAL; + + spin_lock_irqsave(&devfreq->lockers_spinlock, flags); + userlock->deadline = 0; + list_add_tail(&userlock->node, &devfreq->userlocks.unlocked); + spin_unlock_irqrestore(&devfreq->lockers_spinlock, flags); + + return 0; +} +EXPORT_SYMBOL(devfreq_register_dev_user_lock); + +/** + * devfreq_unregister_dev_user_lock() - Helper function to unregister a user + * lock + * @userlock: The devfreq dev user lock object. + * @devfreq: The devfreq object. + * + * Helper function to unregister the @userlock from the @devfreq device. + * + * The function removes the @userlock. + */ +void devfreq_unregister_dev_user_lock(struct devfreq_dev_userlock *userlock, + struct devfreq *devfreq) +{ + unsigned long flags; + + if (!devfreq || !userlock) + return; + + spin_lock_irqsave(&devfreq->lockers_spinlock, flags); + list_del(&userlock->node); + spin_unlock_irqrestore(&devfreq->lockers_spinlock, flags); +} +EXPORT_SYMBOL(devfreq_unregister_dev_user_lock); + +/** + * devfreq_lock_device() - Helper function to lock devfreq + * @devfreq: The devfreq object. + * @userlock: The devfreq dev user lock object. + * + * Helper function to lock the @devfreq device from changing the frequency rate. + * The function moves the @userlock to the list of lockers. + * + * The @userlock should be registered. + * + * Return: 0 on success or -EINVAL if arguments are invalid. + */ +int devfreq_lock_device(struct devfreq *devfreq, + struct devfreq_dev_userlock *userlock) +{ + unsigned long flags; + + if (!devfreq || !userlock) + return -EINVAL; + + spin_lock_irqsave(&devfreq->lockers_spinlock, flags); + list_move_tail(&userlock->node, &devfreq->userlocks.locked); + spin_unlock_irqrestore(&devfreq->lockers_spinlock, flags); + + return 0; +} +EXPORT_SYMBOL(devfreq_lock_device); + +/** + * devfreq_unlock_device() - Helper function to unlock devfreq + * @devfreq: The devfreq object. + * @userlock: The devfreq dev user lock object. + * @deadline: The deadline. + * + * Helper function to unlock the @devfreq device. The function moves the + * @userlock to the list of unlockers. + * + * If @devfreq device has no more lockers in its list, it triggers the pending + * rate change. + * + * The @userlock should be registered. + */ +void devfreq_unlock_device(struct devfreq *devfreq, + struct devfreq_dev_userlock *userlock, + ktime_t deadline) +{ + unsigned long flags; + + if (!devfreq || !userlock) + return; + + if (atomic_read(&devfreq->suspend_count) > 0) + return; + + spin_lock_irqsave(&devfreq->lockers_spinlock, flags); + userlock->deadline = deadline; + list_move_tail(&userlock->node, &devfreq->userlocks.unlocked); + if (list_empty(&devfreq->userlocks.locked) && + devfreq->set_target_pending) + queue_work(system_highpri_wq, &devfreq->set_target_work); + spin_unlock_irqrestore(&devfreq->lockers_spinlock, flags); +} +EXPORT_SYMBOL(devfreq_unlock_device); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index fbffa74bfc1b..0a577a776235 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -35,6 +35,7 @@ struct devfreq; struct devfreq_governor; +struct devfreq_dev_userlock; /** * struct devfreq_dev_status - Data given from devfreq user device to @@ -109,6 +110,21 @@ struct devfreq_dev_profile { unsigned int max_state; }; +/** + * struct devfreq_dev_userlock - Devfreq's user device lock + * @node: list node - contains the userlocks that have been locked or + * unlocked. + * @deadline: The timestamp while the unlock is valid. + * @want_to_change_rate: The callback that is called for every lockers, + * when devfreq wants to change the frequency rate. + */ +struct devfreq_dev_userlock { + struct list_head node; + ktime_t deadline; + void (*want_to_change_rate)(struct devfreq_dev_userlock *user, + struct devfreq *devfreq); +}; + /** * struct devfreq - Device devfreq structure * @node: list node - contains the devices with devfreq that have been @@ -138,6 +154,12 @@ struct devfreq_dev_profile { * @trans_table: Statistics of devfreq transitions * @time_in_state: Statistics of devfreq states * @last_stat_updated: The last time stat updated + * @new_freq: the last frequency change before lock. + * @new_flags: the last flag change before lock. + * @set_target_pending: is a set target pending. + * @set_target_work: work for resuming set target. + * @userlocks: the lists of user locks (unlocked and locked). + * @lock_spinlock: a spinlock to protect accessing to lock attributes. * @transition_notifier_list: list head of DEVFREQ_TRANSITION_NOTIFIER notifier * * This structure stores the devfreq information for a give device. @@ -180,6 +202,16 @@ struct devfreq { unsigned long *time_in_state; unsigned long last_stat_updated; + unsigned long new_freq; + u32 new_flags; + bool set_target_pending; + struct work_struct set_target_work; + struct { + struct list_head unlocked; + struct list_head locked; + } userlocks; + spinlock_t lockers_spinlock; + struct srcu_notifier_head transition_notifier_list; }; @@ -243,6 +275,15 @@ extern void devm_devfreq_unregister_notifier(struct device *dev, unsigned int list); extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, int index); +extern int devfreq_register_dev_user_lock( + struct devfreq_dev_userlock *userlock, struct devfreq *devfreq); +extern void devfreq_unregister_dev_user_lock( + struct devfreq_dev_userlock *userlock, struct devfreq *devfreq); +extern int devfreq_lock_device(struct devfreq *devfreq, + struct devfreq_dev_userlock *userlock); +extern void devfreq_unlock_device(struct devfreq *devfreq, + struct devfreq_dev_userlock *userlock, + ktime_t deadline); #if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) /** @@ -405,6 +446,29 @@ static inline int devfreq_update_stats(struct devfreq *df) { return -EINVAL; } + +static inline int devfreq_register_dev_user_lock( + struct devfreq_dev_userlock *userlock, struct devfreq *devfreq) +{ + return -EINVAL; +} + +static inline void devfreq_unregister_dev_user_unlock( + struct devfreq_dev_userlock *userlock, struct devfreq *devfreq) +{ +} + +static inline int devfreq_lock_device(struct devfreq *devfreq, + struct devfreq_dev_userlock *userlock) +{ + return -EINVAL; +} + +static inline void devfreq_unlock_device(struct devfreq *devfreq, + struct devfreq_dev_userlock *userlock, + ktime_t deadline) +{ +} #endif /* CONFIG_PM_DEVFREQ */ #endif /* __LINUX_DEVFREQ_H__ */ From patchwork Tue Apr 30 18:05:22 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Ga=C3=ABl_PORTAY?= X-Patchwork-Id: 10924667 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CF3961390 for ; Wed, 1 May 2019 07:19:14 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id B6E3028ABC for ; Wed, 1 May 2019 07:19:14 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id AAB0B28B3E; Wed, 1 May 2019 07:19:14 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id D6F7228ABC for ; Wed, 1 May 2019 07:19:13 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id CD1118936B; Wed, 1 May 2019 07:19:08 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [IPv6:2a00:1098:0:82:1000:25:2eeb:e3e3]) by gabe.freedesktop.org (Postfix) with ESMTPS id A618D8915B for ; Tue, 30 Apr 2019 18:05:44 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) (Authenticated sender: gportay) with ESMTPSA id D746728335F From: =?utf-8?q?Ga=C3=ABl_PORTAY?= To: Rob Herring , Mark Rutland , Heiko Stuebner , Michael Turquette , Stephen Boyd , MyungJoo Ham , Kyungmin Park , Chanwoo Choi , Sandy Huang , David Airlie , Daniel Vetter , =?utf-8?q?Ga=C3=ABl_PORTAY?= , devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-pm@vger.kernel.org, dri-devel@lists.freedesktop.org Subject: [RFC 2/4] drm: rockchip: Add DDR devfreq support. Date: Tue, 30 Apr 2019 14:05:22 -0400 Message-Id: <20190430180524.22710-3-gael.portay@collabora.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190430180524.22710-1-gael.portay@collabora.com> References: <20190430180524.22710-1-gael.portay@collabora.com> MIME-Version: 1.0 X-Mailman-Approved-At: Wed, 01 May 2019 07:18:43 +0000 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Lin Huang , Derek Basehore , Douglas Anderson , Matthias Kaehlcke , Boris Brezillon , Enric Balletbo i Serra , kernel@collabora.com Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP This commit adds the support for devfreq to control the DDR frequency dynamically. Glitches affect the display when the DMC devfreq device changes the DDR frequency during the scanout. The DRM driver synchronizes the rate change within the VBLANK. The VOP locks the DMC devfreq device that causes it to notified when a rate change is wanted. Then, the VOP enables the VBLANK interrupt, releases the lock when the interrupt arises and locks the devfreq device again after the vblank pulse ends using a timer. The DRM driver disables the devfreq device if more than one CRTC becomes active. Signed-off-by: Gaël PORTAY Signed-off-by: Gaël PORTAY --- drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 51 +++++- drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 6 + drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 177 +++++++++++++++++++- drivers/gpu/drm/rockchip/rockchip_drm_fb.h | 3 +- drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 82 +++++++++ 5 files changed, 315 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index d7fa17f12769..ef843568a7f8 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include #include @@ -78,6 +80,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; @@ -137,13 +179,19 @@ 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; drm_mode_config_init(drm_dev); - rockchip_drm_mode_config_init(drm_dev); + ret = rockchip_drm_mode_config_init(drm_dev); + if (ret) + goto err_free; /* Try to bind all sub drivers. */ ret = component_bind_all(dev, drm_dev); @@ -201,6 +249,7 @@ static void rockchip_drm_unbind(struct device *dev) drm_atomic_helper_shutdown(drm_dev); component_unbind_all(dev, drm_dev); drm_mode_config_cleanup(drm_dev); + rockchip_drm_mode_config_fini(drm_dev); rockchip_iommu_cleanup(drm_dev); drm_dev->dev_private = NULL; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h index ce48568ec8a0..0ac7e31b5605 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h @@ -18,6 +18,7 @@ #define _ROCKCHIP_DRM_DRV_H #include +#include #include #include @@ -57,6 +58,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; + struct drm_private_obj ddrfreq_lock_manager; }; int rockchip_drm_dma_attach_device(struct drm_device *drm_dev, @@ -66,6 +71,7 @@ void rockchip_drm_dma_detach_device(struct drm_device *drm_dev, int rockchip_drm_wait_vact_end(struct drm_crtc *crtc, unsigned int mstimeout); int rockchip_drm_endpoint_is_subdriver(struct device_node *ep); +uint32_t rockchip_drm_get_vblank_ns(struct drm_display_mode *mode); extern struct platform_driver cdn_dp_driver; extern struct platform_driver dw_hdmi_rockchip_pltfm_driver; extern struct platform_driver dw_mipi_dsi_rockchip_driver; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c index 97438bbbe389..613a99c20ed3 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -25,6 +26,56 @@ #include "rockchip_drm_gem.h" #include "rockchip_drm_psr.h" +struct rockchip_ddrfreq_lock_state { + struct drm_private_state base; + int lock_counter; +}; + +#define to_ddrfreq_lock_state(x) container_of(x, \ + struct rockchip_ddrfreq_lock_state, base) + +struct drm_private_state *rockchip_atomic_duplicate_ddrfreq_lock_state( + struct drm_private_obj *obj) +{ + struct rockchip_ddrfreq_lock_state *ddrfreq_lock_state; + + ddrfreq_lock_state = kmemdup(obj->state, sizeof(*ddrfreq_lock_state), + GFP_KERNEL); + if (!ddrfreq_lock_state) + return NULL; + + __drm_atomic_helper_private_obj_duplicate_state(obj, + &ddrfreq_lock_state->base); + + return &ddrfreq_lock_state->base; +} + +void rockchip_atomic_destroy_ddrfreq_lock_state(struct drm_private_obj *obj, + struct drm_private_state *state) +{ + struct rockchip_ddrfreq_lock_state *ddrfreq_lock_state = + to_ddrfreq_lock_state(state); + + kfree(ddrfreq_lock_state); +} + +struct drm_private_state_funcs rockchip_ddrfreq_lock_state_funcs = { + .atomic_duplicate_state = rockchip_atomic_duplicate_ddrfreq_lock_state, + .atomic_destroy_state = rockchip_atomic_destroy_ddrfreq_lock_state, +}; + +static struct rockchip_ddrfreq_lock_state *rockchip_get_ddrfreq_lock_state( + struct drm_atomic_state *state, struct drm_private_obj *obj) +{ + struct drm_private_state *priv_state; + + priv_state = drm_atomic_get_private_obj_state(state, obj); + if (IS_ERR(priv_state)) + return ERR_CAST(priv_state); + + return to_ddrfreq_lock_state(priv_state); +} + static int rockchip_drm_fb_dirty(struct drm_framebuffer *fb, struct drm_file *file, unsigned int flags, unsigned int color, @@ -127,10 +178,80 @@ rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, return ERR_PTR(ret); } +static void +rockchip_drm_psr_inhibit_get_state(struct drm_atomic_state *state) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_encoder *encoder; + u32 encoder_mask = 0; + int i; + + for_each_old_crtc_in_state(state, crtc, crtc_state, i) { + encoder_mask |= crtc_state->encoder_mask; + encoder_mask |= crtc->state->encoder_mask; + } + + drm_for_each_encoder_mask(encoder, state->dev, encoder_mask) + 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) +{ + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + struct drm_encoder *encoder; + u32 encoder_mask = 0; + int i; + + for_each_old_crtc_in_state(state, crtc, crtc_state, i) { + encoder_mask |= crtc_state->encoder_mask; + encoder_mask |= crtc->state->encoder_mask; + } + + drm_for_each_encoder_mask(encoder, state->dev, encoder_mask) + rockchip_drm_psr_inhibit_put(encoder); +} + 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_state *old_crtc_state, *new_crtc_state; + struct rockchip_ddrfreq_lock_state *ddrfreq_lock_state; + struct drm_crtc *crtc; + int i; + + for_each_oldnew_crtc_in_state(old_state, crtc, old_crtc_state, + new_crtc_state, i) { + if (old_crtc_state->enable == new_crtc_state->enable) + continue; + + ddrfreq_lock_state = rockchip_get_ddrfreq_lock_state(old_state, + &priv->ddrfreq_lock_manager); + if (IS_ERR(ddrfreq_lock_state)) + break; + + /* TODO This logic based on the previous state looks weird :/ */ + if (old_crtc_state->enable && + ddrfreq_lock_state->lock_counter == 2) + devfreq_resume_device(priv->devfreq); + else if (!old_crtc_state->enable && + ddrfreq_lock_state->lock_counter == 1) + devfreq_suspend_device(priv->devfreq); + } rockchip_drm_psr_inhibit_get_state(old_state); @@ -154,10 +275,41 @@ static const struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = .atomic_commit_tail = rockchip_atomic_helper_commit_tail_rpm, }; +static int rockchip_drm_atomic_check(struct drm_device *dev, + struct drm_atomic_state *state) +{ + struct rockchip_drm_private *priv = dev->dev_private; + struct rockchip_ddrfreq_lock_state *ddrfreq_lock_state; + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + int ret; + int i; + + ret = drm_atomic_helper_check(dev, state); + if (ret == 0) { + for_each_new_crtc_in_state(state, crtc, crtc_state, i) { + if (!crtc_state->active_changed) + continue; + + ddrfreq_lock_state = rockchip_get_ddrfreq_lock_state( + state, &priv->ddrfreq_lock_manager); + if (IS_ERR(ddrfreq_lock_state)) + return PTR_ERR(ddrfreq_lock_state); + + if (crtc_state->enable) + ddrfreq_lock_state->lock_counter++; + else + ddrfreq_lock_state->lock_counter--; + } + } + + return ret; +} + static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = { .fb_create = rockchip_user_fb_create, .output_poll_changed = drm_fb_helper_output_poll_changed, - .atomic_check = drm_atomic_helper_check, + .atomic_check = rockchip_drm_atomic_check, .atomic_commit = drm_atomic_helper_commit, }; @@ -175,8 +327,11 @@ rockchip_drm_framebuffer_init(struct drm_device *dev, return fb; } -void rockchip_drm_mode_config_init(struct drm_device *dev) +int rockchip_drm_mode_config_init(struct drm_device *dev) { + struct rockchip_drm_private *priv = dev->dev_private; + struct rockchip_ddrfreq_lock_state *ddrfreq_lock_state; + dev->mode_config.min_width = 0; dev->mode_config.min_height = 0; @@ -190,4 +345,22 @@ void rockchip_drm_mode_config_init(struct drm_device *dev) dev->mode_config.funcs = &rockchip_drm_mode_config_funcs; dev->mode_config.helper_private = &rockchip_mode_config_helpers; + + ddrfreq_lock_state = kzalloc(sizeof(*ddrfreq_lock_state), + GFP_KERNEL); + if (!ddrfreq_lock_state) + return -ENOMEM; + + drm_atomic_private_obj_init(&priv->ddrfreq_lock_manager, + &ddrfreq_lock_state->base, + &rockchip_ddrfreq_lock_state_funcs); + + return 0; +} + +void rockchip_drm_mode_config_fini(struct drm_device *dev) +{ + struct rockchip_drm_private *priv = dev->dev_private; + + drm_atomic_private_obj_fini(&priv->ddrfreq_lock_manager); } diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h index f1265cb1aee8..a81df4ef8b35 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fb.h +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fb.h @@ -21,5 +21,6 @@ rockchip_drm_framebuffer_init(struct drm_device *dev, struct drm_gem_object *obj); void rockchip_drm_framebuffer_fini(struct drm_framebuffer *fb); -void rockchip_drm_mode_config_init(struct drm_device *dev); +int rockchip_drm_mode_config_init(struct drm_device *dev); +void rockchip_drm_mode_config_fini(struct drm_device *dev); #endif /* _ROCKCHIP_DRM_FB_H */ diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 0d4ade9d4722..e76a68ae3991 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -36,6 +36,8 @@ #include #include +#include + #include #include @@ -141,6 +143,12 @@ struct vop { struct completion line_flag_completion; + /* devfreq locking */ + spinlock_t devfreq_lock; + bool want_to_change_rate; + struct devfreq_dev_userlock devfreq_userlock; + struct hrtimer vblank_timer; + const struct vop_data *data; uint32_t *regsbak; @@ -632,9 +640,12 @@ static void vop_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { struct vop *vop = to_vop(crtc); + struct rockchip_drm_private *priv = vop->crtc.dev->dev_private; WARN_ON(vop->event); + devfreq_unlock_device(priv->devfreq, &vop->devfreq_userlock, 0); + mutex_lock(&vop->vop_lock); drm_crtc_vblank_off(crtc); @@ -1028,6 +1039,7 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc, { struct vop *vop = to_vop(crtc); const struct vop_data *vop_data = vop->data; + struct rockchip_drm_private *priv = vop->crtc.dev->dev_private; struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc->state); struct drm_display_mode *adjusted_mode = &crtc->state->adjusted_mode; u16 hsync_len = adjusted_mode->hsync_end - adjusted_mode->hsync_start; @@ -1123,6 +1135,11 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc, VOP_REG_SET(vop, common, standby, 0); mutex_unlock(&vop->vop_lock); + + ret = devfreq_lock_device(priv->devfreq, &vop->devfreq_userlock); + if (ret) + DRM_DEV_ERROR(vop->dev, + "cannot lock devfreq - err %d\n", ret); } static bool vop_fs_irq_is_pending(struct vop *vop) @@ -1349,10 +1366,44 @@ static void vop_handle_vblank(struct vop *vop) drm_flip_work_commit(&vop->fb_unref_work, system_unbound_wq); } +static enum hrtimer_restart vblank_timer_function(struct hrtimer *timer) +{ + struct vop *vop = container_of(timer, struct vop, vblank_timer); + struct rockchip_drm_private *priv = vop->crtc.dev->dev_private; + struct devfreq *devfreq = priv->devfreq; + + drm_crtc_vblank_put(&vop->crtc); + + devfreq_lock_device(devfreq, &vop->devfreq_userlock); + + spin_lock(&vop->devfreq_lock); + vop->want_to_change_rate = false; + spin_unlock(&vop->devfreq_lock); + + return HRTIMER_NORESTART; +} + +static void want_to_change_rate(struct devfreq_dev_userlock *userlock, + struct devfreq *devfreq) +{ + struct vop *vop = container_of(userlock, struct vop, devfreq_userlock); + int err; + + spin_lock(&vop->devfreq_lock); + vop->want_to_change_rate = true; + spin_unlock(&vop->devfreq_lock); + + err = drm_crtc_vblank_get(&vop->crtc); + if (err) + DRM_DEV_ERROR(vop->dev, "couldn't get vblank %d\n", err); +} + static irqreturn_t vop_isr(int irq, void *data) { struct vop *vop = data; struct drm_crtc *crtc = &vop->crtc; + struct rockchip_drm_private *priv = crtc->dev->dev_private; + ktime_t vblank_timeout; uint32_t active_irqs; int ret = IRQ_NONE; @@ -1398,6 +1449,19 @@ static irqreturn_t vop_isr(int irq, void *data) } if (active_irqs & FS_INTR) { + spin_lock(&vop->devfreq_lock); + if (vop->want_to_change_rate) { + vop->want_to_change_rate = false; + vblank_timeout = ktime_add_ns(ktime_get(), + rockchip_drm_get_vblank_ns(&crtc->mode)); + hrtimer_start(&vop->vblank_timer, vblank_timeout, + HRTIMER_MODE_ABS); + devfreq_unlock_device(priv->devfreq, + &vop->devfreq_userlock, + vblank_timeout); + } + spin_unlock(&vop->devfreq_lock); + drm_crtc_handle_vblank(crtc); vop_handle_vblank(vop); active_irqs &= ~FS_INTR; @@ -1746,6 +1810,7 @@ static int vop_bind(struct device *dev, struct device *master, void *data) { struct platform_device *pdev = to_platform_device(dev); const struct vop_data *vop_data; + struct rockchip_drm_private *priv; struct drm_device *drm_dev = data; struct vop *vop; struct resource *res; @@ -1815,6 +1880,20 @@ static int vop_bind(struct device *dev, struct device *master, void *data) } } + hrtimer_init(&vop->vblank_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); + vop->vblank_timer.function = vblank_timer_function; + + INIT_LIST_HEAD(&vop->devfreq_userlock.node); + vop->devfreq_userlock.want_to_change_rate = want_to_change_rate; + + priv = vop->crtc.dev->dev_private; + ret = devfreq_register_dev_user_lock(&vop->devfreq_userlock, + priv->devfreq); + if (ret) + DRM_DEV_ERROR(&pdev->dev, + "cannot register to devfreq user lock - err %d\n", + ret); + return 0; err_disable_pm_runtime: @@ -1826,6 +1905,9 @@ static int vop_bind(struct device *dev, struct device *master, void *data) static void vop_unbind(struct device *dev, struct device *master, void *data) { struct vop *vop = dev_get_drvdata(dev); + struct rockchip_drm_private *priv = vop->crtc.dev->dev_private; + + devfreq_unregister_dev_user_lock(&vop->devfreq_userlock, priv->devfreq); if (vop->rgb) rockchip_rgb_fini(vop->rgb); From patchwork Tue Apr 30 18:05:23 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Ga=C3=ABl_PORTAY?= X-Patchwork-Id: 10924681 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D9F8E1515 for ; Wed, 1 May 2019 07:19:30 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id C05F828ABC for ; Wed, 1 May 2019 07:19:30 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id B43E528B3E; Wed, 1 May 2019 07:19:30 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 1727728ABC for ; Wed, 1 May 2019 07:19:30 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id B581D893B8; Wed, 1 May 2019 07:19:13 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [IPv6:2a00:1098:0:82:1000:25:2eeb:e3e3]) by gabe.freedesktop.org (Postfix) with ESMTPS id 42B7C89177 for ; Tue, 30 Apr 2019 18:05:47 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) (Authenticated sender: gportay) with ESMTPSA id A31AB28336F From: =?utf-8?q?Ga=C3=ABl_PORTAY?= To: Rob Herring , Mark Rutland , Heiko Stuebner , Michael Turquette , Stephen Boyd , MyungJoo Ham , Kyungmin Park , Chanwoo Choi , Sandy Huang , David Airlie , Daniel Vetter , =?utf-8?q?Ga=C3=ABl_PORTAY?= , devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-pm@vger.kernel.org, dri-devel@lists.freedesktop.org Subject: [RFC 3/4] clk: rockchip: merge clk-ddr in dmc devfreq driver Date: Tue, 30 Apr 2019 14:05:23 -0400 Message-Id: <20190430180524.22710-4-gael.portay@collabora.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190430180524.22710-1-gael.portay@collabora.com> References: <20190430180524.22710-1-gael.portay@collabora.com> MIME-Version: 1.0 X-Mailman-Approved-At: Wed, 01 May 2019 07:18:43 +0000 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Lin Huang , Derek Basehore , Douglas Anderson , Matthias Kaehlcke , Boris Brezillon , Enric Balletbo i Serra , kernel@collabora.com Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP The Rockchip DMC devfreq driver is the only user of the Rockchip DDR clock. Both drivers perform SMC calls to the Trusted-Firmware A to run SiP services related to the DDR memory. This commit centralizes the SiP services in the DMC devfreq driver and removes the DDR clock which becomes useless. Signed-off-by: Gaël PORTAY --- drivers/clk/rockchip/Makefile | 1 - drivers/clk/rockchip/clk-ddr.c | 147 ------------------------------ drivers/clk/rockchip/clk-rk3399.c | 2 - drivers/clk/rockchip/clk.c | 9 -- drivers/clk/rockchip/clk.h | 33 ------- drivers/devfreq/rk3399_dmc.c | 42 ++++++--- 6 files changed, 28 insertions(+), 206 deletions(-) delete mode 100644 drivers/clk/rockchip/clk-ddr.c diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile index ff35ab463a6f..25014216c6d7 100644 --- a/drivers/clk/rockchip/Makefile +++ b/drivers/clk/rockchip/Makefile @@ -10,7 +10,6 @@ obj-y += clk-half-divider.o obj-y += clk-inverter.o obj-y += clk-mmc-phase.o obj-y += clk-muxgrf.o -obj-y += clk-ddr.o obj-$(CONFIG_RESET_CONTROLLER) += softrst.o obj-y += clk-px30.o diff --git a/drivers/clk/rockchip/clk-ddr.c b/drivers/clk/rockchip/clk-ddr.c deleted file mode 100644 index ebce5260068b..000000000000 --- a/drivers/clk/rockchip/clk-ddr.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (c) 2016 Rockchip Electronics Co. Ltd. - * Author: Lin Huang - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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 -#include -#include -#include -#include -#include -#include "clk.h" - -struct rockchip_ddrclk { - struct clk_hw hw; - void __iomem *reg_base; - int mux_offset; - int mux_shift; - int mux_width; - int div_shift; - int div_width; - int ddr_flag; - spinlock_t *lock; -}; - -#define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw) - -static int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate, - unsigned long prate) -{ - struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); - unsigned long flags; - struct arm_smccc_res res; - - spin_lock_irqsave(ddrclk->lock, flags); - arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, drate, 0, - ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE, - 0, 0, 0, 0, &res); - spin_unlock_irqrestore(ddrclk->lock, flags); - - return res.a0; -} - -static unsigned long -rockchip_ddrclk_sip_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - struct arm_smccc_res res; - - arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0, - ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE, - 0, 0, 0, 0, &res); - - return res.a0; -} - -static long rockchip_ddrclk_sip_round_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long *prate) -{ - struct arm_smccc_res res; - - arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, rate, 0, - ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE, - 0, 0, 0, 0, &res); - - return res.a0; -} - -static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw) -{ - struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); - u32 val; - - val = clk_readl(ddrclk->reg_base + - ddrclk->mux_offset) >> ddrclk->mux_shift; - val &= GENMASK(ddrclk->mux_width - 1, 0); - - return val; -} - -static const struct clk_ops rockchip_ddrclk_sip_ops = { - .recalc_rate = rockchip_ddrclk_sip_recalc_rate, - .set_rate = rockchip_ddrclk_sip_set_rate, - .round_rate = rockchip_ddrclk_sip_round_rate, - .get_parent = rockchip_ddrclk_get_parent, -}; - -struct clk *rockchip_clk_register_ddrclk(const char *name, int flags, - const char *const *parent_names, - u8 num_parents, int mux_offset, - int mux_shift, int mux_width, - int div_shift, int div_width, - int ddr_flag, void __iomem *reg_base, - spinlock_t *lock) -{ - struct rockchip_ddrclk *ddrclk; - struct clk_init_data init; - struct clk *clk; - - ddrclk = kzalloc(sizeof(*ddrclk), GFP_KERNEL); - if (!ddrclk) - return ERR_PTR(-ENOMEM); - - init.name = name; - init.parent_names = parent_names; - init.num_parents = num_parents; - - init.flags = flags; - init.flags |= CLK_SET_RATE_NO_REPARENT; - - switch (ddr_flag) { - case ROCKCHIP_DDRCLK_SIP: - init.ops = &rockchip_ddrclk_sip_ops; - break; - default: - pr_err("%s: unsupported ddrclk type %d\n", __func__, ddr_flag); - kfree(ddrclk); - return ERR_PTR(-EINVAL); - } - - ddrclk->reg_base = reg_base; - ddrclk->lock = lock; - ddrclk->hw.init = &init; - ddrclk->mux_offset = mux_offset; - ddrclk->mux_shift = mux_shift; - ddrclk->mux_width = mux_width; - ddrclk->div_shift = div_shift; - ddrclk->div_width = div_width; - ddrclk->ddr_flag = ddr_flag; - - clk = clk_register(NULL, &ddrclk->hw); - if (IS_ERR(clk)) - kfree(ddrclk); - - return clk; -} diff --git a/drivers/clk/rockchip/clk-rk3399.c b/drivers/clk/rockchip/clk-rk3399.c index 5a628148f3f0..9f01463ad62e 100644 --- a/drivers/clk/rockchip/clk-rk3399.c +++ b/drivers/clk/rockchip/clk-rk3399.c @@ -1396,8 +1396,6 @@ static struct rockchip_clk_branch rk3399_clk_branches[] __initdata = { 2, GFLAGS), GATE(0, "clk_ddrc_gpll_src", "gpll", 0, RK3399_CLKGATE_CON(3), 3, GFLAGS), - COMPOSITE_DDRCLK(SCLK_DDRC, "sclk_ddrc", mux_ddrclk_p, 0, - RK3399_CLKSEL_CON(6), 4, 2, 0, 0, ROCKCHIP_DDRCLK_SIP), }; static struct rockchip_clk_branch rk3399_clk_pmu_branches[] __initdata = { diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c index c3ad92965823..971fdda693f7 100644 --- a/drivers/clk/rockchip/clk.c +++ b/drivers/clk/rockchip/clk.c @@ -544,15 +544,6 @@ void __init rockchip_clk_register_branches( list->gate_offset, list->gate_shift, list->gate_flags, flags, &ctx->lock); break; - case branch_ddrclk: - clk = rockchip_clk_register_ddrclk( - list->name, list->flags, - list->parent_names, list->num_parents, - list->muxdiv_offset, list->mux_shift, - list->mux_width, list->div_shift, - list->div_width, list->div_flags, - ctx->reg_base, &ctx->lock); - break; } /* none of the cases above matched */ diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h index 6b53fff4cc96..0a486fdffa6a 100644 --- a/drivers/clk/rockchip/clk.h +++ b/drivers/clk/rockchip/clk.h @@ -354,20 +354,6 @@ struct clk *rockchip_clk_register_mmc(const char *name, const char *const *parent_names, u8 num_parents, void __iomem *reg, int shift); -/* - * DDRCLK flags, including method of setting the rate - * ROCKCHIP_DDRCLK_SIP: use SIP call to bl31 to change ddrclk rate. - */ -#define ROCKCHIP_DDRCLK_SIP BIT(0) - -struct clk *rockchip_clk_register_ddrclk(const char *name, int flags, - const char *const *parent_names, - u8 num_parents, int mux_offset, - int mux_shift, int mux_width, - int div_shift, int div_width, - int ddr_flags, void __iomem *reg_base, - spinlock_t *lock); - #define ROCKCHIP_INVERTER_HIWORD_MASK BIT(0) struct clk *rockchip_clk_register_inverter(const char *name, @@ -392,7 +378,6 @@ enum rockchip_clk_branch_type { branch_mmc, branch_inverter, branch_factor, - branch_ddrclk, branch_half_divider, }; @@ -583,24 +568,6 @@ struct rockchip_clk_branch { .child = ch, \ } -#define COMPOSITE_DDRCLK(_id, cname, pnames, f, mo, ms, mw, \ - ds, dw, df) \ - { \ - .id = _id, \ - .branch_type = branch_ddrclk, \ - .name = cname, \ - .parent_names = pnames, \ - .num_parents = ARRAY_SIZE(pnames), \ - .flags = f, \ - .muxdiv_offset = mo, \ - .mux_shift = ms, \ - .mux_width = mw, \ - .div_shift = ds, \ - .div_width = dw, \ - .div_flags = df, \ - .gate_offset = -1, \ - } - #define MUX(_id, cname, pnames, f, o, s, w, mf) \ { \ .id = _id, \ diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c index daf19e121c99..7f9c8c0cf45d 100644 --- a/drivers/devfreq/rk3399_dmc.c +++ b/drivers/devfreq/rk3399_dmc.c @@ -13,7 +13,6 @@ */ #include -#include #include #include #include @@ -67,7 +66,6 @@ struct rk3399_dmcfreq { struct device *dev; struct devfreq *devfreq; struct devfreq_simple_ondemand_data ondemand_data; - struct clk *dmc_clk; struct devfreq_event_dev *edev; struct mutex lock; struct dram_timing timing; @@ -79,6 +77,31 @@ struct rk3399_dmcfreq { int odt_pd_arg0, odt_pd_arg1; }; +static int rk3399_ddrclk_set_rate(unsigned long rate) +{ + struct arm_smccc_res res; + + arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, rate, 0, + ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE, + 0, 0, 0, 0, &res); + + if (rate != res.a0 * 1000000L) + return -1; /* TODO which errno?*/ + + return 0; +} + +static unsigned long rk3399_ddrclk_get_rate(void) +{ + struct arm_smccc_res res; + + arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0, + ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE, + 0, 0, 0, 0, &res); + + return res.a0; +} + static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq, u32 flags) { @@ -130,7 +153,7 @@ static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq, } } - err = clk_set_rate(dmcfreq->dmc_clk, target_rate); + err = rk3399_ddrclk_set_rate(target_rate); if (err) { dev_err(dev, "Cannot set frequency %lu (%d)\n", target_rate, err); @@ -145,7 +168,7 @@ static int rk3399_dmcfreq_target(struct device *dev, unsigned long *freq, * 1. Ddr frequency scaling fail, we still get the old rate. * 2. Ddr frequency scaling sucessful, we get the rate we set. */ - dmcfreq->rate = clk_get_rate(dmcfreq->dmc_clk); + dmcfreq->rate = rk3399_ddrclk_get_rate(); /* If get the incorrect rate, set voltage to old value. */ if (dmcfreq->rate != target_rate) { @@ -338,15 +361,6 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) return PTR_ERR(data->vdd_center); } - data->dmc_clk = devm_clk_get(dev, "dmc_clk"); - if (IS_ERR(data->dmc_clk)) { - if (PTR_ERR(data->dmc_clk) == -EPROBE_DEFER) - return -EPROBE_DEFER; - - dev_err(dev, "Cannot get the clk dmc_clk\n"); - return PTR_ERR(data->dmc_clk); - }; - data->edev = devfreq_event_get_edev_by_phandle(dev, 0); if (IS_ERR(data->edev)) return -EPROBE_DEFER; @@ -441,7 +455,7 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) of_property_read_u32(np, "downdifferential", &data->ondemand_data.downdifferential); - data->rate = clk_get_rate(data->dmc_clk); + data->rate = rk3399_ddrclk_get_rate(); opp = devfreq_recommended_opp(dev, &data->rate, 0); if (IS_ERR(opp)) { From patchwork Tue Apr 30 18:05:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Ga=C3=ABl_PORTAY?= X-Patchwork-Id: 10924665 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A05B81515 for ; Wed, 1 May 2019 07:19:13 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 87C6F28ABC for ; Wed, 1 May 2019 07:19:13 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 7A22028B3E; Wed, 1 May 2019 07:19:13 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00,MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED,UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 0A64A28ABC for ; Wed, 1 May 2019 07:19:13 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id E74A68936E; Wed, 1 May 2019 07:19:08 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [IPv6:2a00:1098:0:82:1000:25:2eeb:e3e3]) by gabe.freedesktop.org (Postfix) with ESMTPS id 01D0189105 for ; Tue, 30 Apr 2019 18:05:50 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) (Authenticated sender: gportay) with ESMTPSA id 67D3F282FE4 From: =?utf-8?q?Ga=C3=ABl_PORTAY?= To: Rob Herring , Mark Rutland , Heiko Stuebner , Michael Turquette , Stephen Boyd , MyungJoo Ham , Kyungmin Park , Chanwoo Choi , Sandy Huang , David Airlie , Daniel Vetter , =?utf-8?q?Ga=C3=ABl_PORTAY?= , devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, linux-clk@vger.kernel.org, linux-pm@vger.kernel.org, dri-devel@lists.freedesktop.org Subject: [RFC 4/4] arm64: dts: rockchip: Set the display-subsystem devfreq Date: Tue, 30 Apr 2019 14:05:24 -0400 Message-Id: <20190430180524.22710-5-gael.portay@collabora.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190430180524.22710-1-gael.portay@collabora.com> References: <20190430180524.22710-1-gael.portay@collabora.com> MIME-Version: 1.0 X-Mailman-Approved-At: Wed, 01 May 2019 07:18:43 +0000 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Lin Huang , Derek Basehore , Douglas Anderson , Matthias Kaehlcke , Boris Brezillon , Enric Balletbo i Serra , kernel@collabora.com Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP Use the Dynamic Memory Controller as the display-subsystem's devfreq device. Signed-off-by: Gaël PORTAY --- arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi | 4 ++++ arch/arm64/boot/dts/rockchip/rk3399.dtsi | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi index 40e78186560b..a5cbbc08f7e1 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399-gru.dtsi @@ -375,6 +375,10 @@ <200000000>; }; +&display_subsystem { + devfreq = <&dmc>; +}; + &emmc_phy { status = "okay"; }; diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi index 87ee084fac89..253b476163fd 100644 --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi @@ -155,7 +155,7 @@ }; }; - display-subsystem { + display_subsystem: display-subsystem { compatible = "rockchip,display-subsystem"; ports = <&vopl_out>, <&vopb_out>; };