1 /* 2 * Copyright 2012 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/client.h> 27 28 #include <nvif/class.h> 29 #include <nvif/unpack.h> 30 31 static int 32 nv04_disp_scanoutpos(struct nvkm_object *object, struct nvkm_disp *disp, 33 void *data, u32 size, int head) 34 { 35 const u32 hoff = head * 0x2000; 36 union { 37 struct nv04_disp_scanoutpos_v0 v0; 38 } *args = data; 39 u32 line; 40 int ret; 41 42 nv_ioctl(object, "disp scanoutpos size %d\n", size); 43 if (nvif_unpack(args->v0, 0, 0, false)) { 44 nv_ioctl(object, "disp scanoutpos vers %d\n", args->v0.version); 45 args->v0.vblanks = nv_rd32(disp, 0x680800 + hoff) & 0xffff; 46 args->v0.vtotal = nv_rd32(disp, 0x680804 + hoff) & 0xffff; 47 args->v0.vblanke = args->v0.vtotal - 1; 48 49 args->v0.hblanks = nv_rd32(disp, 0x680820 + hoff) & 0xffff; 50 args->v0.htotal = nv_rd32(disp, 0x680824 + hoff) & 0xffff; 51 args->v0.hblanke = args->v0.htotal - 1; 52 53 /* 54 * If output is vga instead of digital then vtotal/htotal is 55 * invalid so we have to give up and trigger the timestamping 56 * fallback in the drm core. 57 */ 58 if (!args->v0.vtotal || !args->v0.htotal) 59 return -ENOTSUPP; 60 61 args->v0.time[0] = ktime_to_ns(ktime_get()); 62 line = nv_rd32(disp, 0x600868 + hoff); 63 args->v0.time[1] = ktime_to_ns(ktime_get()); 64 args->v0.hline = (line & 0xffff0000) >> 16; 65 args->v0.vline = (line & 0x0000ffff); 66 } else 67 return ret; 68 69 return 0; 70 } 71 72 static int 73 nv04_disp_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size) 74 { 75 union { 76 struct nv04_disp_mthd_v0 v0; 77 } *args = data; 78 struct nvkm_disp *disp = (void *)object->engine; 79 int head, ret; 80 81 nv_ioctl(object, "disp mthd size %d\n", size); 82 if (nvif_unpack(args->v0, 0, 0, true)) { 83 nv_ioctl(object, "disp mthd vers %d mthd %02x head %d\n", 84 args->v0.version, args->v0.method, args->v0.head); 85 mthd = args->v0.method; 86 head = args->v0.head; 87 } else 88 return ret; 89 90 if (head < 0 || head >= 2) 91 return -ENXIO; 92 93 switch (mthd) { 94 case NV04_DISP_SCANOUTPOS: 95 return nv04_disp_scanoutpos(object, disp, data, size, head); 96 default: 97 break; 98 } 99 100 return -EINVAL; 101 } 102 103 static struct nvkm_ofuncs 104 nv04_disp_ofuncs = { 105 .ctor = _nvkm_object_ctor, 106 .dtor = nvkm_object_destroy, 107 .init = nvkm_object_init, 108 .fini = nvkm_object_fini, 109 .mthd = nv04_disp_mthd, 110 .ntfy = nvkm_disp_ntfy, 111 }; 112 113 static struct nvkm_oclass 114 nv04_disp_sclass[] = { 115 { NV04_DISP, &nv04_disp_ofuncs }, 116 {}, 117 }; 118 119 /******************************************************************************* 120 * Display engine implementation 121 ******************************************************************************/ 122 123 static void 124 nv04_disp_vblank_init(struct nvkm_event *event, int type, int head) 125 { 126 struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); 127 nv_wr32(disp, 0x600140 + (head * 0x2000) , 0x00000001); 128 } 129 130 static void 131 nv04_disp_vblank_fini(struct nvkm_event *event, int type, int head) 132 { 133 struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank); 134 nv_wr32(disp, 0x600140 + (head * 0x2000) , 0x00000000); 135 } 136 137 static const struct nvkm_event_func 138 nv04_disp_vblank_func = { 139 .ctor = nvkm_disp_vblank_ctor, 140 .init = nv04_disp_vblank_init, 141 .fini = nv04_disp_vblank_fini, 142 }; 143 144 static void 145 nv04_disp_intr(struct nvkm_subdev *subdev) 146 { 147 struct nvkm_disp *disp = (void *)subdev; 148 u32 crtc0 = nv_rd32(disp, 0x600100); 149 u32 crtc1 = nv_rd32(disp, 0x602100); 150 u32 pvideo; 151 152 if (crtc0 & 0x00000001) { 153 nvkm_disp_vblank(disp, 0); 154 nv_wr32(disp, 0x600100, 0x00000001); 155 } 156 157 if (crtc1 & 0x00000001) { 158 nvkm_disp_vblank(disp, 1); 159 nv_wr32(disp, 0x602100, 0x00000001); 160 } 161 162 if (nv_device(disp)->chipset >= 0x10 && 163 nv_device(disp)->chipset <= 0x40) { 164 pvideo = nv_rd32(disp, 0x8100); 165 if (pvideo & ~0x11) 166 nv_info(disp, "PVIDEO intr: %08x\n", pvideo); 167 nv_wr32(disp, 0x8100, pvideo); 168 } 169 } 170 171 static int 172 nv04_disp_ctor(struct nvkm_object *parent, struct nvkm_object *engine, 173 struct nvkm_oclass *oclass, void *data, u32 size, 174 struct nvkm_object **pobject) 175 { 176 struct nvkm_disp *disp; 177 int ret; 178 179 ret = nvkm_disp_create(parent, engine, oclass, 2, "DISPLAY", 180 "display", &disp); 181 *pobject = nv_object(disp); 182 if (ret) 183 return ret; 184 185 nv_engine(disp)->sclass = nv04_disp_sclass; 186 nv_subdev(disp)->intr = nv04_disp_intr; 187 return 0; 188 } 189 190 struct nvkm_oclass * 191 nv04_disp_oclass = &(struct nvkm_disp_impl) { 192 .base.handle = NV_ENGINE(DISP, 0x04), 193 .base.ofuncs = &(struct nvkm_ofuncs) { 194 .ctor = nv04_disp_ctor, 195 .dtor = _nvkm_disp_dtor, 196 .init = _nvkm_disp_init, 197 .fini = _nvkm_disp_fini, 198 }, 199 .vblank = &nv04_disp_vblank_func, 200 }.base; 201