高通平台GPU动态调频DCVS . 篇2 . Framework & Procedure


系列文章
高通平台GPU动态调频DCVS . 篇1 . Interface
高通平台GPU动态调频DCVS . 篇2 . Framework & Procedure
高通平台GPU动态调频DCVS . 篇3 . 一个无法调频问题的解决过程


1. 高通平台 GPU DCVS架构

GPU DCVS是基于Linux Devfreq来实现

  • 高通的kgsl(kernel-graphics-support-layer)作为devfreq device
  • msm-adreno-tz 作为devfreq governor

简单的架构如下
在这里插入图片描述

如上图所示

  • 首先 KGSL 作为device,msm-adreno-tz 作为governor注册到devfreq core
  • KGSL driver通过notify机制去调用 governor msm-adreno-tz 的tz_notify() 函数
  • tz_notify() 调用 devfreq core的 update_devfreq()
  • update_devfreq() 调用governor的 get_target_freq()来计算处理目标频率
  • update_devfreq() 调用KGSL的target回调来传递目标频率,并实施调频

2. Adreno 部分

Anreno作为平台相关的GPU驱动,提供了读取和操作GPU硬件的interface

2.1 频率解析
  • 频率表
    频率表位于dts arch/arm64/boot/dts/qcom/sm8150-v2.dtsi
    qcom,gpu-pwrlevels {
        #address-cells = <1>;
        #size-cells = <0>;

        compatible = "qcom,gpu-pwrlevels";

        qcom,gpu-pwrlevel@0 {
            reg = <0>;
            qcom,gpu-freq = <810000000>;
            qcom,bus-freq = <11>;
            qcom,bus-min = <11>;
            qcom,bus-max = <11>;
        };

        qcom,gpu-pwrlevel@1 {
            reg = <1>;
            qcom,gpu-freq = <585000000>;
            qcom,bus-freq = <11>;
            qcom,bus-min = <11>;
            qcom,bus-max = <11>;
        };

        qcom,gpu-pwrlevel@2 {
            reg = <2>;
            qcom,gpu-freq = <499200000>;
            qcom,bus-freq = <9>;
            qcom,bus-min = <8>;
            qcom,bus-max = <11>;
        };

        qcom,gpu-pwrlevel@3 {
            reg = <3>;
            qcom,gpu-freq = <427000000>;
            qcom,bus-freq = <6>;
            qcom,bus-min = <5>;
            qcom,bus-max = <7>;
        };

        qcom,gpu-pwrlevel@4 {
            reg = <4>;
            qcom,gpu-freq = <345000000>;
            qcom,bus-freq = <3>;
            qcom,bus-min = <3>;
            qcom,bus-max = <5>;
        };

        qcom,gpu-pwrlevel@5 {
            reg = <5>;
            qcom,gpu-freq = <257000000>;
            qcom,bus-freq = <3>;
            qcom,bus-min = <2>;
            qcom,bus-max = <4>;
        };

        qcom,gpu-pwrlevel@6 {
            reg = <6>;
            qcom,gpu-freq = <0>;
            qcom,bus-freq = <0>;
            qcom,bus-min = <0>;
            qcom,bus-max = <0>;
        };
        /delete-node/ qcom,gpu-pwrlevel@7;
    };
  • 驱动解析
    drivers/gpu/msm/adreno.c
    adreno 驱动对应的设备名为qcom,kgsl-3d0
static const struct of_device_id adreno_match_table[] = {
    { .compatible = "qcom,kgsl-3d0", .data = &device_3d0 },
    {}
};
adreno_probe()
    -> adreno_of_get_power()
        -> adreno_of_get_pwrlevels()
            -> adreno_of_parse_pwrlevels()

结果保存在 adreno_device->kgsl_device->kgsl_pwrctrl结构体的kgsl_pwrlevel结构体类型的的数组中,pwrlevel作为这个数组的index

/**
 * struct kgsl_pwrlevel - Struct holding different pwrlevel info obtained from
 * from dtsi file
 * @gpu_freq:          GPU frequency vote in Hz
 * @bus_freq:          Bus bandwidth vote index
 * @bus_min:           Min bus index @gpu_freq
 * @bus_max:           Max bus index @gpu_freq
 */
struct kgsl_pwrlevel {
    unsigned int gpu_freq;
    unsigned int bus_freq;
    unsigned int bus_min;
    unsigned int bus_max;
};

3. kgsl 部分

kgsl是抽象层,代码位于drivers/gpu/msm

3.1 注册devfreq device

kgsl是作为Devfreq的device存在, 在kgsl_pwrscale_init() 函数,就会去注册kgsl device到devfreq framework

    devfreq = devfreq_add_device(dev, &pwrscale->gpu_profile.profile,
            governor, pwrscale->gpu_profile.private_data);
3.2 Notify 定义和注册、通知方法

在DCVS架构中,GPU调频实际上是由adreno-kgsl发起,进而通知msm-adreno-tz governor

  • 定义notify head
    kgsl_pwrscale_init()
