Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 12fb4b14 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: kgsl: Update thermal level via OPP notifier"

parents 157ab4a1 c43f30a8
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -172,7 +172,8 @@ static unsigned int _adjust_pwrlevel(struct kgsl_pwrctrl *pwr, int level,
{
	unsigned int max_pwrlevel = max_t(unsigned int, pwr->thermal_pwrlevel,
					pwr->max_pwrlevel);
	unsigned int min_pwrlevel = max_t(unsigned int, pwr->thermal_pwrlevel,
	unsigned int min_pwrlevel = min_t(unsigned int,
					pwr->thermal_pwrlevel_floor,
					pwr->min_pwrlevel);

	switch (pwrc->type) {
@@ -2263,6 +2264,7 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
	pwr->max_pwrlevel = 0;
	pwr->min_pwrlevel = pwr->num_pwrlevels - 2;
	pwr->thermal_pwrlevel = 0;
	pwr->thermal_pwrlevel_floor = pwr->min_pwrlevel;

	pwr->wakeup_maxpwrlevel = 0;

+5 −1
Original line number Diff line number Diff line
/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2010-2018, 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
@@ -125,9 +125,11 @@ struct kgsl_regulator {
 * @grp_clks - Array of clocks structures that we control
 * @power_flags - Control flags for power
 * @pwrlevels - List of supported power levels
 * @nb - Notifier block to receive GPU OPP change event
 * @active_pwrlevel - The currently active power level
 * @previous_pwrlevel - The power level before transition
 * @thermal_pwrlevel - maximum powerlevel constraint from thermal
 * @thermal_pwrlevel_floor - minimum powerlevel constraint from thermal
 * @default_pwrlevel - device wake up power level
 * @max_pwrlevel - maximum allowable powerlevel per the user
 * @min_pwrlevel - minimum allowable powerlevel per the user
@@ -179,9 +181,11 @@ struct kgsl_pwrctrl {
	unsigned long power_flags;
	unsigned long ctrl_flags;
	struct kgsl_pwrlevel pwrlevels[KGSL_MAX_PWRLEVELS];
	struct notifier_block nb;
	unsigned int active_pwrlevel;
	unsigned int previous_pwrlevel;
	unsigned int thermal_pwrlevel;
	unsigned int thermal_pwrlevel_floor;
	unsigned int default_pwrlevel;
	unsigned int wakeup_maxpwrlevel;
	unsigned int max_pwrlevel;
+128 −15
Original line number Diff line number Diff line
/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
/* Copyright (c) 2010-2018, 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,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/devfreq_cooling.h>
#include <linux/pm_opp.h>

#include "kgsl.h"
#include "kgsl_pwrscale.h"
@@ -533,7 +534,6 @@ int kgsl_devfreq_target(struct device *dev, unsigned long *freq, u32 flags)
	int level;
	unsigned int i;
	unsigned long cur_freq, rec_freq;
	struct dev_pm_opp *opp;

	if (device == NULL)
		return -ENODEV;
@@ -552,20 +552,7 @@ int kgsl_devfreq_target(struct device *dev, unsigned long *freq, u32 flags)
		return 0;
	}

	/*
	 * Thermal framework might have disabled/enabled OPP entries
	 * for mitigation. So find the recommended frequency matching
	 * the available opp entries
	 */
	rcu_read_lock();
	rec_freq = *freq;
	opp = devfreq_recommended_opp(dev, &rec_freq, flags);
	if (IS_ERR(opp)) {
		rcu_read_unlock();
		return PTR_ERR(opp);
	}
	rec_freq = dev_pm_opp_get_freq(opp);
	rcu_read_unlock();

	mutex_lock(&device->mutex);
	cur_freq = kgsl_pwrctrl_active_freq(pwr);
@@ -870,6 +857,125 @@ int kgsl_busmon_get_cur_freq(struct device *dev, unsigned long *freq)
	return 0;
}

/*
 * opp_notify - Callback function registered to receive OPP events.
 * @nb: The notifier block
 * @type: The event type. Two OPP events are expected in this function:
 *      - OPP_EVENT_ENABLE: an GPU OPP is enabled. The in_opp parameter
 *	contains the OPP that is enabled
 *	- OPP_EVENT_DISALBE: an GPU OPP is disabled. The in_opp parameter
 *	contains the OPP that is disabled.
 * @in_opp: the GPU OPP whose status is changed and triggered the event
 *
 * GPU OPP event callback function. The function subscribe GPU OPP status
 * change and update thermal power level accordingly.
 */

static int opp_notify(struct notifier_block *nb,
	unsigned long type, void *in_opp)
{
	int result = -EINVAL, level, min_level, max_level;
	struct kgsl_pwrctrl *pwr = container_of(nb, struct kgsl_pwrctrl, nb);
	struct kgsl_device *device = container_of(pwr,
			struct kgsl_device, pwrctrl);
	struct device *dev = &device->pdev->dev;
	struct dev_pm_opp *opp;
	unsigned long min_freq = 0, max_freq = pwr->pwrlevels[0].gpu_freq;

	if (type != OPP_EVENT_ENABLE && type != OPP_EVENT_DISABLE)
		return result;

	rcu_read_lock();
	opp = dev_pm_opp_find_freq_floor(dev, &max_freq);
	if (IS_ERR(opp)) {
		rcu_read_unlock();
		return PTR_ERR(opp);
	}

	max_freq = dev_pm_opp_get_freq(opp);
	if (!max_freq) {
		rcu_read_unlock();
		return result;
	}

	opp = dev_pm_opp_find_freq_ceil(dev, &min_freq);
	if (IS_ERR(opp))
		min_freq = pwr->pwrlevels[pwr->min_pwrlevel].gpu_freq;

	rcu_read_unlock();

	mutex_lock(&device->mutex);

	max_level = pwr->thermal_pwrlevel;
	min_level = pwr->thermal_pwrlevel_floor;

	/* Thermal limit cannot be lower than lowest non-zero operating freq */
	for (level = 0; level < (pwr->num_pwrlevels - 1); level++)
		if (pwr->pwrlevels[level].gpu_freq == max_freq)
			max_level = level;
		if (pwr->pwrlevels[level].gpu_freq == min_freq)
			min_level = level;


	pwr->thermal_pwrlevel = max_level;
	pwr->thermal_pwrlevel_floor = min_level;

	/* Update the current level using the new limit */
	kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel);
	mutex_unlock(&device->mutex);

	return 0;
}

/*
 * kgsl_opp_add_notifier - add a fine grained notifier.
 * @dev: The device
 * @nb: Notifier block that will receive updates.
 *
 * Add a notifier to receive GPU OPP_EVENT_* events
 * from the OPP framework.
 */
static int kgsl_opp_add_notifier(struct device *dev,
		struct notifier_block *nb)
{
	struct srcu_notifier_head *nh;
	int ret = 0;

	rcu_read_lock();
	nh = dev_pm_opp_get_notifier(dev);
	if (IS_ERR(nh))
		ret = PTR_ERR(nh);
	rcu_read_unlock();
	if (!ret)
		ret = srcu_notifier_chain_register(nh, nb);

	return ret;
}

/*
 * kgsl_opp_remove_notifier - remove registered opp event notifier.
 * @dev: The device
 * @nb: Notifier block that will receive updates.
 *
 * Remove gpu notifier that receives GPU OPP_EVENT_* events
 * from the OPP framework.
 */
static int kgsl_opp_remove_notifier(struct device *dev,
		struct notifier_block *nb)
{
	struct srcu_notifier_head *nh;
	int ret = 0;

	rcu_read_lock();
	nh = dev_pm_opp_get_notifier(dev);
	if (IS_ERR(nh))
		ret = PTR_ERR(nh);
	rcu_read_unlock();
	if (!ret)
		ret = srcu_notifier_chain_unregister(nh, nb);

	return ret;
}

/*
 * kgsl_pwrscale_init - Initialize pwrscale.
@@ -900,6 +1006,10 @@ int kgsl_pwrscale_init(struct device *dev, const char *governor)
	gpu_profile = &pwrscale->gpu_profile;
	profile = &pwrscale->gpu_profile.profile;

	pwr->nb.notifier_call = opp_notify;

	kgsl_opp_add_notifier(dev, &pwr->nb);

	srcu_init_notifier_head(&pwrscale->nh);

	profile->initial_freq =
@@ -1053,7 +1163,9 @@ void kgsl_pwrscale_close(struct kgsl_device *device)
{
	int i;
	struct kgsl_pwrscale *pwrscale;
	struct kgsl_pwrctrl *pwr;

	pwr = &device->pwrctrl;
	pwrscale = &device->pwrscale;
	if (!pwrscale->devfreqptr)
		return;
@@ -1068,6 +1180,7 @@ void kgsl_pwrscale_close(struct kgsl_device *device)
	kgsl_midframe = NULL;
	device->pwrscale.devfreqptr = NULL;
	srcu_cleanup_notifier_head(&device->pwrscale.nh);
	kgsl_opp_remove_notifier(&device->pdev->dev, &pwr->nb);
	for (i = 0; i < KGSL_PWREVENT_MAX; i++)
		kfree(pwrscale->history[i].events);
}