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 "channv50.h"
  26#include "dp.h"
  27#include "head.h"
  28#include "ior.h"
  29
  30#include <core/client.h>
  31
  32#include <nvif/class.h>
  33#include <nvif/cl5070.h>
  34#include <nvif/unpack.h>
  35
  36static int
  37nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
  38{
  39        union {
  40                struct nv50_disp_mthd_v0 v0;
  41                struct nv50_disp_mthd_v1 v1;
  42        } *args = data;
  43        struct nv50_disp_root *root = nv50_disp_root(object);
  44        struct nv50_disp *disp = root->disp;
  45        struct nvkm_outp *temp, *outp = NULL;
  46        struct nvkm_head *head;
  47        u16 type, mask = 0;
  48        int hidx, ret = -ENOSYS;
  49
  50        if (mthd != NV50_DISP_MTHD)
  51                return -EINVAL;
  52
  53        nvif_ioctl(object, "disp mthd size %d\n", size);
  54        if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
  55                nvif_ioctl(object, "disp mthd vers %d mthd %02x head %d\n",
  56                           args->v0.version, args->v0.method, args->v0.head);
  57                mthd = args->v0.method;
  58                hidx = args->v0.head;
  59        } else
  60        if (!(ret = nvif_unpack(ret, &data, &size, args->v1, 1, 1, true))) {
  61                nvif_ioctl(object, "disp mthd vers %d mthd %02x "
  62                                   "type %04x mask %04x\n",
  63                           args->v1.version, args->v1.method,
  64                           args->v1.hasht, args->v1.hashm);
  65                mthd = args->v1.method;
  66                type = args->v1.hasht;
  67                mask = args->v1.hashm;
  68                hidx = ffs((mask >> 8) & 0x0f) - 1;
  69        } else
  70                return ret;
  71
  72        if (!(head = nvkm_head_find(&disp->base, hidx)))
  73                return -ENXIO;
  74
  75        if (mask) {
  76                list_for_each_entry(temp, &disp->base.outp, head) {
  77                        if ((temp->info.hasht         == type) &&
  78                            (temp->info.hashm & mask) == mask) {
  79                                outp = temp;
  80                                break;
  81                        }
  82                }
  83                if (outp == NULL)
  84                        return -ENXIO;
  85        }
  86
  87        switch (mthd) {
  88        case NV50_DISP_SCANOUTPOS: {
  89                return nvkm_head_mthd_scanoutpos(object, head, data, size);
  90        }
  91        default:
  92                break;
  93        }
  94
  95        switch (mthd * !!outp) {
  96        case NV50_DISP_MTHD_V1_ACQUIRE: {
  97                union {
  98                        struct nv50_disp_acquire_v0 v0;
  99                } *args = data;
 100                int ret = -ENOSYS;
 101                if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 102                        ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, args->v0.hda);
 103                        if (ret == 0) {
 104                                args->v0.or = outp->ior->id;
 105                                args->v0.link = outp->ior->asy.link;
 106                        }
 107                }
 108                return ret;
 109        }
 110                break;
 111        case NV50_DISP_MTHD_V1_RELEASE:
 112                nvkm_outp_release(outp, NVKM_OUTP_USER);
 113                return 0;
 114        case NV50_DISP_MTHD_V1_DAC_LOAD: {
 115                union {
 116                        struct nv50_disp_dac_load_v0 v0;
 117                } *args = data;
 118                int ret = -ENOSYS;
 119                if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 120                        if (args->v0.data & 0xfff00000)
 121                                return -EINVAL;
 122                        ret = nvkm_outp_acquire(outp, NVKM_OUTP_PRIV, false);
 123                        if (ret)
 124                                return ret;
 125                        ret = outp->ior->func->sense(outp->ior, args->v0.data);
 126                        nvkm_outp_release(outp, NVKM_OUTP_PRIV);
 127                        if (ret < 0)
 128                                return ret;
 129                        args->v0.load = ret;
 130                        return 0;
 131                } else
 132                        return ret;
 133        }
 134                break;
 135        case NV50_DISP_MTHD_V1_SOR_HDA_ELD: {
 136                union {
 137                        struct nv50_disp_sor_hda_eld_v0 v0;
 138                } *args = data;
 139                struct nvkm_ior *ior = outp->ior;
 140                int ret = -ENOSYS;
 141
 142                nvif_ioctl(object, "disp sor hda eld size %d\n", size);
 143                if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
 144                        nvif_ioctl(object, "disp sor hda eld vers %d\n",
 145                                   args->v0.version);
 146                        if (size > 0x60)
 147                                return -E2BIG;
 148                } else
 149                        return ret;
 150
 151                if (!ior->func->hda.hpd)
 152                        return -ENODEV;
 153
 154                if (size && args->v0.data[0]) {
 155                        if (outp->info.type == DCB_OUTPUT_DP)
 156                                ior->func->dp.audio(ior, hidx, true);
 157                        ior->func->hda.hpd(ior, hidx, true);
 158                        ior->func->hda.eld(ior, hidx, data, size);
 159                } else {
 160                        if (outp->info.type == DCB_OUTPUT_DP)
 161                                ior->func->dp.audio(ior, hidx, false);
 162                        ior->func->hda.hpd(ior, hidx, false);
 163                }
 164
 165                return 0;
 166        }
 167                break;
 168        case NV50_DISP_MTHD_V1_SOR_HDMI_PWR: {
 169                union {
 170                        struct nv50_disp_sor_hdmi_pwr_v0 v0;
 171                } *args = data;
 172                u8 *vendor, vendor_size;
 173                u8 *avi, avi_size;
 174                int ret = -ENOSYS;
 175
 176                nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
 177                if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
 178                        nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
 179                                           "max_ac_packet %d rekey %d scdc %d\n",
 180                                   args->v0.version, args->v0.state,
 181                                   args->v0.max_ac_packet, args->v0.rekey,
 182                                   args->v0.scdc);
 183                        if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
 184                                return -EINVAL;
 185                        if ((args->v0.avi_infoframe_length
 186                             + args->v0.vendor_infoframe_length) > size)
 187                                return -EINVAL;
 188                        else
 189                        if ((args->v0.avi_infoframe_length
 190                             + args->v0.vendor_infoframe_length) < size)
 191                                return -E2BIG;
 192                        avi = data;
 193                        avi_size = args->v0.avi_infoframe_length;
 194                        vendor = avi + avi_size;
 195                        vendor_size = args->v0.vendor_infoframe_length;
 196                } else
 197                        return ret;
 198
 199                if (!outp->ior->func->hdmi.ctrl)
 200                        return -ENODEV;
 201
 202                outp->ior->func->hdmi.ctrl(outp->ior, hidx, args->v0.state,
 203                                           args->v0.max_ac_packet,
 204                                           args->v0.rekey, avi, avi_size,
 205                                           vendor, vendor_size);
 206
 207                if (outp->ior->func->hdmi.scdc)
 208                        outp->ior->func->hdmi.scdc(outp->ior, args->v0.scdc);
 209
 210                return 0;
 211        }
 212                break;
 213        case NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT: {
 214                union {
 215                        struct nv50_disp_sor_lvds_script_v0 v0;
 216                } *args = data;
 217                int ret = -ENOSYS;
 218                nvif_ioctl(object, "disp sor lvds script size %d\n", size);
 219                if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 220                        nvif_ioctl(object, "disp sor lvds script "
 221                                           "vers %d name %04x\n",
 222                                   args->v0.version, args->v0.script);
 223                        disp->sor.lvdsconf = args->v0.script;
 224                        return 0;
 225                } else
 226                        return ret;
 227        }
 228                break;
 229        case NV50_DISP_MTHD_V1_SOR_DP_MST_LINK: {
 230                struct nvkm_dp *dp = nvkm_dp(outp);
 231                union {
 232                        struct nv50_disp_sor_dp_mst_link_v0 v0;
 233                } *args = data;
 234                int ret = -ENOSYS;
 235                nvif_ioctl(object, "disp sor dp mst link size %d\n", size);
 236                if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 237                        nvif_ioctl(object, "disp sor dp mst link vers %d state %d\n",
 238                                   args->v0.version, args->v0.state);
 239                        dp->lt.mst = !!args->v0.state;
 240                        return 0;
 241                } else
 242                        return ret;
 243        }
 244                break;
 245        case NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI: {
 246                union {
 247                        struct nv50_disp_sor_dp_mst_vcpi_v0 v0;
 248                } *args = data;
 249                int ret = -ENOSYS;
 250                nvif_ioctl(object, "disp sor dp mst vcpi size %d\n", size);
 251                if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
 252                        nvif_ioctl(object, "disp sor dp mst vcpi vers %d "
 253                                           "slot %02x/%02x pbn %04x/%04x\n",
 254                                   args->v0.version, args->v0.start_slot,
 255                                   args->v0.num_slots, args->v0.pbn,
 256                                   args->v0.aligned_pbn);
 257                        if (!outp->ior->func->dp.vcpi)
 258                                return -ENODEV;
 259                        outp->ior->func->dp.vcpi(outp->ior, hidx,
 260                                                 args->v0.start_slot,
 261                                                 args->v0.num_slots,
 262                                                 args->v0.pbn,
 263                                                 args->v0.aligned_pbn);
 264                        return 0;
 265                } else
 266                        return ret;
 267        }
 268                break;
 269        default:
 270                break;
 271        }
 272
 273        return -EINVAL;
 274}
 275
 276static int
 277nv50_disp_root_child_new_(const struct nvkm_oclass *oclass,
 278                          void *argv, u32 argc, struct nvkm_object **pobject)
 279{
 280        struct nv50_disp *disp = nv50_disp_root(oclass->parent)->disp;
 281        const struct nv50_disp_user *user = oclass->priv;
 282        return user->ctor(oclass, argv, argc, disp, pobject);
 283}
 284
 285static int
 286nv50_disp_root_child_get_(struct nvkm_object *object, int index,
 287                          struct nvkm_oclass *sclass)
 288{
 289        struct nv50_disp_root *root = nv50_disp_root(object);
 290
 291        if (root->func->user[index].ctor) {
 292                sclass->base = root->func->user[index].base;
 293                sclass->priv = root->func->user + index;
 294                sclass->ctor = nv50_disp_root_child_new_;
 295                return 0;
 296        }
 297
 298        return -EINVAL;
 299}
 300
 301static void *
 302nv50_disp_root_dtor_(struct nvkm_object *object)
 303{
 304        struct nv50_disp_root *root = nv50_disp_root(object);
 305        return root;
 306}
 307
 308static const struct nvkm_object_func
 309nv50_disp_root_ = {
 310        .dtor = nv50_disp_root_dtor_,
 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
 324        if (!(root = kzalloc(sizeof(*root), GFP_KERNEL)))
 325                return -ENOMEM;
 326        *pobject = &root->object;
 327
 328        nvkm_object_ctor(&nv50_disp_root_, oclass, &root->object);
 329        root->func = func;
 330        root->disp = disp;
 331        return 0;
 332}
 333
 334static const struct nv50_disp_root_func
 335nv50_disp_root = {
 336        .user = {
 337                {{0,0,NV50_DISP_CURSOR             }, nv50_disp_curs_new },
 338                {{0,0,NV50_DISP_OVERLAY            }, nv50_disp_oimm_new },
 339                {{0,0,NV50_DISP_BASE_CHANNEL_DMA   }, nv50_disp_base_new },
 340                {{0,0,NV50_DISP_CORE_CHANNEL_DMA   }, nv50_disp_core_new },
 341                {{0,0,NV50_DISP_OVERLAY_CHANNEL_DMA}, nv50_disp_ovly_new },
 342                {}
 343        },
 344};
 345
 346static int
 347nv50_disp_root_new(struct nvkm_disp *disp, const struct nvkm_oclass *oclass,
 348                   void *data, u32 size, struct nvkm_object **pobject)
 349{
 350        return nv50_disp_root_new_(&nv50_disp_root, disp, oclass,
 351                                   data, size, pobject);
 352}
 353
 354const struct nvkm_disp_oclass
 355nv50_disp_root_oclass = {
 356        .base.oclass = NV50_DISP,
 357        .base.minver = -1,
 358        .base.maxver = -1,
 359        .ctor = nv50_disp_root_new,
 360};
 361