1/* 2 * Copyright 2010 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 25#include "drmP.h" 26#include "nouveau_drv.h" 27#include "nouveau_hw.h" 28#include "nouveau_gpio.h" 29 30#include "nv50_display.h" 31 32static int 33nv50_gpio_location(int line, u32 *reg, u32 *shift) 34{ 35 const uint32_t nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 }; 36 37 if (line >= 32) 38 return -EINVAL; 39 40 *reg = nv50_gpio_reg[line >> 3]; 41 *shift = (line & 7) << 2; 42 return 0; 43} 44 45int 46nv50_gpio_drive(struct drm_device *dev, int line, int dir, int out) 47{ 48 u32 reg, shift; 49 50 if (nv50_gpio_location(line, ®, &shift)) 51 return -EINVAL; 52 53 nv_mask(dev, reg, 7 << shift, (((dir ^ 1) << 1) | out) << shift); 54 return 0; 55} 56 57int 58nv50_gpio_sense(struct drm_device *dev, int line) 59{ 60 u32 reg, shift; 61 62 if (nv50_gpio_location(line, ®, &shift)) 63 return -EINVAL; 64 65 return !!(nv_rd32(dev, reg) & (4 << shift)); 66} 67 68void 69nv50_gpio_irq_enable(struct drm_device *dev, int line, bool on) 70{ 71 u32 reg = line < 16 ? 0xe050 : 0xe070; 72 u32 mask = 0x00010001 << (line & 0xf); 73 74 nv_wr32(dev, reg + 4, mask); 75 nv_mask(dev, reg + 0, mask, on ? mask : 0); 76} 77 78int 79nvd0_gpio_drive(struct drm_device *dev, int line, int dir, int out) 80{ 81 u32 data = ((dir ^ 1) << 13) | (out << 12); 82 nv_mask(dev, 0x00d610 + (line * 4), 0x00003000, data); 83 nv_mask(dev, 0x00d604, 0x00000001, 0x00000001); /* update? */ 84 return 0; 85} 86 87int 88nvd0_gpio_sense(struct drm_device *dev, int line) 89{ 90 return !!(nv_rd32(dev, 0x00d610 + (line * 4)) & 0x00004000); 91} 92 93static void 94nv50_gpio_isr(struct drm_device *dev) 95{ 96 struct drm_nouveau_private *dev_priv = dev->dev_private; 97 u32 intr0, intr1 = 0; 98 u32 hi, lo; 99 100 intr0 = nv_rd32(dev, 0xe054) & nv_rd32(dev, 0xe050); 101 if (dev_priv->chipset >= 0x90) 102 intr1 = nv_rd32(dev, 0xe074) & nv_rd32(dev, 0xe070); 103 104 hi = (intr0 & 0x0000ffff) | (intr1 << 16); 105 lo = (intr0 >> 16) | (intr1 & 0xffff0000); 106 nouveau_gpio_isr(dev, 0, hi | lo); 107 108 nv_wr32(dev, 0xe054, intr0); 109 if (dev_priv->chipset >= 0x90) 110 nv_wr32(dev, 0xe074, intr1); 111} 112 113int 114nv50_gpio_init(struct drm_device *dev) 115{ 116 struct drm_nouveau_private *dev_priv = dev->dev_private; 117 118 /* disable, and ack any pending gpio interrupts */ 119 nv_wr32(dev, 0xe050, 0x00000000); 120 nv_wr32(dev, 0xe054, 0xffffffff); 121 if (dev_priv->chipset >= 0x90) { 122 nv_wr32(dev, 0xe070, 0x00000000); 123 nv_wr32(dev, 0xe074, 0xffffffff); 124 } 125 126 nouveau_irq_register(dev, 21, nv50_gpio_isr); 127 return 0; 128} 129 130void 131nv50_gpio_fini(struct drm_device *dev) 132{ 133 struct drm_nouveau_private *dev_priv = dev->dev_private; 134 135 nv_wr32(dev, 0xe050, 0x00000000); 136 if (dev_priv->chipset >= 0x90) 137 nv_wr32(dev, 0xe070, 0x00000000); 138 nouveau_irq_unregister(dev, 21); 139} 140