srcu_init_notifier_head(&pwrscale->nh);
  • 提供notify register方法
    提供kgsl_devfreq_add_notifier()方法,供governor使用
/*
 * kgsl_devfreq_add_notifier - add a fine grained notifier.
 * @dev: The device
 * @nb: Notifier block that will recieve updates.
 *
 * Add a notifier to recieve ADRENO_DEVFREQ_NOTIFY_* events
 * from the device.
 */
int kgsl_devfreq_add_notifier(struct device *dev,
        struct notifier_block *nb)
{
    struct kgsl_device *device = dev_get_drvdata(dev);

    if (device == NULL)
        return -ENODEV;

    if (nb == NULL)
        return -EINVAL;

    return srcu_notifier_chain_register(&device->pwrscale.nh, nb);
}
EXPORT_SYMBOL(kgsl_devfreq_add_notifier);
  • 提供notify call chain 方法
    触发governor notify 处理函数tz_notify()工作
static void do_devfreq_notify(struct work_struct *work)
{
    struct kgsl_pwrscale *pwrscale = container_of(work,
            struct kgsl_pwrscale, devfreq_notify_ws);
    struct devfreq *devfreq = pwrscale->devfreqptr;
    srcu_notifier_call_chain(&pwrscale->nh,
                 ADRENO_DEVFREQ_NOTIFY_RETIRE,
                 devfreq);
}
3.3 提供调频回调方法

Devfreq 框架需要device端提供target() 回调, 获取并处理来自devfreq的目标频率

#define KGSL_PWRSCALE_INIT(_priv_data) { \
    .enabled = true, \
    .gpu_profile = { \
        .private_data = _priv_data, \
        .profile = { \
            .target = kgsl_devfreq_target, \
            .get_dev_status = kgsl_devfreq_get_dev_status, \
            .get_cur_freq = kgsl_devfreq_get_cur_freq, \
    } }, \
/*
 * kgsl_devfreq_target - devfreq_dev_profile.target callback
 * @dev: see devfreq.h
 * @freq: see devfreq.h
 * @flags: see devfreq.h
 *
 * This function expects the device mutex to be unlocked.
 */
int kgsl_devfreq_target(struct device *dev, unsigned long *freq, u32 flags)
{
    struct kgsl_device *device = dev_get_drvdata(dev);
    struct kgsl_pwrctrl *pwr;
    struct kgsl_pwrlevel *pwr_level;
    int level;
    unsigned int i;
    unsigned long cur_freq;

    if (device == NULL)
        return -ENODEV;
    if (freq == NULL)
        return -EINVAL;
    if (!device->pwrscale.enabled)
        return 0;

    pwr = &device->pwrctrl;
    if (_check_maxfreq(flags)) {
        /*
         * The GPU is about to get suspended,
         * but it needs to be at the max power level when waking up
        */
        pwr->wakeup_maxpwrlevel = 1;
        return 0;
    }

    mutex_lock(&device->mutex);
    cur_freq = kgsl_pwrctrl_active_freq(pwr);
    level = pwr->active_pwrlevel;
    pwr_level = &pwr->pwrlevels[level];

    /* If the governor recommends a new frequency, update it here */
    if (*freq != cur_freq) {
        level = pwr->max_pwrlevel;
        /*
         * Array index of pwrlevels[] should be within the permitted
         * power levels, i.e., from max_pwrlevel to min_pwrlevel.
         */
        for (i = pwr->min_pwrlevel; (i >= pwr->max_pwrlevel
                    && i <= pwr->min_pwrlevel); i--)
            if (*freq <= pwr->pwrlevels[i].gpu_freq) {
                if (pwr->thermal_cycle == CYCLE_ACTIVE)
                    level = _thermal_adjust(pwr, i);
                else
                    level = popp_trans2(device, i);
                break;
            }
        if (level != pwr->active_pwrlevel)
            kgsl_pwrctrl_pwrlevel_change(device, level);
    } else if (popp_stable(device)) {
        popp_trans1(device);
    }

    *freq = kgsl_pwrctrl_active_freq(pwr);

    mutex_unlock(&device->mutex);
    return 0;
}
EXPORT_SYMBOL(kgsl_devfreq_target);

4. msm-adreno-tz 部分

代码位于drivers/devfreq/governor_msm_adreno_tz.c

4.1 注册devfreq governor
static int __init msm_adreno_tz_init(void)
{
    workqueue = create_freezable_workqueue("governor_msm_adreno_tz_wq");
    if (workqueue == NULL)
        return -ENOMEM;

    return devfreq_add_governor(&msm_adreno_tz);
}
4.2 实现notify callback function

实现notify callback function tz_notify()

