drm/nv50-/disp: audit and version DAC_PWR method
authorBen Skeggs <bskeggs@redhat.com>
Sat, 9 Aug 2014 18:10:26 +0000 (04:10 +1000)
committerBen Skeggs <bskeggs@redhat.com>
Sat, 9 Aug 2014 19:28:07 +0000 (05:28 +1000)
The full object interfaces are about to be exposed to userspace, so we
need to check for any security-related issues and version the structs
to make it easier to handle any changes we may need in the future.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
13 files changed:
drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c
drivers/gpu/drm/nouveau/core/engine/disp/nv50.c
drivers/gpu/drm/nouveau/core/engine/disp/nv50.h
drivers/gpu/drm/nouveau/core/engine/disp/nv84.c
drivers/gpu/drm/nouveau/core/engine/disp/nv94.c
drivers/gpu/drm/nouveau/core/engine/disp/nva3.c
drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c
drivers/gpu/drm/nouveau/core/engine/disp/outp.c
drivers/gpu/drm/nouveau/core/engine/disp/outp.h
drivers/gpu/drm/nouveau/core/include/core/class.h
drivers/gpu/drm/nouveau/nouveau_bios.c
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/nouveau/nvif/class.h

index a66b27c..9fb1a84 100644 (file)
  * Authors: Ben Skeggs
  */
 
-#include <core/os.h>
+#include <core/client.h>
 #include <core/class.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
 
 #include <subdev/bios.h>
 #include <subdev/bios/dcb.h>
 #include "nv50.h"
 
 int
-nv50_dac_power(struct nv50_disp_priv *priv, int or, u32 data)
+nv50_dac_power(NV50_DISP_MTHD_V1)
 {
-       const u32 stat = (data & NV50_DISP_DAC_PWR_HSYNC) |
-                        (data & NV50_DISP_DAC_PWR_VSYNC) |
-                        (data & NV50_DISP_DAC_PWR_DATA) |
-                        (data & NV50_DISP_DAC_PWR_STATE);
-       const u32 doff = (or * 0x800);
+       const u32 doff = outp->or * 0x800;
+       union {
+               struct nv50_disp_dac_pwr_v0 v0;
+       } *args = data;
+       u32 stat;
+       int ret;
+
+       nv_ioctl(object, "disp dac pwr size %d\n", size);
+       if (nvif_unpack(args->v0, 0, 0, false)) {
+               nv_ioctl(object, "disp dac pwr vers %d state %d data %d "
+                                "vsync %d hsync %d\n",
+                        args->v0.version, args->v0.state, args->v0.data,
+                        args->v0.vsync, args->v0.hsync);
+               stat  = 0x00000040 * !args->v0.state;
+               stat |= 0x00000010 * !args->v0.data;
+               stat |= 0x00000004 * !args->v0.vsync;
+               stat |= 0x00000001 * !args->v0.hsync;
+       } else
+               return ret;
+
        nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000);
        nv_mask(priv, 0x61a004 + doff, 0xc000007f, 0x80000000 | stat);
        nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000);
