linux/drivers/gpu/drm/nouveau/nvkm/subdev/gpio/base.c
<<
>>
Prefs
   1/*
   2 * Copyright 2011 Red Hat Inc.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial portions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20 * OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 * Authors: Ben Skeggs
  23 */
  24#include "priv.h"
  25
  26#include <core/notify.h>
  27
  28static int
  29nvkm_gpio_drive(struct nvkm_gpio *gpio, int idx, int line, int dir, int out)
  30{
  31        return gpio->func->drive(gpio, line, dir, out);
  32}
  33
  34static int
  35nvkm_gpio_sense(struct nvkm_gpio *gpio, int idx, int line)
  36{
  37        return gpio->func->sense(gpio, line);
  38}
  39
  40void
  41nvkm_gpio_reset(struct nvkm_gpio *gpio, u8 func)
  42{
  43        if (gpio->func->reset)
  44                gpio->func->reset(gpio, func);
  45}
  46
  47int
  48nvkm_gpio_find(struct nvkm_gpio *gpio, int idx, u8 tag, u8 line,
  49               struct dcb_gpio_func *func)
  50{
  51        struct nvkm_device *device = gpio->subdev.device;
  52        struct nvkm_bios *bios = device->bios;
  53        u8  ver, len;
  54        u16 data;
  55
  56        if (line == 0xff && tag == 0xff)
  57                return -EINVAL;
  58
  59        data = dcb_gpio_match(bios, idx, tag, line, &ver, &len, func);
  60        if (data)
  61                return 0;
  62
  63        /* Apple iMac G4 NV18 */
  64        if (device->quirk && device->quirk->tv_gpio) {
  65                if (tag == DCB_GPIO_TVDAC0) {
  66                        *func = (struct dcb_gpio_func) {
  67                                .func = DCB_GPIO_TVDAC0,
  68                                .line = device->quirk->tv_gpio,
  69                                .log[0] = 0,
  70                                .log[1] = 1,
  71                        };
  72                        return 0;
  73                }
  74        }
  75
  76        return -ENOENT;
  77}
  78
  79int
  80nvkm_gpio_set(struct nvkm_gpio *gpio, int idx, u8 tag, u8 line, int state)
  81{
  82        struct dcb_gpio_func func;
  83        int ret;
  84
  85        ret = nvkm_gpio_find(gpio, idx, tag, line, &func);
  86        if (ret == 0) {
  87                int dir = !!(func.log[state] & 0x02);
  88                int out = !!(func.log[state] & 0x01);
  89                ret = nvkm_gpio_drive(gpio, idx, func.line, dir, out);
  90        }
  91
  92        return ret;
  93}
  94
  95int
  96nvkm_gpio_get(struct nvkm_gpio *gpio, int idx, u8 tag, u8 line)
  97{
  98        struct dcb_gpio_func func;
  99        int ret;
 100
 101        ret = nvkm_gpio_find(gpio, idx, tag, line, &func);
 102        if (ret == 0) {
 103                ret = nvkm_gpio_sense(gpio, idx, func.line);
 104                if (ret >= 0)
 105                        ret = (ret == (func.log[1] & 1));
 106        }
 107
 108        return ret;
 109}
 110
 111static void
 112nvkm_gpio_intr_fini(struct nvkm_event *event, int type, int index)
 113{
 114        struct nvkm_gpio *gpio = container_of(event, typeof(*gpio), event);
 115        gpio->func->intr_mask(gpio, type, 1 << index, 0);
 116}
 117
 118static void
 119nvkm_gpio_intr_init(struct nvkm_event *event, int type, int index)
 120{
 121        struct nvkm_gpio *gpio = container_of(event, typeof(*gpio), event);
 122        gpio->func->intr_mask(gpio, type, 1 << index, 1 << index);
 123}
 124
 125static int
 126nvkm_gpio_intr_ctor(struct nvkm_object *object, void *data, u32 size,
 127                    struct nvkm_notify *notify)
 128{
 129        struct nvkm_gpio_ntfy_req *req = data;
 130        if (!WARN_ON(size != sizeof(*req))) {
 131                notify->size  = sizeof(struct nvkm_gpio_ntfy_rep);
 132                notify->types = req->mask;
 133                notify->index = req->line;
 134                return 0;
 135        }
 136        return -EINVAL;
 137}
 138
 139static const struct nvkm_event_func
 140nvkm_gpio_intr_func = {
 141        .ctor = nvkm_gpio_intr_ctor,
 142        .init = nvkm_gpio_intr_init,
 143        .fini = nvkm_gpio_intr_fini,
 144};
 145
 146static void
 147nvkm_gpio_intr(struct nvkm_subdev *subdev)
 148{
 149        struct nvkm_gpio *gpio = nvkm_gpio(subdev);
 150        u32 hi, lo, i;
 151
 152        gpio->func->intr_stat(gpio, &hi, &lo);
 153
 154        for (i = 0; (hi | lo) && i < gpio->func->lines; i++) {
 155                struct nvkm_gpio_ntfy_rep rep = {
 156                        .mask = (NVKM_GPIO_HI * !!(hi & (1 << i))) |
 157                                (NVKM_GPIO_LO * !!(lo & (1 << i))),
 158                };
 159                nvkm_event_send(&gpio->event, rep.mask, i, &rep, sizeof(rep));
 160        }
 161}
 162
 163static int
 164nvkm_gpio_fini(struct nvkm_subdev *subdev, bool suspend)
 165{
 166        struct nvkm_gpio *gpio = nvkm_gpio(subdev);
 167        u32 mask = (1ULL << gpio->func->lines) - 1;
 168
 169        gpio->func->intr_mask(gpio, NVKM_GPIO_TOGGLED, mask, 0);
 170        gpio->func->intr_stat(gpio, &mask, &mask);
 171        return 0;
 172}
 173
 174static const struct dmi_system_id gpio_reset_ids[] = {
 175        {
 176                .ident = "Apple Macbook 10,1",
 177                .matches = {
 178                        DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
 179                        DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"),
 180                }
 181        },
 182        { }
 183};
 184
 185static int
 186nvkm_gpio_init(struct nvkm_subdev *subdev)
 187{
 188        struct nvkm_gpio *gpio = nvkm_gpio(subdev);
 189        if (dmi_check_system(gpio_reset_ids))
 190                nvkm_gpio_reset(gpio, DCB_GPIO_UNUSED);
 191        return 0;
 192}
 193
 194static void *
 195nvkm_gpio_dtor(struct nvkm_subdev *subdev)
 196{
 197        struct nvkm_gpio *gpio = nvkm_gpio(subdev);
 198        nvkm_event_fini(&gpio->event);
 199        return gpio;
 200}
 201
 202static const struct nvkm_subdev_func
 203nvkm_gpio = {
 204        .dtor = nvkm_gpio_dtor,
 205        .init = nvkm_gpio_init,
 206        .fini = nvkm_gpio_fini,
 207        .intr = nvkm_gpio_intr,
 208};
 209
 210int
 211nvkm_gpio_new_(const struct nvkm_gpio_func *func, struct nvkm_device *device,
 212               int index, struct nvkm_gpio **pgpio)
 213{
 214        struct nvkm_gpio *gpio;
 215
 216        if (!(gpio = *pgpio = kzalloc(sizeof(*gpio), GFP_KERNEL)))
 217                return -ENOMEM;
 218
 219        nvkm_subdev_ctor(&nvkm_gpio, device, index, &gpio->subdev);
 220        gpio->func = func;
 221
 222        return nvkm_event_init(&nvkm_gpio_intr_func, 2, func->lines,
 223                               &gpio->event);
 224}
 225