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: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
parent
2c04ae01df
commit
bf0eb89859
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue