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 25#include "drmP.h" 26#include "nouveau_drv.h" 27#include "nouveau_i2c.h" 28#include "nouveau_gpio.h" 29 30static u8 * 31dcb_gpio_table(struct drm_device *dev) 32{ 33 u8 *dcb = dcb_table(dev); 34 if (dcb) { 35 if (dcb[0] >= 0x30 && dcb[1] >= 0x0c) 36 return ROMPTR(dev, dcb[0x0a]); 37 if (dcb[0] >= 0x22 && dcb[-1] >= 0x13) 38 return ROMPTR(dev, dcb[-15]); 39 } 40 return NULL; 41} 42 43static u8 * 44dcb_gpio_entry(struct drm_device *dev, int idx, int ent, u8 *version) 45{ 46 u8 *table = dcb_gpio_table(dev); 47 if (table) { 48 *version = table[0]; 49 if (*version < 0x30 && ent < table[2]) 50 return table + 3 + (ent * table[1]); 51 else if (ent < table[2]) 52 return table + table[1] + (ent * table[3]); 53 } 54 return NULL; 55} 56 57int 58nouveau_gpio_drive(struct drm_device *dev, int idx, int line, int dir, int out) 59{ 60 struct drm_nouveau_private *dev_priv = dev->dev_private; 61 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; 62 63 return pgpio->drive ? pgpio->drive(dev, line, dir, out) : -ENODEV; 64} 65 66int 67nouveau_gpio_sense(struct drm_device *dev, int idx, int line) 68{ 69 struct drm_nouveau_private *dev_priv = dev->dev_private; 70 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; 71 72 return pgpio->sense ? pgpio->sense(dev, line) : -ENODEV; 73} 74 75int 76nouveau_gpio_find(struct drm_device *dev, int idx, u8 func, u8 line, 77 struct gpio_func *gpio) 78{ 79 u8 *table, *entry, version; 80 int i = -1; 81 82 if (line == 0xff && func == 0xff) 83 return -EINVAL; 84 85 while ((entry = dcb_gpio_entry(dev, idx, ++i, &version))) { 86 if (version < 0x40) { 87 u16 data = ROM16(entry[0]); 88 *gpio = (struct gpio_func) { 89 .line = (data & 0x001f) >> 0, 90 .func = (data & 0x07e0) >> 5, 91 .log[0] = (data & 0x1800) >> 11, 92 .log[1] = (data & 0x6000) >> 13, 93 }; 94 } else 95 if (version < 0x41) { 96 *gpio = (struct gpio_func) { 97 .line = entry[0] & 0x1f, 98 .func = entry[1], 99 .log[0] = (entry[3] & 0x18) >> 3, 100 .log[1] = (entry[3] & 0x60) >> 5, 101 }; 102 } else { 103 *gpio = (struct gpio_func) { 104 .line = entry[0] & 0x3f, 105 .func = entry[1], 106 .log[0] = (entry[4] & 0x30) >> 4, 107 .log[1] = (entry[4] & 0xc0) >> 6, 108 }; 109 } 110 111 if ((line == 0xff || line == gpio->line) && 112 (func == 0xff || func == gpio->func)) 113 return 0; 114 } 115 116 /* DCB 2.2, fixed TVDAC GPIO data */ 117 if ((table = dcb_table(dev)) && table[0] >= 0x22) { 118 if (func == DCB_GPIO_TVDAC0) { 119 *gpio = (struct gpio_func) { 120 .func = DCB_GPIO_TVDAC0, 121 .line = table[-4] >> 4, 122 .log[0] = !!(table[-5] & 2), 123 .log[1] = !(table[-5] & 2), 124 }; 125 return 0; 126 } 127 } 128 129 /* Apple iMac G4 NV18 */ 130 if (nv_match_device(dev, 0x0189, 0x10de, 0x0010)) { 131 if (func == DCB_GPIO_TVDAC0) { 132 *gpio = (struct gpio_func) { 133 .func = DCB_GPIO_TVDAC0, 134 .line = 4, 135 .log[0] = 0, 136 .log[1] = 1, 137 }; 138 return 0; 139 } 140 } 141 142 return -EINVAL; 143} 144 145int 146nouveau_gpio_set(struct drm_device *dev, int idx, u8 tag, u8 line, int state) 147{ 148 struct gpio_func gpio; 149 int ret; 150 151 ret = nouveau_gpio_find(dev, idx, tag, line, &gpio); 152 if (ret == 0) { 153 int dir = !!(gpio.log[state] & 0x02); 154 int out = !!(gpio.log[state] & 0x01); 155 ret = nouveau_gpio_drive(dev, idx, gpio.line, dir, out); 156 } 157 158 return ret; 159} 160 161int 162nouveau_gpio_get(struct drm_device *dev, int idx, u8 tag, u8 line) 163{ 164 struct gpio_func gpio; 165 int ret; 166 167 ret = nouveau_gpio_find(dev, idx, tag, line, &gpio); 168 if (ret == 0) { 169 ret = nouveau_gpio_sense(dev, idx, gpio.line); 170 if (ret >= 0) 171 ret = (ret == (gpio.log[1] & 1)); 172 } 173 174 return ret; 175} 176 177int 178nouveau_gpio_irq(struct drm_device *dev, int idx, u8 tag, u8 line, bool on) 179{ 180 struct drm_nouveau_private *dev_priv = dev->dev_private; 181 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; 182 struct gpio_func gpio; 183 int ret; 184 185 ret = nouveau_gpio_find(dev, idx, tag, line, &gpio); 186 if (ret == 0) { 187 if (idx == 0 && pgpio->irq_enable) 188 pgpio->irq_enable(dev, gpio.line, on); 189 else 190 ret = -ENODEV; 191 } 192 193 return ret; 194} 195 196struct gpio_isr { 197 struct drm_device *dev; 198 struct list_head head; 199 struct work_struct work; 200 int idx; 201 struct gpio_func func; 202 void (*handler)(void *, int); 203 void *data; 204 bool inhibit; 205}; 206 207static void 208nouveau_gpio_isr_bh(struct work_struct *work) 209{ 210 struct gpio_isr *isr = container_of(work, struct gpio_isr, work); 211 struct drm_device *dev = isr->dev; 212 struct drm_nouveau_private *dev_priv = dev->dev_private; 213 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; 214 unsigned long flags; 215 int state; 216 217 state = nouveau_gpio_get(dev, isr->idx, isr->func.func, isr->func.line); 218 if (state >= 0) 219 isr->handler(isr->data, state); 220 221 spin_lock_irqsave(&pgpio->lock, flags); 222 isr->inhibit = false; 223 spin_unlock_irqrestore(&pgpio->lock, flags); 224} 225 226void 227nouveau_gpio_isr(struct drm_device *dev, int idx, u32 line_mask) 228{ 229 struct drm_nouveau_private *dev_priv = dev->dev_private; 230 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; 231 struct gpio_isr *isr; 232 233 if (idx != 0) 234 return; 235 236 spin_lock(&pgpio->lock); 237 list_for_each_entry(isr, &pgpio->isr, head) { 238 if (line_mask & (1 << isr->func.line)) { 239 if (isr->inhibit) 240 continue; 241 isr->inhibit = true; 242 schedule_work(&isr->work); 243 } 244 } 245 spin_unlock(&pgpio->lock); 246} 247 248int 249nouveau_gpio_isr_add(struct drm_device *dev, int idx, u8 tag, u8 line, 250 void (*handler)(void *, int), void *data) 251{ 252 struct drm_nouveau_private *dev_priv = dev->dev_private; 253 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; 254 struct gpio_isr *isr; 255 unsigned long flags; 256 int ret; 257 258 isr = kzalloc(sizeof(*isr), GFP_KERNEL); 259 if (!isr) 260 return -ENOMEM; 261 262 ret = nouveau_gpio_find(dev, idx, tag, line, &isr->func); 263 if (ret) { 264 kfree(isr); 265 return ret; 266 } 267 268 INIT_WORK(&isr->work, nouveau_gpio_isr_bh); 269 isr->dev = dev; 270 isr->handler = handler; 271 isr->data = data; 272 isr->idx = idx; 273 274 spin_lock_irqsave(&pgpio->lock, flags); 275 list_add(&isr->head, &pgpio->isr); 276 spin_unlock_irqrestore(&pgpio->lock, flags); 277 return 0; 278} 279 280void 281nouveau_gpio_isr_del(struct drm_device *dev, int idx, u8 tag, u8 line, 282 void (*handler)(void *, int), void *data) 283{ 284 struct drm_nouveau_private *dev_priv = dev->dev_private; 285 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; 286 struct gpio_isr *isr, *tmp; 287 struct gpio_func func; 288 unsigned long flags; 289 LIST_HEAD(tofree); 290 int ret; 291 292 ret = nouveau_gpio_find(dev, idx, tag, line, &func); 293 if (ret == 0) { 294 spin_lock_irqsave(&pgpio->lock, flags); 295 list_for_each_entry_safe(isr, tmp, &pgpio->isr, head) { 296 if (memcmp(&isr->func, &func, sizeof(func)) || 297 isr->idx != idx || 298 isr->handler != handler || isr->data != data) 299 continue; 300 list_move(&isr->head, &tofree); 301 } 302 spin_unlock_irqrestore(&pgpio->lock, flags); 303 304 list_for_each_entry_safe(isr, tmp, &tofree, head) { 305 flush_work_sync(&isr->work); 306 kfree(isr); 307 } 308 } 309} 310 311int 312nouveau_gpio_create(struct drm_device *dev) 313{ 314 struct drm_nouveau_private *dev_priv = dev->dev_private; 315 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; 316 317 INIT_LIST_HEAD(&pgpio->isr); 318 spin_lock_init(&pgpio->lock); 319 320 return nouveau_gpio_init(dev); 321} 322 323void 324nouveau_gpio_destroy(struct drm_device *dev) 325{ 326 struct drm_nouveau_private *dev_priv = dev->dev_private; 327 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; 328 329 nouveau_gpio_fini(dev); 330 BUG_ON(!list_empty(&pgpio->isr)); 331} 332 333int 334nouveau_gpio_init(struct drm_device *dev) 335{ 336 struct drm_nouveau_private *dev_priv = dev->dev_private; 337 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; 338 int ret = 0; 339 340 if (pgpio->init) 341 ret = pgpio->init(dev); 342 343 return ret; 344} 345 346void 347nouveau_gpio_fini(struct drm_device *dev) 348{ 349 struct drm_nouveau_private *dev_priv = dev->dev_private; 350 struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; 351 352 if (pgpio->fini) 353 pgpio->fini(dev); 354} 355 356void 357nouveau_gpio_reset(struct drm_device *dev) 358{ 359 struct drm_nouveau_private *dev_priv = dev->dev_private; 360 u8 *entry, version; 361 int ent = -1; 362 363 while ((entry = dcb_gpio_entry(dev, 0, ++ent, &version))) { 364 u8 func = 0xff, line, defs, unk0, unk1; 365 if (version >= 0x41) { 366 defs = !!(entry[0] & 0x80); 367 line = entry[0] & 0x3f; 368 func = entry[1]; 369 unk0 = entry[2]; 370 unk1 = entry[3] & 0x1f; 371 } else 372 if (version >= 0x40) { 373 line = entry[0] & 0x1f; 374 func = entry[1]; 375 defs = !!(entry[3] & 0x01); 376 unk0 = !!(entry[3] & 0x02); 377 unk1 = !!(entry[3] & 0x04); 378 } else { 379 break; 380 } 381 382 if (func == 0xff) 383 continue; 384 385 nouveau_gpio_func_set(dev, func, defs); 386 387 if (dev_priv->card_type >= NV_D0) { 388 nv_mask(dev, 0x00d610 + (line * 4), 0xff, unk0); 389 if (unk1--) 390 nv_mask(dev, 0x00d640 + (unk1 * 4), 0xff, line); 391 } else 392 if (dev_priv->card_type >= NV_50) { 393 static const u32 regs[] = { 0xe100, 0xe28c }; 394 u32 val = (unk1 << 16) | unk0; 395 u32 reg = regs[line >> 4]; line &= 0x0f; 396 397 nv_mask(dev, reg, 0x00010001 << line, val << line); 398 } 399 } 400} 401