@@ -80,9 +97,6 @@ nv50_dac_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size)
                return -EINVAL;
 
        switch (mthd & ~0x3f) {
-       case NV50_DISP_DAC_PWR:
-               ret = priv->dac.power(priv, or, data[0]);
-               break;
        case NV50_DISP_DAC_LOAD:
                ret = priv->dac.sense(priv, or, data[0]);
                if (ret >= 0) {
index 4f1ed89..00d1e34 100644 (file)
  */
 
 #include <core/object.h>
+#include <core/client.h>
 #include <core/parent.h>
 #include <core/handle.h>
-#include <core/class.h>
 #include <core/enum.h>
+#include <core/class.h>
+#include <nvif/unpack.h>
+#include <nvif/class.h>
 
 #include <subdev/bios.h>
 #include <subdev/bios/dcb.h>
@@ -839,6 +842,72 @@ nv50_disp_base_scanoutpos(struct nouveau_object *object, u32 mthd,
        return 0;
 }
 
+int
+nv50_disp_base_mthd(struct nouveau_object *object, u32 mthd,
+                   void *data, u32 size)
+{
+       union {
+               struct nv50_disp_mthd_v0 v0;
+               struct nv50_disp_mthd_v1 v1;
+       } *args = data;
+       struct nv50_disp_priv *priv = (void *)object->engine;
+       struct nvkm_output *outp = NULL;
+       struct nvkm_output *temp;
+       u16 type, mask = 0;
+       int head, ret;
+
+       if (mthd != NV50_DISP_MTHD)
+               return -EINVAL;
+
+       nv_ioctl(object, "disp mthd size %d\n", size);
+       if (nvif_unpack(args->v0, 0, 0, true)) {
+               nv_ioctl(object, "disp mthd vers %d mthd %02x head %d\n",
+                        args->v0.version, args->v0.method, args->v0.head);
+               mthd = args->v0.method;
+               head = args->v0.head;
+       } else
+       if (nvif_unpack(args->v1, 1, 1, true)) {
+               nv_ioctl(object, "disp mthd vers %d mthd %02x "
+                                "type %04x mask %04x\n",
+                        args->v1.version, args->v1.method,
+                        args->v1.hasht, args->v1.hashm);
+               mthd = args->v1.method;
+               type = args->v1.hasht;
+               mask = args->v1.hashm;
+               head = ffs((mask >> 8) & 0x0f) - 1;
+       } else
+               return ret;
+
+       if (head < 0 || head >= priv->head.nr)
+               return -ENXIO;
+
+       if (mask) {
+               list_for_each_entry(temp, &priv->base.outp, head) {
+                       if ((temp->info.hasht         == type) &&
+                           (temp->info.hashm & mask) == mask) {
+                               outp = temp;
+                               break;
+                       }
+               }
+               if (outp == NULL)
+                       return -ENXIO;
+       }
+
+       switch (mthd) {
+       default:
+               break;
+       }
+
+       switch (mthd * !!outp) {
+       case NV50_DISP_MTHD_V1_DAC_PWR:
+               return priv->dac.power(object, priv, data, size, head, outp);
+       default:
+               break;
+       }
+
+       return -EINVAL;
+}
+
 int
 nv50_disp_base_ctor(struct nouveau_object *parent,
                    struct nouveau_object *engine,
@@ -954,6 +1023,7 @@ nv50_disp_base_ofuncs = {
        .dtor = nv50_disp_base_dtor,
        .init = nv50_disp_base_init,
        .fini = nv50_disp_base_fini,
+       .mthd = nv50_disp_base_mthd,
 };
 
 static struct nouveau_omthds
@@ -961,7 +1031,6 @@ nv50_disp_base_omthds[] = {
        { HEAD_MTHD(NV50_DISP_SCANOUTPOS)     , nv50_disp_base_scanoutpos },
        { SOR_MTHD(NV50_DISP_SOR_PWR)         , nv50_sor_mthd },
        { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
-       { DAC_MTHD(NV50_DISP_DAC_PWR)         , nv50_dac_mthd },
        { DAC_MTHD(NV50_DISP_DAC_LOAD)        , nv50_dac_mthd },
        { PIOR_MTHD(NV50_DISP_PIOR_PWR)       , nv50_pior_mthd },
        { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR)  , nv50_pior_mthd },
index a1ec6a5..4a874d7 100644 (file)
@@ -24,6 +24,11 @@ struct nv50_disp_impl {
        } mthd;
 };
 
+#define NV50_DISP_MTHD_ struct nouveau_object *object,                         \
+       struct nv50_disp_priv *priv, void *data, u32 size
+#define NV50_DISP_MTHD_V0 NV50_DISP_MTHD_, int head
+#define NV50_DISP_MTHD_V1 NV50_DISP_MTHD_, int head, struct nvkm_output *outp
+
 struct nv50_disp_priv {
        struct nouveau_disp base;
        struct nouveau_oclass *sclass;
@@ -36,7 +41,7 @@ struct nv50_disp_priv {
        } head;
        struct {
                int nr;
-               int (*power)(struct nv50_disp_priv *, int dac, u32 data);
+               int (*power)(NV50_DISP_MTHD_V1);
                int (*sense)(struct nv50_disp_priv *, int dac, u32 load);
        } dac;
        struct {
@@ -56,11 +61,12 @@ struct nv50_disp_priv {
 #define HEAD_MTHD(n) (n), (n) + 0x03
 
 int nv50_disp_base_scanoutpos(struct nouveau_object *, u32, void *, u32);
+int nv50_disp_base_mthd(struct nouveau_object *, u32, void *, u32);
 
 #define DAC_MTHD(n) (n), (n) + 0x03
 
 int nv50_dac_mthd(struct nouveau_object *, u32, void *, u32);
-int nv50_dac_power(struct nv50_disp_priv *, int, u32);
+int nv50_dac_power(NV50_DISP_MTHD_V1);
 int nv50_dac_sense(struct nv50_disp_priv *, int, u32);
 
 #define SOR_MTHD(n) (n), (n) + 0x3f
index 43ef1fa..f2414a4 100644 (file)
@@ -218,7 +218,6 @@ nv84_disp_base_omthds[] = {
        { SOR_MTHD(NV50_DISP_SOR_PWR)         , nv50_sor_mthd },
        { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR)    , nv50_sor_mthd },
        { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
-       { DAC_MTHD(NV50_DISP_DAC_PWR)         , nv50_dac_mthd },
        { DAC_MTHD(NV50_DISP_DAC_LOAD)        , nv50_dac_mthd },
        { PIOR_MTHD(NV50_DISP_PIOR_PWR)       , nv50_pior_mthd },
        { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR)  , nv50_pior_mthd },
index 2e38a79..7ae79cf 100644 (file)
@@ -78,7 +78,6 @@ nv94_disp_base_omthds[] = {
        { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR)    , nv50_sor_mthd },
        { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
        { SOR_MTHD(NV94_DISP_SOR_DP_PWR)      , nv50_sor_mthd },
-       { DAC_MTHD(NV50_DISP_DAC_PWR)         , nv50_dac_mthd },
        { DAC_MTHD(NV50_DISP_DAC_LOAD)        , nv50_dac_mthd },
        { PIOR_MTHD(NV50_DISP_PIOR_PWR)       , nv50_pior_mthd },
        { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR)  , nv50_pior_mthd },
index 4b601c8..31cdf7a 100644 (file)
@@ -51,7 +51,6 @@ nva3_disp_base_omthds[] = {
        { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR)    , nv50_sor_mthd },
        { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
        { SOR_MTHD(NV94_DISP_SOR_DP_PWR)      , nv50_sor_mthd },
-       { DAC_MTHD(NV50_DISP_DAC_PWR)         , nv50_dac_mthd },
        { DAC_MTHD(NV50_DISP_DAC_LOAD)        , nv50_dac_mthd },
        { PIOR_MTHD(NV50_DISP_PIOR_PWR)       , nv50_pior_mthd },
        { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR)  , nv50_pior_mthd },
index 57d6cb3..66959cd 100644 (file)
@@ -706,6 +706,7 @@ nvd0_disp_base_ofuncs = {
        .dtor = nv50_disp_base_dtor,
        .init = nvd0_disp_base_init,
        .fini = nvd0_disp_base_fini,
+       .mthd = nv50_disp_base_mthd,
 };
 
 struct nouveau_omthds
@@ -716,7 +717,6 @@ nvd0_disp_base_omthds[] = {
        { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR)    , nv50_sor_mthd },
        { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd },
        { SOR_MTHD(NV94_DISP_SOR_DP_PWR)      , nv50_sor_mthd },
-       { DAC_MTHD(NV50_DISP_DAC_PWR)         , nv50_dac_mthd },
        { DAC_MTHD(NV50_DISP_DAC_LOAD)        , nv50_dac_mthd },
        { PIOR_MTHD(NV50_DISP_PIOR_PWR)       , nv50_pior_mthd },
        { PIOR_MTHD(NV50_DISP_PIOR_TMDS_PWR)  , nv50_pior_mthd },
index ad9ba7c..a5ff00a 100644 (file)
@@ -78,6 +78,7 @@ nvkm_output_create_(struct nouveau_object *parent,
 
        outp->info = *dcbE;
        outp->index = index;
+       outp->or = ffs(outp->info.or) - 1;
 
        DBG("type %02x loc %d or %d link %d con %x edid %x bus %d head %x\n",
            dcbE->type, dcbE->location, dcbE->or, dcbE->type >= 2 ?
index bc76fbf..187f435 100644 (file)
@@ -9,6 +9,7 @@ struct nvkm_output {
 
        struct dcb_output info;
        int index;
+       int or;
 
        struct nouveau_i2c_port *port;
        struct nouveau_i2c_port *edid;
index d3180e5..026b123 100644 (file)
@@ -49,7 +49,6 @@ struct nv04_display_scanoutpos {
 #define NVF0_DISP_CLASS                                              0x00009270
 #define GM107_DISP_CLASS                                             0x00009470
 
-#define NV50_DISP_MTHD                                               0x00000000
 #define NV50_DISP_MTHD_HEAD                                          0x00000003
 
 #define NV50_DISP_SCANOUTPOS                                         0x00000000
@@ -82,19 +81,6 @@ struct nv04_display_scanoutpos {
 #define NV50_DISP_DAC_MTHD_TYPE                                      0x0000f000
 #define NV50_DISP_DAC_MTHD_OR                                        0x00000003
 
-#define NV50_DISP_DAC_PWR                                            0x00020000
-#define NV50_DISP_DAC_PWR_HSYNC                                      0x00000001
-#define NV50_DISP_DAC_PWR_HSYNC_ON                                   0x00000000
-#define NV50_DISP_DAC_PWR_HSYNC_LO                                   0x00000001
-#define NV50_DISP_DAC_PWR_VSYNC                                      0x00000004
-#define NV50_DISP_DAC_PWR_VSYNC_ON                                   0x00000000
-#define NV50_DISP_DAC_PWR_VSYNC_LO                                   0x00000004
-#define NV50_DISP_DAC_PWR_DATA                                       0x00000010
-#define NV50_DISP_DAC_PWR_DATA_ON                                    0x00000000
-#define NV50_DISP_DAC_PWR_DATA_LO                                    0x00000010
-#define NV50_DISP_DAC_PWR_STATE                                      0x00000040
-#define NV50_DISP_DAC_PWR_STATE_ON                                   0x00000000
-#define NV50_DISP_DAC_PWR_STATE_OFF                                  0x00000040
 #define NV50_DISP_DAC_LOAD                                           0x00020100
 #define NV50_DISP_DAC_LOAD_VALUE                                     0x00000007
 
index ffdf85f..dae2c96 100644 (file)
@@ -1397,6 +1397,7 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
                  uint32_t conn, uint32_t conf, struct dcb_output *entry)
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
+       int link = 0;
 
        entry->type = conn & 0xf;
        entry->i2c_index = (conn >> 4) & 0xf;
@@ -1442,6 +1443,7 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
                        if (conf & 0x4)
                                entry->lvdsconf.use_power_scripts = true;
                        entry->lvdsconf.sor.link = (conf & 0x00000030) >> 4;
+                       link = entry->lvdsconf.sor.link;
                }
                if (conf & mask) {
                        /*
@@ -1490,17 +1492,18 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
                        entry->dpconf.link_nr = 1;
                        break;
                }
+               link = entry->dpconf.sor.link;
                break;
        case DCB_OUTPUT_TMDS:
                if (dcb->version >= 0x40) {
                        entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4;
                        entry->extdev = (conf & 0x0000ff00) >> 8;
+                       link = entry->tmdsconf.sor.link;
                }
                else if (dcb->version >= 0x30)
                        entry->tmdsconf.slave_addr = (conf & 0x00000700) >> 8;
                else if (dcb->version >= 0x22)
                        entry->tmdsconf.slave_addr = (conf & 0x00000070) >> 4;
-
                break;
        case DCB_OUTPUT_EOL:
                /* weird g80 mobile type that "nv" treats as a terminator */
@@ -1524,6 +1527,8 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
        if (conf & 0x100000)
                entry->i2c_upper_default = true;
 
+       entry->hasht = (entry->location << 4) | entry->type;
+       entry->hashm = (entry->heads << 8) | (link << 6) | entry->or;
        return true;
 }
 
index 2d9a02c..d3e6bd3 100644 (file)
@@ -1466,16 +1466,24 @@ nv50_dac_dpms(struct drm_encoder *encoder, int mode)
 {
        struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
        struct nv50_disp *disp = nv50_disp(encoder->dev);
-       int or = nv_encoder->or;
-       u32 dpms_ctrl;
+       struct {
+               struct nv50_disp_mthd_v1 base;
+               struct nv50_disp_dac_pwr_v0 pwr;
+       } args = {
+               .base.version = 1,
+               .base.method = NV50_DISP_MTHD_V1_DAC_PWR,
+               .base.hasht  = nv_encoder->dcb->hasht,
+               .base.hashm  = nv_encoder->dcb->hashm,
+               .pwr.state = 1,
+               .pwr.data  = 1,
+               .pwr.vsync = (mode != DRM_MODE_DPMS_SUSPEND &&
+                             mode != DRM_MODE_DPMS_OFF),
+               .pwr.hsync = (mode != DRM_MODE_DPMS_STANDBY &&
+                             mode != DRM_MODE_DPMS_OFF),
+       };
 
-       dpms_ctrl = 0x00000000;
-       if (mode == DRM_MODE_DPMS_STANDBY || mode == DRM_MODE_DPMS_OFF)
-               dpms_ctrl |= 0x00000001;
-       if (mode == DRM_MODE_DPMS_SUSPEND || mode == DRM_MODE_DPMS_OFF)
-               dpms_ctrl |= 0x00000004;
 
-       nvif_exec(disp->disp, NV50_DISP_DAC_PWR + or, &dpms_ctrl, sizeof(dpms_ctrl));
+       nvif_mthd(disp->disp, 0, &args, sizeof(args));
 }
 
 static bool
index 50c5413..9681a10 100644 (file)
@@ -290,4 +290,47 @@ struct kepler_channel_gpfifo_a_v0 {
        __u64 ioffset;
 };
 
+/*******************************************************************************
+ * legacy display
+ ******************************************************************************/
+
+
+/*******************************************************************************
+ * display
+ ******************************************************************************/
+
+#define NV50_DISP_MTHD                                                     0x00
+
+struct nv50_disp_mthd_v0 {
+       __u8  version;
+       __u8  method;
+       __u8  head;
+       __u8  pad03[5];
+};
+
+struct nv50_disp_mthd_v1 {
+       __u8  version;
+#define NV50_DISP_MTHD_V1_DAC_PWR                                          0x10
+#define NV50_DISP_MTHD_V1_DAC_LOAD                                         0x11
+#define NV50_DISP_MTHD_V1_SOR_PWR                                          0x20
+#define NV50_DISP_MTHD_V1_SOR_HDA_ELD                                      0x21
+#define NV50_DISP_MTHD_V1_SOR_HDMI_PWR                                     0x22
+#define NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT                                  0x23
+#define NV50_DISP_MTHD_V1_SOR_DP_PWR                                       0x24
+#define NV50_DISP_MTHD_V1_PIOR_PWR                                         0x30
+       __u8  method;
+       __u16 hasht;
+       __u16 hashm;
+       __u8  pad06[2];
+};
+
+struct nv50_disp_dac_pwr_v0 {
+       __u8  version;
+       __u8  state;
+       __u8  data;
+       __u8  vsync;
+       __u8  hsync;
+       __u8  pad05[3];
+};
+
 #endif