From b4b7ff4b08e691656c9d77c758fc355833128ac0 Mon Sep 17 00:00:00 2001 From: André Fabian Silva Delgado Date: Wed, 20 Jan 2016 14:01:31 -0300 Subject: Linux-libre 4.4-gnu --- drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild | 1 + drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c | 11 ++- drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c | 119 +++++++++++++++++++++++ drivers/gpu/drm/nouveau/nvkm/subdev/volt/priv.h | 6 ++ 4 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/nouveau/nvkm/subdev/volt/gk104.c (limited to 'drivers/gpu/drm/nouveau/nvkm/subdev/volt') diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/Kbuild index 6b46ff421..b035c6e28 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/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c index 4752dbd33..50b5649ad 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/base.c @@ -30,7 +30,12 @@ int nvkm_volt_get(struct nvkm_volt *volt) { - int ret = volt->func->vid_get(volt), i; + int ret, i; + + if (volt->func->volt_get) + return volt->func->volt_get(volt); + + ret = volt->func->vid_get(volt); if (ret >= 0) { for (i = 0; i < volt->vid_nr; i++) { if (volt->vid[i].vid == ret) @@ -46,6 +51,10 @@ nvkm_volt_set(struct nvkm_volt *volt, u32 uv) { struct nvkm_subdev *subdev = &volt->subdev; int i, ret = -EINVAL; + + if (volt->func->volt_set) + return volt->func->volt_set(volt, uv); + for (i = 0; i < volt->vid_nr; i++) { if (volt->vid[i].uv == uv) { ret = volt->func->vid_set(volt, volt->vid[i].vid); 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 000000000..b735173a1 --- /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, 0x80000000 | 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 394f37c72..d5140d991 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/volt/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/volt/priv.h @@ -9,6 +9,8 @@ int nvkm_volt_new_(const struct nvkm_volt_func *, struct nvkm_device *, int index, struct nvkm_volt **); struct nvkm_volt_func { + int (*volt_get)(struct nvkm_volt *); + int (*volt_set)(struct nvkm_volt *, u32 uv); int (*vid_get)(struct nvkm_volt *); int (*vid_set)(struct nvkm_volt *, u8 vid); int (*set_id)(struct nvkm_volt *, u8 id, int condition); @@ -17,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 -- cgit v1.2.3-54-g00ecf