static int tz_notify(struct notifier_block *nb, unsigned long type, void *devp)
{
    int result = 0;
    struct devfreq *devfreq = devp;

    switch (type) {
    case ADRENO_DEVFREQ_NOTIFY_IDLE:
    case ADRENO_DEVFREQ_NOTIFY_RETIRE:
        mutex_lock(&devfreq->lock);
        result = update_devfreq(devfreq);
        mutex_unlock(&devfreq->lock);
        /* Nofifying partner bus governor if any */
        if (partner_gpu_profile && partner_gpu_profile->bus_devfreq) {
            mutex_lock(&partner_gpu_profile->bus_devfreq->lock);
            update_devfreq(partner_gpu_profile->bus_devfreq);
            mutex_unlock(&partner_gpu_profile->bus_devfreq->lock);
        }
        break;
    /* ignored by this governor */
    case ADRENO_DEVFREQ_NOTIFY_SUBMIT:
    default:
        break;
    }
    return notifier_from_errno(result);
}

tz_notify() 会调用到 update_devfreq()

update_devfreq() 是devfreq core提供通用调频方法, governor通过update_devfreq() 来实现对频率的处理运算以及调用device的target() 回调传递频率到device端.

  • 频率处理运算
    /* Reevaluate the proper frequency */
    err = devfreq->governor->get_target_freq(devfreq, &freq, &flags);
  • 通过target回调传递目标频率到device
    err = devfreq->profile->target(devfreq->dev.parent, &freq, flags);

5. DEVFREQ Framework

Devfreq是该机制的核心框架,所以需要了解,可以参考下文
Linux DEVFREQ - 通用DVFS Framework

6. 流程简述

调频实际上是由Adreno发起,governor是为了满足devfreq标准框架而存在,流程图如下,从上到下的执行顺序
核心的DCVS决策算法位于TrustZone
在这里插入图片描述

adreno_dispatcher_work
    ->kgsl_pwrscale_update
        ->queue_work(devfreq_notify_ws)
            ->do_devfreq_notify(ADRENO_DEVFREQ_NOTIFY_RETIRE)
                ->tz_notify()
                    ->update_devfreq(devfreq)
                        ->profile->get_dev_status()
                            ->profile.target()

最后: 代码来源

Linux 内核部分分析用的代码来源于内核开源代码,这里是以最新的SM8150作为例子
手机厂商内核开源代码

  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 1. 基于AT89C2051单片机的压控晶振控制电路 2. 基于AD9850数字信号发生器的压控晶振控制电路 3. 基于CD4046锁相环芯片的压控晶振控制电路 4. 基于MAX038函数发生器的压控晶振控制电路 5. 基于XR2206函数发生器的压控晶振控制电路 ### 回答2: 常见的压控晶振控制电路主要用于控制晶振的频率,以满足不同的应用需求。以下是一些常见的压控晶振控制电路示例。 1. 直接电压控制电路:这种控制电路使用一个电压源来控制晶振的频率。通过改变电压值,可以改变晶振的频率。这种电路通常简单且成本较低,适用于一些不需要频率变化太大的应用。 2. 锁相环(PLL)控制电路:PLL控制电路使用一个锁相环来实现晶振频率的控制。它通过比较晶振输出信号和一个参考信号,调整晶振的控制电压,从而使晶振的频率与参考信号频率保持同步。这种电路适用于需要高精度和稳定频率的应用,如通信设备、数字处理器等。 3. 数字控制电压控制电路(DCVS):这种电路使用数字信号来控制晶振的频率。通过改变数字控制信号的数值,可以改变晶振的电压控制值,从而改变频率。DCVS电路灵活性较高,适用于需要频率变化范围较大以及动态频率调整的应用,如无线通信设备、射频信号处理器等。 4. 频率合成器:频率合成器控制电路是一种将多个晶振频率合成产生所需频率的电路。它通过组合多个定频的晶振信号,按照一定规律进行相位和频率调整,最终产生所需频率信号。频率合成器适用于需要多种频率输出的复杂电路,如广播设备、雷达系统等。 这些是常见的压控晶振控制电路举例,不同的应用场景需要选择合适的控制电路来满足需求。 ### 回答3: 常见的压控晶振控制电路通常用于调整晶体振荡器(VCO)的频率。其中一个常见的压控晶振控制电路是基于相位锁环(PLL)的设计。 PLL压控晶振控制电路包括一个相位比较器、一个低通滤波器、一个数字控制电压(DCO)以及一个反馈电路。相位比较器用于比较输入的参考信号和输出信号的相位差,并将其转化为电压信号。低通滤波器用于平滑相位比较器输出的电压信号,以获得直流参考电压。DCO是一个能够根据输入的控制电压来调整频率的电路。通过调整DCO的控制电压,可以实现对压控晶振频率的调节。反馈电路将DCO的输出信号送回到相位比较器进行比较,以实现闭环控制。 另外一个常见的压控晶振控制电路是基于电容的设计。这种电路中,晶体振荡器的频率由电容的值来调节。通过改变电容的值,可以调整晶体振荡器的频率。这种压控晶振控制电路通常由一个电容阵列和一个模拟电压调节电路组成。模拟电压调节电路根据输入的电压信号,控制电容阵列中某个或多个电容的接通或断开,从而改变电容的总值,进而调整压控晶振的频率。 这些常见的压控晶振控制电路具有调节频率的能力,并广泛应用于通信设备、雷达系统、仪器仪表等电子设备中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值