From: Martin Peres Date: Tue, 8 Sep 2015 22:34:33 +0000 (+0200) Subject: drm/nouveau/volt/gk104: add support for pwm and gpio modes X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=1531dbbb56820d4e0fee4f9baa9fc84485bb6623;p=GitHub%2Fmoto-9609%2Fandroid_kernel_motorola_exynos9610.git drm/nouveau/volt/gk104: add support for pwm and gpio modes Most Keplers actually use the GPIO-based voltage management instead of the new PWM-based one. Use the GPIO mode as a fallback as it already gracefully handles the case where no GPIOs exist. All the Maxwells seem to use the PWM method though. v2: - Do not forget to commit the PWM configuration change! Signed-off-by: Martin Peres --- diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h index 33be260ddd38..a47d46dda704 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/gpio.h @@ -15,6 +15,7 @@ enum dcb_gpio_func_name { DCB_GPIO_VID5 = 0x74, DCB_GPIO_VID6 = 0x75, DCB_GPIO_VID7 = 0x76, + DCB_GPIO_VID_PWM = 0x81, }; #define DCB_GPIO_LOG_DIR 0x02 diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/volt.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/volt.h index 5c8a3f1196de..b458d046dba7 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/volt.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/volt.h @@ -18,5 +18,6 @@ int nvkm_volt_get(struct nvkm_volt *); int nvkm_volt_set_id(struct nvkm_volt *, u8 id, int condition); int nv40_volt_new(struct nvkm_device *, int, struct nvkm_volt **); +int gk104_volt_new(struct nvkm_device *, int, struct nvkm_volt **); int gk20a_volt_new(struct nvkm_device *, int, struct nvkm_volt **); #endif diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index 94a906b8cb88..7c35f1ff8a64 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -1673,7 +1673,7 @@ nve4_chipset = { .pmu = gk104_pmu_new, .therm = gf119_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gk104_volt_new, .ce[0] = gk104_ce_new, .ce[1] = gk104_ce_new, .ce[2] = gk104_ce_new, @@ -1710,7 +1710,7 @@ nve6_chipset = { .pmu = gk104_pmu_new, .therm = gf119_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gk104_volt_new, .ce[0] = gk104_ce_new, .ce[1] = gk104_ce_new, .ce[2] = gk104_ce_new, @@ -1747,7 +1747,7 @@ nve7_chipset = { .pmu = gf119_pmu_new, .therm = gf119_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gk104_volt_new, .ce[0] = gk104_ce_new, .ce[1] = gk104_ce_new, .ce[2] = gk104_ce_new, @@ -1808,7 +1808,7 @@ nvf0_chipset = { .pmu = gk110_pmu_new, .therm = gf119_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gk104_volt_new, .ce[0] = gk104_ce_new, .ce[1] = gk104_ce_new, .ce[2] = gk104_ce_new, @@ -1844,7 +1844,7 @@ nvf1_chipset = { .pmu = gk110_pmu_new, .therm = gf119_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gk104_volt_new, .ce[0] = gk104_ce_new, .ce[1] = gk104_ce_new, .ce[2] = gk104_ce_new, @@ -1880,7 +1880,7 @@ nv106_chipset = { .pmu = gk208_pmu_new, .therm = gf119_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gk104_volt_new, .ce[0] = gk104_ce_new, .ce[1] = gk104_ce_new, .ce[2] = gk104_ce_new, @@ -1916,7 +1916,7 @@ nv108_chipset = { .pmu = gk208_pmu_new, .therm = gf119_therm_new, .timer = nv41_timer_new, - .volt = nv40_volt_new, + .volt = gk104_volt_new, .ce[0] = gk104_ce_new, .ce[1] = gk104_ce_new, .ce[2] = gk104_ce_new, diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild index 6b46ff4213a3..b035c6e28be8 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild @@ -1,4 +1,5 @@ nvkm-y += nvkm/subdev/volt/base.o nvkm-y += nvkm/subdev/volt/gpio.o nvkm-y += nvkm/subdev/volt/nv40.o +nvkm-y += nvkm/subdev/volt/gk104.o nvkm-y += nvkm/subdev/volt/gk20a.o diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c new file mode 100644 index 000000000000..b61509e26ec9 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c @@ -0,0 +1,119 @@ +/* + * Copyright 2015 Martin Peres + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Martin Peres + */ +#include "priv.h" + +#include +#include +#include +#include + +#define gk104_volt(p) container_of((p), struct gk104_volt, base) +struct gk104_volt { + struct nvkm_volt base; + struct nvbios_volt bios; +}; + +int +gk104_volt_get(struct nvkm_volt *base) +{ + struct nvbios_volt *bios = &gk104_volt(base)->bios; + struct nvkm_device *device = base->subdev.device; + u32 div, duty; + + div = nvkm_rd32(device, 0x20340); + duty = nvkm_rd32(device, 0x20344); + + return bios->base + bios->pwm_range * duty / div; +} + +int +gk104_volt_set(struct nvkm_volt *base, u32 uv) +{ + struct nvbios_volt *bios = &gk104_volt(base)->bios; + struct nvkm_device *device = base->subdev.device; + u32 div, duty; + + /* the blob uses this crystal frequency, let's use it too. */ + div = 27648000 / bios->pwm_freq; + duty = (uv - bios->base) * div / bios->pwm_range; + + nvkm_wr32(device, 0x20340, div); + nvkm_wr32(device, 0x20344, 0x8000000 | duty); + + return 0; +} + +static const struct nvkm_volt_func +gk104_volt_pwm = { + .volt_get = gk104_volt_get, + .volt_set = gk104_volt_set, +}, gk104_volt_gpio = { + .vid_get = nvkm_voltgpio_get, + .vid_set = nvkm_voltgpio_set, +}; + +int +gk104_volt_new(struct nvkm_device *device, int index, struct nvkm_volt **pvolt) +{ + const struct nvkm_volt_func *volt_func = &gk104_volt_gpio; + struct dcb_gpio_func gpio; + struct nvbios_volt bios; + struct gk104_volt *volt; + u8 ver, hdr, cnt, len; + const char *mode; + + if (!nvbios_volt_parse(device->bios, &ver, &hdr, &cnt, &len, &bios)) + return 0; + + if (!nvkm_gpio_find(device->gpio, 0, DCB_GPIO_VID_PWM, 0xff, &gpio) && + bios.type == NVBIOS_VOLT_PWM) { + volt_func = &gk104_volt_pwm; + } + + if (!(volt = kzalloc(sizeof(*volt), GFP_KERNEL))) + return -ENOMEM; + nvkm_volt_ctor(volt_func, device, index, &volt->base); + *pvolt = &volt->base; + volt->bios = bios; + + /* now that we have a subdev, we can show an error if we found through + * the voltage table that we were supposed to use the PWN mode but we + * did not find the right GPIO for it. + */ + if (bios.type == NVBIOS_VOLT_PWM && volt_func != &gk104_volt_pwm) { + nvkm_error(&volt->base.subdev, + "Type mismatch between the voltage table type and " + "the GPIO table. Fallback to GPIO mode.\n"); + } + + if (volt_func == &gk104_volt_gpio) { + nvkm_voltgpio_init(&volt->base); + mode = "GPIO"; + } else + mode = "PWM"; + + nvkm_debug(&volt->base.subdev, "Using %s mode\n", mode); + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/priv.h index cdb3d9f80aa6..d5140d991161 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/priv.h @@ -19,4 +19,8 @@ struct nvkm_volt_func { int nvkm_voltgpio_init(struct nvkm_volt *); int nvkm_voltgpio_get(struct nvkm_volt *); int nvkm_voltgpio_set(struct nvkm_volt *, u8); + +int nvkm_voltpwm_init(struct nvkm_volt *volt); +int nvkm_voltpwm_get(struct nvkm_volt *volt); +int nvkm_voltpwm_set(struct nvkm_volt *volt, u32 uv); #endif