linux/drivers/gpu/drm/nouveau/nvkm/engine/disp/rootnv50.c
<<
>>
Prefs
   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 "rootnv50.h"
  25#include "dmacnv50.h"
  26
  27#include <core/client.h>
  28#include <core/ramht.h>
  29#include <subdev/timer.h>
  30
  31#include <nvif/class.h>
  32#include <nvif/cl5070.h>
  33#include <nvif/unpack.h>
  34
  35int
  36nv50_disp_root_scanoutpos(NV50_DISP_MTHD_V0)
  37{
  38        struct nvkm_device *device = disp->base.engine.subdev.device;
  39        const u32 blanke = nvkm_rd32(device, 0x610aec + (head * 0x540));
  40        const u32 blanks = nvkm_rd32(device, 0x610af4 + (head * 0x540));
  41        const u32 total  = nvkm_rd32(device, 0x610afc + (head * 0x540));
  42        union {
  43                struct nv50_disp_scanoutpos_v0 v0;
  44        } *args = data;
  45        int ret = -ENOSYS;
  46
  47        nvif_ioctl(object, "disp scanoutpos size %d\n", size);
  48        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
  49                nvif_ioctl(object, "disp scanoutpos vers %d\n",
  50                           args->v0.version);
  51                args->v0.vblanke = (blanke & 0xffff0000) >> 16;
  52                args->v0.hblanke = (blanke & 0x0000ffff);
  53                args->v0.vblanks = (blanks & 0xffff0000) >> 16;
  54                args->v0.hblanks = (blanks & 0x0000ffff);
  55                args->v0.vtotal  = ( total & 0xffff0000) >> 16;
  56                args->v0.htotal  = ( total & 0x0000ffff);
  57                args->v0.time[0] = ktime_to_ns(ktime_get());
  58                args->v0.vline = /* vline read locks hline */
  59                        nvkm_rd32(device, 0x616340 + (head * 0x800)) & 0xffff;
  60                args->v0.time[1] = ktime_to_ns(ktime_get());
  61                args->v0.hline =
  62                        nvkm_rd32(device, 0x616344 + (head * 0x800)) & 0xffff;
  63        } else
  64                return ret;
  65
  66        return 0;
  67}
  68
  69static int
  70nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
  71{
  72        union {
  73                struct nv50_disp_mthd_v0 v0;
  74                struct nv50_disp_mthd_v1 v1;
  75        } *args = data;
  76        struct nv50_disp_root *root = nv50_disp_root(object);
  77        struct nv50_disp *disp = root->disp;
  78        const struct nv50_disp_func *func = disp->func;
  79        struct nvkm_output *outp = NULL;
  80        struct nvkm_output *temp;
  81        u16 type, mask = 0;
  82        int head, ret = -ENOSYS;
  83
  84        if (mthd != NV50_DISP_MTHD)
  85                return -EINVAL;
  86
  87        nvif_ioctl(object, "disp mthd size %d\n", size);
  88        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
  89                nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n",
  90                           args->v0.version, args->v0.method, args->v0.head);
  91                mthd = args->v0.method;
  92                head = args->v0.head;
  93        } else
  94        if (!(ret = nvif_unpack(ret, &data, &size, args->v1, 1, 1, true))) {
  95                nvif_ioctl(object, "disp mthd vers %d mthd %02x "
  96                                   "type %04x mask %04x\n",
  97                           args->v1.version, args->v1.method,
  98                           args->v1.hasht, args->v1.hashm);
  99                mthd = args->v1.method;
 100                type = args->v1.hasht;
 101                mask = args->v1.hashm;
 102                head = ffs((mask >> 8) & 0x0f) - 1;
 103        } else
 104                return ret;
 105
 106        if (head < 0 || head >= disp->base.head.nr)
 107                return -ENXIO;
 108
 109        if (mask) {
 110                list_for_each_entry(temp, &disp->base.outp, head) {
 111                        if ((temp->info.hasht         == type) &&
 112                            (temp->info.hashm & mask) == mask) {
 113                                outp = temp;
 114                                break;
 115                        }
 116                }
 117                if (outp == NULL)
 118                        return -ENXIO;
 119        }
 120
 121        switch (mthd) {
 122        case NV50_DISP_SCANOUTPOS:
 123                return func->head.scanoutpos(object, disp, data, size, head);
 124        default:
 125                break;
 126        }
 127
 128        switch (mthd * !!outp) {
 129        case NV50_DISP_MTHD_V1_DAC_PWR:
 130                return func->dac.power(object, disp, data, size, head, outp);
 131        case NV50_DISP_MTHD_V1_DAC_LOAD:
 132                return func->dac.sense(object, disp, data, size, head, outp);
 133        case NV50_DISP_MTHD_V1_SOR_PWR:
 134                return func->sor.power(object, disp, data, size, head, outp);
 135        case NV50_DISP_MTHD_V1_SOR_HDA_ELD:
 136                if (!func->sor.hda_eld)
 137                        return -ENODEV;
 138                return func->sor.hda_eld(object, disp, data, size, head, outp);
 139        case NV50_DISP_MTHD_V1_SOR_HDMI_PWR:
 140                if (!func->sor.hdmi)
 141                        return -ENODEV;
 142                return func->sor.hdmi(object, disp, data, size, head, outp);
 143        case NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT: {
 144                union {
 145                        struct nv50_disp_sor_lvds_script_v0 v0;
 146                } *args = data;
 147                int ret = -ENOSYS;
 148                nvif_ioctl(object, "disp sor lvds script size %d\n", size);
 149                if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 150                        nvif_ioctl(object, "disp sor lvds script "
 151                                           "vers %d name %04x\n",
 152                                   args->v0.version, args->v0.script);
 153                        disp->sor.lvdsconf = args->v0.script;
 154                        return 0;
 155                } else
 156                        return ret;
 157        }
 158                break;
 159        case NV50_DISP_MTHD_V1_SOR_DP_PWR: {
 160                struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
 161                union {
 162                        struct nv50_disp_sor_dp_pwr_v0 v0;
 163                } *args = data;
 164                int ret = -ENOSYS;
 165                nvif_ioctl(object, "disp sor dp pwr size %d\n", size);
 166                if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 167                        nvif_ioctl(object, "disp sor dp pwr vers %d state %d\n",
 168                                   args->v0.version, args->v0.state);
 169                        if (args->v0.state == 0) {
 170                                nvkm_notify_put(&outpdp->irq);
 171                                outpdp->func->lnk_pwr(outpdp, 0);
 172                                atomic_set(&outpdp->lt.done, 0);
 173                                return 0;
 174                        } else
 175                        if (args->v0.state != 0) {
 176                                nvkm_output_dp_train(&outpdp->base, 0);
 177                                return 0;
 178                        }
 179                } else
 180                        return ret;
 181        }
 182                break;
 183        case NV50_DISP_MTHD_V1_SOR_DP_MST_LINK: {
 184                struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
 185                union {
 186                        struct nv50_disp_sor_dp_mst_link_v0 v0;
 187                } *args = data;
 188                int ret = -ENOSYS;
 189                nvif_ioctl(object, "disp sor dp mst link size %d\n", size);
 190                if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 191                        nvif_ioctl(object, "disp sor dp mst link vers %d state %d\n",
 192                                   args->v0.version, args->v0.state);
 193                        if (outpdp->lt.mst != !!args->v0.state) {
 194                                outpdp->lt.mst = !!args->v0.state;
 195                                atomic_set(&outpdp->lt.done, 0);
 196                                nvkm_output_dp_train(&outpdp->base, 0);
 197                        }
 198                        return 0;
 199                } else
 200                        return ret;
 201        }
 202                break;
 203        case NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI: {
 204                struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
 205                union {
 206                        struct nv50_disp_sor_dp_mst_vcpi_v0 v0;
 207                } *args = data;
 208                int ret = -ENOSYS;
 209                nvif_ioctl(object, "disp sor dp mst vcpi size %d\n", size);
 210                if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 211                        nvif_ioctl(object, "disp sor dp mst vcpi vers %d "
 212                                           "slot %02x/%02x pbn %04x/%04x\n",
 213                                   args->v0.version, args->v0.start_slot,
 214                                   args->v0.num_slots, args->v0.pbn,
 215                                   args->v0.aligned_pbn);
 216                        if (!outpdp->func->vcpi)
 217                                return -ENODEV;
 218                        outpdp->func->vcpi(outpdp, head, args->v0.start_slot,
 219                                           args->v0.num_slots, args->v0.pbn,
 220                                           args->v0.aligned_pbn);
 221                        return 0;
 222                } else
 223                        return ret;
 224        }
 225                break;
 226        case NV50_DISP_MTHD_V1_PIOR_PWR:
 227                if (!func->pior.power)
 228                        return -ENODEV;
 229                return func->pior.power(object, disp, data, size, head, outp);
 230        default:
 231                break;
 232        }
 233
 234        return -EINVAL;
 235}
 236
 237static int
 238nv50_disp_root_dmac_new_(const struct nvkm_oclass *oclass,
 239                         void *data, u32 size, struct nvkm_object **pobject)
 240{
 241        const struct nv50_disp_dmac_oclass *sclass = oclass->priv;
 242        struct nv50_disp_root *root = nv50_disp_root(oclass->parent);
 243        return sclass->ctor(sclass->func, sclass->mthd, root, sclass->chid,
 244                            oclass, data, size, pobject);
 245}
 246
 247static int
 248nv50_disp_root_pioc_new_(const struct nvkm_oclass *oclass,
 249                         void *data, u32 size, struct nvkm_object **pobject)
 250{
 251        const struct nv50_disp_pioc_oclass *sclass = oclass->priv;
 252        struct nv50_disp_root *root = nv50_disp_root(oclass->parent);
 253        return sclass->ctor(sclass->func, sclass->mthd, root, sclass->chid.ctrl,
 254                            sclass->chid.user, oclass, data, size, pobject);
 255}
 256
 257static int
 258nv50_disp_root_child_get_(struct nvkm_object *object, int index,
 259                          struct nvkm_oclass *sclass)
 260{
 261        struct nv50_disp_root *root = nv50_disp_root(object);
 262
 263        if (index < ARRAY_SIZE(root->func->dmac)) {
 264                sclass->base = root->func->dmac[index]->base;
 265                sclass->priv = root->func->dmac[index];
 266                sclass->ctor = nv50_disp_root_dmac_new_;
 267                return 0;
 268        }
 269
 270        index -= ARRAY_SIZE(root->func->dmac);
 271
 272        if (index < ARRAY_SIZE(root->func->pioc)) {
 273                sclass->base = root->func->pioc[index]->base;
 274                sclass->priv = root->func->pioc[index];
 275                sclass->ctor = nv50_disp_root_pioc_new_;
 276                return 0;
 277        }
 278
 279        return -EINVAL;
 280}
 281
 282static int
 283nv50_disp_root_fini_(struct nvkm_object *object, bool suspend)
 284{
 285        struct nv50_disp_root *root = nv50_disp_root(object);
 286        root->func->fini(root);
 287        return 0;
 288}
 289
 290static int
 291nv50_disp_root_init_(struct nvkm_object *object)
 292{
 293        struct nv50_disp_root *root = nv50_disp_root(object);
 294        return root->func->init(root);
 295}
 296
 297static void *
 298nv50_disp_root_dtor_(struct nvkm_object *object)
 299{
 300        struct nv50_disp_root *root = nv50_disp_root(object);
 301        nvkm_ramht_del(&root->ramht);
 302        nvkm_gpuobj_del(&root->instmem);
 303        return root;
 304}
 305
 306static const struct nvkm_object_func
 307nv50_disp_root_ = {
 308        .dtor = nv50_disp_root_dtor_,
 309        .init = nv50_disp_root_init_,
 310        .fini = nv50_disp_root_fini_,
 311        .mthd = nv50_disp_root_mthd_,
 312        .ntfy = nvkm_disp_ntfy,
 313        .sclass = nv50_disp_root_child_get_,
 314};
 315
 316int
 317nv50_disp_root_new_(const struct nv50_disp_root_func *func,
 318                    struct nvkm_disp *base, const struct nvkm_oclass *oclass,
 319                    void *data, u32 size, struct nvkm_object **pobject)
 320{
 321        struct nv50_disp *disp = nv50_disp(base);
 322        struct nv50_disp_root *root;
 323        struct nvkm_device *device = disp->base.engine.subdev.device;
 324        int ret;
 325
 326        if (!(root = kzalloc(sizeof(*root), GFP_KERNEL)))
 327                return -ENOMEM;
 328        *pobject = &root->object;
 329
 330        nvkm_object_ctor(&nv50_disp_root_, oclass, &root->object);
 331        root->func = func;
 332        root->disp = disp;
 333
 334        ret = nvkm_gpuobj_new(disp->base.engine.subdev.device, 0x10000, 0x10000,
 335                              false, NULL, &root->instmem);
 336        if (ret)
 337                return ret;
 338
 339        return nvkm_ramht_new(device, 0x1000, 0, root->instmem, &root->ramht);
 340}
 341
 342void
 343nv50_disp_root_fini(struct nv50_disp_root *root)
 344{
 345        struct nvkm_device *device = root->disp->base.engine.subdev.device;
 346        /* disable all interrupts */
 347        nvkm_wr32(device, 0x610024, 0x00000000);
 348        nvkm_wr32(device, 0x610020, 0x00000000);
 349}
 350
 351int
 352nv50_disp_root_init(struct nv50_disp_root *root)
 353{
 354        struct nv50_disp *disp = root->disp;
 355        struct nvkm_device *device = disp->base.engine.subdev.device;
 356        u32 tmp;
 357        int i;
 358
 359        /* The below segments of code copying values from one register to
 360         * another appear to inform EVO of the display capabilities or
 361         * something similar.  NFI what the 0x614004 caps are for..
 362         */
 363        tmp = nvkm_rd32(device, 0x614004);
 364        nvkm_wr32(device, 0x610184, tmp);
 365
 366        /* ... CRTC caps */
 367        for (i = 0; i < disp->base.head.nr; i++) {
 368                tmp = nvkm_rd32(device, 0x616100 + (i * 0x800));
 369                nvkm_wr32(device, 0x610190 + (i * 0x10), tmp);
 370                tmp = nvkm_rd32(device, 0x616104 + (i * 0x800));
 371                nvkm_wr32(device, 0x610194 + (i * 0x10), tmp);
 372                tmp = nvkm_rd32(device, 0x616108 + (i * 0x800));
 373                nvkm_wr32(device, 0x610198 + (i * 0x10), tmp);
 374                tmp = nvkm_rd32(device, 0x61610c + (i * 0x800));
 375                nvkm_wr32(device, 0x61019c + (i * 0x10), tmp);
 376        }
 377
 378        /* ... DAC caps */
 379        for (i = 0; i < disp->func->dac.nr; i++) {
 380                tmp = nvkm_rd32(device, 0x61a000 + (i * 0x800));
 381                nvkm_wr32(device, 0x6101d0 + (i * 0x04), tmp);
 382        }
 383
 384        /* ... SOR caps */
 385        for (i = 0; i < disp->func->sor.nr; i++) {
 386                tmp = nvkm_rd32(device, 0x61c000 + (i * 0x800));
 387                nvkm_wr32(device, 0x6101e0 + (i * 0x04), tmp);
 388        }
 389
 390        /* ... PIOR caps */
 391        for (i = 0; i < disp->func->pior.nr; i++) {
 392                tmp = nvkm_rd32(device, 0x61e000 + (i * 0x800));
 393                nvkm_wr32(device, 0x6101f0 + (i * 0x04), tmp);
 394        }
 395
 396        /* steal display away from vbios, or something like that */
 397        if (nvkm_rd32(device, 0x610024) & 0x00000100) {
 398                nvkm_wr32(device, 0x610024, 0x00000100);
 399                nvkm_mask(device, 0x6194e8, 0x00000001, 0x00000000);
 400                if (nvkm_msec(device, 2000,
 401                        if (!(nvkm_rd32(device, 0x6194e8) & 0x00000002))
 402                                break;
 403                ) < 0)
 404                        return -EBUSY;
 405        }
 406
 407        /* point at display engine memory area (hash table, objects) */
 408        nvkm_wr32(device, 0x610010, (root->instmem->addr >> 8) | 9);
 409
 410        /* enable supervisor interrupts, disable everything else */
 411        nvkm_wr32(device, 0x61002c, 0x00000370);
 412        nvkm_wr32(device, 0x610028, 0x00000000);
 413        return 0;
 414}
 415
 416static const struct nv50_disp_root_func
 417nv50_disp_root = {
 418        .init = nv50_disp_root_init,
 419        .fini = nv50_disp_root_fini,
 420        .dmac = {
 421                &nv50_disp_core_oclass,
 422                &nv50_disp_base_oclass,
 423                &nv50_disp_ovly_oclass,
 424        },
 425        .pioc = {
 426                &nv50_disp_oimm_oclass,
 427                &nv50_disp_curs_oclass,
 428        },
 429};
 430
 431static int
 432nv50_disp_root_new(struct nvkm_disp *disp, const struct nvkm_oclass *oclass,
 433                   void *data, u32 size, struct nvkm_object **pobject)
 434{
 435        return nv50_disp_root_new_(&nv50_disp_root, disp, oclass,
 436                                   data, size, pobject);
 437}
 438
 439const struct nvkm_disp_oclass
 440nv50_disp_root_oclass = {
 441        .base.oclass = NV50_DISP,
 442        .base.minver = -1,
 443        .base.maxver = -1,
 444        .ctor = nv50_disp_root_new,
 445};
 446