···11+/*22+ * Copyright 2010 Red Hat Inc.33+ *44+ * Permission is hereby granted, free of charge, to any person obtaining a55+ * copy of this software and associated documentation files (the "Software"),66+ * to deal in the Software without restriction, including without limitation77+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,88+ * and/or sell copies of the Software, and to permit persons to whom the99+ * Software is furnished to do so, subject to the following conditions:1010+ *1111+ * The above copyright notice and this permission notice shall be included in1212+ * all copies or substantial portions of the Software.1313+ *1414+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR1515+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,1616+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL1717+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR1818+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,1919+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR2020+ * OTHER DEALINGS IN THE SOFTWARE.2121+ *2222+ * Authors: Ben Skeggs2323+ */2424+2525+#ifndef __NOUVEAU_PM_H__2626+#define __NOUVEAU_PM_H__2727+2828+struct nouveau_hwmon {2929+ struct drm_device *dev;3030+ struct device *hwmon;3131+};3232+3333+static inline struct nouveau_hwmon *3434+nouveau_hwmon(struct drm_device *dev)3535+{3636+ return nouveau_drm(dev)->hwmon;3737+}3838+3939+/* nouveau_hwmon.c */4040+int nouveau_hwmon_init(struct drm_device *dev);4141+void nouveau_hwmon_fini(struct drm_device *dev);4242+4343+#endif
-115
drivers/gpu/drm/nouveau/nouveau_hwsq.h
···11-/*22- * Copyright 2010 Red Hat Inc.33- *44- * Permission is hereby granted, free of charge, to any person obtaining a55- * copy of this software and associated documentation files (the "Software"),66- * to deal in the Software without restriction, including without limitation77- * the rights to use, copy, modify, merge, publish, distribute, sublicense,88- * and/or sell copies of the Software, and to permit persons to whom the99- * Software is furnished to do so, subject to the following conditions:1010- *1111- * The above copyright notice and this permission notice shall be included in1212- * all copies or substantial portions of the Software.1313- *1414- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR1515- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,1616- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL1717- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR1818- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,1919- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR2020- * OTHER DEALINGS IN THE SOFTWARE.2121- *2222- * Authors: Ben Skeggs2323- */2424-2525-#ifndef __NOUVEAU_HWSQ_H__2626-#define __NOUVEAU_HWSQ_H__2727-2828-struct hwsq_ucode {2929- u8 data[0x200];3030- union {3131- u8 *u08;3232- u16 *u16;3333- u32 *u32;3434- } ptr;3535- u16 len;3636-3737- u32 reg;3838- u32 val;3939-};4040-4141-static inline void4242-hwsq_init(struct hwsq_ucode *hwsq)4343-{4444- hwsq->ptr.u08 = hwsq->data;4545- hwsq->reg = 0xffffffff;4646- hwsq->val = 0xffffffff;4747-}4848-4949-static inline void5050-hwsq_fini(struct hwsq_ucode *hwsq)5151-{5252- do {5353- *hwsq->ptr.u08++ = 0x7f;5454- hwsq->len = hwsq->ptr.u08 - hwsq->data;5555- } while (hwsq->len & 3);5656- hwsq->ptr.u08 = hwsq->data;5757-}5858-5959-static inline void6060-hwsq_usec(struct hwsq_ucode *hwsq, u8 usec)6161-{6262- u32 shift = 0;6363- while (usec & ~3) {6464- usec >>= 2;6565- shift++;6666- }6767-6868- *hwsq->ptr.u08++ = (shift << 2) | usec;6969-}7070-7171-static inline void7272-hwsq_setf(struct hwsq_ucode *hwsq, u8 flag, int val)7373-{7474- flag += 0x80;7575- if (val >= 0)7676- flag += 0x20;7777- if (val >= 1)7878- flag += 0x20;7979- *hwsq->ptr.u08++ = flag;8080-}8181-8282-static inline void8383-hwsq_op5f(struct hwsq_ucode *hwsq, u8 v0, u8 v1)8484-{8585- *hwsq->ptr.u08++ = 0x5f;8686- *hwsq->ptr.u08++ = v0;8787- *hwsq->ptr.u08++ = v1;8888-}8989-9090-static inline void9191-hwsq_wr32(struct hwsq_ucode *hwsq, u32 reg, u32 val)9292-{9393- if (val != hwsq->val) {9494- if ((val & 0xffff0000) == (hwsq->val & 0xffff0000)) {9595- *hwsq->ptr.u08++ = 0x42;9696- *hwsq->ptr.u16++ = (val & 0x0000ffff);9797- } else {9898- *hwsq->ptr.u08++ = 0xe2;9999- *hwsq->ptr.u32++ = val;100100- }101101-102102- hwsq->val = val;103103- }104104-105105- if ((reg & 0xffff0000) == (hwsq->reg & 0xffff0000)) {106106- *hwsq->ptr.u08++ = 0x40;107107- *hwsq->ptr.u16++ = (reg & 0x0000ffff);108108- } else {109109- *hwsq->ptr.u08++ = 0xe0;110110- *hwsq->ptr.u32++ = reg;111111- }112112- hwsq->reg = reg;113113-}114114-115115-#endif
···3232#include <drm/drmP.h>33333434#include "nouveau_drm.h"3535-#include "nouveau_pm.h"3535+#include "nouveau_hwmon.h"36363737#include <subdev/gpio.h>3838#include <subdev/timer.h>3939#include <subdev/therm.h>4040-4141-MODULE_PARM_DESC(perflvl, "Performance level (default: boot)");4242-static char *nouveau_perflvl;4343-module_param_named(perflvl, nouveau_perflvl, charp, 0400);4444-4545-MODULE_PARM_DESC(perflvl_wr, "Allow perflvl changes (warning: dangerous!)");4646-static int nouveau_perflvl_wr;4747-module_param_named(perflvl_wr, nouveau_perflvl_wr, int, 0400);4848-4949-static int5050-nouveau_pm_perflvl_aux(struct drm_device *dev, struct nouveau_pm_level *perflvl,5151- struct nouveau_pm_level *a, struct nouveau_pm_level *b)5252-{5353- struct nouveau_drm *drm = nouveau_drm(dev);5454- struct nouveau_pm *pm = nouveau_pm(dev);5555- struct nouveau_therm *therm = nouveau_therm(drm->device);5656- int ret;5757-5858- /*XXX: not on all boards, we should control based on temperature5959- * on recent boards.. or maybe on some other factor we don't6060- * know about?6161- */6262- if (therm && therm->fan_set &&6363- a->fanspeed && b->fanspeed && b->fanspeed > a->fanspeed) {6464- ret = therm->fan_set(therm, perflvl->fanspeed);6565- if (ret && ret != -ENODEV) {6666- NV_ERROR(drm, "fanspeed set failed: %d\n", ret);6767- }6868- }6969-7070- if (pm->voltage.supported && pm->voltage_set) {7171- if (perflvl->volt_min && b->volt_min > a->volt_min) {7272- ret = pm->voltage_set(dev, perflvl->volt_min);7373- if (ret) {7474- NV_ERROR(drm, "voltage set failed: %d\n", ret);7575- return ret;7676- }7777- }7878- }7979-8080- return 0;8181-}8282-8383-static int8484-nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)8585-{8686- struct nouveau_pm *pm = nouveau_pm(dev);8787- void *state;8888- int ret;8989-9090- if (perflvl == pm->cur)9191- return 0;9292-9393- ret = nouveau_pm_perflvl_aux(dev, perflvl, pm->cur, perflvl);9494- if (ret)9595- return ret;9696-9797- state = pm->clocks_pre(dev, perflvl);9898- if (IS_ERR(state)) {9999- ret = PTR_ERR(state);100100- goto error;101101- }102102- ret = pm->clocks_set(dev, state);103103- if (ret)104104- goto error;105105-106106- ret = nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur);107107- if (ret)108108- return ret;109109-110110- pm->cur = perflvl;111111- return 0;112112-113113-error:114114- /* restore the fan speed and voltage before leaving */115115- nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur);116116- return ret;117117-}118118-119119-void120120-nouveau_pm_trigger(struct drm_device *dev)121121-{122122- struct nouveau_drm *drm = nouveau_drm(dev);123123- struct nouveau_timer *ptimer = nouveau_timer(drm->device);124124- struct nouveau_pm *pm = nouveau_pm(dev);125125- struct nouveau_pm_profile *profile = NULL;126126- struct nouveau_pm_level *perflvl = NULL;127127- int ret;128128-129129- /* select power profile based on current power source */130130- if (power_supply_is_system_supplied())131131- profile = pm->profile_ac;132132- else133133- profile = pm->profile_dc;134134-135135- if (profile != pm->profile) {136136- pm->profile->func->fini(pm->profile);137137- pm->profile = profile;138138- pm->profile->func->init(pm->profile);139139- }140140-141141- /* select performance level based on profile */142142- perflvl = profile->func->select(profile);143143-144144- /* change perflvl, if necessary */145145- if (perflvl != pm->cur) {146146- u64 time0 = ptimer->read(ptimer);147147-148148- NV_INFO(drm, "setting performance level: %d", perflvl->id);149149- ret = nouveau_pm_perflvl_set(dev, perflvl);150150- if (ret)151151- NV_INFO(drm, "> reclocking failed: %d\n\n", ret);152152-153153- NV_INFO(drm, "> reclocking took %lluns\n\n",154154- ptimer->read(ptimer) - time0);155155- }156156-}157157-158158-static struct nouveau_pm_profile *159159-profile_find(struct drm_device *dev, const char *string)160160-{161161- struct nouveau_pm *pm = nouveau_pm(dev);162162- struct nouveau_pm_profile *profile;163163-164164- list_for_each_entry(profile, &pm->profiles, head) {165165- if (!strncmp(profile->name, string, sizeof(profile->name)))166166- return profile;167167- }168168-169169- return NULL;170170-}171171-172172-static int173173-nouveau_pm_profile_set(struct drm_device *dev, const char *profile)174174-{175175- struct nouveau_pm *pm = nouveau_pm(dev);176176- struct nouveau_pm_profile *ac = NULL, *dc = NULL;177177- char string[16], *cur = string, *ptr;178178-179179- /* safety precaution, for now */180180- if (nouveau_perflvl_wr != 7777)181181- return -EPERM;182182-183183- strncpy(string, profile, sizeof(string));184184- string[sizeof(string) - 1] = 0;185185- if ((ptr = strchr(string, '\n')))186186- *ptr = '\0';187187-188188- ptr = strsep(&cur, ",");189189- if (ptr)190190- ac = profile_find(dev, ptr);191191-192192- ptr = strsep(&cur, ",");193193- if (ptr)194194- dc = profile_find(dev, ptr);195195- else196196- dc = ac;197197-198198- if (ac == NULL || dc == NULL)199199- return -EINVAL;200200-201201- pm->profile_ac = ac;202202- pm->profile_dc = dc;203203- nouveau_pm_trigger(dev);204204- return 0;205205-}206206-207207-static void208208-nouveau_pm_static_dummy(struct nouveau_pm_profile *profile)209209-{210210-}211211-212212-static struct nouveau_pm_level *213213-nouveau_pm_static_select(struct nouveau_pm_profile *profile)214214-{215215- return container_of(profile, struct nouveau_pm_level, profile);216216-}217217-218218-const struct nouveau_pm_profile_func nouveau_pm_static_profile_func = {219219- .destroy = nouveau_pm_static_dummy,220220- .init = nouveau_pm_static_dummy,221221- .fini = nouveau_pm_static_dummy,222222- .select = nouveau_pm_static_select,223223-};224224-225225-static int226226-nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)227227-{228228- struct nouveau_drm *drm = nouveau_drm(dev);229229- struct nouveau_pm *pm = nouveau_pm(dev);230230- struct nouveau_therm *therm = nouveau_therm(drm->device);231231- int ret;232232-233233- memset(perflvl, 0, sizeof(*perflvl));234234-235235- if (pm->clocks_get) {236236- ret = pm->clocks_get(dev, perflvl);237237- if (ret)238238- return ret;239239- }240240-241241- if (pm->voltage.supported && pm->voltage_get) {242242- ret = pm->voltage_get(dev);243243- if (ret > 0) {244244- perflvl->volt_min = ret;245245- perflvl->volt_max = ret;246246- }247247- }248248-249249- if (therm && therm->fan_get) {250250- ret = therm->fan_get(therm);251251- if (ret >= 0)252252- perflvl->fanspeed = ret;253253- }254254-255255- nouveau_mem_timing_read(dev, &perflvl->timing);256256- return 0;257257-}258258-259259-static void260260-nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)261261-{262262- char c[16], s[16], v[32], f[16], m[16];263263-264264- c[0] = '\0';265265- if (perflvl->core)266266- snprintf(c, sizeof(c), " core %dMHz", perflvl->core / 1000);267267-268268- s[0] = '\0';269269- if (perflvl->shader)270270- snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);271271-272272- m[0] = '\0';273273- if (perflvl->memory)274274- snprintf(m, sizeof(m), " memory %dMHz", perflvl->memory / 1000);275275-276276- v[0] = '\0';277277- if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) {278278- snprintf(v, sizeof(v), " voltage %dmV-%dmV",279279- perflvl->volt_min / 1000, perflvl->volt_max / 1000);280280- } else281281- if (perflvl->volt_min) {282282- snprintf(v, sizeof(v), " voltage %dmV",283283- perflvl->volt_min / 1000);284284- }285285-286286- f[0] = '\0';287287- if (perflvl->fanspeed)288288- snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);289289-290290- snprintf(ptr, len, "%s%s%s%s%s\n", c, s, m, v, f);291291-}292292-293293-static ssize_t294294-nouveau_pm_get_perflvl_info(struct device *d,295295- struct device_attribute *a, char *buf)296296-{297297- struct nouveau_pm_level *perflvl =298298- container_of(a, struct nouveau_pm_level, dev_attr);299299- char *ptr = buf;300300- int len = PAGE_SIZE;301301-302302- snprintf(ptr, len, "%d:", perflvl->id);303303- ptr += strlen(buf);304304- len -= strlen(buf);305305-306306- nouveau_pm_perflvl_info(perflvl, ptr, len);307307- return strlen(buf);308308-}309309-310310-static ssize_t311311-nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)312312-{313313- struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));314314- struct nouveau_pm *pm = nouveau_pm(dev);315315- struct nouveau_pm_level cur;316316- int len = PAGE_SIZE, ret;317317- char *ptr = buf;318318-319319- snprintf(ptr, len, "profile: %s, %s\nc:",320320- pm->profile_ac->name, pm->profile_dc->name);321321- ptr += strlen(buf);322322- len -= strlen(buf);323323-324324- ret = nouveau_pm_perflvl_get(dev, &cur);325325- if (ret == 0)326326- nouveau_pm_perflvl_info(&cur, ptr, len);327327- return strlen(buf);328328-}329329-330330-static ssize_t331331-nouveau_pm_set_perflvl(struct device *d, struct device_attribute *a,332332- const char *buf, size_t count)333333-{334334- struct drm_device *dev = pci_get_drvdata(to_pci_dev(d));335335- int ret;336336-337337- ret = nouveau_pm_profile_set(dev, buf);338338- if (ret)339339- return ret;340340- return strlen(buf);341341-}342342-343343-static DEVICE_ATTR(performance_level, S_IRUGO | S_IWUSR,344344- nouveau_pm_get_perflvl, nouveau_pm_set_perflvl);345345-346346-static int347347-nouveau_sysfs_init(struct drm_device *dev)348348-{349349- struct nouveau_drm *drm = nouveau_drm(dev);350350- struct nouveau_pm *pm = nouveau_pm(dev);351351- struct device *d = &dev->pdev->dev;352352- int ret, i;353353-354354- ret = device_create_file(d, &dev_attr_performance_level);355355- if (ret)356356- return ret;357357-358358- for (i = 0; i < pm->nr_perflvl; i++) {359359- struct nouveau_pm_level *perflvl = &pm->perflvl[i];360360-361361- perflvl->dev_attr.attr.name = perflvl->name;362362- perflvl->dev_attr.attr.mode = S_IRUGO;363363- perflvl->dev_attr.show = nouveau_pm_get_perflvl_info;364364- perflvl->dev_attr.store = NULL;365365- sysfs_attr_init(&perflvl->dev_attr.attr);366366-367367- ret = device_create_file(d, &perflvl->dev_attr);368368- if (ret) {369369- NV_ERROR(drm, "failed pervlvl %d sysfs: %d\n",370370- perflvl->id, i);371371- perflvl->dev_attr.attr.name = NULL;372372- nouveau_pm_fini(dev);373373- return ret;374374- }375375- }376376-377377- return 0;378378-}379379-380380-static void381381-nouveau_sysfs_fini(struct drm_device *dev)382382-{383383- struct nouveau_pm *pm = nouveau_pm(dev);384384- struct device *d = &dev->pdev->dev;385385- int i;386386-387387- device_remove_file(d, &dev_attr_performance_level);388388- for (i = 0; i < pm->nr_perflvl; i++) {389389- struct nouveau_pm_level *pl = &pm->perflvl[i];390390-391391- if (!pl->dev_attr.attr.name)392392- break;393393-394394- device_remove_file(d, &pl->dev_attr);395395- }396396-}3974039841#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))39942static ssize_t···421778 int ret = -ENODEV;422779 long value;423780424424- if (nouveau_perflvl_wr != 7777)425425- return -EPERM;426426-427781 if (kstrtol(buf, 10, &value) == -EINVAL)428782 return -EINVAL;429783···559919};560920#endif561921562562-static int922922+int563923nouveau_hwmon_init(struct drm_device *dev)564924{565565- struct nouveau_pm *pm = nouveau_pm(dev);566566-567925#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))568926 struct nouveau_drm *drm = nouveau_drm(dev);569927 struct nouveau_therm *therm = nouveau_therm(drm->device);928928+ struct nouveau_hwmon *hwmon;570929 struct device *hwmon_dev;571930 int ret = 0;931931+932932+ hwmon = drm->hwmon = kzalloc(sizeof(*hwmon), GFP_KERNEL);933933+ if (!hwmon)934934+ return -ENOMEM;935935+ hwmon->dev = dev;572936573937 if (!therm || !therm->temp_get || !therm->attr_get || !therm->attr_set)574938 return -ENODEV;···620976 goto error;621977 }622978623623- pm->hwmon = hwmon_dev;979979+ hwmon->hwmon = hwmon_dev;624980625981 return 0;626982627983error:628984 NV_ERROR(drm, "Unable to create some hwmon sysfs files: %d\n", ret);629985 hwmon_device_unregister(hwmon_dev);630630- pm->hwmon = NULL;986986+ hwmon->hwmon = NULL;631987 return ret;632988#else633633- pm->hwmon = NULL;989989+ hwmon->hwmon = NULL;634990 return 0;635991#endif636992}637993638638-static void994994+void639995nouveau_hwmon_fini(struct drm_device *dev)640996{641997#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))642642- struct nouveau_pm *pm = nouveau_pm(dev);998998+ struct nouveau_hwmon *hwmon = nouveau_hwmon(dev);643999644644- if (pm->hwmon) {645645- sysfs_remove_group(&pm->hwmon->kobj, &hwmon_default_attrgroup);646646- sysfs_remove_group(&pm->hwmon->kobj, &hwmon_temp_attrgroup);647647- sysfs_remove_group(&pm->hwmon->kobj, &hwmon_pwm_fan_attrgroup);648648- sysfs_remove_group(&pm->hwmon->kobj, &hwmon_fan_rpm_attrgroup);10001000+ if (hwmon->hwmon) {10011001+ sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_default_attrgroup);10021002+ sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_temp_attrgroup);10031003+ sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_pwm_fan_attrgroup);10041004+ sysfs_remove_group(&hwmon->hwmon->kobj, &hwmon_fan_rpm_attrgroup);6491005650650- hwmon_device_unregister(pm->hwmon);10061006+ hwmon_device_unregister(hwmon->hwmon);6511007 }10081008+10091009+ nouveau_drm(dev)->hwmon = NULL;10101010+ kfree(hwmon);6521011#endif653653-}654654-655655-#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)656656-static int657657-nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)658658-{659659- struct nouveau_pm *pm = container_of(nb, struct nouveau_pm, acpi_nb);660660- struct nouveau_drm *drm = nouveau_drm(pm->dev);661661- struct acpi_bus_event *entry = (struct acpi_bus_event *)data;662662-663663- if (strcmp(entry->device_class, "ac_adapter") == 0) {664664- bool ac = power_supply_is_system_supplied();665665-666666- NV_DEBUG(drm, "power supply changed: %s\n", ac ? "AC" : "DC");667667- nouveau_pm_trigger(pm->dev);668668- }669669-670670- return NOTIFY_OK;671671-}672672-#endif673673-674674-int675675-nouveau_pm_init(struct drm_device *dev)676676-{677677- struct nouveau_device *device = nouveau_dev(dev);678678- struct nouveau_drm *drm = nouveau_drm(dev);679679- struct nouveau_pm *pm;680680- char info[256];681681- int ret, i;682682-683683- pm = drm->pm = kzalloc(sizeof(*pm), GFP_KERNEL);684684- if (!pm)685685- return -ENOMEM;686686-687687- pm->dev = dev;688688-689689- if (device->card_type < NV_40) {690690- pm->clocks_get = nv04_pm_clocks_get;691691- pm->clocks_pre = nv04_pm_clocks_pre;692692- pm->clocks_set = nv04_pm_clocks_set;693693- if (nouveau_gpio(drm->device)) {694694- pm->voltage_get = nouveau_voltage_gpio_get;695695- pm->voltage_set = nouveau_voltage_gpio_set;696696- }697697- } else698698- if (device->card_type < NV_50) {699699- pm->clocks_get = nv40_pm_clocks_get;700700- pm->clocks_pre = nv40_pm_clocks_pre;701701- pm->clocks_set = nv40_pm_clocks_set;702702- pm->voltage_get = nouveau_voltage_gpio_get;703703- pm->voltage_set = nouveau_voltage_gpio_set;704704- } else705705- if (device->card_type < NV_C0) {706706- if (device->chipset < 0xa3 ||707707- device->chipset == 0xaa ||708708- device->chipset == 0xac) {709709- pm->clocks_get = nv50_pm_clocks_get;710710- pm->clocks_pre = nv50_pm_clocks_pre;711711- pm->clocks_set = nv50_pm_clocks_set;712712- } else {713713- pm->clocks_get = nva3_pm_clocks_get;714714- pm->clocks_pre = nva3_pm_clocks_pre;715715- pm->clocks_set = nva3_pm_clocks_set;716716- }717717- pm->voltage_get = nouveau_voltage_gpio_get;718718- pm->voltage_set = nouveau_voltage_gpio_set;719719- } else720720- if (device->card_type < NV_E0) {721721- pm->clocks_get = nvc0_pm_clocks_get;722722- pm->clocks_pre = nvc0_pm_clocks_pre;723723- pm->clocks_set = nvc0_pm_clocks_set;724724- pm->voltage_get = nouveau_voltage_gpio_get;725725- pm->voltage_set = nouveau_voltage_gpio_set;726726- }727727-728728-729729- /* parse aux tables from vbios */730730- nouveau_volt_init(dev);731731-732732- INIT_LIST_HEAD(&pm->profiles);733733-734734- /* determine current ("boot") performance level */735735- ret = nouveau_pm_perflvl_get(dev, &pm->boot);736736- if (ret) {737737- NV_ERROR(drm, "failed to determine boot perflvl\n");738738- return ret;739739- }740740-741741- strncpy(pm->boot.name, "boot", 4);742742- strncpy(pm->boot.profile.name, "boot", 4);743743- pm->boot.profile.func = &nouveau_pm_static_profile_func;744744-745745- list_add(&pm->boot.profile.head, &pm->profiles);746746-747747- pm->profile_ac = &pm->boot.profile;748748- pm->profile_dc = &pm->boot.profile;749749- pm->profile = &pm->boot.profile;750750- pm->cur = &pm->boot;751751-752752- /* add performance levels from vbios */753753- nouveau_perf_init(dev);754754-755755- /* display available performance levels */756756- NV_INFO(drm, "%d available performance level(s)\n", pm->nr_perflvl);757757- for (i = 0; i < pm->nr_perflvl; i++) {758758- nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));759759- NV_INFO(drm, "%d:%s", pm->perflvl[i].id, info);760760- }761761-762762- nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));763763- NV_INFO(drm, "c:%s", info);764764-765765- /* switch performance levels now if requested */766766- if (nouveau_perflvl != NULL)767767- nouveau_pm_profile_set(dev, nouveau_perflvl);768768-769769- nouveau_sysfs_init(dev);770770- nouveau_hwmon_init(dev);771771-#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)772772- pm->acpi_nb.notifier_call = nouveau_pm_acpi_event;773773- register_acpi_notifier(&pm->acpi_nb);774774-#endif775775-776776- return 0;777777-}778778-779779-void780780-nouveau_pm_fini(struct drm_device *dev)781781-{782782- struct nouveau_pm *pm = nouveau_pm(dev);783783- struct nouveau_pm_profile *profile, *tmp;784784-785785- list_for_each_entry_safe(profile, tmp, &pm->profiles, head) {786786- list_del(&profile->head);787787- profile->func->destroy(profile);788788- }789789-790790- if (pm->cur != &pm->boot)791791- nouveau_pm_perflvl_set(dev, &pm->boot);792792-793793- nouveau_perf_fini(dev);794794- nouveau_volt_fini(dev);795795-796796-#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)797797- unregister_acpi_notifier(&pm->acpi_nb);798798-#endif799799- nouveau_hwmon_fini(dev);800800- nouveau_sysfs_fini(dev);801801-802802- nouveau_drm(dev)->pm = NULL;803803- kfree(pm);804804-}805805-806806-void807807-nouveau_pm_resume(struct drm_device *dev)808808-{809809- struct nouveau_pm *pm = nouveau_pm(dev);810810- struct nouveau_pm_level *perflvl;811811-812812- if (!pm->cur || pm->cur == &pm->boot)813813- return;814814-815815- perflvl = pm->cur;816816- pm->cur = &pm->boot;817817- nouveau_pm_perflvl_set(dev, perflvl);8181012}
-283
drivers/gpu/drm/nouveau/nouveau_pm.h
···11-/*22- * Copyright 2010 Red Hat Inc.33- *44- * Permission is hereby granted, free of charge, to any person obtaining a55- * copy of this software and associated documentation files (the "Software"),66- * to deal in the Software without restriction, including without limitation77- * the rights to use, copy, modify, merge, publish, distribute, sublicense,88- * and/or sell copies of the Software, and to permit persons to whom the99- * Software is furnished to do so, subject to the following conditions:1010- *1111- * The above copyright notice and this permission notice shall be included in1212- * all copies or substantial portions of the Software.1313- *1414- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR1515- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,1616- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL1717- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR1818- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,1919- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR2020- * OTHER DEALINGS IN THE SOFTWARE.2121- *2222- * Authors: Ben Skeggs2323- */2424-2525-#ifndef __NOUVEAU_PM_H__2626-#define __NOUVEAU_PM_H__2727-2828-#include <subdev/bios/pll.h>2929-#include <subdev/clock.h>3030-3131-struct nouveau_pm_voltage_level {3232- u32 voltage; /* microvolts */3333- u8 vid;3434-};3535-3636-struct nouveau_pm_voltage {3737- bool supported;3838- u8 version;3939- u8 vid_mask;4040-4141- struct nouveau_pm_voltage_level *level;4242- int nr_level;4343-};4444-4545-/* Exclusive upper limits */4646-#define NV_MEM_CL_DDR2_MAX 84747-#define NV_MEM_WR_DDR2_MAX 94848-#define NV_MEM_CL_DDR3_MAX 174949-#define NV_MEM_WR_DDR3_MAX 175050-#define NV_MEM_CL_GDDR3_MAX 165151-#define NV_MEM_WR_GDDR3_MAX 185252-#define NV_MEM_CL_GDDR5_MAX 215353-#define NV_MEM_WR_GDDR5_MAX 205454-5555-struct nouveau_pm_memtiming {5656- int id;5757-5858- u32 reg[9];5959- u32 mr[4];6060-6161- u8 tCWL;6262-6363- u8 odt;6464- u8 drive_strength;6565-};6666-6767-struct nouveau_pm_tbl_header {6868- u8 version;6969- u8 header_len;7070- u8 entry_cnt;7171- u8 entry_len;7272-};7373-7474-struct nouveau_pm_tbl_entry {7575- u8 tWR;7676- u8 tWTR;7777- u8 tCL;7878- u8 tRC;7979- u8 empty_4;8080- u8 tRFC; /* Byte 5 */8181- u8 empty_6;8282- u8 tRAS; /* Byte 7 */8383- u8 empty_8;8484- u8 tRP; /* Byte 9 */8585- u8 tRCDRD;8686- u8 tRCDWR;8787- u8 tRRD;8888- u8 tUNK_13;8989- u8 RAM_FT1; /* 14, a bitmask of random RAM features */9090- u8 empty_15;9191- u8 tUNK_16;9292- u8 empty_17;9393- u8 tUNK_18;9494- u8 tCWL;9595- u8 tUNK_20, tUNK_21;9696-};9797-9898-struct nouveau_pm_profile;9999-struct nouveau_pm_profile_func {100100- void (*destroy)(struct nouveau_pm_profile *);101101- void (*init)(struct nouveau_pm_profile *);102102- void (*fini)(struct nouveau_pm_profile *);103103- struct nouveau_pm_level *(*select)(struct nouveau_pm_profile *);104104-};105105-106106-struct nouveau_pm_profile {107107- const struct nouveau_pm_profile_func *func;108108- struct list_head head;109109- char name[8];110110-};111111-112112-#define NOUVEAU_PM_MAX_LEVEL 8113113-struct nouveau_pm_level {114114- struct nouveau_pm_profile profile;115115- struct device_attribute dev_attr;116116- char name[32];117117- int id;118118-119119- struct nouveau_pm_memtiming timing;120120- u32 memory;121121- u16 memscript;122122-123123- u32 core;124124- u32 shader;125125- u32 rop;126126- u32 copy;127127- u32 daemon;128128- u32 vdec;129129- u32 dom6;130130- u32 unka0; /* nva3:nvc0 */131131- u32 hub01; /* nvc0- */132132- u32 hub06; /* nvc0- */133133- u32 hub07; /* nvc0- */134134-135135- u32 volt_min; /* microvolts */136136- u32 volt_max;137137- u8 fanspeed;138138-};139139-140140-struct nouveau_pm_temp_sensor_constants {141141- u16 offset_constant;142142- s16 offset_mult;143143- s16 offset_div;144144- s16 slope_mult;145145- s16 slope_div;146146-};147147-148148-struct nouveau_pm_threshold_temp {149149- s16 critical;150150- s16 down_clock;151151-};152152-153153-struct nouveau_pm {154154- struct drm_device *dev;155155-156156- struct nouveau_pm_voltage voltage;157157- struct nouveau_pm_level perflvl[NOUVEAU_PM_MAX_LEVEL];158158- int nr_perflvl;159159- struct nouveau_pm_temp_sensor_constants sensor_constants;160160- struct nouveau_pm_threshold_temp threshold_temp;161161-162162- struct nouveau_pm_profile *profile_ac;163163- struct nouveau_pm_profile *profile_dc;164164- struct nouveau_pm_profile *profile;165165- struct list_head profiles;166166-167167- struct nouveau_pm_level boot;168168- struct nouveau_pm_level *cur;169169-170170- struct device *hwmon;171171- struct notifier_block acpi_nb;172172-173173- int (*clocks_get)(struct drm_device *, struct nouveau_pm_level *);174174- void *(*clocks_pre)(struct drm_device *, struct nouveau_pm_level *);175175- int (*clocks_set)(struct drm_device *, void *);176176-177177- int (*voltage_get)(struct drm_device *);178178- int (*voltage_set)(struct drm_device *, int voltage);179179-};180180-181181-static inline struct nouveau_pm *182182-nouveau_pm(struct drm_device *dev)183183-{184184- return nouveau_drm(dev)->pm;185185-}186186-187187-struct nouveau_mem_exec_func {188188- struct drm_device *dev;189189- void (*precharge)(struct nouveau_mem_exec_func *);190190- void (*refresh)(struct nouveau_mem_exec_func *);191191- void (*refresh_auto)(struct nouveau_mem_exec_func *, bool);192192- void (*refresh_self)(struct nouveau_mem_exec_func *, bool);193193- void (*wait)(struct nouveau_mem_exec_func *, u32 nsec);194194- u32 (*mrg)(struct nouveau_mem_exec_func *, int mr);195195- void (*mrs)(struct nouveau_mem_exec_func *, int mr, u32 data);196196- void (*clock_set)(struct nouveau_mem_exec_func *);197197- void (*timing_set)(struct nouveau_mem_exec_func *);198198- void *priv;199199-};200200-201201-/* nouveau_mem.c */202202-int nouveau_mem_exec(struct nouveau_mem_exec_func *,203203- struct nouveau_pm_level *);204204-205205-/* nouveau_pm.c */206206-int nouveau_pm_init(struct drm_device *dev);207207-void nouveau_pm_fini(struct drm_device *dev);208208-void nouveau_pm_resume(struct drm_device *dev);209209-extern const struct nouveau_pm_profile_func nouveau_pm_static_profile_func;210210-void nouveau_pm_trigger(struct drm_device *dev);211211-212212-/* nouveau_volt.c */213213-void nouveau_volt_init(struct drm_device *);214214-void nouveau_volt_fini(struct drm_device *);215215-int nouveau_volt_vid_lookup(struct drm_device *, int voltage);216216-int nouveau_volt_lvl_lookup(struct drm_device *, int vid);217217-int nouveau_voltage_gpio_get(struct drm_device *);218218-int nouveau_voltage_gpio_set(struct drm_device *, int voltage);219219-220220-/* nouveau_perf.c */221221-void nouveau_perf_init(struct drm_device *);222222-void nouveau_perf_fini(struct drm_device *);223223-u8 *nouveau_perf_rammap(struct drm_device *, u32 freq, u8 *ver,224224- u8 *hdr, u8 *cnt, u8 *len);225225-u8 *nouveau_perf_ramcfg(struct drm_device *, u32 freq, u8 *ver, u8 *len);226226-u8 *nouveau_perf_timing(struct drm_device *, u32 freq, u8 *ver, u8 *len);227227-228228-/* nouveau_mem.c */229229-void nouveau_mem_timing_init(struct drm_device *);230230-void nouveau_mem_timing_fini(struct drm_device *);231231-232232-/* nv04_pm.c */233233-int nv04_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);234234-void *nv04_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);235235-int nv04_pm_clocks_set(struct drm_device *, void *);236236-237237-/* nv40_pm.c */238238-int nv40_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);239239-void *nv40_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);240240-int nv40_pm_clocks_set(struct drm_device *, void *);241241-int nv40_pm_pwm_get(struct drm_device *, int, u32 *, u32 *);242242-int nv40_pm_pwm_set(struct drm_device *, int, u32, u32);243243-244244-/* nv50_pm.c */245245-int nv50_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);246246-void *nv50_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);247247-int nv50_pm_clocks_set(struct drm_device *, void *);248248-int nv50_pm_pwm_get(struct drm_device *, int, u32 *, u32 *);249249-int nv50_pm_pwm_set(struct drm_device *, int, u32, u32);250250-251251-/* nva3_pm.c */252252-int nva3_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);253253-void *nva3_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);254254-int nva3_pm_clocks_set(struct drm_device *, void *);255255-256256-/* nvc0_pm.c */257257-int nvc0_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);258258-void *nvc0_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);259259-int nvc0_pm_clocks_set(struct drm_device *, void *);260260-261261-/* nouveau_mem.c */262262-int nouveau_mem_timing_calc(struct drm_device *, u32 freq,263263- struct nouveau_pm_memtiming *);264264-void nouveau_mem_timing_read(struct drm_device *,265265- struct nouveau_pm_memtiming *);266266-267267-static inline int268268-nva3_calc_pll(struct drm_device *dev, struct nvbios_pll *pll, u32 freq,269269- int *N, int *fN, int *M, int *P)270270-{271271- struct nouveau_device *device = nouveau_dev(dev);272272- struct nouveau_clock *clk = nouveau_clock(device);273273- struct nouveau_pll_vals pv;274274- int ret;275275-276276- ret = clk->pll_calc(clk, pll, freq, &pv);277277- *N = pv.N1;278278- *M = pv.M1;279279- *P = pv.log2P;280280- return ret;281281-}282282-283283-#endif
-250
drivers/gpu/drm/nouveau/nouveau_volt.c
···11-/*22- * Copyright 2010 Red Hat Inc.33- *44- * Permission is hereby granted, free of charge, to any person obtaining a55- * copy of this software and associated documentation files (the "Software"),66- * to deal in the Software without restriction, including without limitation77- * the rights to use, copy, modify, merge, publish, distribute, sublicense,88- * and/or sell copies of the Software, and to permit persons to whom the99- * Software is furnished to do so, subject to the following conditions:1010- *1111- * The above copyright notice and this permission notice shall be included in1212- * all copies or substantial portions of the Software.1313- *1414- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR1515- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,1616- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL1717- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR1818- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,1919- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR2020- * OTHER DEALINGS IN THE SOFTWARE.2121- *2222- * Authors: Ben Skeggs2323- */2424-2525-#include <drm/drmP.h>2626-2727-#include "nouveau_drm.h"2828-#include "nouveau_pm.h"2929-3030-#include <subdev/bios/gpio.h>3131-#include <subdev/gpio.h>3232-3333-static const enum dcb_gpio_func_name vidtag[] = { 0x04, 0x05, 0x06, 0x1a, 0x73 };3434-static int nr_vidtag = sizeof(vidtag) / sizeof(vidtag[0]);3535-3636-int3737-nouveau_voltage_gpio_get(struct drm_device *dev)3838-{3939- struct nouveau_pm_voltage *volt = &nouveau_pm(dev)->voltage;4040- struct nouveau_device *device = nouveau_dev(dev);4141- struct nouveau_gpio *gpio = nouveau_gpio(device);4242- u8 vid = 0;4343- int i;4444-4545- for (i = 0; i < nr_vidtag; i++) {4646- if (!(volt->vid_mask & (1 << i)))4747- continue;4848-4949- vid |= gpio->get(gpio, 0, vidtag[i], 0xff) << i;5050- }5151-5252- return nouveau_volt_lvl_lookup(dev, vid);5353-}5454-5555-int5656-nouveau_voltage_gpio_set(struct drm_device *dev, int voltage)5757-{5858- struct nouveau_device *device = nouveau_dev(dev);5959- struct nouveau_gpio *gpio = nouveau_gpio(device);6060- struct nouveau_pm_voltage *volt = &nouveau_pm(dev)->voltage;6161- int vid, i;6262-6363- vid = nouveau_volt_vid_lookup(dev, voltage);6464- if (vid < 0)6565- return vid;6666-6767- for (i = 0; i < nr_vidtag; i++) {6868- if (!(volt->vid_mask & (1 << i)))6969- continue;7070-7171- gpio->set(gpio, 0, vidtag[i], 0xff, !!(vid & (1 << i)));7272- }7373-7474- return 0;7575-}7676-7777-int7878-nouveau_volt_vid_lookup(struct drm_device *dev, int voltage)7979-{8080- struct nouveau_pm_voltage *volt = &nouveau_pm(dev)->voltage;8181- int i;8282-8383- for (i = 0; i < volt->nr_level; i++) {8484- if (volt->level[i].voltage == voltage)8585- return volt->level[i].vid;8686- }8787-8888- return -ENOENT;8989-}9090-9191-int9292-nouveau_volt_lvl_lookup(struct drm_device *dev, int vid)9393-{9494- struct nouveau_pm_voltage *volt = &nouveau_pm(dev)->voltage;9595- int i;9696-9797- for (i = 0; i < volt->nr_level; i++) {9898- if (volt->level[i].vid == vid)9999- return volt->level[i].voltage;100100- }101101-102102- return -ENOENT;103103-}104104-105105-void106106-nouveau_volt_init(struct drm_device *dev)107107-{108108- struct nouveau_drm *drm = nouveau_drm(dev);109109- struct nouveau_gpio *gpio = nouveau_gpio(drm->device);110110- struct nouveau_pm *pm = nouveau_pm(dev);111111- struct nouveau_pm_voltage *voltage = &pm->voltage;112112- struct nvbios *bios = &drm->vbios;113113- struct dcb_gpio_func func;114114- struct bit_entry P;115115- u8 *volt = NULL, *entry;116116- int i, headerlen, recordlen, entries, vidmask, vidshift;117117-118118- if (bios->type == NVBIOS_BIT) {119119- if (bit_table(dev, 'P', &P))120120- return;121121-122122- if (P.version == 1)123123- volt = ROMPTR(dev, P.data[16]);124124- else125125- if (P.version == 2)126126- volt = ROMPTR(dev, P.data[12]);127127- else {128128- NV_WARN(drm, "unknown volt for BIT P %d\n", P.version);129129- }130130- } else {131131- if (bios->data[bios->offset + 6] < 0x27) {132132- NV_DEBUG(drm, "BMP version too old for voltage\n");133133- return;134134- }135135-136136- volt = ROMPTR(dev, bios->data[bios->offset + 0x98]);137137- }138138-139139- if (!volt) {140140- NV_DEBUG(drm, "voltage table pointer invalid\n");141141- return;142142- }143143-144144- switch (volt[0]) {145145- case 0x10:146146- case 0x11:147147- case 0x12:148148- headerlen = 5;149149- recordlen = volt[1];150150- entries = volt[2];151151- vidshift = 0;152152- vidmask = volt[4];153153- break;154154- case 0x20:155155- headerlen = volt[1];156156- recordlen = volt[3];157157- entries = volt[2];158158- vidshift = 0; /* could be vidshift like 0x30? */159159- vidmask = volt[5];160160- break;161161- case 0x30:162162- headerlen = volt[1];163163- recordlen = volt[2];164164- entries = volt[3];165165- vidmask = volt[4];166166- /* no longer certain what volt[5] is, if it's related to167167- * the vid shift then it's definitely not a function of168168- * how many bits are set.169169- *170170- * after looking at a number of nva3+ vbios images, they171171- * all seem likely to have a static shift of 2.. lets172172- * go with that for now until proven otherwise.173173- */174174- vidshift = 2;175175- break;176176- case 0x40:177177- headerlen = volt[1];178178- recordlen = volt[2];179179- entries = volt[3]; /* not a clue what the entries are for.. */180180- vidmask = volt[11]; /* guess.. */181181- vidshift = 0;182182- break;183183- default:184184- NV_WARN(drm, "voltage table 0x%02x unknown\n", volt[0]);185185- return;186186- }187187-188188- /* validate vid mask */189189- voltage->vid_mask = vidmask;190190- if (!voltage->vid_mask)191191- return;192192-193193- i = 0;194194- while (vidmask) {195195- if (i > nr_vidtag) {196196- NV_DEBUG(drm, "vid bit %d unknown\n", i);197197- return;198198- }199199-200200- if (gpio && gpio->find(gpio, 0, vidtag[i], 0xff, &func)) {201201- NV_DEBUG(drm, "vid bit %d has no gpio tag\n", i);202202- return;203203- }204204-205205- vidmask >>= 1;206206- i++;207207- }208208-209209- /* parse vbios entries into common format */210210- voltage->version = volt[0];211211- if (voltage->version < 0x40) {212212- voltage->nr_level = entries;213213- voltage->level =214214- kcalloc(entries, sizeof(*voltage->level), GFP_KERNEL);215215- if (!voltage->level)216216- return;217217-218218- entry = volt + headerlen;219219- for (i = 0; i < entries; i++, entry += recordlen) {220220- voltage->level[i].voltage = entry[0] * 10000;221221- voltage->level[i].vid = entry[1] >> vidshift;222222- }223223- } else {224224- u32 volt_uv = ROM32(volt[4]);225225- s16 step_uv = ROM16(volt[8]);226226- u8 vid;227227-228228- voltage->nr_level = voltage->vid_mask + 1;229229- voltage->level = kcalloc(voltage->nr_level,230230- sizeof(*voltage->level), GFP_KERNEL);231231- if (!voltage->level)232232- return;233233-234234- for (vid = 0; vid <= voltage->vid_mask; vid++) {235235- voltage->level[vid].voltage = volt_uv;236236- voltage->level[vid].vid = vid;237237- volt_uv += step_uv;238238- }239239- }240240-241241- voltage->supported = true;242242-}243243-244244-void245245-nouveau_volt_fini(struct drm_device *dev)246246-{247247- struct nouveau_pm_voltage *volt = &nouveau_pm(dev)->voltage;248248-249249- kfree(volt->level);250250-}
-146
drivers/gpu/drm/nouveau/nv04_pm.c
···11-/*22- * Copyright 2010 Red Hat Inc.33- *44- * Permission is hereby granted, free of charge, to any person obtaining a55- * copy of this software and associated documentation files (the "Software"),66- * to deal in the Software without restriction, including without limitation77- * the rights to use, copy, modify, merge, publish, distribute, sublicense,88- * and/or sell copies of the Software, and to permit persons to whom the99- * Software is furnished to do so, subject to the following conditions:1010- *1111- * The above copyright notice and this permission notice shall be included in1212- * all copies or substantial portions of the Software.1313- *1414- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR1515- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,1616- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL1717- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR1818- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,1919- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR2020- * OTHER DEALINGS IN THE SOFTWARE.2121- *2222- * Authors: Ben Skeggs2323- */2424-2525-#include <drm/drmP.h>2626-#include "nouveau_drm.h"2727-#include "nouveau_reg.h"2828-#include "dispnv04/hw.h"2929-#include "nouveau_pm.h"3030-3131-#include <subdev/bios/pll.h>3232-#include <subdev/clock.h>3333-#include <subdev/timer.h>3434-3535-int3636-nv04_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)3737-{3838- int ret;3939-4040- ret = nouveau_hw_get_clock(dev, PLL_CORE);4141- if (ret < 0)4242- return ret;4343- perflvl->core = ret;4444-4545- ret = nouveau_hw_get_clock(dev, PLL_MEMORY);4646- if (ret < 0)4747- return ret;4848- perflvl->memory = ret;4949-5050- return 0;5151-}5252-5353-struct nv04_pm_clock {5454- struct nvbios_pll pll;5555- struct nouveau_pll_vals calc;5656-};5757-5858-struct nv04_pm_state {5959- struct nv04_pm_clock core;6060- struct nv04_pm_clock memory;6161-};6262-6363-static int6464-calc_pll(struct drm_device *dev, u32 id, int khz, struct nv04_pm_clock *clk)6565-{6666- struct nouveau_device *device = nouveau_dev(dev);6767- struct nouveau_bios *bios = nouveau_bios(device);6868- struct nouveau_clock *pclk = nouveau_clock(device);6969- int ret;7070-7171- ret = nvbios_pll_parse(bios, id, &clk->pll);7272- if (ret)7373- return ret;7474-7575- ret = pclk->pll_calc(pclk, &clk->pll, khz, &clk->calc);7676- if (!ret)7777- return -EINVAL;7878-7979- return 0;8080-}8181-8282-void *8383-nv04_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)8484-{8585- struct nv04_pm_state *info;8686- int ret;8787-8888- info = kzalloc(sizeof(*info), GFP_KERNEL);8989- if (!info)9090- return ERR_PTR(-ENOMEM);9191-9292- ret = calc_pll(dev, PLL_CORE, perflvl->core, &info->core);9393- if (ret)9494- goto error;9595-9696- if (perflvl->memory) {9797- ret = calc_pll(dev, PLL_MEMORY, perflvl->memory, &info->memory);9898- if (ret)9999- goto error;100100- }101101-102102- return info;103103-error:104104- kfree(info);105105- return ERR_PTR(ret);106106-}107107-108108-static void109109-prog_pll(struct drm_device *dev, struct nv04_pm_clock *clk)110110-{111111- struct nouveau_device *device = nouveau_dev(dev);112112- struct nouveau_clock *pclk = nouveau_clock(device);113113- u32 reg = clk->pll.reg;114114-115115- /* thank the insane nouveau_hw_setpll() interface for this */116116- if (device->card_type >= NV_40)117117- reg += 4;118118-119119- pclk->pll_prog(pclk, reg, &clk->calc);120120-}121121-122122-int123123-nv04_pm_clocks_set(struct drm_device *dev, void *pre_state)124124-{125125- struct nouveau_device *device = nouveau_dev(dev);126126- struct nouveau_timer *ptimer = nouveau_timer(device);127127- struct nv04_pm_state *state = pre_state;128128-129129- prog_pll(dev, &state->core);130130-131131- if (state->memory.pll.reg) {132132- prog_pll(dev, &state->memory);133133- if (device->card_type < NV_30) {134134- if (device->card_type == NV_20)135135- nv_mask(device, 0x1002c4, 0, 1 << 20);136136-137137- /* Reset the DLLs */138138- nv_mask(device, 0x1002c0, 0, 1 << 8);139139- }140140- }141141-142142- nv_ofuncs(ptimer)->init(nv_object(ptimer));143143-144144- kfree(state);145145- return 0;146146-}
-353
drivers/gpu/drm/nouveau/nv40_pm.c
···11-/*22- * Copyright 2011 Red Hat Inc.33- *44- * Permission is hereby granted, free of charge, to any person obtaining a55- * copy of this software and associated documentation files (the "Software"),66- * to deal in the Software without restriction, including without limitation77- * the rights to use, copy, modify, merge, publish, distribute, sublicense,88- * and/or sell copies of the Software, and to permit persons to whom the99- * Software is furnished to do so, subject to the following conditions:1010- *1111- * The above copyright notice and this permission notice shall be included in1212- * all copies or substantial portions of the Software.1313- *1414- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR1515- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,1616- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL1717- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR1818- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,1919- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR2020- * OTHER DEALINGS IN THE SOFTWARE.2121- *2222- * Authors: Ben Skeggs2323- */2424-2525-#include <drm/drmP.h>2626-#include "nouveau_drm.h"2727-#include "nouveau_bios.h"2828-#include "nouveau_pm.h"2929-#include "dispnv04/hw.h"3030-3131-#include <subdev/bios/pll.h>3232-#include <subdev/clock.h>3333-#include <subdev/timer.h>3434-3535-#include <engine/fifo.h>3636-3737-#define min2(a,b) ((a) < (b) ? (a) : (b))3838-3939-static u324040-read_pll_1(struct drm_device *dev, u32 reg)4141-{4242- struct nouveau_device *device = nouveau_dev(dev);4343- u32 ctrl = nv_rd32(device, reg + 0x00);4444- int P = (ctrl & 0x00070000) >> 16;4545- int N = (ctrl & 0x0000ff00) >> 8;4646- int M = (ctrl & 0x000000ff) >> 0;4747- u32 ref = 27000, clk = 0;4848-4949- if (ctrl & 0x80000000)5050- clk = ref * N / M;5151-5252- return clk >> P;5353-}5454-5555-static u325656-read_pll_2(struct drm_device *dev, u32 reg)5757-{5858- struct nouveau_device *device = nouveau_dev(dev);5959- u32 ctrl = nv_rd32(device, reg + 0x00);6060- u32 coef = nv_rd32(device, reg + 0x04);6161- int N2 = (coef & 0xff000000) >> 24;6262- int M2 = (coef & 0x00ff0000) >> 16;6363- int N1 = (coef & 0x0000ff00) >> 8;6464- int M1 = (coef & 0x000000ff) >> 0;6565- int P = (ctrl & 0x00070000) >> 16;6666- u32 ref = 27000, clk = 0;6767-6868- if ((ctrl & 0x80000000) && M1) {6969- clk = ref * N1 / M1;7070- if ((ctrl & 0x40000100) == 0x40000000) {7171- if (M2)7272- clk = clk * N2 / M2;7373- else7474- clk = 0;7575- }7676- }7777-7878- return clk >> P;7979-}8080-8181-static u328282-read_clk(struct drm_device *dev, u32 src)8383-{8484- switch (src) {8585- case 3:8686- return read_pll_2(dev, 0x004000);8787- case 2:8888- return read_pll_1(dev, 0x004008);8989- default:9090- break;9191- }9292-9393- return 0;9494-}9595-9696-int9797-nv40_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)9898-{9999- struct nouveau_device *device = nouveau_dev(dev);100100- u32 ctrl = nv_rd32(device, 0x00c040);101101-102102- perflvl->core = read_clk(dev, (ctrl & 0x00000003) >> 0);103103- perflvl->shader = read_clk(dev, (ctrl & 0x00000030) >> 4);104104- perflvl->memory = read_pll_2(dev, 0x4020);105105- return 0;106106-}107107-108108-struct nv40_pm_state {109109- u32 ctrl;110110- u32 npll_ctrl;111111- u32 npll_coef;112112- u32 spll;113113- u32 mpll_ctrl;114114- u32 mpll_coef;115115-};116116-117117-static int118118-nv40_calc_pll(struct drm_device *dev, u32 reg, struct nvbios_pll *pll,119119- u32 clk, int *N1, int *M1, int *N2, int *M2, int *log2P)120120-{121121- struct nouveau_device *device = nouveau_dev(dev);122122- struct nouveau_bios *bios = nouveau_bios(device);123123- struct nouveau_clock *pclk = nouveau_clock(device);124124- struct nouveau_pll_vals coef;125125- int ret;126126-127127- ret = nvbios_pll_parse(bios, reg, pll);128128- if (ret)129129- return ret;130130-131131- if (clk < pll->vco1.max_freq)132132- pll->vco2.max_freq = 0;133133-134134- ret = pclk->pll_calc(pclk, pll, clk, &coef);135135- if (ret == 0)136136- return -ERANGE;137137-138138- *N1 = coef.N1;139139- *M1 = coef.M1;140140- if (N2 && M2) {141141- if (pll->vco2.max_freq) {142142- *N2 = coef.N2;143143- *M2 = coef.M2;144144- } else {145145- *N2 = 1;146146- *M2 = 1;147147- }148148- }149149- *log2P = coef.log2P;150150- return 0;151151-}152152-153153-void *154154-nv40_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)155155-{156156- struct nv40_pm_state *info;157157- struct nvbios_pll pll;158158- int N1, N2, M1, M2, log2P;159159- int ret;160160-161161- info = kmalloc(sizeof(*info), GFP_KERNEL);162162- if (!info)163163- return ERR_PTR(-ENOMEM);164164-165165- /* core/geometric clock */166166- ret = nv40_calc_pll(dev, 0x004000, &pll, perflvl->core,167167- &N1, &M1, &N2, &M2, &log2P);168168- if (ret < 0)169169- goto out;170170-171171- if (N2 == M2) {172172- info->npll_ctrl = 0x80000100 | (log2P << 16);173173- info->npll_coef = (N1 << 8) | M1;174174- } else {175175- info->npll_ctrl = 0xc0000000 | (log2P << 16);176176- info->npll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1;177177- }178178-179179- /* use the second PLL for shader/rop clock, if it differs from core */180180- if (perflvl->shader && perflvl->shader != perflvl->core) {181181- ret = nv40_calc_pll(dev, 0x004008, &pll, perflvl->shader,182182- &N1, &M1, NULL, NULL, &log2P);183183- if (ret < 0)184184- goto out;185185-186186- info->spll = 0xc0000000 | (log2P << 16) | (N1 << 8) | M1;187187- info->ctrl = 0x00000223;188188- } else {189189- info->spll = 0x00000000;190190- info->ctrl = 0x00000333;191191- }192192-193193- /* memory clock */194194- if (!perflvl->memory) {195195- info->mpll_ctrl = 0x00000000;196196- goto out;197197- }198198-199199- ret = nv40_calc_pll(dev, 0x004020, &pll, perflvl->memory,200200- &N1, &M1, &N2, &M2, &log2P);201201- if (ret < 0)202202- goto out;203203-204204- info->mpll_ctrl = 0x80000000 | (log2P << 16);205205- info->mpll_ctrl |= min2(pll.bias_p + log2P, pll.max_p) << 20;206206- if (N2 == M2) {207207- info->mpll_ctrl |= 0x00000100;208208- info->mpll_coef = (N1 << 8) | M1;209209- } else {210210- info->mpll_ctrl |= 0x40000000;211211- info->mpll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1;212212- }213213-214214-out:215215- if (ret < 0) {216216- kfree(info);217217- info = ERR_PTR(ret);218218- }219219- return info;220220-}221221-222222-static bool223223-nv40_pm_gr_idle(void *data)224224-{225225- struct drm_device *dev = data;226226- struct nouveau_device *device = nouveau_dev(dev);227227-228228- if ((nv_rd32(device, 0x400760) & 0x000000f0) >> 4 !=229229- (nv_rd32(device, 0x400760) & 0x0000000f))230230- return false;231231-232232- if (nv_rd32(device, 0x400700))233233- return false;234234-235235- return true;236236-}237237-238238-int239239-nv40_pm_clocks_set(struct drm_device *dev, void *pre_state)240240-{241241- struct nouveau_device *device = nouveau_dev(dev);242242- struct nouveau_fifo *pfifo = nouveau_fifo(device);243243- struct nouveau_drm *drm = nouveau_drm(dev);244244- struct nv40_pm_state *info = pre_state;245245- unsigned long flags;246246- struct bit_entry M;247247- u32 crtc_mask = 0;248248- u8 sr1[2];249249- int i, ret = -EAGAIN;250250-251251- /* determine which CRTCs are active, fetch VGA_SR1 for each */252252- for (i = 0; i < 2; i++) {253253- u32 vbl = nv_rd32(device, 0x600808 + (i * 0x2000));254254- u32 cnt = 0;255255- do {256256- if (vbl != nv_rd32(device, 0x600808 + (i * 0x2000))) {257257- nv_wr08(device, 0x0c03c4 + (i * 0x2000), 0x01);258258- sr1[i] = nv_rd08(device, 0x0c03c5 + (i * 0x2000));259259- if (!(sr1[i] & 0x20))260260- crtc_mask |= (1 << i);261261- break;262262- }263263- udelay(1);264264- } while (cnt++ < 32);265265- }266266-267267- /* halt and idle engines */268268- pfifo->pause(pfifo, &flags);269269-270270- if (!nv_wait_cb(device, nv40_pm_gr_idle, dev))271271- goto resume;272272-273273- ret = 0;274274-275275- /* set engine clocks */276276- nv_mask(device, 0x00c040, 0x00000333, 0x00000000);277277- nv_wr32(device, 0x004004, info->npll_coef);278278- nv_mask(device, 0x004000, 0xc0070100, info->npll_ctrl);279279- nv_mask(device, 0x004008, 0xc007ffff, info->spll);280280- mdelay(5);281281- nv_mask(device, 0x00c040, 0x00000333, info->ctrl);282282-283283- if (!info->mpll_ctrl)284284- goto resume;285285-286286- /* wait for vblank start on active crtcs, disable memory access */287287- for (i = 0; i < 2; i++) {288288- if (!(crtc_mask & (1 << i)))289289- continue;290290- nv_wait(device, 0x600808 + (i * 0x2000), 0x00010000, 0x00000000);291291- nv_wait(device, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000);292292- nv_wr08(device, 0x0c03c4 + (i * 0x2000), 0x01);293293- nv_wr08(device, 0x0c03c5 + (i * 0x2000), sr1[i] | 0x20);294294- }295295-296296- /* prepare ram for reclocking */297297- nv_wr32(device, 0x1002d4, 0x00000001); /* precharge */298298- nv_wr32(device, 0x1002d0, 0x00000001); /* refresh */299299- nv_wr32(device, 0x1002d0, 0x00000001); /* refresh */300300- nv_mask(device, 0x100210, 0x80000000, 0x00000000); /* no auto refresh */301301- nv_wr32(device, 0x1002dc, 0x00000001); /* enable self-refresh */302302-303303- /* change the PLL of each memory partition */304304- nv_mask(device, 0x00c040, 0x0000c000, 0x00000000);305305- switch (nv_device(drm->device)->chipset) {306306- case 0x40:307307- case 0x45:308308- case 0x41:309309- case 0x42:310310- case 0x47:311311- nv_mask(device, 0x004044, 0xc0771100, info->mpll_ctrl);312312- nv_mask(device, 0x00402c, 0xc0771100, info->mpll_ctrl);313313- nv_wr32(device, 0x004048, info->mpll_coef);314314- nv_wr32(device, 0x004030, info->mpll_coef);315315- case 0x43:316316- case 0x49:317317- case 0x4b:318318- nv_mask(device, 0x004038, 0xc0771100, info->mpll_ctrl);319319- nv_wr32(device, 0x00403c, info->mpll_coef);320320- default:321321- nv_mask(device, 0x004020, 0xc0771100, info->mpll_ctrl);322322- nv_wr32(device, 0x004024, info->mpll_coef);323323- break;324324- }325325- udelay(100);326326- nv_mask(device, 0x00c040, 0x0000c000, 0x0000c000);327327-328328- /* re-enable normal operation of memory controller */329329- nv_wr32(device, 0x1002dc, 0x00000000);330330- nv_mask(device, 0x100210, 0x80000000, 0x80000000);331331- udelay(100);332332-333333- /* execute memory reset script from vbios */334334- if (!bit_table(dev, 'M', &M))335335- nouveau_bios_run_init_table(dev, ROM16(M.data[0]), NULL, 0);336336-337337- /* make sure we're in vblank (hopefully the same one as before), and338338- * then re-enable crtc memory access339339- */340340- for (i = 0; i < 2; i++) {341341- if (!(crtc_mask & (1 << i)))342342- continue;343343- nv_wait(device, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000);344344- nv_wr08(device, 0x0c03c4 + (i * 0x2000), 0x01);345345- nv_wr08(device, 0x0c03c5 + (i * 0x2000), sr1[i]);346346- }347347-348348- /* resume engines */349349-resume:350350- pfifo->start(pfifo, &flags);351351- kfree(info);352352- return ret;353353-}
-855
drivers/gpu/drm/nouveau/nv50_pm.c
···11-/*22- * Copyright 2010 Red Hat Inc.33- *44- * Permission is hereby granted, free of charge, to any person obtaining a55- * copy of this software and associated documentation files (the "Software"),66- * to deal in the Software without restriction, including without limitation77- * the rights to use, copy, modify, merge, publish, distribute, sublicense,88- * and/or sell copies of the Software, and to permit persons to whom the99- * Software is furnished to do so, subject to the following conditions:1010- *1111- * The above copyright notice and this permission notice shall be included in1212- * all copies or substantial portions of the Software.1313- *1414- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR1515- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,1616- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL1717- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR1818- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,1919- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR2020- * OTHER DEALINGS IN THE SOFTWARE.2121- *2222- * Authors: Ben Skeggs2323- */2424-2525-#include <drm/drmP.h>2626-#include "nouveau_drm.h"2727-#include "nouveau_bios.h"2828-#include "dispnv04/hw.h"2929-#include "nouveau_pm.h"3030-#include "nouveau_hwsq.h"3131-3232-#include "nv50_display.h"3333-3434-#include <subdev/bios/pll.h>3535-#include <subdev/clock.h>3636-#include <subdev/timer.h>3737-#include <subdev/fb.h>3838-3939-enum clk_src {4040- clk_src_crystal,4141- clk_src_href,4242- clk_src_hclk,4343- clk_src_hclkm3,4444- clk_src_hclkm3d2,4545- clk_src_host,4646- clk_src_nvclk,4747- clk_src_sclk,4848- clk_src_mclk,4949- clk_src_vdec,5050- clk_src_dom65151-};5252-5353-static u32 read_clk(struct drm_device *, enum clk_src);5454-5555-static u325656-read_div(struct drm_device *dev)5757-{5858- struct nouveau_device *device = nouveau_dev(dev);5959- struct nouveau_drm *drm = nouveau_drm(dev);6060-6161- switch (nv_device(drm->device)->chipset) {6262- case 0x50: /* it exists, but only has bit 31, not the dividers.. */6363- case 0x84:6464- case 0x86:6565- case 0x98:6666- case 0xa0:6767- return nv_rd32(device, 0x004700);6868- case 0x92:6969- case 0x94:7070- case 0x96:7171- return nv_rd32(device, 0x004800);7272- default:7373- return 0x00000000;7474- }7575-}7676-7777-static u327878-read_pll_src(struct drm_device *dev, u32 base)7979-{8080- struct nouveau_device *device = nouveau_dev(dev);8181- struct nouveau_drm *drm = nouveau_drm(dev);8282- u32 coef, ref = read_clk(dev, clk_src_crystal);8383- u32 rsel = nv_rd32(device, 0x00e18c);8484- int P, N, M, id;8585-8686- switch (nv_device(drm->device)->chipset) {8787- case 0x50:8888- case 0xa0:8989- switch (base) {9090- case 0x4020:9191- case 0x4028: id = !!(rsel & 0x00000004); break;9292- case 0x4008: id = !!(rsel & 0x00000008); break;9393- case 0x4030: id = 0; break;9494- default:9595- NV_ERROR(drm, "ref: bad pll 0x%06x\n", base);9696- return 0;9797- }9898-9999- coef = nv_rd32(device, 0x00e81c + (id * 0x0c));100100- ref *= (coef & 0x01000000) ? 2 : 4;101101- P = (coef & 0x00070000) >> 16;102102- N = ((coef & 0x0000ff00) >> 8) + 1;103103- M = ((coef & 0x000000ff) >> 0) + 1;104104- break;105105- case 0x84:106106- case 0x86:107107- case 0x92:108108- coef = nv_rd32(device, 0x00e81c);109109- P = (coef & 0x00070000) >> 16;110110- N = (coef & 0x0000ff00) >> 8;111111- M = (coef & 0x000000ff) >> 0;112112- break;113113- case 0x94:114114- case 0x96:115115- case 0x98:116116- rsel = nv_rd32(device, 0x00c050);117117- switch (base) {118118- case 0x4020: rsel = (rsel & 0x00000003) >> 0; break;119119- case 0x4008: rsel = (rsel & 0x0000000c) >> 2; break;120120- case 0x4028: rsel = (rsel & 0x00001800) >> 11; break;121121- case 0x4030: rsel = 3; break;122122- default:123123- NV_ERROR(drm, "ref: bad pll 0x%06x\n", base);124124- return 0;125125- }126126-127127- switch (rsel) {128128- case 0: id = 1; break;129129- case 1: return read_clk(dev, clk_src_crystal);130130- case 2: return read_clk(dev, clk_src_href);131131- case 3: id = 0; break;132132- }133133-134134- coef = nv_rd32(device, 0x00e81c + (id * 0x28));135135- P = (nv_rd32(device, 0x00e824 + (id * 0x28)) >> 16) & 7;136136- P += (coef & 0x00070000) >> 16;137137- N = (coef & 0x0000ff00) >> 8;138138- M = (coef & 0x000000ff) >> 0;139139- break;140140- default:141141- BUG_ON(1);142142- }143143-144144- if (M)145145- return (ref * N / M) >> P;146146- return 0;147147-}148148-149149-static u32150150-read_pll_ref(struct drm_device *dev, u32 base)151151-{152152- struct nouveau_device *device = nouveau_dev(dev);153153- struct nouveau_drm *drm = nouveau_drm(dev);154154- u32 src, mast = nv_rd32(device, 0x00c040);155155-156156- switch (base) {157157- case 0x004028:158158- src = !!(mast & 0x00200000);159159- break;160160- case 0x004020:161161- src = !!(mast & 0x00400000);162162- break;163163- case 0x004008:164164- src = !!(mast & 0x00010000);165165- break;166166- case 0x004030:167167- src = !!(mast & 0x02000000);168168- break;169169- case 0x00e810:170170- return read_clk(dev, clk_src_crystal);171171- default:172172- NV_ERROR(drm, "bad pll 0x%06x\n", base);173173- return 0;174174- }175175-176176- if (src)177177- return read_clk(dev, clk_src_href);178178- return read_pll_src(dev, base);179179-}180180-181181-static u32182182-read_pll(struct drm_device *dev, u32 base)183183-{184184- struct nouveau_device *device = nouveau_dev(dev);185185- struct nouveau_drm *drm = nouveau_drm(dev);186186- u32 mast = nv_rd32(device, 0x00c040);187187- u32 ctrl = nv_rd32(device, base + 0);188188- u32 coef = nv_rd32(device, base + 4);189189- u32 ref = read_pll_ref(dev, base);190190- u32 clk = 0;191191- int N1, N2, M1, M2;192192-193193- if (base == 0x004028 && (mast & 0x00100000)) {194194- /* wtf, appears to only disable post-divider on nva0 */195195- if (nv_device(drm->device)->chipset != 0xa0)196196- return read_clk(dev, clk_src_dom6);197197- }198198-199199- N2 = (coef & 0xff000000) >> 24;200200- M2 = (coef & 0x00ff0000) >> 16;201201- N1 = (coef & 0x0000ff00) >> 8;202202- M1 = (coef & 0x000000ff);203203- if ((ctrl & 0x80000000) && M1) {204204- clk = ref * N1 / M1;205205- if ((ctrl & 0x40000100) == 0x40000000) {206206- if (M2)207207- clk = clk * N2 / M2;208208- else209209- clk = 0;210210- }211211- }212212-213213- return clk;214214-}215215-216216-static u32217217-read_clk(struct drm_device *dev, enum clk_src src)218218-{219219- struct nouveau_device *device = nouveau_dev(dev);220220- struct nouveau_drm *drm = nouveau_drm(dev);221221- u32 mast = nv_rd32(device, 0x00c040);222222- u32 P = 0;223223-224224- switch (src) {225225- case clk_src_crystal:226226- return device->crystal;227227- case clk_src_href:228228- return 100000; /* PCIE reference clock */229229- case clk_src_hclk:230230- return read_clk(dev, clk_src_href) * 27778 / 10000;231231- case clk_src_hclkm3:232232- return read_clk(dev, clk_src_hclk) * 3;233233- case clk_src_hclkm3d2:234234- return read_clk(dev, clk_src_hclk) * 3 / 2;235235- case clk_src_host:236236- switch (mast & 0x30000000) {237237- case 0x00000000: return read_clk(dev, clk_src_href);238238- case 0x10000000: break;239239- case 0x20000000: /* !0x50 */240240- case 0x30000000: return read_clk(dev, clk_src_hclk);241241- }242242- break;243243- case clk_src_nvclk:244244- if (!(mast & 0x00100000))245245- P = (nv_rd32(device, 0x004028) & 0x00070000) >> 16;246246- switch (mast & 0x00000003) {247247- case 0x00000000: return read_clk(dev, clk_src_crystal) >> P;248248- case 0x00000001: return read_clk(dev, clk_src_dom6);249249- case 0x00000002: return read_pll(dev, 0x004020) >> P;250250- case 0x00000003: return read_pll(dev, 0x004028) >> P;251251- }252252- break;253253- case clk_src_sclk:254254- P = (nv_rd32(device, 0x004020) & 0x00070000) >> 16;255255- switch (mast & 0x00000030) {256256- case 0x00000000:257257- if (mast & 0x00000080)258258- return read_clk(dev, clk_src_host) >> P;259259- return read_clk(dev, clk_src_crystal) >> P;260260- case 0x00000010: break;261261- case 0x00000020: return read_pll(dev, 0x004028) >> P;262262- case 0x00000030: return read_pll(dev, 0x004020) >> P;263263- }264264- break;265265- case clk_src_mclk:266266- P = (nv_rd32(device, 0x004008) & 0x00070000) >> 16;267267- if (nv_rd32(device, 0x004008) & 0x00000200) {268268- switch (mast & 0x0000c000) {269269- case 0x00000000:270270- return read_clk(dev, clk_src_crystal) >> P;271271- case 0x00008000:272272- case 0x0000c000:273273- return read_clk(dev, clk_src_href) >> P;274274- }275275- } else {276276- return read_pll(dev, 0x004008) >> P;277277- }278278- break;279279- case clk_src_vdec:280280- P = (read_div(dev) & 0x00000700) >> 8;281281- switch (nv_device(drm->device)->chipset) {282282- case 0x84:283283- case 0x86:284284- case 0x92:285285- case 0x94:286286- case 0x96:287287- case 0xa0:288288- switch (mast & 0x00000c00) {289289- case 0x00000000:290290- if (nv_device(drm->device)->chipset == 0xa0) /* wtf?? */291291- return read_clk(dev, clk_src_nvclk) >> P;292292- return read_clk(dev, clk_src_crystal) >> P;293293- case 0x00000400:294294- return 0;295295- case 0x00000800:296296- if (mast & 0x01000000)297297- return read_pll(dev, 0x004028) >> P;298298- return read_pll(dev, 0x004030) >> P;299299- case 0x00000c00:300300- return read_clk(dev, clk_src_nvclk) >> P;301301- }302302- break;303303- case 0x98:304304- switch (mast & 0x00000c00) {305305- case 0x00000000:306306- return read_clk(dev, clk_src_nvclk) >> P;307307- case 0x00000400:308308- return 0;309309- case 0x00000800:310310- return read_clk(dev, clk_src_hclkm3d2) >> P;311311- case 0x00000c00:312312- return read_clk(dev, clk_src_mclk) >> P;313313- }314314- break;315315- }316316- break;317317- case clk_src_dom6:318318- switch (nv_device(drm->device)->chipset) {319319- case 0x50:320320- case 0xa0:321321- return read_pll(dev, 0x00e810) >> 2;322322- case 0x84:323323- case 0x86:324324- case 0x92:325325- case 0x94:326326- case 0x96:327327- case 0x98:328328- P = (read_div(dev) & 0x00000007) >> 0;329329- switch (mast & 0x0c000000) {330330- case 0x00000000: return read_clk(dev, clk_src_href);331331- case 0x04000000: break;332332- case 0x08000000: return read_clk(dev, clk_src_hclk);333333- case 0x0c000000:334334- return read_clk(dev, clk_src_hclkm3) >> P;335335- }336336- break;337337- default:338338- break;339339- }340340- default:341341- break;342342- }343343-344344- NV_DEBUG(drm, "unknown clock source %d 0x%08x\n", src, mast);345345- return 0;346346-}347347-348348-int349349-nv50_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)350350-{351351- struct nouveau_drm *drm = nouveau_drm(dev);352352- if (nv_device(drm->device)->chipset == 0xaa ||353353- nv_device(drm->device)->chipset == 0xac)354354- return 0;355355-356356- perflvl->core = read_clk(dev, clk_src_nvclk);357357- perflvl->shader = read_clk(dev, clk_src_sclk);358358- perflvl->memory = read_clk(dev, clk_src_mclk);359359- if (nv_device(drm->device)->chipset != 0x50) {360360- perflvl->vdec = read_clk(dev, clk_src_vdec);361361- perflvl->dom6 = read_clk(dev, clk_src_dom6);362362- }363363-364364- return 0;365365-}366366-367367-struct nv50_pm_state {368368- struct nouveau_pm_level *perflvl;369369- struct hwsq_ucode eclk_hwsq;370370- struct hwsq_ucode mclk_hwsq;371371- u32 mscript;372372- u32 mmast;373373- u32 mctrl;374374- u32 mcoef;375375-};376376-377377-static u32378378-calc_pll(struct drm_device *dev, u32 reg, struct nvbios_pll *pll,379379- u32 clk, int *N1, int *M1, int *log2P)380380-{381381- struct nouveau_device *device = nouveau_dev(dev);382382- struct nouveau_bios *bios = nouveau_bios(device);383383- struct nouveau_clock *pclk = nouveau_clock(device);384384- struct nouveau_pll_vals coef;385385- int ret;386386-387387- ret = nvbios_pll_parse(bios, reg, pll);388388- if (ret)389389- return 0;390390-391391- pll->vco2.max_freq = 0;392392- pll->refclk = read_pll_ref(dev, reg);393393- if (!pll->refclk)394394- return 0;395395-396396- ret = pclk->pll_calc(pclk, pll, clk, &coef);397397- if (ret == 0)398398- return 0;399399-400400- *N1 = coef.N1;401401- *M1 = coef.M1;402402- *log2P = coef.log2P;403403- return ret;404404-}405405-406406-static inline u32407407-calc_div(u32 src, u32 target, int *div)408408-{409409- u32 clk0 = src, clk1 = src;410410- for (*div = 0; *div <= 7; (*div)++) {411411- if (clk0 <= target) {412412- clk1 = clk0 << (*div ? 1 : 0);413413- break;414414- }415415- clk0 >>= 1;416416- }417417-418418- if (target - clk0 <= clk1 - target)419419- return clk0;420420- (*div)--;421421- return clk1;422422-}423423-424424-static inline u32425425-clk_same(u32 a, u32 b)426426-{427427- return ((a / 1000) == (b / 1000));428428-}429429-430430-static void431431-mclk_precharge(struct nouveau_mem_exec_func *exec)432432-{433433- struct nv50_pm_state *info = exec->priv;434434- struct hwsq_ucode *hwsq = &info->mclk_hwsq;435435-436436- hwsq_wr32(hwsq, 0x1002d4, 0x00000001);437437-}438438-439439-static void440440-mclk_refresh(struct nouveau_mem_exec_func *exec)441441-{442442- struct nv50_pm_state *info = exec->priv;443443- struct hwsq_ucode *hwsq = &info->mclk_hwsq;444444-445445- hwsq_wr32(hwsq, 0x1002d0, 0x00000001);446446-}447447-448448-static void449449-mclk_refresh_auto(struct nouveau_mem_exec_func *exec, bool enable)450450-{451451- struct nv50_pm_state *info = exec->priv;452452- struct hwsq_ucode *hwsq = &info->mclk_hwsq;453453-454454- hwsq_wr32(hwsq, 0x100210, enable ? 0x80000000 : 0x00000000);455455-}456456-457457-static void458458-mclk_refresh_self(struct nouveau_mem_exec_func *exec, bool enable)459459-{460460- struct nv50_pm_state *info = exec->priv;461461- struct hwsq_ucode *hwsq = &info->mclk_hwsq;462462-463463- hwsq_wr32(hwsq, 0x1002dc, enable ? 0x00000001 : 0x00000000);464464-}465465-466466-static void467467-mclk_wait(struct nouveau_mem_exec_func *exec, u32 nsec)468468-{469469- struct nv50_pm_state *info = exec->priv;470470- struct hwsq_ucode *hwsq = &info->mclk_hwsq;471471-472472- if (nsec > 1000)473473- hwsq_usec(hwsq, (nsec + 500) / 1000);474474-}475475-476476-static u32477477-mclk_mrg(struct nouveau_mem_exec_func *exec, int mr)478478-{479479- struct nouveau_device *device = nouveau_dev(exec->dev);480480- if (mr <= 1)481481- return nv_rd32(device, 0x1002c0 + ((mr - 0) * 4));482482- if (mr <= 3)483483- return nv_rd32(device, 0x1002e0 + ((mr - 2) * 4));484484- return 0;485485-}486486-487487-static void488488-mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data)489489-{490490- struct nouveau_device *device = nouveau_dev(exec->dev);491491- struct nouveau_fb *pfb = nouveau_fb(device);492492- struct nv50_pm_state *info = exec->priv;493493- struct hwsq_ucode *hwsq = &info->mclk_hwsq;494494-495495- if (mr <= 1) {496496- if (pfb->ram->ranks > 1)497497- hwsq_wr32(hwsq, 0x1002c8 + ((mr - 0) * 4), data);498498- hwsq_wr32(hwsq, 0x1002c0 + ((mr - 0) * 4), data);499499- } else500500- if (mr <= 3) {501501- if (pfb->ram->ranks > 1)502502- hwsq_wr32(hwsq, 0x1002e8 + ((mr - 2) * 4), data);503503- hwsq_wr32(hwsq, 0x1002e0 + ((mr - 2) * 4), data);504504- }505505-}506506-507507-static void508508-mclk_clock_set(struct nouveau_mem_exec_func *exec)509509-{510510- struct nouveau_device *device = nouveau_dev(exec->dev);511511- struct nv50_pm_state *info = exec->priv;512512- struct hwsq_ucode *hwsq = &info->mclk_hwsq;513513- u32 ctrl = nv_rd32(device, 0x004008);514514-515515- info->mmast = nv_rd32(device, 0x00c040);516516- info->mmast &= ~0xc0000000; /* get MCLK_2 from HREF */517517- info->mmast |= 0x0000c000; /* use MCLK_2 as MPLL_BYPASS clock */518518-519519- hwsq_wr32(hwsq, 0xc040, info->mmast);520520- hwsq_wr32(hwsq, 0x4008, ctrl | 0x00000200); /* bypass MPLL */521521- if (info->mctrl & 0x80000000)522522- hwsq_wr32(hwsq, 0x400c, info->mcoef);523523- hwsq_wr32(hwsq, 0x4008, info->mctrl);524524-}525525-526526-static void527527-mclk_timing_set(struct nouveau_mem_exec_func *exec)528528-{529529- struct nouveau_device *device = nouveau_dev(exec->dev);530530- struct nv50_pm_state *info = exec->priv;531531- struct nouveau_pm_level *perflvl = info->perflvl;532532- struct hwsq_ucode *hwsq = &info->mclk_hwsq;533533- int i;534534-535535- for (i = 0; i < 9; i++) {536536- u32 reg = 0x100220 + (i * 4);537537- u32 val = nv_rd32(device, reg);538538- if (val != perflvl->timing.reg[i])539539- hwsq_wr32(hwsq, reg, perflvl->timing.reg[i]);540540- }541541-}542542-543543-static int544544-calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl,545545- struct nv50_pm_state *info)546546-{547547- struct nouveau_drm *drm = nouveau_drm(dev);548548- struct nouveau_device *device = nouveau_dev(dev);549549- u32 crtc_mask = 0; /*XXX: nv50_display_active_crtcs(dev); */550550- struct nouveau_mem_exec_func exec = {551551- .dev = dev,552552- .precharge = mclk_precharge,553553- .refresh = mclk_refresh,554554- .refresh_auto = mclk_refresh_auto,555555- .refresh_self = mclk_refresh_self,556556- .wait = mclk_wait,557557- .mrg = mclk_mrg,558558- .mrs = mclk_mrs,559559- .clock_set = mclk_clock_set,560560- .timing_set = mclk_timing_set,561561- .priv = info562562- };563563- struct hwsq_ucode *hwsq = &info->mclk_hwsq;564564- struct nvbios_pll pll;565565- int N, M, P;566566- int ret;567567-568568- /* use pcie refclock if possible, otherwise use mpll */569569- info->mctrl = nv_rd32(device, 0x004008);570570- info->mctrl &= ~0x81ff0200;571571- if (clk_same(perflvl->memory, read_clk(dev, clk_src_href))) {572572- info->mctrl |= 0x00000200 | (pll.bias_p << 19);573573- } else {574574- ret = calc_pll(dev, 0x4008, &pll, perflvl->memory, &N, &M, &P);575575- if (ret == 0)576576- return -EINVAL;577577-578578- info->mctrl |= 0x80000000 | (P << 22) | (P << 16);579579- info->mctrl |= pll.bias_p << 19;580580- info->mcoef = (N << 8) | M;581581- }582582-583583- /* build the ucode which will reclock the memory for us */584584- hwsq_init(hwsq);585585- if (crtc_mask) {586586- hwsq_op5f(hwsq, crtc_mask, 0x00); /* wait for scanout */587587- hwsq_op5f(hwsq, crtc_mask, 0x01); /* wait for vblank */588588- }589589- if (nv_device(drm->device)->chipset >= 0x92)590590- hwsq_wr32(hwsq, 0x611200, 0x00003300); /* disable scanout */591591- hwsq_setf(hwsq, 0x10, 0); /* disable bus access */592592- hwsq_op5f(hwsq, 0x00, 0x01); /* no idea :s */593593-594594- ret = nouveau_mem_exec(&exec, perflvl);595595- if (ret)596596- return ret;597597-598598- hwsq_setf(hwsq, 0x10, 1); /* enable bus access */599599- hwsq_op5f(hwsq, 0x00, 0x00); /* no idea, reverse of 0x00, 0x01? */600600- if (nv_device(drm->device)->chipset >= 0x92)601601- hwsq_wr32(hwsq, 0x611200, 0x00003330); /* enable scanout */602602- hwsq_fini(hwsq);603603- return 0;604604-}605605-606606-void *607607-nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)608608-{609609- struct nouveau_device *device = nouveau_dev(dev);610610- struct nouveau_drm *drm = nouveau_drm(dev);611611- struct nv50_pm_state *info;612612- struct hwsq_ucode *hwsq;613613- struct nvbios_pll pll;614614- u32 out, mast, divs, ctrl;615615- int clk, ret = -EINVAL;616616- int N, M, P1, P2;617617-618618- if (nv_device(drm->device)->chipset == 0xaa ||619619- nv_device(drm->device)->chipset == 0xac)620620- return ERR_PTR(-ENODEV);621621-622622- info = kmalloc(sizeof(*info), GFP_KERNEL);623623- if (!info)624624- return ERR_PTR(-ENOMEM);625625- info->perflvl = perflvl;626626-627627- /* memory: build hwsq ucode which we'll use to reclock memory.628628- * use pcie refclock if possible, otherwise use mpll */629629- info->mclk_hwsq.len = 0;630630- if (perflvl->memory) {631631- ret = calc_mclk(dev, perflvl, info);632632- if (ret)633633- goto error;634634- info->mscript = perflvl->memscript;635635- }636636-637637- divs = read_div(dev);638638- mast = info->mmast;639639-640640- /* start building HWSQ script for engine reclocking */641641- hwsq = &info->eclk_hwsq;642642- hwsq_init(hwsq);643643- hwsq_setf(hwsq, 0x10, 0); /* disable bus access */644644- hwsq_op5f(hwsq, 0x00, 0x01); /* wait for access disabled? */645645-646646- /* vdec/dom6: switch to "safe" clocks temporarily */647647- if (perflvl->vdec) {648648- mast &= ~0x00000c00;649649- divs &= ~0x00000700;650650- }651651-652652- if (perflvl->dom6) {653653- mast &= ~0x0c000000;654654- divs &= ~0x00000007;655655- }656656-657657- hwsq_wr32(hwsq, 0x00c040, mast);658658-659659- /* vdec: avoid modifying xpll until we know exactly how the other660660- * clock domains work, i suspect at least some of them can also be661661- * tied to xpll...662662- */663663- if (perflvl->vdec) {664664- /* see how close we can get using nvclk as a source */665665- clk = calc_div(perflvl->core, perflvl->vdec, &P1);666666-667667- /* see how close we can get using xpll/hclk as a source */668668- if (nv_device(drm->device)->chipset != 0x98)669669- out = read_pll(dev, 0x004030);670670- else671671- out = read_clk(dev, clk_src_hclkm3d2);672672- out = calc_div(out, perflvl->vdec, &P2);673673-674674- /* select whichever gets us closest */675675- if (abs((int)perflvl->vdec - clk) <=676676- abs((int)perflvl->vdec - out)) {677677- if (nv_device(drm->device)->chipset != 0x98)678678- mast |= 0x00000c00;679679- divs |= P1 << 8;680680- } else {681681- mast |= 0x00000800;682682- divs |= P2 << 8;683683- }684684- }685685-686686- /* dom6: nfi what this is, but we're limited to various combinations687687- * of the host clock frequency688688- */689689- if (perflvl->dom6) {690690- if (clk_same(perflvl->dom6, read_clk(dev, clk_src_href))) {691691- mast |= 0x00000000;692692- } else693693- if (clk_same(perflvl->dom6, read_clk(dev, clk_src_hclk))) {694694- mast |= 0x08000000;695695- } else {696696- clk = read_clk(dev, clk_src_hclk) * 3;697697- clk = calc_div(clk, perflvl->dom6, &P1);698698-699699- mast |= 0x0c000000;700700- divs |= P1;701701- }702702- }703703-704704- /* vdec/dom6: complete switch to new clocks */705705- switch (nv_device(drm->device)->chipset) {706706- case 0x92:707707- case 0x94:708708- case 0x96:709709- hwsq_wr32(hwsq, 0x004800, divs);710710- break;711711- default:712712- hwsq_wr32(hwsq, 0x004700, divs);713713- break;714714- }715715-716716- hwsq_wr32(hwsq, 0x00c040, mast);717717-718718- /* core/shader: make sure sclk/nvclk are disconnected from their719719- * PLLs (nvclk to dom6, sclk to hclk)720720- */721721- if (nv_device(drm->device)->chipset < 0x92)722722- mast = (mast & ~0x001000b0) | 0x00100080;723723- else724724- mast = (mast & ~0x000000b3) | 0x00000081;725725-726726- hwsq_wr32(hwsq, 0x00c040, mast);727727-728728- /* core: for the moment at least, always use nvpll */729729- clk = calc_pll(dev, 0x4028, &pll, perflvl->core, &N, &M, &P1);730730- if (clk == 0)731731- goto error;732732-733733- ctrl = nv_rd32(device, 0x004028) & ~0xc03f0100;734734- mast &= ~0x00100000;735735- mast |= 3;736736-737737- hwsq_wr32(hwsq, 0x004028, 0x80000000 | (P1 << 19) | (P1 << 16) | ctrl);738738- hwsq_wr32(hwsq, 0x00402c, (N << 8) | M);739739-740740- /* shader: tie to nvclk if possible, otherwise use spll. have to be741741- * very careful that the shader clock is at least twice the core, or742742- * some chipsets will be very unhappy. i expect most or all of these743743- * cases will be handled by tying to nvclk, but it's possible there's744744- * corners745745- */746746- ctrl = nv_rd32(device, 0x004020) & ~0xc03f0100;747747-748748- if (P1-- && perflvl->shader == (perflvl->core << 1)) {749749- hwsq_wr32(hwsq, 0x004020, (P1 << 19) | (P1 << 16) | ctrl);750750- hwsq_wr32(hwsq, 0x00c040, 0x00000020 | mast);751751- } else {752752- clk = calc_pll(dev, 0x4020, &pll, perflvl->shader, &N, &M, &P1);753753- if (clk == 0)754754- goto error;755755- ctrl |= 0x80000000;756756-757757- hwsq_wr32(hwsq, 0x004020, (P1 << 19) | (P1 << 16) | ctrl);758758- hwsq_wr32(hwsq, 0x004024, (N << 8) | M);759759- hwsq_wr32(hwsq, 0x00c040, 0x00000030 | mast);760760- }761761-762762- hwsq_setf(hwsq, 0x10, 1); /* enable bus access */763763- hwsq_op5f(hwsq, 0x00, 0x00); /* wait for access enabled? */764764- hwsq_fini(hwsq);765765-766766- return info;767767-error:768768- kfree(info);769769- return ERR_PTR(ret);770770-}771771-772772-static int773773-prog_hwsq(struct drm_device *dev, struct hwsq_ucode *hwsq)774774-{775775- struct nouveau_device *device = nouveau_dev(dev);776776- struct nouveau_drm *drm = nouveau_drm(dev);777777- u32 hwsq_data, hwsq_kick;778778- int i;779779-780780- if (nv_device(drm->device)->chipset < 0x94) {781781- hwsq_data = 0x001400;782782- hwsq_kick = 0x00000003;783783- } else {784784- hwsq_data = 0x080000;785785- hwsq_kick = 0x00000001;786786- }787787- /* upload hwsq ucode */788788- nv_mask(device, 0x001098, 0x00000008, 0x00000000);789789- nv_wr32(device, 0x001304, 0x00000000);790790- if (nv_device(drm->device)->chipset >= 0x92)791791- nv_wr32(device, 0x001318, 0x00000000);792792- for (i = 0; i < hwsq->len / 4; i++)793793- nv_wr32(device, hwsq_data + (i * 4), hwsq->ptr.u32[i]);794794- nv_mask(device, 0x001098, 0x00000018, 0x00000018);795795-796796- /* launch, and wait for completion */797797- nv_wr32(device, 0x00130c, hwsq_kick);798798- if (!nv_wait(device, 0x001308, 0x00000100, 0x00000000)) {799799- NV_ERROR(drm, "hwsq ucode exec timed out\n");800800- NV_ERROR(drm, "0x001308: 0x%08x\n", nv_rd32(device, 0x001308));801801- for (i = 0; i < hwsq->len / 4; i++) {802802- NV_ERROR(drm, "0x%06x: 0x%08x\n", 0x1400 + (i * 4),803803- nv_rd32(device, 0x001400 + (i * 4)));804804- }805805-806806- return -EIO;807807- }808808-809809- return 0;810810-}811811-812812-int813813-nv50_pm_clocks_set(struct drm_device *dev, void *data)814814-{815815- struct nouveau_device *device = nouveau_dev(dev);816816- struct nv50_pm_state *info = data;817817- struct bit_entry M;818818- int ret = -EBUSY;819819-820820- /* halt and idle execution engines */821821- nv_mask(device, 0x002504, 0x00000001, 0x00000001);822822- if (!nv_wait(device, 0x002504, 0x00000010, 0x00000010))823823- goto resume;824824- if (!nv_wait(device, 0x00251c, 0x0000003f, 0x0000003f))825825- goto resume;826826-827827- /* program memory clock, if necessary - must come before engine clock828828- * reprogramming due to how we construct the hwsq scripts in pre()829829- */830830-#define nouveau_bios_init_exec(a,b) nouveau_bios_run_init_table((a), (b), NULL, 0)831831- if (info->mclk_hwsq.len) {832832- /* execute some scripts that do ??? from the vbios.. */833833- if (!bit_table(dev, 'M', &M) && M.version == 1) {834834- if (M.length >= 6)835835- nouveau_bios_init_exec(dev, ROM16(M.data[5]));836836- if (M.length >= 8)837837- nouveau_bios_init_exec(dev, ROM16(M.data[7]));838838- if (M.length >= 10)839839- nouveau_bios_init_exec(dev, ROM16(M.data[9]));840840- nouveau_bios_init_exec(dev, info->mscript);841841- }842842-843843- ret = prog_hwsq(dev, &info->mclk_hwsq);844844- if (ret)845845- goto resume;846846- }847847-848848- /* program engine clocks */849849- ret = prog_hwsq(dev, &info->eclk_hwsq);850850-851851-resume:852852- nv_mask(device, 0x002504, 0x00000001, 0x00000000);853853- kfree(info);854854- return ret;855855-}
-624
drivers/gpu/drm/nouveau/nva3_pm.c
···11-/*22- * Copyright 2010 Red Hat Inc.33- *44- * Permission is hereby granted, free of charge, to any person obtaining a55- * copy of this software and associated documentation files (the "Software"),66- * to deal in the Software without restriction, including without limitation77- * the rights to use, copy, modify, merge, publish, distribute, sublicense,88- * and/or sell copies of the Software, and to permit persons to whom the99- * Software is furnished to do so, subject to the following conditions:1010- *1111- * The above copyright notice and this permission notice shall be included in1212- * all copies or substantial portions of the Software.1313- *1414- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR1515- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,1616- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL1717- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR1818- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,1919- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR2020- * OTHER DEALINGS IN THE SOFTWARE.2121- *2222- * Authors: Ben Skeggs2323- */2424-2525-#include <drm/drmP.h>2626-#include "nouveau_drm.h"2727-#include "nouveau_bios.h"2828-#include "nouveau_pm.h"2929-3030-#include <subdev/bios/pll.h>3131-#include <subdev/bios.h>3232-#include <subdev/clock.h>3333-#include <subdev/timer.h>3434-#include <subdev/fb.h>3535-3636-static u32 read_clk(struct drm_device *, int, bool);3737-static u32 read_pll(struct drm_device *, int, u32);3838-3939-static u324040-read_vco(struct drm_device *dev, int clk)4141-{4242- struct nouveau_device *device = nouveau_dev(dev);4343- u32 sctl = nv_rd32(device, 0x4120 + (clk * 4));4444- if ((sctl & 0x00000030) != 0x00000030)4545- return read_pll(dev, 0x41, 0x00e820);4646- return read_pll(dev, 0x42, 0x00e8a0);4747-}4848-4949-static u325050-read_clk(struct drm_device *dev, int clk, bool ignore_en)5151-{5252- struct nouveau_device *device = nouveau_dev(dev);5353- struct nouveau_drm *drm = nouveau_drm(dev);5454- u32 sctl, sdiv, sclk;5555-5656- /* refclk for the 0xe8xx plls is a fixed frequency */5757- if (clk >= 0x40) {5858- if (nv_device(drm->device)->chipset == 0xaf) {5959- /* no joke.. seriously.. sigh.. */6060- return nv_rd32(device, 0x00471c) * 1000;6161- }6262-6363- return device->crystal;6464- }6565-6666- sctl = nv_rd32(device, 0x4120 + (clk * 4));6767- if (!ignore_en && !(sctl & 0x00000100))6868- return 0;6969-7070- switch (sctl & 0x00003000) {7171- case 0x00000000:7272- return device->crystal;7373- case 0x00002000:7474- if (sctl & 0x00000040)7575- return 108000;7676- return 100000;7777- case 0x00003000:7878- sclk = read_vco(dev, clk);7979- sdiv = ((sctl & 0x003f0000) >> 16) + 2;8080- return (sclk * 2) / sdiv;8181- default:8282- return 0;8383- }8484-}8585-8686-static u328787-read_pll(struct drm_device *dev, int clk, u32 pll)8888-{8989- struct nouveau_device *device = nouveau_dev(dev);9090- u32 ctrl = nv_rd32(device, pll + 0);9191- u32 sclk = 0, P = 1, N = 1, M = 1;9292-9393- if (!(ctrl & 0x00000008)) {9494- if (ctrl & 0x00000001) {9595- u32 coef = nv_rd32(device, pll + 4);9696- M = (coef & 0x000000ff) >> 0;9797- N = (coef & 0x0000ff00) >> 8;9898- P = (coef & 0x003f0000) >> 16;9999-100100- /* no post-divider on these.. */101101- if ((pll & 0x00ff00) == 0x00e800)102102- P = 1;103103-104104- sclk = read_clk(dev, 0x00 + clk, false);105105- }106106- } else {107107- sclk = read_clk(dev, 0x10 + clk, false);108108- }109109-110110- if (M * P)111111- return sclk * N / (M * P);112112- return 0;113113-}114114-115115-struct creg {116116- u32 clk;117117- u32 pll;118118-};119119-120120-static int121121-calc_clk(struct drm_device *dev, int clk, u32 pll, u32 khz, struct creg *reg)122122-{123123- struct nouveau_drm *drm = nouveau_drm(dev);124124- struct nouveau_device *device = nouveau_dev(dev);125125- struct nouveau_bios *bios = nouveau_bios(device);126126- struct nvbios_pll limits;127127- u32 oclk, sclk, sdiv;128128- int P, N, M, diff;129129- int ret;130130-131131- reg->pll = 0;132132- reg->clk = 0;133133- if (!khz) {134134- NV_DEBUG(drm, "no clock for 0x%04x/0x%02x\n", pll, clk);135135- return 0;136136- }137137-138138- switch (khz) {139139- case 27000:140140- reg->clk = 0x00000100;141141- return khz;142142- case 100000:143143- reg->clk = 0x00002100;144144- return khz;145145- case 108000:146146- reg->clk = 0x00002140;147147- return khz;148148- default:149149- sclk = read_vco(dev, clk);150150- sdiv = min((sclk * 2) / (khz - 2999), (u32)65);151151- /* if the clock has a PLL attached, and we can get a within152152- * [-2, 3) MHz of a divider, we'll disable the PLL and use153153- * the divider instead.154154- *155155- * divider can go as low as 2, limited here because NVIDIA156156- * and the VBIOS on my NVA8 seem to prefer using the PLL157157- * for 810MHz - is there a good reason?158158- */159159- if (sdiv > 4) {160160- oclk = (sclk * 2) / sdiv;161161- diff = khz - oclk;162162- if (!pll || (diff >= -2000 && diff < 3000)) {163163- reg->clk = (((sdiv - 2) << 16) | 0x00003100);164164- return oclk;165165- }166166- }167167-168168- if (!pll) {169169- NV_ERROR(drm, "bad freq %02x: %d %d\n", clk, khz, sclk);170170- return -ERANGE;171171- }172172-173173- break;174174- }175175-176176- ret = nvbios_pll_parse(bios, pll, &limits);177177- if (ret)178178- return ret;179179-180180- limits.refclk = read_clk(dev, clk - 0x10, true);181181- if (!limits.refclk)182182- return -EINVAL;183183-184184- ret = nva3_calc_pll(dev, &limits, khz, &N, NULL, &M, &P);185185- if (ret >= 0) {186186- reg->clk = nv_rd32(device, 0x4120 + (clk * 4));187187- reg->pll = (P << 16) | (N << 8) | M;188188- }189189-190190- return ret;191191-}192192-193193-static void194194-prog_pll(struct drm_device *dev, int clk, u32 pll, struct creg *reg)195195-{196196- struct nouveau_device *device = nouveau_dev(dev);197197- struct nouveau_drm *drm = nouveau_drm(dev);198198- const u32 src0 = 0x004120 + (clk * 4);199199- const u32 src1 = 0x004160 + (clk * 4);200200- const u32 ctrl = pll + 0;201201- const u32 coef = pll + 4;202202-203203- if (!reg->clk && !reg->pll) {204204- NV_DEBUG(drm, "no clock for %02x\n", clk);205205- return;206206- }207207-208208- if (reg->pll) {209209- nv_mask(device, src0, 0x00000101, 0x00000101);210210- nv_wr32(device, coef, reg->pll);211211- nv_mask(device, ctrl, 0x00000015, 0x00000015);212212- nv_mask(device, ctrl, 0x00000010, 0x00000000);213213- nv_wait(device, ctrl, 0x00020000, 0x00020000);214214- nv_mask(device, ctrl, 0x00000010, 0x00000010);215215- nv_mask(device, ctrl, 0x00000008, 0x00000000);216216- nv_mask(device, src1, 0x00000100, 0x00000000);217217- nv_mask(device, src1, 0x00000001, 0x00000000);218218- } else {219219- nv_mask(device, src1, 0x003f3141, 0x00000101 | reg->clk);220220- nv_mask(device, ctrl, 0x00000018, 0x00000018);221221- udelay(20);222222- nv_mask(device, ctrl, 0x00000001, 0x00000000);223223- nv_mask(device, src0, 0x00000100, 0x00000000);224224- nv_mask(device, src0, 0x00000001, 0x00000000);225225- }226226-}227227-228228-static void229229-prog_clk(struct drm_device *dev, int clk, struct creg *reg)230230-{231231- struct nouveau_device *device = nouveau_dev(dev);232232- struct nouveau_drm *drm = nouveau_drm(dev);233233-234234- if (!reg->clk) {235235- NV_DEBUG(drm, "no clock for %02x\n", clk);236236- return;237237- }238238-239239- nv_mask(device, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | reg->clk);240240-}241241-242242-int243243-nva3_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)244244-{245245- perflvl->core = read_pll(dev, 0x00, 0x4200);246246- perflvl->shader = read_pll(dev, 0x01, 0x4220);247247- perflvl->memory = read_pll(dev, 0x02, 0x4000);248248- perflvl->unka0 = read_clk(dev, 0x20, false);249249- perflvl->vdec = read_clk(dev, 0x21, false);250250- perflvl->daemon = read_clk(dev, 0x25, false);251251- perflvl->copy = perflvl->core;252252- return 0;253253-}254254-255255-struct nva3_pm_state {256256- struct nouveau_pm_level *perflvl;257257-258258- struct creg nclk;259259- struct creg sclk;260260- struct creg vdec;261261- struct creg unka0;262262-263263- struct creg mclk;264264- u8 *rammap;265265- u8 rammap_ver;266266- u8 rammap_len;267267- u8 *ramcfg;268268- u8 ramcfg_len;269269- u32 r004018;270270- u32 r100760;271271-};272272-273273-void *274274-nva3_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)275275-{276276- struct nva3_pm_state *info;277277- u8 ramcfg_cnt;278278- int ret;279279-280280- info = kzalloc(sizeof(*info), GFP_KERNEL);281281- if (!info)282282- return ERR_PTR(-ENOMEM);283283-284284- ret = calc_clk(dev, 0x10, 0x4200, perflvl->core, &info->nclk);285285- if (ret < 0)286286- goto out;287287-288288- ret = calc_clk(dev, 0x11, 0x4220, perflvl->shader, &info->sclk);289289- if (ret < 0)290290- goto out;291291-292292- ret = calc_clk(dev, 0x12, 0x4000, perflvl->memory, &info->mclk);293293- if (ret < 0)294294- goto out;295295-296296- ret = calc_clk(dev, 0x20, 0x0000, perflvl->unka0, &info->unka0);297297- if (ret < 0)298298- goto out;299299-300300- ret = calc_clk(dev, 0x21, 0x0000, perflvl->vdec, &info->vdec);301301- if (ret < 0)302302- goto out;303303-304304- info->rammap = nouveau_perf_rammap(dev, perflvl->memory,305305- &info->rammap_ver,306306- &info->rammap_len,307307- &ramcfg_cnt, &info->ramcfg_len);308308- if (info->rammap_ver != 0x10 || info->rammap_len < 5)309309- info->rammap = NULL;310310-311311- info->ramcfg = nouveau_perf_ramcfg(dev, perflvl->memory,312312- &info->rammap_ver,313313- &info->ramcfg_len);314314- if (info->rammap_ver != 0x10)315315- info->ramcfg = NULL;316316-317317- info->perflvl = perflvl;318318-out:319319- if (ret < 0) {320320- kfree(info);321321- info = ERR_PTR(ret);322322- }323323- return info;324324-}325325-326326-static bool327327-nva3_pm_grcp_idle(void *data)328328-{329329- struct drm_device *dev = data;330330- struct nouveau_device *device = nouveau_dev(dev);331331-332332- if (!(nv_rd32(device, 0x400304) & 0x00000001))333333- return true;334334- if (nv_rd32(device, 0x400308) == 0x0050001c)335335- return true;336336- return false;337337-}338338-339339-static void340340-mclk_precharge(struct nouveau_mem_exec_func *exec)341341-{342342- struct nouveau_device *device = nouveau_dev(exec->dev);343343- nv_wr32(device, 0x1002d4, 0x00000001);344344-}345345-346346-static void347347-mclk_refresh(struct nouveau_mem_exec_func *exec)348348-{349349- struct nouveau_device *device = nouveau_dev(exec->dev);350350- nv_wr32(device, 0x1002d0, 0x00000001);351351-}352352-353353-static void354354-mclk_refresh_auto(struct nouveau_mem_exec_func *exec, bool enable)355355-{356356- struct nouveau_device *device = nouveau_dev(exec->dev);357357- nv_wr32(device, 0x100210, enable ? 0x80000000 : 0x00000000);358358-}359359-360360-static void361361-mclk_refresh_self(struct nouveau_mem_exec_func *exec, bool enable)362362-{363363- struct nouveau_device *device = nouveau_dev(exec->dev);364364- nv_wr32(device, 0x1002dc, enable ? 0x00000001 : 0x00000000);365365-}366366-367367-static void368368-mclk_wait(struct nouveau_mem_exec_func *exec, u32 nsec)369369-{370370- struct nouveau_device *device = nouveau_dev(exec->dev);371371- volatile u32 post = nv_rd32(device, 0); (void)post;372372- udelay((nsec + 500) / 1000);373373-}374374-375375-static u32376376-mclk_mrg(struct nouveau_mem_exec_func *exec, int mr)377377-{378378- struct nouveau_device *device = nouveau_dev(exec->dev);379379- if (mr <= 1)380380- return nv_rd32(device, 0x1002c0 + ((mr - 0) * 4));381381- if (mr <= 3)382382- return nv_rd32(device, 0x1002e0 + ((mr - 2) * 4));383383- return 0;384384-}385385-386386-static void387387-mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data)388388-{389389- struct nouveau_device *device = nouveau_dev(exec->dev);390390- struct nouveau_fb *pfb = nouveau_fb(device);391391- if (mr <= 1) {392392- if (pfb->ram->ranks > 1)393393- nv_wr32(device, 0x1002c8 + ((mr - 0) * 4), data);394394- nv_wr32(device, 0x1002c0 + ((mr - 0) * 4), data);395395- } else396396- if (mr <= 3) {397397- if (pfb->ram->ranks > 1)398398- nv_wr32(device, 0x1002e8 + ((mr - 2) * 4), data);399399- nv_wr32(device, 0x1002e0 + ((mr - 2) * 4), data);400400- }401401-}402402-403403-static void404404-mclk_clock_set(struct nouveau_mem_exec_func *exec)405405-{406406- struct nouveau_device *device = nouveau_dev(exec->dev);407407- struct nva3_pm_state *info = exec->priv;408408- u32 ctrl;409409-410410- ctrl = nv_rd32(device, 0x004000);411411- if (!(ctrl & 0x00000008) && info->mclk.pll) {412412- nv_wr32(device, 0x004000, (ctrl |= 0x00000008));413413- nv_mask(device, 0x1110e0, 0x00088000, 0x00088000);414414- nv_wr32(device, 0x004018, 0x00001000);415415- nv_wr32(device, 0x004000, (ctrl &= ~0x00000001));416416- nv_wr32(device, 0x004004, info->mclk.pll);417417- nv_wr32(device, 0x004000, (ctrl |= 0x00000001));418418- udelay(64);419419- nv_wr32(device, 0x004018, 0x00005000 | info->r004018);420420- udelay(20);421421- } else422422- if (!info->mclk.pll) {423423- nv_mask(device, 0x004168, 0x003f3040, info->mclk.clk);424424- nv_wr32(device, 0x004000, (ctrl |= 0x00000008));425425- nv_mask(device, 0x1110e0, 0x00088000, 0x00088000);426426- nv_wr32(device, 0x004018, 0x0000d000 | info->r004018);427427- }428428-429429- if (info->rammap) {430430- if (info->ramcfg && (info->rammap[4] & 0x08)) {431431- u32 unk5a0 = (ROM16(info->ramcfg[5]) << 8) |432432- info->ramcfg[5];433433- u32 unk5a4 = ROM16(info->ramcfg[7]);434434- u32 unk804 = (info->ramcfg[9] & 0xf0) << 16 |435435- (info->ramcfg[3] & 0x0f) << 16 |436436- (info->ramcfg[9] & 0x0f) |437437- 0x80000000;438438- nv_wr32(device, 0x1005a0, unk5a0);439439- nv_wr32(device, 0x1005a4, unk5a4);440440- nv_wr32(device, 0x10f804, unk804);441441- nv_mask(device, 0x10053c, 0x00001000, 0x00000000);442442- } else {443443- nv_mask(device, 0x10053c, 0x00001000, 0x00001000);444444- nv_mask(device, 0x10f804, 0x80000000, 0x00000000);445445- nv_mask(device, 0x100760, 0x22222222, info->r100760);446446- nv_mask(device, 0x1007a0, 0x22222222, info->r100760);447447- nv_mask(device, 0x1007e0, 0x22222222, info->r100760);448448- }449449- }450450-451451- if (info->mclk.pll) {452452- nv_mask(device, 0x1110e0, 0x00088000, 0x00011000);453453- nv_wr32(device, 0x004000, (ctrl &= ~0x00000008));454454- }455455-}456456-457457-static void458458-mclk_timing_set(struct nouveau_mem_exec_func *exec)459459-{460460- struct nouveau_device *device = nouveau_dev(exec->dev);461461- struct nva3_pm_state *info = exec->priv;462462- struct nouveau_pm_level *perflvl = info->perflvl;463463- int i;464464-465465- for (i = 0; i < 9; i++)466466- nv_wr32(device, 0x100220 + (i * 4), perflvl->timing.reg[i]);467467-468468- if (info->ramcfg) {469469- u32 data = (info->ramcfg[2] & 0x08) ? 0x00000000 : 0x00001000;470470- nv_mask(device, 0x100200, 0x00001000, data);471471- }472472-473473- if (info->ramcfg) {474474- u32 unk714 = nv_rd32(device, 0x100714) & ~0xf0000010;475475- u32 unk718 = nv_rd32(device, 0x100718) & ~0x00000100;476476- u32 unk71c = nv_rd32(device, 0x10071c) & ~0x00000100;477477- if ( (info->ramcfg[2] & 0x20))478478- unk714 |= 0xf0000000;479479- if (!(info->ramcfg[2] & 0x04))480480- unk714 |= 0x00000010;481481- nv_wr32(device, 0x100714, unk714);482482-483483- if (info->ramcfg[2] & 0x01)484484- unk71c |= 0x00000100;485485- nv_wr32(device, 0x10071c, unk71c);486486-487487- if (info->ramcfg[2] & 0x02)488488- unk718 |= 0x00000100;489489- nv_wr32(device, 0x100718, unk718);490490-491491- if (info->ramcfg[2] & 0x10)492492- nv_wr32(device, 0x111100, 0x48000000); /*XXX*/493493- }494494-}495495-496496-static void497497-prog_mem(struct drm_device *dev, struct nva3_pm_state *info)498498-{499499- struct nouveau_device *device = nouveau_dev(dev);500500- struct nouveau_mem_exec_func exec = {501501- .dev = dev,502502- .precharge = mclk_precharge,503503- .refresh = mclk_refresh,504504- .refresh_auto = mclk_refresh_auto,505505- .refresh_self = mclk_refresh_self,506506- .wait = mclk_wait,507507- .mrg = mclk_mrg,508508- .mrs = mclk_mrs,509509- .clock_set = mclk_clock_set,510510- .timing_set = mclk_timing_set,511511- .priv = info512512- };513513- u32 ctrl;514514-515515- /* XXX: where the fuck does 750MHz come from? */516516- if (info->perflvl->memory <= 750000) {517517- info->r004018 = 0x10000000;518518- info->r100760 = 0x22222222;519519- }520520-521521- ctrl = nv_rd32(device, 0x004000);522522- if (ctrl & 0x00000008) {523523- if (info->mclk.pll) {524524- nv_mask(device, 0x004128, 0x00000101, 0x00000101);525525- nv_wr32(device, 0x004004, info->mclk.pll);526526- nv_wr32(device, 0x004000, (ctrl |= 0x00000001));527527- nv_wr32(device, 0x004000, (ctrl &= 0xffffffef));528528- nv_wait(device, 0x004000, 0x00020000, 0x00020000);529529- nv_wr32(device, 0x004000, (ctrl |= 0x00000010));530530- nv_wr32(device, 0x004018, 0x00005000 | info->r004018);531531- nv_wr32(device, 0x004000, (ctrl |= 0x00000004));532532- }533533- } else {534534- u32 ssel = 0x00000101;535535- if (info->mclk.clk)536536- ssel |= info->mclk.clk;537537- else538538- ssel |= 0x00080000; /* 324MHz, shouldn't matter... */539539- nv_mask(device, 0x004168, 0x003f3141, ctrl);540540- }541541-542542- if (info->ramcfg) {543543- if (info->ramcfg[2] & 0x10) {544544- nv_mask(device, 0x111104, 0x00000600, 0x00000000);545545- } else {546546- nv_mask(device, 0x111100, 0x40000000, 0x40000000);547547- nv_mask(device, 0x111104, 0x00000180, 0x00000000);548548- }549549- }550550- if (info->rammap && !(info->rammap[4] & 0x02))551551- nv_mask(device, 0x100200, 0x00000800, 0x00000000);552552- nv_wr32(device, 0x611200, 0x00003300);553553- if (!(info->ramcfg[2] & 0x10))554554- nv_wr32(device, 0x111100, 0x4c020000); /*XXX*/555555-556556- nouveau_mem_exec(&exec, info->perflvl);557557-558558- nv_wr32(device, 0x611200, 0x00003330);559559- if (info->rammap && (info->rammap[4] & 0x02))560560- nv_mask(device, 0x100200, 0x00000800, 0x00000800);561561- if (info->ramcfg) {562562- if (info->ramcfg[2] & 0x10) {563563- nv_mask(device, 0x111104, 0x00000180, 0x00000180);564564- nv_mask(device, 0x111100, 0x40000000, 0x00000000);565565- } else {566566- nv_mask(device, 0x111104, 0x00000600, 0x00000600);567567- }568568- }569569-570570- if (info->mclk.pll) {571571- nv_mask(device, 0x004168, 0x00000001, 0x00000000);572572- nv_mask(device, 0x004168, 0x00000100, 0x00000000);573573- } else {574574- nv_mask(device, 0x004000, 0x00000001, 0x00000000);575575- nv_mask(device, 0x004128, 0x00000001, 0x00000000);576576- nv_mask(device, 0x004128, 0x00000100, 0x00000000);577577- }578578-}579579-580580-int581581-nva3_pm_clocks_set(struct drm_device *dev, void *pre_state)582582-{583583- struct nouveau_device *device = nouveau_dev(dev);584584- struct nouveau_drm *drm = nouveau_drm(dev);585585- struct nva3_pm_state *info = pre_state;586586- int ret = -EAGAIN;587587-588588- /* prevent any new grctx switches from starting */589589- nv_wr32(device, 0x400324, 0x00000000);590590- nv_wr32(device, 0x400328, 0x0050001c); /* wait flag 0x1c */591591- /* wait for any pending grctx switches to complete */592592- if (!nv_wait_cb(device, nva3_pm_grcp_idle, dev)) {593593- NV_ERROR(drm, "pm: ctxprog didn't go idle\n");594594- goto cleanup;595595- }596596- /* freeze PFIFO */597597- nv_mask(device, 0x002504, 0x00000001, 0x00000001);598598- if (!nv_wait(device, 0x002504, 0x00000010, 0x00000010)) {599599- NV_ERROR(drm, "pm: fifo didn't go idle\n");600600- goto cleanup;601601- }602602-603603- prog_pll(dev, 0x00, 0x004200, &info->nclk);604604- prog_pll(dev, 0x01, 0x004220, &info->sclk);605605- prog_clk(dev, 0x20, &info->unka0);606606- prog_clk(dev, 0x21, &info->vdec);607607-608608- if (info->mclk.clk || info->mclk.pll)609609- prog_mem(dev, info);610610-611611- ret = 0;612612-613613-cleanup:614614- /* unfreeze PFIFO */615615- nv_mask(device, 0x002504, 0x00000001, 0x00000000);616616- /* restore ctxprog to normal */617617- nv_wr32(device, 0x400324, 0x00000000);618618- nv_wr32(device, 0x400328, 0x0070009c); /* set flag 0x1c */619619- /* unblock it if necessary */620620- if (nv_rd32(device, 0x400308) == 0x0050001c)621621- nv_mask(device, 0x400824, 0x10000000, 0x10000000);622622- kfree(info);623623- return ret;624624-}
-599
drivers/gpu/drm/nouveau/nvc0_pm.c
···11-/*22- * Copyright 2011 Red Hat Inc.33- *44- * Permission is hereby granted, free of charge, to any person obtaining a55- * copy of this software and associated documentation files (the "Software"),66- * to deal in the Software without restriction, including without limitation77- * the rights to use, copy, modify, merge, publish, distribute, sublicense,88- * and/or sell copies of the Software, and to permit persons to whom the99- * Software is furnished to do so, subject to the following conditions:1010- *1111- * The above copyright notice and this permission notice shall be included in1212- * all copies or substantial portions of the Software.1313- *1414- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR1515- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,1616- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL1717- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR1818- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,1919- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR2020- * OTHER DEALINGS IN THE SOFTWARE.2121- *2222- * Authors: Ben Skeggs2323- */2424-2525-#include "nouveau_drm.h"2626-#include "nouveau_bios.h"2727-#include "nouveau_pm.h"2828-2929-#include <subdev/bios/pll.h>3030-#include <subdev/bios.h>3131-#include <subdev/clock.h>3232-#include <subdev/timer.h>3333-#include <subdev/fb.h>3434-3535-static u32 read_div(struct drm_device *, int, u32, u32);3636-static u32 read_pll(struct drm_device *, u32);3737-3838-static u323939-read_vco(struct drm_device *dev, u32 dsrc)4040-{4141- struct nouveau_device *device = nouveau_dev(dev);4242- u32 ssrc = nv_rd32(device, dsrc);4343- if (!(ssrc & 0x00000100))4444- return read_pll(dev, 0x00e800);4545- return read_pll(dev, 0x00e820);4646-}4747-4848-static u324949-read_pll(struct drm_device *dev, u32 pll)5050-{5151- struct nouveau_device *device = nouveau_dev(dev);5252- u32 ctrl = nv_rd32(device, pll + 0);5353- u32 coef = nv_rd32(device, pll + 4);5454- u32 P = (coef & 0x003f0000) >> 16;5555- u32 N = (coef & 0x0000ff00) >> 8;5656- u32 M = (coef & 0x000000ff) >> 0;5757- u32 sclk, doff;5858-5959- if (!(ctrl & 0x00000001))6060- return 0;6161-6262- switch (pll & 0xfff000) {6363- case 0x00e000:6464- sclk = 27000;6565- P = 1;6666- break;6767- case 0x137000:6868- doff = (pll - 0x137000) / 0x20;6969- sclk = read_div(dev, doff, 0x137120, 0x137140);7070- break;7171- case 0x132000:7272- switch (pll) {7373- case 0x132000:7474- sclk = read_pll(dev, 0x132020);7575- break;7676- case 0x132020:7777- sclk = read_div(dev, 0, 0x137320, 0x137330);7878- break;7979- default:8080- return 0;8181- }8282- break;8383- default:8484- return 0;8585- }8686-8787- return sclk * N / M / P;8888-}8989-9090-static u329191-read_div(struct drm_device *dev, int doff, u32 dsrc, u32 dctl)9292-{9393- struct nouveau_device *device = nouveau_dev(dev);9494- u32 ssrc = nv_rd32(device, dsrc + (doff * 4));9595- u32 sctl = nv_rd32(device, dctl + (doff * 4));9696-9797- switch (ssrc & 0x00000003) {9898- case 0:9999- if ((ssrc & 0x00030000) != 0x00030000)100100- return 27000;101101- return 108000;102102- case 2:103103- return 100000;104104- case 3:105105- if (sctl & 0x80000000) {106106- u32 sclk = read_vco(dev, dsrc + (doff * 4));107107- u32 sdiv = (sctl & 0x0000003f) + 2;108108- return (sclk * 2) / sdiv;109109- }110110-111111- return read_vco(dev, dsrc + (doff * 4));112112- default:113113- return 0;114114- }115115-}116116-117117-static u32118118-read_mem(struct drm_device *dev)119119-{120120- struct nouveau_device *device = nouveau_dev(dev);121121- u32 ssel = nv_rd32(device, 0x1373f0);122122- if (ssel & 0x00000001)123123- return read_div(dev, 0, 0x137300, 0x137310);124124- return read_pll(dev, 0x132000);125125-}126126-127127-static u32128128-read_clk(struct drm_device *dev, int clk)129129-{130130- struct nouveau_device *device = nouveau_dev(dev);131131- u32 sctl = nv_rd32(device, 0x137250 + (clk * 4));132132- u32 ssel = nv_rd32(device, 0x137100);133133- u32 sclk, sdiv;134134-135135- if (ssel & (1 << clk)) {136136- if (clk < 7)137137- sclk = read_pll(dev, 0x137000 + (clk * 0x20));138138- else139139- sclk = read_pll(dev, 0x1370e0);140140- sdiv = ((sctl & 0x00003f00) >> 8) + 2;141141- } else {142142- sclk = read_div(dev, clk, 0x137160, 0x1371d0);143143- sdiv = ((sctl & 0x0000003f) >> 0) + 2;144144- }145145-146146- if (sctl & 0x80000000)147147- return (sclk * 2) / sdiv;148148- return sclk;149149-}150150-151151-int152152-nvc0_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)153153-{154154- perflvl->shader = read_clk(dev, 0x00);155155- perflvl->core = perflvl->shader / 2;156156- perflvl->memory = read_mem(dev);157157- perflvl->rop = read_clk(dev, 0x01);158158- perflvl->hub07 = read_clk(dev, 0x02);159159- perflvl->hub06 = read_clk(dev, 0x07);160160- perflvl->hub01 = read_clk(dev, 0x08);161161- perflvl->copy = read_clk(dev, 0x09);162162- perflvl->daemon = read_clk(dev, 0x0c);163163- perflvl->vdec = read_clk(dev, 0x0e);164164- return 0;165165-}166166-167167-struct nvc0_pm_clock {168168- u32 freq;169169- u32 ssel;170170- u32 mdiv;171171- u32 dsrc;172172- u32 ddiv;173173- u32 coef;174174-};175175-176176-struct nvc0_pm_state {177177- struct nouveau_pm_level *perflvl;178178- struct nvc0_pm_clock eng[16];179179- struct nvc0_pm_clock mem;180180-};181181-182182-static u32183183-calc_div(struct drm_device *dev, int clk, u32 ref, u32 freq, u32 *ddiv)184184-{185185- u32 div = min((ref * 2) / freq, (u32)65);186186- if (div < 2)187187- div = 2;188188-189189- *ddiv = div - 2;190190- return (ref * 2) / div;191191-}192192-193193-static u32194194-calc_src(struct drm_device *dev, int clk, u32 freq, u32 *dsrc, u32 *ddiv)195195-{196196- u32 sclk;197197-198198- /* use one of the fixed frequencies if possible */199199- *ddiv = 0x00000000;200200- switch (freq) {201201- case 27000:202202- case 108000:203203- *dsrc = 0x00000000;204204- if (freq == 108000)205205- *dsrc |= 0x00030000;206206- return freq;207207- case 100000:208208- *dsrc = 0x00000002;209209- return freq;210210- default:211211- *dsrc = 0x00000003;212212- break;213213- }214214-215215- /* otherwise, calculate the closest divider */216216- sclk = read_vco(dev, clk);217217- if (clk < 7)218218- sclk = calc_div(dev, clk, sclk, freq, ddiv);219219- return sclk;220220-}221221-222222-static u32223223-calc_pll(struct drm_device *dev, int clk, u32 freq, u32 *coef)224224-{225225- struct nouveau_device *device = nouveau_dev(dev);226226- struct nouveau_bios *bios = nouveau_bios(device);227227- struct nvbios_pll limits;228228- int N, M, P, ret;229229-230230- ret = nvbios_pll_parse(bios, 0x137000 + (clk * 0x20), &limits);231231- if (ret)232232- return 0;233233-234234- limits.refclk = read_div(dev, clk, 0x137120, 0x137140);235235- if (!limits.refclk)236236- return 0;237237-238238- ret = nva3_calc_pll(dev, &limits, freq, &N, NULL, &M, &P);239239- if (ret <= 0)240240- return 0;241241-242242- *coef = (P << 16) | (N << 8) | M;243243- return ret;244244-}245245-246246-/* A (likely rather simplified and incomplete) view of the clock tree247247- *248248- * Key:249249- *250250- * S: source select251251- * D: divider252252- * P: pll253253- * F: switch254254- *255255- * Engine clocks:256256- *257257- * 137250(D) ---- 137100(F0) ---- 137160(S)/1371d0(D) ------------------- ref258258- * (F1) ---- 1370X0(P) ---- 137120(S)/137140(D) ---- ref259259- *260260- * Not all registers exist for all clocks. For example: clocks >= 8 don't261261- * have their own PLL (all tied to clock 7's PLL when in PLL mode), nor do262262- * they have the divider at 1371d0, though the source selection at 137160263263- * still exists. You must use the divider at 137250 for these instead.264264- *265265- * Memory clock:266266- *267267- * TBD, read_mem() above is likely very wrong...268268- *269269- */270270-271271-static int272272-calc_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info, u32 freq)273273-{274274- u32 src0, div0, div1D, div1P = 0;275275- u32 clk0, clk1 = 0;276276-277277- /* invalid clock domain */278278- if (!freq)279279- return 0;280280-281281- /* first possible path, using only dividers */282282- clk0 = calc_src(dev, clk, freq, &src0, &div0);283283- clk0 = calc_div(dev, clk, clk0, freq, &div1D);284284-285285- /* see if we can get any closer using PLLs */286286- if (clk0 != freq && (0x00004387 & (1 << clk))) {287287- if (clk < 7)288288- clk1 = calc_pll(dev, clk, freq, &info->coef);289289- else290290- clk1 = read_pll(dev, 0x1370e0);291291- clk1 = calc_div(dev, clk, clk1, freq, &div1P);292292- }293293-294294- /* select the method which gets closest to target freq */295295- if (abs((int)freq - clk0) <= abs((int)freq - clk1)) {296296- info->dsrc = src0;297297- if (div0) {298298- info->ddiv |= 0x80000000;299299- info->ddiv |= div0 << 8;300300- info->ddiv |= div0;301301- }302302- if (div1D) {303303- info->mdiv |= 0x80000000;304304- info->mdiv |= div1D;305305- }306306- info->ssel = 0;307307- info->freq = clk0;308308- } else {309309- if (div1P) {310310- info->mdiv |= 0x80000000;311311- info->mdiv |= div1P << 8;312312- }313313- info->ssel = (1 << clk);314314- info->freq = clk1;315315- }316316-317317- return 0;318318-}319319-320320-static int321321-calc_mem(struct drm_device *dev, struct nvc0_pm_clock *info, u32 freq)322322-{323323- struct nouveau_device *device = nouveau_dev(dev);324324- struct nouveau_bios *bios = nouveau_bios(device);325325- struct nvbios_pll pll;326326- int N, M, P, ret;327327- u32 ctrl;328328-329329- /* mclk pll input freq comes from another pll, make sure it's on */330330- ctrl = nv_rd32(device, 0x132020);331331- if (!(ctrl & 0x00000001)) {332332- /* if not, program it to 567MHz. nfi where this value comes333333- * from - it looks like it's in the pll limits table for334334- * 132000 but the binary driver ignores all my attempts to335335- * change this value.336336- */337337- nv_wr32(device, 0x137320, 0x00000103);338338- nv_wr32(device, 0x137330, 0x81200606);339339- nv_wait(device, 0x132020, 0x00010000, 0x00010000);340340- nv_wr32(device, 0x132024, 0x0001150f);341341- nv_mask(device, 0x132020, 0x00000001, 0x00000001);342342- nv_wait(device, 0x137390, 0x00020000, 0x00020000);343343- nv_mask(device, 0x132020, 0x00000004, 0x00000004);344344- }345345-346346- /* for the moment, until the clock tree is better understood, use347347- * pll mode for all clock frequencies348348- */349349- ret = nvbios_pll_parse(bios, 0x132000, &pll);350350- if (ret == 0) {351351- pll.refclk = read_pll(dev, 0x132020);352352- if (pll.refclk) {353353- ret = nva3_calc_pll(dev, &pll, freq, &N, NULL, &M, &P);354354- if (ret > 0) {355355- info->coef = (P << 16) | (N << 8) | M;356356- return 0;357357- }358358- }359359- }360360-361361- return -EINVAL;362362-}363363-364364-void *365365-nvc0_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)366366-{367367- struct nouveau_device *device = nouveau_dev(dev);368368- struct nvc0_pm_state *info;369369- int ret;370370-371371- info = kzalloc(sizeof(*info), GFP_KERNEL);372372- if (!info)373373- return ERR_PTR(-ENOMEM);374374-375375- /* NFI why this is still in the performance table, the ROPCs appear376376- * to get their clock from clock 2 ("hub07", actually hub05 on this377377- * chip, but, anyway...) as well. nvatiming confirms hub05 and ROP378378- * are always the same freq with the binary driver even when the379379- * performance table says they should differ.380380- */381381- if (device->chipset == 0xd9)382382- perflvl->rop = 0;383383-384384- if ((ret = calc_clk(dev, 0x00, &info->eng[0x00], perflvl->shader)) ||385385- (ret = calc_clk(dev, 0x01, &info->eng[0x01], perflvl->rop)) ||386386- (ret = calc_clk(dev, 0x02, &info->eng[0x02], perflvl->hub07)) ||387387- (ret = calc_clk(dev, 0x07, &info->eng[0x07], perflvl->hub06)) ||388388- (ret = calc_clk(dev, 0x08, &info->eng[0x08], perflvl->hub01)) ||389389- (ret = calc_clk(dev, 0x09, &info->eng[0x09], perflvl->copy)) ||390390- (ret = calc_clk(dev, 0x0c, &info->eng[0x0c], perflvl->daemon)) ||391391- (ret = calc_clk(dev, 0x0e, &info->eng[0x0e], perflvl->vdec))) {392392- kfree(info);393393- return ERR_PTR(ret);394394- }395395-396396- if (perflvl->memory) {397397- ret = calc_mem(dev, &info->mem, perflvl->memory);398398- if (ret) {399399- kfree(info);400400- return ERR_PTR(ret);401401- }402402- }403403-404404- info->perflvl = perflvl;405405- return info;406406-}407407-408408-static void409409-prog_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info)410410-{411411- struct nouveau_device *device = nouveau_dev(dev);412412-413413- /* program dividers at 137160/1371d0 first */414414- if (clk < 7 && !info->ssel) {415415- nv_mask(device, 0x1371d0 + (clk * 0x04), 0x80003f3f, info->ddiv);416416- nv_wr32(device, 0x137160 + (clk * 0x04), info->dsrc);417417- }418418-419419- /* switch clock to non-pll mode */420420- nv_mask(device, 0x137100, (1 << clk), 0x00000000);421421- nv_wait(device, 0x137100, (1 << clk), 0x00000000);422422-423423- /* reprogram pll */424424- if (clk < 7) {425425- /* make sure it's disabled first... */426426- u32 base = 0x137000 + (clk * 0x20);427427- u32 ctrl = nv_rd32(device, base + 0x00);428428- if (ctrl & 0x00000001) {429429- nv_mask(device, base + 0x00, 0x00000004, 0x00000000);430430- nv_mask(device, base + 0x00, 0x00000001, 0x00000000);431431- }432432- /* program it to new values, if necessary */433433- if (info->ssel) {434434- nv_wr32(device, base + 0x04, info->coef);435435- nv_mask(device, base + 0x00, 0x00000001, 0x00000001);436436- nv_wait(device, base + 0x00, 0x00020000, 0x00020000);437437- nv_mask(device, base + 0x00, 0x00020004, 0x00000004);438438- }439439- }440440-441441- /* select pll/non-pll mode, and program final clock divider */442442- nv_mask(device, 0x137100, (1 << clk), info->ssel);443443- nv_wait(device, 0x137100, (1 << clk), info->ssel);444444- nv_mask(device, 0x137250 + (clk * 0x04), 0x00003f3f, info->mdiv);445445-}446446-447447-static void448448-mclk_precharge(struct nouveau_mem_exec_func *exec)449449-{450450-}451451-452452-static void453453-mclk_refresh(struct nouveau_mem_exec_func *exec)454454-{455455-}456456-457457-static void458458-mclk_refresh_auto(struct nouveau_mem_exec_func *exec, bool enable)459459-{460460- struct nouveau_device *device = nouveau_dev(exec->dev);461461- nv_wr32(device, 0x10f210, enable ? 0x80000000 : 0x00000000);462462-}463463-464464-static void465465-mclk_refresh_self(struct nouveau_mem_exec_func *exec, bool enable)466466-{467467-}468468-469469-static void470470-mclk_wait(struct nouveau_mem_exec_func *exec, u32 nsec)471471-{472472- udelay((nsec + 500) / 1000);473473-}474474-475475-static u32476476-mclk_mrg(struct nouveau_mem_exec_func *exec, int mr)477477-{478478- struct nouveau_device *device = nouveau_dev(exec->dev);479479- struct nouveau_fb *pfb = nouveau_fb(device);480480- if (pfb->ram->type != NV_MEM_TYPE_GDDR5) {481481- if (mr <= 1)482482- return nv_rd32(device, 0x10f300 + ((mr - 0) * 4));483483- return nv_rd32(device, 0x10f320 + ((mr - 2) * 4));484484- } else {485485- if (mr == 0)486486- return nv_rd32(device, 0x10f300 + (mr * 4));487487- else488488- if (mr <= 7)489489- return nv_rd32(device, 0x10f32c + (mr * 4));490490- return nv_rd32(device, 0x10f34c);491491- }492492-}493493-494494-static void495495-mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data)496496-{497497- struct nouveau_device *device = nouveau_dev(exec->dev);498498- struct nouveau_fb *pfb = nouveau_fb(device);499499- if (pfb->ram->type != NV_MEM_TYPE_GDDR5) {500500- if (mr <= 1) {501501- nv_wr32(device, 0x10f300 + ((mr - 0) * 4), data);502502- if (pfb->ram->ranks > 1)503503- nv_wr32(device, 0x10f308 + ((mr - 0) * 4), data);504504- } else505505- if (mr <= 3) {506506- nv_wr32(device, 0x10f320 + ((mr - 2) * 4), data);507507- if (pfb->ram->ranks > 1)508508- nv_wr32(device, 0x10f328 + ((mr - 2) * 4), data);509509- }510510- } else {511511- if (mr == 0) nv_wr32(device, 0x10f300 + (mr * 4), data);512512- else if (mr <= 7) nv_wr32(device, 0x10f32c + (mr * 4), data);513513- else if (mr == 15) nv_wr32(device, 0x10f34c, data);514514- }515515-}516516-517517-static void518518-mclk_clock_set(struct nouveau_mem_exec_func *exec)519519-{520520- struct nouveau_device *device = nouveau_dev(exec->dev);521521- struct nvc0_pm_state *info = exec->priv;522522- u32 ctrl = nv_rd32(device, 0x132000);523523-524524- nv_wr32(device, 0x137360, 0x00000001);525525- nv_wr32(device, 0x137370, 0x00000000);526526- nv_wr32(device, 0x137380, 0x00000000);527527- if (ctrl & 0x00000001)528528- nv_wr32(device, 0x132000, (ctrl &= ~0x00000001));529529-530530- nv_wr32(device, 0x132004, info->mem.coef);531531- nv_wr32(device, 0x132000, (ctrl |= 0x00000001));532532- nv_wait(device, 0x137390, 0x00000002, 0x00000002);533533- nv_wr32(device, 0x132018, 0x00005000);534534-535535- nv_wr32(device, 0x137370, 0x00000001);536536- nv_wr32(device, 0x137380, 0x00000001);537537- nv_wr32(device, 0x137360, 0x00000000);538538-}539539-540540-static void541541-mclk_timing_set(struct nouveau_mem_exec_func *exec)542542-{543543- struct nouveau_device *device = nouveau_dev(exec->dev);544544- struct nvc0_pm_state *info = exec->priv;545545- struct nouveau_pm_level *perflvl = info->perflvl;546546- int i;547547-548548- for (i = 0; i < 5; i++)549549- nv_wr32(device, 0x10f290 + (i * 4), perflvl->timing.reg[i]);550550-}551551-552552-static void553553-prog_mem(struct drm_device *dev, struct nvc0_pm_state *info)554554-{555555- struct nouveau_device *device = nouveau_dev(dev);556556- struct nouveau_mem_exec_func exec = {557557- .dev = dev,558558- .precharge = mclk_precharge,559559- .refresh = mclk_refresh,560560- .refresh_auto = mclk_refresh_auto,561561- .refresh_self = mclk_refresh_self,562562- .wait = mclk_wait,563563- .mrg = mclk_mrg,564564- .mrs = mclk_mrs,565565- .clock_set = mclk_clock_set,566566- .timing_set = mclk_timing_set,567567- .priv = info568568- };569569-570570- if (device->chipset < 0xd0)571571- nv_wr32(device, 0x611200, 0x00003300);572572- else573573- nv_wr32(device, 0x62c000, 0x03030000);574574-575575- nouveau_mem_exec(&exec, info->perflvl);576576-577577- if (device->chipset < 0xd0)578578- nv_wr32(device, 0x611200, 0x00003330);579579- else580580- nv_wr32(device, 0x62c000, 0x03030300);581581-}582582-int583583-nvc0_pm_clocks_set(struct drm_device *dev, void *data)584584-{585585- struct nvc0_pm_state *info = data;586586- int i;587587-588588- if (info->mem.coef)589589- prog_mem(dev, info);590590-591591- for (i = 0; i < 16; i++) {592592- if (!info->eng[i].freq)593593- continue;594594- prog_clk(dev, i, &info->eng[i]);595595- }596596-597597- kfree(info);598598- return 0;599599-}