diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c')
| -rw-r--r-- | drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c | 146 |
1 files changed, 101 insertions, 45 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c index 723dcbde2ac2..57719f675eec 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/channv50.c | |||
| @@ -26,6 +26,7 @@ | |||
| 26 | 26 | ||
| 27 | #include <core/client.h> | 27 | #include <core/client.h> |
| 28 | #include <core/notify.h> | 28 | #include <core/notify.h> |
| 29 | #include <core/oproxy.h> | ||
| 29 | #include <core/ramht.h> | 30 | #include <core/ramht.h> |
| 30 | #include <engine/dma.h> | 31 | #include <engine/dma.h> |
| 31 | 32 | ||
| @@ -65,7 +66,7 @@ nv50_disp_mthd_list(struct nv50_disp *disp, int debug, u32 base, int c, | |||
| 65 | void | 66 | void |
| 66 | nv50_disp_chan_mthd(struct nv50_disp_chan *chan, int debug) | 67 | nv50_disp_chan_mthd(struct nv50_disp_chan *chan, int debug) |
| 67 | { | 68 | { |
| 68 | struct nv50_disp *disp = chan->root->disp; | 69 | struct nv50_disp *disp = chan->disp; |
| 69 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; | 70 | struct nvkm_subdev *subdev = &disp->base.engine.subdev; |
| 70 | const struct nv50_disp_chan_mthd *mthd = chan->mthd; | 71 | const struct nv50_disp_chan_mthd *mthd = chan->mthd; |
| 71 | const struct nv50_disp_mthd_list *list; | 72 | const struct nv50_disp_mthd_list *list; |
| @@ -154,13 +155,29 @@ nv50_disp_chan_uevent = { | |||
| 154 | .fini = nv50_disp_chan_uevent_fini, | 155 | .fini = nv50_disp_chan_uevent_fini, |
| 155 | }; | 156 | }; |
| 156 | 157 | ||
| 158 | u64 | ||
| 159 | nv50_disp_chan_user(struct nv50_disp_chan *chan, u64 *psize) | ||
| 160 | { | ||
| 161 | *psize = 0x1000; | ||
| 162 | return 0x640000 + (chan->chid.user * 0x1000); | ||
| 163 | } | ||
| 164 | |||
| 165 | void | ||
| 166 | nv50_disp_chan_intr(struct nv50_disp_chan *chan, bool en) | ||
| 167 | { | ||
| 168 | struct nvkm_device *device = chan->disp->base.engine.subdev.device; | ||
| 169 | const u64 mask = 0x00010001 << chan->chid.user; | ||
| 170 | const u64 data = en ? 0x00010000 : 0x00000000; | ||
| 171 | nvkm_mask(device, 0x610028, mask, data); | ||
| 172 | } | ||
| 173 | |||
| 157 | static int | 174 | static int |
| 158 | nv50_disp_chan_rd32(struct nvkm_object *object, u64 addr, u32 *data) | 175 | nv50_disp_chan_rd32(struct nvkm_object *object, u64 addr, u32 *data) |
| 159 | { | 176 | { |
| 160 | struct nv50_disp_chan *chan = nv50_disp_chan(object); | 177 | struct nv50_disp_chan *chan = nv50_disp_chan(object); |
| 161 | struct nv50_disp *disp = chan->root->disp; | 178 | struct nvkm_device *device = chan->disp->base.engine.subdev.device; |
| 162 | struct nvkm_device *device = disp->base.engine.subdev.device; | 179 | u64 size, base = chan->func->user(chan, &size); |
| 163 | *data = nvkm_rd32(device, 0x640000 + (chan->chid.user * 0x1000) + addr); | 180 | *data = nvkm_rd32(device, base + addr); |
| 164 | return 0; | 181 | return 0; |
| 165 | } | 182 | } |
| 166 | 183 | ||
| @@ -168,9 +185,9 @@ static int | |||
| 168 | nv50_disp_chan_wr32(struct nvkm_object *object, u64 addr, u32 data) | 185 | nv50_disp_chan_wr32(struct nvkm_object *object, u64 addr, u32 data) |
| 169 | { | 186 | { |
| 170 | struct nv50_disp_chan *chan = nv50_disp_chan(object); | 187 | struct nv50_disp_chan *chan = nv50_disp_chan(object); |
| 171 | struct nv50_disp *disp = chan->root->disp; | 188 | struct nvkm_device *device = chan->disp->base.engine.subdev.device; |
| 172 | struct nvkm_device *device = disp->base.engine.subdev.device; | 189 | u64 size, base = chan->func->user(chan, &size); |
| 173 | nvkm_wr32(device, 0x640000 + (chan->chid.user * 0x1000) + addr, data); | 190 | nvkm_wr32(device, base + addr, data); |
| 174 | return 0; | 191 | return 0; |
| 175 | } | 192 | } |
| 176 | 193 | ||
| @@ -179,7 +196,7 @@ nv50_disp_chan_ntfy(struct nvkm_object *object, u32 type, | |||
| 179 | struct nvkm_event **pevent) | 196 | struct nvkm_event **pevent) |
| 180 | { | 197 | { |
| 181 | struct nv50_disp_chan *chan = nv50_disp_chan(object); | 198 | struct nv50_disp_chan *chan = nv50_disp_chan(object); |
| 182 | struct nv50_disp *disp = chan->root->disp; | 199 | struct nv50_disp *disp = chan->disp; |
| 183 | switch (type) { | 200 | switch (type) { |
| 184 | case NV50_DISP_CORE_CHANNEL_DMA_V0_NTFY_UEVENT: | 201 | case NV50_DISP_CORE_CHANNEL_DMA_V0_NTFY_UEVENT: |
| 185 | *pevent = &disp->uevent; | 202 | *pevent = &disp->uevent; |
| @@ -195,34 +212,83 @@ nv50_disp_chan_map(struct nvkm_object *object, void *argv, u32 argc, | |||
| 195 | enum nvkm_object_map *type, u64 *addr, u64 *size) | 212 | enum nvkm_object_map *type, u64 *addr, u64 *size) |
| 196 | { | 213 | { |
| 197 | struct nv50_disp_chan *chan = nv50_disp_chan(object); | 214 | struct nv50_disp_chan *chan = nv50_disp_chan(object); |
| 198 | struct nv50_disp *disp = chan->root->disp; | 215 | struct nvkm_device *device = chan->disp->base.engine.subdev.device; |
| 199 | struct nvkm_device *device = disp->base.engine.subdev.device; | 216 | const u64 base = device->func->resource_addr(device, 0); |
| 200 | *type = NVKM_OBJECT_MAP_IO; | 217 | *type = NVKM_OBJECT_MAP_IO; |
| 201 | *addr = device->func->resource_addr(device, 0) + | 218 | *addr = base + chan->func->user(chan, size); |
| 202 | 0x640000 + (chan->chid.user * 0x1000); | ||
| 203 | *size = 0x001000; | ||
| 204 | return 0; | 219 | return 0; |
| 205 | } | 220 | } |
| 206 | 221 | ||
| 222 | struct nv50_disp_chan_object { | ||
| 223 | struct nvkm_oproxy oproxy; | ||
| 224 | struct nv50_disp *disp; | ||
| 225 | int hash; | ||
| 226 | }; | ||
| 227 | |||
| 228 | static void | ||
| 229 | nv50_disp_chan_child_del_(struct nvkm_oproxy *base) | ||
| 230 | { | ||
| 231 | struct nv50_disp_chan_object *object = | ||
| 232 | container_of(base, typeof(*object), oproxy); | ||
| 233 | nvkm_ramht_remove(object->disp->ramht, object->hash); | ||
| 234 | } | ||
| 235 | |||
| 236 | static const struct nvkm_oproxy_func | ||
| 237 | nv50_disp_chan_child_func_ = { | ||
| 238 | .dtor[0] = nv50_disp_chan_child_del_, | ||
| 239 | }; | ||
| 240 | |||
| 207 | static int | 241 | static int |
| 208 | nv50_disp_chan_child_new(const struct nvkm_oclass *oclass, | 242 | nv50_disp_chan_child_new(const struct nvkm_oclass *oclass, |
| 209 | void *data, u32 size, struct nvkm_object **pobject) | 243 | void *argv, u32 argc, struct nvkm_object **pobject) |
| 210 | { | 244 | { |
| 211 | struct nv50_disp_chan *chan = nv50_disp_chan(oclass->parent); | 245 | struct nv50_disp_chan *chan = nv50_disp_chan(oclass->parent); |
| 212 | return chan->func->child_new(chan, oclass, data, size, pobject); | 246 | struct nv50_disp *disp = chan->disp; |
| 247 | struct nvkm_device *device = disp->base.engine.subdev.device; | ||
| 248 | const struct nvkm_device_oclass *sclass = oclass->priv; | ||
| 249 | struct nv50_disp_chan_object *object; | ||
| 250 | int ret; | ||
| 251 | |||
| 252 | if (!(object = kzalloc(sizeof(*object), GFP_KERNEL))) | ||
| 253 | return -ENOMEM; | ||
| 254 | nvkm_oproxy_ctor(&nv50_disp_chan_child_func_, oclass, &object->oproxy); | ||
| 255 | object->disp = disp; | ||
| 256 | *pobject = &object->oproxy.base; | ||
| 257 | |||
| 258 | ret = sclass->ctor(device, oclass, argv, argc, &object->oproxy.object); | ||
| 259 | if (ret) | ||
| 260 | return ret; | ||
| 261 | |||
| 262 | object->hash = chan->func->bind(chan, object->oproxy.object, | ||
| 263 | oclass->handle); | ||
| 264 | if (object->hash < 0) | ||
| 265 | return object->hash; | ||
| 266 | |||
| 267 | return 0; | ||
| 213 | } | 268 | } |
| 214 | 269 | ||
| 215 | static int | 270 | static int |
| 216 | nv50_disp_chan_child_get(struct nvkm_object *object, int index, | 271 | nv50_disp_chan_child_get(struct nvkm_object *object, int index, |
| 217 | struct nvkm_oclass *oclass) | 272 | struct nvkm_oclass *sclass) |
| 218 | { | 273 | { |
| 219 | struct nv50_disp_chan *chan = nv50_disp_chan(object); | 274 | struct nv50_disp_chan *chan = nv50_disp_chan(object); |
| 220 | if (chan->func->child_get) { | 275 | struct nvkm_device *device = chan->disp->base.engine.subdev.device; |
| 221 | int ret = chan->func->child_get(chan, index, oclass); | 276 | const struct nvkm_device_oclass *oclass = NULL; |
| 222 | if (ret == 0) | 277 | |
| 223 | oclass->ctor = nv50_disp_chan_child_new; | 278 | if (chan->func->bind) |
| 224 | return ret; | 279 | sclass->engine = nvkm_device_engine(device, NVKM_ENGINE_DMAOBJ); |
| 280 | else | ||
| 281 | sclass->engine = NULL; | ||
| 282 | |||
| 283 | if (sclass->engine && sclass->engine->func->base.sclass) { | ||
| 284 | sclass->engine->func->base.sclass(sclass, index, &oclass); | ||
| 285 | if (oclass) { | ||
| 286 | sclass->ctor = nv50_disp_chan_child_new, | ||
| 287 | sclass->priv = oclass; | ||
| 288 | return 0; | ||
| 289 | } | ||
| 225 | } | 290 | } |
| 291 | |||
| 226 | return -EINVAL; | 292 | return -EINVAL; |
| 227 | } | 293 | } |
| 228 | 294 | ||
| @@ -231,6 +297,7 @@ nv50_disp_chan_fini(struct nvkm_object *object, bool suspend) | |||
| 231 | { | 297 | { |
| 232 | struct nv50_disp_chan *chan = nv50_disp_chan(object); | 298 | struct nv50_disp_chan *chan = nv50_disp_chan(object); |
| 233 | chan->func->fini(chan); | 299 | chan->func->fini(chan); |
| 300 | chan->func->intr(chan, false); | ||
| 234 | return 0; | 301 | return 0; |
| 235 | } | 302 | } |
| 236 | 303 | ||
| @@ -238,6 +305,7 @@ static int | |||
| 238 | nv50_disp_chan_init(struct nvkm_object *object) | 305 | nv50_disp_chan_init(struct nvkm_object *object) |
| 239 | { | 306 | { |
| 240 | struct nv50_disp_chan *chan = nv50_disp_chan(object); | 307 | struct nv50_disp_chan *chan = nv50_disp_chan(object); |
| 308 | chan->func->intr(chan, true); | ||
| 241 | return chan->func->init(chan); | 309 | return chan->func->init(chan); |
| 242 | } | 310 | } |
| 243 | 311 | ||
| @@ -245,10 +313,11 @@ static void * | |||
| 245 | nv50_disp_chan_dtor(struct nvkm_object *object) | 313 | nv50_disp_chan_dtor(struct nvkm_object *object) |
| 246 | { | 314 | { |
| 247 | struct nv50_disp_chan *chan = nv50_disp_chan(object); | 315 | struct nv50_disp_chan *chan = nv50_disp_chan(object); |
| 248 | struct nv50_disp *disp = chan->root->disp; | 316 | struct nv50_disp *disp = chan->disp; |
| 249 | if (chan->chid.user >= 0) | 317 | if (chan->chid.user >= 0) |
| 250 | disp->chan[chan->chid.user] = NULL; | 318 | disp->chan[chan->chid.user] = NULL; |
| 251 | return chan->func->dtor ? chan->func->dtor(chan) : chan; | 319 | nvkm_memory_unref(&chan->memory); |
| 320 | return chan; | ||
| 252 | } | 321 | } |
| 253 | 322 | ||
| 254 | static const struct nvkm_object_func | 323 | static const struct nvkm_object_func |
| @@ -264,18 +333,22 @@ nv50_disp_chan = { | |||
| 264 | }; | 333 | }; |
| 265 | 334 | ||
| 266 | int | 335 | int |
| 267 | nv50_disp_chan_ctor(const struct nv50_disp_chan_func *func, | 336 | nv50_disp_chan_new_(const struct nv50_disp_chan_func *func, |
| 268 | const struct nv50_disp_chan_mthd *mthd, | 337 | const struct nv50_disp_chan_mthd *mthd, |
| 269 | struct nv50_disp_root *root, int ctrl, int user, int head, | 338 | struct nv50_disp *disp, int ctrl, int user, int head, |
| 270 | const struct nvkm_oclass *oclass, | 339 | const struct nvkm_oclass *oclass, |
| 271 | struct nv50_disp_chan *chan) | 340 | struct nvkm_object **pobject) |
| 272 | { | 341 | { |
| 273 | struct nv50_disp *disp = root->disp; | 342 | struct nv50_disp_chan *chan; |
| 343 | |||
| 344 | if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) | ||
| 345 | return -ENOMEM; | ||
| 346 | *pobject = &chan->object; | ||
| 274 | 347 | ||
| 275 | nvkm_object_ctor(&nv50_disp_chan, oclass, &chan->object); | 348 | nvkm_object_ctor(&nv50_disp_chan, oclass, &chan->object); |
| 276 | chan->func = func; | 349 | chan->func = func; |
| 277 | chan->mthd = mthd; | 350 | chan->mthd = mthd; |
| 278 | chan->root = root; | 351 | chan->disp = disp; |
| 279 | chan->chid.ctrl = ctrl; | 352 | chan->chid.ctrl = ctrl; |
| 280 | chan->chid.user = user; | 353 | chan->chid.user = user; |
| 281 | chan->head = head; | 354 | chan->head = head; |
| @@ -287,20 +360,3 @@ nv50_disp_chan_ctor(const struct nv50_disp_chan_func *func, | |||
| 287 | disp->chan[chan->chid.user] = chan; | 360 | disp->chan[chan->chid.user] = chan; |
| 288 | return 0; | 361 | return 0; |
| 289 | } | 362 | } |
| 290 | |||
| 291 | int | ||
| 292 | nv50_disp_chan_new_(const struct nv50_disp_chan_func *func, | ||
| 293 | const struct nv50_disp_chan_mthd *mthd, | ||
| 294 | struct nv50_disp_root *root, int ctrl, int user, int head, | ||
| 295 | const struct nvkm_oclass *oclass, | ||
| 296 | struct nvkm_object **pobject) | ||
| 297 | { | ||
| 298 | struct nv50_disp_chan *chan; | ||
| 299 | |||
| 300 | if (!(chan = kzalloc(sizeof(*chan), GFP_KERNEL))) | ||
| 301 | return -ENOMEM; | ||
| 302 | *pobject = &chan->object; | ||
| 303 | |||
| 304 | return nv50_disp_chan_ctor(func, mthd, root, ctrl, user, | ||
| 305 | head, oclass, chan); | ||
| 306 | } | ||
