1796 lines · c
1/*2 * Copyright 2012 Red Hat Inc.3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice shall be included in12 * all copies or substantial portions of the Software.13 *14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR18 * 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 OR20 * OTHER DEALINGS IN THE SOFTWARE.21 *22 * Authors: Ben Skeggs23 */24#include "priv.h"25#include "chan.h"26#include "conn.h"27#include "head.h"28#include "dp.h"29#include "ior.h"30#include "outp.h"31 32#include <core/client.h>33#include <core/ramht.h>34#include <subdev/bios.h>35#include <subdev/bios/disp.h>36#include <subdev/bios/init.h>37#include <subdev/bios/pll.h>38#include <subdev/devinit.h>39#include <subdev/i2c.h>40#include <subdev/mmu.h>41#include <subdev/timer.h>42 43#include <nvif/class.h>44#include <nvif/unpack.h>45 46static void47nv50_pior_clock(struct nvkm_ior *pior)48{49 struct nvkm_device *device = pior->disp->engine.subdev.device;50 const u32 poff = nv50_ior_base(pior);51 52 nvkm_mask(device, 0x614380 + poff, 0x00000707, 0x00000001);53}54 55static int56nv50_pior_dp_links(struct nvkm_ior *pior, struct nvkm_i2c_aux *aux)57{58 int ret = nvkm_i2c_aux_lnk_ctl(aux, pior->dp.nr, pior->dp.bw, pior->dp.ef);59 if (ret)60 return ret;61 62 return 1;63}64 65static const struct nvkm_ior_func_dp66nv50_pior_dp = {67 .links = nv50_pior_dp_links,68};69 70static void71nv50_pior_power_wait(struct nvkm_device *device, u32 poff)72{73 nvkm_msec(device, 2000,74 if (!(nvkm_rd32(device, 0x61e004 + poff) & 0x80000000))75 break;76 );77}78 79static void80nv50_pior_power(struct nvkm_ior *pior, bool normal, bool pu, bool data, bool vsync, bool hsync)81{82 struct nvkm_device *device = pior->disp->engine.subdev.device;83 const u32 poff = nv50_ior_base(pior);84 const u32 shift = normal ? 0 : 16;85 const u32 state = 0x80000000 | (0x00000001 * !!pu) << shift;86 const u32 field = 0x80000000 | (0x00000101 << shift);87 88 nv50_pior_power_wait(device, poff);89 nvkm_mask(device, 0x61e004 + poff, field, state);90 nv50_pior_power_wait(device, poff);91}92 93void94nv50_pior_depth(struct nvkm_ior *ior, struct nvkm_ior_state *state, u32 ctrl)95{96 /* GF119 moves this information to per-head methods, which is97 * a lot more convenient, and where our shared code expect it.98 */99 if (state->head && state == &ior->asy) {100 struct nvkm_head *head = nvkm_head_find(ior->disp, __ffs(state->head));101 102 if (!WARN_ON(!head)) {103 struct nvkm_head_state *state = &head->asy;104 switch ((ctrl & 0x000f0000) >> 16) {105 case 6: state->or.depth = 30; break;106 case 5: state->or.depth = 24; break;107 case 2: state->or.depth = 18; break;108 case 0: state->or.depth = 18; break; /*XXX*/109 default:110 state->or.depth = 18;111 WARN_ON(1);112 break;113 }114 }115 }116}117 118static void119nv50_pior_state(struct nvkm_ior *pior, struct nvkm_ior_state *state)120{121 struct nvkm_device *device = pior->disp->engine.subdev.device;122 const u32 coff = pior->id * 8 + (state == &pior->arm) * 4;123 u32 ctrl = nvkm_rd32(device, 0x610b80 + coff);124 125 state->proto_evo = (ctrl & 0x00000f00) >> 8;126 state->rgdiv = 1;127 switch (state->proto_evo) {128 case 0: state->proto = TMDS; break;129 default:130 state->proto = UNKNOWN;131 break;132 }133 134 state->head = ctrl & 0x00000003;135 nv50_pior_depth(pior, state, ctrl);136}137 138static const struct nvkm_ior_func139nv50_pior = {140 .state = nv50_pior_state,141 .power = nv50_pior_power,142 .clock = nv50_pior_clock,143 .dp = &nv50_pior_dp,144};145 146int147nv50_pior_new(struct nvkm_disp *disp, int id)148{149 return nvkm_ior_new_(&nv50_pior, disp, PIOR, id, false);150}151 152int153nv50_pior_cnt(struct nvkm_disp *disp, unsigned long *pmask)154{155 struct nvkm_device *device = disp->engine.subdev.device;156 157 *pmask = (nvkm_rd32(device, 0x610184) & 0x70000000) >> 28;158 return 3;159}160 161static int162nv50_sor_bl_set(struct nvkm_ior *ior, int lvl)163{164 struct nvkm_device *device = ior->disp->engine.subdev.device;165 const u32 soff = nv50_ior_base(ior);166 u32 div = 1025;167 u32 val = (lvl * div) / 100;168 169 nvkm_wr32(device, 0x61c084 + soff, 0x80000000 | val);170 return 0;171}172 173static int174nv50_sor_bl_get(struct nvkm_ior *ior)175{176 struct nvkm_device *device = ior->disp->engine.subdev.device;177 const u32 soff = nv50_ior_base(ior);178 u32 div = 1025;179 u32 val;180 181 val = nvkm_rd32(device, 0x61c084 + soff);182 val &= 0x000007ff;183 return ((val * 100) + (div / 2)) / div;184}185 186const struct nvkm_ior_func_bl187nv50_sor_bl = {188 .get = nv50_sor_bl_get,189 .set = nv50_sor_bl_set,190};191 192void193nv50_sor_clock(struct nvkm_ior *sor)194{195 struct nvkm_device *device = sor->disp->engine.subdev.device;196 const int div = sor->asy.link == 3;197 const u32 soff = nv50_ior_base(sor);198 199 nvkm_mask(device, 0x614300 + soff, 0x00000707, (div << 8) | div);200}201 202static void203nv50_sor_power_wait(struct nvkm_device *device, u32 soff)204{205 nvkm_msec(device, 2000,206 if (!(nvkm_rd32(device, 0x61c004 + soff) & 0x80000000))207 break;208 );209}210 211void212nv50_sor_power(struct nvkm_ior *sor, bool normal, bool pu, bool data, bool vsync, bool hsync)213{214 struct nvkm_device *device = sor->disp->engine.subdev.device;215 const u32 soff = nv50_ior_base(sor);216 const u32 shift = normal ? 0 : 16;217 const u32 state = 0x80000000 | (0x00000001 * !!pu) << shift;218 const u32 field = 0x80000000 | (0x00000001 << shift);219 220 nv50_sor_power_wait(device, soff);221 nvkm_mask(device, 0x61c004 + soff, field, state);222 nv50_sor_power_wait(device, soff);223 224 nvkm_msec(device, 2000,225 if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))226 break;227 );228}229 230void231nv50_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state)232{233 struct nvkm_device *device = sor->disp->engine.subdev.device;234 const u32 coff = sor->id * 8 + (state == &sor->arm) * 4;235 u32 ctrl = nvkm_rd32(device, 0x610b70 + coff);236 237 state->proto_evo = (ctrl & 0x00000f00) >> 8;238 switch (state->proto_evo) {239 case 0: state->proto = LVDS; state->link = 1; break;240 case 1: state->proto = TMDS; state->link = 1; break;241 case 2: state->proto = TMDS; state->link = 2; break;242 case 5: state->proto = TMDS; state->link = 3; break;243 default:244 state->proto = UNKNOWN;245 break;246 }247 248 state->head = ctrl & 0x00000003;249}250 251static const struct nvkm_ior_func252nv50_sor = {253 .state = nv50_sor_state,254 .power = nv50_sor_power,255 .clock = nv50_sor_clock,256 .bl = &nv50_sor_bl,257};258 259static int260nv50_sor_new(struct nvkm_disp *disp, int id)261{262 return nvkm_ior_new_(&nv50_sor, disp, SOR, id, false);263}264 265int266nv50_sor_cnt(struct nvkm_disp *disp, unsigned long *pmask)267{268 struct nvkm_device *device = disp->engine.subdev.device;269 270 *pmask = (nvkm_rd32(device, 0x610184) & 0x03000000) >> 24;271 return 2;272}273 274static void275nv50_dac_clock(struct nvkm_ior *dac)276{277 struct nvkm_device *device = dac->disp->engine.subdev.device;278 const u32 doff = nv50_ior_base(dac);279 280 nvkm_mask(device, 0x614280 + doff, 0x07070707, 0x00000000);281}282 283int284nv50_dac_sense(struct nvkm_ior *dac, u32 loadval)285{286 struct nvkm_device *device = dac->disp->engine.subdev.device;287 const u32 doff = nv50_ior_base(dac);288 289 dac->func->power(dac, false, true, false, false, false);290 291 nvkm_wr32(device, 0x61a00c + doff, 0x00100000 | loadval);292 mdelay(9);293 udelay(500);294 loadval = nvkm_mask(device, 0x61a00c + doff, 0xffffffff, 0x00000000);295 296 dac->func->power(dac, false, false, false, false, false);297 if (!(loadval & 0x80000000))298 return -ETIMEDOUT;299 300 return (loadval & 0x38000000) >> 27;301}302 303static void304nv50_dac_power_wait(struct nvkm_device *device, const u32 doff)305{306 nvkm_msec(device, 2000,307 if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000))308 break;309 );310}311 312void313nv50_dac_power(struct nvkm_ior *dac, bool normal, bool pu, bool data, bool vsync, bool hsync)314{315 struct nvkm_device *device = dac->disp->engine.subdev.device;316 const u32 doff = nv50_ior_base(dac);317 const u32 shift = normal ? 0 : 16;318 const u32 state = 0x80000000 | (0x00000040 * ! pu |319 0x00000010 * ! data |320 0x00000004 * ! vsync |321 0x00000001 * ! hsync) << shift;322 const u32 field = 0xc0000000 | (0x00000055 << shift);323 324 nv50_dac_power_wait(device, doff);325 nvkm_mask(device, 0x61a004 + doff, field, state);326 nv50_dac_power_wait(device, doff);327}328 329static void330nv50_dac_state(struct nvkm_ior *dac, struct nvkm_ior_state *state)331{332 struct nvkm_device *device = dac->disp->engine.subdev.device;333 const u32 coff = dac->id * 8 + (state == &dac->arm) * 4;334 u32 ctrl = nvkm_rd32(device, 0x610b58 + coff);335 336 state->proto_evo = (ctrl & 0x00000f00) >> 8;337 switch (state->proto_evo) {338 case 0: state->proto = CRT; break;339 default:340 state->proto = UNKNOWN;341 break;342 }343 344 state->head = ctrl & 0x00000003;345}346 347static const struct nvkm_ior_func348nv50_dac = {349 .state = nv50_dac_state,350 .power = nv50_dac_power,351 .sense = nv50_dac_sense,352 .clock = nv50_dac_clock,353};354 355int356nv50_dac_new(struct nvkm_disp *disp, int id)357{358 return nvkm_ior_new_(&nv50_dac, disp, DAC, id, false);359}360 361int362nv50_dac_cnt(struct nvkm_disp *disp, unsigned long *pmask)363{364 struct nvkm_device *device = disp->engine.subdev.device;365 366 *pmask = (nvkm_rd32(device, 0x610184) & 0x00700000) >> 20;367 return 3;368}369 370static void371nv50_head_vblank_put(struct nvkm_head *head)372{373 struct nvkm_device *device = head->disp->engine.subdev.device;374 375 nvkm_mask(device, 0x61002c, (4 << head->id), 0);376}377 378static void379nv50_head_vblank_get(struct nvkm_head *head)380{381 struct nvkm_device *device = head->disp->engine.subdev.device;382 383 nvkm_mask(device, 0x61002c, (4 << head->id), (4 << head->id));384}385 386static void387nv50_head_rgclk(struct nvkm_head *head, int div)388{389 struct nvkm_device *device = head->disp->engine.subdev.device;390 391 nvkm_mask(device, 0x614200 + (head->id * 0x800), 0x0000000f, div);392}393 394void395nv50_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline)396{397 struct nvkm_device *device = head->disp->engine.subdev.device;398 const u32 hoff = head->id * 0x800;399 400 /* vline read locks hline. */401 *vline = nvkm_rd32(device, 0x616340 + hoff) & 0x0000ffff;402 *hline = nvkm_rd32(device, 0x616344 + hoff) & 0x0000ffff;403}404 405static void406nv50_head_state(struct nvkm_head *head, struct nvkm_head_state *state)407{408 struct nvkm_device *device = head->disp->engine.subdev.device;409 const u32 hoff = head->id * 0x540 + (state == &head->arm) * 4;410 u32 data;411 412 data = nvkm_rd32(device, 0x610ae8 + hoff);413 state->vblanke = (data & 0xffff0000) >> 16;414 state->hblanke = (data & 0x0000ffff);415 data = nvkm_rd32(device, 0x610af0 + hoff);416 state->vblanks = (data & 0xffff0000) >> 16;417 state->hblanks = (data & 0x0000ffff);418 data = nvkm_rd32(device, 0x610af8 + hoff);419 state->vtotal = (data & 0xffff0000) >> 16;420 state->htotal = (data & 0x0000ffff);421 data = nvkm_rd32(device, 0x610b00 + hoff);422 state->vsynce = (data & 0xffff0000) >> 16;423 state->hsynce = (data & 0x0000ffff);424 state->hz = (nvkm_rd32(device, 0x610ad0 + hoff) & 0x003fffff) * 1000;425}426 427static const struct nvkm_head_func428nv50_head = {429 .state = nv50_head_state,430 .rgpos = nv50_head_rgpos,431 .rgclk = nv50_head_rgclk,432 .vblank_get = nv50_head_vblank_get,433 .vblank_put = nv50_head_vblank_put,434};435 436int437nv50_head_new(struct nvkm_disp *disp, int id)438{439 return nvkm_head_new_(&nv50_head, disp, id);440}441 442int443nv50_head_cnt(struct nvkm_disp *disp, unsigned long *pmask)444{445 *pmask = 3;446 return 2;447}448 449 450static void451nv50_disp_mthd_list(struct nvkm_disp *disp, int debug, u32 base, int c,452 const struct nvkm_disp_mthd_list *list, int inst)453{454 struct nvkm_subdev *subdev = &disp->engine.subdev;455 struct nvkm_device *device = subdev->device;456 int i;457 458 for (i = 0; list->data[i].mthd; i++) {459 if (list->data[i].addr) {460 u32 next = nvkm_rd32(device, list->data[i].addr + base + 0);461 u32 prev = nvkm_rd32(device, list->data[i].addr + base + c);462 u32 mthd = list->data[i].mthd + (list->mthd * inst);463 const char *name = list->data[i].name;464 char mods[16];465 466 if (prev != next)467 snprintf(mods, sizeof(mods), "-> %08x", next);468 else469 snprintf(mods, sizeof(mods), "%13c", ' ');470 471 nvkm_printk_(subdev, debug, info,472 "\t%04x: %08x %s%s%s\n",473 mthd, prev, mods, name ? " // " : "",474 name ? name : "");475 }476 }477}478 479void480nv50_disp_chan_mthd(struct nvkm_disp_chan *chan, int debug)481{482 struct nvkm_disp *disp = chan->disp;483 struct nvkm_subdev *subdev = &disp->engine.subdev;484 const struct nvkm_disp_chan_mthd *mthd = chan->mthd;485 const struct nvkm_disp_mthd_list *list;486 int i, j;487 488 if (debug > subdev->debug)489 return;490 if (!mthd)491 return;492 493 for (i = 0; (list = mthd->data[i].mthd) != NULL; i++) {494 u32 base = chan->head * mthd->addr;495 for (j = 0; j < mthd->data[i].nr; j++, base += list->addr) {496 const char *cname = mthd->name;497 const char *sname = "";498 char cname_[16], sname_[16];499 500 if (mthd->addr) {501 snprintf(cname_, sizeof(cname_), "%s %d",502 mthd->name, chan->chid.user);503 cname = cname_;504 }505 506 if (mthd->data[i].nr > 1) {507 snprintf(sname_, sizeof(sname_), " - %s %d",508 mthd->data[i].name, j);509 sname = sname_;510 }511 512 nvkm_printk_(subdev, debug, info, "%s%s:\n", cname, sname);513 nv50_disp_mthd_list(disp, debug, base, mthd->prev,514 list, j);515 }516 }517}518 519static void520nv50_disp_chan_uevent_fini(struct nvkm_event *event, int type, int index)521{522 struct nvkm_disp *disp = container_of(event, typeof(*disp), uevent);523 struct nvkm_device *device = disp->engine.subdev.device;524 nvkm_mask(device, 0x610028, 0x00000001 << index, 0x00000000 << index);525 nvkm_wr32(device, 0x610020, 0x00000001 << index);526}527 528static void529nv50_disp_chan_uevent_init(struct nvkm_event *event, int types, int index)530{531 struct nvkm_disp *disp = container_of(event, typeof(*disp), uevent);532 struct nvkm_device *device = disp->engine.subdev.device;533 nvkm_wr32(device, 0x610020, 0x00000001 << index);534 nvkm_mask(device, 0x610028, 0x00000001 << index, 0x00000001 << index);535}536 537void538nv50_disp_chan_uevent_send(struct nvkm_disp *disp, int chid)539{540 nvkm_event_ntfy(&disp->uevent, chid, NVKM_DISP_EVENT_CHAN_AWAKEN);541}542 543const struct nvkm_event_func544nv50_disp_chan_uevent = {545 .init = nv50_disp_chan_uevent_init,546 .fini = nv50_disp_chan_uevent_fini,547};548 549u64550nv50_disp_chan_user(struct nvkm_disp_chan *chan, u64 *psize)551{552 *psize = 0x1000;553 return 0x640000 + (chan->chid.user * 0x1000);554}555 556void557nv50_disp_chan_intr(struct nvkm_disp_chan *chan, bool en)558{559 struct nvkm_device *device = chan->disp->engine.subdev.device;560 const u32 mask = 0x00010001 << chan->chid.user;561 const u32 data = en ? 0x00010000 << chan->chid.user : 0x00000000;562 nvkm_mask(device, 0x610028, mask, data);563}564 565static void566nv50_disp_pioc_fini(struct nvkm_disp_chan *chan)567{568 struct nvkm_disp *disp = chan->disp;569 struct nvkm_subdev *subdev = &disp->engine.subdev;570 struct nvkm_device *device = subdev->device;571 int ctrl = chan->chid.ctrl;572 int user = chan->chid.user;573 574 nvkm_mask(device, 0x610200 + (ctrl * 0x10), 0x00000001, 0x00000000);575 if (nvkm_msec(device, 2000,576 if (!(nvkm_rd32(device, 0x610200 + (ctrl * 0x10)) & 0x00030000))577 break;578 ) < 0) {579 nvkm_error(subdev, "ch %d timeout: %08x\n", user,580 nvkm_rd32(device, 0x610200 + (ctrl * 0x10)));581 }582}583 584static int585nv50_disp_pioc_init(struct nvkm_disp_chan *chan)586{587 struct nvkm_disp *disp = chan->disp;588 struct nvkm_subdev *subdev = &disp->engine.subdev;589 struct nvkm_device *device = subdev->device;590 int ctrl = chan->chid.ctrl;591 int user = chan->chid.user;592 593 nvkm_wr32(device, 0x610200 + (ctrl * 0x10), 0x00002000);594 if (nvkm_msec(device, 2000,595 if (!(nvkm_rd32(device, 0x610200 + (ctrl * 0x10)) & 0x00030000))596 break;597 ) < 0) {598 nvkm_error(subdev, "ch %d timeout0: %08x\n", user,599 nvkm_rd32(device, 0x610200 + (ctrl * 0x10)));600 return -EBUSY;601 }602 603 nvkm_wr32(device, 0x610200 + (ctrl * 0x10), 0x00000001);604 if (nvkm_msec(device, 2000,605 u32 tmp = nvkm_rd32(device, 0x610200 + (ctrl * 0x10));606 if ((tmp & 0x00030000) == 0x00010000)607 break;608 ) < 0) {609 nvkm_error(subdev, "ch %d timeout1: %08x\n", user,610 nvkm_rd32(device, 0x610200 + (ctrl * 0x10)));611 return -EBUSY;612 }613 614 return 0;615}616 617const struct nvkm_disp_chan_func618nv50_disp_pioc_func = {619 .init = nv50_disp_pioc_init,620 .fini = nv50_disp_pioc_fini,621 .intr = nv50_disp_chan_intr,622 .user = nv50_disp_chan_user,623};624 625int626nv50_disp_dmac_bind(struct nvkm_disp_chan *chan, struct nvkm_object *object, u32 handle)627{628 return nvkm_ramht_insert(chan->disp->ramht, object, chan->chid.user, -10, handle,629 chan->chid.user << 28 | chan->chid.user);630}631 632static void633nv50_disp_dmac_fini(struct nvkm_disp_chan *chan)634{635 struct nvkm_subdev *subdev = &chan->disp->engine.subdev;636 struct nvkm_device *device = subdev->device;637 int ctrl = chan->chid.ctrl;638 int user = chan->chid.user;639 640 /* deactivate channel */641 nvkm_mask(device, 0x610200 + (ctrl * 0x0010), 0x00001010, 0x00001000);642 nvkm_mask(device, 0x610200 + (ctrl * 0x0010), 0x00000003, 0x00000000);643 if (nvkm_msec(device, 2000,644 if (!(nvkm_rd32(device, 0x610200 + (ctrl * 0x10)) & 0x001e0000))645 break;646 ) < 0) {647 nvkm_error(subdev, "ch %d fini timeout, %08x\n", user,648 nvkm_rd32(device, 0x610200 + (ctrl * 0x10)));649 }650 651 chan->suspend_put = nvkm_rd32(device, 0x640000 + (ctrl * 0x1000));652}653 654static int655nv50_disp_dmac_init(struct nvkm_disp_chan *chan)656{657 struct nvkm_subdev *subdev = &chan->disp->engine.subdev;658 struct nvkm_device *device = subdev->device;659 int ctrl = chan->chid.ctrl;660 int user = chan->chid.user;661 662 /* initialise channel for dma command submission */663 nvkm_wr32(device, 0x610204 + (ctrl * 0x0010), chan->push);664 nvkm_wr32(device, 0x610208 + (ctrl * 0x0010), 0x00010000);665 nvkm_wr32(device, 0x61020c + (ctrl * 0x0010), ctrl);666 nvkm_mask(device, 0x610200 + (ctrl * 0x0010), 0x00000010, 0x00000010);667 nvkm_wr32(device, 0x640000 + (ctrl * 0x1000), chan->suspend_put);668 nvkm_wr32(device, 0x610200 + (ctrl * 0x0010), 0x00000013);669 670 /* wait for it to go inactive */671 if (nvkm_msec(device, 2000,672 if (!(nvkm_rd32(device, 0x610200 + (ctrl * 0x10)) & 0x80000000))673 break;674 ) < 0) {675 nvkm_error(subdev, "ch %d init timeout, %08x\n", user,676 nvkm_rd32(device, 0x610200 + (ctrl * 0x10)));677 return -EBUSY;678 }679 680 return 0;681}682 683int684nv50_disp_dmac_push(struct nvkm_disp_chan *chan, u64 object)685{686 chan->memory = nvkm_umem_search(chan->object.client, object);687 if (IS_ERR(chan->memory))688 return PTR_ERR(chan->memory);689 690 if (nvkm_memory_size(chan->memory) < 0x1000)691 return -EINVAL;692 693 switch (nvkm_memory_target(chan->memory)) {694 case NVKM_MEM_TARGET_VRAM: chan->push = 0x00000001; break;695 case NVKM_MEM_TARGET_NCOH: chan->push = 0x00000002; break;696 case NVKM_MEM_TARGET_HOST: chan->push = 0x00000003; break;697 default:698 return -EINVAL;699 }700 701 chan->push |= nvkm_memory_addr(chan->memory) >> 8;702 return 0;703}704 705const struct nvkm_disp_chan_func706nv50_disp_dmac_func = {707 .push = nv50_disp_dmac_push,708 .init = nv50_disp_dmac_init,709 .fini = nv50_disp_dmac_fini,710 .intr = nv50_disp_chan_intr,711 .user = nv50_disp_chan_user,712 .bind = nv50_disp_dmac_bind,713};714 715const struct nvkm_disp_chan_user716nv50_disp_curs = {717 .func = &nv50_disp_pioc_func,718 .ctrl = 7,719 .user = 7,720};721 722const struct nvkm_disp_chan_user723nv50_disp_oimm = {724 .func = &nv50_disp_pioc_func,725 .ctrl = 5,726 .user = 5,727};728 729static const struct nvkm_disp_mthd_list730nv50_disp_ovly_mthd_base = {731 .mthd = 0x0000,732 .addr = 0x000000,733 .data = {734 { 0x0080, 0x000000 },735 { 0x0084, 0x0009a0 },736 { 0x0088, 0x0009c0 },737 { 0x008c, 0x0009c8 },738 { 0x0090, 0x6109b4 },739 { 0x0094, 0x610970 },740 { 0x00a0, 0x610998 },741 { 0x00a4, 0x610964 },742 { 0x00c0, 0x610958 },743 { 0x00e0, 0x6109a8 },744 { 0x00e4, 0x6109d0 },745 { 0x00e8, 0x6109d8 },746 { 0x0100, 0x61094c },747 { 0x0104, 0x610984 },748 { 0x0108, 0x61098c },749 { 0x0800, 0x6109f8 },750 { 0x0808, 0x610a08 },751 { 0x080c, 0x610a10 },752 { 0x0810, 0x610a00 },753 {}754 }755};756 757static const struct nvkm_disp_chan_mthd758nv50_disp_ovly_mthd = {759 .name = "Overlay",760 .addr = 0x000540,761 .prev = 0x000004,762 .data = {763 { "Global", 1, &nv50_disp_ovly_mthd_base },764 {}765 }766};767 768static const struct nvkm_disp_chan_user769nv50_disp_ovly = {770 .func = &nv50_disp_dmac_func,771 .ctrl = 3,772 .user = 3,773 .mthd = &nv50_disp_ovly_mthd,774};775 776static const struct nvkm_disp_mthd_list777nv50_disp_base_mthd_base = {778 .mthd = 0x0000,779 .addr = 0x000000,780 .data = {781 { 0x0080, 0x000000 },782 { 0x0084, 0x0008c4 },783 { 0x0088, 0x0008d0 },784 { 0x008c, 0x0008dc },785 { 0x0090, 0x0008e4 },786 { 0x0094, 0x610884 },787 { 0x00a0, 0x6108a0 },788 { 0x00a4, 0x610878 },789 { 0x00c0, 0x61086c },790 { 0x00e0, 0x610858 },791 { 0x00e4, 0x610860 },792 { 0x00e8, 0x6108ac },793 { 0x00ec, 0x6108b4 },794 { 0x0100, 0x610894 },795 { 0x0110, 0x6108bc },796 { 0x0114, 0x61088c },797 {}798 }799};800 801const struct nvkm_disp_mthd_list802nv50_disp_base_mthd_image = {803 .mthd = 0x0400,804 .addr = 0x000000,805 .data = {806 { 0x0800, 0x6108f0 },807 { 0x0804, 0x6108fc },808 { 0x0808, 0x61090c },809 { 0x080c, 0x610914 },810 { 0x0810, 0x610904 },811 {}812 }813};814 815static const struct nvkm_disp_chan_mthd816nv50_disp_base_mthd = {817 .name = "Base",818 .addr = 0x000540,819 .prev = 0x000004,820 .data = {821 { "Global", 1, &nv50_disp_base_mthd_base },822 { "Image", 2, &nv50_disp_base_mthd_image },823 {}824 }825};826 827static const struct nvkm_disp_chan_user828nv50_disp_base = {829 .func = &nv50_disp_dmac_func,830 .ctrl = 1,831 .user = 1,832 .mthd = &nv50_disp_base_mthd,833};834 835const struct nvkm_disp_mthd_list836nv50_disp_core_mthd_base = {837 .mthd = 0x0000,838 .addr = 0x000000,839 .data = {840 { 0x0080, 0x000000 },841 { 0x0084, 0x610bb8 },842 { 0x0088, 0x610b9c },843 { 0x008c, 0x000000 },844 {}845 }846};847 848static const struct nvkm_disp_mthd_list849nv50_disp_core_mthd_dac = {850 .mthd = 0x0080,851 .addr = 0x000008,852 .data = {853 { 0x0400, 0x610b58 },854 { 0x0404, 0x610bdc },855 { 0x0420, 0x610828 },856 {}857 }858};859 860const struct nvkm_disp_mthd_list861nv50_disp_core_mthd_sor = {862 .mthd = 0x0040,863 .addr = 0x000008,864 .data = {865 { 0x0600, 0x610b70 },866 {}867 }868};869 870const struct nvkm_disp_mthd_list871nv50_disp_core_mthd_pior = {872 .mthd = 0x0040,873 .addr = 0x000008,874 .data = {875 { 0x0700, 0x610b80 },876 {}877 }878};879 880static const struct nvkm_disp_mthd_list881nv50_disp_core_mthd_head = {882 .mthd = 0x0400,883 .addr = 0x000540,884 .data = {885 { 0x0800, 0x610ad8 },886 { 0x0804, 0x610ad0 },887 { 0x0808, 0x610a48 },888 { 0x080c, 0x610a78 },889 { 0x0810, 0x610ac0 },890 { 0x0814, 0x610af8 },891 { 0x0818, 0x610b00 },892 { 0x081c, 0x610ae8 },893 { 0x0820, 0x610af0 },894 { 0x0824, 0x610b08 },895 { 0x0828, 0x610b10 },896 { 0x082c, 0x610a68 },897 { 0x0830, 0x610a60 },898 { 0x0834, 0x000000 },899 { 0x0838, 0x610a40 },900 { 0x0840, 0x610a24 },901 { 0x0844, 0x610a2c },902 { 0x0848, 0x610aa8 },903 { 0x084c, 0x610ab0 },904 { 0x0860, 0x610a84 },905 { 0x0864, 0x610a90 },906 { 0x0868, 0x610b18 },907 { 0x086c, 0x610b20 },908 { 0x0870, 0x610ac8 },909 { 0x0874, 0x610a38 },910 { 0x0880, 0x610a58 },911 { 0x0884, 0x610a9c },912 { 0x08a0, 0x610a70 },913 { 0x08a4, 0x610a50 },914 { 0x08a8, 0x610ae0 },915 { 0x08c0, 0x610b28 },916 { 0x08c4, 0x610b30 },917 { 0x08c8, 0x610b40 },918 { 0x08d4, 0x610b38 },919 { 0x08d8, 0x610b48 },920 { 0x08dc, 0x610b50 },921 { 0x0900, 0x610a18 },922 { 0x0904, 0x610ab8 },923 {}924 }925};926 927static const struct nvkm_disp_chan_mthd928nv50_disp_core_mthd = {929 .name = "Core",930 .addr = 0x000000,931 .prev = 0x000004,932 .data = {933 { "Global", 1, &nv50_disp_core_mthd_base },934 { "DAC", 3, &nv50_disp_core_mthd_dac },935 { "SOR", 2, &nv50_disp_core_mthd_sor },936 { "PIOR", 3, &nv50_disp_core_mthd_pior },937 { "HEAD", 2, &nv50_disp_core_mthd_head },938 {}939 }940};941 942static void943nv50_disp_core_fini(struct nvkm_disp_chan *chan)944{945 struct nvkm_subdev *subdev = &chan->disp->engine.subdev;946 struct nvkm_device *device = subdev->device;947 948 /* deactivate channel */949 nvkm_mask(device, 0x610200, 0x00000010, 0x00000000);950 nvkm_mask(device, 0x610200, 0x00000003, 0x00000000);951 if (nvkm_msec(device, 2000,952 if (!(nvkm_rd32(device, 0x610200) & 0x001e0000))953 break;954 ) < 0) {955 nvkm_error(subdev, "core fini: %08x\n",956 nvkm_rd32(device, 0x610200));957 }958 959 chan->suspend_put = nvkm_rd32(device, 0x640000);960}961 962static int963nv50_disp_core_init(struct nvkm_disp_chan *chan)964{965 struct nvkm_subdev *subdev = &chan->disp->engine.subdev;966 struct nvkm_device *device = subdev->device;967 968 /* attempt to unstick channel from some unknown state */969 if ((nvkm_rd32(device, 0x610200) & 0x009f0000) == 0x00020000)970 nvkm_mask(device, 0x610200, 0x00800000, 0x00800000);971 if ((nvkm_rd32(device, 0x610200) & 0x003f0000) == 0x00030000)972 nvkm_mask(device, 0x610200, 0x00600000, 0x00600000);973 974 /* initialise channel for dma command submission */975 nvkm_wr32(device, 0x610204, chan->push);976 nvkm_wr32(device, 0x610208, 0x00010000);977 nvkm_wr32(device, 0x61020c, 0x00000000);978 nvkm_mask(device, 0x610200, 0x00000010, 0x00000010);979 nvkm_wr32(device, 0x640000, chan->suspend_put);980 nvkm_wr32(device, 0x610200, 0x01000013);981 982 /* wait for it to go inactive */983 if (nvkm_msec(device, 2000,984 if (!(nvkm_rd32(device, 0x610200) & 0x80000000))985 break;986 ) < 0) {987 nvkm_error(subdev, "core init: %08x\n",988 nvkm_rd32(device, 0x610200));989 return -EBUSY;990 }991 992 return 0;993}994 995const struct nvkm_disp_chan_func996nv50_disp_core_func = {997 .push = nv50_disp_dmac_push,998 .init = nv50_disp_core_init,999 .fini = nv50_disp_core_fini,1000 .intr = nv50_disp_chan_intr,1001 .user = nv50_disp_chan_user,1002 .bind = nv50_disp_dmac_bind,1003};1004 1005static const struct nvkm_disp_chan_user1006nv50_disp_core = {1007 .func = &nv50_disp_core_func,1008 .ctrl = 0,1009 .user = 0,1010 .mthd = &nv50_disp_core_mthd,1011};1012 1013static u321014nv50_disp_super_iedt(struct nvkm_head *head, struct nvkm_outp *outp,1015 u8 *ver, u8 *hdr, u8 *cnt, u8 *len,1016 struct nvbios_outp *iedt)1017{1018 struct nvkm_bios *bios = head->disp->engine.subdev.device->bios;1019 const u8 l = ffs(outp->info.link);1020 const u16 t = outp->info.hasht;1021 const u16 m = (0x0100 << head->id) | (l << 6) | outp->info.or;1022 u32 data = nvbios_outp_match(bios, t, m, ver, hdr, cnt, len, iedt);1023 if (!data)1024 OUTP_DBG(outp, "missing IEDT for %04x:%04x", t, m);1025 return data;1026}1027 1028static void1029nv50_disp_super_ied_on(struct nvkm_head *head,1030 struct nvkm_ior *ior, int id, u32 khz)1031{1032 struct nvkm_subdev *subdev = &head->disp->engine.subdev;1033 struct nvkm_bios *bios = subdev->device->bios;1034 struct nvkm_outp *outp = ior->asy.outp;1035 struct nvbios_ocfg iedtrs;1036 struct nvbios_outp iedt;1037 u8 ver, hdr, cnt, len, flags = 0x00;1038 u32 data;1039 1040 if (!outp) {1041 IOR_DBG(ior, "nothing to attach");1042 return;1043 }1044 1045 /* Lookup IED table for the device. */1046 data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt);1047 if (!data)1048 return;1049 1050 /* Lookup IEDT runtime settings for the current configuration. */1051 if (ior->type == SOR) {1052 if (ior->asy.proto == LVDS) {1053 if (head->asy.or.depth == 24)1054 flags |= 0x02;1055 }1056 if (ior->asy.link == 3)1057 flags |= 0x01;1058 }1059 1060 data = nvbios_ocfg_match(bios, data, ior->asy.proto_evo, flags,1061 &ver, &hdr, &cnt, &len, &iedtrs);1062 if (!data) {1063 OUTP_DBG(outp, "missing IEDT RS for %02x:%02x",1064 ior->asy.proto_evo, flags);1065 return;1066 }1067 1068 /* Execute the OnInt[23] script for the current frequency. */1069 data = nvbios_oclk_match(bios, iedtrs.clkcmp[id], khz);1070 if (!data) {1071 OUTP_DBG(outp, "missing IEDT RSS %d for %02x:%02x %d khz",1072 id, ior->asy.proto_evo, flags, khz);1073 return;1074 }1075 1076 nvbios_init(subdev, data,1077 init.outp = &outp->info;1078 init.or = ior->id;1079 init.link = ior->asy.link;1080 init.head = head->id;1081 );1082}1083 1084static void1085nv50_disp_super_ied_off(struct nvkm_head *head, struct nvkm_ior *ior, int id)1086{1087 struct nvkm_outp *outp = ior->arm.outp;1088 struct nvbios_outp iedt;1089 u8 ver, hdr, cnt, len;1090 u32 data;1091 1092 if (!outp) {1093 IOR_DBG(ior, "nothing attached");1094 return;1095 }1096 1097 data = nv50_disp_super_iedt(head, outp, &ver, &hdr, &cnt, &len, &iedt);1098 if (!data)1099 return;1100 1101 nvbios_init(&head->disp->engine.subdev, iedt.script[id],1102 init.outp = &outp->info;1103 init.or = ior->id;1104 init.link = ior->arm.link;1105 init.head = head->id;1106 );1107}1108 1109static struct nvkm_ior *1110nv50_disp_super_ior_asy(struct nvkm_head *head)1111{1112 struct nvkm_ior *ior;1113 list_for_each_entry(ior, &head->disp->iors, head) {1114 if (ior->asy.head & (1 << head->id)) {1115 HEAD_DBG(head, "to %s", ior->name);1116 return ior;1117 }1118 }1119 HEAD_DBG(head, "nothing to attach");1120 return NULL;1121}1122 1123static struct nvkm_ior *1124nv50_disp_super_ior_arm(struct nvkm_head *head)1125{1126 struct nvkm_ior *ior;1127 list_for_each_entry(ior, &head->disp->iors, head) {1128 if (ior->arm.head & (1 << head->id)) {1129 HEAD_DBG(head, "on %s", ior->name);1130 return ior;1131 }1132 }1133 HEAD_DBG(head, "nothing attached");1134 return NULL;1135}1136 1137void1138nv50_disp_super_3_0(struct nvkm_disp *disp, struct nvkm_head *head)1139{1140 struct nvkm_ior *ior;1141 1142 /* Determine which OR, if any, we're attaching to the head. */1143 HEAD_DBG(head, "supervisor 3.0");1144 ior = nv50_disp_super_ior_asy(head);1145 if (!ior)1146 return;1147 1148 /* Execute OnInt3 IED script. */1149 nv50_disp_super_ied_on(head, ior, 1, head->asy.hz / 1000);1150 1151 /* OR-specific handling. */1152 if (ior->func->war_3)1153 ior->func->war_3(ior);1154}1155 1156static void1157nv50_disp_super_2_2_dp(struct nvkm_head *head, struct nvkm_ior *ior)1158{1159 struct nvkm_subdev *subdev = &head->disp->engine.subdev;1160 const u32 khz = head->asy.hz / 1000;1161 const u32 linkKBps = ior->dp.bw * 27000;1162 const u32 symbol = 100000;1163 int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;1164 int TU, VTUi, VTUf, VTUa;1165 u64 link_data_rate, link_ratio, unk;1166 u32 best_diff = 64 * symbol;1167 u64 h, v;1168 1169 /* symbols/hblank - algorithm taken from comments in tegra driver */1170 h = head->asy.hblanke + head->asy.htotal - head->asy.hblanks - 7;1171 h = h * linkKBps;1172 do_div(h, khz);1173 h = h - (3 * ior->dp.ef) - (12 / ior->dp.nr);1174 1175 /* symbols/vblank - algorithm taken from comments in tegra driver */1176 v = head->asy.vblanks - head->asy.vblanke - 25;1177 v = v * linkKBps;1178 do_div(v, khz);1179 v = v - ((36 / ior->dp.nr) + 3) - 1;1180 1181 ior->func->dp->audio_sym(ior, head->id, h, v);1182 1183 /* watermark / activesym */1184 link_data_rate = (khz * head->asy.or.depth / 8) / ior->dp.nr;1185 1186 /* calculate ratio of packed data rate to link symbol rate */1187 link_ratio = link_data_rate * symbol;1188 do_div(link_ratio, linkKBps);1189 1190 for (TU = 64; ior->func->dp->activesym && TU >= 32; TU--) {1191 /* calculate average number of valid symbols in each TU */1192 u32 tu_valid = link_ratio * TU;1193 u32 calc, diff;1194 1195 /* find a hw representation for the fraction.. */1196 VTUi = tu_valid / symbol;1197 calc = VTUi * symbol;1198 diff = tu_valid - calc;1199 if (diff) {1200 if (diff >= (symbol / 2)) {1201 VTUf = symbol / (symbol - diff);1202 if (symbol - (VTUf * diff))1203 VTUf++;1204 1205 if (VTUf <= 15) {1206 VTUa = 1;1207 calc += symbol - (symbol / VTUf);1208 } else {1209 VTUa = 0;1210 VTUf = 1;1211 calc += symbol;1212 }1213 } else {1214 VTUa = 0;1215 VTUf = min((int)(symbol / diff), 15);1216 calc += symbol / VTUf;1217 }1218 1219 diff = calc - tu_valid;1220 } else {1221 /* no remainder, but the hw doesn't like the fractional1222 * part to be zero. decrement the integer part and1223 * have the fraction add a whole symbol back1224 */1225 VTUa = 0;1226 VTUf = 1;1227 VTUi--;1228 }1229 1230 if (diff < best_diff) {1231 best_diff = diff;1232 bestTU = TU;1233 bestVTUa = VTUa;1234 bestVTUf = VTUf;1235 bestVTUi = VTUi;1236 if (diff == 0)1237 break;1238 }1239 }1240 1241 if (ior->func->dp->activesym) {1242 if (!bestTU) {1243 nvkm_error(subdev, "unable to determine dp config\n");1244 return;1245 }1246 1247 ior->func->dp->activesym(ior, head->id, bestTU, bestVTUa, bestVTUf, bestVTUi);1248 } else {1249 bestTU = 64;1250 }1251 1252 /* XXX close to vbios numbers, but not right */1253 unk = (symbol - link_ratio) * bestTU;1254 unk *= link_ratio;1255 do_div(unk, symbol);1256 do_div(unk, symbol);1257 unk += 6;1258 1259 ior->func->dp->watermark(ior, head->id, unk);1260}1261 1262void1263nv50_disp_super_2_2(struct nvkm_disp *disp, struct nvkm_head *head)1264{1265 const u32 khz = head->asy.hz / 1000;1266 struct nvkm_outp *outp;1267 struct nvkm_ior *ior;1268 1269 /* Determine which OR, if any, we're attaching from the head. */1270 HEAD_DBG(head, "supervisor 2.2");1271 ior = nv50_disp_super_ior_asy(head);1272 if (!ior)1273 return;1274 1275 outp = ior->asy.outp;1276 1277 /* For some reason, NVIDIA decided not to:1278 *1279 * A) Give dual-link LVDS a separate EVO protocol, like for TMDS.1280 * and1281 * B) Use SetControlOutputResource.PixelDepth on LVDS.1282 *1283 * Override the values we usually read from HW with the same1284 * data we pass though an ioctl instead.1285 */1286 if (outp && ior->type == SOR && ior->asy.proto == LVDS) {1287 head->asy.or.depth = outp->lvds.bpc8 ? 24 : 18;1288 ior->asy.link = outp->lvds.dual ? 3 : 1;1289 }1290 1291 /* Execute OnInt2 IED script. */1292 nv50_disp_super_ied_on(head, ior, 0, khz);1293 1294 /* Program RG clock divider. */1295 head->func->rgclk(head, ior->asy.rgdiv);1296 1297 /* Mode-specific internal DP configuration. */1298 if (ior->type == SOR && ior->asy.proto == DP)1299 nv50_disp_super_2_2_dp(head, ior);1300 1301 /* OR-specific handling. */1302 ior->func->clock(ior);1303 if (ior->func->war_2)1304 ior->func->war_2(ior);1305}1306 1307void1308nv50_disp_super_2_1(struct nvkm_disp *disp, struct nvkm_head *head)1309{1310 struct nvkm_devinit *devinit = disp->engine.subdev.device->devinit;1311 const u32 khz = head->asy.hz / 1000;1312 HEAD_DBG(head, "supervisor 2.1 - %d khz", khz);1313 if (khz)1314 nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head->id, khz);1315}1316 1317void1318nv50_disp_super_2_0(struct nvkm_disp *disp, struct nvkm_head *head)1319{1320 struct nvkm_ior *ior;1321 1322 /* Determine which OR, if any, we're detaching from the head. */1323 HEAD_DBG(head, "supervisor 2.0");1324 ior = nv50_disp_super_ior_arm(head);1325 if (!ior)1326 return;1327 1328 /* Execute OffInt2 IED script. */1329 nv50_disp_super_ied_off(head, ior, 2);1330}1331 1332void1333nv50_disp_super_1_0(struct nvkm_disp *disp, struct nvkm_head *head)1334{1335 struct nvkm_ior *ior;1336 1337 /* Determine which OR, if any, we're detaching from the head. */1338 HEAD_DBG(head, "supervisor 1.0");1339 ior = nv50_disp_super_ior_arm(head);1340 if (!ior)1341 return;1342 1343 /* Execute OffInt1 IED script. */1344 nv50_disp_super_ied_off(head, ior, 1);1345}1346 1347void1348nv50_disp_super_1(struct nvkm_disp *disp)1349{1350 struct nvkm_head *head;1351 struct nvkm_ior *ior;1352 1353 list_for_each_entry(head, &disp->heads, head) {1354 head->func->state(head, &head->arm);1355 head->func->state(head, &head->asy);1356 }1357 1358 list_for_each_entry(ior, &disp->iors, head) {1359 ior->func->state(ior, &ior->arm);1360 ior->func->state(ior, &ior->asy);1361 }1362}1363 1364void1365nv50_disp_super(struct work_struct *work)1366{1367 struct nvkm_disp *disp = container_of(work, struct nvkm_disp, super.work);1368 struct nvkm_subdev *subdev = &disp->engine.subdev;1369 struct nvkm_device *device = subdev->device;1370 struct nvkm_head *head;1371 u32 super;1372 1373 mutex_lock(&disp->super.mutex);1374 super = nvkm_rd32(device, 0x610030);1375 1376 nvkm_debug(subdev, "supervisor %08x %08x\n", disp->super.pending, super);1377 1378 if (disp->super.pending & 0x00000010) {1379 nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG);1380 nv50_disp_super_1(disp);1381 list_for_each_entry(head, &disp->heads, head) {1382 if (!(super & (0x00000020 << head->id)))1383 continue;1384 if (!(super & (0x00000080 << head->id)))1385 continue;1386 nv50_disp_super_1_0(disp, head);1387 }1388 } else1389 if (disp->super.pending & 0x00000020) {1390 list_for_each_entry(head, &disp->heads, head) {1391 if (!(super & (0x00000080 << head->id)))1392 continue;1393 nv50_disp_super_2_0(disp, head);1394 }1395 list_for_each_entry(head, &disp->heads, head) {1396 if (!(super & (0x00000200 << head->id)))1397 continue;1398 nv50_disp_super_2_1(disp, head);1399 }1400 list_for_each_entry(head, &disp->heads, head) {1401 if (!(super & (0x00000080 << head->id)))1402 continue;1403 nv50_disp_super_2_2(disp, head);1404 }1405 } else1406 if (disp->super.pending & 0x00000040) {1407 list_for_each_entry(head, &disp->heads, head) {1408 if (!(super & (0x00000080 << head->id)))1409 continue;1410 nv50_disp_super_3_0(disp, head);1411 }1412 }1413 1414 nvkm_wr32(device, 0x610030, 0x80000000);1415 mutex_unlock(&disp->super.mutex);1416}1417 1418const struct nvkm_enum1419nv50_disp_intr_error_type[] = {1420 { 0, "NONE" },1421 { 1, "PUSHBUFFER_ERR" },1422 { 2, "TRAP" },1423 { 3, "RESERVED_METHOD" },1424 { 4, "INVALID_ARG" },1425 { 5, "INVALID_STATE" },1426 { 7, "UNRESOLVABLE_HANDLE" },1427 {}1428};1429 1430static const struct nvkm_enum1431nv50_disp_intr_error_code[] = {1432 { 0x00, "" },1433 {}1434};1435 1436static void1437nv50_disp_intr_error(struct nvkm_disp *disp, int chid)1438{1439 struct nvkm_subdev *subdev = &disp->engine.subdev;1440 struct nvkm_device *device = subdev->device;1441 u32 data = nvkm_rd32(device, 0x610084 + (chid * 0x08));1442 u32 addr = nvkm_rd32(device, 0x610080 + (chid * 0x08));1443 u32 code = (addr & 0x00ff0000) >> 16;1444 u32 type = (addr & 0x00007000) >> 12;1445 u32 mthd = (addr & 0x00000ffc);1446 const struct nvkm_enum *ec, *et;1447 1448 et = nvkm_enum_find(nv50_disp_intr_error_type, type);1449 ec = nvkm_enum_find(nv50_disp_intr_error_code, code);1450 1451 nvkm_error(subdev,1452 "ERROR %d [%s] %02x [%s] chid %d mthd %04x data %08x\n",1453 type, et ? et->name : "", code, ec ? ec->name : "",1454 chid, mthd, data);1455 1456 if (chid < ARRAY_SIZE(disp->chan)) {1457 switch (mthd) {1458 case 0x0080:1459 nv50_disp_chan_mthd(disp->chan[chid], NV_DBG_ERROR);1460 break;1461 default:1462 break;1463 }1464 }1465 1466 nvkm_wr32(device, 0x610020, 0x00010000 << chid);1467 nvkm_wr32(device, 0x610080 + (chid * 0x08), 0x90000000);1468}1469 1470void1471nv50_disp_intr(struct nvkm_disp *disp)1472{1473 struct nvkm_device *device = disp->engine.subdev.device;1474 u32 intr0 = nvkm_rd32(device, 0x610020);1475 u32 intr1 = nvkm_rd32(device, 0x610024);1476 1477 while (intr0 & 0x001f0000) {1478 u32 chid = __ffs(intr0 & 0x001f0000) - 16;1479 nv50_disp_intr_error(disp, chid);1480 intr0 &= ~(0x00010000 << chid);1481 }1482 1483 while (intr0 & 0x0000001f) {1484 u32 chid = __ffs(intr0 & 0x0000001f);1485 nv50_disp_chan_uevent_send(disp, chid);1486 intr0 &= ~(0x00000001 << chid);1487 }1488 1489 if (intr1 & 0x00000004) {1490 nvkm_disp_vblank(disp, 0);1491 nvkm_wr32(device, 0x610024, 0x00000004);1492 }1493 1494 if (intr1 & 0x00000008) {1495 nvkm_disp_vblank(disp, 1);1496 nvkm_wr32(device, 0x610024, 0x00000008);1497 }1498 1499 if (intr1 & 0x00000070) {1500 disp->super.pending = (intr1 & 0x00000070);1501 queue_work(disp->super.wq, &disp->super.work);1502 nvkm_wr32(device, 0x610024, disp->super.pending);1503 }1504}1505 1506void1507nv50_disp_fini(struct nvkm_disp *disp, bool suspend)1508{1509 struct nvkm_device *device = disp->engine.subdev.device;1510 /* disable all interrupts */1511 nvkm_wr32(device, 0x610024, 0x00000000);1512 nvkm_wr32(device, 0x610020, 0x00000000);1513}1514 1515int1516nv50_disp_init(struct nvkm_disp *disp)1517{1518 struct nvkm_device *device = disp->engine.subdev.device;1519 struct nvkm_head *head;1520 u32 tmp;1521 int i;1522 1523 /* The below segments of code copying values from one register to1524 * another appear to inform EVO of the display capabilities or1525 * something similar. NFI what the 0x614004 caps are for..1526 */1527 tmp = nvkm_rd32(device, 0x614004);1528 nvkm_wr32(device, 0x610184, tmp);1529 1530 /* ... CRTC caps */1531 list_for_each_entry(head, &disp->heads, head) {1532 tmp = nvkm_rd32(device, 0x616100 + (head->id * 0x800));1533 nvkm_wr32(device, 0x610190 + (head->id * 0x10), tmp);1534 tmp = nvkm_rd32(device, 0x616104 + (head->id * 0x800));1535 nvkm_wr32(device, 0x610194 + (head->id * 0x10), tmp);1536 tmp = nvkm_rd32(device, 0x616108 + (head->id * 0x800));1537 nvkm_wr32(device, 0x610198 + (head->id * 0x10), tmp);1538 tmp = nvkm_rd32(device, 0x61610c + (head->id * 0x800));1539 nvkm_wr32(device, 0x61019c + (head->id * 0x10), tmp);1540 }1541 1542 /* ... DAC caps */1543 for (i = 0; i < disp->dac.nr; i++) {1544 tmp = nvkm_rd32(device, 0x61a000 + (i * 0x800));1545 nvkm_wr32(device, 0x6101d0 + (i * 0x04), tmp);1546 }1547 1548 /* ... SOR caps */1549 for (i = 0; i < disp->sor.nr; i++) {1550 tmp = nvkm_rd32(device, 0x61c000 + (i * 0x800));1551 nvkm_wr32(device, 0x6101e0 + (i * 0x04), tmp);1552 }1553 1554 /* ... PIOR caps */1555 for (i = 0; i < disp->pior.nr; i++) {1556 tmp = nvkm_rd32(device, 0x61e000 + (i * 0x800));1557 nvkm_wr32(device, 0x6101f0 + (i * 0x04), tmp);1558 }1559 1560 /* steal display away from vbios, or something like that */1561 if (nvkm_rd32(device, 0x610024) & 0x00000100) {1562 nvkm_wr32(device, 0x610024, 0x00000100);1563 nvkm_mask(device, 0x6194e8, 0x00000001, 0x00000000);1564 if (nvkm_msec(device, 2000,1565 if (!(nvkm_rd32(device, 0x6194e8) & 0x00000002))1566 break;1567 ) < 0)1568 return -EBUSY;1569 }1570 1571 /* point at display engine memory area (hash table, objects) */1572 nvkm_wr32(device, 0x610010, (disp->inst->addr >> 8) | 9);1573 1574 /* enable supervisor interrupts, disable everything else */1575 nvkm_wr32(device, 0x61002c, 0x00000370);1576 nvkm_wr32(device, 0x610028, 0x00000000);1577 return 0;1578}1579 1580int1581nv50_disp_oneinit(struct nvkm_disp *disp)1582{1583 const struct nvkm_disp_func *func = disp->func;1584 struct nvkm_subdev *subdev = &disp->engine.subdev;1585 struct nvkm_device *device = subdev->device;1586 struct nvkm_bios *bios = device->bios;1587 struct nvkm_outp *outp, *outt, *pair;1588 struct nvkm_conn *conn;1589 struct nvkm_ior *ior;1590 int ret, i;1591 u8 ver, hdr;1592 u32 data;1593 struct dcb_output dcbE;1594 struct nvbios_connE connE;1595 1596 if (func->wndw.cnt) {1597 disp->wndw.nr = func->wndw.cnt(disp, &disp->wndw.mask);1598 nvkm_debug(subdev, "Window(s): %d (%08lx)\n", disp->wndw.nr, disp->wndw.mask);1599 }1600 1601 disp->head.nr = func->head.cnt(disp, &disp->head.mask);1602 nvkm_debug(subdev, " Head(s): %d (%02lx)\n", disp->head.nr, disp->head.mask);1603 for_each_set_bit(i, &disp->head.mask, disp->head.nr) {1604 ret = func->head.new(disp, i);1605 if (ret)1606 return ret;1607 }1608 1609 if (func->dac.cnt) {1610 disp->dac.nr = func->dac.cnt(disp, &disp->dac.mask);1611 nvkm_debug(subdev, " DAC(s): %d (%02lx)\n", disp->dac.nr, disp->dac.mask);1612 for_each_set_bit(i, &disp->dac.mask, disp->dac.nr) {1613 ret = func->dac.new(disp, i);1614 if (ret)1615 return ret;1616 }1617 }1618 1619 if (func->pior.cnt) {1620 disp->pior.nr = func->pior.cnt(disp, &disp->pior.mask);1621 nvkm_debug(subdev, " PIOR(s): %d (%02lx)\n", disp->pior.nr, disp->pior.mask);1622 for_each_set_bit(i, &disp->pior.mask, disp->pior.nr) {1623 ret = func->pior.new(disp, i);1624 if (ret)1625 return ret;1626 }1627 }1628 1629 disp->sor.nr = func->sor.cnt(disp, &disp->sor.mask);1630 nvkm_debug(subdev, " SOR(s): %d (%02lx)\n", disp->sor.nr, disp->sor.mask);1631 for_each_set_bit(i, &disp->sor.mask, disp->sor.nr) {1632 ret = func->sor.new(disp, i);1633 if (ret)1634 return ret;1635 }1636 1637 ret = nvkm_gpuobj_new(device, 0x10000, 0x10000, false, NULL, &disp->inst);1638 if (ret)1639 return ret;1640 1641 ret = nvkm_ramht_new(device, func->ramht_size ? func->ramht_size : 0x1000, 0, disp->inst,1642 &disp->ramht);1643 if (ret)1644 return ret;1645 1646 /* Create output path objects for each VBIOS display path. */1647 i = -1;1648 while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) {1649 if (WARN_ON((ver & 0xf0) != 0x40))1650 return -EINVAL;1651 if (dcbE.type == DCB_OUTPUT_UNUSED)1652 continue;1653 if (dcbE.type == DCB_OUTPUT_EOL)1654 break;1655 outp = NULL;1656 1657 switch (dcbE.type) {1658 case DCB_OUTPUT_ANALOG:1659 case DCB_OUTPUT_TMDS:1660 case DCB_OUTPUT_LVDS:1661 ret = nvkm_outp_new(disp, i, &dcbE, &outp);1662 break;1663 case DCB_OUTPUT_DP:1664 ret = nvkm_dp_new(disp, i, &dcbE, &outp);1665 break;1666 case DCB_OUTPUT_TV:1667 case DCB_OUTPUT_WFD:1668 /* No support for WFD yet. */1669 ret = -ENODEV;1670 continue;1671 default:1672 nvkm_warn(subdev, "dcb %d type %d unknown\n",1673 i, dcbE.type);1674 continue;1675 }1676 1677 if (ret) {1678 if (outp) {1679 if (ret != -ENODEV)1680 OUTP_ERR(outp, "ctor failed: %d", ret);1681 else1682 OUTP_DBG(outp, "not supported");1683 nvkm_outp_del(&outp);1684 continue;1685 }1686 nvkm_error(subdev, "failed to create outp %d\n", i);1687 continue;1688 }1689 1690 list_add_tail(&outp->head, &disp->outps);1691 }1692 1693 /* Create connector objects based on available output paths. */1694 list_for_each_entry_safe(outp, outt, &disp->outps, head) {1695 /* VBIOS data *should* give us the most useful information. */1696 data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr,1697 &connE);1698 1699 /* No bios connector data... */1700 if (!data) {1701 /* Heuristic: anything with the same ccb index is1702 * considered to be on the same connector, any1703 * output path without an associated ccb entry will1704 * be put on its own connector.1705 */1706 int ccb_index = outp->info.i2c_index;1707 if (ccb_index != 0xf) {1708 list_for_each_entry(pair, &disp->outps, head) {1709 if (pair->info.i2c_index == ccb_index) {1710 outp->conn = pair->conn;1711 break;1712 }1713 }1714 }1715 1716 /* Connector shared with another output path. */1717 if (outp->conn)1718 continue;1719 1720 memset(&connE, 0x00, sizeof(connE));1721 connE.type = DCB_CONNECTOR_NONE;1722 i = -1;1723 } else {1724 i = outp->info.connector;1725 }1726 1727 /* Check that we haven't already created this connector. */1728 list_for_each_entry(conn, &disp->conns, head) {1729 if (conn->index == outp->info.connector) {1730 outp->conn = conn;1731 break;1732 }1733 }1734 1735 if (outp->conn)1736 continue;1737 1738 /* Apparently we need to create a new one! */1739 ret = nvkm_conn_new(disp, i, &connE, &outp->conn);1740 if (ret) {1741 nvkm_error(subdev, "failed to create outp %d conn: %d\n", outp->index, ret);1742 nvkm_conn_del(&outp->conn);1743 list_del(&outp->head);1744 nvkm_outp_del(&outp);1745 continue;1746 }1747 1748 list_add_tail(&outp->conn->head, &disp->conns);1749 }1750 1751 /* Enforce identity-mapped SOR assignment for panels, which have1752 * certain bits (ie. backlight controls) wired to a specific SOR.1753 */1754 list_for_each_entry(outp, &disp->outps, head) {1755 if (outp->conn->info.type == DCB_CONNECTOR_LVDS ||1756 outp->conn->info.type == DCB_CONNECTOR_eDP) {1757 ior = nvkm_ior_find(disp, SOR, ffs(outp->info.or) - 1);1758 if (!WARN_ON(!ior))1759 ior->identity = true;1760 outp->identity = true;1761 }1762 }1763 1764 return 0;1765}1766 1767static const struct nvkm_disp_func1768nv50_disp = {1769 .oneinit = nv50_disp_oneinit,1770 .init = nv50_disp_init,1771 .fini = nv50_disp_fini,1772 .intr = nv50_disp_intr,1773 .super = nv50_disp_super,1774 .uevent = &nv50_disp_chan_uevent,1775 .head = { .cnt = nv50_head_cnt, .new = nv50_head_new },1776 .dac = { .cnt = nv50_dac_cnt, .new = nv50_dac_new },1777 .sor = { .cnt = nv50_sor_cnt, .new = nv50_sor_new },1778 .pior = { .cnt = nv50_pior_cnt, .new = nv50_pior_new },1779 .root = { 0, 0, NV50_DISP },1780 .user = {1781 {{0,0,NV50_DISP_CURSOR }, nvkm_disp_chan_new, &nv50_disp_curs },1782 {{0,0,NV50_DISP_OVERLAY }, nvkm_disp_chan_new, &nv50_disp_oimm },1783 {{0,0,NV50_DISP_BASE_CHANNEL_DMA }, nvkm_disp_chan_new, &nv50_disp_base },1784 {{0,0,NV50_DISP_CORE_CHANNEL_DMA }, nvkm_disp_core_new, &nv50_disp_core },1785 {{0,0,NV50_DISP_OVERLAY_CHANNEL_DMA}, nvkm_disp_chan_new, &nv50_disp_ovly },1786 {}1787 }1788};1789 1790int1791nv50_disp_new(struct nvkm_device *device, enum nvkm_subdev_type type, int inst,1792 struct nvkm_disp **pdisp)1793{1794 return nvkm_disp_new_(&nv50_disp, device, type, inst, pdisp);1795}1796