Commit bf0eb898 authored by Ben Skeggs's avatar Ben Skeggs

drm/nv50-/disp: audit and version DAC_PWR method

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: default avatarBen Skeggs <bskeggs@redhat.com>
parent 2c04ae01
......@@ -22,8 +22,10 @@
* 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>
......@@ -32,13 +34,28 @@
#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) {
......
......@@ -23,10 +23,13 @@
*/
#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 },
......
......@@ -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
......
......@@ -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 },
......
......@@ -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 },
......
......@@ -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 },
......
......@@ -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 },
......
......@@ -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 ?
......
......@@ -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;
......
......@@ -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
......
......@@ -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;
}
......
......@@ -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
......
......@@ -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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment