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
  69int
  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, true);
 177                                return 0;
 178                        }
 179                } else
 180                        return ret;
 181        }
 182                break;
 183        case NV50_DISP_MTHD_V1_PIOR_PWR:
 184                if (!func->pior.power)
 185                        return -ENODEV;
 186                return func->pior.power(object, disp, data, size, head, outp);
 187        default:
 188                break;
 189        }
 190
 191        return -EINVAL;
 192}
 193
 194static int
 195nv50_disp_root_dmac_new_(const struct nvkm_oclass *oclass,
 196                         void *data, u32 size, struct nvkm_object **pobject)
 197{
 198        const struct nv50_disp_dmac_oclass *sclass = oclass->priv;
 199        struct nv50_disp_root *root = nv50_disp_root(oclass->parent);
 200        return sclass->ctor(sclass->func, sclass->mthd, root, sclass->chid,
 201                            oclass, data, size, pobject);
 202}
 203
 204static int
 205nv50_disp_root_pioc_new_(const struct nvkm_oclass *oclass,
 206                         void *data, u32 size, struct nvkm_object **pobject)
 207{
 208        const struct nv50_disp_pioc_oclass *sclass = oclass->priv;
 209        struct nv50_disp_root *root = nv50_disp_root(oclass->parent);
 210        return sclass->ctor(sclass->func, sclass->mthd, root, sclass->chid,
 211                            oclass, data, size, pobject);
 212}
 213
 214static int
 215nv50_disp_root_child_get_(struct nvkm_object *object, int index,
 216                          struct nvkm_oclass *sclass)
 217{
 218        struct nv50_disp_root *root = nv50_disp_root(object);
 219
 220        if (index < ARRAY_SIZE(root->func->dmac)) {
 221                sclass->base = root->func->dmac[index]->base;
 222                sclass->priv = root->func->dmac[index];
 223                sclass->ctor = nv50_disp_root_dmac_new_;
 224                return 0;
 225        }
 226
 227        index -= ARRAY_SIZE(root->func->dmac);
 228
 229        if (index < ARRAY_SIZE(root->func->pioc)) {
 230                sclass->base = root->func->pioc[index]->base;
 231                sclass->priv = root->func->pioc[index];
 232                sclass->ctor = nv50_disp_root_pioc_new_;
 233                return 0;
 234        }
 235
 236        return -EINVAL;
 237}
 238
 239static int
 240nv50_disp_root_fini_(struct nvkm_object *object, bool suspend)
 241{
 242        struct nv50_disp_root *root = nv50_disp_root(object);
 243        root->func->fini(root);
 244        return 0;
 245}
 246
 247static int
 248nv50_disp_root_init_(struct nvkm_object *object)
 249{
 250        struct nv50_disp_root *root = nv50_disp_root(object);
 251        return root->func->init(root);
 252}
 253
 254static void *
 255nv50_disp_root_dtor_(struct nvkm_object *object)
 256{
 257        struct nv50_disp_root *root = nv50_disp_root(object);
 258        nvkm_ramht_del(&root->ramht);
 259        nvkm_gpuobj_del(&root->instmem);
 260        return root;
 261}
 262
 263static const struct nvkm_object_func
 264nv50_disp_root_ = {
 265        .dtor = nv50_disp_root_dtor_,
 266        .init = nv50_disp_root_init_,
 267        .fini = nv50_disp_root_fini_,
 268        .mthd = nv50_disp_root_mthd_,
 269        .ntfy = nvkm_disp_ntfy,
 270        .sclass = nv50_disp_root_child_get_,
 271};
 272
 273int
 274nv50_disp_root_new_(const struct nv50_disp_root_func *func,
 275                    struct nvkm_disp *base, const struct nvkm_oclass *oclass,
 276                    void *data, u32 size, struct nvkm_object **pobject)
 277{
 278        struct nv50_disp *disp = nv50_disp(base);
 279        struct nv50_disp_root *root;
 280        struct nvkm_device *device = disp->base.engine.subdev.device;
 281        int ret;
 282
 283        if (!(root = kzalloc(sizeof(*root), GFP_KERNEL)))
 284                return -ENOMEM;
 285        *pobject = &root->object;
 286
 287        nvkm_object_ctor(&nv50_disp_root_, oclass, &root->object);
 288        root->func = func;
 289        root->disp = disp;
 290
 291        ret = nvkm_gpuobj_new(disp->base.engine.subdev.device, 0x10000, 0x10000,
 292                              false, NULL, &root->instmem);
 293        if (ret)
 294                return ret;
 295
 296        return nvkm_ramht_new(device, 0x1000, 0, root->instmem, &root->ramht);
 297}
 298
 299void
 300nv50_disp_root_fini(struct nv50_disp_root *root)
 301{
 302        struct nvkm_device *device = root->disp->base.engine.subdev.device;
 303        /* disable all interrupts */
 304        nvkm_wr32(device, 0x610024, 0x00000000);
 305        nvkm_wr32(device, 0x610020, 0x00000000);
 306}
 307
 308int
 309nv50_disp_root_init(struct nv50_disp_root *root)
 310{
 311        struct nv50_disp *disp = root->disp;
 312        struct nvkm_device *device = disp->base.engine.subdev.device;
 313        u32 tmp;
 314        int i;
 315
 316        /* The below segments of code copying values from one register to
 317         * another appear to inform EVO of the display capabilities or
 318         * something similar.  NFI what the 0x614004 caps are for..
 319         */
 320        tmp = nvkm_rd32(device, 0x614004);
 321        nvkm_wr32(device, 0x610184, tmp);
 322
 323        /* ... CRTC caps */
 324        for (i = 0; i < disp->base.head.nr; i++) {
 325                tmp = nvkm_rd32(device, 0x616100 + (i * 0x800));
 326                nvkm_wr32(device, 0x610190 + (i * 0x10), tmp);
 327                tmp = nvkm_rd32(device, 0x616104 + (i * 0x800));
 328                nvkm_wr32(device, 0x610194 + (i * 0x10), tmp);
 329                tmp = nvkm_rd32(device, 0x616108 + (i * 0x800));
 330                nvkm_wr32(device, 0x610198 + (i * 0x10), tmp);
 331                tmp = nvkm_rd32(device, 0x61610c + (i * 0x800));
 332                nvkm_wr32(device, 0x61019c + (i * 0x10), tmp);
 333        }
 334
 335        /* ... DAC caps */
 336        for (i = 0; i < disp->func->dac.nr; i++) {
 337                tmp = nvkm_rd32(device, 0x61a000 + (i * 0x800));
 338                nvkm_wr32(device, 0x6101d0 + (i * 0x04), tmp);
 339        }
 340
 341        /* ... SOR caps */
 342        for (i = 0; i < disp->func->sor.nr; i++) {
 343                tmp = nvkm_rd32(device, 0x61c000 + (i * 0x800));
 344                nvkm_wr32(device, 0x6101e0 + (i * 0x04), tmp);
 345        }
 346
 347        /* ... PIOR caps */
 348        for (i = 0; i < disp->func->pior.nr; i++) {
 349                tmp = nvkm_rd32(device, 0x61e000 + (i * 0x800));
 350                nvkm_wr32(device, 0x6101f0 + (i * 0x04), tmp);
 351        }
 352
 353        /* steal display away from vbios, or something like that */
 354        if (nvkm_rd32(device, 0x610024) & 0x00000100) {
 355                nvkm_wr32(device, 0x610024, 0x00000100);
 356                nvkm_mask(device, 0x6194e8, 0x00000001, 0x00000000);
 357                if (nvkm_msec(device, 2000,
 358                        if (!(nvkm_rd32(device, 0x6194e8) & 0x00000002))
 359                                break;
 360                ) < 0)
 361                        return -EBUSY;
 362        }
 363
 364        /* point at display engine memory area (hash table, objects) */
 365        nvkm_wr32(device, 0x610010, (root->instmem->addr >> 8) | 9);
 366
 367        /* enable supervisor interrupts, disable everything else */
 368        nvkm_wr32(device, 0x61002c, 0x00000370);
 369        nvkm_wr32(device, 0x610028, 0x00000000);
 370        return 0;
 371}
 372
 373static const struct nv50_disp_root_func
 374nv50_disp_root = {
 375        .init = nv50_disp_root_init,
 376        .fini = nv50_disp_root_fini,
 377        .dmac = {
 378                &nv50_disp_core_oclass,
 379                &nv50_disp_base_oclass,
 380                &nv50_disp_ovly_oclass,
 381        },
 382        .pioc = {
 383                &nv50_disp_oimm_oclass,
 384                &nv50_disp_curs_oclass,
 385        },
 386};
 387
 388static int
 389nv50_disp_root_new(struct nvkm_disp *disp, const struct nvkm_oclass *oclass,
 390                   void *data, u32 size, struct nvkm_object **pobject)
 391{
 392        return nv50_disp_root_new_(&nv50_disp_root, disp, oclass,
 393                                   data, size, pobject);
 394}
 395
 396const struct nvkm_disp_oclass
 397nv50_disp_root_oclass = {
 398        .base.oclass = NV50_DISP,
 399        .base.minver = -1,
 400        .base.maxver = -1,
 401        .ctor = nv50_disp_root_new,
 402};
 403