1/* 2 * Copyright 2013 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#include "conn.h" 26#include "outp.h" 27 28#include <core/notify.h> 29#include <subdev/bios.h> 30#include <subdev/bios/dcb.h> 31 32#include <nvif/class.h> 33#include <nvif/event.h> 34#include <nvif/unpack.h> 35 36int 37nvkm_disp_vblank_ctor(struct nvkm_object *object, void *data, u32 size, 38 struct nvkm_notify *notify) 39{ 40 struct nvkm_disp *disp = 41 container_of(notify->event, typeof(*disp), vblank); 42 union { 43 struct nvif_notify_head_req_v0 v0; 44 } *req = data; 45 int ret; 46 47 if (nvif_unpack(req->v0, 0, 0, false)) { 48 notify->size = sizeof(struct nvif_notify_head_rep_v0); 49 if (ret = -ENXIO, req->v0.head <= disp->vblank.index_nr) { 50 notify->types = 1; 51 notify->index = req->v0.head; 52 return 0; 53 } 54 } 55 56 return ret; 57} 58 59void 60nvkm_disp_vblank(struct nvkm_disp *disp, int head) 61{ 62 struct nvif_notify_head_rep_v0 rep = {}; 63 nvkm_event_send(&disp->vblank, 1, head, &rep, sizeof(rep)); 64} 65 66static int 67nvkm_disp_hpd_ctor(struct nvkm_object *object, void *data, u32 size, 68 struct nvkm_notify *notify) 69{ 70 struct nvkm_disp *disp = 71 container_of(notify->event, typeof(*disp), hpd); 72 union { 73 struct nvif_notify_conn_req_v0 v0; 74 } *req = data; 75 struct nvkm_output *outp; 76 int ret; 77 78 if (nvif_unpack(req->v0, 0, 0, false)) { 79 notify->size = sizeof(struct nvif_notify_conn_rep_v0); 80 list_for_each_entry(outp, &disp->outp, head) { 81 if (ret = -ENXIO, outp->conn->index == req->v0.conn) { 82 if (ret = -ENODEV, outp->conn->hpd.event) { 83 notify->types = req->v0.mask; 84 notify->index = req->v0.conn; 85 ret = 0; 86 } 87 break; 88 } 89 } 90 } 91 92 return ret; 93} 94 95static const struct nvkm_event_func 96nvkm_disp_hpd_func = { 97 .ctor = nvkm_disp_hpd_ctor 98}; 99 100int 101nvkm_disp_ntfy(struct nvkm_object *object, u32 type, struct nvkm_event **event) 102{ 103 struct nvkm_disp *disp = (void *)object->engine; 104 switch (type) { 105 case NV04_DISP_NTFY_VBLANK: 106 *event = &disp->vblank; 107 return 0; 108 case NV04_DISP_NTFY_CONN: 109 *event = &disp->hpd; 110 return 0; 111 default: 112 break; 113 } 114 return -EINVAL; 115} 116 117int 118_nvkm_disp_fini(struct nvkm_object *object, bool suspend) 119{ 120 struct nvkm_disp *disp = (void *)object; 121 struct nvkm_output *outp; 122 int ret; 123 124 list_for_each_entry(outp, &disp->outp, head) { 125 ret = nv_ofuncs(outp)->fini(nv_object(outp), suspend); 126 if (ret && suspend) 127 goto fail_outp; 128 } 129 130 return nvkm_engine_fini(&disp->base, suspend); 131 132fail_outp: 133 list_for_each_entry_continue_reverse(outp, &disp->outp, head) { 134 nv_ofuncs(outp)->init(nv_object(outp)); 135 } 136 137 return ret; 138} 139 140int 141_nvkm_disp_init(struct nvkm_object *object) 142{ 143 struct nvkm_disp *disp = (void *)object; 144 struct nvkm_output *outp; 145 int ret; 146 147 ret = nvkm_engine_init(&disp->base); 148 if (ret) 149 return ret; 150 151 list_for_each_entry(outp, &disp->outp, head) { 152 ret = nv_ofuncs(outp)->init(nv_object(outp)); 153 if (ret) 154 goto fail_outp; 155 } 156 157 return ret; 158 159fail_outp: 160 list_for_each_entry_continue_reverse(outp, &disp->outp, head) { 161 nv_ofuncs(outp)->fini(nv_object(outp), false); 162 } 163 164 return ret; 165} 166 167void 168_nvkm_disp_dtor(struct nvkm_object *object) 169{ 170 struct nvkm_disp *disp = (void *)object; 171 struct nvkm_output *outp, *outt; 172 173 nvkm_event_fini(&disp->vblank); 174 nvkm_event_fini(&disp->hpd); 175 176 if (disp->outp.next) { 177 list_for_each_entry_safe(outp, outt, &disp->outp, head) { 178 nvkm_object_ref(NULL, (struct nvkm_object **)&outp); 179 } 180 } 181 182 nvkm_engine_destroy(&disp->base); 183} 184 185int 186nvkm_disp_create_(struct nvkm_object *parent, struct nvkm_object *engine, 187 struct nvkm_oclass *oclass, int heads, const char *intname, 188 const char *extname, int length, void **pobject) 189{ 190 struct nvkm_disp_impl *impl = (void *)oclass; 191 struct nvkm_bios *bios = nvkm_bios(parent); 192 struct nvkm_disp *disp; 193 struct nvkm_oclass **sclass; 194 struct nvkm_object *object; 195 struct dcb_output dcbE; 196 u8 hpd = 0, ver, hdr; 197 u32 data; 198 int ret, i; 199 200 ret = nvkm_engine_create_(parent, engine, oclass, true, intname, 201 extname, length, pobject); 202 disp = *pobject; 203 if (ret) 204 return ret; 205 206 INIT_LIST_HEAD(&disp->outp); 207 208 /* create output objects for each display path in the vbios */ 209 i = -1; 210 while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) { 211 if (dcbE.type == DCB_OUTPUT_UNUSED) 212 continue; 213 if (dcbE.type == DCB_OUTPUT_EOL) 214 break; 215 data = dcbE.location << 4 | dcbE.type; 216 217 oclass = nvkm_output_oclass; 218 sclass = impl->outp; 219 while (sclass && sclass[0]) { 220 if (sclass[0]->handle == data) { 221 oclass = sclass[0]; 222 break; 223 } 224 sclass++; 225 } 226 227 nvkm_object_ctor(*pobject, NULL, oclass, &dcbE, i, &object); 228 hpd = max(hpd, (u8)(dcbE.connector + 1)); 229 } 230 231 ret = nvkm_event_init(&nvkm_disp_hpd_func, 3, hpd, &disp->hpd); 232 if (ret) 233 return ret; 234 235 ret = nvkm_event_init(impl->vblank, 1, heads, &disp->vblank); 236 if (ret) 237 return ret; 238 239 return 0; 240} 241