mirror of https://gitee.com/openkylin/linux.git
Merge branch 'linux-4.13' of git://github.com/skeggsb/linux into drm-next
- HDMI stereoscopic support - Rework of display code to properly support SOR pad macro routing on >=GM20x GPUs - Various other fixes/improvements. * 'linux-4.13' of git://github.com/skeggsb/linux: (73 commits) drm/nouveau/disp/nv50-: avoid creating ORs that aren't present on HW drm/nouveau: use proper prototype in nouveau_pmops_runtime() definition drm/nouveau: Skip vga_fini on non-PCI device drm/nouveau/tegra: Don't leave GPU in reset drm/nouveau/tegra: Skip manual unpowergating when not necessary drm/nouveau/hwmon: Change permissions to numeric drm/nouveau/hwmon: expose the auto_point and pwm_min/max attrs drm/nouveau/hwmon: Remove old code, add .write/.read operations drm/nouveau/hwmon: Add nouveau_hwmon_ops structure with .is_visible/.read_string drm/nouveau/hwmon: Add config for all sensors and their settings drm/nouveau/disp/gm200-: allow non-identity mapping of SOR <-> macro links drm/nouveau/disp/nv50-: implement a common supervisor 3.0 drm/nouveau/disp/nv50-: implement a common supervisor 2.2 drm/nouveau/disp/nv50-: implement a common supervisor 2.1 drm/nouveau/disp/nv50-: implement a common supervisor 2.0 drm/nouveau/disp/nv50-: implement a common supervisor 1.0 drm/nouveau/disp/nv50-gt21x: remove workaround for dp->tmds hotplug issues drm/nouveau/disp/dp: use new devinit script interpreter entry-point drm/nouveau/disp/dp: determine link bandwidth requirements from head state drm/nouveau/disp: introduce acquire/release display path methods ...
This commit is contained in:
commit
d02b0ffb00
|
@ -169,18 +169,10 @@ static inline void
|
|||
nouveau_bios_run_init_table(struct drm_device *dev, u16 table,
|
||||
struct dcb_output *outp, int crtc)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nvkm_bios *bios = nvxx_bios(&drm->client.device);
|
||||
struct nvbios_init init = {
|
||||
.subdev = &bios->subdev,
|
||||
.bios = bios,
|
||||
.offset = table,
|
||||
.outp = outp,
|
||||
.crtc = crtc,
|
||||
.execute = 1,
|
||||
};
|
||||
|
||||
nvbios_exec(&init);
|
||||
nvbios_init(&nvxx_bios(&nouveau_drm(dev)->client.device)->subdev, table,
|
||||
init.outp = outp;
|
||||
init.head = crtc;
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -27,29 +27,25 @@ struct nv50_disp_scanoutpos_v0 {
|
|||
|
||||
struct nv50_disp_mthd_v1 {
|
||||
__u8 version;
|
||||
#define NV50_DISP_MTHD_V1_DAC_PWR 0x10
|
||||
#define NV50_DISP_MTHD_V1_ACQUIRE 0x01
|
||||
#define NV50_DISP_MTHD_V1_RELEASE 0x02
|
||||
#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_SOR_DP_MST_LINK 0x25
|
||||
#define NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI 0x26
|
||||
#define NV50_DISP_MTHD_V1_PIOR_PWR 0x30
|
||||
__u8 method;
|
||||
__u16 hasht;
|
||||
__u16 hashm;
|
||||
__u8 pad06[2];
|
||||
};
|
||||
|
||||
struct nv50_disp_dac_pwr_v0 {
|
||||
struct nv50_disp_acquire_v0 {
|
||||
__u8 version;
|
||||
__u8 state;
|
||||
__u8 data;
|
||||
__u8 vsync;
|
||||
__u8 hsync;
|
||||
__u8 pad05[3];
|
||||
__u8 or;
|
||||
__u8 link;
|
||||
__u8 pad03[5];
|
||||
};
|
||||
|
||||
struct nv50_disp_dac_load_v0 {
|
||||
|
@ -59,12 +55,6 @@ struct nv50_disp_dac_load_v0 {
|
|||
__u32 data;
|
||||
};
|
||||
|
||||
struct nv50_disp_sor_pwr_v0 {
|
||||
__u8 version;
|
||||
__u8 state;
|
||||
__u8 pad02[6];
|
||||
};
|
||||
|
||||
struct nv50_disp_sor_hda_eld_v0 {
|
||||
__u8 version;
|
||||
__u8 pad01[7];
|
||||
|
@ -76,7 +66,9 @@ struct nv50_disp_sor_hdmi_pwr_v0 {
|
|||
__u8 state;
|
||||
__u8 max_ac_packet;
|
||||
__u8 rekey;
|
||||
__u8 pad04[4];
|
||||
__u8 avi_infoframe_length;
|
||||
__u8 vendor_infoframe_length;
|
||||
__u8 pad06[2];
|
||||
};
|
||||
|
||||
struct nv50_disp_sor_lvds_script_v0 {
|
||||
|
@ -86,12 +78,6 @@ struct nv50_disp_sor_lvds_script_v0 {
|
|||
__u8 pad04[4];
|
||||
};
|
||||
|
||||
struct nv50_disp_sor_dp_pwr_v0 {
|
||||
__u8 version;
|
||||
__u8 state;
|
||||
__u8 pad02[6];
|
||||
};
|
||||
|
||||
struct nv50_disp_sor_dp_mst_link_v0 {
|
||||
__u8 version;
|
||||
__u8 state;
|
||||
|
@ -106,11 +92,4 @@ struct nv50_disp_sor_dp_mst_vcpi_v0 {
|
|||
__u16 pbn;
|
||||
__u16 aligned_pbn;
|
||||
};
|
||||
|
||||
struct nv50_disp_pior_pwr_v0 {
|
||||
__u8 version;
|
||||
__u8 state;
|
||||
__u8 type;
|
||||
__u8 pad03[5];
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -8,17 +8,15 @@ struct nvkm_disp {
|
|||
const struct nvkm_disp_func *func;
|
||||
struct nvkm_engine engine;
|
||||
|
||||
struct nvkm_oproxy *client;
|
||||
|
||||
struct list_head head;
|
||||
struct list_head ior;
|
||||
struct list_head outp;
|
||||
struct list_head conn;
|
||||
|
||||
struct nvkm_event hpd;
|
||||
struct nvkm_event vblank;
|
||||
|
||||
struct {
|
||||
int nr;
|
||||
} head;
|
||||
struct nvkm_oproxy *client;
|
||||
};
|
||||
|
||||
int nv04_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
|
@ -26,7 +24,9 @@ int nv50_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
|||
int g84_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
int gt200_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
int g94_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
int mcp77_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
int gt215_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
int mcp89_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
int gf119_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
int gk104_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
int gk110_disp_new(struct nvkm_device *, int, struct nvkm_disp **);
|
||||
|
|
|
@ -3,19 +3,34 @@
|
|||
|
||||
struct nvbios_init {
|
||||
struct nvkm_subdev *subdev;
|
||||
struct nvkm_bios *bios;
|
||||
u16 offset;
|
||||
u32 offset;
|
||||
|
||||
struct dcb_output *outp;
|
||||
int crtc;
|
||||
int or;
|
||||
int link;
|
||||
int head;
|
||||
|
||||
/* internal state used during parsing */
|
||||
u8 execute;
|
||||
u32 nested;
|
||||
u16 repeat;
|
||||
u16 repend;
|
||||
u32 repeat;
|
||||
u32 repend;
|
||||
u32 ramcfg;
|
||||
};
|
||||
|
||||
#define nvbios_init(s,o,ARGS...) ({ \
|
||||
struct nvbios_init init = { \
|
||||
.subdev = (s), \
|
||||
.offset = (o), \
|
||||
.or = -1, \
|
||||
.link = 0, \
|
||||
.head = -1, \
|
||||
.execute = 1, \
|
||||
}; \
|
||||
ARGS \
|
||||
nvbios_exec(&init); \
|
||||
})
|
||||
int nvbios_exec(struct nvbios_init *);
|
||||
int nvbios_init(struct nvkm_subdev *, bool execute);
|
||||
|
||||
int nvbios_post(struct nvkm_subdev *, bool execute);
|
||||
#endif
|
||||
|
|
|
@ -26,7 +26,6 @@ struct nvkm_timer {
|
|||
|
||||
u64 nvkm_timer_read(struct nvkm_timer *);
|
||||
void nvkm_timer_alarm(struct nvkm_timer *, u32 nsec, struct nvkm_alarm *);
|
||||
void nvkm_timer_alarm_cancel(struct nvkm_timer *, struct nvkm_alarm *);
|
||||
|
||||
/* Delay based on GPU time (ie. PTIMER).
|
||||
*
|
||||
|
|
|
@ -1533,7 +1533,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->hasht = (entry->extdev << 8) | (entry->location << 4) |
|
||||
entry->type;
|
||||
entry->hashm = (entry->heads << 8) | (link << 6) | entry->or;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1045,6 +1045,9 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
|
|||
return MODE_BAD;
|
||||
}
|
||||
|
||||
if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
|
||||
clock *= 2;
|
||||
|
||||
if (clock < min_clock)
|
||||
return MODE_CLOCK_LOW;
|
||||
|
||||
|
@ -1321,6 +1324,13 @@ nouveau_connector_create(struct drm_device *dev, int index)
|
|||
break;
|
||||
}
|
||||
|
||||
/* HDMI 3D support */
|
||||
if ((disp->disp.oclass >= G82_DISP)
|
||||
&& ((type == DRM_MODE_CONNECTOR_DisplayPort)
|
||||
|| (type == DRM_MODE_CONNECTOR_eDP)
|
||||
|| (type == DRM_MODE_CONNECTOR_HDMIA)))
|
||||
connector->stereo_allowed = true;
|
||||
|
||||
/* defaults, will get overridden in detect() */
|
||||
connector->interlace_allowed = false;
|
||||
connector->doublescan_allowed = false;
|
||||
|
|
|
@ -727,7 +727,7 @@ nouveau_pmops_thaw(struct device *dev)
|
|||
}
|
||||
|
||||
bool
|
||||
nouveau_pmops_runtime()
|
||||
nouveau_pmops_runtime(void)
|
||||
{
|
||||
if (nouveau_runtime_pm == -1)
|
||||
return nouveau_is_optimus() || nouveau_is_v1_dsm();
|
||||
|
|
|
@ -42,6 +42,7 @@ struct nouveau_encoder {
|
|||
|
||||
struct dcb_output *dcb;
|
||||
int or;
|
||||
int link;
|
||||
|
||||
struct i2c_adapter *i2c;
|
||||
struct nvkm_i2c_aux *aux;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -111,6 +111,10 @@ nouveau_vga_fini(struct nouveau_drm *drm)
|
|||
struct drm_device *dev = drm->dev;
|
||||
bool runtime = nouveau_pmops_runtime();
|
||||
|
||||
/* only relevant for PCI devices */
|
||||
if (!dev->pdev)
|
||||
return;
|
||||
|
||||
vga_client_register(dev->pdev, NULL, NULL, NULL);
|
||||
|
||||
if (pci_is_thunderbolt_attached(dev->pdev))
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/hdmi.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
|
@ -31,6 +32,7 @@
|
|||
#include <drm/drm_dp_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_plane_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
#include <nvif/class.h>
|
||||
#include <nvif/cl0002.h>
|
||||
|
@ -1965,6 +1967,7 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh,
|
|||
struct drm_display_mode *umode = &asyh->state.mode;
|
||||
int mode = asyc->scaler.mode;
|
||||
struct edid *edid;
|
||||
int umode_vdisplay, omode_hdisplay, omode_vdisplay;
|
||||
|
||||
if (connector->edid_blob_ptr)
|
||||
edid = (struct edid *)connector->edid_blob_ptr->data;
|
||||
|
@ -1979,12 +1982,18 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh,
|
|||
mode = DRM_MODE_SCALE_FULLSCREEN;
|
||||
}
|
||||
|
||||
/* For the user-specified mode, we must ignore doublescan and
|
||||
* the like, but honor frame packing.
|
||||
*/
|
||||
umode_vdisplay = umode->vdisplay;
|
||||
if ((umode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING)
|
||||
umode_vdisplay += umode->vtotal;
|
||||
asyh->view.iW = umode->hdisplay;
|
||||
asyh->view.iH = umode->vdisplay;
|
||||
asyh->view.oW = omode->hdisplay;
|
||||
asyh->view.oH = omode->vdisplay;
|
||||
if (omode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
asyh->view.oH *= 2;
|
||||
asyh->view.iH = umode_vdisplay;
|
||||
/* For the output mode, we can just use the stock helper. */
|
||||
drm_mode_get_hv_timing(omode, &omode_hdisplay, &omode_vdisplay);
|
||||
asyh->view.oW = omode_hdisplay;
|
||||
asyh->view.oH = omode_vdisplay;
|
||||
|
||||
/* Add overscan compensation if necessary, will keep the aspect
|
||||
* ratio the same as the backend mode unless overridden by the
|
||||
|
@ -2014,7 +2023,7 @@ nv50_head_atomic_check_view(struct nv50_head_atom *armh,
|
|||
switch (mode) {
|
||||
case DRM_MODE_SCALE_CENTER:
|
||||
asyh->view.oW = min((u16)umode->hdisplay, asyh->view.oW);
|
||||
asyh->view.oH = min((u16)umode->vdisplay, asyh->view.oH);
|
||||
asyh->view.oH = min((u16)umode_vdisplay, asyh->view.oH);
|
||||
/* fall-through */
|
||||
case DRM_MODE_SCALE_ASPECT:
|
||||
if (asyh->view.oH < asyh->view.oW) {
|
||||
|
@ -2036,34 +2045,37 @@ static void
|
|||
nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
|
||||
{
|
||||
struct drm_display_mode *mode = &asyh->state.adjusted_mode;
|
||||
u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1;
|
||||
u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1;
|
||||
u32 hbackp = mode->htotal - mode->hsync_end;
|
||||
u32 vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace;
|
||||
u32 hfrontp = mode->hsync_start - mode->hdisplay;
|
||||
u32 vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace;
|
||||
u32 blankus;
|
||||
struct nv50_head_mode *m = &asyh->mode;
|
||||
u32 blankus;
|
||||
|
||||
m->h.active = mode->htotal;
|
||||
m->h.synce = mode->hsync_end - mode->hsync_start - 1;
|
||||
m->h.blanke = m->h.synce + hbackp;
|
||||
m->h.blanks = mode->htotal - hfrontp - 1;
|
||||
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V | CRTC_STEREO_DOUBLE);
|
||||
|
||||
m->v.active = mode->vtotal * vscan / ilace;
|
||||
m->v.synce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1;
|
||||
m->v.blanke = m->v.synce + vbackp;
|
||||
m->v.blanks = m->v.active - vfrontp - 1;
|
||||
/*
|
||||
* DRM modes are defined in terms of a repeating interval
|
||||
* starting with the active display area. The hardware modes
|
||||
* are defined in terms of a repeating interval starting one
|
||||
* unit (pixel or line) into the sync pulse. So, add bias.
|
||||
*/
|
||||
|
||||
m->h.active = mode->crtc_htotal;
|
||||
m->h.synce = mode->crtc_hsync_end - mode->crtc_hsync_start - 1;
|
||||
m->h.blanke = mode->crtc_hblank_end - mode->crtc_hsync_start - 1;
|
||||
m->h.blanks = m->h.blanke + mode->crtc_hdisplay;
|
||||
|
||||
m->v.active = mode->crtc_vtotal;
|
||||
m->v.synce = mode->crtc_vsync_end - mode->crtc_vsync_start - 1;
|
||||
m->v.blanke = mode->crtc_vblank_end - mode->crtc_vsync_start - 1;
|
||||
m->v.blanks = m->v.blanke + mode->crtc_vdisplay;
|
||||
|
||||
/*XXX: Safe underestimate, even "0" works */
|
||||
blankus = (m->v.active - mode->vdisplay - 2) * m->h.active;
|
||||
blankus = (m->v.active - mode->crtc_vdisplay - 2) * m->h.active;
|
||||
blankus *= 1000;
|
||||
blankus /= mode->clock;
|
||||
blankus /= mode->crtc_clock;
|
||||
m->v.blankus = blankus;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
|
||||
m->v.blank2e = m->v.active + m->v.synce + vbackp;
|
||||
m->v.blank2s = m->v.blank2e + (mode->vdisplay * vscan / ilace);
|
||||
m->v.blank2e = m->v.active + m->v.blanke;
|
||||
m->v.blank2s = m->v.blank2e + mode->crtc_vdisplay;
|
||||
m->v.active = (m->v.active * 2) + 1;
|
||||
m->interlace = true;
|
||||
} else {
|
||||
|
@ -2071,9 +2083,8 @@ nv50_head_atomic_check_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
|
|||
m->v.blank2s = 1;
|
||||
m->interlace = false;
|
||||
}
|
||||
m->clock = mode->clock;
|
||||
m->clock = mode->crtc_clock;
|
||||
|
||||
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
|
||||
asyh->set.mode = true;
|
||||
}
|
||||
|
||||
|
@ -2392,6 +2403,51 @@ nv50_head_create(struct drm_device *dev, int index)
|
|||
/******************************************************************************
|
||||
* Output path helpers
|
||||
*****************************************************************************/
|
||||
static void
|
||||
nv50_outp_release(struct nouveau_encoder *nv_encoder)
|
||||
{
|
||||
struct nv50_disp *disp = nv50_disp(nv_encoder->base.base.dev);
|
||||
struct {
|
||||
struct nv50_disp_mthd_v1 base;
|
||||
} args = {
|
||||
.base.version = 1,
|
||||
.base.method = NV50_DISP_MTHD_V1_RELEASE,
|
||||
.base.hasht = nv_encoder->dcb->hasht,
|
||||
.base.hashm = nv_encoder->dcb->hashm,
|
||||
};
|
||||
|
||||
nvif_mthd(disp->disp, 0, &args, sizeof(args));
|
||||
nv_encoder->or = -1;
|
||||
nv_encoder->link = 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_outp_acquire(struct nouveau_encoder *nv_encoder)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev);
|
||||
struct nv50_disp *disp = nv50_disp(drm->dev);
|
||||
struct {
|
||||
struct nv50_disp_mthd_v1 base;
|
||||
struct nv50_disp_acquire_v0 info;
|
||||
} args = {
|
||||
.base.version = 1,
|
||||
.base.method = NV50_DISP_MTHD_V1_ACQUIRE,
|
||||
.base.hasht = nv_encoder->dcb->hasht,
|
||||
.base.hashm = nv_encoder->dcb->hashm,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = nvif_mthd(disp->disp, 0, &args, sizeof(args));
|
||||
if (ret) {
|
||||
NV_ERROR(drm, "error acquiring output path: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
nv_encoder->or = args.info.or;
|
||||
nv_encoder->link = args.info.link;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_outp_atomic_check_view(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
|
@ -2448,30 +2504,6 @@ nv50_outp_atomic_check(struct drm_encoder *encoder,
|
|||
/******************************************************************************
|
||||
* DAC
|
||||
*****************************************************************************/
|
||||
static void
|
||||
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);
|
||||
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),
|
||||
};
|
||||
|
||||
nvif_mthd(disp->disp, 0, &args, sizeof(args));
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_dac_disable(struct drm_encoder *encoder)
|
||||
{
|
||||
|
@ -2495,6 +2527,7 @@ nv50_dac_disable(struct drm_encoder *encoder)
|
|||
}
|
||||
|
||||
nv_encoder->crtc = NULL;
|
||||
nv50_outp_release(nv_encoder);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -2506,6 +2539,8 @@ nv50_dac_enable(struct drm_encoder *encoder)
|
|||
struct drm_display_mode *mode = &nv_crtc->base.state->adjusted_mode;
|
||||
u32 *push;
|
||||
|
||||
nv50_outp_acquire(nv_encoder);
|
||||
|
||||
push = evo_wait(mast, 8);
|
||||
if (push) {
|
||||
if (nv50_vers(mast) < GF110_DISP_CORE_CHANNEL_DMA) {
|
||||
|
@ -2573,7 +2608,6 @@ nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
|
|||
|
||||
static const struct drm_encoder_helper_funcs
|
||||
nv50_dac_help = {
|
||||
.dpms = nv50_dac_dpms,
|
||||
.atomic_check = nv50_outp_atomic_check,
|
||||
.enable = nv50_dac_enable,
|
||||
.disable = nv50_dac_disable,
|
||||
|
@ -2606,7 +2640,6 @@ nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe)
|
|||
if (!nv_encoder)
|
||||
return -ENOMEM;
|
||||
nv_encoder->dcb = dcbe;
|
||||
nv_encoder->or = ffs(dcbe->or) - 1;
|
||||
|
||||
bus = nvkm_i2c_bus_find(i2c, dcbe->i2c_index);
|
||||
if (bus)
|
||||
|
@ -2708,6 +2741,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
|||
struct {
|
||||
struct nv50_disp_mthd_v1 base;
|
||||
struct nv50_disp_sor_hdmi_pwr_v0 pwr;
|
||||
u8 infoframes[2 * 17]; /* two frames, up to 17 bytes each */
|
||||
} args = {
|
||||
.base.version = 1,
|
||||
.base.method = NV50_DISP_MTHD_V1_SOR_HDMI_PWR,
|
||||
|
@ -2719,17 +2753,42 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct drm_display_mode *mode)
|
|||
};
|
||||
struct nouveau_connector *nv_connector;
|
||||
u32 max_ac_packet;
|
||||
union hdmi_infoframe avi_frame;
|
||||
union hdmi_infoframe vendor_frame;
|
||||
int ret;
|
||||
int size;
|
||||
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
if (!drm_detect_hdmi_monitor(nv_connector->edid))
|
||||
return;
|
||||
|
||||
ret = drm_hdmi_avi_infoframe_from_display_mode(&avi_frame.avi, mode);
|
||||
if (!ret) {
|
||||
/* We have an AVI InfoFrame, populate it to the display */
|
||||
args.pwr.avi_infoframe_length
|
||||
= hdmi_infoframe_pack(&avi_frame, args.infoframes, 17);
|
||||
}
|
||||
|
||||
ret = drm_hdmi_vendor_infoframe_from_display_mode(&vendor_frame.vendor.hdmi, mode);
|
||||
if (!ret) {
|
||||
/* We have a Vendor InfoFrame, populate it to the display */
|
||||
args.pwr.vendor_infoframe_length
|
||||
= hdmi_infoframe_pack(&vendor_frame,
|
||||
args.infoframes
|
||||
+ args.pwr.avi_infoframe_length,
|
||||
17);
|
||||
}
|
||||
|
||||
max_ac_packet = mode->htotal - mode->hdisplay;
|
||||
max_ac_packet -= args.pwr.rekey;
|
||||
max_ac_packet -= 18; /* constant from tegra */
|
||||
args.pwr.max_ac_packet = max_ac_packet / 32;
|
||||
|
||||
nvif_mthd(disp->disp, 0, &args, sizeof(args));
|
||||
size = sizeof(args.base)
|
||||
+ sizeof(args.pwr)
|
||||
+ args.pwr.avi_infoframe_length
|
||||
+ args.pwr.vendor_infoframe_length;
|
||||
nvif_mthd(disp->disp, 0, &args, size);
|
||||
nv50_audio_enable(encoder, mode);
|
||||
}
|
||||
|
||||
|
@ -2747,6 +2806,8 @@ struct nv50_mstm {
|
|||
struct nv50_msto *msto[4];
|
||||
|
||||
bool modified;
|
||||
bool disabled;
|
||||
int links;
|
||||
};
|
||||
|
||||
struct nv50_mstc {
|
||||
|
@ -2895,7 +2956,10 @@ nv50_msto_enable(struct drm_encoder *encoder)
|
|||
r = drm_dp_mst_allocate_vcpi(&mstm->mgr, mstc->port, mstc->pbn, slots);
|
||||
WARN_ON(!r);
|
||||
|
||||
if (mstm->outp->dcb->sorconf.link & 1)
|
||||
if (!mstm->links++)
|
||||
nv50_outp_acquire(mstm->outp);
|
||||
|
||||
if (mstm->outp->link & 1)
|
||||
proto = 0x8;
|
||||
else
|
||||
proto = 0x9;
|
||||
|
@ -2927,6 +2991,8 @@ nv50_msto_disable(struct drm_encoder *encoder)
|
|||
|
||||
mstm->outp->update(mstm->outp, msto->head->base.index, NULL, 0, 0);
|
||||
mstm->modified = true;
|
||||
if (!--mstm->links)
|
||||
mstm->disabled = true;
|
||||
msto->disabled = true;
|
||||
}
|
||||
|
||||
|
@ -3142,6 +3208,12 @@ nv50_mstm_prepare(struct nv50_mstm *mstm)
|
|||
nv50_msto_prepare(msto);
|
||||
}
|
||||
}
|
||||
|
||||
if (mstm->disabled) {
|
||||
if (!mstm->links)
|
||||
nv50_outp_release(mstm->outp);
|
||||
mstm->disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -3368,25 +3440,6 @@ nv50_mstm_new(struct nouveau_encoder *outp, struct drm_dp_aux *aux, int aux_max,
|
|||
/******************************************************************************
|
||||
* SOR
|
||||
*****************************************************************************/
|
||||
static void
|
||||
nv50_sor_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nv50_disp *disp = nv50_disp(encoder->dev);
|
||||
struct {
|
||||
struct nv50_disp_mthd_v1 base;
|
||||
struct nv50_disp_sor_pwr_v0 pwr;
|
||||
} args = {
|
||||
.base.version = 1,
|
||||
.base.method = NV50_DISP_MTHD_V1_SOR_PWR,
|
||||
.base.hasht = nv_encoder->dcb->hasht,
|
||||
.base.hashm = nv_encoder->dcb->hashm,
|
||||
.pwr.state = mode == DRM_MODE_DPMS_ON,
|
||||
};
|
||||
|
||||
nvif_mthd(disp->disp, 0, &args, sizeof(args));
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_sor_update(struct nouveau_encoder *nv_encoder, u8 head,
|
||||
struct drm_display_mode *mode, u8 proto, u8 depth)
|
||||
|
@ -3459,6 +3512,7 @@ nv50_sor_disable(struct drm_encoder *encoder)
|
|||
nv_encoder->update(nv_encoder, nv_crtc->index, NULL, 0, 0);
|
||||
nv50_audio_disable(encoder, nv_crtc);
|
||||
nv50_hdmi_disable(&nv_encoder->base.base, nv_crtc);
|
||||
nv50_outp_release(nv_encoder);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3487,10 +3541,11 @@ nv50_sor_enable(struct drm_encoder *encoder)
|
|||
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
nv_encoder->crtc = encoder->crtc;
|
||||
nv50_outp_acquire(nv_encoder);
|
||||
|
||||
switch (nv_encoder->dcb->type) {
|
||||
case DCB_OUTPUT_TMDS:
|
||||
if (nv_encoder->dcb->sorconf.link & 1) {
|
||||
if (nv_encoder->link & 1) {
|
||||
proto = 0x1;
|
||||
/* Only enable dual-link if:
|
||||
* - Need to (i.e. rate > 165MHz)
|
||||
|
@ -3548,7 +3603,7 @@ nv50_sor_enable(struct drm_encoder *encoder)
|
|||
else
|
||||
depth = 0x6;
|
||||
|
||||
if (nv_encoder->dcb->sorconf.link & 1)
|
||||
if (nv_encoder->link & 1)
|
||||
proto = 0x8;
|
||||
else
|
||||
proto = 0x9;
|
||||
|
@ -3565,7 +3620,6 @@ nv50_sor_enable(struct drm_encoder *encoder)
|
|||
|
||||
static const struct drm_encoder_helper_funcs
|
||||
nv50_sor_help = {
|
||||
.dpms = nv50_sor_dpms,
|
||||
.atomic_check = nv50_outp_atomic_check,
|
||||
.enable = nv50_sor_enable,
|
||||
.disable = nv50_sor_disable,
|
||||
|
@ -3608,7 +3662,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
|
|||
if (!nv_encoder)
|
||||
return -ENOMEM;
|
||||
nv_encoder->dcb = dcbe;
|
||||
nv_encoder->or = ffs(dcbe->or) - 1;
|
||||
nv_encoder->update = nv50_sor_update;
|
||||
|
||||
encoder = to_drm_encoder(nv_encoder);
|
||||
|
@ -3649,26 +3702,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
|
|||
/******************************************************************************
|
||||
* PIOR
|
||||
*****************************************************************************/
|
||||
static void
|
||||
nv50_pior_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct nv50_disp *disp = nv50_disp(encoder->dev);
|
||||
struct {
|
||||
struct nv50_disp_mthd_v1 base;
|
||||
struct nv50_disp_pior_pwr_v0 pwr;
|
||||
} args = {
|
||||
.base.version = 1,
|
||||
.base.method = NV50_DISP_MTHD_V1_PIOR_PWR,
|
||||
.base.hasht = nv_encoder->dcb->hasht,
|
||||
.base.hashm = nv_encoder->dcb->hashm,
|
||||
.pwr.state = mode == DRM_MODE_DPMS_ON,
|
||||
.pwr.type = nv_encoder->dcb->type,
|
||||
};
|
||||
|
||||
nvif_mthd(disp->disp, 0, &args, sizeof(args));
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_pior_atomic_check(struct drm_encoder *encoder,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
|
@ -3701,6 +3734,7 @@ nv50_pior_disable(struct drm_encoder *encoder)
|
|||
}
|
||||
|
||||
nv_encoder->crtc = NULL;
|
||||
nv50_outp_release(nv_encoder);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -3715,6 +3749,8 @@ nv50_pior_enable(struct drm_encoder *encoder)
|
|||
u8 proto, depth;
|
||||
u32 *push;
|
||||
|
||||
nv50_outp_acquire(nv_encoder);
|
||||
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
switch (nv_connector->base.display_info.bpc) {
|
||||
case 10: depth = 0x6; break;
|
||||
|
@ -3753,7 +3789,6 @@ nv50_pior_enable(struct drm_encoder *encoder)
|
|||
|
||||
static const struct drm_encoder_helper_funcs
|
||||
nv50_pior_help = {
|
||||
.dpms = nv50_pior_dpms,
|
||||
.atomic_check = nv50_pior_atomic_check,
|
||||
.enable = nv50_pior_enable,
|
||||
.disable = nv50_pior_disable,
|
||||
|
@ -3803,7 +3838,6 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe)
|
|||
if (!nv_encoder)
|
||||
return -ENOMEM;
|
||||
nv_encoder->dcb = dcbe;
|
||||
nv_encoder->or = ffs(dcbe->or) - 1;
|
||||
nv_encoder->i2c = ddc;
|
||||
nv_encoder->aux = aux;
|
||||
|
||||
|
@ -4318,14 +4352,8 @@ nv50_display_init(struct drm_device *dev)
|
|||
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
||||
if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
|
||||
const struct drm_encoder_helper_funcs *help;
|
||||
struct nouveau_encoder *nv_encoder;
|
||||
|
||||
nv_encoder = nouveau_encoder(encoder);
|
||||
help = encoder->helper_private;
|
||||
if (help && help->dpms)
|
||||
help->dpms(encoder, DRM_MODE_DPMS_ON);
|
||||
|
||||
struct nouveau_encoder *nv_encoder =
|
||||
nouveau_encoder(encoder);
|
||||
nv50_mstm_init(nv_encoder->dp.mstm);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1257,7 +1257,7 @@ nvaa_chipset = {
|
|||
.therm = g84_therm_new,
|
||||
.timer = nv41_timer_new,
|
||||
.volt = nv40_volt_new,
|
||||
.disp = g94_disp_new,
|
||||
.disp = mcp77_disp_new,
|
||||
.dma = nv50_dma_new,
|
||||
.fifo = g84_fifo_new,
|
||||
.gr = gt200_gr_new,
|
||||
|
@ -1289,7 +1289,7 @@ nvac_chipset = {
|
|||
.therm = g84_therm_new,
|
||||
.timer = nv41_timer_new,
|
||||
.volt = nv40_volt_new,
|
||||
.disp = g94_disp_new,
|
||||
.disp = mcp77_disp_new,
|
||||
.dma = nv50_dma_new,
|
||||
.fifo = g84_fifo_new,
|
||||
.gr = mcp79_gr_new,
|
||||
|
@ -1323,7 +1323,7 @@ nvaf_chipset = {
|
|||
.timer = nv41_timer_new,
|
||||
.volt = nv40_volt_new,
|
||||
.ce[0] = gt215_ce_new,
|
||||
.disp = gt215_disp_new,
|
||||
.disp = mcp89_disp_new,
|
||||
.dma = nv50_dma_new,
|
||||
.fifo = g84_fifo_new,
|
||||
.gr = mcp89_gr_new,
|
||||
|
|
|
@ -51,10 +51,12 @@ nvkm_device_tegra_power_up(struct nvkm_device_tegra *tdev)
|
|||
reset_control_assert(tdev->rst);
|
||||
udelay(10);
|
||||
|
||||
ret = tegra_powergate_remove_clamping(TEGRA_POWERGATE_3D);
|
||||
if (ret)
|
||||
goto err_clamp;
|
||||
udelay(10);
|
||||
if (!tdev->pdev->dev.pm_domain) {
|
||||
ret = tegra_powergate_remove_clamping(TEGRA_POWERGATE_3D);
|
||||
if (ret)
|
||||
goto err_clamp;
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
reset_control_deassert(tdev->rst);
|
||||
udelay(10);
|
||||
|
@ -80,9 +82,6 @@ nvkm_device_tegra_power_down(struct nvkm_device_tegra *tdev)
|
|||
{
|
||||
int ret;
|
||||
|
||||
reset_control_assert(tdev->rst);
|
||||
udelay(10);
|
||||
|
||||
clk_disable_unprepare(tdev->clk_pwr);
|
||||
if (tdev->clk_ref)
|
||||
clk_disable_unprepare(tdev->clk_ref);
|
||||
|
|
|
@ -4,7 +4,9 @@ nvkm-y += nvkm/engine/disp/nv50.o
|
|||
nvkm-y += nvkm/engine/disp/g84.o
|
||||
nvkm-y += nvkm/engine/disp/g94.o
|
||||
nvkm-y += nvkm/engine/disp/gt200.o
|
||||
nvkm-y += nvkm/engine/disp/mcp77.o
|
||||
nvkm-y += nvkm/engine/disp/gt215.o
|
||||
nvkm-y += nvkm/engine/disp/mcp89.o
|
||||
nvkm-y += nvkm/engine/disp/gf119.o
|
||||
nvkm-y += nvkm/engine/disp/gk104.o
|
||||
nvkm-y += nvkm/engine/disp/gk110.o
|
||||
|
@ -12,29 +14,41 @@ nvkm-y += nvkm/engine/disp/gm107.o
|
|||
nvkm-y += nvkm/engine/disp/gm200.o
|
||||
nvkm-y += nvkm/engine/disp/gp100.o
|
||||
nvkm-y += nvkm/engine/disp/gp102.o
|
||||
nvkm-y += nvkm/engine/disp/vga.o
|
||||
|
||||
nvkm-y += nvkm/engine/disp/outp.o
|
||||
nvkm-y += nvkm/engine/disp/outpdp.o
|
||||
nvkm-y += nvkm/engine/disp/head.o
|
||||
nvkm-y += nvkm/engine/disp/headnv04.o
|
||||
nvkm-y += nvkm/engine/disp/headnv50.o
|
||||
nvkm-y += nvkm/engine/disp/headgf119.o
|
||||
|
||||
nvkm-y += nvkm/engine/disp/ior.o
|
||||
nvkm-y += nvkm/engine/disp/dacnv50.o
|
||||
nvkm-y += nvkm/engine/disp/dacgf119.o
|
||||
nvkm-y += nvkm/engine/disp/piornv50.o
|
||||
nvkm-y += nvkm/engine/disp/sornv50.o
|
||||
nvkm-y += nvkm/engine/disp/sorg84.o
|
||||
nvkm-y += nvkm/engine/disp/sorg94.o
|
||||
nvkm-y += nvkm/engine/disp/sormcp77.o
|
||||
nvkm-y += nvkm/engine/disp/sorgt215.o
|
||||
nvkm-y += nvkm/engine/disp/sormcp89.o
|
||||
nvkm-y += nvkm/engine/disp/sorgf119.o
|
||||
nvkm-y += nvkm/engine/disp/sorgk104.o
|
||||
nvkm-y += nvkm/engine/disp/sorgm107.o
|
||||
nvkm-y += nvkm/engine/disp/sorgm200.o
|
||||
nvkm-y += nvkm/engine/disp/dport.o
|
||||
|
||||
nvkm-y += nvkm/engine/disp/conn.o
|
||||
nvkm-y += nvkm/engine/disp/outp.o
|
||||
nvkm-y += nvkm/engine/disp/dp.o
|
||||
|
||||
nvkm-y += nvkm/engine/disp/hdagt215.o
|
||||
nvkm-y += nvkm/engine/disp/hdagf119.o
|
||||
|
||||
nvkm-y += nvkm/engine/disp/hdmi.o
|
||||
nvkm-y += nvkm/engine/disp/hdmig84.o
|
||||
nvkm-y += nvkm/engine/disp/hdmigt215.o
|
||||
nvkm-y += nvkm/engine/disp/hdmigf119.o
|
||||
nvkm-y += nvkm/engine/disp/hdmigk104.o
|
||||
|
||||
nvkm-y += nvkm/engine/disp/vga.o
|
||||
nvkm-y += nvkm/engine/disp/conn.o
|
||||
|
||||
nvkm-y += nvkm/engine/disp/rootnv04.o
|
||||
nvkm-y += nvkm/engine/disp/rootnv50.o
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
*/
|
||||
#include "priv.h"
|
||||
#include "conn.h"
|
||||
#include "dp.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
#include "outp.h"
|
||||
|
||||
#include <core/client.h>
|
||||
|
@ -37,17 +40,21 @@
|
|||
#include <nvif/unpack.h>
|
||||
|
||||
static void
|
||||
nvkm_disp_vblank_fini(struct nvkm_event *event, int type, int head)
|
||||
nvkm_disp_vblank_fini(struct nvkm_event *event, int type, int id)
|
||||
{
|
||||
struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank);
|
||||
disp->func->head.vblank_fini(disp, head);
|
||||
struct nvkm_head *head = nvkm_head_find(disp, id);
|
||||
if (head)
|
||||
head->func->vblank_put(head);
|
||||
}
|
||||
|
||||
static void
|
||||
nvkm_disp_vblank_init(struct nvkm_event *event, int type, int head)
|
||||
nvkm_disp_vblank_init(struct nvkm_event *event, int type, int id)
|
||||
{
|
||||
struct nvkm_disp *disp = container_of(event, typeof(*disp), vblank);
|
||||
disp->func->head.vblank_init(disp, head);
|
||||
struct nvkm_head *head = nvkm_head_find(disp, id);
|
||||
if (head)
|
||||
head->func->vblank_get(head);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -96,7 +103,7 @@ nvkm_disp_hpd_ctor(struct nvkm_object *object, void *data, u32 size,
|
|||
union {
|
||||
struct nvif_notify_conn_req_v0 v0;
|
||||
} *req = data;
|
||||
struct nvkm_output *outp;
|
||||
struct nvkm_outp *outp;
|
||||
int ret = -ENOSYS;
|
||||
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, req->v0, 0, 0, false))) {
|
||||
|
@ -210,15 +217,15 @@ static int
|
|||
nvkm_disp_fini(struct nvkm_engine *engine, bool suspend)
|
||||
{
|
||||
struct nvkm_disp *disp = nvkm_disp(engine);
|
||||
struct nvkm_connector *conn;
|
||||
struct nvkm_output *outp;
|
||||
struct nvkm_conn *conn;
|
||||
struct nvkm_outp *outp;
|
||||
|
||||
list_for_each_entry(outp, &disp->outp, head) {
|
||||
nvkm_output_fini(outp);
|
||||
nvkm_outp_fini(outp);
|
||||
}
|
||||
|
||||
list_for_each_entry(conn, &disp->conn, head) {
|
||||
nvkm_connector_fini(conn);
|
||||
nvkm_conn_fini(conn);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -228,129 +235,70 @@ static int
|
|||
nvkm_disp_init(struct nvkm_engine *engine)
|
||||
{
|
||||
struct nvkm_disp *disp = nvkm_disp(engine);
|
||||
struct nvkm_connector *conn;
|
||||
struct nvkm_output *outp;
|
||||
struct nvkm_conn *conn;
|
||||
struct nvkm_outp *outp;
|
||||
|
||||
list_for_each_entry(conn, &disp->conn, head) {
|
||||
nvkm_connector_init(conn);
|
||||
nvkm_conn_init(conn);
|
||||
}
|
||||
|
||||
list_for_each_entry(outp, &disp->outp, head) {
|
||||
nvkm_output_init(outp);
|
||||
nvkm_outp_init(outp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *
|
||||
nvkm_disp_dtor(struct nvkm_engine *engine)
|
||||
static int
|
||||
nvkm_disp_oneinit(struct nvkm_engine *engine)
|
||||
{
|
||||
struct nvkm_disp *disp = nvkm_disp(engine);
|
||||
struct nvkm_connector *conn;
|
||||
struct nvkm_output *outp;
|
||||
void *data = disp;
|
||||
|
||||
if (disp->func->dtor)
|
||||
data = disp->func->dtor(disp);
|
||||
|
||||
nvkm_event_fini(&disp->vblank);
|
||||
nvkm_event_fini(&disp->hpd);
|
||||
|
||||
while (!list_empty(&disp->outp)) {
|
||||
outp = list_first_entry(&disp->outp, typeof(*outp), head);
|
||||
list_del(&outp->head);
|
||||
nvkm_output_del(&outp);
|
||||
}
|
||||
|
||||
while (!list_empty(&disp->conn)) {
|
||||
conn = list_first_entry(&disp->conn, typeof(*conn), head);
|
||||
list_del(&conn->head);
|
||||
nvkm_connector_del(&conn);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static const struct nvkm_engine_func
|
||||
nvkm_disp = {
|
||||
.dtor = nvkm_disp_dtor,
|
||||
.init = nvkm_disp_init,
|
||||
.fini = nvkm_disp_fini,
|
||||
.intr = nvkm_disp_intr,
|
||||
.base.sclass = nvkm_disp_class_get,
|
||||
};
|
||||
|
||||
int
|
||||
nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
|
||||
int index, int heads, struct nvkm_disp *disp)
|
||||
{
|
||||
struct nvkm_bios *bios = device->bios;
|
||||
struct nvkm_output *outp, *outt, *pair;
|
||||
struct nvkm_connector *conn;
|
||||
struct nvkm_subdev *subdev = &disp->engine.subdev;
|
||||
struct nvkm_bios *bios = subdev->device->bios;
|
||||
struct nvkm_outp *outp, *outt, *pair;
|
||||
struct nvkm_conn *conn;
|
||||
struct nvkm_head *head;
|
||||
struct nvbios_connE connE;
|
||||
struct dcb_output dcbE;
|
||||
u8 hpd = 0, ver, hdr;
|
||||
u32 data;
|
||||
int ret, i;
|
||||
|
||||
INIT_LIST_HEAD(&disp->outp);
|
||||
INIT_LIST_HEAD(&disp->conn);
|
||||
disp->func = func;
|
||||
disp->head.nr = heads;
|
||||
|
||||
ret = nvkm_engine_ctor(&nvkm_disp, device, index, true, &disp->engine);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* create output objects for each display path in the vbios */
|
||||
/* Create output path objects for each VBIOS display path. */
|
||||
i = -1;
|
||||
while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) {
|
||||
const struct nvkm_disp_func_outp *outps;
|
||||
int (*ctor)(struct nvkm_disp *, int, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
|
||||
if (dcbE.type == DCB_OUTPUT_UNUSED)
|
||||
continue;
|
||||
if (dcbE.type == DCB_OUTPUT_EOL)
|
||||
break;
|
||||
outp = NULL;
|
||||
|
||||
switch (dcbE.location) {
|
||||
case 0: outps = &disp->func->outp.internal; break;
|
||||
case 1: outps = &disp->func->outp.external; break;
|
||||
default:
|
||||
nvkm_warn(&disp->engine.subdev,
|
||||
"dcb %d locn %d unknown\n", i, dcbE.location);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (dcbE.type) {
|
||||
case DCB_OUTPUT_ANALOG: ctor = outps->crt ; break;
|
||||
case DCB_OUTPUT_TV : ctor = outps->tv ; break;
|
||||
case DCB_OUTPUT_TMDS : ctor = outps->tmds; break;
|
||||
case DCB_OUTPUT_LVDS : ctor = outps->lvds; break;
|
||||
case DCB_OUTPUT_DP : ctor = outps->dp ; break;
|
||||
case DCB_OUTPUT_ANALOG:
|
||||
case DCB_OUTPUT_TV:
|
||||
case DCB_OUTPUT_TMDS:
|
||||
case DCB_OUTPUT_LVDS:
|
||||
ret = nvkm_outp_new(disp, i, &dcbE, &outp);
|
||||
break;
|
||||
case DCB_OUTPUT_DP:
|
||||
ret = nvkm_dp_new(disp, i, &dcbE, &outp);
|
||||
break;
|
||||
default:
|
||||
nvkm_warn(&disp->engine.subdev,
|
||||
"dcb %d type %d unknown\n", i, dcbE.type);
|
||||
nvkm_warn(subdev, "dcb %d type %d unknown\n",
|
||||
i, dcbE.type);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ctor)
|
||||
ret = ctor(disp, i, &dcbE, &outp);
|
||||
else
|
||||
ret = -ENODEV;
|
||||
|
||||
if (ret) {
|
||||
if (ret == -ENODEV) {
|
||||
nvkm_debug(&disp->engine.subdev,
|
||||
"dcb %d %d/%d not supported\n",
|
||||
i, dcbE.location, dcbE.type);
|
||||
if (outp) {
|
||||
if (ret != -ENODEV)
|
||||
OUTP_ERR(outp, "ctor failed: %d", ret);
|
||||
else
|
||||
OUTP_DBG(outp, "not supported");
|
||||
nvkm_outp_del(&outp);
|
||||
continue;
|
||||
}
|
||||
nvkm_error(&disp->engine.subdev,
|
||||
"failed to create output %d\n", i);
|
||||
nvkm_output_del(&outp);
|
||||
nvkm_error(subdev, "failed to create outp %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -358,18 +306,18 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
|
|||
hpd = max(hpd, (u8)(dcbE.connector + 1));
|
||||
}
|
||||
|
||||
/* create connector objects based on the outputs we support */
|
||||
/* Create connector objects based on available output paths. */
|
||||
list_for_each_entry_safe(outp, outt, &disp->outp, head) {
|
||||
/* bios data *should* give us the most useful information */
|
||||
/* VBIOS data *should* give us the most useful information. */
|
||||
data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr,
|
||||
&connE);
|
||||
|
||||
/* no bios connector data... */
|
||||
/* No bios connector data... */
|
||||
if (!data) {
|
||||
/* heuristic: anything with the same ccb index is
|
||||
/* Heuristic: anything with the same ccb index is
|
||||
* considered to be on the same connector, any
|
||||
* output path without an associated ccb entry will
|
||||
* be put on its own connector
|
||||
* be put on its own connector.
|
||||
*/
|
||||
int ccb_index = outp->info.i2c_index;
|
||||
if (ccb_index != 0xf) {
|
||||
|
@ -381,7 +329,7 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
|
|||
}
|
||||
}
|
||||
|
||||
/* connector shared with another output path */
|
||||
/* Connector shared with another output path. */
|
||||
if (outp->conn)
|
||||
continue;
|
||||
|
||||
|
@ -392,7 +340,7 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
|
|||
i = outp->info.connector;
|
||||
}
|
||||
|
||||
/* check that we haven't already created this connector */
|
||||
/* Check that we haven't already created this connector. */
|
||||
list_for_each_entry(conn, &disp->conn, head) {
|
||||
if (conn->index == outp->info.connector) {
|
||||
outp->conn = conn;
|
||||
|
@ -403,15 +351,15 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
|
|||
if (outp->conn)
|
||||
continue;
|
||||
|
||||
/* apparently we need to create a new one! */
|
||||
ret = nvkm_connector_new(disp, i, &connE, &outp->conn);
|
||||
/* Apparently we need to create a new one! */
|
||||
ret = nvkm_conn_new(disp, i, &connE, &outp->conn);
|
||||
if (ret) {
|
||||
nvkm_error(&disp->engine.subdev,
|
||||
"failed to create output %d conn: %d\n",
|
||||
"failed to create outp %d conn: %d\n",
|
||||
outp->index, ret);
|
||||
nvkm_connector_del(&outp->conn);
|
||||
nvkm_conn_del(&outp->conn);
|
||||
list_del(&outp->head);
|
||||
nvkm_output_del(&outp);
|
||||
nvkm_outp_del(&outp);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -422,18 +370,81 @@ nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nvkm_event_init(&nvkm_disp_vblank_func, 1, heads, &disp->vblank);
|
||||
if (ret)
|
||||
return ret;
|
||||
i = 0;
|
||||
list_for_each_entry(head, &disp->head, head)
|
||||
i = max(i, head->id + 1);
|
||||
|
||||
return 0;
|
||||
return nvkm_event_init(&nvkm_disp_vblank_func, 1, i, &disp->vblank);
|
||||
}
|
||||
|
||||
static void *
|
||||
nvkm_disp_dtor(struct nvkm_engine *engine)
|
||||
{
|
||||
struct nvkm_disp *disp = nvkm_disp(engine);
|
||||
struct nvkm_conn *conn;
|
||||
struct nvkm_outp *outp;
|
||||
void *data = disp;
|
||||
|
||||
if (disp->func->dtor)
|
||||
data = disp->func->dtor(disp);
|
||||
|
||||
nvkm_event_fini(&disp->vblank);
|
||||
nvkm_event_fini(&disp->hpd);
|
||||
|
||||
while (!list_empty(&disp->conn)) {
|
||||
conn = list_first_entry(&disp->conn, typeof(*conn), head);
|
||||
list_del(&conn->head);
|
||||
nvkm_conn_del(&conn);
|
||||
}
|
||||
|
||||
while (!list_empty(&disp->outp)) {
|
||||
outp = list_first_entry(&disp->outp, typeof(*outp), head);
|
||||
list_del(&outp->head);
|
||||
nvkm_outp_del(&outp);
|
||||
}
|
||||
|
||||
while (!list_empty(&disp->ior)) {
|
||||
struct nvkm_ior *ior =
|
||||
list_first_entry(&disp->ior, typeof(*ior), head);
|
||||
nvkm_ior_del(&ior);
|
||||
}
|
||||
|
||||
while (!list_empty(&disp->head)) {
|
||||
struct nvkm_head *head =
|
||||
list_first_entry(&disp->head, typeof(*head), head);
|
||||
nvkm_head_del(&head);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static const struct nvkm_engine_func
|
||||
nvkm_disp = {
|
||||
.dtor = nvkm_disp_dtor,
|
||||
.oneinit = nvkm_disp_oneinit,
|
||||
.init = nvkm_disp_init,
|
||||
.fini = nvkm_disp_fini,
|
||||
.intr = nvkm_disp_intr,
|
||||
.base.sclass = nvkm_disp_class_get,
|
||||
};
|
||||
|
||||
int
|
||||
nvkm_disp_ctor(const struct nvkm_disp_func *func, struct nvkm_device *device,
|
||||
int index, struct nvkm_disp *disp)
|
||||
{
|
||||
disp->func = func;
|
||||
INIT_LIST_HEAD(&disp->head);
|
||||
INIT_LIST_HEAD(&disp->ior);
|
||||
INIT_LIST_HEAD(&disp->outp);
|
||||
INIT_LIST_HEAD(&disp->conn);
|
||||
return nvkm_engine_ctor(&nvkm_disp, device, index, true, &disp->engine);
|
||||
}
|
||||
|
||||
int
|
||||
nvkm_disp_new_(const struct nvkm_disp_func *func, struct nvkm_device *device,
|
||||
int index, int heads, struct nvkm_disp **pdisp)
|
||||
int index, struct nvkm_disp **pdisp)
|
||||
{
|
||||
if (!(*pdisp = kzalloc(sizeof(**pdisp), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
return nvkm_disp_ctor(func, device, index, heads, *pdisp);
|
||||
return nvkm_disp_ctor(func, device, index, *pdisp);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "dmacnv50.h"
|
||||
#include "head.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
#include <core/client.h>
|
||||
|
@ -50,7 +51,7 @@ nv50_disp_base_new(const struct nv50_disp_dmac_func *func,
|
|||
nvif_ioctl(parent, "create disp base channel dma vers %d "
|
||||
"pushbuf %016llx head %d\n",
|
||||
args->v0.version, args->v0.pushbuf, args->v0.head);
|
||||
if (args->v0.head > disp->base.head.nr)
|
||||
if (!nvkm_head_find(&disp->base, args->v0.head))
|
||||
return -EINVAL;
|
||||
push = args->v0.pushbuf;
|
||||
head = args->v0.head;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "rootnv50.h"
|
||||
|
||||
#include <core/client.h>
|
||||
#include <core/notify.h>
|
||||
#include <core/ramht.h>
|
||||
#include <engine/dma.h>
|
||||
|
||||
|
|
|
@ -30,9 +30,9 @@
|
|||
#include <nvif/event.h>
|
||||
|
||||
static int
|
||||
nvkm_connector_hpd(struct nvkm_notify *notify)
|
||||
nvkm_conn_hpd(struct nvkm_notify *notify)
|
||||
{
|
||||
struct nvkm_connector *conn = container_of(notify, typeof(*conn), hpd);
|
||||
struct nvkm_conn *conn = container_of(notify, typeof(*conn), hpd);
|
||||
struct nvkm_disp *disp = conn->disp;
|
||||
struct nvkm_gpio *gpio = disp->engine.subdev.device->gpio;
|
||||
const struct nvkm_gpio_ntfy_rep *line = notify->data;
|
||||
|
@ -52,21 +52,21 @@ nvkm_connector_hpd(struct nvkm_notify *notify)
|
|||
}
|
||||
|
||||
void
|
||||
nvkm_connector_fini(struct nvkm_connector *conn)
|
||||
nvkm_conn_fini(struct nvkm_conn *conn)
|
||||
{
|
||||
nvkm_notify_put(&conn->hpd);
|
||||
}
|
||||
|
||||
void
|
||||
nvkm_connector_init(struct nvkm_connector *conn)
|
||||
nvkm_conn_init(struct nvkm_conn *conn)
|
||||
{
|
||||
nvkm_notify_get(&conn->hpd);
|
||||
}
|
||||
|
||||
void
|
||||
nvkm_connector_del(struct nvkm_connector **pconn)
|
||||
nvkm_conn_del(struct nvkm_conn **pconn)
|
||||
{
|
||||
struct nvkm_connector *conn = *pconn;
|
||||
struct nvkm_conn *conn = *pconn;
|
||||
if (conn) {
|
||||
nvkm_notify_fini(&conn->hpd);
|
||||
kfree(*pconn);
|
||||
|
@ -75,8 +75,8 @@ nvkm_connector_del(struct nvkm_connector **pconn)
|
|||
}
|
||||
|
||||
static void
|
||||
nvkm_connector_ctor(struct nvkm_disp *disp, int index,
|
||||
struct nvbios_connE *info, struct nvkm_connector *conn)
|
||||
nvkm_conn_ctor(struct nvkm_disp *disp, int index, struct nvbios_connE *info,
|
||||
struct nvkm_conn *conn)
|
||||
{
|
||||
static const u8 hpd[] = { 0x07, 0x08, 0x51, 0x52, 0x5e, 0x5f, 0x60 };
|
||||
struct nvkm_gpio *gpio = disp->engine.subdev.device->gpio;
|
||||
|
@ -105,7 +105,7 @@ nvkm_connector_ctor(struct nvkm_disp *disp, int index,
|
|||
return;
|
||||
}
|
||||
|
||||
ret = nvkm_notify_init(NULL, &gpio->event, nvkm_connector_hpd,
|
||||
ret = nvkm_notify_init(NULL, &gpio->event, nvkm_conn_hpd,
|
||||
true, &(struct nvkm_gpio_ntfy_req) {
|
||||
.mask = NVKM_GPIO_TOGGLED,
|
||||
.line = func.line,
|
||||
|
@ -122,11 +122,11 @@ nvkm_connector_ctor(struct nvkm_disp *disp, int index,
|
|||
}
|
||||
|
||||
int
|
||||
nvkm_connector_new(struct nvkm_disp *disp, int index,
|
||||
struct nvbios_connE *info, struct nvkm_connector **pconn)
|
||||
nvkm_conn_new(struct nvkm_disp *disp, int index, struct nvbios_connE *info,
|
||||
struct nvkm_conn **pconn)
|
||||
{
|
||||
if (!(*pconn = kzalloc(sizeof(**pconn), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
nvkm_connector_ctor(disp, index, info, *pconn);
|
||||
nvkm_conn_ctor(disp, index, info, *pconn);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
#include <subdev/bios.h>
|
||||
#include <subdev/bios/conn.h>
|
||||
|
||||
struct nvkm_connector {
|
||||
struct nvkm_conn {
|
||||
struct nvkm_disp *disp;
|
||||
int index;
|
||||
struct nvbios_connE info;
|
||||
|
@ -16,14 +16,14 @@ struct nvkm_connector {
|
|||
struct list_head head;
|
||||
};
|
||||
|
||||
int nvkm_connector_new(struct nvkm_disp *, int index, struct nvbios_connE *,
|
||||
struct nvkm_connector **);
|
||||
void nvkm_connector_del(struct nvkm_connector **);
|
||||
void nvkm_connector_init(struct nvkm_connector *);
|
||||
void nvkm_connector_fini(struct nvkm_connector *);
|
||||
int nvkm_conn_new(struct nvkm_disp *, int index, struct nvbios_connE *,
|
||||
struct nvkm_conn **);
|
||||
void nvkm_conn_del(struct nvkm_conn **);
|
||||
void nvkm_conn_init(struct nvkm_conn *);
|
||||
void nvkm_conn_fini(struct nvkm_conn *);
|
||||
|
||||
#define CONN_MSG(c,l,f,a...) do { \
|
||||
struct nvkm_connector *_conn = (c); \
|
||||
struct nvkm_conn *_conn = (c); \
|
||||
nvkm_##l(&_conn->disp->engine.subdev, "conn %02x:%02x%02x: "f"\n", \
|
||||
_conn->index, _conn->info.location, _conn->info.type, ##a); \
|
||||
} while(0)
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "channv50.h"
|
||||
#include "head.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
#include <core/client.h>
|
||||
|
@ -48,7 +49,7 @@ nv50_disp_curs_new(const struct nv50_disp_chan_func *func,
|
|||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_ioctl(parent, "create disp cursor vers %d head %d\n",
|
||||
args->v0.version, args->v0.head);
|
||||
if (args->v0.head > disp->base.head.nr)
|
||||
if (!nvkm_head_find(&disp->base, args->v0.head))
|
||||
return -EINVAL;
|
||||
head = args->v0.head;
|
||||
} else
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright 2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "ior.h"
|
||||
|
||||
static void
|
||||
gf119_dac_clock(struct nvkm_ior *dac)
|
||||
{
|
||||
struct nvkm_device *device = dac->disp->engine.subdev.device;
|
||||
const u32 doff = nv50_ior_base(dac);
|
||||
nvkm_mask(device, 0x612280 + doff, 0x07070707, 0x00000000);
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_dac_state(struct nvkm_ior *dac, struct nvkm_ior_state *state)
|
||||
{
|
||||
struct nvkm_device *device = dac->disp->engine.subdev.device;
|
||||
const u32 coff = (state == &dac->asy) * 0x20000 + dac->id * 0x20;
|
||||
u32 ctrl = nvkm_rd32(device, 0x640180 + coff);
|
||||
|
||||
state->proto_evo = (ctrl & 0x00000f00) >> 8;
|
||||
switch (state->proto_evo) {
|
||||
case 0: state->proto = CRT; break;
|
||||
default:
|
||||
state->proto = UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
state->head = ctrl & 0x0000000f;
|
||||
}
|
||||
|
||||
static const struct nvkm_ior_func
|
||||
gf119_dac = {
|
||||
.state = gf119_dac_state,
|
||||
.power = nv50_dac_power,
|
||||
.sense = nv50_dac_sense,
|
||||
.clock = gf119_dac_clock,
|
||||
};
|
||||
|
||||
int
|
||||
gf119_dac_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
struct nvkm_device *device = disp->engine.subdev.device;
|
||||
if (!(nvkm_rd32(device, 0x612004) & (0x00000010 << id)))
|
||||
return 0;
|
||||
return nvkm_ior_new_(&gf119_dac, disp, DAC, id);
|
||||
}
|
|
@ -21,106 +21,96 @@
|
|||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "outp.h"
|
||||
#include "ior.h"
|
||||
|
||||
#include <core/client.h>
|
||||
#include <subdev/timer.h>
|
||||
|
||||
#include <nvif/cl5070.h>
|
||||
#include <nvif/unpack.h>
|
||||
|
||||
int
|
||||
nv50_dac_power(NV50_DISP_MTHD_V1)
|
||||
static void
|
||||
nv50_dac_clock(struct nvkm_ior *dac)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
const u32 doff = outp->or * 0x800;
|
||||
union {
|
||||
struct nv50_disp_dac_pwr_v0 v0;
|
||||
} *args = data;
|
||||
u32 stat;
|
||||
int ret = -ENOSYS;
|
||||
|
||||
nvif_ioctl(object, "disp dac pwr size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_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;
|
||||
|
||||
nvkm_msec(device, 2000,
|
||||
if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000))
|
||||
break;
|
||||
);
|
||||
nvkm_mask(device, 0x61a004 + doff, 0xc000007f, 0x80000000 | stat);
|
||||
nvkm_msec(device, 2000,
|
||||
if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000))
|
||||
break;
|
||||
);
|
||||
return 0;
|
||||
struct nvkm_device *device = dac->disp->engine.subdev.device;
|
||||
const u32 doff = nv50_ior_base(dac);
|
||||
nvkm_mask(device, 0x614280 + doff, 0x07070707, 0x00000000);
|
||||
}
|
||||
|
||||
int
|
||||
nv50_dac_sense(NV50_DISP_MTHD_V1)
|
||||
nv50_dac_sense(struct nvkm_ior *dac, u32 loadval)
|
||||
{
|
||||
struct nvkm_subdev *subdev = &disp->base.engine.subdev;
|
||||
struct nvkm_device *device = subdev->device;
|
||||
union {
|
||||
struct nv50_disp_dac_load_v0 v0;
|
||||
} *args = data;
|
||||
const u32 doff = outp->or * 0x800;
|
||||
u32 loadval;
|
||||
int ret = -ENOSYS;
|
||||
struct nvkm_device *device = dac->disp->engine.subdev.device;
|
||||
const u32 doff = nv50_ior_base(dac);
|
||||
|
||||
nvif_ioctl(object, "disp dac load size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_ioctl(object, "disp dac load vers %d data %08x\n",
|
||||
args->v0.version, args->v0.data);
|
||||
if (args->v0.data & 0xfff00000)
|
||||
return -EINVAL;
|
||||
loadval = args->v0.data;
|
||||
} else
|
||||
return ret;
|
||||
|
||||
nvkm_mask(device, 0x61a004 + doff, 0x807f0000, 0x80150000);
|
||||
nvkm_msec(device, 2000,
|
||||
if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000))
|
||||
break;
|
||||
);
|
||||
dac->func->power(dac, false, true, false, false, false);
|
||||
|
||||
nvkm_wr32(device, 0x61a00c + doff, 0x00100000 | loadval);
|
||||
mdelay(9);
|
||||
udelay(500);
|
||||
loadval = nvkm_mask(device, 0x61a00c + doff, 0xffffffff, 0x00000000);
|
||||
|
||||
nvkm_mask(device, 0x61a004 + doff, 0x807f0000, 0x80550000);
|
||||
dac->func->power(dac, false, false, false, false, false);
|
||||
if (!(loadval & 0x80000000))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return (loadval & 0x38000000) >> 27;
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_dac_power_wait(struct nvkm_device *device, const u32 doff)
|
||||
{
|
||||
nvkm_msec(device, 2000,
|
||||
if (!(nvkm_rd32(device, 0x61a004 + doff) & 0x80000000))
|
||||
break;
|
||||
);
|
||||
|
||||
nvkm_debug(subdev, "DAC%d sense: %08x\n", outp->or, loadval);
|
||||
if (!(loadval & 0x80000000))
|
||||
return -ETIMEDOUT;
|
||||
|
||||
args->v0.load = (loadval & 0x38000000) >> 27;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nvkm_output_func
|
||||
nv50_dac_output_func = {
|
||||
void
|
||||
nv50_dac_power(struct nvkm_ior *dac, bool normal, bool pu,
|
||||
bool data, bool vsync, bool hsync)
|
||||
{
|
||||
struct nvkm_device *device = dac->disp->engine.subdev.device;
|
||||
const u32 doff = nv50_ior_base(dac);
|
||||
const u32 shift = normal ? 0 : 16;
|
||||
const u32 state = 0x80000000 | (0x00000040 * ! pu |
|
||||
0x00000010 * ! data |
|
||||
0x00000004 * ! vsync |
|
||||
0x00000001 * ! hsync) << shift;
|
||||
const u32 field = 0xc0000000 | (0x00000055 << shift);
|
||||
|
||||
nv50_dac_power_wait(device, doff);
|
||||
nvkm_mask(device, 0x61a004 + doff, field, state);
|
||||
nv50_dac_power_wait(device, doff);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_dac_state(struct nvkm_ior *dac, struct nvkm_ior_state *state)
|
||||
{
|
||||
struct nvkm_device *device = dac->disp->engine.subdev.device;
|
||||
const u32 coff = dac->id * 8 + (state == &dac->arm) * 4;
|
||||
u32 ctrl = nvkm_rd32(device, 0x610b58 + coff);
|
||||
|
||||
state->proto_evo = (ctrl & 0x00000f00) >> 8;
|
||||
switch (state->proto_evo) {
|
||||
case 0: state->proto = CRT; break;
|
||||
default:
|
||||
state->proto = UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
state->head = ctrl & 0x00000003;
|
||||
}
|
||||
|
||||
static const struct nvkm_ior_func
|
||||
nv50_dac = {
|
||||
.state = nv50_dac_state,
|
||||
.power = nv50_dac_power,
|
||||
.sense = nv50_dac_sense,
|
||||
.clock = nv50_dac_clock,
|
||||
};
|
||||
|
||||
int
|
||||
nv50_dac_output_new(struct nvkm_disp *disp, int index,
|
||||
struct dcb_output *dcbE, struct nvkm_output **poutp)
|
||||
nv50_dac_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
return nvkm_output_new_(&nv50_dac_output_func, disp,
|
||||
index, dcbE, poutp);
|
||||
struct nvkm_device *device = disp->engine.subdev.device;
|
||||
if (!(nvkm_rd32(device, 0x610184) & (0x00100000 << id)))
|
||||
return 0;
|
||||
return nvkm_ior_new_(&nv50_dac, disp, DAC, id);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,652 @@
|
|||
/*
|
||||
* Copyright 2014 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "dp.h"
|
||||
#include "conn.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
|
||||
#include <subdev/bios.h>
|
||||
#include <subdev/bios/init.h>
|
||||
#include <subdev/i2c.h>
|
||||
|
||||
#include <nvif/event.h>
|
||||
|
||||
struct lt_state {
|
||||
struct nvkm_dp *dp;
|
||||
u8 stat[6];
|
||||
u8 conf[4];
|
||||
bool pc2;
|
||||
u8 pc2stat;
|
||||
u8 pc2conf[2];
|
||||
};
|
||||
|
||||
static int
|
||||
nvkm_dp_train_sense(struct lt_state *lt, bool pc, u32 delay)
|
||||
{
|
||||
struct nvkm_dp *dp = lt->dp;
|
||||
int ret;
|
||||
|
||||
if (dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL])
|
||||
mdelay(dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4);
|
||||
else
|
||||
udelay(delay);
|
||||
|
||||
ret = nvkm_rdaux(dp->aux, DPCD_LS02, lt->stat, 6);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pc) {
|
||||
ret = nvkm_rdaux(dp->aux, DPCD_LS0C, <->pc2stat, 1);
|
||||
if (ret)
|
||||
lt->pc2stat = 0x00;
|
||||
OUTP_TRACE(&dp->outp, "status %6ph pc2 %02x",
|
||||
lt->stat, lt->pc2stat);
|
||||
} else {
|
||||
OUTP_TRACE(&dp->outp, "status %6ph", lt->stat);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_dp_train_drive(struct lt_state *lt, bool pc)
|
||||
{
|
||||
struct nvkm_dp *dp = lt->dp;
|
||||
struct nvkm_ior *ior = dp->outp.ior;
|
||||
struct nvkm_bios *bios = ior->disp->engine.subdev.device->bios;
|
||||
struct nvbios_dpout info;
|
||||
struct nvbios_dpcfg ocfg;
|
||||
u8 ver, hdr, cnt, len;
|
||||
u32 data;
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < ior->dp.nr; i++) {
|
||||
u8 lane = (lt->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
|
||||
u8 lpc2 = (lt->pc2stat >> (i * 2)) & 0x3;
|
||||
u8 lpre = (lane & 0x0c) >> 2;
|
||||
u8 lvsw = (lane & 0x03) >> 0;
|
||||
u8 hivs = 3 - lpre;
|
||||
u8 hipe = 3;
|
||||
u8 hipc = 3;
|
||||
|
||||
if (lpc2 >= hipc)
|
||||
lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED;
|
||||
if (lpre >= hipe) {
|
||||
lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */
|
||||
lvsw = hivs = 3 - (lpre & 3);
|
||||
} else
|
||||
if (lvsw >= hivs) {
|
||||
lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED;
|
||||
}
|
||||
|
||||
lt->conf[i] = (lpre << 3) | lvsw;
|
||||
lt->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4);
|
||||
|
||||
OUTP_TRACE(&dp->outp, "config lane %d %02x %02x",
|
||||
i, lt->conf[i], lpc2);
|
||||
|
||||
data = nvbios_dpout_match(bios, dp->outp.info.hasht,
|
||||
dp->outp.info.hashm,
|
||||
&ver, &hdr, &cnt, &len, &info);
|
||||
if (!data)
|
||||
continue;
|
||||
|
||||
data = nvbios_dpcfg_match(bios, data, lpc2 & 3, lvsw & 3,
|
||||
lpre & 3, &ver, &hdr, &cnt, &len,
|
||||
&ocfg);
|
||||
if (!data)
|
||||
continue;
|
||||
|
||||
ior->func->dp.drive(ior, i, ocfg.pc, ocfg.dc,
|
||||
ocfg.pe, ocfg.tx_pu);
|
||||
}
|
||||
|
||||
ret = nvkm_wraux(dp->aux, DPCD_LC03(0), lt->conf, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pc) {
|
||||
ret = nvkm_wraux(dp->aux, DPCD_LC0F, lt->pc2conf, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
nvkm_dp_train_pattern(struct lt_state *lt, u8 pattern)
|
||||
{
|
||||
struct nvkm_dp *dp = lt->dp;
|
||||
u8 sink_tp;
|
||||
|
||||
OUTP_TRACE(&dp->outp, "training pattern %d", pattern);
|
||||
dp->outp.ior->func->dp.pattern(dp->outp.ior, pattern);
|
||||
|
||||
nvkm_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1);
|
||||
sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET;
|
||||
sink_tp |= pattern;
|
||||
nvkm_wraux(dp->aux, DPCD_LC02, &sink_tp, 1);
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_dp_train_eq(struct lt_state *lt)
|
||||
{
|
||||
bool eq_done = false, cr_done = true;
|
||||
int tries = 0, i;
|
||||
|
||||
if (lt->dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED)
|
||||
nvkm_dp_train_pattern(lt, 3);
|
||||
else
|
||||
nvkm_dp_train_pattern(lt, 2);
|
||||
|
||||
do {
|
||||
if ((tries &&
|
||||
nvkm_dp_train_drive(lt, lt->pc2)) ||
|
||||
nvkm_dp_train_sense(lt, lt->pc2, 400))
|
||||
break;
|
||||
|
||||
eq_done = !!(lt->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE);
|
||||
for (i = 0; i < lt->dp->outp.ior->dp.nr && eq_done; i++) {
|
||||
u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
|
||||
if (!(lane & DPCD_LS02_LANE0_CR_DONE))
|
||||
cr_done = false;
|
||||
if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
|
||||
!(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED))
|
||||
eq_done = false;
|
||||
}
|
||||
} while (!eq_done && cr_done && ++tries <= 5);
|
||||
|
||||
return eq_done ? 0 : -1;
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_dp_train_cr(struct lt_state *lt)
|
||||
{
|
||||
bool cr_done = false, abort = false;
|
||||
int voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
|
||||
int tries = 0, i;
|
||||
|
||||
nvkm_dp_train_pattern(lt, 1);
|
||||
|
||||
do {
|
||||
if (nvkm_dp_train_drive(lt, false) ||
|
||||
nvkm_dp_train_sense(lt, false, 100))
|
||||
break;
|
||||
|
||||
cr_done = true;
|
||||
for (i = 0; i < lt->dp->outp.ior->dp.nr; i++) {
|
||||
u8 lane = (lt->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
|
||||
if (!(lane & DPCD_LS02_LANE0_CR_DONE)) {
|
||||
cr_done = false;
|
||||
if (lt->conf[i] & DPCD_LC03_MAX_SWING_REACHED)
|
||||
abort = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) {
|
||||
voltage = lt->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
|
||||
tries = 0;
|
||||
}
|
||||
} while (!cr_done && !abort && ++tries < 5);
|
||||
|
||||
return cr_done ? 0 : -1;
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_dp_train_links(struct nvkm_dp *dp)
|
||||
{
|
||||
struct nvkm_ior *ior = dp->outp.ior;
|
||||
struct nvkm_disp *disp = dp->outp.disp;
|
||||
struct nvkm_subdev *subdev = &disp->engine.subdev;
|
||||
struct nvkm_bios *bios = subdev->device->bios;
|
||||
struct lt_state lt = {
|
||||
.dp = dp,
|
||||
};
|
||||
u32 lnkcmp;
|
||||
u8 sink[2];
|
||||
int ret;
|
||||
|
||||
OUTP_DBG(&dp->outp, "training %d x %d MB/s",
|
||||
ior->dp.nr, ior->dp.bw * 27);
|
||||
|
||||
/* Intersect misc. capabilities of the OR and sink. */
|
||||
if (disp->engine.subdev.device->chipset < 0xd0)
|
||||
dp->dpcd[DPCD_RC02] &= ~DPCD_RC02_TPS3_SUPPORTED;
|
||||
lt.pc2 = dp->dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED;
|
||||
|
||||
/* Set desired link configuration on the source. */
|
||||
if ((lnkcmp = lt.dp->info.lnkcmp)) {
|
||||
if (dp->version < 0x30) {
|
||||
while ((ior->dp.bw * 2700) < nvbios_rd16(bios, lnkcmp))
|
||||
lnkcmp += 4;
|
||||
lnkcmp = nvbios_rd16(bios, lnkcmp + 2);
|
||||
} else {
|
||||
while (ior->dp.bw < nvbios_rd08(bios, lnkcmp))
|
||||
lnkcmp += 3;
|
||||
lnkcmp = nvbios_rd16(bios, lnkcmp + 1);
|
||||
}
|
||||
|
||||
nvbios_init(subdev, lnkcmp,
|
||||
init.outp = &dp->outp.info;
|
||||
init.or = ior->id;
|
||||
init.link = ior->asy.link;
|
||||
);
|
||||
}
|
||||
|
||||
ret = ior->func->dp.links(ior, dp->aux);
|
||||
if (ret) {
|
||||
if (ret < 0) {
|
||||
OUTP_ERR(&dp->outp, "train failed with %d", ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ior->func->dp.power(ior, ior->dp.nr);
|
||||
|
||||
/* Set desired link configuration on the sink. */
|
||||
sink[0] = ior->dp.bw;
|
||||
sink[1] = ior->dp.nr;
|
||||
if (ior->dp.ef)
|
||||
sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
|
||||
|
||||
ret = nvkm_wraux(dp->aux, DPCD_LC00_LINK_BW_SET, sink, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Attempt to train the link in this configuration. */
|
||||
memset(lt.stat, 0x00, sizeof(lt.stat));
|
||||
ret = nvkm_dp_train_cr(<);
|
||||
if (ret == 0)
|
||||
ret = nvkm_dp_train_eq(<);
|
||||
nvkm_dp_train_pattern(<, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
nvkm_dp_train_fini(struct nvkm_dp *dp)
|
||||
{
|
||||
/* Execute AfterLinkTraining script from DP Info table. */
|
||||
nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[1],
|
||||
init.outp = &dp->outp.info;
|
||||
init.or = dp->outp.ior->id;
|
||||
init.link = dp->outp.ior->asy.link;
|
||||
);
|
||||
}
|
||||
|
||||
static void
|
||||
nvkm_dp_train_init(struct nvkm_dp *dp)
|
||||
{
|
||||
/* Execute EnableSpread/DisableSpread script from DP Info table. */
|
||||
if (dp->dpcd[DPCD_RC03] & DPCD_RC03_MAX_DOWNSPREAD) {
|
||||
nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[2],
|
||||
init.outp = &dp->outp.info;
|
||||
init.or = dp->outp.ior->id;
|
||||
init.link = dp->outp.ior->asy.link;
|
||||
);
|
||||
} else {
|
||||
nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[3],
|
||||
init.outp = &dp->outp.info;
|
||||
init.or = dp->outp.ior->id;
|
||||
init.link = dp->outp.ior->asy.link;
|
||||
);
|
||||
}
|
||||
|
||||
/* Execute BeforeLinkTraining script from DP Info table. */
|
||||
nvbios_init(&dp->outp.disp->engine.subdev, dp->info.script[0],
|
||||
init.outp = &dp->outp.info;
|
||||
init.or = dp->outp.ior->id;
|
||||
init.link = dp->outp.ior->asy.link;
|
||||
);
|
||||
}
|
||||
|
||||
static const struct dp_rates {
|
||||
u32 rate;
|
||||
u8 bw;
|
||||
u8 nr;
|
||||
} nvkm_dp_rates[] = {
|
||||
{ 2160000, 0x14, 4 },
|
||||
{ 1080000, 0x0a, 4 },
|
||||
{ 1080000, 0x14, 2 },
|
||||
{ 648000, 0x06, 4 },
|
||||
{ 540000, 0x0a, 2 },
|
||||
{ 540000, 0x14, 1 },
|
||||
{ 324000, 0x06, 2 },
|
||||
{ 270000, 0x0a, 1 },
|
||||
{ 162000, 0x06, 1 },
|
||||
{}
|
||||
};
|
||||
|
||||
static int
|
||||
nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps)
|
||||
{
|
||||
struct nvkm_ior *ior = dp->outp.ior;
|
||||
const u8 sink_nr = dp->dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT;
|
||||
const u8 sink_bw = dp->dpcd[DPCD_RC01_MAX_LINK_RATE];
|
||||
const u8 outp_nr = dp->outp.info.dpconf.link_nr;
|
||||
const u8 outp_bw = dp->outp.info.dpconf.link_bw;
|
||||
const struct dp_rates *failsafe = NULL, *cfg;
|
||||
int ret = -EINVAL;
|
||||
u8 pwr;
|
||||
|
||||
/* Find the lowest configuration of the OR that can support
|
||||
* the required link rate.
|
||||
*
|
||||
* We will refuse to program the OR to lower rates, even if
|
||||
* link training fails at higher rates (or even if the sink
|
||||
* can't support the rate at all, though the DD is supposed
|
||||
* to prevent such situations from happening).
|
||||
*
|
||||
* Attempting to do so can cause the entire display to hang,
|
||||
* and it's better to have a failed modeset than that.
|
||||
*/
|
||||
for (cfg = nvkm_dp_rates; cfg->rate; cfg++) {
|
||||
if (cfg->nr <= outp_nr && cfg->nr <= outp_bw)
|
||||
failsafe = cfg;
|
||||
if (failsafe && cfg[1].rate < dataKBps)
|
||||
break;
|
||||
}
|
||||
|
||||
if (WARN_ON(!failsafe))
|
||||
return ret;
|
||||
|
||||
/* Ensure sink is not in a low-power state. */
|
||||
if (!nvkm_rdaux(dp->aux, DPCD_SC00, &pwr, 1)) {
|
||||
if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) {
|
||||
pwr &= ~DPCD_SC00_SET_POWER;
|
||||
pwr |= DPCD_SC00_SET_POWER_D0;
|
||||
nvkm_wraux(dp->aux, DPCD_SC00, &pwr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Link training. */
|
||||
OUTP_DBG(&dp->outp, "training (min: %d x %d MB/s)",
|
||||
failsafe->nr, failsafe->bw * 27);
|
||||
nvkm_dp_train_init(dp);
|
||||
for (cfg = nvkm_dp_rates; ret < 0 && cfg <= failsafe; cfg++) {
|
||||
/* Skip configurations not supported by both OR and sink. */
|
||||
if ((cfg->nr > outp_nr || cfg->bw > outp_bw ||
|
||||
cfg->nr > sink_nr || cfg->bw > sink_bw)) {
|
||||
if (cfg != failsafe)
|
||||
continue;
|
||||
OUTP_ERR(&dp->outp, "link rate unsupported by sink");
|
||||
}
|
||||
ior->dp.mst = dp->lt.mst;
|
||||
ior->dp.ef = dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP;
|
||||
ior->dp.bw = cfg->bw;
|
||||
ior->dp.nr = cfg->nr;
|
||||
|
||||
/* Program selected link configuration. */
|
||||
ret = nvkm_dp_train_links(dp);
|
||||
}
|
||||
nvkm_dp_train_fini(dp);
|
||||
if (ret < 0)
|
||||
OUTP_ERR(&dp->outp, "training failed");
|
||||
else
|
||||
OUTP_DBG(&dp->outp, "training done");
|
||||
atomic_set(&dp->lt.done, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
nvkm_dp_release(struct nvkm_outp *outp, struct nvkm_ior *ior)
|
||||
{
|
||||
struct nvkm_dp *dp = nvkm_dp(outp);
|
||||
|
||||
/* Prevent link from being retrained if sink sends an IRQ. */
|
||||
atomic_set(&dp->lt.done, 0);
|
||||
ior->dp.nr = 0;
|
||||
|
||||
/* Execute DisableLT script from DP Info Table. */
|
||||
nvbios_init(&ior->disp->engine.subdev, dp->info.script[4],
|
||||
init.outp = &dp->outp.info;
|
||||
init.or = ior->id;
|
||||
init.link = ior->arm.link;
|
||||
);
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_dp_acquire(struct nvkm_outp *outp)
|
||||
{
|
||||
struct nvkm_dp *dp = nvkm_dp(outp);
|
||||
struct nvkm_ior *ior = dp->outp.ior;
|
||||
struct nvkm_head *head;
|
||||
bool retrain = true;
|
||||
u32 datakbps = 0;
|
||||
u32 dataKBps;
|
||||
u32 linkKBps;
|
||||
u8 stat[3];
|
||||
int ret, i;
|
||||
|
||||
mutex_lock(&dp->mutex);
|
||||
|
||||
/* Check that link configuration meets current requirements. */
|
||||
list_for_each_entry(head, &outp->disp->head, head) {
|
||||
if (ior->asy.head & (1 << head->id)) {
|
||||
u32 khz = (head->asy.hz >> ior->asy.rgdiv) / 1000;
|
||||
datakbps += khz * head->asy.or.depth;
|
||||
}
|
||||
}
|
||||
|
||||
linkKBps = ior->dp.bw * 27000 * ior->dp.nr;
|
||||
dataKBps = DIV_ROUND_UP(datakbps, 8);
|
||||
OUTP_DBG(&dp->outp, "data %d KB/s link %d KB/s mst %d->%d",
|
||||
dataKBps, linkKBps, ior->dp.mst, dp->lt.mst);
|
||||
if (linkKBps < dataKBps || ior->dp.mst != dp->lt.mst) {
|
||||
OUTP_DBG(&dp->outp, "link requirements changed");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Check that link is still trained. */
|
||||
ret = nvkm_rdaux(dp->aux, DPCD_LS02, stat, 3);
|
||||
if (ret) {
|
||||
OUTP_DBG(&dp->outp,
|
||||
"failed to read link status, assuming no sink");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) {
|
||||
for (i = 0; i < ior->dp.nr; i++) {
|
||||
u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f;
|
||||
if (!(lane & DPCD_LS02_LANE0_CR_DONE) ||
|
||||
!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
|
||||
!(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) {
|
||||
OUTP_DBG(&dp->outp,
|
||||
"lane %d not equalised", lane);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
retrain = false;
|
||||
} else {
|
||||
OUTP_DBG(&dp->outp, "no inter-lane alignment");
|
||||
}
|
||||
|
||||
done:
|
||||
if (retrain || !atomic_read(&dp->lt.done))
|
||||
ret = nvkm_dp_train(dp, dataKBps);
|
||||
mutex_unlock(&dp->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
nvkm_dp_enable(struct nvkm_dp *dp, bool enable)
|
||||
{
|
||||
struct nvkm_i2c_aux *aux = dp->aux;
|
||||
|
||||
if (enable) {
|
||||
if (!dp->present) {
|
||||
OUTP_DBG(&dp->outp, "aux power -> always");
|
||||
nvkm_i2c_aux_monitor(aux, true);
|
||||
dp->present = true;
|
||||
}
|
||||
|
||||
if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, dp->dpcd,
|
||||
sizeof(dp->dpcd)))
|
||||
return;
|
||||
}
|
||||
|
||||
if (dp->present) {
|
||||
OUTP_DBG(&dp->outp, "aux power -> demand");
|
||||
nvkm_i2c_aux_monitor(aux, false);
|
||||
dp->present = false;
|
||||
}
|
||||
|
||||
atomic_set(&dp->lt.done, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_dp_hpd(struct nvkm_notify *notify)
|
||||
{
|
||||
const struct nvkm_i2c_ntfy_rep *line = notify->data;
|
||||
struct nvkm_dp *dp = container_of(notify, typeof(*dp), hpd);
|
||||
struct nvkm_conn *conn = dp->outp.conn;
|
||||
struct nvkm_disp *disp = dp->outp.disp;
|
||||
struct nvif_notify_conn_rep_v0 rep = {};
|
||||
|
||||
OUTP_DBG(&dp->outp, "HPD: %d", line->mask);
|
||||
if (line->mask & NVKM_I2C_IRQ) {
|
||||
if (atomic_read(&dp->lt.done))
|
||||
dp->outp.func->acquire(&dp->outp);
|
||||
rep.mask |= NVIF_NOTIFY_CONN_V0_IRQ;
|
||||
} else {
|
||||
nvkm_dp_enable(dp, true);
|
||||
}
|
||||
|
||||
if (line->mask & NVKM_I2C_UNPLUG)
|
||||
rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG;
|
||||
if (line->mask & NVKM_I2C_PLUG)
|
||||
rep.mask |= NVIF_NOTIFY_CONN_V0_PLUG;
|
||||
|
||||
nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep));
|
||||
return NVKM_NOTIFY_KEEP;
|
||||
}
|
||||
|
||||
static void
|
||||
nvkm_dp_fini(struct nvkm_outp *outp)
|
||||
{
|
||||
struct nvkm_dp *dp = nvkm_dp(outp);
|
||||
nvkm_notify_put(&dp->hpd);
|
||||
nvkm_dp_enable(dp, false);
|
||||
}
|
||||
|
||||
static void
|
||||
nvkm_dp_init(struct nvkm_outp *outp)
|
||||
{
|
||||
struct nvkm_dp *dp = nvkm_dp(outp);
|
||||
nvkm_notify_put(&dp->outp.conn->hpd);
|
||||
nvkm_dp_enable(dp, true);
|
||||
nvkm_notify_get(&dp->hpd);
|
||||
}
|
||||
|
||||
static void *
|
||||
nvkm_dp_dtor(struct nvkm_outp *outp)
|
||||
{
|
||||
struct nvkm_dp *dp = nvkm_dp(outp);
|
||||
nvkm_notify_fini(&dp->hpd);
|
||||
return dp;
|
||||
}
|
||||
|
||||
static const struct nvkm_outp_func
|
||||
nvkm_dp_func = {
|
||||
.dtor = nvkm_dp_dtor,
|
||||
.init = nvkm_dp_init,
|
||||
.fini = nvkm_dp_fini,
|
||||
.acquire = nvkm_dp_acquire,
|
||||
.release = nvkm_dp_release,
|
||||
};
|
||||
|
||||
static int
|
||||
nvkm_dp_ctor(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
|
||||
struct nvkm_i2c_aux *aux, struct nvkm_dp *dp)
|
||||
{
|
||||
struct nvkm_device *device = disp->engine.subdev.device;
|
||||
struct nvkm_bios *bios = device->bios;
|
||||
struct nvkm_i2c *i2c = device->i2c;
|
||||
u8 hdr, cnt, len;
|
||||
u32 data;
|
||||
int ret;
|
||||
|
||||
ret = nvkm_outp_ctor(&nvkm_dp_func, disp, index, dcbE, &dp->outp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dp->aux = aux;
|
||||
if (!dp->aux) {
|
||||
OUTP_ERR(&dp->outp, "no aux");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* bios data is not optional */
|
||||
data = nvbios_dpout_match(bios, dp->outp.info.hasht,
|
||||
dp->outp.info.hashm, &dp->version,
|
||||
&hdr, &cnt, &len, &dp->info);
|
||||
if (!data) {
|
||||
OUTP_ERR(&dp->outp, "no bios dp data");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
OUTP_DBG(&dp->outp, "bios dp %02x %02x %02x %02x",
|
||||
dp->version, hdr, cnt, len);
|
||||
|
||||
/* hotplug detect, replaces gpio-based mechanism with aux events */
|
||||
ret = nvkm_notify_init(NULL, &i2c->event, nvkm_dp_hpd, true,
|
||||
&(struct nvkm_i2c_ntfy_req) {
|
||||
.mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG |
|
||||
NVKM_I2C_IRQ,
|
||||
.port = dp->aux->id,
|
||||
},
|
||||
sizeof(struct nvkm_i2c_ntfy_req),
|
||||
sizeof(struct nvkm_i2c_ntfy_rep),
|
||||
&dp->hpd);
|
||||
if (ret) {
|
||||
OUTP_ERR(&dp->outp, "error monitoring aux hpd: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_init(&dp->mutex);
|
||||
atomic_set(&dp->lt.done, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
|
||||
struct nvkm_outp **poutp)
|
||||
{
|
||||
struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c;
|
||||
struct nvkm_i2c_aux *aux;
|
||||
struct nvkm_dp *dp;
|
||||
|
||||
if (dcbE->location == 0)
|
||||
aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_CCB(dcbE->i2c_index));
|
||||
else
|
||||
aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbE->extdev));
|
||||
|
||||
if (!(dp = kzalloc(sizeof(*dp), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
*poutp = &dp->outp;
|
||||
|
||||
return nvkm_dp_ctor(disp, index, dcbE, aux, dp);
|
||||
}
|
|
@ -1,6 +1,36 @@
|
|||
#ifndef __NVKM_DISP_DPORT_H__
|
||||
#define __NVKM_DISP_DPORT_H__
|
||||
struct nvkm_output_dp;
|
||||
#ifndef __NVKM_DISP_DP_H__
|
||||
#define __NVKM_DISP_DP_H__
|
||||
#define nvkm_dp(p) container_of((p), struct nvkm_dp, outp)
|
||||
#include "outp.h"
|
||||
|
||||
#include <core/notify.h>
|
||||
#include <subdev/bios.h>
|
||||
#include <subdev/bios/dp.h>
|
||||
|
||||
struct nvkm_dp {
|
||||
union {
|
||||
struct nvkm_outp base;
|
||||
struct nvkm_outp outp;
|
||||
};
|
||||
|
||||
struct nvbios_dpout info;
|
||||
u8 version;
|
||||
|
||||
struct nvkm_i2c_aux *aux;
|
||||
|
||||
struct nvkm_notify hpd;
|
||||
bool present;
|
||||
u8 dpcd[16];
|
||||
|
||||
struct mutex mutex;
|
||||
struct {
|
||||
atomic_t done;
|
||||
bool mst;
|
||||
} lt;
|
||||
};
|
||||
|
||||
int nvkm_dp_new(struct nvkm_disp *, int index, struct dcb_output *,
|
||||
struct nvkm_outp **);
|
||||
|
||||
/* DPCD Receiver Capabilities */
|
||||
#define DPCD_RC00_DPCD_REV 0x00000
|
||||
|
@ -76,6 +106,4 @@ struct nvkm_output_dp;
|
|||
#define DPCD_SC00_SET_POWER 0x03
|
||||
#define DPCD_SC00_SET_POWER_D0 0x01
|
||||
#define DPCD_SC00_SET_POWER_D3 0x03
|
||||
|
||||
void nvkm_dp_train(struct nvkm_output_dp *);
|
||||
#endif
|
|
@ -1,401 +0,0 @@
|
|||
/*
|
||||
* Copyright 2013 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "dport.h"
|
||||
#include "outpdp.h"
|
||||
#include "nv50.h"
|
||||
|
||||
#include <subdev/bios.h>
|
||||
#include <subdev/bios/init.h>
|
||||
#include <subdev/i2c.h>
|
||||
|
||||
#include <nvif/class.h>
|
||||
|
||||
/******************************************************************************
|
||||
* link training
|
||||
*****************************************************************************/
|
||||
struct dp_state {
|
||||
struct nvkm_output_dp *outp;
|
||||
int link_nr;
|
||||
u32 link_bw;
|
||||
u8 stat[6];
|
||||
u8 conf[4];
|
||||
bool pc2;
|
||||
u8 pc2stat;
|
||||
u8 pc2conf[2];
|
||||
};
|
||||
|
||||
static int
|
||||
dp_set_link_config(struct dp_state *dp)
|
||||
{
|
||||
struct nvkm_output_dp *outp = dp->outp;
|
||||
struct nvkm_disp *disp = outp->base.disp;
|
||||
struct nvkm_subdev *subdev = &disp->engine.subdev;
|
||||
struct nvkm_bios *bios = subdev->device->bios;
|
||||
struct nvbios_init init = {
|
||||
.subdev = subdev,
|
||||
.bios = bios,
|
||||
.offset = 0x0000,
|
||||
.outp = &outp->base.info,
|
||||
.crtc = -1,
|
||||
.execute = 1,
|
||||
};
|
||||
u32 lnkcmp;
|
||||
u8 sink[2];
|
||||
int ret;
|
||||
|
||||
OUTP_DBG(&outp->base, "%d lanes at %d KB/s", dp->link_nr, dp->link_bw);
|
||||
|
||||
/* set desired link configuration on the source */
|
||||
if ((lnkcmp = dp->outp->info.lnkcmp)) {
|
||||
if (outp->version < 0x30) {
|
||||
while ((dp->link_bw / 10) < nvbios_rd16(bios, lnkcmp))
|
||||
lnkcmp += 4;
|
||||
init.offset = nvbios_rd16(bios, lnkcmp + 2);
|
||||
} else {
|
||||
while ((dp->link_bw / 27000) < nvbios_rd08(bios, lnkcmp))
|
||||
lnkcmp += 3;
|
||||
init.offset = nvbios_rd16(bios, lnkcmp + 1);
|
||||
}
|
||||
|
||||
nvbios_exec(&init);
|
||||
}
|
||||
|
||||
ret = outp->func->lnk_ctl(outp, dp->link_nr, dp->link_bw / 27000,
|
||||
outp->dpcd[DPCD_RC02] &
|
||||
DPCD_RC02_ENHANCED_FRAME_CAP);
|
||||
if (ret) {
|
||||
if (ret < 0)
|
||||
OUTP_ERR(&outp->base, "lnk_ctl failed with %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
outp->func->lnk_pwr(outp, dp->link_nr);
|
||||
|
||||
/* set desired link configuration on the sink */
|
||||
sink[0] = dp->link_bw / 27000;
|
||||
sink[1] = dp->link_nr;
|
||||
if (outp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
|
||||
sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
|
||||
|
||||
return nvkm_wraux(outp->aux, DPCD_LC00_LINK_BW_SET, sink, 2);
|
||||
}
|
||||
|
||||
static void
|
||||
dp_set_training_pattern(struct dp_state *dp, u8 pattern)
|
||||
{
|
||||
struct nvkm_output_dp *outp = dp->outp;
|
||||
u8 sink_tp;
|
||||
|
||||
OUTP_DBG(&outp->base, "training pattern %d", pattern);
|
||||
outp->func->pattern(outp, pattern);
|
||||
|
||||
nvkm_rdaux(outp->aux, DPCD_LC02, &sink_tp, 1);
|
||||
sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET;
|
||||
sink_tp |= pattern;
|
||||
nvkm_wraux(outp->aux, DPCD_LC02, &sink_tp, 1);
|
||||
}
|
||||
|
||||
static int
|
||||
dp_link_train_commit(struct dp_state *dp, bool pc)
|
||||
{
|
||||
struct nvkm_output_dp *outp = dp->outp;
|
||||
int ret, i;
|
||||
|
||||
for (i = 0; i < dp->link_nr; i++) {
|
||||
u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
|
||||
u8 lpc2 = (dp->pc2stat >> (i * 2)) & 0x3;
|
||||
u8 lpre = (lane & 0x0c) >> 2;
|
||||
u8 lvsw = (lane & 0x03) >> 0;
|
||||
u8 hivs = 3 - lpre;
|
||||
u8 hipe = 3;
|
||||
u8 hipc = 3;
|
||||
|
||||
if (lpc2 >= hipc)
|
||||
lpc2 = hipc | DPCD_LC0F_LANE0_MAX_POST_CURSOR2_REACHED;
|
||||
if (lpre >= hipe) {
|
||||
lpre = hipe | DPCD_LC03_MAX_SWING_REACHED; /* yes. */
|
||||
lvsw = hivs = 3 - (lpre & 3);
|
||||
} else
|
||||
if (lvsw >= hivs) {
|
||||
lvsw = hivs | DPCD_LC03_MAX_SWING_REACHED;
|
||||
}
|
||||
|
||||
dp->conf[i] = (lpre << 3) | lvsw;
|
||||
dp->pc2conf[i >> 1] |= lpc2 << ((i & 1) * 4);
|
||||
|
||||
OUTP_DBG(&outp->base, "config lane %d %02x %02x",
|
||||
i, dp->conf[i], lpc2);
|
||||
outp->func->drv_ctl(outp, i, lvsw & 3, lpre & 3, lpc2 & 3);
|
||||
}
|
||||
|
||||
ret = nvkm_wraux(outp->aux, DPCD_LC03(0), dp->conf, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pc) {
|
||||
ret = nvkm_wraux(outp->aux, DPCD_LC0F, dp->pc2conf, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dp_link_train_update(struct dp_state *dp, bool pc, u32 delay)
|
||||
{
|
||||
struct nvkm_output_dp *outp = dp->outp;
|
||||
int ret;
|
||||
|
||||
if (outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL])
|
||||
mdelay(outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4);
|
||||
else
|
||||
udelay(delay);
|
||||
|
||||
ret = nvkm_rdaux(outp->aux, DPCD_LS02, dp->stat, 6);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pc) {
|
||||
ret = nvkm_rdaux(outp->aux, DPCD_LS0C, &dp->pc2stat, 1);
|
||||
if (ret)
|
||||
dp->pc2stat = 0x00;
|
||||
OUTP_DBG(&outp->base, "status %6ph pc2 %02x",
|
||||
dp->stat, dp->pc2stat);
|
||||
} else {
|
||||
OUTP_DBG(&outp->base, "status %6ph", dp->stat);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
dp_link_train_cr(struct dp_state *dp)
|
||||
{
|
||||
bool cr_done = false, abort = false;
|
||||
int voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
|
||||
int tries = 0, i;
|
||||
|
||||
dp_set_training_pattern(dp, 1);
|
||||
|
||||
do {
|
||||
if (dp_link_train_commit(dp, false) ||
|
||||
dp_link_train_update(dp, false, 100))
|
||||
break;
|
||||
|
||||
cr_done = true;
|
||||
for (i = 0; i < dp->link_nr; i++) {
|
||||
u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
|
||||
if (!(lane & DPCD_LS02_LANE0_CR_DONE)) {
|
||||
cr_done = false;
|
||||
if (dp->conf[i] & DPCD_LC03_MAX_SWING_REACHED)
|
||||
abort = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET) != voltage) {
|
||||
voltage = dp->conf[0] & DPCD_LC03_VOLTAGE_SWING_SET;
|
||||
tries = 0;
|
||||
}
|
||||
} while (!cr_done && !abort && ++tries < 5);
|
||||
|
||||
return cr_done ? 0 : -1;
|
||||
}
|
||||
|
||||
static int
|
||||
dp_link_train_eq(struct dp_state *dp)
|
||||
{
|
||||
struct nvkm_output_dp *outp = dp->outp;
|
||||
bool eq_done = false, cr_done = true;
|
||||
int tries = 0, i;
|
||||
|
||||
if (outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED)
|
||||
dp_set_training_pattern(dp, 3);
|
||||
else
|
||||
dp_set_training_pattern(dp, 2);
|
||||
|
||||
do {
|
||||
if ((tries &&
|
||||
dp_link_train_commit(dp, dp->pc2)) ||
|
||||
dp_link_train_update(dp, dp->pc2, 400))
|
||||
break;
|
||||
|
||||
eq_done = !!(dp->stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE);
|
||||
for (i = 0; i < dp->link_nr && eq_done; i++) {
|
||||
u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf;
|
||||
if (!(lane & DPCD_LS02_LANE0_CR_DONE))
|
||||
cr_done = false;
|
||||
if (!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
|
||||
!(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED))
|
||||
eq_done = false;
|
||||
}
|
||||
} while (!eq_done && cr_done && ++tries <= 5);
|
||||
|
||||
return eq_done ? 0 : -1;
|
||||
}
|
||||
|
||||
static void
|
||||
dp_link_train_init(struct dp_state *dp, bool spread)
|
||||
{
|
||||
struct nvkm_output_dp *outp = dp->outp;
|
||||
struct nvkm_disp *disp = outp->base.disp;
|
||||
struct nvkm_subdev *subdev = &disp->engine.subdev;
|
||||
struct nvbios_init init = {
|
||||
.subdev = subdev,
|
||||
.bios = subdev->device->bios,
|
||||
.outp = &outp->base.info,
|
||||
.crtc = -1,
|
||||
.execute = 1,
|
||||
};
|
||||
|
||||
/* set desired spread */
|
||||
if (spread)
|
||||
init.offset = outp->info.script[2];
|
||||
else
|
||||
init.offset = outp->info.script[3];
|
||||
nvbios_exec(&init);
|
||||
|
||||
/* pre-train script */
|
||||
init.offset = outp->info.script[0];
|
||||
nvbios_exec(&init);
|
||||
}
|
||||
|
||||
static void
|
||||
dp_link_train_fini(struct dp_state *dp)
|
||||
{
|
||||
struct nvkm_output_dp *outp = dp->outp;
|
||||
struct nvkm_disp *disp = outp->base.disp;
|
||||
struct nvkm_subdev *subdev = &disp->engine.subdev;
|
||||
struct nvbios_init init = {
|
||||
.subdev = subdev,
|
||||
.bios = subdev->device->bios,
|
||||
.outp = &outp->base.info,
|
||||
.crtc = -1,
|
||||
.execute = 1,
|
||||
};
|
||||
|
||||
/* post-train script */
|
||||
init.offset = outp->info.script[1],
|
||||
nvbios_exec(&init);
|
||||
}
|
||||
|
||||
static const struct dp_rates {
|
||||
u32 rate;
|
||||
u8 bw;
|
||||
u8 nr;
|
||||
} nvkm_dp_rates[] = {
|
||||
{ 2160000, 0x14, 4 },
|
||||
{ 1080000, 0x0a, 4 },
|
||||
{ 1080000, 0x14, 2 },
|
||||
{ 648000, 0x06, 4 },
|
||||
{ 540000, 0x0a, 2 },
|
||||
{ 540000, 0x14, 1 },
|
||||
{ 324000, 0x06, 2 },
|
||||
{ 270000, 0x0a, 1 },
|
||||
{ 162000, 0x06, 1 },
|
||||
{}
|
||||
};
|
||||
|
||||
void
|
||||
nvkm_dp_train(struct nvkm_output_dp *outp)
|
||||
{
|
||||
struct nv50_disp *disp = nv50_disp(outp->base.disp);
|
||||
const struct dp_rates *cfg = nvkm_dp_rates;
|
||||
struct dp_state _dp = {
|
||||
.outp = outp,
|
||||
}, *dp = &_dp;
|
||||
u32 datarate = 0;
|
||||
u8 pwr;
|
||||
int ret;
|
||||
|
||||
if (!outp->base.info.location && disp->func->sor.magic)
|
||||
disp->func->sor.magic(&outp->base);
|
||||
|
||||
/* bring capabilities within encoder limits */
|
||||
if (disp->base.engine.subdev.device->chipset < 0xd0)
|
||||
outp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED;
|
||||
if ((outp->dpcd[2] & 0x1f) > outp->base.info.dpconf.link_nr) {
|
||||
outp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT;
|
||||
outp->dpcd[2] |= outp->base.info.dpconf.link_nr;
|
||||
}
|
||||
if (outp->dpcd[1] > outp->base.info.dpconf.link_bw)
|
||||
outp->dpcd[1] = outp->base.info.dpconf.link_bw;
|
||||
dp->pc2 = outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED;
|
||||
|
||||
/* restrict link config to the lowest required rate, if requested */
|
||||
if (datarate) {
|
||||
datarate = (datarate / 8) * 10; /* 8B/10B coding overhead */
|
||||
while (cfg[1].rate >= datarate)
|
||||
cfg++;
|
||||
}
|
||||
cfg--;
|
||||
|
||||
/* ensure sink is not in a low-power state */
|
||||
if (!nvkm_rdaux(outp->aux, DPCD_SC00, &pwr, 1)) {
|
||||
if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) {
|
||||
pwr &= ~DPCD_SC00_SET_POWER;
|
||||
pwr |= DPCD_SC00_SET_POWER_D0;
|
||||
nvkm_wraux(outp->aux, DPCD_SC00, &pwr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* enable down-spreading and execute pre-train script from vbios */
|
||||
dp_link_train_init(dp, outp->dpcd[3] & 0x01);
|
||||
|
||||
while (ret = -EIO, (++cfg)->rate) {
|
||||
/* select next configuration supported by encoder and sink */
|
||||
while (cfg->nr > (outp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) ||
|
||||
cfg->bw > (outp->dpcd[DPCD_RC01_MAX_LINK_RATE]))
|
||||
cfg++;
|
||||
dp->link_bw = cfg->bw * 27000;
|
||||
dp->link_nr = cfg->nr;
|
||||
|
||||
/* program selected link configuration */
|
||||
ret = dp_set_link_config(dp);
|
||||
if (ret == 0) {
|
||||
/* attempt to train the link at this configuration */
|
||||
memset(dp->stat, 0x00, sizeof(dp->stat));
|
||||
if (!dp_link_train_cr(dp) &&
|
||||
!dp_link_train_eq(dp))
|
||||
break;
|
||||
} else
|
||||
if (ret) {
|
||||
/* dp_set_link_config() handled training, or
|
||||
* we failed to communicate with the sink.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* finish link training and execute post-train script from vbios */
|
||||
dp_set_training_pattern(dp, 0);
|
||||
if (ret < 0)
|
||||
OUTP_ERR(&outp->base, "link training failed");
|
||||
|
||||
dp_link_train_fini(dp);
|
||||
|
||||
OUTP_DBG(&outp->base, "training complete");
|
||||
atomic_set(&outp->lt.done, 1);
|
||||
}
|
|
@ -22,30 +22,20 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
static const struct nv50_disp_func
|
||||
g84_disp = {
|
||||
.intr = nv50_disp_intr,
|
||||
.uevent = &nv50_disp_chan_uevent,
|
||||
.super = nv50_disp_intr_supervisor,
|
||||
.super = nv50_disp_super,
|
||||
.root = &g84_disp_root_oclass,
|
||||
.head.vblank_init = nv50_disp_vblank_init,
|
||||
.head.vblank_fini = nv50_disp_vblank_fini,
|
||||
.head.scanoutpos = nv50_disp_root_scanoutpos,
|
||||
.outp.internal.crt = nv50_dac_output_new,
|
||||
.outp.internal.tmds = nv50_sor_output_new,
|
||||
.outp.internal.lvds = nv50_sor_output_new,
|
||||
.outp.external.tmds = nv50_pior_output_new,
|
||||
.outp.external.dp = nv50_pior_dp_new,
|
||||
.dac.nr = 3,
|
||||
.dac.power = nv50_dac_power,
|
||||
.dac.sense = nv50_dac_sense,
|
||||
.sor.nr = 2,
|
||||
.sor.power = nv50_sor_power,
|
||||
.sor.hdmi = g84_hdmi_ctrl,
|
||||
.pior.nr = 3,
|
||||
.pior.power = nv50_pior_power,
|
||||
.head.new = nv50_head_new,
|
||||
.dac = { .nr = 3, .new = nv50_dac_new },
|
||||
.sor = { .nr = 2, .new = g84_sor_new },
|
||||
.pior = { .nr = 3, .new = nv50_pior_new },
|
||||
};
|
||||
|
||||
int
|
||||
|
|
|
@ -22,31 +22,20 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
static const struct nv50_disp_func
|
||||
g94_disp = {
|
||||
.intr = nv50_disp_intr,
|
||||
.uevent = &nv50_disp_chan_uevent,
|
||||
.super = nv50_disp_intr_supervisor,
|
||||
.super = nv50_disp_super,
|
||||
.root = &g94_disp_root_oclass,
|
||||
.head.vblank_init = nv50_disp_vblank_init,
|
||||
.head.vblank_fini = nv50_disp_vblank_fini,
|
||||
.head.scanoutpos = nv50_disp_root_scanoutpos,
|
||||
.outp.internal.crt = nv50_dac_output_new,
|
||||
.outp.internal.tmds = nv50_sor_output_new,
|
||||
.outp.internal.lvds = nv50_sor_output_new,
|
||||
.outp.internal.dp = g94_sor_dp_new,
|
||||
.outp.external.tmds = nv50_pior_output_new,
|
||||
.outp.external.dp = nv50_pior_dp_new,
|
||||
.dac.nr = 3,
|
||||
.dac.power = nv50_dac_power,
|
||||
.dac.sense = nv50_dac_sense,
|
||||
.sor.nr = 4,
|
||||
.sor.power = nv50_sor_power,
|
||||
.sor.hdmi = g84_hdmi_ctrl,
|
||||
.pior.nr = 3,
|
||||
.pior.power = nv50_pior_power,
|
||||
.head.new = nv50_head_new,
|
||||
.dac = { .nr = 3, .new = nv50_dac_new },
|
||||
.sor = { .nr = 4, .new = g94_sor_new },
|
||||
.pior = { .nr = 3, .new = nv50_pior_new },
|
||||
};
|
||||
|
||||
int
|
||||
|
|
|
@ -22,397 +22,63 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
#include <subdev/bios.h>
|
||||
#include <subdev/bios/disp.h>
|
||||
#include <subdev/bios/init.h>
|
||||
#include <subdev/bios/pll.h>
|
||||
#include <subdev/devinit.h>
|
||||
|
||||
void
|
||||
gf119_disp_vblank_init(struct nv50_disp *disp, int head)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
nvkm_mask(device, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000001);
|
||||
}
|
||||
|
||||
void
|
||||
gf119_disp_vblank_fini(struct nv50_disp *disp, int head)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
nvkm_mask(device, 0x6100c0 + (head * 0x800), 0x00000001, 0x00000000);
|
||||
}
|
||||
|
||||
static struct nvkm_output *
|
||||
exec_lookup(struct nv50_disp *disp, int head, int or, u32 ctrl,
|
||||
u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
|
||||
struct nvbios_outp *info)
|
||||
{
|
||||
struct nvkm_subdev *subdev = &disp->base.engine.subdev;
|
||||
struct nvkm_bios *bios = subdev->device->bios;
|
||||
struct nvkm_output *outp;
|
||||
u16 mask, type;
|
||||
|
||||
if (or < 4) {
|
||||
type = DCB_OUTPUT_ANALOG;
|
||||
mask = 0;
|
||||
} else {
|
||||
or -= 4;
|
||||
switch (ctrl & 0x00000f00) {
|
||||
case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
|
||||
case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
|
||||
case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break;
|
||||
case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break;
|
||||
case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break;
|
||||
case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break;
|
||||
default:
|
||||
nvkm_error(subdev, "unknown SOR mc %08x\n", ctrl);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
mask = 0x00c0 & (mask << 6);
|
||||
mask |= 0x0001 << or;
|
||||
mask |= 0x0100 << head;
|
||||
|
||||
list_for_each_entry(outp, &disp->base.outp, head) {
|
||||
if ((outp->info.hasht & 0xff) == type &&
|
||||
(outp->info.hashm & mask) == mask) {
|
||||
*data = nvbios_outp_match(bios, outp->info.hasht, mask,
|
||||
ver, hdr, cnt, len, info);
|
||||
if (!*data)
|
||||
return NULL;
|
||||
return outp;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct nvkm_output *
|
||||
exec_script(struct nv50_disp *disp, int head, int id)
|
||||
{
|
||||
struct nvkm_subdev *subdev = &disp->base.engine.subdev;
|
||||
struct nvkm_device *device = subdev->device;
|
||||
struct nvkm_bios *bios = device->bios;
|
||||
struct nvkm_output *outp;
|
||||
struct nvbios_outp info;
|
||||
u8 ver, hdr, cnt, len;
|
||||
u32 data, ctrl = 0;
|
||||
int or;
|
||||
|
||||
for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) {
|
||||
ctrl = nvkm_rd32(device, 0x640180 + (or * 0x20));
|
||||
if (ctrl & (1 << head))
|
||||
break;
|
||||
}
|
||||
|
||||
if (or == 8)
|
||||
return NULL;
|
||||
|
||||
outp = exec_lookup(disp, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info);
|
||||
if (outp) {
|
||||
struct nvbios_init init = {
|
||||
.subdev = subdev,
|
||||
.bios = bios,
|
||||
.offset = info.script[id],
|
||||
.outp = &outp->info,
|
||||
.crtc = head,
|
||||
.execute = 1,
|
||||
};
|
||||
|
||||
nvbios_exec(&init);
|
||||
}
|
||||
|
||||
return outp;
|
||||
}
|
||||
|
||||
static struct nvkm_output *
|
||||
exec_clkcmp(struct nv50_disp *disp, int head, int id, u32 pclk, u32 *conf)
|
||||
{
|
||||
struct nvkm_subdev *subdev = &disp->base.engine.subdev;
|
||||
struct nvkm_device *device = subdev->device;
|
||||
struct nvkm_bios *bios = device->bios;
|
||||
struct nvkm_output *outp;
|
||||
struct nvbios_outp info1;
|
||||
struct nvbios_ocfg info2;
|
||||
u8 ver, hdr, cnt, len;
|
||||
u32 data, ctrl = 0;
|
||||
int or;
|
||||
|
||||
for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) {
|
||||
ctrl = nvkm_rd32(device, 0x660180 + (or * 0x20));
|
||||
if (ctrl & (1 << head))
|
||||
break;
|
||||
}
|
||||
|
||||
if (or == 8)
|
||||
return NULL;
|
||||
|
||||
outp = exec_lookup(disp, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info1);
|
||||
if (!outp)
|
||||
return NULL;
|
||||
|
||||
*conf = (ctrl & 0x00000f00) >> 8;
|
||||
switch (outp->info.type) {
|
||||
case DCB_OUTPUT_TMDS:
|
||||
if (*conf == 5)
|
||||
*conf |= 0x0100;
|
||||
break;
|
||||
case DCB_OUTPUT_LVDS:
|
||||
*conf |= disp->sor.lvdsconf;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
data = nvbios_ocfg_match(bios, data, *conf & 0xff, *conf >> 8,
|
||||
&ver, &hdr, &cnt, &len, &info2);
|
||||
if (data && id < 0xff) {
|
||||
data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
|
||||
if (data) {
|
||||
struct nvbios_init init = {
|
||||
.subdev = subdev,
|
||||
.bios = bios,
|
||||
.offset = data,
|
||||
.outp = &outp->info,
|
||||
.crtc = head,
|
||||
.execute = 1,
|
||||
};
|
||||
|
||||
nvbios_exec(&init);
|
||||
}
|
||||
}
|
||||
|
||||
return outp;
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_disp_intr_unk1_0(struct nv50_disp *disp, int head)
|
||||
{
|
||||
exec_script(disp, head, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_disp_intr_unk2_0(struct nv50_disp *disp, int head)
|
||||
{
|
||||
struct nvkm_subdev *subdev = &disp->base.engine.subdev;
|
||||
struct nvkm_output *outp = exec_script(disp, head, 2);
|
||||
|
||||
/* see note in nv50_disp_intr_unk20_0() */
|
||||
if (outp && outp->info.type == DCB_OUTPUT_DP) {
|
||||
struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
|
||||
if (!outpdp->lt.mst) {
|
||||
struct nvbios_init init = {
|
||||
.subdev = subdev,
|
||||
.bios = subdev->device->bios,
|
||||
.outp = &outp->info,
|
||||
.crtc = head,
|
||||
.offset = outpdp->info.script[4],
|
||||
.execute = 1,
|
||||
};
|
||||
|
||||
nvkm_notify_put(&outpdp->irq);
|
||||
nvbios_exec(&init);
|
||||
atomic_set(&outpdp->lt.done, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_disp_intr_unk2_1(struct nv50_disp *disp, int head)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
struct nvkm_devinit *devinit = device->devinit;
|
||||
u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000;
|
||||
if (pclk)
|
||||
nvkm_devinit_pll_set(devinit, PLL_VPLL0 + head, pclk);
|
||||
nvkm_wr32(device, 0x612200 + (head * 0x800), 0x00000000);
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_disp_intr_unk2_2_tu(struct nv50_disp *disp, int head,
|
||||
struct dcb_output *outp)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
const int or = ffs(outp->or) - 1;
|
||||
const u32 ctrl = nvkm_rd32(device, 0x660200 + (or * 0x020));
|
||||
const u32 conf = nvkm_rd32(device, 0x660404 + (head * 0x300));
|
||||
const s32 vactive = nvkm_rd32(device, 0x660414 + (head * 0x300)) & 0xffff;
|
||||
const s32 vblanke = nvkm_rd32(device, 0x66041c + (head * 0x300)) & 0xffff;
|
||||
const s32 vblanks = nvkm_rd32(device, 0x660420 + (head * 0x300)) & 0xffff;
|
||||
const u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000;
|
||||
const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1;
|
||||
const u32 hoff = (head * 0x800);
|
||||
const u32 soff = ( or * 0x800);
|
||||
const u32 loff = (link * 0x080) + soff;
|
||||
const u32 symbol = 100000;
|
||||
const u32 TU = 64;
|
||||
u32 dpctrl = nvkm_rd32(device, 0x61c10c + loff);
|
||||
u32 clksor = nvkm_rd32(device, 0x612300 + soff);
|
||||
u32 datarate, link_nr, link_bw, bits;
|
||||
u64 ratio, value;
|
||||
|
||||
link_nr = hweight32(dpctrl & 0x000f0000);
|
||||
link_bw = (clksor & 0x007c0000) >> 18;
|
||||
link_bw *= 27000;
|
||||
|
||||
/* symbols/hblank - algorithm taken from comments in tegra driver */
|
||||
value = vblanke + vactive - vblanks - 7;
|
||||
value = value * link_bw;
|
||||
do_div(value, pclk);
|
||||
value = value - (3 * !!(dpctrl & 0x00004000)) - (12 / link_nr);
|
||||
nvkm_mask(device, 0x616620 + hoff, 0x0000ffff, value);
|
||||
|
||||
/* symbols/vblank - algorithm taken from comments in tegra driver */
|
||||
value = vblanks - vblanke - 25;
|
||||
value = value * link_bw;
|
||||
do_div(value, pclk);
|
||||
value = value - ((36 / link_nr) + 3) - 1;
|
||||
nvkm_mask(device, 0x616624 + hoff, 0x00ffffff, value);
|
||||
|
||||
/* watermark */
|
||||
if ((conf & 0x3c0) == 0x180) bits = 30;
|
||||
else if ((conf & 0x3c0) == 0x140) bits = 24;
|
||||
else bits = 18;
|
||||
datarate = (pclk * bits) / 8;
|
||||
|
||||
ratio = datarate;
|
||||
ratio *= symbol;
|
||||
do_div(ratio, link_nr * link_bw);
|
||||
|
||||
value = (symbol - ratio) * TU;
|
||||
value *= ratio;
|
||||
do_div(value, symbol);
|
||||
do_div(value, symbol);
|
||||
|
||||
value += 5;
|
||||
value |= 0x08000000;
|
||||
|
||||
nvkm_wr32(device, 0x616610 + hoff, value);
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_disp_intr_unk2_2(struct nv50_disp *disp, int head)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
struct nvkm_output *outp;
|
||||
u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000;
|
||||
u32 conf, addr, data;
|
||||
|
||||
outp = exec_clkcmp(disp, head, 0xff, pclk, &conf);
|
||||
if (!outp)
|
||||
return;
|
||||
|
||||
/* see note in nv50_disp_intr_unk20_2() */
|
||||
if (outp->info.type == DCB_OUTPUT_DP) {
|
||||
u32 sync = nvkm_rd32(device, 0x660404 + (head * 0x300));
|
||||
switch ((sync & 0x000003c0) >> 6) {
|
||||
case 6: pclk = pclk * 30; break;
|
||||
case 5: pclk = pclk * 24; break;
|
||||
case 2:
|
||||
default:
|
||||
pclk = pclk * 18;
|
||||
break;
|
||||
}
|
||||
|
||||
if (nvkm_output_dp_train(outp, pclk))
|
||||
OUTP_ERR(outp, "link not trained before attach");
|
||||
} else {
|
||||
if (disp->func->sor.magic)
|
||||
disp->func->sor.magic(outp);
|
||||
}
|
||||
|
||||
exec_clkcmp(disp, head, 0, pclk, &conf);
|
||||
|
||||
if (outp->info.type == DCB_OUTPUT_ANALOG) {
|
||||
addr = 0x612280 + (ffs(outp->info.or) - 1) * 0x800;
|
||||
data = 0x00000000;
|
||||
} else {
|
||||
addr = 0x612300 + (ffs(outp->info.or) - 1) * 0x800;
|
||||
data = (conf & 0x0100) ? 0x00000101 : 0x00000000;
|
||||
switch (outp->info.type) {
|
||||
case DCB_OUTPUT_TMDS:
|
||||
nvkm_mask(device, addr, 0x007c0000, 0x00280000);
|
||||
break;
|
||||
case DCB_OUTPUT_DP:
|
||||
gf119_disp_intr_unk2_2_tu(disp, head, &outp->info);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nvkm_mask(device, addr, 0x00000707, data);
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_disp_intr_unk4_0(struct nv50_disp *disp, int head)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000;
|
||||
u32 conf;
|
||||
|
||||
exec_clkcmp(disp, head, 1, pclk, &conf);
|
||||
}
|
||||
|
||||
void
|
||||
gf119_disp_intr_supervisor(struct work_struct *work)
|
||||
gf119_disp_super(struct work_struct *work)
|
||||
{
|
||||
struct nv50_disp *disp =
|
||||
container_of(work, struct nv50_disp, supervisor);
|
||||
struct nvkm_subdev *subdev = &disp->base.engine.subdev;
|
||||
struct nvkm_device *device = subdev->device;
|
||||
struct nvkm_head *head;
|
||||
u32 mask[4];
|
||||
int head;
|
||||
|
||||
nvkm_debug(subdev, "supervisor %d\n", ffs(disp->super));
|
||||
for (head = 0; head < disp->base.head.nr; head++) {
|
||||
mask[head] = nvkm_rd32(device, 0x6101d4 + (head * 0x800));
|
||||
nvkm_debug(subdev, "head %d: %08x\n", head, mask[head]);
|
||||
list_for_each_entry(head, &disp->base.head, head) {
|
||||
mask[head->id] = nvkm_rd32(device, 0x6101d4 + (head->id * 0x800));
|
||||
HEAD_DBG(head, "%08x", mask[head->id]);
|
||||
}
|
||||
|
||||
if (disp->super & 0x00000001) {
|
||||
nv50_disp_chan_mthd(disp->chan[0], NV_DBG_DEBUG);
|
||||
for (head = 0; head < disp->base.head.nr; head++) {
|
||||
if (!(mask[head] & 0x00001000))
|
||||
nv50_disp_super_1(disp);
|
||||
list_for_each_entry(head, &disp->base.head, head) {
|
||||
if (!(mask[head->id] & 0x00001000))
|
||||
continue;
|
||||
nvkm_debug(subdev, "supervisor 1.0 - head %d\n", head);
|
||||
gf119_disp_intr_unk1_0(disp, head);
|
||||
nv50_disp_super_1_0(disp, head);
|
||||
}
|
||||
} else
|
||||
if (disp->super & 0x00000002) {
|
||||
for (head = 0; head < disp->base.head.nr; head++) {
|
||||
if (!(mask[head] & 0x00001000))
|
||||
list_for_each_entry(head, &disp->base.head, head) {
|
||||
if (!(mask[head->id] & 0x00001000))
|
||||
continue;
|
||||
nvkm_debug(subdev, "supervisor 2.0 - head %d\n", head);
|
||||
gf119_disp_intr_unk2_0(disp, head);
|
||||
nv50_disp_super_2_0(disp, head);
|
||||
}
|
||||
for (head = 0; head < disp->base.head.nr; head++) {
|
||||
if (!(mask[head] & 0x00010000))
|
||||
nvkm_outp_route(&disp->base);
|
||||
list_for_each_entry(head, &disp->base.head, head) {
|
||||
if (!(mask[head->id] & 0x00010000))
|
||||
continue;
|
||||
nvkm_debug(subdev, "supervisor 2.1 - head %d\n", head);
|
||||
gf119_disp_intr_unk2_1(disp, head);
|
||||
nv50_disp_super_2_1(disp, head);
|
||||
}
|
||||
for (head = 0; head < disp->base.head.nr; head++) {
|
||||
if (!(mask[head] & 0x00001000))
|
||||
list_for_each_entry(head, &disp->base.head, head) {
|
||||
if (!(mask[head->id] & 0x00001000))
|
||||
continue;
|
||||
nvkm_debug(subdev, "supervisor 2.2 - head %d\n", head);
|
||||
gf119_disp_intr_unk2_2(disp, head);
|
||||
nv50_disp_super_2_2(disp, head);
|
||||
}
|
||||
} else
|
||||
if (disp->super & 0x00000004) {
|
||||
for (head = 0; head < disp->base.head.nr; head++) {
|
||||
if (!(mask[head] & 0x00001000))
|
||||
list_for_each_entry(head, &disp->base.head, head) {
|
||||
if (!(mask[head->id] & 0x00001000))
|
||||
continue;
|
||||
nvkm_debug(subdev, "supervisor 3.0 - head %d\n", head);
|
||||
gf119_disp_intr_unk4_0(disp, head);
|
||||
nv50_disp_super_3_0(disp, head);
|
||||
}
|
||||
}
|
||||
|
||||
for (head = 0; head < disp->base.head.nr; head++)
|
||||
nvkm_wr32(device, 0x6101d4 + (head * 0x800), 0x00000000);
|
||||
list_for_each_entry(head, &disp->base.head, head)
|
||||
nvkm_wr32(device, 0x6101d4 + (head->id * 0x800), 0x00000000);
|
||||
nvkm_wr32(device, 0x6101d0, 0x80000000);
|
||||
}
|
||||
|
||||
|
@ -447,8 +113,8 @@ gf119_disp_intr(struct nv50_disp *disp)
|
|||
{
|
||||
struct nvkm_subdev *subdev = &disp->base.engine.subdev;
|
||||
struct nvkm_device *device = subdev->device;
|
||||
struct nvkm_head *head;
|
||||
u32 intr = nvkm_rd32(device, 0x610088);
|
||||
int i;
|
||||
|
||||
if (intr & 0x00000001) {
|
||||
u32 stat = nvkm_rd32(device, 0x61008c);
|
||||
|
@ -472,7 +138,7 @@ gf119_disp_intr(struct nv50_disp *disp)
|
|||
u32 stat = nvkm_rd32(device, 0x6100ac);
|
||||
if (stat & 0x00000007) {
|
||||
disp->super = (stat & 0x00000007);
|
||||
schedule_work(&disp->supervisor);
|
||||
queue_work(disp->wq, &disp->supervisor);
|
||||
nvkm_wr32(device, 0x6100ac, disp->super);
|
||||
stat &= ~0x00000007;
|
||||
}
|
||||
|
@ -485,14 +151,15 @@ gf119_disp_intr(struct nv50_disp *disp)
|
|||
intr &= ~0x00100000;
|
||||
}
|
||||
|
||||
for (i = 0; i < disp->base.head.nr; i++) {
|
||||
u32 mask = 0x01000000 << i;
|
||||
list_for_each_entry(head, &disp->base.head, head) {
|
||||
const u32 hoff = head->id * 0x800;
|
||||
u32 mask = 0x01000000 << head->id;
|
||||
if (mask & intr) {
|
||||
u32 stat = nvkm_rd32(device, 0x6100bc + (i * 0x800));
|
||||
u32 stat = nvkm_rd32(device, 0x6100bc + hoff);
|
||||
if (stat & 0x00000001)
|
||||
nvkm_disp_vblank(&disp->base, i);
|
||||
nvkm_mask(device, 0x6100bc + (i * 0x800), 0, 0);
|
||||
nvkm_rd32(device, 0x6100c0 + (i * 0x800));
|
||||
nvkm_disp_vblank(&disp->base, head->id);
|
||||
nvkm_mask(device, 0x6100bc + hoff, 0, 0);
|
||||
nvkm_rd32(device, 0x6100c0 + hoff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -510,22 +177,11 @@ gf119_disp = {
|
|||
.intr = gf119_disp_intr,
|
||||
.intr_error = gf119_disp_intr_error,
|
||||
.uevent = &gf119_disp_chan_uevent,
|
||||
.super = gf119_disp_intr_supervisor,
|
||||
.super = gf119_disp_super,
|
||||
.root = &gf119_disp_root_oclass,
|
||||
.head.vblank_init = gf119_disp_vblank_init,
|
||||
.head.vblank_fini = gf119_disp_vblank_fini,
|
||||
.head.scanoutpos = gf119_disp_root_scanoutpos,
|
||||
.outp.internal.crt = nv50_dac_output_new,
|
||||
.outp.internal.tmds = nv50_sor_output_new,
|
||||
.outp.internal.lvds = nv50_sor_output_new,
|
||||
.outp.internal.dp = gf119_sor_dp_new,
|
||||
.dac.nr = 3,
|
||||
.dac.power = nv50_dac_power,
|
||||
.dac.sense = nv50_dac_sense,
|
||||
.sor.nr = 4,
|
||||
.sor.power = nv50_sor_power,
|
||||
.sor.hda_eld = gf119_hda_eld,
|
||||
.sor.hdmi = gf119_hdmi_ctrl,
|
||||
.head.new = gf119_head_new,
|
||||
.dac = { .nr = 3, .new = gf119_dac_new },
|
||||
.sor = { .nr = 4, .new = gf119_sor_new },
|
||||
};
|
||||
|
||||
int
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
static const struct nv50_disp_func
|
||||
|
@ -29,22 +31,11 @@ gk104_disp = {
|
|||
.intr = gf119_disp_intr,
|
||||
.intr_error = gf119_disp_intr_error,
|
||||
.uevent = &gf119_disp_chan_uevent,
|
||||
.super = gf119_disp_intr_supervisor,
|
||||
.super = gf119_disp_super,
|
||||
.root = &gk104_disp_root_oclass,
|
||||
.head.vblank_init = gf119_disp_vblank_init,
|
||||
.head.vblank_fini = gf119_disp_vblank_fini,
|
||||
.head.scanoutpos = gf119_disp_root_scanoutpos,
|
||||
.outp.internal.crt = nv50_dac_output_new,
|
||||
.outp.internal.tmds = nv50_sor_output_new,
|
||||
.outp.internal.lvds = nv50_sor_output_new,
|
||||
.outp.internal.dp = gf119_sor_dp_new,
|
||||
.dac.nr = 3,
|
||||
.dac.power = nv50_dac_power,
|
||||
.dac.sense = nv50_dac_sense,
|
||||
.sor.nr = 4,
|
||||
.sor.power = nv50_sor_power,
|
||||
.sor.hda_eld = gf119_hda_eld,
|
||||
.sor.hdmi = gk104_hdmi_ctrl,
|
||||
.head.new = gf119_head_new,
|
||||
.dac = { .nr = 3, .new = gf119_dac_new },
|
||||
.sor = { .nr = 4, .new = gk104_sor_new },
|
||||
};
|
||||
|
||||
int
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
static const struct nv50_disp_func
|
||||
|
@ -29,22 +31,11 @@ gk110_disp = {
|
|||
.intr = gf119_disp_intr,
|
||||
.intr_error = gf119_disp_intr_error,
|
||||
.uevent = &gf119_disp_chan_uevent,
|
||||
.super = gf119_disp_intr_supervisor,
|
||||
.super = gf119_disp_super,
|
||||
.root = &gk110_disp_root_oclass,
|
||||
.head.vblank_init = gf119_disp_vblank_init,
|
||||
.head.vblank_fini = gf119_disp_vblank_fini,
|
||||
.head.scanoutpos = gf119_disp_root_scanoutpos,
|
||||
.outp.internal.crt = nv50_dac_output_new,
|
||||
.outp.internal.tmds = nv50_sor_output_new,
|
||||
.outp.internal.lvds = nv50_sor_output_new,
|
||||
.outp.internal.dp = gf119_sor_dp_new,
|
||||
.dac.nr = 3,
|
||||
.dac.power = nv50_dac_power,
|
||||
.dac.sense = nv50_dac_sense,
|
||||
.sor.nr = 4,
|
||||
.sor.power = nv50_sor_power,
|
||||
.sor.hda_eld = gf119_hda_eld,
|
||||
.sor.hdmi = gk104_hdmi_ctrl,
|
||||
.head.new = gf119_head_new,
|
||||
.dac = { .nr = 3, .new = gf119_dac_new },
|
||||
.sor = { .nr = 4, .new = gk104_sor_new },
|
||||
};
|
||||
|
||||
int
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
static const struct nv50_disp_func
|
||||
|
@ -29,22 +31,11 @@ gm107_disp = {
|
|||
.intr = gf119_disp_intr,
|
||||
.intr_error = gf119_disp_intr_error,
|
||||
.uevent = &gf119_disp_chan_uevent,
|
||||
.super = gf119_disp_intr_supervisor,
|
||||
.super = gf119_disp_super,
|
||||
.root = &gm107_disp_root_oclass,
|
||||
.head.vblank_init = gf119_disp_vblank_init,
|
||||
.head.vblank_fini = gf119_disp_vblank_fini,
|
||||
.head.scanoutpos = gf119_disp_root_scanoutpos,
|
||||
.outp.internal.crt = nv50_dac_output_new,
|
||||
.outp.internal.tmds = nv50_sor_output_new,
|
||||
.outp.internal.lvds = nv50_sor_output_new,
|
||||
.outp.internal.dp = gm107_sor_dp_new,
|
||||
.dac.nr = 3,
|
||||
.dac.power = nv50_dac_power,
|
||||
.dac.sense = nv50_dac_sense,
|
||||
.sor.nr = 4,
|
||||
.sor.power = nv50_sor_power,
|
||||
.sor.hda_eld = gf119_hda_eld,
|
||||
.sor.hdmi = gk104_hdmi_ctrl,
|
||||
.head.new = gf119_head_new,
|
||||
.dac = { .nr = 3, .new = gf119_dac_new },
|
||||
.sor = { .nr = 4, .new = gm107_sor_new },
|
||||
};
|
||||
|
||||
int
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
static const struct nv50_disp_func
|
||||
|
@ -29,23 +31,11 @@ gm200_disp = {
|
|||
.intr = gf119_disp_intr,
|
||||
.intr_error = gf119_disp_intr_error,
|
||||
.uevent = &gf119_disp_chan_uevent,
|
||||
.super = gf119_disp_intr_supervisor,
|
||||
.super = gf119_disp_super,
|
||||
.root = &gm200_disp_root_oclass,
|
||||
.head.vblank_init = gf119_disp_vblank_init,
|
||||
.head.vblank_fini = gf119_disp_vblank_fini,
|
||||
.head.scanoutpos = gf119_disp_root_scanoutpos,
|
||||
.outp.internal.crt = nv50_dac_output_new,
|
||||
.outp.internal.tmds = nv50_sor_output_new,
|
||||
.outp.internal.lvds = nv50_sor_output_new,
|
||||
.outp.internal.dp = gm200_sor_dp_new,
|
||||
.dac.nr = 3,
|
||||
.dac.power = nv50_dac_power,
|
||||
.dac.sense = nv50_dac_sense,
|
||||
.sor.nr = 4,
|
||||
.sor.power = nv50_sor_power,
|
||||
.sor.hda_eld = gf119_hda_eld,
|
||||
.sor.hdmi = gk104_hdmi_ctrl,
|
||||
.sor.magic = gm200_sor_magic,
|
||||
.head.new = gf119_head_new,
|
||||
.dac = { .nr = 3, .new = gf119_dac_new },
|
||||
.sor = { .nr = 4, .new = gm200_sor_new },
|
||||
};
|
||||
|
||||
int
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
* Authors: Ben Skeggs <bskeggs@redhat.com>
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
static const struct nv50_disp_func
|
||||
|
@ -29,23 +31,10 @@ gp100_disp = {
|
|||
.intr = gf119_disp_intr,
|
||||
.intr_error = gf119_disp_intr_error,
|
||||
.uevent = &gf119_disp_chan_uevent,
|
||||
.super = gf119_disp_intr_supervisor,
|
||||
.super = gf119_disp_super,
|
||||
.root = &gp100_disp_root_oclass,
|
||||
.head.vblank_init = gf119_disp_vblank_init,
|
||||
.head.vblank_fini = gf119_disp_vblank_fini,
|
||||
.head.scanoutpos = gf119_disp_root_scanoutpos,
|
||||
.outp.internal.crt = nv50_dac_output_new,
|
||||
.outp.internal.tmds = nv50_sor_output_new,
|
||||
.outp.internal.lvds = nv50_sor_output_new,
|
||||
.outp.internal.dp = gm200_sor_dp_new,
|
||||
.dac.nr = 3,
|
||||
.dac.power = nv50_dac_power,
|
||||
.dac.sense = nv50_dac_sense,
|
||||
.sor.nr = 4,
|
||||
.sor.power = nv50_sor_power,
|
||||
.sor.hda_eld = gf119_hda_eld,
|
||||
.sor.hdmi = gk104_hdmi_ctrl,
|
||||
.sor.magic = gm200_sor_magic,
|
||||
.head.new = gf119_head_new,
|
||||
.sor = { .nr = 4, .new = gm200_sor_new },
|
||||
};
|
||||
|
||||
int
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
* Authors: Ben Skeggs <bskeggs@redhat.com>
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
static void
|
||||
|
@ -55,23 +57,10 @@ gp102_disp = {
|
|||
.intr = gf119_disp_intr,
|
||||
.intr_error = gp102_disp_intr_error,
|
||||
.uevent = &gf119_disp_chan_uevent,
|
||||
.super = gf119_disp_intr_supervisor,
|
||||
.super = gf119_disp_super,
|
||||
.root = &gp102_disp_root_oclass,
|
||||
.head.vblank_init = gf119_disp_vblank_init,
|
||||
.head.vblank_fini = gf119_disp_vblank_fini,
|
||||
.head.scanoutpos = gf119_disp_root_scanoutpos,
|
||||
.outp.internal.crt = nv50_dac_output_new,
|
||||
.outp.internal.tmds = nv50_sor_output_new,
|
||||
.outp.internal.lvds = nv50_sor_output_new,
|
||||
.outp.internal.dp = gm200_sor_dp_new,
|
||||
.dac.nr = 3,
|
||||
.dac.power = nv50_dac_power,
|
||||
.dac.sense = nv50_dac_sense,
|
||||
.sor.nr = 4,
|
||||
.sor.power = nv50_sor_power,
|
||||
.sor.hda_eld = gf119_hda_eld,
|
||||
.sor.hdmi = gk104_hdmi_ctrl,
|
||||
.sor.magic = gm200_sor_magic,
|
||||
.head.new = gf119_head_new,
|
||||
.sor = { .nr = 4, .new = gm200_sor_new },
|
||||
};
|
||||
|
||||
int
|
||||
|
|
|
@ -22,30 +22,20 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
static const struct nv50_disp_func
|
||||
gt200_disp = {
|
||||
.intr = nv50_disp_intr,
|
||||
.uevent = &nv50_disp_chan_uevent,
|
||||
.super = nv50_disp_intr_supervisor,
|
||||
.super = nv50_disp_super,
|
||||
.root = >200_disp_root_oclass,
|
||||
.head.vblank_init = nv50_disp_vblank_init,
|
||||
.head.vblank_fini = nv50_disp_vblank_fini,
|
||||
.head.scanoutpos = nv50_disp_root_scanoutpos,
|
||||
.outp.internal.crt = nv50_dac_output_new,
|
||||
.outp.internal.tmds = nv50_sor_output_new,
|
||||
.outp.internal.lvds = nv50_sor_output_new,
|
||||
.outp.external.tmds = nv50_pior_output_new,
|
||||
.outp.external.dp = nv50_pior_dp_new,
|
||||
.dac.nr = 3,
|
||||
.dac.power = nv50_dac_power,
|
||||
.dac.sense = nv50_dac_sense,
|
||||
.sor.nr = 2,
|
||||
.sor.power = nv50_sor_power,
|
||||
.sor.hdmi = g84_hdmi_ctrl,
|
||||
.pior.nr = 3,
|
||||
.pior.power = nv50_pior_power,
|
||||
.head.new = nv50_head_new,
|
||||
.dac = { .nr = 3, .new = nv50_dac_new },
|
||||
.sor = { .nr = 2, .new = g84_sor_new },
|
||||
.pior = { .nr = 3, .new = nv50_pior_new },
|
||||
};
|
||||
|
||||
int
|
||||
|
|
|
@ -22,32 +22,20 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
static const struct nv50_disp_func
|
||||
gt215_disp = {
|
||||
.intr = nv50_disp_intr,
|
||||
.uevent = &nv50_disp_chan_uevent,
|
||||
.super = nv50_disp_intr_supervisor,
|
||||
.super = nv50_disp_super,
|
||||
.root = >215_disp_root_oclass,
|
||||
.head.vblank_init = nv50_disp_vblank_init,
|
||||
.head.vblank_fini = nv50_disp_vblank_fini,
|
||||
.head.scanoutpos = nv50_disp_root_scanoutpos,
|
||||
.outp.internal.crt = nv50_dac_output_new,
|
||||
.outp.internal.tmds = nv50_sor_output_new,
|
||||
.outp.internal.lvds = nv50_sor_output_new,
|
||||
.outp.internal.dp = g94_sor_dp_new,
|
||||
.outp.external.tmds = nv50_pior_output_new,
|
||||
.outp.external.dp = nv50_pior_dp_new,
|
||||
.dac.nr = 3,
|
||||
.dac.power = nv50_dac_power,
|
||||
.dac.sense = nv50_dac_sense,
|
||||
.sor.nr = 4,
|
||||
.sor.power = nv50_sor_power,
|
||||
.sor.hda_eld = gt215_hda_eld,
|
||||
.sor.hdmi = gt215_hdmi_ctrl,
|
||||
.pior.nr = 3,
|
||||
.pior.power = nv50_pior_power,
|
||||
.head.new = nv50_head_new,
|
||||
.dac = { .nr = 3, .new = nv50_dac_new },
|
||||
.sor = { .nr = 4, .new = gt215_sor_new },
|
||||
.pior = { .nr = 3, .new = nv50_pior_new },
|
||||
};
|
||||
|
||||
int
|
||||
|
|
|
@ -21,63 +21,34 @@
|
|||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "outp.h"
|
||||
#include "ior.h"
|
||||
|
||||
#include <core/client.h>
|
||||
#include <subdev/bios.h>
|
||||
#include <subdev/bios/dcb.h>
|
||||
#include <subdev/timer.h>
|
||||
|
||||
#include <nvif/cl5070.h>
|
||||
#include <nvif/unpack.h>
|
||||
|
||||
int
|
||||
gf119_hda_eld(NV50_DISP_MTHD_V1)
|
||||
void
|
||||
gf119_hda_eld(struct nvkm_ior *ior, u8 *data, u8 size)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
union {
|
||||
struct nv50_disp_sor_hda_eld_v0 v0;
|
||||
} *args = data;
|
||||
const u32 soff = outp->or * 0x030;
|
||||
const u32 hoff = head * 0x800;
|
||||
int ret = -ENOSYS, i;
|
||||
struct nvkm_device *device = ior->disp->engine.subdev.device;
|
||||
const u32 soff = 0x030 * ior->id;
|
||||
int i;
|
||||
|
||||
nvif_ioctl(object, "disp sor hda eld size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
|
||||
nvif_ioctl(object, "disp sor hda eld vers %d\n",
|
||||
args->v0.version);
|
||||
if (size > 0x60)
|
||||
return -E2BIG;
|
||||
} else
|
||||
return ret;
|
||||
|
||||
if (size && args->v0.data[0]) {
|
||||
if (outp->info.type == DCB_OUTPUT_DP) {
|
||||
nvkm_mask(device, 0x616618 + hoff, 0x8000000c, 0x80000001);
|
||||
nvkm_msec(device, 2000,
|
||||
u32 tmp = nvkm_rd32(device, 0x616618 + hoff);
|
||||
if (!(tmp & 0x80000000))
|
||||
break;
|
||||
);
|
||||
}
|
||||
nvkm_mask(device, 0x616548 + hoff, 0x00000070, 0x00000000);
|
||||
for (i = 0; i < size; i++)
|
||||
nvkm_wr32(device, 0x10ec00 + soff, (i << 8) | args->v0.data[i]);
|
||||
for (; i < 0x60; i++)
|
||||
nvkm_wr32(device, 0x10ec00 + soff, (i << 8));
|
||||
nvkm_mask(device, 0x10ec10 + soff, 0x80000003, 0x80000003);
|
||||
} else {
|
||||
if (outp->info.type == DCB_OUTPUT_DP) {
|
||||
nvkm_mask(device, 0x616618 + hoff, 0x80000001, 0x80000000);
|
||||
nvkm_msec(device, 2000,
|
||||
u32 tmp = nvkm_rd32(device, 0x616618 + hoff);
|
||||
if (!(tmp & 0x80000000))
|
||||
break;
|
||||
);
|
||||
}
|
||||
nvkm_mask(device, 0x10ec10 + soff, 0x80000003, 0x80000000 | !!size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
for (i = 0; i < size; i++)
|
||||
nvkm_wr32(device, 0x10ec00 + soff, (i << 8) | data[i]);
|
||||
for (; i < 0x60; i++)
|
||||
nvkm_wr32(device, 0x10ec00 + soff, (i << 8));
|
||||
nvkm_mask(device, 0x10ec10 + soff, 0x80000002, 0x80000002);
|
||||
}
|
||||
|
||||
void
|
||||
gf119_hda_hpd(struct nvkm_ior *ior, int head, bool present)
|
||||
{
|
||||
struct nvkm_device *device = ior->disp->engine.subdev.device;
|
||||
const u32 hoff = 0x800 * head;
|
||||
u32 data = 0x80000000;
|
||||
u32 mask = 0x80000001;
|
||||
if (present) {
|
||||
nvkm_mask(device, 0x616548 + hoff, 0x00000070, 0x00000000);
|
||||
data |= 0x00000001;
|
||||
} else {
|
||||
mask |= 0x00000002;
|
||||
}
|
||||
nvkm_mask(device, 0x10ec10 + ior->id * 0x030, mask, data);
|
||||
}
|
||||
|
|
|
@ -21,59 +21,31 @@
|
|||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "outp.h"
|
||||
#include "ior.h"
|
||||
|
||||
#include <core/client.h>
|
||||
#include <subdev/timer.h>
|
||||
|
||||
#include <nvif/cl5070.h>
|
||||
#include <nvif/unpack.h>
|
||||
|
||||
int
|
||||
gt215_hda_eld(NV50_DISP_MTHD_V1)
|
||||
void
|
||||
gt215_hda_eld(struct nvkm_ior *ior, u8 *data, u8 size)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
union {
|
||||
struct nv50_disp_sor_hda_eld_v0 v0;
|
||||
} *args = data;
|
||||
const u32 soff = outp->or * 0x800;
|
||||
int ret = -ENOSYS, i;
|
||||
struct nvkm_device *device = ior->disp->engine.subdev.device;
|
||||
const u32 soff = ior->id * 0x800;
|
||||
int i;
|
||||
|
||||
nvif_ioctl(object, "disp sor hda eld size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
|
||||
nvif_ioctl(object, "disp sor hda eld vers %d\n",
|
||||
args->v0.version);
|
||||
if (size > 0x60)
|
||||
return -E2BIG;
|
||||
} else
|
||||
return ret;
|
||||
|
||||
if (size && args->v0.data[0]) {
|
||||
if (outp->info.type == DCB_OUTPUT_DP) {
|
||||
nvkm_mask(device, 0x61c1e0 + soff, 0x8000000d, 0x80000001);
|
||||
nvkm_msec(device, 2000,
|
||||
u32 tmp = nvkm_rd32(device, 0x61c1e0 + soff);
|
||||
if (!(tmp & 0x80000000))
|
||||
break;
|
||||
);
|
||||
}
|
||||
for (i = 0; i < size; i++)
|
||||
nvkm_wr32(device, 0x61c440 + soff, (i << 8) | args->v0.data[i]);
|
||||
for (; i < 0x60; i++)
|
||||
nvkm_wr32(device, 0x61c440 + soff, (i << 8));
|
||||
nvkm_mask(device, 0x61c448 + soff, 0x80000003, 0x80000003);
|
||||
} else {
|
||||
if (outp->info.type == DCB_OUTPUT_DP) {
|
||||
nvkm_mask(device, 0x61c1e0 + soff, 0x80000001, 0x80000000);
|
||||
nvkm_msec(device, 2000,
|
||||
u32 tmp = nvkm_rd32(device, 0x61c1e0 + soff);
|
||||
if (!(tmp & 0x80000000))
|
||||
break;
|
||||
);
|
||||
}
|
||||
nvkm_mask(device, 0x61c448 + soff, 0x80000003, 0x80000000 | !!size);
|
||||
}
|
||||
|
||||
return 0;
|
||||
for (i = 0; i < size; i++)
|
||||
nvkm_wr32(device, 0x61c440 + soff, (i << 8) | data[i]);
|
||||
for (; i < 0x60; i++)
|
||||
nvkm_wr32(device, 0x61c440 + soff, (i << 8));
|
||||
nvkm_mask(device, 0x61c448 + soff, 0x80000002, 0x80000002);
|
||||
}
|
||||
|
||||
void
|
||||
gt215_hda_hpd(struct nvkm_ior *ior, int head, bool present)
|
||||
{
|
||||
struct nvkm_device *device = ior->disp->engine.subdev.device;
|
||||
u32 data = 0x80000000;
|
||||
u32 mask = 0x80000001;
|
||||
if (present)
|
||||
data |= 0x00000001;
|
||||
else
|
||||
mask |= 0x00000002;
|
||||
nvkm_mask(device, 0x61c448 + ior->id * 0x800, mask, data);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
#include "hdmi.h"
|
||||
|
||||
void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame,
|
||||
u8 *raw_frame, ssize_t len)
|
||||
{
|
||||
u32 header = 0;
|
||||
u32 subpack0_low = 0;
|
||||
u32 subpack0_high = 0;
|
||||
u32 subpack1_low = 0;
|
||||
u32 subpack1_high = 0;
|
||||
|
||||
switch (len) {
|
||||
/*
|
||||
* "When in doubt, use brute force."
|
||||
* -- Ken Thompson.
|
||||
*/
|
||||
default:
|
||||
/*
|
||||
* We presume that no valid frame is longer than 17
|
||||
* octets, including header... And truncate to that
|
||||
* if it's longer.
|
||||
*/
|
||||
case 17:
|
||||
subpack1_high = (raw_frame[16] << 16);
|
||||
case 16:
|
||||
subpack1_high |= (raw_frame[15] << 8);
|
||||
case 15:
|
||||
subpack1_high |= raw_frame[14];
|
||||
case 14:
|
||||
subpack1_low = (raw_frame[13] << 24);
|
||||
case 13:
|
||||
subpack1_low |= (raw_frame[12] << 16);
|
||||
case 12:
|
||||
subpack1_low |= (raw_frame[11] << 8);
|
||||
case 11:
|
||||
subpack1_low |= raw_frame[10];
|
||||
case 10:
|
||||
subpack0_high = (raw_frame[9] << 16);
|
||||
case 9:
|
||||
subpack0_high |= (raw_frame[8] << 8);
|
||||
case 8:
|
||||
subpack0_high |= raw_frame[7];
|
||||
case 7:
|
||||
subpack0_low = (raw_frame[6] << 24);
|
||||
case 6:
|
||||
subpack0_low |= (raw_frame[5] << 16);
|
||||
case 5:
|
||||
subpack0_low |= (raw_frame[4] << 8);
|
||||
case 4:
|
||||
subpack0_low |= raw_frame[3];
|
||||
case 3:
|
||||
header = (raw_frame[2] << 16);
|
||||
case 2:
|
||||
header |= (raw_frame[1] << 8);
|
||||
case 1:
|
||||
header |= raw_frame[0];
|
||||
case 0:
|
||||
break;
|
||||
}
|
||||
|
||||
packed_frame->header = header;
|
||||
packed_frame->subpack0_low = subpack0_low;
|
||||
packed_frame->subpack0_high = subpack0_high;
|
||||
packed_frame->subpack1_low = subpack1_low;
|
||||
packed_frame->subpack1_high = subpack1_high;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef __NVKM_DISP_HDMI_H__
|
||||
#define __NVKM_DISP_HDMI_H__
|
||||
#include "ior.h"
|
||||
|
||||
struct packed_hdmi_infoframe {
|
||||
u32 header;
|
||||
u32 subpack0_low;
|
||||
u32 subpack0_high;
|
||||
u32 subpack1_low;
|
||||
u32 subpack1_high;
|
||||
};
|
||||
|
||||
void pack_hdmi_infoframe(struct packed_hdmi_infoframe *packed_frame,
|
||||
u8 *raw_frame, ssize_t len);
|
||||
#endif
|
|
@ -21,54 +21,42 @@
|
|||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "hdmi.h"
|
||||
|
||||
#include <core/client.h>
|
||||
|
||||
#include <nvif/cl5070.h>
|
||||
#include <nvif/unpack.h>
|
||||
|
||||
int
|
||||
g84_hdmi_ctrl(NV50_DISP_MTHD_V1)
|
||||
void
|
||||
g84_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet,
|
||||
u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
const u32 hoff = (head * 0x800);
|
||||
union {
|
||||
struct nv50_disp_sor_hdmi_pwr_v0 v0;
|
||||
} *args = data;
|
||||
u32 ctrl;
|
||||
int ret = -ENOSYS;
|
||||
struct nvkm_device *device = ior->disp->engine.subdev.device;
|
||||
const u32 ctrl = 0x40000000 * enable |
|
||||
0x1f000000 /* ??? */ |
|
||||
max_ac_packet << 16 |
|
||||
rekey;
|
||||
const u32 hoff = head * 0x800;
|
||||
struct packed_hdmi_infoframe avi_infoframe;
|
||||
struct packed_hdmi_infoframe vendor_infoframe;
|
||||
|
||||
nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
|
||||
"max_ac_packet %d rekey %d\n",
|
||||
args->v0.version, args->v0.state,
|
||||
args->v0.max_ac_packet, args->v0.rekey);
|
||||
if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
|
||||
return -EINVAL;
|
||||
ctrl = 0x40000000 * !!args->v0.state;
|
||||
ctrl |= args->v0.max_ac_packet << 16;
|
||||
ctrl |= args->v0.rekey;
|
||||
ctrl |= 0x1f000000; /* ??? */
|
||||
} else
|
||||
return ret;
|
||||
pack_hdmi_infoframe(&avi_infoframe, avi, avi_size);
|
||||
pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size);
|
||||
|
||||
if (!(ctrl & 0x40000000)) {
|
||||
nvkm_mask(device, 0x6165a4 + hoff, 0x40000000, 0x00000000);
|
||||
nvkm_mask(device, 0x61653c + hoff, 0x00000001, 0x00000000);
|
||||
nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000);
|
||||
nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* AVI InfoFrame */
|
||||
nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000000);
|
||||
nvkm_wr32(device, 0x616528 + hoff, 0x000d0282);
|
||||
nvkm_wr32(device, 0x61652c + hoff, 0x0000006f);
|
||||
nvkm_wr32(device, 0x616530 + hoff, 0x00000000);
|
||||
nvkm_wr32(device, 0x616534 + hoff, 0x00000000);
|
||||
nvkm_wr32(device, 0x616538 + hoff, 0x00000000);
|
||||
nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000001);
|
||||
if (avi_size) {
|
||||
nvkm_wr32(device, 0x616528 + hoff, avi_infoframe.header);
|
||||
nvkm_wr32(device, 0x61652c + hoff, avi_infoframe.subpack0_low);
|
||||
nvkm_wr32(device, 0x616530 + hoff, avi_infoframe.subpack0_high);
|
||||
nvkm_wr32(device, 0x616534 + hoff, avi_infoframe.subpack1_low);
|
||||
nvkm_wr32(device, 0x616538 + hoff, avi_infoframe.subpack1_high);
|
||||
nvkm_mask(device, 0x616520 + hoff, 0x00000001, 0x00000001);
|
||||
}
|
||||
|
||||
/* Audio InfoFrame */
|
||||
nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000000);
|
||||
|
@ -77,6 +65,18 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1)
|
|||
nvkm_wr32(device, 0x616510 + hoff, 0x00000000);
|
||||
nvkm_mask(device, 0x616500 + hoff, 0x00000001, 0x00000001);
|
||||
|
||||
/* Vendor InfoFrame */
|
||||
nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010000);
|
||||
if (vendor_size) {
|
||||
nvkm_wr32(device, 0x616544 + hoff, vendor_infoframe.header);
|
||||
nvkm_wr32(device, 0x616548 + hoff, vendor_infoframe.subpack0_low);
|
||||
nvkm_wr32(device, 0x61654c + hoff, vendor_infoframe.subpack0_high);
|
||||
/* Is there a second (or up to fourth?) set of subpack registers here? */
|
||||
/* nvkm_wr32(device, 0x616550 + hoff, vendor_infoframe->subpack1_low); */
|
||||
/* nvkm_wr32(device, 0x616554 + hoff, vendor_infoframe->subpack1_high); */
|
||||
nvkm_mask(device, 0x61653c + hoff, 0x00010001, 0x00010001);
|
||||
}
|
||||
|
||||
nvkm_mask(device, 0x6165d0 + hoff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */
|
||||
nvkm_mask(device, 0x616568 + hoff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */
|
||||
nvkm_mask(device, 0x616578 + hoff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */
|
||||
|
@ -88,5 +88,4 @@ g84_hdmi_ctrl(NV50_DISP_MTHD_V1)
|
|||
|
||||
/* HDMI_CTRL */
|
||||
nvkm_mask(device, 0x6165a4 + hoff, 0x5f1f007f, ctrl);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -21,53 +21,56 @@
|
|||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "hdmi.h"
|
||||
|
||||
#include <core/client.h>
|
||||
|
||||
#include <nvif/cl5070.h>
|
||||
#include <nvif/unpack.h>
|
||||
|
||||
int
|
||||
gf119_hdmi_ctrl(NV50_DISP_MTHD_V1)
|
||||
void
|
||||
gf119_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet,
|
||||
u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
const u32 hoff = (head * 0x800);
|
||||
union {
|
||||
struct nv50_disp_sor_hdmi_pwr_v0 v0;
|
||||
} *args = data;
|
||||
u32 ctrl;
|
||||
int ret = -ENOSYS;
|
||||
struct nvkm_device *device = ior->disp->engine.subdev.device;
|
||||
const u32 ctrl = 0x40000000 * enable |
|
||||
max_ac_packet << 16 |
|
||||
rekey;
|
||||
const u32 hoff = head * 0x800;
|
||||
struct packed_hdmi_infoframe avi_infoframe;
|
||||
struct packed_hdmi_infoframe vendor_infoframe;
|
||||
|
||||
nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
|
||||
"max_ac_packet %d rekey %d\n",
|
||||
args->v0.version, args->v0.state,
|
||||
args->v0.max_ac_packet, args->v0.rekey);
|
||||
if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
|
||||
return -EINVAL;
|
||||
ctrl = 0x40000000 * !!args->v0.state;
|
||||
ctrl |= args->v0.max_ac_packet << 16;
|
||||
ctrl |= args->v0.rekey;
|
||||
} else
|
||||
return ret;
|
||||
pack_hdmi_infoframe(&avi_infoframe, avi, avi_size);
|
||||
pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size);
|
||||
|
||||
if (!(ctrl & 0x40000000)) {
|
||||
nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000);
|
||||
nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000000);
|
||||
nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000);
|
||||
nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* AVI InfoFrame */
|
||||
nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000000);
|
||||
nvkm_wr32(device, 0x61671c + hoff, 0x000d0282);
|
||||
nvkm_wr32(device, 0x616720 + hoff, 0x0000006f);
|
||||
nvkm_wr32(device, 0x616724 + hoff, 0x00000000);
|
||||
nvkm_wr32(device, 0x616728 + hoff, 0x00000000);
|
||||
nvkm_wr32(device, 0x61672c + hoff, 0x00000000);
|
||||
nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000001);
|
||||
if (avi_size) {
|
||||
nvkm_wr32(device, 0x61671c + hoff, avi_infoframe.header);
|
||||
nvkm_wr32(device, 0x616720 + hoff, avi_infoframe.subpack0_low);
|
||||
nvkm_wr32(device, 0x616724 + hoff, avi_infoframe.subpack0_high);
|
||||
nvkm_wr32(device, 0x616728 + hoff, avi_infoframe.subpack1_low);
|
||||
nvkm_wr32(device, 0x61672c + hoff, avi_infoframe.subpack1_high);
|
||||
nvkm_mask(device, 0x616714 + hoff, 0x00000001, 0x00000001);
|
||||
}
|
||||
|
||||
/* GENERIC(?) / Vendor InfoFrame? */
|
||||
nvkm_mask(device, 0x616730 + hoff, 0x00010001, 0x00010000);
|
||||
if (vendor_size) {
|
||||
/*
|
||||
* These appear to be the audio infoframe registers,
|
||||
* but no other set of infoframe registers has yet
|
||||
* been found.
|
||||
*/
|
||||
nvkm_wr32(device, 0x616738 + hoff, vendor_infoframe.header);
|
||||
nvkm_wr32(device, 0x61673c + hoff, vendor_infoframe.subpack0_low);
|
||||
nvkm_wr32(device, 0x616740 + hoff, vendor_infoframe.subpack0_high);
|
||||
/* Is there a second (or further?) set of subpack registers here? */
|
||||
nvkm_mask(device, 0x616730 + hoff, 0x00000001, 0x00000001);
|
||||
}
|
||||
|
||||
/* ??? InfoFrame? */
|
||||
nvkm_mask(device, 0x6167a4 + hoff, 0x00000001, 0x00000000);
|
||||
|
@ -76,5 +79,4 @@ gf119_hdmi_ctrl(NV50_DISP_MTHD_V1)
|
|||
|
||||
/* HDMI_CTRL */
|
||||
nvkm_mask(device, 0x616798 + hoff, 0x401f007f, ctrl);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -21,54 +21,53 @@
|
|||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "hdmi.h"
|
||||
|
||||
#include <core/client.h>
|
||||
|
||||
#include <nvif/cl5070.h>
|
||||
#include <nvif/unpack.h>
|
||||
|
||||
int
|
||||
gk104_hdmi_ctrl(NV50_DISP_MTHD_V1)
|
||||
void
|
||||
gk104_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet,
|
||||
u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
const u32 hoff = (head * 0x800);
|
||||
const u32 hdmi = (head * 0x400);
|
||||
union {
|
||||
struct nv50_disp_sor_hdmi_pwr_v0 v0;
|
||||
} *args = data;
|
||||
u32 ctrl;
|
||||
int ret = -ENOSYS;
|
||||
struct nvkm_device *device = ior->disp->engine.subdev.device;
|
||||
const u32 ctrl = 0x40000000 * enable |
|
||||
max_ac_packet << 16 |
|
||||
rekey;
|
||||
const u32 hoff = head * 0x800;
|
||||
const u32 hdmi = head * 0x400;
|
||||
struct packed_hdmi_infoframe avi_infoframe;
|
||||
struct packed_hdmi_infoframe vendor_infoframe;
|
||||
|
||||
nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
|
||||
"max_ac_packet %d rekey %d\n",
|
||||
args->v0.version, args->v0.state,
|
||||
args->v0.max_ac_packet, args->v0.rekey);
|
||||
if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
|
||||
return -EINVAL;
|
||||
ctrl = 0x40000000 * !!args->v0.state;
|
||||
ctrl |= args->v0.max_ac_packet << 16;
|
||||
ctrl |= args->v0.rekey;
|
||||
} else
|
||||
return ret;
|
||||
pack_hdmi_infoframe(&avi_infoframe, avi, avi_size);
|
||||
pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size);
|
||||
|
||||
if (!(ctrl & 0x40000000)) {
|
||||
nvkm_mask(device, 0x616798 + hoff, 0x40000000, 0x00000000);
|
||||
nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000000);
|
||||
nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000);
|
||||
nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* AVI InfoFrame */
|
||||
nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000000);
|
||||
nvkm_wr32(device, 0x690008 + hdmi, 0x000d0282);
|
||||
nvkm_wr32(device, 0x69000c + hdmi, 0x0000006f);
|
||||
nvkm_wr32(device, 0x690010 + hdmi, 0x00000000);
|
||||
nvkm_wr32(device, 0x690014 + hdmi, 0x00000000);
|
||||
nvkm_wr32(device, 0x690018 + hdmi, 0x00000000);
|
||||
nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000001);
|
||||
if (avi_size) {
|
||||
nvkm_wr32(device, 0x690008 + hdmi, avi_infoframe.header);
|
||||
nvkm_wr32(device, 0x69000c + hdmi, avi_infoframe.subpack0_low);
|
||||
nvkm_wr32(device, 0x690010 + hdmi, avi_infoframe.subpack0_high);
|
||||
nvkm_wr32(device, 0x690014 + hdmi, avi_infoframe.subpack1_low);
|
||||
nvkm_wr32(device, 0x690018 + hdmi, avi_infoframe.subpack1_high);
|
||||
nvkm_mask(device, 0x690000 + hdmi, 0x00000001, 0x00000001);
|
||||
}
|
||||
|
||||
/* GENERIC(?) / Vendor InfoFrame? */
|
||||
nvkm_mask(device, 0x690100 + hdmi, 0x00010001, 0x00000000);
|
||||
if (vendor_size) {
|
||||
nvkm_wr32(device, 0x690108 + hdmi, vendor_infoframe.header);
|
||||
nvkm_wr32(device, 0x69010c + hdmi, vendor_infoframe.subpack0_low);
|
||||
nvkm_wr32(device, 0x690110 + hdmi, vendor_infoframe.subpack0_high);
|
||||
/* Is there a second (or further?) set of subpack registers here? */
|
||||
nvkm_mask(device, 0x690100 + hdmi, 0x00000001, 0x00000001);
|
||||
}
|
||||
|
||||
|
||||
/* ??? InfoFrame? */
|
||||
nvkm_mask(device, 0x6900c0 + hdmi, 0x00000001, 0x00000000);
|
||||
|
@ -80,5 +79,4 @@ gk104_hdmi_ctrl(NV50_DISP_MTHD_V1)
|
|||
|
||||
/* HDMI_CTRL */
|
||||
nvkm_mask(device, 0x616798 + hoff, 0x401f007f, ctrl);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -21,55 +21,42 @@
|
|||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "outp.h"
|
||||
#include "hdmi.h"
|
||||
|
||||
#include <core/client.h>
|
||||
|
||||
#include <nvif/cl5070.h>
|
||||
#include <nvif/unpack.h>
|
||||
|
||||
int
|
||||
gt215_hdmi_ctrl(NV50_DISP_MTHD_V1)
|
||||
void
|
||||
gt215_hdmi_ctrl(struct nvkm_ior *ior, int head, bool enable, u8 max_ac_packet,
|
||||
u8 rekey, u8 *avi, u8 avi_size, u8 *vendor, u8 vendor_size)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
const u32 soff = outp->or * 0x800;
|
||||
union {
|
||||
struct nv50_disp_sor_hdmi_pwr_v0 v0;
|
||||
} *args = data;
|
||||
u32 ctrl;
|
||||
int ret = -ENOSYS;
|
||||
struct nvkm_device *device = ior->disp->engine.subdev.device;
|
||||
const u32 ctrl = 0x40000000 * enable |
|
||||
0x1f000000 /* ??? */ |
|
||||
max_ac_packet << 16 |
|
||||
rekey;
|
||||
const u32 soff = nv50_ior_base(ior);
|
||||
struct packed_hdmi_infoframe avi_infoframe;
|
||||
struct packed_hdmi_infoframe vendor_infoframe;
|
||||
|
||||
nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
|
||||
"max_ac_packet %d rekey %d\n",
|
||||
args->v0.version, args->v0.state,
|
||||
args->v0.max_ac_packet, args->v0.rekey);
|
||||
if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
|
||||
return -EINVAL;
|
||||
ctrl = 0x40000000 * !!args->v0.state;
|
||||
ctrl |= args->v0.max_ac_packet << 16;
|
||||
ctrl |= args->v0.rekey;
|
||||
ctrl |= 0x1f000000; /* ??? */
|
||||
} else
|
||||
return ret;
|
||||
pack_hdmi_infoframe(&avi_infoframe, avi, avi_size);
|
||||
pack_hdmi_infoframe(&vendor_infoframe, vendor, vendor_size);
|
||||
|
||||
if (!(ctrl & 0x40000000)) {
|
||||
nvkm_mask(device, 0x61c5a4 + soff, 0x40000000, 0x00000000);
|
||||
nvkm_mask(device, 0x61c53c + soff, 0x00000001, 0x00000000);
|
||||
nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000);
|
||||
nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* AVI InfoFrame */
|
||||
nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000000);
|
||||
nvkm_wr32(device, 0x61c528 + soff, 0x000d0282);
|
||||
nvkm_wr32(device, 0x61c52c + soff, 0x0000006f);
|
||||
nvkm_wr32(device, 0x61c530 + soff, 0x00000000);
|
||||
nvkm_wr32(device, 0x61c534 + soff, 0x00000000);
|
||||
nvkm_wr32(device, 0x61c538 + soff, 0x00000000);
|
||||
nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001);
|
||||
if (avi_size) {
|
||||
nvkm_wr32(device, 0x61c528 + soff, avi_infoframe.header);
|
||||
nvkm_wr32(device, 0x61c52c + soff, avi_infoframe.subpack0_low);
|
||||
nvkm_wr32(device, 0x61c530 + soff, avi_infoframe.subpack0_high);
|
||||
nvkm_wr32(device, 0x61c534 + soff, avi_infoframe.subpack1_low);
|
||||
nvkm_wr32(device, 0x61c538 + soff, avi_infoframe.subpack1_high);
|
||||
nvkm_mask(device, 0x61c520 + soff, 0x00000001, 0x00000001);
|
||||
}
|
||||
|
||||
/* Audio InfoFrame */
|
||||
nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000000);
|
||||
|
@ -78,6 +65,18 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1)
|
|||
nvkm_wr32(device, 0x61c510 + soff, 0x00000000);
|
||||
nvkm_mask(device, 0x61c500 + soff, 0x00000001, 0x00000001);
|
||||
|
||||
/* Vendor InfoFrame */
|
||||
nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010000);
|
||||
if (vendor_size) {
|
||||
nvkm_wr32(device, 0x61c544 + soff, vendor_infoframe.header);
|
||||
nvkm_wr32(device, 0x61c548 + soff, vendor_infoframe.subpack0_low);
|
||||
nvkm_wr32(device, 0x61c54c + soff, vendor_infoframe.subpack0_high);
|
||||
/* Is there a second (or up to fourth?) set of subpack registers here? */
|
||||
/* nvkm_wr32(device, 0x61c550 + soff, vendor_infoframe.subpack1_low); */
|
||||
/* nvkm_wr32(device, 0x61c554 + soff, vendor_infoframe.subpack1_high); */
|
||||
nvkm_mask(device, 0x61c53c + soff, 0x00010001, 0x00010001);
|
||||
}
|
||||
|
||||
nvkm_mask(device, 0x61c5d0 + soff, 0x00070001, 0x00010001); /* SPARE, HW_CTS */
|
||||
nvkm_mask(device, 0x61c568 + soff, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */
|
||||
nvkm_mask(device, 0x61c578 + soff, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */
|
||||
|
@ -89,5 +88,4 @@ gt215_hdmi_ctrl(NV50_DISP_MTHD_V1)
|
|||
|
||||
/* HDMI_CTRL */
|
||||
nvkm_mask(device, 0x61c5a4 + soff, 0x5f1f007f, ctrl);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright 2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors: Ben Skeggs <bskeggs@redhat.com>
|
||||
*/
|
||||
#include "head.h"
|
||||
|
||||
#include <core/client.h>
|
||||
|
||||
#include <nvif/cl0046.h>
|
||||
#include <nvif/unpack.h>
|
||||
|
||||
struct nvkm_head *
|
||||
nvkm_head_find(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
struct nvkm_head *head;
|
||||
list_for_each_entry(head, &disp->head, head) {
|
||||
if (head->id == id)
|
||||
return head;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
nvkm_head_mthd_scanoutpos(struct nvkm_object *object,
|
||||
struct nvkm_head *head, void *data, u32 size)
|
||||
{
|
||||
union {
|
||||
struct nv04_disp_scanoutpos_v0 v0;
|
||||
} *args = data;
|
||||
int ret = -ENOSYS;
|
||||
|
||||
nvif_ioctl(object, "head scanoutpos size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_ioctl(object, "head scanoutpos vers %d\n",
|
||||
args->v0.version);
|
||||
|
||||
head->func->state(head, &head->arm);
|
||||
args->v0.vtotal = head->arm.vtotal;
|
||||
args->v0.vblanks = head->arm.vblanks;
|
||||
args->v0.vblanke = head->arm.vblanke;
|
||||
args->v0.htotal = head->arm.htotal;
|
||||
args->v0.hblanks = head->arm.hblanks;
|
||||
args->v0.hblanke = head->arm.hblanke;
|
||||
|
||||
/* We don't support reading htotal/vtotal on pre-NV50 VGA,
|
||||
* so we have to give up and trigger the timestamping
|
||||
* fallback in the drm core.
|
||||
*/
|
||||
if (!args->v0.vtotal || !args->v0.htotal)
|
||||
return -ENOTSUPP;
|
||||
|
||||
args->v0.time[0] = ktime_to_ns(ktime_get());
|
||||
head->func->rgpos(head, &args->v0.hline, &args->v0.vline);
|
||||
args->v0.time[1] = ktime_to_ns(ktime_get());
|
||||
} else
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nvkm_head_del(struct nvkm_head **phead)
|
||||
{
|
||||
struct nvkm_head *head = *phead;
|
||||
if (head) {
|
||||
HEAD_DBG(head, "dtor");
|
||||
list_del(&head->head);
|
||||
kfree(*phead);
|
||||
*phead = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nvkm_head_new_(const struct nvkm_head_func *func,
|
||||
struct nvkm_disp *disp, int id)
|
||||
{
|
||||
struct nvkm_head *head;
|
||||
if (!(head = kzalloc(sizeof(*head), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
head->func = func;
|
||||
head->disp = disp;
|
||||
head->id = id;
|
||||
list_add_tail(&head->head, &disp->head);
|
||||
HEAD_DBG(head, "ctor");
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef __NVKM_DISP_HEAD_H__
|
||||
#define __NVKM_DISP_HEAD_H__
|
||||
#include "priv.h"
|
||||
|
||||
struct nvkm_head {
|
||||
const struct nvkm_head_func *func;
|
||||
struct nvkm_disp *disp;
|
||||
int id;
|
||||
|
||||
struct list_head head;
|
||||
|
||||
struct nvkm_head_state {
|
||||
u16 htotal;
|
||||
u16 hsynce;
|
||||
u16 hblanke;
|
||||
u16 hblanks;
|
||||
u16 vtotal;
|
||||
u16 vsynce;
|
||||
u16 vblanke;
|
||||
u16 vblanks;
|
||||
u32 hz;
|
||||
|
||||
/* Prior to GF119, these are set by the OR. */
|
||||
struct {
|
||||
u8 depth;
|
||||
} or;
|
||||
} arm, asy;
|
||||
};
|
||||
|
||||
int nvkm_head_new_(const struct nvkm_head_func *, struct nvkm_disp *, int id);
|
||||
void nvkm_head_del(struct nvkm_head **);
|
||||
int nvkm_head_mthd_scanoutpos(struct nvkm_object *,
|
||||
struct nvkm_head *, void *, u32);
|
||||
struct nvkm_head *nvkm_head_find(struct nvkm_disp *, int id);
|
||||
|
||||
struct nvkm_head_func {
|
||||
void (*state)(struct nvkm_head *, struct nvkm_head_state *);
|
||||
void (*rgpos)(struct nvkm_head *, u16 *hline, u16 *vline);
|
||||
void (*rgclk)(struct nvkm_head *, int div);
|
||||
void (*vblank_get)(struct nvkm_head *);
|
||||
void (*vblank_put)(struct nvkm_head *);
|
||||
};
|
||||
|
||||
void nv50_head_rgpos(struct nvkm_head *, u16 *, u16 *);
|
||||
|
||||
#define HEAD_MSG(h,l,f,a...) do { \
|
||||
struct nvkm_head *_h = (h); \
|
||||
nvkm_##l(&_h->disp->engine.subdev, "head-%d: "f"\n", _h->id, ##a); \
|
||||
} while(0)
|
||||
#define HEAD_WARN(h,f,a...) HEAD_MSG((h), warn, f, ##a)
|
||||
#define HEAD_DBG(h,f,a...) HEAD_MSG((h), debug, f, ##a)
|
||||
|
||||
int nv04_head_new(struct nvkm_disp *, int id);
|
||||
int nv50_head_new(struct nvkm_disp *, int id);
|
||||
int gf119_head_new(struct nvkm_disp *, int id);
|
||||
#endif
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright 2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors: Ben Skeggs <bskeggs@redhat.com>
|
||||
*/
|
||||
#include "head.h"
|
||||
|
||||
static void
|
||||
gf119_head_vblank_put(struct nvkm_head *head)
|
||||
{
|
||||
struct nvkm_device *device = head->disp->engine.subdev.device;
|
||||
const u32 hoff = head->id * 0x800;
|
||||
nvkm_mask(device, 0x6100c0 + hoff, 0x00000001, 0x00000000);
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_head_vblank_get(struct nvkm_head *head)
|
||||
{
|
||||
struct nvkm_device *device = head->disp->engine.subdev.device;
|
||||
const u32 hoff = head->id * 0x800;
|
||||
nvkm_mask(device, 0x6100c0 + hoff, 0x00000001, 0x00000001);
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_head_rgclk(struct nvkm_head *head, int div)
|
||||
{
|
||||
struct nvkm_device *device = head->disp->engine.subdev.device;
|
||||
nvkm_mask(device, 0x612200 + (head->id * 0x800), 0x0000000f, div);
|
||||
}
|
||||
|
||||
static void
|
||||
gf119_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
|
||||
{
|
||||
struct nvkm_device *device = head->disp->engine.subdev.device;
|
||||
const u32 hoff = (state == &head->asy) * 0x20000 + head->id * 0x300;
|
||||
u32 data;
|
||||
|
||||
data = nvkm_rd32(device, 0x640414 + hoff);
|
||||
state->vtotal = (data & 0xffff0000) >> 16;
|
||||
state->htotal = (data & 0x0000ffff);
|
||||
data = nvkm_rd32(device, 0x640418 + hoff);
|
||||
state->vsynce = (data & 0xffff0000) >> 16;
|
||||
state->hsynce = (data & 0x0000ffff);
|
||||
data = nvkm_rd32(device, 0x64041c + hoff);
|
||||
state->vblanke = (data & 0xffff0000) >> 16;
|
||||
state->hblanke = (data & 0x0000ffff);
|
||||
data = nvkm_rd32(device, 0x640420 + hoff);
|
||||
state->vblanks = (data & 0xffff0000) >> 16;
|
||||
state->hblanks = (data & 0x0000ffff);
|
||||
state->hz = nvkm_rd32(device, 0x640450 + hoff);
|
||||
|
||||
data = nvkm_rd32(device, 0x640404 + hoff);
|
||||
switch ((data & 0x000003c0) >> 6) {
|
||||
case 6: state->or.depth = 30; break;
|
||||
case 5: state->or.depth = 24; break;
|
||||
case 2: state->or.depth = 18; break;
|
||||
case 0: state->or.depth = 18; break; /*XXX: "default" */
|
||||
default:
|
||||
state->or.depth = 18;
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct nvkm_head_func
|
||||
gf119_head = {
|
||||
.state = gf119_head_state,
|
||||
.rgpos = nv50_head_rgpos,
|
||||
.rgclk = gf119_head_rgclk,
|
||||
.vblank_get = gf119_head_vblank_get,
|
||||
.vblank_put = gf119_head_vblank_put,
|
||||
};
|
||||
|
||||
int
|
||||
gf119_head_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
return nvkm_head_new_(&gf119_head, disp, id);
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors: Ben Skeggs <bskeggs@redhat.com>
|
||||
*/
|
||||
#include "head.h"
|
||||
|
||||
static void
|
||||
nv04_head_vblank_put(struct nvkm_head *head)
|
||||
{
|
||||
struct nvkm_device *device = head->disp->engine.subdev.device;
|
||||
nvkm_wr32(device, 0x600140 + (head->id * 0x2000) , 0x00000000);
|
||||
}
|
||||
|
||||
static void
|
||||
nv04_head_vblank_get(struct nvkm_head *head)
|
||||
{
|
||||
struct nvkm_device *device = head->disp->engine.subdev.device;
|
||||
nvkm_wr32(device, 0x600140 + (head->id * 0x2000) , 0x00000001);
|
||||
}
|
||||
|
||||
static void
|
||||
nv04_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline)
|
||||
{
|
||||
struct nvkm_device *device = head->disp->engine.subdev.device;
|
||||
u32 data = nvkm_rd32(device, 0x600868 + (head->id * 0x2000));
|
||||
*hline = (data & 0xffff0000) >> 16;
|
||||
*vline = (data & 0x0000ffff);
|
||||
}
|
||||
|
||||
static void
|
||||
nv04_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
|
||||
{
|
||||
struct nvkm_device *device = head->disp->engine.subdev.device;
|
||||
const u32 hoff = head->id * 0x0200;
|
||||
state->vblanks = nvkm_rd32(device, 0x680800 + hoff) & 0x0000ffff;
|
||||
state->vtotal = nvkm_rd32(device, 0x680804 + hoff) & 0x0000ffff;
|
||||
state->vblanke = state->vtotal - 1;
|
||||
state->hblanks = nvkm_rd32(device, 0x680820 + hoff) & 0x0000ffff;
|
||||
state->htotal = nvkm_rd32(device, 0x680824 + hoff) & 0x0000ffff;
|
||||
state->hblanke = state->htotal - 1;
|
||||
}
|
||||
|
||||
static const struct nvkm_head_func
|
||||
nv04_head = {
|
||||
.state = nv04_head_state,
|
||||
.rgpos = nv04_head_rgpos,
|
||||
.vblank_get = nv04_head_vblank_get,
|
||||
.vblank_put = nv04_head_vblank_put,
|
||||
};
|
||||
|
||||
int
|
||||
nv04_head_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
return nvkm_head_new_(&nv04_head, disp, id);
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright 2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors: Ben Skeggs <bskeggs@redhat.com>
|
||||
*/
|
||||
#include "head.h"
|
||||
|
||||
static void
|
||||
nv50_head_vblank_put(struct nvkm_head *head)
|
||||
{
|
||||
struct nvkm_device *device = head->disp->engine.subdev.device;
|
||||
nvkm_mask(device, 0x61002c, (4 << head->id), 0);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_head_vblank_get(struct nvkm_head *head)
|
||||
{
|
||||
struct nvkm_device *device = head->disp->engine.subdev.device;
|
||||
nvkm_mask(device, 0x61002c, (4 << head->id), (4 << head->id));
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_head_rgclk(struct nvkm_head *head, int div)
|
||||
{
|
||||
struct nvkm_device *device = head->disp->engine.subdev.device;
|
||||
nvkm_mask(device, 0x614200 + (head->id * 0x800), 0x0000000f, div);
|
||||
}
|
||||
|
||||
void
|
||||
nv50_head_rgpos(struct nvkm_head *head, u16 *hline, u16 *vline)
|
||||
{
|
||||
struct nvkm_device *device = head->disp->engine.subdev.device;
|
||||
const u32 hoff = head->id * 0x800;
|
||||
/* vline read locks hline. */
|
||||
*vline = nvkm_rd32(device, 0x616340 + hoff) & 0x0000ffff;
|
||||
*hline = nvkm_rd32(device, 0x616344 + hoff) & 0x0000ffff;
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_head_state(struct nvkm_head *head, struct nvkm_head_state *state)
|
||||
{
|
||||
struct nvkm_device *device = head->disp->engine.subdev.device;
|
||||
const u32 hoff = head->id * 0x540 + (state == &head->arm) * 4;
|
||||
u32 data;
|
||||
|
||||
data = nvkm_rd32(device, 0x610ae8 + hoff);
|
||||
state->vblanke = (data & 0xffff0000) >> 16;
|
||||
state->hblanke = (data & 0x0000ffff);
|
||||
data = nvkm_rd32(device, 0x610af0 + hoff);
|
||||
state->vblanks = (data & 0xffff0000) >> 16;
|
||||
state->hblanks = (data & 0x0000ffff);
|
||||
data = nvkm_rd32(device, 0x610af8 + hoff);
|
||||
state->vtotal = (data & 0xffff0000) >> 16;
|
||||
state->htotal = (data & 0x0000ffff);
|
||||
data = nvkm_rd32(device, 0x610b00 + hoff);
|
||||
state->vsynce = (data & 0xffff0000) >> 16;
|
||||
state->hsynce = (data & 0x0000ffff);
|
||||
state->hz = (nvkm_rd32(device, 0x610ad0 + hoff) & 0x003fffff) * 1000;
|
||||
}
|
||||
|
||||
static const struct nvkm_head_func
|
||||
nv50_head = {
|
||||
.state = nv50_head_state,
|
||||
.rgpos = nv50_head_rgpos,
|
||||
.rgclk = nv50_head_rgclk,
|
||||
.vblank_get = nv50_head_vblank_get,
|
||||
.vblank_put = nv50_head_vblank_put,
|
||||
};
|
||||
|
||||
int
|
||||
nv50_head_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
return nvkm_head_new_(&nv50_head, disp, id);
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright 2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors: Ben Skeggs <bskeggs@redhat.com>
|
||||
*/
|
||||
#include "ior.h"
|
||||
|
||||
static const char *
|
||||
nvkm_ior_name[] = {
|
||||
[DAC] = "DAC",
|
||||
[SOR] = "SOR",
|
||||
[PIOR] = "PIOR",
|
||||
};
|
||||
|
||||
struct nvkm_ior *
|
||||
nvkm_ior_find(struct nvkm_disp *disp, enum nvkm_ior_type type, int id)
|
||||
{
|
||||
struct nvkm_ior *ior;
|
||||
list_for_each_entry(ior, &disp->ior, head) {
|
||||
if (ior->type == type && (id < 0 || ior->id == id))
|
||||
return ior;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
nvkm_ior_del(struct nvkm_ior **pior)
|
||||
{
|
||||
struct nvkm_ior *ior = *pior;
|
||||
if (ior) {
|
||||
IOR_DBG(ior, "dtor");
|
||||
list_del(&ior->head);
|
||||
kfree(*pior);
|
||||
*pior = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nvkm_ior_new_(const struct nvkm_ior_func *func, struct nvkm_disp *disp,
|
||||
enum nvkm_ior_type type, int id)
|
||||
{
|
||||
struct nvkm_ior *ior;
|
||||
if (!(ior = kzalloc(sizeof(*ior), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
ior->func = func;
|
||||
ior->disp = disp;
|
||||
ior->type = type;
|
||||
ior->id = id;
|
||||
snprintf(ior->name, sizeof(ior->name), "%s-%d",
|
||||
nvkm_ior_name[ior->type], ior->id);
|
||||
list_add_tail(&ior->head, &disp->ior);
|
||||
IOR_DBG(ior, "ctor");
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
#ifndef __NVKM_DISP_IOR_H__
|
||||
#define __NVKM_DISP_IOR_H__
|
||||
#include "priv.h"
|
||||
struct nvkm_i2c_aux;
|
||||
|
||||
struct nvkm_ior {
|
||||
const struct nvkm_ior_func *func;
|
||||
struct nvkm_disp *disp;
|
||||
enum nvkm_ior_type {
|
||||
DAC,
|
||||
SOR,
|
||||
PIOR,
|
||||
} type;
|
||||
int id;
|
||||
char name[8];
|
||||
|
||||
struct list_head head;
|
||||
|
||||
struct nvkm_ior_state {
|
||||
struct nvkm_outp *outp;
|
||||
unsigned rgdiv;
|
||||
unsigned proto_evo:4;
|
||||
enum nvkm_ior_proto {
|
||||
CRT,
|
||||
TMDS,
|
||||
LVDS,
|
||||
DP,
|
||||
UNKNOWN
|
||||
} proto:3;
|
||||
unsigned link:2;
|
||||
unsigned head:4;
|
||||
} arm, asy;
|
||||
|
||||
/* Armed DP state. */
|
||||
struct {
|
||||
bool mst;
|
||||
bool ef;
|
||||
u8 nr;
|
||||
u8 bw;
|
||||
} dp;
|
||||
};
|
||||
|
||||
struct nvkm_ior_func {
|
||||
struct {
|
||||
int (*get)(struct nvkm_outp *, int *link);
|
||||
void (*set)(struct nvkm_outp *, struct nvkm_ior *);
|
||||
} route;
|
||||
|
||||
void (*state)(struct nvkm_ior *, struct nvkm_ior_state *);
|
||||
void (*power)(struct nvkm_ior *, bool normal, bool pu,
|
||||
bool data, bool vsync, bool hsync);
|
||||
int (*sense)(struct nvkm_ior *, u32 loadval);
|
||||
void (*clock)(struct nvkm_ior *);
|
||||
void (*war_2)(struct nvkm_ior *);
|
||||
void (*war_3)(struct nvkm_ior *);
|
||||
|
||||
struct {
|
||||
void (*ctrl)(struct nvkm_ior *, int head, bool enable,
|
||||
u8 max_ac_packet, u8 rekey, u8 *avi, u8 avi_size,
|
||||
u8 *vendor, u8 vendor_size);
|
||||
} hdmi;
|
||||
|
||||
struct {
|
||||
u8 lanes[4];
|
||||
int (*links)(struct nvkm_ior *, struct nvkm_i2c_aux *);
|
||||
void (*power)(struct nvkm_ior *, int nr);
|
||||
void (*pattern)(struct nvkm_ior *, int pattern);
|
||||
void (*drive)(struct nvkm_ior *, int ln, int pc,
|
||||
int dc, int pe, int tx_pu);
|
||||
void (*vcpi)(struct nvkm_ior *, int head, u8 slot,
|
||||
u8 slot_nr, u16 pbn, u16 aligned);
|
||||
void (*audio)(struct nvkm_ior *, int head, bool enable);
|
||||
void (*audio_sym)(struct nvkm_ior *, int head, u16 h, u32 v);
|
||||
void (*activesym)(struct nvkm_ior *, int head,
|
||||
u8 TU, u8 VTUa, u8 VTUf, u8 VTUi);
|
||||
void (*watermark)(struct nvkm_ior *, int head, u8 watermark);
|
||||
} dp;
|
||||
|
||||
struct {
|
||||
void (*hpd)(struct nvkm_ior *, int head, bool present);
|
||||
void (*eld)(struct nvkm_ior *, u8 *data, u8 size);
|
||||
} hda;
|
||||
};
|
||||
|
||||
int nvkm_ior_new_(const struct nvkm_ior_func *func, struct nvkm_disp *,
|
||||
enum nvkm_ior_type type, int id);
|
||||
void nvkm_ior_del(struct nvkm_ior **);
|
||||
struct nvkm_ior *nvkm_ior_find(struct nvkm_disp *, enum nvkm_ior_type, int id);
|
||||
|
||||
static inline u32
|
||||
nv50_ior_base(struct nvkm_ior *ior)
|
||||
{
|
||||
return ior->id * 0x800;
|
||||
}
|
||||
|
||||
void nv50_dac_power(struct nvkm_ior *, bool, bool, bool, bool, bool);
|
||||
int nv50_dac_sense(struct nvkm_ior *, u32);
|
||||
|
||||
void nv50_pior_depth(struct nvkm_ior *, struct nvkm_ior_state *, u32 ctrl);
|
||||
|
||||
static inline u32
|
||||
nv50_sor_link(struct nvkm_ior *ior)
|
||||
{
|
||||
return nv50_ior_base(ior) + ((ior->asy.link == 2) * 0x80);
|
||||
}
|
||||
|
||||
int nv50_sor_new_(const struct nvkm_ior_func *, struct nvkm_disp *, int id);
|
||||
void nv50_sor_state(struct nvkm_ior *, struct nvkm_ior_state *);
|
||||
void nv50_sor_power(struct nvkm_ior *, bool, bool, bool, bool, bool);
|
||||
void nv50_sor_clock(struct nvkm_ior *);
|
||||
|
||||
void g94_sor_state(struct nvkm_ior *, struct nvkm_ior_state *);
|
||||
int g94_sor_dp_links(struct nvkm_ior *, struct nvkm_i2c_aux *);
|
||||
void g94_sor_dp_power(struct nvkm_ior *, int);
|
||||
void g94_sor_dp_pattern(struct nvkm_ior *, int);
|
||||
void g94_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int);
|
||||
void g94_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32);
|
||||
void g94_sor_dp_activesym(struct nvkm_ior *, int, u8, u8, u8, u8);
|
||||
void g94_sor_dp_watermark(struct nvkm_ior *, int, u8);
|
||||
|
||||
void gt215_sor_dp_audio(struct nvkm_ior *, int, bool);
|
||||
|
||||
int gf119_sor_new_(const struct nvkm_ior_func *, struct nvkm_disp *, int id);
|
||||
void gf119_sor_state(struct nvkm_ior *, struct nvkm_ior_state *);
|
||||
void gf119_sor_clock(struct nvkm_ior *);
|
||||
int gf119_sor_dp_links(struct nvkm_ior *, struct nvkm_i2c_aux *);
|
||||
void gf119_sor_dp_pattern(struct nvkm_ior *, int);
|
||||
void gf119_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int);
|
||||
void gf119_sor_dp_vcpi(struct nvkm_ior *, int, u8, u8, u16, u16);
|
||||
void gf119_sor_dp_audio(struct nvkm_ior *, int, bool);
|
||||
void gf119_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32);
|
||||
void gf119_sor_dp_watermark(struct nvkm_ior *, int, u8);
|
||||
|
||||
void gm107_sor_dp_pattern(struct nvkm_ior *, int);
|
||||
|
||||
void g84_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
|
||||
void gt215_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
|
||||
void gf119_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
|
||||
void gk104_hdmi_ctrl(struct nvkm_ior *, int, bool, u8, u8, u8 *, u8 , u8 *, u8);
|
||||
|
||||
void gt215_hda_hpd(struct nvkm_ior *, int, bool);
|
||||
void gt215_hda_eld(struct nvkm_ior *, u8 *, u8);
|
||||
|
||||
void gf119_hda_hpd(struct nvkm_ior *, int, bool);
|
||||
void gf119_hda_eld(struct nvkm_ior *, u8 *, u8);
|
||||
|
||||
#define IOR_MSG(i,l,f,a...) do { \
|
||||
struct nvkm_ior *_ior = (i); \
|
||||
nvkm_##l(&_ior->disp->engine.subdev, "%s: "f, _ior->name, ##a); \
|
||||
} while(0)
|
||||
#define IOR_WARN(i,f,a...) IOR_MSG((i), warn, f, ##a)
|
||||
#define IOR_DBG(i,f,a...) IOR_MSG((i), debug, f, ##a)
|
||||
|
||||
int nv50_dac_new(struct nvkm_disp *, int);
|
||||
int gf119_dac_new(struct nvkm_disp *, int);
|
||||
|
||||
int nv50_pior_new(struct nvkm_disp *, int);
|
||||
|
||||
int nv50_sor_new(struct nvkm_disp *, int);
|
||||
int g84_sor_new(struct nvkm_disp *, int);
|
||||
int g94_sor_new(struct nvkm_disp *, int);
|
||||
int mcp77_sor_new(struct nvkm_disp *, int);
|
||||
int gt215_sor_new(struct nvkm_disp *, int);
|
||||
int mcp89_sor_new(struct nvkm_disp *, int);
|
||||
int gf119_sor_new(struct nvkm_disp *, int);
|
||||
int gk104_sor_new(struct nvkm_disp *, int);
|
||||
int gm107_sor_new(struct nvkm_disp *, int);
|
||||
int gm200_sor_new(struct nvkm_disp *, int);
|
||||
#endif
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
static const struct nv50_disp_func
|
||||
mcp77_disp = {
|
||||
.intr = nv50_disp_intr,
|
||||
.uevent = &nv50_disp_chan_uevent,
|
||||
.super = nv50_disp_super,
|
||||
.root = &g94_disp_root_oclass,
|
||||
.head.new = nv50_head_new,
|
||||
.dac = { .nr = 3, .new = nv50_dac_new },
|
||||
.sor = { .nr = 4, .new = mcp77_sor_new },
|
||||
.pior = { .nr = 3, .new = nv50_pior_new },
|
||||
};
|
||||
|
||||
int
|
||||
mcp77_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp)
|
||||
{
|
||||
return nv50_disp_new_(&mcp77_disp, device, index, 2, pdisp);
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright 2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
static const struct nv50_disp_func
|
||||
mcp89_disp = {
|
||||
.intr = nv50_disp_intr,
|
||||
.uevent = &nv50_disp_chan_uevent,
|
||||
.super = nv50_disp_super,
|
||||
.root = >215_disp_root_oclass,
|
||||
.head.new = nv50_head_new,
|
||||
.dac = { .nr = 3, .new = nv50_dac_new },
|
||||
.sor = { .nr = 4, .new = mcp89_sor_new },
|
||||
.pior = { .nr = 3, .new = nv50_pior_new },
|
||||
};
|
||||
|
||||
int
|
||||
mcp89_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp)
|
||||
{
|
||||
return nv50_disp_new_(&mcp89_disp, device, index, 2, pdisp);
|
||||
}
|
|
@ -22,6 +22,7 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "priv.h"
|
||||
#include "head.h"
|
||||
|
||||
static const struct nvkm_disp_oclass *
|
||||
nv04_disp_root(struct nvkm_disp *disp)
|
||||
|
@ -29,20 +30,6 @@ nv04_disp_root(struct nvkm_disp *disp)
|
|||
return &nv04_disp_root_oclass;
|
||||
}
|
||||
|
||||
static void
|
||||
nv04_disp_vblank_init(struct nvkm_disp *disp, int head)
|
||||
{
|
||||
struct nvkm_device *device = disp->engine.subdev.device;
|
||||
nvkm_wr32(device, 0x600140 + (head * 0x2000) , 0x00000001);
|
||||
}
|
||||
|
||||
static void
|
||||
nv04_disp_vblank_fini(struct nvkm_disp *disp, int head)
|
||||
{
|
||||
struct nvkm_device *device = disp->engine.subdev.device;
|
||||
nvkm_wr32(device, 0x600140 + (head * 0x2000) , 0x00000000);
|
||||
}
|
||||
|
||||
static void
|
||||
nv04_disp_intr(struct nvkm_disp *disp)
|
||||
{
|
||||
|
@ -74,12 +61,22 @@ static const struct nvkm_disp_func
|
|||
nv04_disp = {
|
||||
.intr = nv04_disp_intr,
|
||||
.root = nv04_disp_root,
|
||||
.head.vblank_init = nv04_disp_vblank_init,
|
||||
.head.vblank_fini = nv04_disp_vblank_fini,
|
||||
};
|
||||
|
||||
int
|
||||
nv04_disp_new(struct nvkm_device *device, int index, struct nvkm_disp **pdisp)
|
||||
{
|
||||
return nvkm_disp_new_(&nv04_disp, device, index, 2, pdisp);
|
||||
int ret, i;
|
||||
|
||||
ret = nvkm_disp_new_(&nv04_disp, device, index, pdisp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
ret = nv04_head_new(*pdisp, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,18 +2,13 @@
|
|||
#define __NV50_DISP_H__
|
||||
#define nv50_disp(p) container_of((p), struct nv50_disp, base)
|
||||
#include "priv.h"
|
||||
struct nvkm_output;
|
||||
struct nvkm_output_dp;
|
||||
|
||||
#define NV50_DISP_MTHD_ struct nvkm_object *object, \
|
||||
struct nv50_disp *disp, 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 nvkm_head;
|
||||
|
||||
struct nv50_disp {
|
||||
const struct nv50_disp_func *func;
|
||||
struct nvkm_disp base;
|
||||
|
||||
struct workqueue_struct *wq;
|
||||
struct work_struct supervisor;
|
||||
u32 super;
|
||||
|
||||
|
@ -30,42 +25,18 @@ struct nv50_disp {
|
|||
struct nv50_disp_chan *chan[17];
|
||||
};
|
||||
|
||||
int nv50_disp_root_scanoutpos(NV50_DISP_MTHD_V0);
|
||||
|
||||
int gf119_disp_root_scanoutpos(NV50_DISP_MTHD_V0);
|
||||
|
||||
int nv50_dac_power(NV50_DISP_MTHD_V1);
|
||||
int nv50_dac_sense(NV50_DISP_MTHD_V1);
|
||||
|
||||
int gt215_hda_eld(NV50_DISP_MTHD_V1);
|
||||
int gf119_hda_eld(NV50_DISP_MTHD_V1);
|
||||
|
||||
int g84_hdmi_ctrl(NV50_DISP_MTHD_V1);
|
||||
int gt215_hdmi_ctrl(NV50_DISP_MTHD_V1);
|
||||
int gf119_hdmi_ctrl(NV50_DISP_MTHD_V1);
|
||||
int gk104_hdmi_ctrl(NV50_DISP_MTHD_V1);
|
||||
|
||||
int nv50_sor_power(NV50_DISP_MTHD_V1);
|
||||
int nv50_pior_power(NV50_DISP_MTHD_V1);
|
||||
void nv50_disp_super_1(struct nv50_disp *);
|
||||
void nv50_disp_super_1_0(struct nv50_disp *, struct nvkm_head *);
|
||||
void nv50_disp_super_2_0(struct nv50_disp *, struct nvkm_head *);
|
||||
void nv50_disp_super_2_1(struct nv50_disp *, struct nvkm_head *);
|
||||
void nv50_disp_super_2_2(struct nv50_disp *, struct nvkm_head *);
|
||||
void nv50_disp_super_3_0(struct nv50_disp *, struct nvkm_head *);
|
||||
|
||||
int nv50_disp_new_(const struct nv50_disp_func *, struct nvkm_device *,
|
||||
int index, int heads, struct nvkm_disp **);
|
||||
int gf119_disp_new_(const struct nv50_disp_func *, struct nvkm_device *,
|
||||
int index, struct nvkm_disp **);
|
||||
|
||||
struct nv50_disp_func_outp {
|
||||
int (* crt)(struct nvkm_disp *, int index, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
int (* tv)(struct nvkm_disp *, int index, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
int (*tmds)(struct nvkm_disp *, int index, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
int (*lvds)(struct nvkm_disp *, int index, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
int (* dp)(struct nvkm_disp *, int index, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
};
|
||||
|
||||
struct nv50_disp_func {
|
||||
void (*intr)(struct nv50_disp *);
|
||||
void (*intr_error)(struct nv50_disp *, int chid);
|
||||
|
@ -76,44 +47,33 @@ struct nv50_disp_func {
|
|||
const struct nvkm_disp_oclass *root;
|
||||
|
||||
struct {
|
||||
void (*vblank_init)(struct nv50_disp *, int head);
|
||||
void (*vblank_fini)(struct nv50_disp *, int head);
|
||||
int (*scanoutpos)(NV50_DISP_MTHD_V0);
|
||||
int (*new)(struct nvkm_disp *, int id);
|
||||
} head;
|
||||
|
||||
struct {
|
||||
const struct nv50_disp_func_outp internal;
|
||||
const struct nv50_disp_func_outp external;
|
||||
} outp;
|
||||
|
||||
struct {
|
||||
int nr;
|
||||
int (*power)(NV50_DISP_MTHD_V1);
|
||||
int (*sense)(NV50_DISP_MTHD_V1);
|
||||
int (*new)(struct nvkm_disp *, int id);
|
||||
} dac;
|
||||
|
||||
struct {
|
||||
int nr;
|
||||
int (*power)(NV50_DISP_MTHD_V1);
|
||||
int (*hda_eld)(NV50_DISP_MTHD_V1);
|
||||
int (*hdmi)(NV50_DISP_MTHD_V1);
|
||||
void (*magic)(struct nvkm_output *);
|
||||
int (*new)(struct nvkm_disp *, int id);
|
||||
} sor;
|
||||
|
||||
struct {
|
||||
int nr;
|
||||
int (*power)(NV50_DISP_MTHD_V1);
|
||||
int (*new)(struct nvkm_disp *, int id);
|
||||
} pior;
|
||||
};
|
||||
|
||||
void nv50_disp_vblank_init(struct nv50_disp *, int);
|
||||
void nv50_disp_vblank_fini(struct nv50_disp *, int);
|
||||
void nv50_disp_intr(struct nv50_disp *);
|
||||
void nv50_disp_intr_supervisor(struct work_struct *);
|
||||
void nv50_disp_super(struct work_struct *);
|
||||
|
||||
void gf119_disp_vblank_init(struct nv50_disp *, int);
|
||||
void gf119_disp_vblank_fini(struct nv50_disp *, int);
|
||||
void gf119_disp_intr(struct nv50_disp *);
|
||||
void gf119_disp_intr_supervisor(struct work_struct *);
|
||||
void gf119_disp_super(struct work_struct *);
|
||||
void gf119_disp_intr_error(struct nv50_disp *, int);
|
||||
|
||||
void nv50_disp_dptmds_war_2(struct nv50_disp *, struct dcb_output *);
|
||||
void nv50_disp_dptmds_war_3(struct nv50_disp *, struct dcb_output *);
|
||||
void nv50_disp_update_sppll1(struct nv50_disp *);
|
||||
#endif
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "channv50.h"
|
||||
#include "head.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
#include <core/client.h>
|
||||
|
@ -48,7 +49,7 @@ nv50_disp_oimm_new(const struct nv50_disp_chan_func *func,
|
|||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_ioctl(parent, "create disp overlay vers %d head %d\n",
|
||||
args->v0.version, args->v0.head);
|
||||
if (args->v0.head > disp->base.head.nr)
|
||||
if (!nvkm_head_find(&disp->base, args->v0.head))
|
||||
return -EINVAL;
|
||||
head = args->v0.head;
|
||||
} else
|
||||
|
|
|
@ -22,29 +22,207 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "outp.h"
|
||||
#include "ior.h"
|
||||
|
||||
#include <subdev/bios.h>
|
||||
#include <subdev/bios/dcb.h>
|
||||
#include <subdev/i2c.h>
|
||||
|
||||
void
|
||||
nvkm_output_fini(struct nvkm_output *outp)
|
||||
nvkm_outp_route(struct nvkm_disp *disp)
|
||||
{
|
||||
struct nvkm_outp *outp;
|
||||
struct nvkm_ior *ior;
|
||||
|
||||
list_for_each_entry(ior, &disp->ior, head) {
|
||||
if ((outp = ior->arm.outp) && ior->arm.outp != ior->asy.outp) {
|
||||
OUTP_DBG(outp, "release %s", ior->name);
|
||||
if (ior->func->route.set)
|
||||
ior->func->route.set(outp, NULL);
|
||||
ior->arm.outp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(ior, &disp->ior, head) {
|
||||
if ((outp = ior->asy.outp)) {
|
||||
OUTP_DBG(outp, "acquire %s", ior->name);
|
||||
if (ior->asy.outp != ior->arm.outp) {
|
||||
if (ior->func->route.set)
|
||||
ior->func->route.set(outp, ior);
|
||||
ior->arm.outp = ior->asy.outp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static enum nvkm_ior_proto
|
||||
nvkm_outp_xlat(struct nvkm_outp *outp, enum nvkm_ior_type *type)
|
||||
{
|
||||
switch (outp->info.location) {
|
||||
case 0:
|
||||
switch (outp->info.type) {
|
||||
case DCB_OUTPUT_ANALOG: *type = DAC; return CRT;
|
||||
case DCB_OUTPUT_TMDS : *type = SOR; return TMDS;
|
||||
case DCB_OUTPUT_LVDS : *type = SOR; return LVDS;
|
||||
case DCB_OUTPUT_DP : *type = SOR; return DP;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
switch (outp->info.type) {
|
||||
case DCB_OUTPUT_TMDS: *type = PIOR; return TMDS;
|
||||
case DCB_OUTPUT_DP : *type = PIOR; return TMDS; /* not a bug */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
WARN_ON(1);
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
void
|
||||
nvkm_outp_release(struct nvkm_outp *outp, u8 user)
|
||||
{
|
||||
struct nvkm_ior *ior = outp->ior;
|
||||
OUTP_TRACE(outp, "release %02x &= %02x %p", outp->acquired, ~user, ior);
|
||||
if (ior) {
|
||||
outp->acquired &= ~user;
|
||||
if (!outp->acquired) {
|
||||
outp->ior->asy.outp = NULL;
|
||||
outp->ior = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
nvkm_outp_acquire_ior(struct nvkm_outp *outp, u8 user, struct nvkm_ior *ior)
|
||||
{
|
||||
outp->ior = ior;
|
||||
outp->ior->asy.outp = outp;
|
||||
outp->ior->asy.link = outp->info.sorconf.link;
|
||||
outp->acquired |= user;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nvkm_outp_acquire(struct nvkm_outp *outp, u8 user)
|
||||
{
|
||||
struct nvkm_ior *ior = outp->ior;
|
||||
enum nvkm_ior_proto proto;
|
||||
enum nvkm_ior_type type;
|
||||
|
||||
OUTP_TRACE(outp, "acquire %02x |= %02x %p", outp->acquired, user, ior);
|
||||
if (ior) {
|
||||
outp->acquired |= user;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Lookup a compatible, and unused, OR to assign to the device. */
|
||||
proto = nvkm_outp_xlat(outp, &type);
|
||||
if (proto == UNKNOWN)
|
||||
return -ENOSYS;
|
||||
|
||||
/* First preference is to reuse the OR that is currently armed
|
||||
* on HW, if any, in order to prevent unnecessary switching.
|
||||
*/
|
||||
list_for_each_entry(ior, &outp->disp->ior, head) {
|
||||
if (!ior->asy.outp && ior->arm.outp == outp)
|
||||
return nvkm_outp_acquire_ior(outp, user, ior);
|
||||
}
|
||||
|
||||
/* Failing that, a completely unused OR is the next best thing. */
|
||||
list_for_each_entry(ior, &outp->disp->ior, head) {
|
||||
if (!ior->asy.outp && ior->type == type && !ior->arm.outp &&
|
||||
(ior->func->route.set || ior->id == __ffs(outp->info.or)))
|
||||
return nvkm_outp_acquire_ior(outp, user, ior);
|
||||
}
|
||||
|
||||
/* Last resort is to assign an OR that's already active on HW,
|
||||
* but will be released during the next modeset.
|
||||
*/
|
||||
list_for_each_entry(ior, &outp->disp->ior, head) {
|
||||
if (!ior->asy.outp && ior->type == type &&
|
||||
(ior->func->route.set || ior->id == __ffs(outp->info.or)))
|
||||
return nvkm_outp_acquire_ior(outp, user, ior);
|
||||
}
|
||||
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
void
|
||||
nvkm_outp_fini(struct nvkm_outp *outp)
|
||||
{
|
||||
if (outp->func->fini)
|
||||
outp->func->fini(outp);
|
||||
}
|
||||
|
||||
void
|
||||
nvkm_output_init(struct nvkm_output *outp)
|
||||
static void
|
||||
nvkm_outp_init_route(struct nvkm_outp *outp)
|
||||
{
|
||||
struct nvkm_disp *disp = outp->disp;
|
||||
enum nvkm_ior_proto proto;
|
||||
enum nvkm_ior_type type;
|
||||
struct nvkm_ior *ior;
|
||||
int id, link;
|
||||
|
||||
/* Find any OR from the class that is able to support this device. */
|
||||
proto = nvkm_outp_xlat(outp, &type);
|
||||
if (proto == UNKNOWN)
|
||||
return;
|
||||
|
||||
ior = nvkm_ior_find(disp, type, -1);
|
||||
if (!ior) {
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Determine the specific OR, if any, this device is attached to. */
|
||||
if (ior->func->route.get) {
|
||||
id = ior->func->route.get(outp, &link);
|
||||
if (id < 0) {
|
||||
OUTP_DBG(outp, "no route");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
/* Prior to DCB 4.1, this is hardwired like so. */
|
||||
id = ffs(outp->info.or) - 1;
|
||||
link = (ior->type == SOR) ? outp->info.sorconf.link : 0;
|
||||
}
|
||||
|
||||
ior = nvkm_ior_find(disp, type, id);
|
||||
if (!ior) {
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Determine if the OR is already configured for this device. */
|
||||
ior->func->state(ior, &ior->arm);
|
||||
if (!ior->arm.head || ior->arm.proto != proto) {
|
||||
OUTP_DBG(outp, "no heads (%x %d %d)", ior->arm.head,
|
||||
ior->arm.proto, proto);
|
||||
return;
|
||||
}
|
||||
|
||||
OUTP_DBG(outp, "on %s link %x", ior->name, ior->arm.link);
|
||||
ior->arm.outp = outp;
|
||||
}
|
||||
|
||||
void
|
||||
nvkm_outp_init(struct nvkm_outp *outp)
|
||||
{
|
||||
nvkm_outp_init_route(outp);
|
||||
if (outp->func->init)
|
||||
outp->func->init(outp);
|
||||
}
|
||||
|
||||
void
|
||||
nvkm_output_del(struct nvkm_output **poutp)
|
||||
nvkm_outp_del(struct nvkm_outp **poutp)
|
||||
{
|
||||
struct nvkm_output *outp = *poutp;
|
||||
struct nvkm_outp *outp = *poutp;
|
||||
if (outp && !WARN_ON(!outp->func)) {
|
||||
if (outp->func->dtor)
|
||||
*poutp = outp->func->dtor(outp);
|
||||
|
@ -53,11 +231,13 @@ nvkm_output_del(struct nvkm_output **poutp)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nvkm_output_ctor(const struct nvkm_output_func *func, struct nvkm_disp *disp,
|
||||
int index, struct dcb_output *dcbE, struct nvkm_output *outp)
|
||||
int
|
||||
nvkm_outp_ctor(const struct nvkm_outp_func *func, struct nvkm_disp *disp,
|
||||
int index, struct dcb_output *dcbE, struct nvkm_outp *outp)
|
||||
{
|
||||
struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c;
|
||||
enum nvkm_ior_proto proto;
|
||||
enum nvkm_ior_type type;
|
||||
|
||||
outp->func = func;
|
||||
outp->disp = disp;
|
||||
|
@ -72,16 +252,24 @@ nvkm_output_ctor(const struct nvkm_output_func *func, struct nvkm_disp *disp,
|
|||
outp->info.type >= 2 ? outp->info.sorconf.link : 0,
|
||||
outp->info.connector, outp->info.i2c_index,
|
||||
outp->info.bus, outp->info.heads);
|
||||
|
||||
/* Cull output paths we can't map to an output resource. */
|
||||
proto = nvkm_outp_xlat(outp, &type);
|
||||
if (proto == UNKNOWN)
|
||||
return -ENODEV;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nvkm_outp_func
|
||||
nvkm_outp = {
|
||||
};
|
||||
|
||||
int
|
||||
nvkm_output_new_(const struct nvkm_output_func *func,
|
||||
struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
|
||||
struct nvkm_output **poutp)
|
||||
nvkm_outp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
|
||||
struct nvkm_outp **poutp)
|
||||
{
|
||||
if (!(*poutp = kzalloc(sizeof(**poutp), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
|
||||
nvkm_output_ctor(func, disp, index, dcbE, *poutp);
|
||||
return 0;
|
||||
return nvkm_outp_ctor(&nvkm_outp, disp, index, dcbE, *poutp);
|
||||
}
|
||||
|
|
|
@ -5,47 +5,46 @@
|
|||
#include <subdev/bios.h>
|
||||
#include <subdev/bios/dcb.h>
|
||||
|
||||
struct nvkm_output {
|
||||
const struct nvkm_output_func *func;
|
||||
struct nvkm_outp {
|
||||
const struct nvkm_outp_func *func;
|
||||
struct nvkm_disp *disp;
|
||||
int index;
|
||||
struct dcb_output info;
|
||||
|
||||
// whatever (if anything) is pointed at by the dcb device entry
|
||||
struct nvkm_i2c_bus *i2c;
|
||||
int or;
|
||||
|
||||
struct list_head head;
|
||||
struct nvkm_connector *conn;
|
||||
struct nvkm_conn *conn;
|
||||
|
||||
/* Assembly state. */
|
||||
#define NVKM_OUTP_PRIV 1
|
||||
#define NVKM_OUTP_USER 2
|
||||
u8 acquired:2;
|
||||
struct nvkm_ior *ior;
|
||||
};
|
||||
|
||||
struct nvkm_output_func {
|
||||
void *(*dtor)(struct nvkm_output *);
|
||||
void (*init)(struct nvkm_output *);
|
||||
void (*fini)(struct nvkm_output *);
|
||||
int nvkm_outp_ctor(const struct nvkm_outp_func *, struct nvkm_disp *,
|
||||
int index, struct dcb_output *, struct nvkm_outp *);
|
||||
int nvkm_outp_new(struct nvkm_disp *, int index, struct dcb_output *,
|
||||
struct nvkm_outp **);
|
||||
void nvkm_outp_del(struct nvkm_outp **);
|
||||
void nvkm_outp_init(struct nvkm_outp *);
|
||||
void nvkm_outp_fini(struct nvkm_outp *);
|
||||
int nvkm_outp_acquire(struct nvkm_outp *, u8 user);
|
||||
void nvkm_outp_release(struct nvkm_outp *, u8 user);
|
||||
void nvkm_outp_route(struct nvkm_disp *);
|
||||
|
||||
struct nvkm_outp_func {
|
||||
void *(*dtor)(struct nvkm_outp *);
|
||||
void (*init)(struct nvkm_outp *);
|
||||
void (*fini)(struct nvkm_outp *);
|
||||
int (*acquire)(struct nvkm_outp *);
|
||||
void (*release)(struct nvkm_outp *, struct nvkm_ior *);
|
||||
};
|
||||
|
||||
void nvkm_output_ctor(const struct nvkm_output_func *, struct nvkm_disp *,
|
||||
int index, struct dcb_output *, struct nvkm_output *);
|
||||
int nvkm_output_new_(const struct nvkm_output_func *, struct nvkm_disp *,
|
||||
int index, struct dcb_output *, struct nvkm_output **);
|
||||
void nvkm_output_del(struct nvkm_output **);
|
||||
void nvkm_output_init(struct nvkm_output *);
|
||||
void nvkm_output_fini(struct nvkm_output *);
|
||||
|
||||
int nv50_dac_output_new(struct nvkm_disp *, int, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
int nv50_sor_output_new(struct nvkm_disp *, int, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
int nv50_pior_output_new(struct nvkm_disp *, int, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
|
||||
u32 g94_sor_dp_lane_map(struct nvkm_device *, u8 lane);
|
||||
|
||||
void gm200_sor_magic(struct nvkm_output *outp);
|
||||
|
||||
#define OUTP_MSG(o,l,f,a...) do { \
|
||||
struct nvkm_output *_outp = (o); \
|
||||
struct nvkm_outp *_outp = (o); \
|
||||
nvkm_##l(&_outp->disp->engine.subdev, "outp %02x:%04x:%04x: "f"\n", \
|
||||
_outp->index, _outp->info.hasht, _outp->info.hashm, ##a); \
|
||||
} while(0)
|
||||
|
|
|
@ -1,282 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "outpdp.h"
|
||||
#include "conn.h"
|
||||
#include "dport.h"
|
||||
#include "priv.h"
|
||||
|
||||
#include <subdev/i2c.h>
|
||||
|
||||
#include <nvif/event.h>
|
||||
|
||||
int
|
||||
nvkm_output_dp_train(struct nvkm_output *base, u32 datarate)
|
||||
{
|
||||
struct nvkm_output_dp *outp = nvkm_output_dp(base);
|
||||
bool retrain = true;
|
||||
u8 link[2], stat[3];
|
||||
u32 linkrate;
|
||||
int ret, i;
|
||||
|
||||
mutex_lock(&outp->mutex);
|
||||
|
||||
/* check that the link is trained at a high enough rate */
|
||||
ret = nvkm_rdaux(outp->aux, DPCD_LC00_LINK_BW_SET, link, 2);
|
||||
if (ret) {
|
||||
OUTP_DBG(&outp->base,
|
||||
"failed to read link config, assuming no sink");
|
||||
goto done;
|
||||
}
|
||||
|
||||
linkrate = link[0] * 27000 * (link[1] & DPCD_LC01_LANE_COUNT_SET);
|
||||
linkrate = (linkrate * 8) / 10; /* 8B/10B coding overhead */
|
||||
datarate = (datarate + 9) / 10; /* -> decakilobits */
|
||||
if (linkrate < datarate) {
|
||||
OUTP_DBG(&outp->base, "link not trained at sufficient rate");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* check that link is still trained */
|
||||
ret = nvkm_rdaux(outp->aux, DPCD_LS02, stat, 3);
|
||||
if (ret) {
|
||||
OUTP_DBG(&outp->base,
|
||||
"failed to read link status, assuming no sink");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) {
|
||||
for (i = 0; i < (link[1] & DPCD_LC01_LANE_COUNT_SET); i++) {
|
||||
u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f;
|
||||
if (!(lane & DPCD_LS02_LANE0_CR_DONE) ||
|
||||
!(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) ||
|
||||
!(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) {
|
||||
OUTP_DBG(&outp->base,
|
||||
"lane %d not equalised", lane);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
retrain = false;
|
||||
} else {
|
||||
OUTP_DBG(&outp->base, "no inter-lane alignment");
|
||||
}
|
||||
|
||||
done:
|
||||
if (retrain || !atomic_read(&outp->lt.done)) {
|
||||
/* no sink, but still need to configure source */
|
||||
if (outp->dpcd[DPCD_RC00_DPCD_REV] == 0x00) {
|
||||
outp->dpcd[DPCD_RC01_MAX_LINK_RATE] =
|
||||
outp->base.info.dpconf.link_bw;
|
||||
outp->dpcd[DPCD_RC02] =
|
||||
outp->base.info.dpconf.link_nr;
|
||||
}
|
||||
nvkm_dp_train(outp);
|
||||
}
|
||||
|
||||
mutex_unlock(&outp->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
nvkm_output_dp_enable(struct nvkm_output_dp *outp, bool enable)
|
||||
{
|
||||
struct nvkm_i2c_aux *aux = outp->aux;
|
||||
|
||||
if (enable) {
|
||||
if (!outp->present) {
|
||||
OUTP_DBG(&outp->base, "aux power -> always");
|
||||
nvkm_i2c_aux_monitor(aux, true);
|
||||
outp->present = true;
|
||||
}
|
||||
|
||||
if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, outp->dpcd,
|
||||
sizeof(outp->dpcd))) {
|
||||
nvkm_output_dp_train(&outp->base, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (outp->present) {
|
||||
OUTP_DBG(&outp->base, "aux power -> demand");
|
||||
nvkm_i2c_aux_monitor(aux, false);
|
||||
outp->present = false;
|
||||
}
|
||||
|
||||
atomic_set(&outp->lt.done, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_output_dp_hpd(struct nvkm_notify *notify)
|
||||
{
|
||||
const struct nvkm_i2c_ntfy_rep *line = notify->data;
|
||||
struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), hpd);
|
||||
struct nvkm_connector *conn = outp->base.conn;
|
||||
struct nvkm_disp *disp = outp->base.disp;
|
||||
struct nvif_notify_conn_rep_v0 rep = {};
|
||||
|
||||
OUTP_DBG(&outp->base, "HPD: %d", line->mask);
|
||||
nvkm_output_dp_enable(outp, true);
|
||||
|
||||
if (line->mask & NVKM_I2C_UNPLUG)
|
||||
rep.mask |= NVIF_NOTIFY_CONN_V0_UNPLUG;
|
||||
if (line->mask & NVKM_I2C_PLUG)
|
||||
rep.mask |= NVIF_NOTIFY_CONN_V0_PLUG;
|
||||
|
||||
nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep));
|
||||
return NVKM_NOTIFY_KEEP;
|
||||
}
|
||||
|
||||
static int
|
||||
nvkm_output_dp_irq(struct nvkm_notify *notify)
|
||||
{
|
||||
const struct nvkm_i2c_ntfy_rep *line = notify->data;
|
||||
struct nvkm_output_dp *outp = container_of(notify, typeof(*outp), irq);
|
||||
struct nvkm_connector *conn = outp->base.conn;
|
||||
struct nvkm_disp *disp = outp->base.disp;
|
||||
struct nvif_notify_conn_rep_v0 rep = {
|
||||
.mask = NVIF_NOTIFY_CONN_V0_IRQ,
|
||||
};
|
||||
|
||||
OUTP_DBG(&outp->base, "IRQ: %d", line->mask);
|
||||
nvkm_output_dp_train(&outp->base, 0);
|
||||
|
||||
nvkm_event_send(&disp->hpd, rep.mask, conn->index, &rep, sizeof(rep));
|
||||
return NVKM_NOTIFY_KEEP;
|
||||
}
|
||||
|
||||
static void
|
||||
nvkm_output_dp_fini(struct nvkm_output *base)
|
||||
{
|
||||
struct nvkm_output_dp *outp = nvkm_output_dp(base);
|
||||
nvkm_notify_put(&outp->hpd);
|
||||
nvkm_notify_put(&outp->irq);
|
||||
nvkm_output_dp_enable(outp, false);
|
||||
}
|
||||
|
||||
static void
|
||||
nvkm_output_dp_init(struct nvkm_output *base)
|
||||
{
|
||||
struct nvkm_output_dp *outp = nvkm_output_dp(base);
|
||||
nvkm_notify_put(&outp->base.conn->hpd);
|
||||
nvkm_output_dp_enable(outp, true);
|
||||
nvkm_notify_get(&outp->irq);
|
||||
nvkm_notify_get(&outp->hpd);
|
||||
}
|
||||
|
||||
static void *
|
||||
nvkm_output_dp_dtor(struct nvkm_output *base)
|
||||
{
|
||||
struct nvkm_output_dp *outp = nvkm_output_dp(base);
|
||||
nvkm_notify_fini(&outp->hpd);
|
||||
nvkm_notify_fini(&outp->irq);
|
||||
return outp;
|
||||
}
|
||||
|
||||
static const struct nvkm_output_func
|
||||
nvkm_output_dp_func = {
|
||||
.dtor = nvkm_output_dp_dtor,
|
||||
.init = nvkm_output_dp_init,
|
||||
.fini = nvkm_output_dp_fini,
|
||||
};
|
||||
|
||||
int
|
||||
nvkm_output_dp_ctor(const struct nvkm_output_dp_func *func,
|
||||
struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
|
||||
struct nvkm_i2c_aux *aux, struct nvkm_output_dp *outp)
|
||||
{
|
||||
struct nvkm_device *device = disp->engine.subdev.device;
|
||||
struct nvkm_bios *bios = device->bios;
|
||||
struct nvkm_i2c *i2c = device->i2c;
|
||||
u8 hdr, cnt, len;
|
||||
u32 data;
|
||||
int ret;
|
||||
|
||||
nvkm_output_ctor(&nvkm_output_dp_func, disp, index, dcbE, &outp->base);
|
||||
outp->func = func;
|
||||
outp->aux = aux;
|
||||
if (!outp->aux) {
|
||||
OUTP_ERR(&outp->base, "no aux");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* bios data is not optional */
|
||||
data = nvbios_dpout_match(bios, outp->base.info.hasht,
|
||||
outp->base.info.hashm, &outp->version,
|
||||
&hdr, &cnt, &len, &outp->info);
|
||||
if (!data) {
|
||||
OUTP_ERR(&outp->base, "no bios dp data");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
OUTP_DBG(&outp->base, "bios dp %02x %02x %02x %02x",
|
||||
outp->version, hdr, cnt, len);
|
||||
|
||||
/* link maintenance */
|
||||
ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_irq, true,
|
||||
&(struct nvkm_i2c_ntfy_req) {
|
||||
.mask = NVKM_I2C_IRQ,
|
||||
.port = outp->aux->id,
|
||||
},
|
||||
sizeof(struct nvkm_i2c_ntfy_req),
|
||||
sizeof(struct nvkm_i2c_ntfy_rep),
|
||||
&outp->irq);
|
||||
if (ret) {
|
||||
OUTP_ERR(&outp->base, "error monitoring aux irq: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_init(&outp->mutex);
|
||||
atomic_set(&outp->lt.done, 0);
|
||||
|
||||
/* hotplug detect, replaces gpio-based mechanism with aux events */
|
||||
ret = nvkm_notify_init(NULL, &i2c->event, nvkm_output_dp_hpd, true,
|
||||
&(struct nvkm_i2c_ntfy_req) {
|
||||
.mask = NVKM_I2C_PLUG | NVKM_I2C_UNPLUG,
|
||||
.port = outp->aux->id,
|
||||
},
|
||||
sizeof(struct nvkm_i2c_ntfy_req),
|
||||
sizeof(struct nvkm_i2c_ntfy_rep),
|
||||
&outp->hpd);
|
||||
if (ret) {
|
||||
OUTP_ERR(&outp->base, "error monitoring aux hpd: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nvkm_output_dp_new_(const struct nvkm_output_dp_func *func,
|
||||
struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
|
||||
struct nvkm_output **poutp)
|
||||
{
|
||||
struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c;
|
||||
struct nvkm_i2c_aux *aux = nvkm_i2c_aux_find(i2c, dcbE->i2c_index);
|
||||
struct nvkm_output_dp *outp;
|
||||
|
||||
if (!(outp = kzalloc(sizeof(*outp), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
*poutp = &outp->base;
|
||||
|
||||
return nvkm_output_dp_ctor(func, disp, index, dcbE, aux, outp);
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
#ifndef __NVKM_DISP_OUTP_DP_H__
|
||||
#define __NVKM_DISP_OUTP_DP_H__
|
||||
#define nvkm_output_dp(p) container_of((p), struct nvkm_output_dp, base)
|
||||
#ifndef MSG
|
||||
#define MSG(l,f,a...) \
|
||||
nvkm_##l(&outp->base.disp->engine.subdev, "%02x:%04x:%04x: "f, \
|
||||
outp->base.index, outp->base.info.hasht, \
|
||||
outp->base.info.hashm, ##a)
|
||||
#define DBG(f,a...) MSG(debug, f, ##a)
|
||||
#define ERR(f,a...) MSG(error, f, ##a)
|
||||
#endif
|
||||
#include "outp.h"
|
||||
|
||||
#include <core/notify.h>
|
||||
#include <subdev/bios.h>
|
||||
#include <subdev/bios/dp.h>
|
||||
|
||||
struct nvkm_output_dp {
|
||||
const struct nvkm_output_dp_func *func;
|
||||
struct nvkm_output base;
|
||||
|
||||
struct nvbios_dpout info;
|
||||
u8 version;
|
||||
|
||||
struct nvkm_i2c_aux *aux;
|
||||
|
||||
struct nvkm_notify irq;
|
||||
struct nvkm_notify hpd;
|
||||
bool present;
|
||||
u8 dpcd[16];
|
||||
|
||||
struct mutex mutex;
|
||||
struct {
|
||||
atomic_t done;
|
||||
bool mst;
|
||||
} lt;
|
||||
};
|
||||
|
||||
struct nvkm_output_dp_func {
|
||||
int (*pattern)(struct nvkm_output_dp *, int);
|
||||
int (*lnk_pwr)(struct nvkm_output_dp *, int nr);
|
||||
int (*lnk_ctl)(struct nvkm_output_dp *, int nr, int bw, bool ef);
|
||||
int (*drv_ctl)(struct nvkm_output_dp *, int ln, int vs, int pe, int pc);
|
||||
void (*vcpi)(struct nvkm_output_dp *, int head, u8 start_slot,
|
||||
u8 num_slots, u16 pbn, u16 aligned_pbn);
|
||||
};
|
||||
|
||||
int nvkm_output_dp_train(struct nvkm_output *, u32 rate);
|
||||
|
||||
int nvkm_output_dp_ctor(const struct nvkm_output_dp_func *, struct nvkm_disp *,
|
||||
int index, struct dcb_output *, struct nvkm_i2c_aux *,
|
||||
struct nvkm_output_dp *);
|
||||
int nvkm_output_dp_new_(const struct nvkm_output_dp_func *, struct nvkm_disp *,
|
||||
int index, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
|
||||
int nv50_pior_dp_new(struct nvkm_disp *, int, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
|
||||
int g94_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
int g94_sor_dp_lnk_pwr(struct nvkm_output_dp *, int);
|
||||
|
||||
int gf119_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
int gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *, int, int, bool);
|
||||
int gf119_sor_dp_drv_ctl(struct nvkm_output_dp *, int, int, int, int);
|
||||
void gf119_sor_dp_vcpi(struct nvkm_output_dp *, int, u8, u8, u16, u16);
|
||||
|
||||
int gm107_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
int gm107_sor_dp_pattern(struct nvkm_output_dp *, int);
|
||||
|
||||
int gm200_sor_dp_new(struct nvkm_disp *, int, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
#endif
|
|
@ -22,6 +22,7 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "dmacnv50.h"
|
||||
#include "head.h"
|
||||
#include "rootnv50.h"
|
||||
|
||||
#include <core/client.h>
|
||||
|
@ -50,7 +51,7 @@ nv50_disp_ovly_new(const struct nv50_disp_dmac_func *func,
|
|||
nvif_ioctl(parent, "create disp overlay channel dma vers %d "
|
||||
"pushbuf %016llx head %d\n",
|
||||
args->v0.version, args->v0.pushbuf, args->v0.head);
|
||||
if (args->v0.head > disp->base.head.nr)
|
||||
if (!nvkm_head_find(&disp->base, args->v0.head))
|
||||
return -EINVAL;
|
||||
push = args->v0.pushbuf;
|
||||
head = args->v0.head;
|
||||
|
|
|
@ -21,111 +21,114 @@
|
|||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "outpdp.h"
|
||||
#include "nv50.h"
|
||||
#include "ior.h"
|
||||
#include "head.h"
|
||||
|
||||
#include <core/client.h>
|
||||
#include <subdev/i2c.h>
|
||||
#include <subdev/timer.h>
|
||||
|
||||
#include <nvif/cl5070.h>
|
||||
#include <nvif/unpack.h>
|
||||
|
||||
int
|
||||
nv50_pior_power(NV50_DISP_MTHD_V1)
|
||||
static void
|
||||
nv50_pior_clock(struct nvkm_ior *pior)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
const u32 soff = outp->or * 0x800;
|
||||
union {
|
||||
struct nv50_disp_pior_pwr_v0 v0;
|
||||
} *args = data;
|
||||
u32 ctrl, type;
|
||||
int ret = -ENOSYS;
|
||||
|
||||
nvif_ioctl(object, "disp pior pwr size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_ioctl(object, "disp pior pwr vers %d state %d type %x\n",
|
||||
args->v0.version, args->v0.state, args->v0.type);
|
||||
if (args->v0.type > 0x0f)
|
||||
return -EINVAL;
|
||||
ctrl = !!args->v0.state;
|
||||
type = args->v0.type;
|
||||
} else
|
||||
return ret;
|
||||
|
||||
nvkm_msec(device, 2000,
|
||||
if (!(nvkm_rd32(device, 0x61e004 + soff) & 0x80000000))
|
||||
break;
|
||||
);
|
||||
nvkm_mask(device, 0x61e004 + soff, 0x80000101, 0x80000000 | ctrl);
|
||||
nvkm_msec(device, 2000,
|
||||
if (!(nvkm_rd32(device, 0x61e004 + soff) & 0x80000000))
|
||||
break;
|
||||
);
|
||||
disp->pior.type[outp->or] = type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* TMDS
|
||||
*****************************************************************************/
|
||||
static const struct nvkm_output_func
|
||||
nv50_pior_output_func = {
|
||||
};
|
||||
|
||||
int
|
||||
nv50_pior_output_new(struct nvkm_disp *disp, int index,
|
||||
struct dcb_output *dcbE, struct nvkm_output **poutp)
|
||||
{
|
||||
return nvkm_output_new_(&nv50_pior_output_func, disp,
|
||||
index, dcbE, poutp);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* DisplayPort
|
||||
*****************************************************************************/
|
||||
static int
|
||||
nv50_pior_output_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
||||
{
|
||||
return 0;
|
||||
struct nvkm_device *device = pior->disp->engine.subdev.device;
|
||||
const u32 poff = nv50_ior_base(pior);
|
||||
nvkm_mask(device, 0x614380 + poff, 0x00000707, 0x00000001);
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_pior_output_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
|
||||
nv50_pior_dp_links(struct nvkm_ior *pior, struct nvkm_i2c_aux *aux)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_pior_output_dp_lnk_ctl(struct nvkm_output_dp *outp,
|
||||
int nr, int bw, bool ef)
|
||||
{
|
||||
int ret = nvkm_i2c_aux_lnk_ctl(outp->aux, nr, bw, ef);
|
||||
int ret = nvkm_i2c_aux_lnk_ctl(aux, pior->dp.nr, pior->dp.bw,
|
||||
pior->dp.ef);
|
||||
if (ret)
|
||||
return ret;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const struct nvkm_output_dp_func
|
||||
nv50_pior_output_dp_func = {
|
||||
.pattern = nv50_pior_output_dp_pattern,
|
||||
.lnk_pwr = nv50_pior_output_dp_lnk_pwr,
|
||||
.lnk_ctl = nv50_pior_output_dp_lnk_ctl,
|
||||
static void
|
||||
nv50_pior_power_wait(struct nvkm_device *device, u32 poff)
|
||||
{
|
||||
nvkm_msec(device, 2000,
|
||||
if (!(nvkm_rd32(device, 0x61e004 + poff) & 0x80000000))
|
||||
break;
|
||||
);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_pior_power(struct nvkm_ior *pior, bool normal, bool pu,
|
||||
bool data, bool vsync, bool hsync)
|
||||
{
|
||||
struct nvkm_device *device = pior->disp->engine.subdev.device;
|
||||
const u32 poff = nv50_ior_base(pior);
|
||||
const u32 shift = normal ? 0 : 16;
|
||||
const u32 state = 0x80000000 | (0x00000001 * !!pu) << shift;
|
||||
const u32 field = 0x80000000 | (0x00000101 << shift);
|
||||
|
||||
nv50_pior_power_wait(device, poff);
|
||||
nvkm_mask(device, 0x61e004 + poff, field, state);
|
||||
nv50_pior_power_wait(device, poff);
|
||||
}
|
||||
|
||||
void
|
||||
nv50_pior_depth(struct nvkm_ior *ior, struct nvkm_ior_state *state, u32 ctrl)
|
||||
{
|
||||
/* GF119 moves this information to per-head methods, which is
|
||||
* a lot more convenient, and where our shared code expect it.
|
||||
*/
|
||||
if (state->head && state == &ior->asy) {
|
||||
struct nvkm_head *head =
|
||||
nvkm_head_find(ior->disp, __ffs(state->head));
|
||||
if (!WARN_ON(!head)) {
|
||||
struct nvkm_head_state *state = &head->asy;
|
||||
switch ((ctrl & 0x000f0000) >> 16) {
|
||||
case 6: state->or.depth = 30; break;
|
||||
case 5: state->or.depth = 24; break;
|
||||
case 2: state->or.depth = 18; break;
|
||||
case 0: state->or.depth = 18; break; /*XXX*/
|
||||
default:
|
||||
state->or.depth = 18;
|
||||
WARN_ON(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_pior_state(struct nvkm_ior *pior, struct nvkm_ior_state *state)
|
||||
{
|
||||
struct nvkm_device *device = pior->disp->engine.subdev.device;
|
||||
const u32 coff = pior->id * 8 + (state == &pior->arm) * 4;
|
||||
u32 ctrl = nvkm_rd32(device, 0x610b80 + coff);
|
||||
|
||||
state->proto_evo = (ctrl & 0x00000f00) >> 8;
|
||||
state->rgdiv = 1;
|
||||
switch (state->proto_evo) {
|
||||
case 0: state->proto = TMDS; break;
|
||||
default:
|
||||
state->proto = UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
state->head = ctrl & 0x00000003;
|
||||
nv50_pior_depth(pior, state, ctrl);
|
||||
}
|
||||
|
||||
static const struct nvkm_ior_func
|
||||
nv50_pior = {
|
||||
.state = nv50_pior_state,
|
||||
.power = nv50_pior_power,
|
||||
.clock = nv50_pior_clock,
|
||||
.dp = {
|
||||
.links = nv50_pior_dp_links,
|
||||
},
|
||||
};
|
||||
|
||||
int
|
||||
nv50_pior_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
|
||||
struct nvkm_output **poutp)
|
||||
nv50_pior_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
struct nvkm_i2c *i2c = disp->engine.subdev.device->i2c;
|
||||
struct nvkm_i2c_aux *aux =
|
||||
nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbE->extdev));
|
||||
struct nvkm_output_dp *outp;
|
||||
|
||||
if (!(outp = kzalloc(sizeof(*outp), GFP_KERNEL)))
|
||||
return -ENOMEM;
|
||||
*poutp = &outp->base;
|
||||
|
||||
return nvkm_output_dp_ctor(&nv50_pior_output_dp_func, disp,
|
||||
index, dcbE, aux, outp);
|
||||
struct nvkm_device *device = disp->engine.subdev.device;
|
||||
if (!(nvkm_rd32(device, 0x610184) & (0x10000000 << id)))
|
||||
return 0;
|
||||
return nvkm_ior_new_(&nv50_pior, disp, PIOR, id);
|
||||
}
|
||||
|
|
|
@ -2,42 +2,18 @@
|
|||
#define __NVKM_DISP_PRIV_H__
|
||||
#include <engine/disp.h>
|
||||
#include "outp.h"
|
||||
#include "outpdp.h"
|
||||
|
||||
int nvkm_disp_ctor(const struct nvkm_disp_func *, struct nvkm_device *,
|
||||
int index, int heads, struct nvkm_disp *);
|
||||
int index, struct nvkm_disp *);
|
||||
int nvkm_disp_new_(const struct nvkm_disp_func *, struct nvkm_device *,
|
||||
int index, int heads, struct nvkm_disp **);
|
||||
int index, struct nvkm_disp **);
|
||||
void nvkm_disp_vblank(struct nvkm_disp *, int head);
|
||||
|
||||
struct nvkm_disp_func_outp {
|
||||
int (* crt)(struct nvkm_disp *, int index, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
int (* tv)(struct nvkm_disp *, int index, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
int (*tmds)(struct nvkm_disp *, int index, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
int (*lvds)(struct nvkm_disp *, int index, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
int (* dp)(struct nvkm_disp *, int index, struct dcb_output *,
|
||||
struct nvkm_output **);
|
||||
};
|
||||
|
||||
struct nvkm_disp_func {
|
||||
void *(*dtor)(struct nvkm_disp *);
|
||||
void (*intr)(struct nvkm_disp *);
|
||||
|
||||
const struct nvkm_disp_oclass *(*root)(struct nvkm_disp *);
|
||||
|
||||
struct {
|
||||
void (*vblank_init)(struct nvkm_disp *, int head);
|
||||
void (*vblank_fini)(struct nvkm_disp *, int head);
|
||||
} head;
|
||||
|
||||
struct {
|
||||
const struct nvkm_disp_func_outp internal;
|
||||
const struct nvkm_disp_func_outp external;
|
||||
} outp;
|
||||
};
|
||||
|
||||
int nvkm_disp_ntfy(struct nvkm_object *, u32, struct nvkm_event **);
|
||||
|
|
|
@ -22,49 +22,13 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "rootnv50.h"
|
||||
#include "head.h"
|
||||
#include "dmacnv50.h"
|
||||
|
||||
#include <core/client.h>
|
||||
#include <core/ramht.h>
|
||||
#include <subdev/timer.h>
|
||||
|
||||
#include <nvif/class.h>
|
||||
#include <nvif/cl5070.h>
|
||||
#include <nvif/unpack.h>
|
||||
|
||||
int
|
||||
gf119_disp_root_scanoutpos(NV50_DISP_MTHD_V0)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
const u32 total = nvkm_rd32(device, 0x640414 + (head * 0x300));
|
||||
const u32 blanke = nvkm_rd32(device, 0x64041c + (head * 0x300));
|
||||
const u32 blanks = nvkm_rd32(device, 0x640420 + (head * 0x300));
|
||||
union {
|
||||
struct nv50_disp_scanoutpos_v0 v0;
|
||||
} *args = data;
|
||||
int ret = -ENOSYS;
|
||||
|
||||
nvif_ioctl(object, "disp scanoutpos size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_ioctl(object, "disp scanoutpos vers %d\n",
|
||||
args->v0.version);
|
||||
args->v0.vblanke = (blanke & 0xffff0000) >> 16;
|
||||
args->v0.hblanke = (blanke & 0x0000ffff);
|
||||
args->v0.vblanks = (blanks & 0xffff0000) >> 16;
|
||||
args->v0.hblanks = (blanks & 0x0000ffff);
|
||||
args->v0.vtotal = ( total & 0xffff0000) >> 16;
|
||||
args->v0.htotal = ( total & 0x0000ffff);
|
||||
args->v0.time[0] = ktime_to_ns(ktime_get());
|
||||
args->v0.vline = /* vline read locks hline */
|
||||
nvkm_rd32(device, 0x616340 + (head * 0x800)) & 0xffff;
|
||||
args->v0.time[1] = ktime_to_ns(ktime_get());
|
||||
args->v0.hline =
|
||||
nvkm_rd32(device, 0x616344 + (head * 0x800)) & 0xffff;
|
||||
} else
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
gf119_disp_root_fini(struct nv50_disp_root *root)
|
||||
|
@ -78,6 +42,7 @@ int
|
|||
gf119_disp_root_init(struct nv50_disp_root *root)
|
||||
{
|
||||
struct nv50_disp *disp = root->disp;
|
||||
struct nvkm_head *head;
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
u32 tmp;
|
||||
int i;
|
||||
|
@ -88,13 +53,14 @@ gf119_disp_root_init(struct nv50_disp_root *root)
|
|||
*/
|
||||
|
||||
/* ... CRTC caps */
|
||||
for (i = 0; i < disp->base.head.nr; i++) {
|
||||
tmp = nvkm_rd32(device, 0x616104 + (i * 0x800));
|
||||
nvkm_wr32(device, 0x6101b4 + (i * 0x800), tmp);
|
||||
tmp = nvkm_rd32(device, 0x616108 + (i * 0x800));
|
||||
nvkm_wr32(device, 0x6101b8 + (i * 0x800), tmp);
|
||||
tmp = nvkm_rd32(device, 0x61610c + (i * 0x800));
|
||||
nvkm_wr32(device, 0x6101bc + (i * 0x800), tmp);
|
||||
list_for_each_entry(head, &disp->base.head, head) {
|
||||
const u32 hoff = head->id * 0x800;
|
||||
tmp = nvkm_rd32(device, 0x616104 + hoff);
|
||||
nvkm_wr32(device, 0x6101b4 + hoff, tmp);
|
||||
tmp = nvkm_rd32(device, 0x616108 + hoff);
|
||||
nvkm_wr32(device, 0x6101b8 + hoff, tmp);
|
||||
tmp = nvkm_rd32(device, 0x61610c + hoff);
|
||||
nvkm_wr32(device, 0x6101bc + hoff, tmp);
|
||||
}
|
||||
|
||||
/* ... DAC caps */
|
||||
|
@ -134,8 +100,10 @@ gf119_disp_root_init(struct nv50_disp_root *root)
|
|||
*
|
||||
* ftp://download.nvidia.com/open-gpu-doc/gk104-disable-underflow-reporting/1/gk104-disable-underflow-reporting.txt
|
||||
*/
|
||||
for (i = 0; i < disp->base.head.nr; i++)
|
||||
nvkm_mask(device, 0x616308 + (i * 0x800), 0x00000111, 0x00000010);
|
||||
list_for_each_entry(head, &disp->base.head, head) {
|
||||
const u32 hoff = head->id * 0x800;
|
||||
nvkm_mask(device, 0x616308 + hoff, 0x00000111, 0x00000010);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*/
|
||||
#define nv04_disp_root(p) container_of((p), struct nv04_disp_root, object)
|
||||
#include "priv.h"
|
||||
#include "head.h"
|
||||
|
||||
#include <core/client.h>
|
||||
|
||||
|
@ -35,50 +36,6 @@ struct nv04_disp_root {
|
|||
struct nvkm_disp *disp;
|
||||
};
|
||||
|
||||
static int
|
||||
nv04_disp_scanoutpos(struct nv04_disp_root *root,
|
||||
void *data, u32 size, int head)
|
||||
{
|
||||
struct nvkm_device *device = root->disp->engine.subdev.device;
|
||||
struct nvkm_object *object = &root->object;
|
||||
const u32 hoff = head * 0x2000;
|
||||
union {
|
||||
struct nv04_disp_scanoutpos_v0 v0;
|
||||
} *args = data;
|
||||
u32 line;
|
||||
int ret = -ENOSYS;
|
||||
|
||||
nvif_ioctl(object, "disp scanoutpos size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_ioctl(object, "disp scanoutpos vers %d\n",
|
||||
args->v0.version);
|
||||
args->v0.vblanks = nvkm_rd32(device, 0x680800 + hoff) & 0xffff;
|
||||
args->v0.vtotal = nvkm_rd32(device, 0x680804 + hoff) & 0xffff;
|
||||
args->v0.vblanke = args->v0.vtotal - 1;
|
||||
|
||||
args->v0.hblanks = nvkm_rd32(device, 0x680820 + hoff) & 0xffff;
|
||||
args->v0.htotal = nvkm_rd32(device, 0x680824 + hoff) & 0xffff;
|
||||
args->v0.hblanke = args->v0.htotal - 1;
|
||||
|
||||
/*
|
||||
* If output is vga instead of digital then vtotal/htotal is
|
||||
* invalid so we have to give up and trigger the timestamping
|
||||
* fallback in the drm core.
|
||||
*/
|
||||
if (!args->v0.vtotal || !args->v0.htotal)
|
||||
return -ENOTSUPP;
|
||||
|
||||
args->v0.time[0] = ktime_to_ns(ktime_get());
|
||||
line = nvkm_rd32(device, 0x600868 + hoff);
|
||||
args->v0.time[1] = ktime_to_ns(ktime_get());
|
||||
args->v0.hline = (line & 0xffff0000) >> 16;
|
||||
args->v0.vline = (line & 0x0000ffff);
|
||||
} else
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nv04_disp_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
|
||||
{
|
||||
|
@ -86,23 +43,24 @@ nv04_disp_mthd(struct nvkm_object *object, u32 mthd, void *data, u32 size)
|
|||
union {
|
||||
struct nv04_disp_mthd_v0 v0;
|
||||
} *args = data;
|
||||
int head, ret = -ENOSYS;
|
||||
struct nvkm_head *head;
|
||||
int id, ret = -ENOSYS;
|
||||
|
||||
nvif_ioctl(object, "disp mthd size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
|
||||
nvif_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;
|
||||
id = args->v0.head;
|
||||
} else
|
||||
return ret;
|
||||
|
||||
if (head < 0 || head >= 2)
|
||||
if (!(head = nvkm_head_find(root->disp, id)))
|
||||
return -ENXIO;
|
||||
|
||||
switch (mthd) {
|
||||
case NV04_DISP_SCANOUTPOS:
|
||||
return nv04_disp_scanoutpos(root, data, size, head);
|
||||
return nvkm_head_mthd_scanoutpos(object, head, data, size);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
*/
|
||||
#include "rootnv50.h"
|
||||
#include "dmacnv50.h"
|
||||
#include "dp.h"
|
||||
#include "head.h"
|
||||
#include "ior.h"
|
||||
|
||||
#include <core/client.h>
|
||||
#include <core/ramht.h>
|
||||
|
@ -32,40 +35,6 @@
|
|||
#include <nvif/cl5070.h>
|
||||
#include <nvif/unpack.h>
|
||||
|
||||
int
|
||||
nv50_disp_root_scanoutpos(NV50_DISP_MTHD_V0)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
const u32 blanke = nvkm_rd32(device, 0x610aec + (head * 0x540));
|
||||
const u32 blanks = nvkm_rd32(device, 0x610af4 + (head * 0x540));
|
||||
const u32 total = nvkm_rd32(device, 0x610afc + (head * 0x540));
|
||||
union {
|
||||
struct nv50_disp_scanoutpos_v0 v0;
|
||||
} *args = data;
|
||||
int ret = -ENOSYS;
|
||||
|
||||
nvif_ioctl(object, "disp scanoutpos size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_ioctl(object, "disp scanoutpos vers %d\n",
|
||||
args->v0.version);
|
||||
args->v0.vblanke = (blanke & 0xffff0000) >> 16;
|
||||
args->v0.hblanke = (blanke & 0x0000ffff);
|
||||
args->v0.vblanks = (blanks & 0xffff0000) >> 16;
|
||||
args->v0.hblanks = (blanks & 0x0000ffff);
|
||||
args->v0.vtotal = ( total & 0xffff0000) >> 16;
|
||||
args->v0.htotal = ( total & 0x0000ffff);
|
||||
args->v0.time[0] = ktime_to_ns(ktime_get());
|
||||
args->v0.vline = /* vline read locks hline */
|
||||
nvkm_rd32(device, 0x616340 + (head * 0x800)) & 0xffff;
|
||||
args->v0.time[1] = ktime_to_ns(ktime_get());
|
||||
args->v0.hline =
|
||||
nvkm_rd32(device, 0x616344 + (head * 0x800)) & 0xffff;
|
||||
} else
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
|
||||
{
|
||||
|
@ -75,11 +44,10 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
|
|||
} *args = data;
|
||||
struct nv50_disp_root *root = nv50_disp_root(object);
|
||||
struct nv50_disp *disp = root->disp;
|
||||
const struct nv50_disp_func *func = disp->func;
|
||||
struct nvkm_output *outp = NULL;
|
||||
struct nvkm_output *temp;
|
||||
struct nvkm_outp *temp, *outp = NULL;
|
||||
struct nvkm_head *head;
|
||||
u16 type, mask = 0;
|
||||
int head, ret = -ENOSYS;
|
||||
int hidx, ret = -ENOSYS;
|
||||
|
||||
if (mthd != NV50_DISP_MTHD)
|
||||
return -EINVAL;
|
||||
|
@ -89,7 +57,7 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
|
|||
nvif_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;
|
||||
hidx = args->v0.head;
|
||||
} else
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v1, 1, 1, true))) {
|
||||
nvif_ioctl(object, "disp mthd vers %d mthd %02x "
|
||||
|
@ -99,11 +67,11 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
|
|||
mthd = args->v1.method;
|
||||
type = args->v1.hasht;
|
||||
mask = args->v1.hashm;
|
||||
head = ffs((mask >> 8) & 0x0f) - 1;
|
||||
hidx = ffs((mask >> 8) & 0x0f) - 1;
|
||||
} else
|
||||
return ret;
|
||||
|
||||
if (head < 0 || head >= disp->base.head.nr)
|
||||
if (!(head = nvkm_head_find(&disp->base, hidx)))
|
||||
return -ENXIO;
|
||||
|
||||
if (mask) {
|
||||
|
@ -119,27 +87,126 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
|
|||
}
|
||||
|
||||
switch (mthd) {
|
||||
case NV50_DISP_SCANOUTPOS:
|
||||
return func->head.scanoutpos(object, disp, data, size, head);
|
||||
case NV50_DISP_SCANOUTPOS: {
|
||||
return nvkm_head_mthd_scanoutpos(object, head, data, size);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (mthd * !!outp) {
|
||||
case NV50_DISP_MTHD_V1_DAC_PWR:
|
||||
return func->dac.power(object, disp, data, size, head, outp);
|
||||
case NV50_DISP_MTHD_V1_DAC_LOAD:
|
||||
return func->dac.sense(object, disp, data, size, head, outp);
|
||||
case NV50_DISP_MTHD_V1_SOR_PWR:
|
||||
return func->sor.power(object, disp, data, size, head, outp);
|
||||
case NV50_DISP_MTHD_V1_SOR_HDA_ELD:
|
||||
if (!func->sor.hda_eld)
|
||||
case NV50_DISP_MTHD_V1_ACQUIRE: {
|
||||
union {
|
||||
struct nv50_disp_acquire_v0 v0;
|
||||
} *args = data;
|
||||
int ret = -ENOSYS;
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER);
|
||||
if (ret == 0) {
|
||||
args->v0.or = outp->ior->id;
|
||||
args->v0.link = outp->ior->asy.link;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case NV50_DISP_MTHD_V1_RELEASE:
|
||||
nvkm_outp_release(outp, NVKM_OUTP_USER);
|
||||
return 0;
|
||||
case NV50_DISP_MTHD_V1_DAC_LOAD: {
|
||||
union {
|
||||
struct nv50_disp_dac_load_v0 v0;
|
||||
} *args = data;
|
||||
int ret = -ENOSYS;
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
if (args->v0.data & 0xfff00000)
|
||||
return -EINVAL;
|
||||
ret = nvkm_outp_acquire(outp, NVKM_OUTP_PRIV);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = outp->ior->func->sense(outp->ior, args->v0.data);
|
||||
nvkm_outp_release(outp, NVKM_OUTP_PRIV);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
args->v0.load = ret;
|
||||
return 0;
|
||||
} else
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case NV50_DISP_MTHD_V1_SOR_HDA_ELD: {
|
||||
union {
|
||||
struct nv50_disp_sor_hda_eld_v0 v0;
|
||||
} *args = data;
|
||||
struct nvkm_ior *ior = outp->ior;
|
||||
int ret = -ENOSYS;
|
||||
|
||||
nvif_ioctl(object, "disp sor hda eld size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
|
||||
nvif_ioctl(object, "disp sor hda eld vers %d\n",
|
||||
args->v0.version);
|
||||
if (size > 0x60)
|
||||
return -E2BIG;
|
||||
} else
|
||||
return ret;
|
||||
|
||||
if (!ior->func->hda.hpd)
|
||||
return -ENODEV;
|
||||
return func->sor.hda_eld(object, disp, data, size, head, outp);
|
||||
case NV50_DISP_MTHD_V1_SOR_HDMI_PWR:
|
||||
if (!func->sor.hdmi)
|
||||
|
||||
if (size && args->v0.data[0]) {
|
||||
if (outp->info.type == DCB_OUTPUT_DP)
|
||||
ior->func->dp.audio(ior, hidx, true);
|
||||
ior->func->hda.hpd(ior, hidx, true);
|
||||
ior->func->hda.eld(ior, data, size);
|
||||
} else {
|
||||
if (outp->info.type == DCB_OUTPUT_DP)
|
||||
ior->func->dp.audio(ior, hidx, false);
|
||||
ior->func->hda.hpd(ior, hidx, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case NV50_DISP_MTHD_V1_SOR_HDMI_PWR: {
|
||||
union {
|
||||
struct nv50_disp_sor_hdmi_pwr_v0 v0;
|
||||
} *args = data;
|
||||
u8 *vendor, vendor_size;
|
||||
u8 *avi, avi_size;
|
||||
int ret = -ENOSYS;
|
||||
|
||||
nvif_ioctl(object, "disp sor hdmi ctrl size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, true))) {
|
||||
nvif_ioctl(object, "disp sor hdmi ctrl vers %d state %d "
|
||||
"max_ac_packet %d rekey %d\n",
|
||||
args->v0.version, args->v0.state,
|
||||
args->v0.max_ac_packet, args->v0.rekey);
|
||||
if (args->v0.max_ac_packet > 0x1f || args->v0.rekey > 0x7f)
|
||||
return -EINVAL;
|
||||
if ((args->v0.avi_infoframe_length
|
||||
+ args->v0.vendor_infoframe_length) > size)
|
||||
return -EINVAL;
|
||||
else
|
||||
if ((args->v0.avi_infoframe_length
|
||||
+ args->v0.vendor_infoframe_length) < size)
|
||||
return -E2BIG;
|
||||
avi = data;
|
||||
avi_size = args->v0.avi_infoframe_length;
|
||||
vendor = avi + avi_size;
|
||||
vendor_size = args->v0.vendor_infoframe_length;
|
||||
} else
|
||||
return ret;
|
||||
|
||||
if (!outp->ior->func->hdmi.ctrl)
|
||||
return -ENODEV;
|
||||
return func->sor.hdmi(object, disp, data, size, head, outp);
|
||||
|
||||
outp->ior->func->hdmi.ctrl(outp->ior, hidx, args->v0.state,
|
||||
args->v0.max_ac_packet,
|
||||
args->v0.rekey, avi, avi_size,
|
||||
vendor, vendor_size);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case NV50_DISP_MTHD_V1_SOR_LVDS_SCRIPT: {
|
||||
union {
|
||||
struct nv50_disp_sor_lvds_script_v0 v0;
|
||||
|
@ -156,32 +223,8 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
|
|||
return ret;
|
||||
}
|
||||
break;
|
||||
case NV50_DISP_MTHD_V1_SOR_DP_PWR: {
|
||||
struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
|
||||
union {
|
||||
struct nv50_disp_sor_dp_pwr_v0 v0;
|
||||
} *args = data;
|
||||
int ret = -ENOSYS;
|
||||
nvif_ioctl(object, "disp sor dp pwr size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_ioctl(object, "disp sor dp pwr vers %d state %d\n",
|
||||
args->v0.version, args->v0.state);
|
||||
if (args->v0.state == 0) {
|
||||
nvkm_notify_put(&outpdp->irq);
|
||||
outpdp->func->lnk_pwr(outpdp, 0);
|
||||
atomic_set(&outpdp->lt.done, 0);
|
||||
return 0;
|
||||
} else
|
||||
if (args->v0.state != 0) {
|
||||
nvkm_output_dp_train(&outpdp->base, 0);
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case NV50_DISP_MTHD_V1_SOR_DP_MST_LINK: {
|
||||
struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
|
||||
struct nvkm_dp *dp = nvkm_dp(outp);
|
||||
union {
|
||||
struct nv50_disp_sor_dp_mst_link_v0 v0;
|
||||
} *args = data;
|
||||
|
@ -190,18 +233,13 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
|
|||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_ioctl(object, "disp sor dp mst link vers %d state %d\n",
|
||||
args->v0.version, args->v0.state);
|
||||
if (outpdp->lt.mst != !!args->v0.state) {
|
||||
outpdp->lt.mst = !!args->v0.state;
|
||||
atomic_set(&outpdp->lt.done, 0);
|
||||
nvkm_output_dp_train(&outpdp->base, 0);
|
||||
}
|
||||
dp->lt.mst = !!args->v0.state;
|
||||
return 0;
|
||||
} else
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI: {
|
||||
struct nvkm_output_dp *outpdp = nvkm_output_dp(outp);
|
||||
union {
|
||||
struct nv50_disp_sor_dp_mst_vcpi_v0 v0;
|
||||
} *args = data;
|
||||
|
@ -213,20 +251,18 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
|
|||
args->v0.version, args->v0.start_slot,
|
||||
args->v0.num_slots, args->v0.pbn,
|
||||
args->v0.aligned_pbn);
|
||||
if (!outpdp->func->vcpi)
|
||||
if (!outp->ior->func->dp.vcpi)
|
||||
return -ENODEV;
|
||||
outpdp->func->vcpi(outpdp, head, args->v0.start_slot,
|
||||
args->v0.num_slots, args->v0.pbn,
|
||||
args->v0.aligned_pbn);
|
||||
outp->ior->func->dp.vcpi(outp->ior, hidx,
|
||||
args->v0.start_slot,
|
||||
args->v0.num_slots,
|
||||
args->v0.pbn,
|
||||
args->v0.aligned_pbn);
|
||||
return 0;
|
||||
} else
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case NV50_DISP_MTHD_V1_PIOR_PWR:
|
||||
if (!func->pior.power)
|
||||
return -ENODEV;
|
||||
return func->pior.power(object, disp, data, size, head, outp);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -291,7 +327,21 @@ static int
|
|||
nv50_disp_root_init_(struct nvkm_object *object)
|
||||
{
|
||||
struct nv50_disp_root *root = nv50_disp_root(object);
|
||||
return root->func->init(root);
|
||||
struct nvkm_ior *ior;
|
||||
int ret;
|
||||
|
||||
ret = root->func->init(root);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set 'normal' (ie. when it's attached to a head) state for
|
||||
* each output resource to 'fully enabled'.
|
||||
*/
|
||||
list_for_each_entry(ior, &root->disp->base.ior, head) {
|
||||
ior->func->power(ior, true, true, true, true, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *
|
||||
|
@ -352,6 +402,7 @@ int
|
|||
nv50_disp_root_init(struct nv50_disp_root *root)
|
||||
{
|
||||
struct nv50_disp *disp = root->disp;
|
||||
struct nvkm_head *head;
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
u32 tmp;
|
||||
int i;
|
||||
|
@ -364,15 +415,15 @@ nv50_disp_root_init(struct nv50_disp_root *root)
|
|||
nvkm_wr32(device, 0x610184, tmp);
|
||||
|
||||
/* ... CRTC caps */
|
||||
for (i = 0; i < disp->base.head.nr; i++) {
|
||||
tmp = nvkm_rd32(device, 0x616100 + (i * 0x800));
|
||||
nvkm_wr32(device, 0x610190 + (i * 0x10), tmp);
|
||||
tmp = nvkm_rd32(device, 0x616104 + (i * 0x800));
|
||||
nvkm_wr32(device, 0x610194 + (i * 0x10), tmp);
|
||||
tmp = nvkm_rd32(device, 0x616108 + (i * 0x800));
|
||||
nvkm_wr32(device, 0x610198 + (i * 0x10), tmp);
|
||||
tmp = nvkm_rd32(device, 0x61610c + (i * 0x800));
|
||||
nvkm_wr32(device, 0x61019c + (i * 0x10), tmp);
|
||||
list_for_each_entry(head, &disp->base.head, head) {
|
||||
tmp = nvkm_rd32(device, 0x616100 + (head->id * 0x800));
|
||||
nvkm_wr32(device, 0x610190 + (head->id * 0x10), tmp);
|
||||
tmp = nvkm_rd32(device, 0x616104 + (head->id * 0x800));
|
||||
nvkm_wr32(device, 0x610194 + (head->id * 0x10), tmp);
|
||||
tmp = nvkm_rd32(device, 0x616108 + (head->id * 0x800));
|
||||
nvkm_wr32(device, 0x610198 + (head->id * 0x10), tmp);
|
||||
tmp = nvkm_rd32(device, 0x61610c + (head->id * 0x800));
|
||||
nvkm_wr32(device, 0x61019c + (head->id * 0x10), tmp);
|
||||
}
|
||||
|
||||
/* ... DAC caps */
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "ior.h"
|
||||
|
||||
static const struct nvkm_ior_func
|
||||
g84_sor = {
|
||||
.state = nv50_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = nv50_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = g84_hdmi_ctrl,
|
||||
},
|
||||
};
|
||||
|
||||
int
|
||||
g84_sor_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
return nv50_sor_new_(&g84_sor, disp, id);
|
||||
}
|
|
@ -21,58 +21,75 @@
|
|||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "outpdp.h"
|
||||
#include "ior.h"
|
||||
|
||||
#include <subdev/timer.h>
|
||||
|
||||
static inline u32
|
||||
g94_sor_soff(struct nvkm_output_dp *outp)
|
||||
void
|
||||
g94_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark)
|
||||
{
|
||||
return (ffs(outp->base.info.or) - 1) * 0x800;
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 loff = nv50_sor_link(sor);
|
||||
nvkm_mask(device, 0x61c128 + loff, 0x0000003f, watermark);
|
||||
}
|
||||
|
||||
static inline u32
|
||||
g94_sor_loff(struct nvkm_output_dp *outp)
|
||||
void
|
||||
g94_sor_dp_activesym(struct nvkm_ior *sor, int head,
|
||||
u8 TU, u8 VTUa, u8 VTUf, u8 VTUi)
|
||||
{
|
||||
return g94_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 loff = nv50_sor_link(sor);
|
||||
nvkm_mask(device, 0x61c10c + loff, 0x000001fc, TU << 2);
|
||||
nvkm_mask(device, 0x61c128 + loff, 0x010f7f00, VTUa << 24 |
|
||||
VTUf << 16 |
|
||||
VTUi << 8);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* DisplayPort
|
||||
******************************************************************************/
|
||||
u32
|
||||
g94_sor_dp_lane_map(struct nvkm_device *device, u8 lane)
|
||||
void
|
||||
g94_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v)
|
||||
{
|
||||
static const u8 gm100[] = { 0, 8, 16, 24 };
|
||||
static const u8 mcp89[] = { 24, 16, 8, 0 }; /* thanks, apple.. */
|
||||
static const u8 g94[] = { 16, 8, 0, 24 };
|
||||
if (device->chipset >= 0x110)
|
||||
return gm100[lane];
|
||||
if (device->chipset == 0xaf)
|
||||
return mcp89[lane];
|
||||
return g94[lane];
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
nvkm_mask(device, 0x61c1e8 + soff, 0x0000ffff, h);
|
||||
nvkm_mask(device, 0x61c1ec + soff, 0x00ffffff, v);
|
||||
}
|
||||
|
||||
static int
|
||||
g94_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
||||
void
|
||||
g94_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu)
|
||||
{
|
||||
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||
const u32 loff = g94_sor_loff(outp);
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 loff = nv50_sor_link(sor);
|
||||
const u32 shift = sor->func->dp.lanes[ln] * 8;
|
||||
u32 data[3];
|
||||
|
||||
data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
|
||||
data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
|
||||
data[2] = nvkm_rd32(device, 0x61c130 + loff);
|
||||
if ((data[2] & 0x0000ff00) < (pu << 8) || ln == 0)
|
||||
data[2] = (data[2] & ~0x0000ff00) | (pu << 8);
|
||||
nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift));
|
||||
nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift));
|
||||
nvkm_wr32(device, 0x61c130 + loff, data[2]);
|
||||
}
|
||||
|
||||
void
|
||||
g94_sor_dp_pattern(struct nvkm_ior *sor, int pattern)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 loff = nv50_sor_link(sor);
|
||||
nvkm_mask(device, 0x61c10c + loff, 0x0f000000, pattern << 24);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
g94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
|
||||
void
|
||||
g94_sor_dp_power(struct nvkm_ior *sor, int nr)
|
||||
{
|
||||
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||
const u32 soff = g94_sor_soff(outp);
|
||||
const u32 loff = g94_sor_loff(outp);
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
const u32 loff = nv50_sor_link(sor);
|
||||
u32 mask = 0, i;
|
||||
|
||||
for (i = 0; i < nr; i++)
|
||||
mask |= 1 << (g94_sor_dp_lane_map(device, i) >> 3);
|
||||
mask |= 1 << sor->func->dp.lanes[i];
|
||||
|
||||
nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask);
|
||||
nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000);
|
||||
|
@ -80,22 +97,21 @@ g94_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
|
|||
if (!(nvkm_rd32(device, 0x61c034 + soff) & 0x80000000))
|
||||
break;
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
|
||||
int
|
||||
g94_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux)
|
||||
{
|
||||
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||
const u32 soff = g94_sor_soff(outp);
|
||||
const u32 loff = g94_sor_loff(outp);
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
const u32 loff = nv50_sor_link(sor);
|
||||
u32 dpctrl = 0x00000000;
|
||||
u32 clksor = 0x00000000;
|
||||
|
||||
dpctrl |= ((1 << nr) - 1) << 16;
|
||||
if (ef)
|
||||
dpctrl |= ((1 << sor->dp.nr) - 1) << 16;
|
||||
if (sor->dp.ef)
|
||||
dpctrl |= 0x00004000;
|
||||
if (bw > 0x06)
|
||||
if (sor->dp.bw > 0x06)
|
||||
clksor |= 0x00040000;
|
||||
|
||||
nvkm_mask(device, 0x614300 + soff, 0x000c0000, clksor);
|
||||
|
@ -103,51 +119,165 @@ g94_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
g94_sor_dp_drv_ctl(struct nvkm_output_dp *outp, int ln, int vs, int pe, int pc)
|
||||
static bool
|
||||
g94_sor_war_needed(struct nvkm_ior *sor)
|
||||
{
|
||||
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||
struct nvkm_bios *bios = device->bios;
|
||||
const u32 shift = g94_sor_dp_lane_map(device, ln);
|
||||
const u32 loff = g94_sor_loff(outp);
|
||||
u32 addr, data[3];
|
||||
u8 ver, hdr, cnt, len;
|
||||
struct nvbios_dpout info;
|
||||
struct nvbios_dpcfg ocfg;
|
||||
|
||||
addr = nvbios_dpout_match(bios, outp->base.info.hasht,
|
||||
outp->base.info.hashm,
|
||||
&ver, &hdr, &cnt, &len, &info);
|
||||
if (!addr)
|
||||
return -ENODEV;
|
||||
|
||||
addr = nvbios_dpcfg_match(bios, addr, 0, vs, pe,
|
||||
&ver, &hdr, &cnt, &len, &ocfg);
|
||||
if (!addr)
|
||||
return -EINVAL;
|
||||
|
||||
data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
|
||||
data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
|
||||
data[2] = nvkm_rd32(device, 0x61c130 + loff);
|
||||
if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0)
|
||||
data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8);
|
||||
nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
|
||||
nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
|
||||
nvkm_wr32(device, 0x61c130 + loff, data[2]);
|
||||
return 0;
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
if (sor->asy.proto == TMDS) {
|
||||
switch (nvkm_rd32(device, 0x614300 + soff) & 0x00030000) {
|
||||
case 0x00000000:
|
||||
case 0x00030000:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct nvkm_output_dp_func
|
||||
g94_sor_dp_func = {
|
||||
.pattern = g94_sor_dp_pattern,
|
||||
.lnk_pwr = g94_sor_dp_lnk_pwr,
|
||||
.lnk_ctl = g94_sor_dp_lnk_ctl,
|
||||
.drv_ctl = g94_sor_dp_drv_ctl,
|
||||
static void
|
||||
g94_sor_war_update_sppll1(struct nvkm_disp *disp)
|
||||
{
|
||||
struct nvkm_device *device = disp->engine.subdev.device;
|
||||
struct nvkm_ior *ior;
|
||||
bool used = false;
|
||||
u32 clksor;
|
||||
|
||||
list_for_each_entry(ior, &disp->ior, head) {
|
||||
if (ior->type != SOR)
|
||||
continue;
|
||||
|
||||
clksor = nvkm_rd32(device, 0x614300 + nv50_ior_base(ior));
|
||||
switch (clksor & 0x03000000) {
|
||||
case 0x02000000:
|
||||
case 0x03000000:
|
||||
used = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (used)
|
||||
return;
|
||||
|
||||
nvkm_mask(device, 0x00e840, 0x80000000, 0x00000000);
|
||||
}
|
||||
|
||||
static void
|
||||
g94_sor_war_3(struct nvkm_ior *sor)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
u32 sorpwr;
|
||||
|
||||
if (!g94_sor_war_needed(sor))
|
||||
return;
|
||||
|
||||
sorpwr = nvkm_rd32(device, 0x61c004 + soff);
|
||||
if (sorpwr & 0x00000001) {
|
||||
u32 seqctl = nvkm_rd32(device, 0x61c030 + soff);
|
||||
u32 pd_pc = (seqctl & 0x00000f00) >> 8;
|
||||
u32 pu_pc = seqctl & 0x0000000f;
|
||||
|
||||
nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x1f008000);
|
||||
|
||||
nvkm_msec(device, 2000,
|
||||
if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))
|
||||
break;
|
||||
);
|
||||
nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000000);
|
||||
nvkm_msec(device, 2000,
|
||||
if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))
|
||||
break;
|
||||
);
|
||||
|
||||
nvkm_wr32(device, 0x61c040 + soff + pd_pc * 4, 0x00002000);
|
||||
nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f000000);
|
||||
}
|
||||
|
||||
nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000000);
|
||||
nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x00000000);
|
||||
|
||||
if (sorpwr & 0x00000001) {
|
||||
nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000001);
|
||||
}
|
||||
|
||||
g94_sor_war_update_sppll1(sor->disp);
|
||||
}
|
||||
|
||||
static void
|
||||
g94_sor_war_2(struct nvkm_ior *sor)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
|
||||
if (!g94_sor_war_needed(sor))
|
||||
return;
|
||||
|
||||
nvkm_mask(device, 0x00e840, 0x80000000, 0x80000000);
|
||||
nvkm_mask(device, 0x614300 + soff, 0x03000000, 0x03000000);
|
||||
nvkm_mask(device, 0x61c10c + soff, 0x00000001, 0x00000001);
|
||||
|
||||
nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x00000000);
|
||||
nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x14000000);
|
||||
nvkm_usec(device, 400, NVKM_DELAY);
|
||||
nvkm_mask(device, 0x61c008 + soff, 0xff000000, 0x00000000);
|
||||
nvkm_mask(device, 0x61c00c + soff, 0x0f000000, 0x01000000);
|
||||
|
||||
if (nvkm_rd32(device, 0x61c004 + soff) & 0x00000001) {
|
||||
u32 seqctl = nvkm_rd32(device, 0x61c030 + soff);
|
||||
u32 pu_pc = seqctl & 0x0000000f;
|
||||
nvkm_wr32(device, 0x61c040 + soff + pu_pc * 4, 0x1f008000);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
g94_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 coff = sor->id * 8 + (state == &sor->arm) * 4;
|
||||
u32 ctrl = nvkm_rd32(device, 0x610794 + coff);
|
||||
|
||||
state->proto_evo = (ctrl & 0x00000f00) >> 8;
|
||||
switch (state->proto_evo) {
|
||||
case 0: state->proto = LVDS; state->link = 1; break;
|
||||
case 1: state->proto = TMDS; state->link = 1; break;
|
||||
case 2: state->proto = TMDS; state->link = 2; break;
|
||||
case 5: state->proto = TMDS; state->link = 3; break;
|
||||
case 8: state->proto = DP; state->link = 1; break;
|
||||
case 9: state->proto = DP; state->link = 2; break;
|
||||
default:
|
||||
state->proto = UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
state->head = ctrl & 0x00000003;
|
||||
nv50_pior_depth(sor, state, ctrl);
|
||||
}
|
||||
|
||||
static const struct nvkm_ior_func
|
||||
g94_sor = {
|
||||
.state = g94_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = nv50_sor_clock,
|
||||
.war_2 = g94_sor_war_2,
|
||||
.war_3 = g94_sor_war_3,
|
||||
.dp = {
|
||||
.lanes = { 2, 1, 0, 3},
|
||||
.links = g94_sor_dp_links,
|
||||
.power = g94_sor_dp_power,
|
||||
.pattern = g94_sor_dp_pattern,
|
||||
.drive = g94_sor_dp_drive,
|
||||
.audio_sym = g94_sor_dp_audio_sym,
|
||||
.activesym = g94_sor_dp_activesym,
|
||||
.watermark = g94_sor_dp_watermark,
|
||||
},
|
||||
};
|
||||
|
||||
int
|
||||
g94_sor_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
|
||||
struct nvkm_output **poutp)
|
||||
g94_sor_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
return nvkm_output_dp_new_(&g94_sor_dp_func, disp, index, dcbE, poutp);
|
||||
return nv50_sor_new_(&g94_sor, disp, id);
|
||||
}
|
||||
|
|
|
@ -21,44 +21,94 @@
|
|||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "outpdp.h"
|
||||
#include "ior.h"
|
||||
|
||||
static inline u32
|
||||
gf119_sor_soff(struct nvkm_output_dp *outp)
|
||||
#include <subdev/timer.h>
|
||||
|
||||
void
|
||||
gf119_sor_dp_watermark(struct nvkm_ior *sor, int head, u8 watermark)
|
||||
{
|
||||
return (ffs(outp->base.info.or) - 1) * 0x800;
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 hoff = head * 0x800;
|
||||
nvkm_mask(device, 0x616610 + hoff, 0x0800003f, 0x08000000 | watermark);
|
||||
}
|
||||
|
||||
static inline u32
|
||||
gf119_sor_loff(struct nvkm_output_dp *outp)
|
||||
void
|
||||
gf119_sor_dp_audio_sym(struct nvkm_ior *sor, int head, u16 h, u32 v)
|
||||
{
|
||||
return gf119_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 hoff = head * 0x800;
|
||||
nvkm_mask(device, 0x616620 + hoff, 0x0000ffff, h);
|
||||
nvkm_mask(device, 0x616624 + hoff, 0x00ffffff, v);
|
||||
}
|
||||
|
||||
static int
|
||||
gf119_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
||||
void
|
||||
gf119_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable)
|
||||
{
|
||||
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||
const u32 soff = gf119_sor_soff(outp);
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 hoff = 0x800 * head;
|
||||
const u32 data = 0x80000000 | (0x00000001 * enable);
|
||||
const u32 mask = 0x8000000d;
|
||||
nvkm_mask(device, 0x616618 + hoff, mask, data);
|
||||
nvkm_msec(device, 2000,
|
||||
if (!(nvkm_rd32(device, 0x616618 + hoff) & 0x80000000))
|
||||
break;
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
gf119_sor_dp_vcpi(struct nvkm_ior *sor, int head,
|
||||
u8 slot, u8 slot_nr, u16 pbn, u16 aligned)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 hoff = head * 0x800;
|
||||
|
||||
nvkm_mask(device, 0x616588 + hoff, 0x00003f3f, (slot_nr << 8) | slot);
|
||||
nvkm_mask(device, 0x61658c + hoff, 0xffffffff, (aligned << 16) | pbn);
|
||||
}
|
||||
|
||||
void
|
||||
gf119_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 loff = nv50_sor_link(sor);
|
||||
const u32 shift = sor->func->dp.lanes[ln] * 8;
|
||||
u32 data[4];
|
||||
|
||||
data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
|
||||
data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
|
||||
data[2] = nvkm_rd32(device, 0x61c130 + loff);
|
||||
if ((data[2] & 0x0000ff00) < (pu << 8) || ln == 0)
|
||||
data[2] = (data[2] & ~0x0000ff00) | (pu << 8);
|
||||
nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift));
|
||||
nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift));
|
||||
nvkm_wr32(device, 0x61c130 + loff, data[2]);
|
||||
data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift);
|
||||
nvkm_wr32(device, 0x61c13c + loff, data[3] | (pc << shift));
|
||||
}
|
||||
|
||||
void
|
||||
gf119_sor_dp_pattern(struct nvkm_ior *sor, int pattern)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, 0x01010101 * pattern);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
|
||||
gf119_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux)
|
||||
{
|
||||
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||
const u32 soff = gf119_sor_soff(outp);
|
||||
const u32 loff = gf119_sor_loff(outp);
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
const u32 loff = nv50_sor_link(sor);
|
||||
u32 dpctrl = 0x00000000;
|
||||
u32 clksor = 0x00000000;
|
||||
|
||||
clksor |= bw << 18;
|
||||
dpctrl |= ((1 << nr) - 1) << 16;
|
||||
if (outp->lt.mst)
|
||||
clksor |= sor->dp.bw << 18;
|
||||
dpctrl |= ((1 << sor->dp.nr) - 1) << 16;
|
||||
if (sor->dp.mst)
|
||||
dpctrl |= 0x40000000;
|
||||
if (ef)
|
||||
if (sor->dp.ef)
|
||||
dpctrl |= 0x00004000;
|
||||
|
||||
nvkm_mask(device, 0x612300 + soff, 0x007c0000, clksor);
|
||||
|
@ -66,66 +116,77 @@ gf119_sor_dp_lnk_ctl(struct nvkm_output_dp *outp, int nr, int bw, bool ef)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
gf119_sor_dp_drv_ctl(struct nvkm_output_dp *outp,
|
||||
int ln, int vs, int pe, int pc)
|
||||
void
|
||||
gf119_sor_clock(struct nvkm_ior *sor)
|
||||
{
|
||||
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||
struct nvkm_bios *bios = device->bios;
|
||||
const u32 shift = g94_sor_dp_lane_map(device, ln);
|
||||
const u32 loff = gf119_sor_loff(outp);
|
||||
u32 addr, data[4];
|
||||
u8 ver, hdr, cnt, len;
|
||||
struct nvbios_dpout info;
|
||||
struct nvbios_dpcfg ocfg;
|
||||
|
||||
addr = nvbios_dpout_match(bios, outp->base.info.hasht,
|
||||
outp->base.info.hashm,
|
||||
&ver, &hdr, &cnt, &len, &info);
|
||||
if (!addr)
|
||||
return -ENODEV;
|
||||
|
||||
addr = nvbios_dpcfg_match(bios, addr, pc, vs, pe,
|
||||
&ver, &hdr, &cnt, &len, &ocfg);
|
||||
if (!addr)
|
||||
return -EINVAL;
|
||||
|
||||
data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
|
||||
data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
|
||||
data[2] = nvkm_rd32(device, 0x61c130 + loff);
|
||||
if ((data[2] & 0x0000ff00) < (ocfg.tx_pu << 8) || ln == 0)
|
||||
data[2] = (data[2] & ~0x0000ff00) | (ocfg.tx_pu << 8);
|
||||
nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
|
||||
nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
|
||||
nvkm_wr32(device, 0x61c130 + loff, data[2]);
|
||||
data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift);
|
||||
nvkm_wr32(device, 0x61c13c + loff, data[3] | (ocfg.pc << shift));
|
||||
return 0;
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const int div = sor->asy.link == 3;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
if (sor->asy.proto == TMDS) {
|
||||
/* NFI why, but this sets DP_LINK_BW_2_7 when using TMDS. */
|
||||
nvkm_mask(device, 0x612300 + soff, 0x007c0000, 0x0a << 18);
|
||||
}
|
||||
nvkm_mask(device, 0x612300 + soff, 0x00000707, (div << 8) | div);
|
||||
}
|
||||
|
||||
void
|
||||
gf119_sor_dp_vcpi(struct nvkm_output_dp *outp, int head, u8 slot,
|
||||
u8 slot_nr, u16 pbn, u16 aligned)
|
||||
gf119_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state)
|
||||
{
|
||||
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||
const u32 hoff = head * 0x800;
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 coff = (state == &sor->asy) * 0x20000 + sor->id * 0x20;
|
||||
u32 ctrl = nvkm_rd32(device, 0x640200 + coff);
|
||||
|
||||
nvkm_mask(device, 0x616588 + hoff, 0x00003f3f, (slot_nr << 8) | slot);
|
||||
nvkm_mask(device, 0x61658c + hoff, 0xffffffff, (aligned << 16) | pbn);
|
||||
state->proto_evo = (ctrl & 0x00000f00) >> 8;
|
||||
switch (state->proto_evo) {
|
||||
case 0: state->proto = LVDS; state->link = 1; break;
|
||||
case 1: state->proto = TMDS; state->link = 1; break;
|
||||
case 2: state->proto = TMDS; state->link = 2; break;
|
||||
case 5: state->proto = TMDS; state->link = 3; break;
|
||||
case 8: state->proto = DP; state->link = 1; break;
|
||||
case 9: state->proto = DP; state->link = 2; break;
|
||||
default:
|
||||
state->proto = UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
state->head = ctrl & 0x0000000f;
|
||||
}
|
||||
|
||||
static const struct nvkm_output_dp_func
|
||||
gf119_sor_dp_func = {
|
||||
.pattern = gf119_sor_dp_pattern,
|
||||
.lnk_pwr = g94_sor_dp_lnk_pwr,
|
||||
.lnk_ctl = gf119_sor_dp_lnk_ctl,
|
||||
.drv_ctl = gf119_sor_dp_drv_ctl,
|
||||
.vcpi = gf119_sor_dp_vcpi,
|
||||
int
|
||||
gf119_sor_new_(const struct nvkm_ior_func *func, struct nvkm_disp *disp, int id)
|
||||
{
|
||||
struct nvkm_device *device = disp->engine.subdev.device;
|
||||
if (!(nvkm_rd32(device, 0x612004) & (0x00000100 << id)))
|
||||
return 0;
|
||||
return nvkm_ior_new_(func, disp, SOR, id);
|
||||
}
|
||||
|
||||
static const struct nvkm_ior_func
|
||||
gf119_sor = {
|
||||
.state = gf119_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = gf119_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = gf119_hdmi_ctrl,
|
||||
},
|
||||
.dp = {
|
||||
.lanes = { 2, 1, 0, 3 },
|
||||
.links = gf119_sor_dp_links,
|
||||
.power = g94_sor_dp_power,
|
||||
.pattern = gf119_sor_dp_pattern,
|
||||
.vcpi = gf119_sor_dp_vcpi,
|
||||
.audio = gf119_sor_dp_audio,
|
||||
.audio_sym = gf119_sor_dp_audio_sym,
|
||||
.watermark = gf119_sor_dp_watermark,
|
||||
},
|
||||
.hda = {
|
||||
.hpd = gf119_hda_hpd,
|
||||
.eld = gf119_hda_eld,
|
||||
},
|
||||
};
|
||||
|
||||
int
|
||||
gf119_sor_dp_new(struct nvkm_disp *disp, int index,
|
||||
struct dcb_output *dcbE, struct nvkm_output **poutp)
|
||||
gf119_sor_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
return nvkm_output_dp_new_(&gf119_sor_dp_func, disp, index, dcbE, poutp);
|
||||
return gf119_sor_new_(&gf119_sor, disp, id);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "ior.h"
|
||||
|
||||
static const struct nvkm_ior_func
|
||||
gk104_sor = {
|
||||
.state = gf119_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = gf119_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = gk104_hdmi_ctrl,
|
||||
},
|
||||
.dp = {
|
||||
.lanes = { 2, 1, 0, 3 },
|
||||
.links = gf119_sor_dp_links,
|
||||
.power = g94_sor_dp_power,
|
||||
.pattern = gf119_sor_dp_pattern,
|
||||
.drive = gf119_sor_dp_drive,
|
||||
.vcpi = gf119_sor_dp_vcpi,
|
||||
.audio = gf119_sor_dp_audio,
|
||||
.audio_sym = gf119_sor_dp_audio_sym,
|
||||
.watermark = gf119_sor_dp_watermark,
|
||||
},
|
||||
.hda = {
|
||||
.hpd = gf119_hda_hpd,
|
||||
.eld = gf119_hda_eld,
|
||||
},
|
||||
};
|
||||
|
||||
int
|
||||
gk104_sor_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
return gf119_sor_new_(&gk104_sor, disp, id);
|
||||
}
|
|
@ -21,34 +21,47 @@
|
|||
*
|
||||
* Authors: Ben Skeggs <bskeggs@redhat.com>
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "outpdp.h"
|
||||
#include "ior.h"
|
||||
|
||||
int
|
||||
gm107_sor_dp_pattern(struct nvkm_output_dp *outp, int pattern)
|
||||
void
|
||||
gm107_sor_dp_pattern(struct nvkm_ior *sor, int pattern)
|
||||
{
|
||||
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||
const u32 soff = outp->base.or * 0x800;
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
const u32 data = 0x01010101 * pattern;
|
||||
if (outp->base.info.sorconf.link & 1)
|
||||
if (sor->asy.link & 1)
|
||||
nvkm_mask(device, 0x61c110 + soff, 0x0f0f0f0f, data);
|
||||
else
|
||||
nvkm_mask(device, 0x61c12c + soff, 0x0f0f0f0f, data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nvkm_output_dp_func
|
||||
gm107_sor_dp_func = {
|
||||
.pattern = gm107_sor_dp_pattern,
|
||||
.lnk_pwr = g94_sor_dp_lnk_pwr,
|
||||
.lnk_ctl = gf119_sor_dp_lnk_ctl,
|
||||
.drv_ctl = gf119_sor_dp_drv_ctl,
|
||||
.vcpi = gf119_sor_dp_vcpi,
|
||||
static const struct nvkm_ior_func
|
||||
gm107_sor = {
|
||||
.state = gf119_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = gf119_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = gk104_hdmi_ctrl,
|
||||
},
|
||||
.dp = {
|
||||
.lanes = { 0, 1, 2, 3 },
|
||||
.links = gf119_sor_dp_links,
|
||||
.power = g94_sor_dp_power,
|
||||
.pattern = gm107_sor_dp_pattern,
|
||||
.drive = gf119_sor_dp_drive,
|
||||
.vcpi = gf119_sor_dp_vcpi,
|
||||
.audio = gf119_sor_dp_audio,
|
||||
.audio_sym = gf119_sor_dp_audio_sym,
|
||||
.watermark = gf119_sor_dp_watermark,
|
||||
},
|
||||
.hda = {
|
||||
.hpd = gf119_hda_hpd,
|
||||
.eld = gf119_hda_eld,
|
||||
},
|
||||
};
|
||||
|
||||
int
|
||||
gm107_sor_dp_new(struct nvkm_disp *disp, int index,
|
||||
struct dcb_output *dcbE, struct nvkm_output **poutp)
|
||||
gm107_sor_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
return nvkm_output_dp_new_(&gm107_sor_dp_func, disp, index, dcbE, poutp);
|
||||
return gf119_sor_new_(&gm107_sor, disp, id);
|
||||
}
|
||||
|
|
|
@ -21,111 +21,104 @@
|
|||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "outpdp.h"
|
||||
#include "ior.h"
|
||||
|
||||
#include <subdev/timer.h>
|
||||
|
||||
static inline u32
|
||||
gm200_sor_soff(struct nvkm_output_dp *outp)
|
||||
static void
|
||||
gm200_sor_dp_drive(struct nvkm_ior *sor, int ln, int pc, int dc, int pe, int pu)
|
||||
{
|
||||
return (ffs(outp->base.info.or) - 1) * 0x800;
|
||||
}
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 loff = nv50_sor_link(sor);
|
||||
const u32 shift = sor->func->dp.lanes[ln] * 8;
|
||||
u32 data[4];
|
||||
|
||||
static inline u32
|
||||
gm200_sor_loff(struct nvkm_output_dp *outp)
|
||||
{
|
||||
return gm200_sor_soff(outp) + !(outp->base.info.sorconf.link & 1) * 0x80;
|
||||
}
|
||||
|
||||
void
|
||||
gm200_sor_magic(struct nvkm_output *outp)
|
||||
{
|
||||
struct nvkm_device *device = outp->disp->engine.subdev.device;
|
||||
const u32 soff = outp->or * 0x100;
|
||||
const u32 data = outp->or + 1;
|
||||
if (outp->info.sorconf.link & 1)
|
||||
nvkm_mask(device, 0x612308 + soff, 0x0000001f, 0x00000000 | data);
|
||||
if (outp->info.sorconf.link & 2)
|
||||
nvkm_mask(device, 0x612388 + soff, 0x0000001f, 0x00000010 | data);
|
||||
}
|
||||
|
||||
static inline u32
|
||||
gm200_sor_dp_lane_map(struct nvkm_device *device, u8 lane)
|
||||
{
|
||||
return lane * 0x08;
|
||||
}
|
||||
|
||||
static int
|
||||
gm200_sor_dp_lnk_pwr(struct nvkm_output_dp *outp, int nr)
|
||||
{
|
||||
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||
const u32 soff = gm200_sor_soff(outp);
|
||||
const u32 loff = gm200_sor_loff(outp);
|
||||
u32 mask = 0, i;
|
||||
|
||||
for (i = 0; i < nr; i++)
|
||||
mask |= 1 << (gm200_sor_dp_lane_map(device, i) >> 3);
|
||||
|
||||
nvkm_mask(device, 0x61c130 + loff, 0x0000000f, mask);
|
||||
nvkm_mask(device, 0x61c034 + soff, 0x80000000, 0x80000000);
|
||||
nvkm_msec(device, 2000,
|
||||
if (!(nvkm_rd32(device, 0x61c034 + soff) & 0x80000000))
|
||||
break;
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gm200_sor_dp_drv_ctl(struct nvkm_output_dp *outp,
|
||||
int ln, int vs, int pe, int pc)
|
||||
{
|
||||
struct nvkm_device *device = outp->base.disp->engine.subdev.device;
|
||||
struct nvkm_bios *bios = device->bios;
|
||||
const u32 shift = gm200_sor_dp_lane_map(device, ln);
|
||||
const u32 loff = gm200_sor_loff(outp);
|
||||
u32 addr, data[4];
|
||||
u8 ver, hdr, cnt, len;
|
||||
struct nvbios_dpout info;
|
||||
struct nvbios_dpcfg ocfg;
|
||||
|
||||
addr = nvbios_dpout_match(bios, outp->base.info.hasht,
|
||||
outp->base.info.hashm,
|
||||
&ver, &hdr, &cnt, &len, &info);
|
||||
if (!addr)
|
||||
return -ENODEV;
|
||||
|
||||
addr = nvbios_dpcfg_match(bios, addr, pc, vs, pe,
|
||||
&ver, &hdr, &cnt, &len, &ocfg);
|
||||
if (!addr)
|
||||
return -EINVAL;
|
||||
ocfg.tx_pu &= 0x0f;
|
||||
pu &= 0x0f;
|
||||
|
||||
data[0] = nvkm_rd32(device, 0x61c118 + loff) & ~(0x000000ff << shift);
|
||||
data[1] = nvkm_rd32(device, 0x61c120 + loff) & ~(0x000000ff << shift);
|
||||
data[2] = nvkm_rd32(device, 0x61c130 + loff);
|
||||
if ((data[2] & 0x00000f00) < (ocfg.tx_pu << 8) || ln == 0)
|
||||
data[2] = (data[2] & ~0x00000f00) | (ocfg.tx_pu << 8);
|
||||
nvkm_wr32(device, 0x61c118 + loff, data[0] | (ocfg.dc << shift));
|
||||
nvkm_wr32(device, 0x61c120 + loff, data[1] | (ocfg.pe << shift));
|
||||
if ((data[2] & 0x00000f00) < (pu << 8) || ln == 0)
|
||||
data[2] = (data[2] & ~0x00000f00) | (pu << 8);
|
||||
nvkm_wr32(device, 0x61c118 + loff, data[0] | (dc << shift));
|
||||
nvkm_wr32(device, 0x61c120 + loff, data[1] | (pe << shift));
|
||||
nvkm_wr32(device, 0x61c130 + loff, data[2]);
|
||||
data[3] = nvkm_rd32(device, 0x61c13c + loff) & ~(0x000000ff << shift);
|
||||
nvkm_wr32(device, 0x61c13c + loff, data[3] | (ocfg.pc << shift));
|
||||
return 0;
|
||||
nvkm_wr32(device, 0x61c13c + loff, data[3] | (pc << shift));
|
||||
}
|
||||
|
||||
static const struct nvkm_output_dp_func
|
||||
gm200_sor_dp_func = {
|
||||
.pattern = gm107_sor_dp_pattern,
|
||||
.lnk_pwr = gm200_sor_dp_lnk_pwr,
|
||||
.lnk_ctl = gf119_sor_dp_lnk_ctl,
|
||||
.drv_ctl = gm200_sor_dp_drv_ctl,
|
||||
.vcpi = gf119_sor_dp_vcpi,
|
||||
static void
|
||||
gm200_sor_route_set(struct nvkm_outp *outp, struct nvkm_ior *ior)
|
||||
{
|
||||
struct nvkm_device *device = outp->disp->engine.subdev.device;
|
||||
const u32 moff = __ffs(outp->info.or) * 0x100;
|
||||
const u32 sor = ior ? ior->id + 1 : 0;
|
||||
u32 link = ior ? (ior->asy.link == 2) : 0;
|
||||
|
||||
if (outp->info.sorconf.link & 1) {
|
||||
nvkm_mask(device, 0x612308 + moff, 0x0000001f, link << 4 | sor);
|
||||
link++;
|
||||
}
|
||||
|
||||
if (outp->info.sorconf.link & 2)
|
||||
nvkm_mask(device, 0x612388 + moff, 0x0000001f, link << 4 | sor);
|
||||
}
|
||||
|
||||
static int
|
||||
gm200_sor_route_get(struct nvkm_outp *outp, int *link)
|
||||
{
|
||||
struct nvkm_device *device = outp->disp->engine.subdev.device;
|
||||
const int sublinks = outp->info.sorconf.link;
|
||||
int lnk[2], sor[2], m, s;
|
||||
|
||||
for (*link = 0, m = __ffs(outp->info.or) * 2, s = 0; s < 2; m++, s++) {
|
||||
if (sublinks & BIT(s)) {
|
||||
u32 data = nvkm_rd32(device, 0x612308 + (m * 0x80));
|
||||
lnk[s] = (data & 0x00000010) >> 4;
|
||||
sor[s] = (data & 0x0000000f);
|
||||
if (!sor[s])
|
||||
return -1;
|
||||
*link |= lnk[s];
|
||||
}
|
||||
}
|
||||
|
||||
if (sublinks == 3) {
|
||||
if (sor[0] != sor[1] || WARN_ON(lnk[0] || !lnk[1]))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ((sublinks & 1) ? sor[0] : sor[1]) - 1;
|
||||
}
|
||||
|
||||
static const struct nvkm_ior_func
|
||||
gm200_sor = {
|
||||
.route = {
|
||||
.get = gm200_sor_route_get,
|
||||
.set = gm200_sor_route_set,
|
||||
},
|
||||
.state = gf119_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = gf119_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = gk104_hdmi_ctrl,
|
||||
},
|
||||
.dp = {
|
||||
.lanes = { 0, 1, 2, 3 },
|
||||
.links = gf119_sor_dp_links,
|
||||
.power = g94_sor_dp_power,
|
||||
.pattern = gm107_sor_dp_pattern,
|
||||
.drive = gm200_sor_dp_drive,
|
||||
.vcpi = gf119_sor_dp_vcpi,
|
||||
.audio = gf119_sor_dp_audio,
|
||||
.audio_sym = gf119_sor_dp_audio_sym,
|
||||
.watermark = gf119_sor_dp_watermark,
|
||||
},
|
||||
.hda = {
|
||||
.hpd = gf119_hda_hpd,
|
||||
.eld = gf119_hda_eld,
|
||||
},
|
||||
};
|
||||
|
||||
int
|
||||
gm200_sor_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE,
|
||||
struct nvkm_output **poutp)
|
||||
gm200_sor_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
return nvkm_output_dp_new_(&gm200_sor_dp_func, disp, index, dcbE, poutp);
|
||||
return gf119_sor_new_(&gm200_sor, disp, id);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright 2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "ior.h"
|
||||
|
||||
#include <subdev/timer.h>
|
||||
|
||||
void
|
||||
gt215_sor_dp_audio(struct nvkm_ior *sor, int head, bool enable)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
const u32 data = 0x80000000 | (0x00000001 * enable);
|
||||
const u32 mask = 0x8000000d;
|
||||
nvkm_mask(device, 0x61c1e0 + soff, mask, data);
|
||||
nvkm_msec(device, 2000,
|
||||
if (!(nvkm_rd32(device, 0x61c1e0 + soff) & 0x80000000))
|
||||
break;
|
||||
);
|
||||
}
|
||||
|
||||
static const struct nvkm_ior_func
|
||||
gt215_sor = {
|
||||
.state = g94_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = nv50_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = gt215_hdmi_ctrl,
|
||||
},
|
||||
.dp = {
|
||||
.lanes = { 2, 1, 0, 3 },
|
||||
.links = g94_sor_dp_links,
|
||||
.power = g94_sor_dp_power,
|
||||
.pattern = g94_sor_dp_pattern,
|
||||
.drive = g94_sor_dp_drive,
|
||||
.audio = gt215_sor_dp_audio,
|
||||
.audio_sym = g94_sor_dp_audio_sym,
|
||||
.activesym = g94_sor_dp_activesym,
|
||||
.watermark = g94_sor_dp_watermark,
|
||||
},
|
||||
.hda = {
|
||||
.hpd = gt215_hda_hpd,
|
||||
.eld = gt215_hda_eld,
|
||||
},
|
||||
};
|
||||
|
||||
int
|
||||
gt215_sor_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
return nv50_sor_new_(>215_sor, disp, id);
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright 2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "ior.h"
|
||||
|
||||
static const struct nvkm_ior_func
|
||||
mcp77_sor = {
|
||||
.state = g94_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = nv50_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = g84_hdmi_ctrl,
|
||||
},
|
||||
.dp = {
|
||||
.lanes = { 2, 1, 0, 3},
|
||||
.links = g94_sor_dp_links,
|
||||
.power = g94_sor_dp_power,
|
||||
.pattern = g94_sor_dp_pattern,
|
||||
.drive = g94_sor_dp_drive,
|
||||
.audio_sym = g94_sor_dp_audio_sym,
|
||||
.activesym = g94_sor_dp_activesym,
|
||||
.watermark = g94_sor_dp_watermark,
|
||||
},
|
||||
};
|
||||
|
||||
int
|
||||
mcp77_sor_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
return nv50_sor_new_(&mcp77_sor, disp, id);
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2017 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "ior.h"
|
||||
|
||||
static const struct nvkm_ior_func
|
||||
mcp89_sor = {
|
||||
.state = g94_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = nv50_sor_clock,
|
||||
.hdmi = {
|
||||
.ctrl = gt215_hdmi_ctrl,
|
||||
},
|
||||
.dp = {
|
||||
.lanes = { 3, 2, 1, 0 },
|
||||
.links = g94_sor_dp_links,
|
||||
.power = g94_sor_dp_power,
|
||||
.pattern = g94_sor_dp_pattern,
|
||||
.drive = g94_sor_dp_drive,
|
||||
.audio = gt215_sor_dp_audio,
|
||||
.audio_sym = g94_sor_dp_audio_sym,
|
||||
.activesym = g94_sor_dp_activesym,
|
||||
.watermark = g94_sor_dp_watermark,
|
||||
},
|
||||
.hda = {
|
||||
.hpd = gt215_hda_hpd,
|
||||
.eld = gt215_hda_eld,
|
||||
},
|
||||
};
|
||||
|
||||
int
|
||||
mcp89_sor_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
return nv50_sor_new_(&mcp89_sor, disp, id);
|
||||
}
|
|
@ -21,59 +21,87 @@
|
|||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
#include "nv50.h"
|
||||
#include "outp.h"
|
||||
#include "ior.h"
|
||||
|
||||
#include <core/client.h>
|
||||
#include <subdev/timer.h>
|
||||
|
||||
#include <nvif/cl5070.h>
|
||||
#include <nvif/unpack.h>
|
||||
|
||||
int
|
||||
nv50_sor_power(NV50_DISP_MTHD_V1)
|
||||
void
|
||||
nv50_sor_clock(struct nvkm_ior *sor)
|
||||
{
|
||||
struct nvkm_device *device = disp->base.engine.subdev.device;
|
||||
union {
|
||||
struct nv50_disp_sor_pwr_v0 v0;
|
||||
} *args = data;
|
||||
const u32 soff = outp->or * 0x800;
|
||||
u32 stat;
|
||||
int ret = -ENOSYS;
|
||||
|
||||
nvif_ioctl(object, "disp sor pwr size %d\n", size);
|
||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
||||
nvif_ioctl(object, "disp sor pwr vers %d state %d\n",
|
||||
args->v0.version, args->v0.state);
|
||||
stat = !!args->v0.state;
|
||||
} else
|
||||
return ret;
|
||||
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const int div = sor->asy.link == 3;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
nvkm_mask(device, 0x614300 + soff, 0x00000707, (div << 8) | div);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_sor_power_wait(struct nvkm_device *device, u32 soff)
|
||||
{
|
||||
nvkm_msec(device, 2000,
|
||||
if (!(nvkm_rd32(device, 0x61c004 + soff) & 0x80000000))
|
||||
break;
|
||||
);
|
||||
nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000000 | stat);
|
||||
nvkm_msec(device, 2000,
|
||||
if (!(nvkm_rd32(device, 0x61c004 + soff) & 0x80000000))
|
||||
break;
|
||||
);
|
||||
}
|
||||
|
||||
void
|
||||
nv50_sor_power(struct nvkm_ior *sor, bool normal, bool pu,
|
||||
bool data, bool vsync, bool hsync)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 soff = nv50_ior_base(sor);
|
||||
const u32 shift = normal ? 0 : 16;
|
||||
const u32 state = 0x80000000 | (0x00000001 * !!pu) << shift;
|
||||
const u32 field = 0x80000000 | (0x00000001 << shift);
|
||||
|
||||
nv50_sor_power_wait(device, soff);
|
||||
nvkm_mask(device, 0x61c004 + soff, field, state);
|
||||
nv50_sor_power_wait(device, soff);
|
||||
|
||||
nvkm_msec(device, 2000,
|
||||
if (!(nvkm_rd32(device, 0x61c030 + soff) & 0x10000000))
|
||||
break;
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct nvkm_output_func
|
||||
nv50_sor_output_func = {
|
||||
void
|
||||
nv50_sor_state(struct nvkm_ior *sor, struct nvkm_ior_state *state)
|
||||
{
|
||||
struct nvkm_device *device = sor->disp->engine.subdev.device;
|
||||
const u32 coff = sor->id * 8 + (state == &sor->arm) * 4;
|
||||
u32 ctrl = nvkm_rd32(device, 0x610b70 + coff);
|
||||
|
||||
state->proto_evo = (ctrl & 0x00000f00) >> 8;
|
||||
switch (state->proto_evo) {
|
||||
case 0: state->proto = LVDS; state->link = 1; break;
|
||||
case 1: state->proto = TMDS; state->link = 1; break;
|
||||
case 2: state->proto = TMDS; state->link = 2; break;
|
||||
case 5: state->proto = TMDS; state->link = 3; break;
|
||||
default:
|
||||
state->proto = UNKNOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
state->head = ctrl & 0x00000003;
|
||||
}
|
||||
|
||||
int
|
||||
nv50_sor_new_(const struct nvkm_ior_func *func, struct nvkm_disp *disp, int id)
|
||||
{
|
||||
struct nvkm_device *device = disp->engine.subdev.device;
|
||||
if (!(nvkm_rd32(device, 0x610184) & (0x01000000 << id)))
|
||||
return 0;
|
||||
return nvkm_ior_new_(func, disp, SOR, id);
|
||||
}
|
||||
|
||||
static const struct nvkm_ior_func
|
||||
nv50_sor = {
|
||||
.state = nv50_sor_state,
|
||||
.power = nv50_sor_power,
|
||||
.clock = nv50_sor_clock,
|
||||
};
|
||||
|
||||
int
|
||||
nv50_sor_output_new(struct nvkm_disp *disp, int index,
|
||||
struct dcb_output *dcbE, struct nvkm_output **poutp)
|
||||
nv50_sor_new(struct nvkm_disp *disp, int id)
|
||||
{
|
||||
return nvkm_output_new_(&nv50_sor_output_func, disp,
|
||||
index, dcbE, poutp);
|
||||
return nv50_sor_new_(&nv50_sor, disp, id);
|
||||
}
|
||||
|
|
|
@ -87,7 +87,10 @@ nvbios_iccsense_parse(struct nvkm_bios *bios, struct nvbios_iccsense *iccsense)
|
|||
|
||||
switch(ver) {
|
||||
case 0x10:
|
||||
rail->mode = nvbios_rd08(bios, entry + 0x1);
|
||||
if ((nvbios_rd08(bios, entry + 0x1) & 0xf8) == 0xf8)
|
||||
rail->mode = 1;
|
||||
else
|
||||
rail->mode = 0;
|
||||
rail->extdev_id = nvbios_rd08(bios, entry + 0x2);
|
||||
res_start = 0x3;
|
||||
break;
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
#include <subdev/vga.h>
|
||||
|
||||
#define bioslog(lvl, fmt, args...) do { \
|
||||
nvkm_printk(init->subdev, lvl, info, "0x%04x[%c]: "fmt, \
|
||||
nvkm_printk(init->subdev, lvl, info, "0x%08x[%c]: "fmt, \
|
||||
init->offset, init_exec(init) ? \
|
||||
'0' + (init->nested - 1) : ' ', ##args); \
|
||||
} while(0)
|
||||
|
@ -87,8 +87,8 @@ static inline int
|
|||
init_or(struct nvbios_init *init)
|
||||
{
|
||||
if (init_exec(init)) {
|
||||
if (init->outp)
|
||||
return ffs(init->outp->or) - 1;
|
||||
if (init->or >= 0)
|
||||
return init->or;
|
||||
error("script needs OR!!\n");
|
||||
}
|
||||
return 0;
|
||||
|
@ -98,20 +98,20 @@ static inline int
|
|||
init_link(struct nvbios_init *init)
|
||||
{
|
||||
if (init_exec(init)) {
|
||||
if (init->outp)
|
||||
return !(init->outp->sorconf.link & 1);
|
||||
if (init->link)
|
||||
return init->link == 2;
|
||||
error("script needs OR link\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
init_crtc(struct nvbios_init *init)
|
||||
init_head(struct nvbios_init *init)
|
||||
{
|
||||
if (init_exec(init)) {
|
||||
if (init->crtc >= 0)
|
||||
return init->crtc;
|
||||
error("script needs crtc\n");
|
||||
if (init->head >= 0)
|
||||
return init->head;
|
||||
error("script needs head\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ init_crtc(struct nvbios_init *init)
|
|||
static u8
|
||||
init_conn(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
struct nvbios_connE connE;
|
||||
u8 ver, hdr;
|
||||
u32 conn;
|
||||
|
@ -141,7 +141,7 @@ init_conn(struct nvbios_init *init)
|
|||
static inline u32
|
||||
init_nvreg(struct nvbios_init *init, u32 reg)
|
||||
{
|
||||
struct nvkm_devinit *devinit = init->bios->subdev.device->devinit;
|
||||
struct nvkm_devinit *devinit = init->subdev->device->devinit;
|
||||
|
||||
/* C51 (at least) sometimes has the lower bits set which the VBIOS
|
||||
* interprets to mean that access needs to go through certain IO
|
||||
|
@ -154,9 +154,9 @@ init_nvreg(struct nvbios_init *init, u32 reg)
|
|||
/* GF8+ display scripts need register addresses mangled a bit to
|
||||
* select a specific CRTC/OR
|
||||
*/
|
||||
if (init->bios->subdev.device->card_type >= NV_50) {
|
||||
if (init->subdev->device->card_type >= NV_50) {
|
||||
if (reg & 0x80000000) {
|
||||
reg += init_crtc(init) * 0x800;
|
||||
reg += init_head(init) * 0x800;
|
||||
reg &= ~0x80000000;
|
||||
}
|
||||
|
||||
|
@ -179,7 +179,7 @@ init_nvreg(struct nvbios_init *init, u32 reg)
|
|||
static u32
|
||||
init_rd32(struct nvbios_init *init, u32 reg)
|
||||
{
|
||||
struct nvkm_device *device = init->bios->subdev.device;
|
||||
struct nvkm_device *device = init->subdev->device;
|
||||
reg = init_nvreg(init, reg);
|
||||
if (reg != ~0 && init_exec(init))
|
||||
return nvkm_rd32(device, reg);
|
||||
|
@ -189,7 +189,7 @@ init_rd32(struct nvbios_init *init, u32 reg)
|
|||
static void
|
||||
init_wr32(struct nvbios_init *init, u32 reg, u32 val)
|
||||
{
|
||||
struct nvkm_device *device = init->bios->subdev.device;
|
||||
struct nvkm_device *device = init->subdev->device;
|
||||
reg = init_nvreg(init, reg);
|
||||
if (reg != ~0 && init_exec(init))
|
||||
nvkm_wr32(device, reg, val);
|
||||
|
@ -198,7 +198,7 @@ init_wr32(struct nvbios_init *init, u32 reg, u32 val)
|
|||
static u32
|
||||
init_mask(struct nvbios_init *init, u32 reg, u32 mask, u32 val)
|
||||
{
|
||||
struct nvkm_device *device = init->bios->subdev.device;
|
||||
struct nvkm_device *device = init->subdev->device;
|
||||
reg = init_nvreg(init, reg);
|
||||
if (reg != ~0 && init_exec(init)) {
|
||||
u32 tmp = nvkm_rd32(device, reg);
|
||||
|
@ -212,7 +212,7 @@ static u8
|
|||
init_rdport(struct nvbios_init *init, u16 port)
|
||||
{
|
||||
if (init_exec(init))
|
||||
return nvkm_rdport(init->subdev->device, init->crtc, port);
|
||||
return nvkm_rdport(init->subdev->device, init->head, port);
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
|
@ -220,7 +220,7 @@ static void
|
|||
init_wrport(struct nvbios_init *init, u16 port, u8 value)
|
||||
{
|
||||
if (init_exec(init))
|
||||
nvkm_wrport(init->subdev->device, init->crtc, port, value);
|
||||
nvkm_wrport(init->subdev->device, init->head, port, value);
|
||||
}
|
||||
|
||||
static u8
|
||||
|
@ -228,7 +228,7 @@ init_rdvgai(struct nvbios_init *init, u16 port, u8 index)
|
|||
{
|
||||
struct nvkm_subdev *subdev = init->subdev;
|
||||
if (init_exec(init)) {
|
||||
int head = init->crtc < 0 ? 0 : init->crtc;
|
||||
int head = init->head < 0 ? 0 : init->head;
|
||||
return nvkm_rdvgai(subdev->device, head, port, index);
|
||||
}
|
||||
return 0x00;
|
||||
|
@ -242,25 +242,25 @@ init_wrvgai(struct nvbios_init *init, u16 port, u8 index, u8 value)
|
|||
/* force head 0 for updates to cr44, it only exists on first head */
|
||||
if (device->card_type < NV_50) {
|
||||
if (port == 0x03d4 && index == 0x44)
|
||||
init->crtc = 0;
|
||||
init->head = 0;
|
||||
}
|
||||
|
||||
if (init_exec(init)) {
|
||||
int head = init->crtc < 0 ? 0 : init->crtc;
|
||||
int head = init->head < 0 ? 0 : init->head;
|
||||
nvkm_wrvgai(device, head, port, index, value);
|
||||
}
|
||||
|
||||
/* select head 1 if cr44 write selected it */
|
||||
if (device->card_type < NV_50) {
|
||||
if (port == 0x03d4 && index == 0x44 && value == 3)
|
||||
init->crtc = 1;
|
||||
init->head = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static struct i2c_adapter *
|
||||
init_i2c(struct nvbios_init *init, int index)
|
||||
{
|
||||
struct nvkm_i2c *i2c = init->bios->subdev.device->i2c;
|
||||
struct nvkm_i2c *i2c = init->subdev->device->i2c;
|
||||
struct nvkm_i2c_bus *bus;
|
||||
|
||||
if (index == 0xff) {
|
||||
|
@ -300,7 +300,7 @@ init_wri2cr(struct nvbios_init *init, u8 index, u8 addr, u8 reg, u8 val)
|
|||
static struct nvkm_i2c_aux *
|
||||
init_aux(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_i2c *i2c = init->bios->subdev.device->i2c;
|
||||
struct nvkm_i2c *i2c = init->subdev->device->i2c;
|
||||
if (!init->outp) {
|
||||
if (init_exec(init))
|
||||
error("script needs output for aux\n");
|
||||
|
@ -341,7 +341,7 @@ init_wrauxr(struct nvbios_init *init, u32 addr, u8 data)
|
|||
static void
|
||||
init_prog_pll(struct nvbios_init *init, u32 id, u32 freq)
|
||||
{
|
||||
struct nvkm_devinit *devinit = init->bios->subdev.device->devinit;
|
||||
struct nvkm_devinit *devinit = init->subdev->device->devinit;
|
||||
if (init_exec(init)) {
|
||||
int ret = nvkm_devinit_pll_set(devinit, id, freq);
|
||||
if (ret)
|
||||
|
@ -374,7 +374,7 @@ init_table(struct nvkm_bios *bios, u16 *len)
|
|||
static u16
|
||||
init_table_(struct nvbios_init *init, u16 offset, const char *name)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 len, data = init_table(bios, &len);
|
||||
if (data) {
|
||||
if (len >= offset + 2) {
|
||||
|
@ -406,7 +406,7 @@ init_table_(struct nvbios_init *init, u16 offset, const char *name)
|
|||
static u16
|
||||
init_script(struct nvkm_bios *bios, int index)
|
||||
{
|
||||
struct nvbios_init init = { .bios = bios };
|
||||
struct nvbios_init init = { .subdev = &bios->subdev };
|
||||
u16 bmp_ver = bmp_version(bios), data;
|
||||
|
||||
if (bmp_ver && bmp_ver < 0x0510) {
|
||||
|
@ -436,7 +436,7 @@ init_unknown_script(struct nvkm_bios *bios)
|
|||
static u8
|
||||
init_ram_restrict_group_count(struct nvbios_init *init)
|
||||
{
|
||||
return nvbios_ramcfg_count(init->bios);
|
||||
return nvbios_ramcfg_count(init->subdev->device->bios);
|
||||
}
|
||||
|
||||
static u8
|
||||
|
@ -450,7 +450,7 @@ init_ram_restrict(struct nvbios_init *init)
|
|||
* Preserving the non-caching behaviour on earlier chipsets just
|
||||
* in case *not* re-reading the strap causes similar breakage.
|
||||
*/
|
||||
if (!init->ramcfg || init->bios->version.major < 0x70)
|
||||
if (!init->ramcfg || init->subdev->device->bios->version.major < 0x70)
|
||||
init->ramcfg = 0x80000000 | nvbios_ramcfg_index(init->subdev);
|
||||
return (init->ramcfg & 0x7fffffff);
|
||||
}
|
||||
|
@ -458,7 +458,7 @@ init_ram_restrict(struct nvbios_init *init)
|
|||
static u8
|
||||
init_xlat_(struct nvbios_init *init, u8 index, u8 offset)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 table = init_xlat_table(init);
|
||||
if (table) {
|
||||
u16 data = nvbios_rd16(bios, table + (index * 2));
|
||||
|
@ -476,7 +476,7 @@ init_xlat_(struct nvbios_init *init, u8 index, u8 offset)
|
|||
static bool
|
||||
init_condition_met(struct nvbios_init *init, u8 cond)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 table = init_condition_table(init);
|
||||
if (table) {
|
||||
u32 reg = nvbios_rd32(bios, table + (cond * 12) + 0);
|
||||
|
@ -492,7 +492,7 @@ init_condition_met(struct nvbios_init *init, u8 cond)
|
|||
static bool
|
||||
init_io_condition_met(struct nvbios_init *init, u8 cond)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 table = init_io_condition_table(init);
|
||||
if (table) {
|
||||
u16 port = nvbios_rd16(bios, table + (cond * 5) + 0);
|
||||
|
@ -509,7 +509,7 @@ init_io_condition_met(struct nvbios_init *init, u8 cond)
|
|||
static bool
|
||||
init_io_flag_condition_met(struct nvbios_init *init, u8 cond)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 table = init_io_flag_condition_table(init);
|
||||
if (table) {
|
||||
u16 port = nvbios_rd16(bios, table + (cond * 9) + 0);
|
||||
|
@ -580,7 +580,8 @@ init_tmds_reg(struct nvbios_init *init, u8 tmds)
|
|||
static void
|
||||
init_reserved(struct nvbios_init *init)
|
||||
{
|
||||
u8 opcode = nvbios_rd08(init->bios, init->offset);
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 opcode = nvbios_rd08(bios, init->offset);
|
||||
u8 length, i;
|
||||
|
||||
switch (opcode) {
|
||||
|
@ -594,7 +595,7 @@ init_reserved(struct nvbios_init *init)
|
|||
|
||||
trace("RESERVED 0x%02x\t", opcode);
|
||||
for (i = 1; i < length; i++)
|
||||
cont(" 0x%02x", nvbios_rd08(init->bios, init->offset + i));
|
||||
cont(" 0x%02x", nvbios_rd08(bios, init->offset + i));
|
||||
cont("\n");
|
||||
init->offset += length;
|
||||
}
|
||||
|
@ -617,7 +618,7 @@ init_done(struct nvbios_init *init)
|
|||
static void
|
||||
init_io_restrict_prog(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 port = nvbios_rd16(bios, init->offset + 1);
|
||||
u8 index = nvbios_rd08(bios, init->offset + 3);
|
||||
u8 mask = nvbios_rd08(bios, init->offset + 4);
|
||||
|
@ -654,7 +655,7 @@ init_io_restrict_prog(struct nvbios_init *init)
|
|||
static void
|
||||
init_repeat(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 count = nvbios_rd08(bios, init->offset + 1);
|
||||
u16 repeat = init->repeat;
|
||||
|
||||
|
@ -680,7 +681,7 @@ init_repeat(struct nvbios_init *init)
|
|||
static void
|
||||
init_io_restrict_pll(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 port = nvbios_rd16(bios, init->offset + 1);
|
||||
u8 index = nvbios_rd08(bios, init->offset + 3);
|
||||
u8 mask = nvbios_rd08(bios, init->offset + 4);
|
||||
|
@ -736,7 +737,7 @@ init_end_repeat(struct nvbios_init *init)
|
|||
static void
|
||||
init_copy(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
||||
u8 shift = nvbios_rd08(bios, init->offset + 5);
|
||||
u8 smask = nvbios_rd08(bios, init->offset + 6);
|
||||
|
@ -775,7 +776,7 @@ init_not(struct nvbios_init *init)
|
|||
static void
|
||||
init_io_flag_condition(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 cond = nvbios_rd08(bios, init->offset + 1);
|
||||
|
||||
trace("IO_FLAG_CONDITION\t0x%02x\n", cond);
|
||||
|
@ -792,7 +793,7 @@ init_io_flag_condition(struct nvbios_init *init)
|
|||
static void
|
||||
init_generic_condition(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
struct nvbios_dpout info;
|
||||
u8 cond = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 size = nvbios_rd08(bios, init->offset + 2);
|
||||
|
@ -841,7 +842,7 @@ init_generic_condition(struct nvbios_init *init)
|
|||
static void
|
||||
init_io_mask_or(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 index = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 or = init_or(init);
|
||||
u8 data;
|
||||
|
@ -860,7 +861,7 @@ init_io_mask_or(struct nvbios_init *init)
|
|||
static void
|
||||
init_io_or(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 index = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 or = init_or(init);
|
||||
u8 data;
|
||||
|
@ -879,7 +880,7 @@ init_io_or(struct nvbios_init *init)
|
|||
static void
|
||||
init_andn_reg(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
||||
u32 mask = nvbios_rd32(bios, init->offset + 5);
|
||||
|
||||
|
@ -896,7 +897,7 @@ init_andn_reg(struct nvbios_init *init)
|
|||
static void
|
||||
init_or_reg(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
||||
u32 mask = nvbios_rd32(bios, init->offset + 5);
|
||||
|
||||
|
@ -913,7 +914,7 @@ init_or_reg(struct nvbios_init *init)
|
|||
static void
|
||||
init_idx_addr_latched(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 creg = nvbios_rd32(bios, init->offset + 1);
|
||||
u32 dreg = nvbios_rd32(bios, init->offset + 5);
|
||||
u32 mask = nvbios_rd32(bios, init->offset + 9);
|
||||
|
@ -943,7 +944,7 @@ init_idx_addr_latched(struct nvbios_init *init)
|
|||
static void
|
||||
init_io_restrict_pll2(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 port = nvbios_rd16(bios, init->offset + 1);
|
||||
u8 index = nvbios_rd08(bios, init->offset + 3);
|
||||
u8 mask = nvbios_rd08(bios, init->offset + 4);
|
||||
|
@ -978,7 +979,7 @@ init_io_restrict_pll2(struct nvbios_init *init)
|
|||
static void
|
||||
init_pll2(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
||||
u32 freq = nvbios_rd32(bios, init->offset + 5);
|
||||
|
||||
|
@ -995,7 +996,7 @@ init_pll2(struct nvbios_init *init)
|
|||
static void
|
||||
init_i2c_byte(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 index = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1;
|
||||
u8 count = nvbios_rd08(bios, init->offset + 3);
|
||||
|
@ -1026,7 +1027,7 @@ init_i2c_byte(struct nvbios_init *init)
|
|||
static void
|
||||
init_zm_i2c_byte(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 index = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1;
|
||||
u8 count = nvbios_rd08(bios, init->offset + 3);
|
||||
|
@ -1052,7 +1053,7 @@ init_zm_i2c_byte(struct nvbios_init *init)
|
|||
static void
|
||||
init_zm_i2c(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 index = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1;
|
||||
u8 count = nvbios_rd08(bios, init->offset + 3);
|
||||
|
@ -1086,7 +1087,7 @@ init_zm_i2c(struct nvbios_init *init)
|
|||
static void
|
||||
init_tmds(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 tmds = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 addr = nvbios_rd08(bios, init->offset + 2);
|
||||
u8 mask = nvbios_rd08(bios, init->offset + 3);
|
||||
|
@ -1112,7 +1113,7 @@ init_tmds(struct nvbios_init *init)
|
|||
static void
|
||||
init_zm_tmds_group(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 tmds = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 count = nvbios_rd08(bios, init->offset + 2);
|
||||
u32 reg = init_tmds_reg(init, tmds);
|
||||
|
@ -1139,7 +1140,7 @@ init_zm_tmds_group(struct nvbios_init *init)
|
|||
static void
|
||||
init_cr_idx_adr_latch(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 addr0 = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 addr1 = nvbios_rd08(bios, init->offset + 2);
|
||||
u8 base = nvbios_rd08(bios, init->offset + 3);
|
||||
|
@ -1169,7 +1170,7 @@ init_cr_idx_adr_latch(struct nvbios_init *init)
|
|||
static void
|
||||
init_cr(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 addr = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 mask = nvbios_rd08(bios, init->offset + 2);
|
||||
u8 data = nvbios_rd08(bios, init->offset + 3);
|
||||
|
@ -1189,7 +1190,7 @@ init_cr(struct nvbios_init *init)
|
|||
static void
|
||||
init_zm_cr(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 addr = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 data = nvbios_rd08(bios, init->offset + 2);
|
||||
|
||||
|
@ -1206,7 +1207,7 @@ init_zm_cr(struct nvbios_init *init)
|
|||
static void
|
||||
init_zm_cr_group(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 count = nvbios_rd08(bios, init->offset + 1);
|
||||
|
||||
trace("ZM_CR_GROUP\n");
|
||||
|
@ -1230,7 +1231,7 @@ init_zm_cr_group(struct nvbios_init *init)
|
|||
static void
|
||||
init_condition_time(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 cond = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 retry = nvbios_rd08(bios, init->offset + 2);
|
||||
u8 wait = min((u16)retry * 50, 100);
|
||||
|
@ -1257,7 +1258,7 @@ init_condition_time(struct nvbios_init *init)
|
|||
static void
|
||||
init_ltime(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 msec = nvbios_rd16(bios, init->offset + 1);
|
||||
|
||||
trace("LTIME\t0x%04x\n", msec);
|
||||
|
@ -1274,7 +1275,7 @@ init_ltime(struct nvbios_init *init)
|
|||
static void
|
||||
init_zm_reg_sequence(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 base = nvbios_rd32(bios, init->offset + 1);
|
||||
u8 count = nvbios_rd08(bios, init->offset + 5);
|
||||
|
||||
|
@ -1299,7 +1300,7 @@ init_zm_reg_sequence(struct nvbios_init *init)
|
|||
static void
|
||||
init_pll_indirect(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
||||
u16 addr = nvbios_rd16(bios, init->offset + 5);
|
||||
u32 freq = (u32)nvbios_rd16(bios, addr) * 1000;
|
||||
|
@ -1318,7 +1319,7 @@ init_pll_indirect(struct nvbios_init *init)
|
|||
static void
|
||||
init_zm_reg_indirect(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
||||
u16 addr = nvbios_rd16(bios, init->offset + 5);
|
||||
u32 data = nvbios_rd32(bios, addr);
|
||||
|
@ -1337,7 +1338,7 @@ init_zm_reg_indirect(struct nvbios_init *init)
|
|||
static void
|
||||
init_sub_direct(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 addr = nvbios_rd16(bios, init->offset + 1);
|
||||
u16 save;
|
||||
|
||||
|
@ -1363,7 +1364,7 @@ init_sub_direct(struct nvbios_init *init)
|
|||
static void
|
||||
init_jump(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 offset = nvbios_rd16(bios, init->offset + 1);
|
||||
|
||||
trace("JUMP\t0x%04x\n", offset);
|
||||
|
@ -1381,7 +1382,7 @@ init_jump(struct nvbios_init *init)
|
|||
static void
|
||||
init_i2c_if(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 index = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 addr = nvbios_rd08(bios, init->offset + 2);
|
||||
u8 reg = nvbios_rd08(bios, init->offset + 3);
|
||||
|
@ -1408,7 +1409,7 @@ init_i2c_if(struct nvbios_init *init)
|
|||
static void
|
||||
init_copy_nv_reg(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 sreg = nvbios_rd32(bios, init->offset + 1);
|
||||
u8 shift = nvbios_rd08(bios, init->offset + 5);
|
||||
u32 smask = nvbios_rd32(bios, init->offset + 6);
|
||||
|
@ -1434,7 +1435,7 @@ init_copy_nv_reg(struct nvbios_init *init)
|
|||
static void
|
||||
init_zm_index_io(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 port = nvbios_rd16(bios, init->offset + 1);
|
||||
u8 index = nvbios_rd08(bios, init->offset + 3);
|
||||
u8 data = nvbios_rd08(bios, init->offset + 4);
|
||||
|
@ -1452,7 +1453,7 @@ init_zm_index_io(struct nvbios_init *init)
|
|||
static void
|
||||
init_compute_mem(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_devinit *devinit = init->bios->subdev.device->devinit;
|
||||
struct nvkm_devinit *devinit = init->subdev->device->devinit;
|
||||
|
||||
trace("COMPUTE_MEM\n");
|
||||
init->offset += 1;
|
||||
|
@ -1470,7 +1471,7 @@ init_compute_mem(struct nvbios_init *init)
|
|||
static void
|
||||
init_reset(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
||||
u32 data1 = nvbios_rd32(bios, init->offset + 5);
|
||||
u32 data2 = nvbios_rd32(bios, init->offset + 9);
|
||||
|
@ -1497,7 +1498,7 @@ init_reset(struct nvbios_init *init)
|
|||
static u16
|
||||
init_configure_mem_clk(struct nvbios_init *init)
|
||||
{
|
||||
u16 mdata = bmp_mem_init_table(init->bios);
|
||||
u16 mdata = bmp_mem_init_table(init->subdev->device->bios);
|
||||
if (mdata)
|
||||
mdata += (init_rdvgai(init, 0x03d4, 0x3c) >> 4) * 66;
|
||||
return mdata;
|
||||
|
@ -1506,7 +1507,7 @@ init_configure_mem_clk(struct nvbios_init *init)
|
|||
static void
|
||||
init_configure_mem(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 mdata, sdata;
|
||||
u32 addr, data;
|
||||
|
||||
|
@ -1556,7 +1557,7 @@ init_configure_mem(struct nvbios_init *init)
|
|||
static void
|
||||
init_configure_clk(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 mdata, clock;
|
||||
|
||||
trace("CONFIGURE_CLK\n");
|
||||
|
@ -1590,7 +1591,7 @@ init_configure_clk(struct nvbios_init *init)
|
|||
static void
|
||||
init_configure_preinit(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 strap;
|
||||
|
||||
trace("CONFIGURE_PREINIT\n");
|
||||
|
@ -1616,7 +1617,7 @@ init_configure_preinit(struct nvbios_init *init)
|
|||
static void
|
||||
init_io(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 port = nvbios_rd16(bios, init->offset + 1);
|
||||
u8 mask = nvbios_rd16(bios, init->offset + 3);
|
||||
u8 data = nvbios_rd16(bios, init->offset + 4);
|
||||
|
@ -1656,7 +1657,7 @@ init_io(struct nvbios_init *init)
|
|||
static void
|
||||
init_sub(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 index = nvbios_rd08(bios, init->offset + 1);
|
||||
u16 addr, save;
|
||||
|
||||
|
@ -1683,7 +1684,7 @@ init_sub(struct nvbios_init *init)
|
|||
static void
|
||||
init_ram_condition(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 mask = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 value = nvbios_rd08(bios, init->offset + 2);
|
||||
|
||||
|
@ -1702,7 +1703,7 @@ init_ram_condition(struct nvbios_init *init)
|
|||
static void
|
||||
init_nv_reg(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
||||
u32 mask = nvbios_rd32(bios, init->offset + 5);
|
||||
u32 data = nvbios_rd32(bios, init->offset + 9);
|
||||
|
@ -1720,7 +1721,7 @@ init_nv_reg(struct nvbios_init *init)
|
|||
static void
|
||||
init_macro(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 macro = nvbios_rd08(bios, init->offset + 1);
|
||||
u16 table;
|
||||
|
||||
|
@ -1756,7 +1757,7 @@ init_resume(struct nvbios_init *init)
|
|||
static void
|
||||
init_strap_condition(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 mask = nvbios_rd32(bios, init->offset + 1);
|
||||
u32 value = nvbios_rd32(bios, init->offset + 5);
|
||||
|
||||
|
@ -1774,7 +1775,7 @@ init_strap_condition(struct nvbios_init *init)
|
|||
static void
|
||||
init_time(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 usec = nvbios_rd16(bios, init->offset + 1);
|
||||
|
||||
trace("TIME\t0x%04x\n", usec);
|
||||
|
@ -1795,7 +1796,7 @@ init_time(struct nvbios_init *init)
|
|||
static void
|
||||
init_condition(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 cond = nvbios_rd08(bios, init->offset + 1);
|
||||
|
||||
trace("CONDITION\t0x%02x\n", cond);
|
||||
|
@ -1812,7 +1813,7 @@ init_condition(struct nvbios_init *init)
|
|||
static void
|
||||
init_io_condition(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 cond = nvbios_rd08(bios, init->offset + 1);
|
||||
|
||||
trace("IO_CONDITION\t0x%02x\n", cond);
|
||||
|
@ -1829,7 +1830,7 @@ init_io_condition(struct nvbios_init *init)
|
|||
static void
|
||||
init_zm_reg16(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 addr = nvbios_rd32(bios, init->offset + 1);
|
||||
u16 data = nvbios_rd16(bios, init->offset + 5);
|
||||
|
||||
|
@ -1846,7 +1847,7 @@ init_zm_reg16(struct nvbios_init *init)
|
|||
static void
|
||||
init_index_io(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u16 port = nvbios_rd16(bios, init->offset + 1);
|
||||
u8 index = nvbios_rd16(bios, init->offset + 3);
|
||||
u8 mask = nvbios_rd08(bios, init->offset + 4);
|
||||
|
@ -1868,7 +1869,7 @@ init_index_io(struct nvbios_init *init)
|
|||
static void
|
||||
init_pll(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 reg = nvbios_rd32(bios, init->offset + 1);
|
||||
u32 freq = nvbios_rd16(bios, init->offset + 5) * 10;
|
||||
|
||||
|
@ -1885,7 +1886,7 @@ init_pll(struct nvbios_init *init)
|
|||
static void
|
||||
init_zm_reg(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 addr = nvbios_rd32(bios, init->offset + 1);
|
||||
u32 data = nvbios_rd32(bios, init->offset + 5);
|
||||
|
||||
|
@ -1905,7 +1906,7 @@ init_zm_reg(struct nvbios_init *init)
|
|||
static void
|
||||
init_ram_restrict_pll(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 type = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 count = init_ram_restrict_group_count(init);
|
||||
u8 strap = init_ram_restrict(init);
|
||||
|
@ -1935,7 +1936,7 @@ init_ram_restrict_pll(struct nvbios_init *init)
|
|||
static void
|
||||
init_gpio(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_gpio *gpio = init->bios->subdev.device->gpio;
|
||||
struct nvkm_gpio *gpio = init->subdev->device->gpio;
|
||||
|
||||
trace("GPIO\n");
|
||||
init->offset += 1;
|
||||
|
@ -1951,7 +1952,7 @@ init_gpio(struct nvbios_init *init)
|
|||
static void
|
||||
init_ram_restrict_zm_reg_group(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 addr = nvbios_rd32(bios, init->offset + 1);
|
||||
u8 incr = nvbios_rd08(bios, init->offset + 5);
|
||||
u8 num = nvbios_rd08(bios, init->offset + 6);
|
||||
|
@ -1989,7 +1990,7 @@ init_ram_restrict_zm_reg_group(struct nvbios_init *init)
|
|||
static void
|
||||
init_copy_zm_reg(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 sreg = nvbios_rd32(bios, init->offset + 1);
|
||||
u32 dreg = nvbios_rd32(bios, init->offset + 5);
|
||||
|
||||
|
@ -2006,7 +2007,7 @@ init_copy_zm_reg(struct nvbios_init *init)
|
|||
static void
|
||||
init_zm_reg_group(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 addr = nvbios_rd32(bios, init->offset + 1);
|
||||
u8 count = nvbios_rd08(bios, init->offset + 5);
|
||||
|
||||
|
@ -2028,7 +2029,7 @@ init_zm_reg_group(struct nvbios_init *init)
|
|||
static void
|
||||
init_xlat(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 saddr = nvbios_rd32(bios, init->offset + 1);
|
||||
u8 sshift = nvbios_rd08(bios, init->offset + 5);
|
||||
u8 smask = nvbios_rd08(bios, init->offset + 6);
|
||||
|
@ -2056,7 +2057,7 @@ init_xlat(struct nvbios_init *init)
|
|||
static void
|
||||
init_zm_mask_add(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 addr = nvbios_rd32(bios, init->offset + 1);
|
||||
u32 mask = nvbios_rd32(bios, init->offset + 5);
|
||||
u32 add = nvbios_rd32(bios, init->offset + 9);
|
||||
|
@ -2077,7 +2078,7 @@ init_zm_mask_add(struct nvbios_init *init)
|
|||
static void
|
||||
init_auxch(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 addr = nvbios_rd32(bios, init->offset + 1);
|
||||
u8 count = nvbios_rd08(bios, init->offset + 5);
|
||||
|
||||
|
@ -2101,7 +2102,7 @@ init_auxch(struct nvbios_init *init)
|
|||
static void
|
||||
init_zm_auxch(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u32 addr = nvbios_rd32(bios, init->offset + 1);
|
||||
u8 count = nvbios_rd08(bios, init->offset + 5);
|
||||
|
||||
|
@ -2123,7 +2124,7 @@ init_zm_auxch(struct nvbios_init *init)
|
|||
static void
|
||||
init_i2c_long_if(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
u8 index = nvbios_rd08(bios, init->offset + 1);
|
||||
u8 addr = nvbios_rd08(bios, init->offset + 2) >> 1;
|
||||
u8 reglo = nvbios_rd08(bios, init->offset + 3);
|
||||
|
@ -2162,7 +2163,7 @@ init_i2c_long_if(struct nvbios_init *init)
|
|||
static void
|
||||
init_gpio_ne(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->bios;
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
struct nvkm_gpio *gpio = bios->subdev.device->gpio;
|
||||
struct dcb_gpio_func func;
|
||||
u8 count = nvbios_rd08(bios, init->offset + 1);
|
||||
|
@ -2275,9 +2276,11 @@ static struct nvbios_init_opcode {
|
|||
int
|
||||
nvbios_exec(struct nvbios_init *init)
|
||||
{
|
||||
struct nvkm_bios *bios = init->subdev->device->bios;
|
||||
|
||||
init->nested++;
|
||||
while (init->offset) {
|
||||
u8 opcode = nvbios_rd08(init->bios, init->offset);
|
||||
u8 opcode = nvbios_rd08(bios, init->offset);
|
||||
if (opcode >= init_opcode_nr || !init_opcode[opcode].exec) {
|
||||
error("unknown opcode 0x%02x\n", opcode);
|
||||
return -EINVAL;
|
||||
|
@ -2290,7 +2293,7 @@ nvbios_exec(struct nvbios_init *init)
|
|||
}
|
||||
|
||||
int
|
||||
nvbios_init(struct nvkm_subdev *subdev, bool execute)
|
||||
nvbios_post(struct nvkm_subdev *subdev, bool execute)
|
||||
{
|
||||
struct nvkm_bios *bios = subdev->device->bios;
|
||||
int ret = 0;
|
||||
|
@ -2300,32 +2303,18 @@ nvbios_init(struct nvkm_subdev *subdev, bool execute)
|
|||
if (execute)
|
||||
nvkm_debug(subdev, "running init tables\n");
|
||||
while (!ret && (data = (init_script(bios, ++i)))) {
|
||||
struct nvbios_init init = {
|
||||
.subdev = subdev,
|
||||
.bios = bios,
|
||||
.offset = data,
|
||||
.outp = NULL,
|
||||
.crtc = -1,
|
||||
.execute = execute ? 1 : 0,
|
||||
};
|
||||
|
||||
ret = nvbios_exec(&init);
|
||||
ret = nvbios_init(subdev, data,
|
||||
init.execute = execute ? 1 : 0;
|
||||
);
|
||||
}
|
||||
|
||||
/* the vbios parser will run this right after the normal init
|
||||
* tables, whereas the binary driver appears to run it later.
|
||||
*/
|
||||
if (!ret && (data = init_unknown_script(bios))) {
|
||||
struct nvbios_init init = {
|
||||
.subdev = subdev,
|
||||
.bios = bios,
|
||||
.offset = data,
|
||||
.outp = NULL,
|
||||
.crtc = -1,
|
||||
.execute = execute ? 1 : 0,
|
||||
};
|
||||
|
||||
ret = nvbios_exec(&init);
|
||||
ret = nvbios_init(subdev, data,
|
||||
init.execute = execute ? 1 : 0;
|
||||
);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -93,9 +93,9 @@ nvbios_volt_parse(struct nvkm_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
|
|||
info->step = nvbios_rd16(bios, volt + 0x08);
|
||||
info->vidmask = nvbios_rd08(bios, volt + 0x0b);
|
||||
info->ranged = true; /* XXX: find the flag byte */
|
||||
/*XXX*/
|
||||
info->min = 0;
|
||||
info->max = info->base;
|
||||
info->min = min(info->base,
|
||||
info->base + info->step * info->vidmask);
|
||||
info->max = nvbios_rd32(bios, volt + 0x0e);
|
||||
break;
|
||||
case 0x50:
|
||||
info->min = nvbios_rd32(bios, volt + 0x0a);
|
||||
|
|
|
@ -393,7 +393,7 @@ nv04_devinit_pll_set(struct nvkm_devinit *devinit, u32 type, u32 freq)
|
|||
int
|
||||
nv04_devinit_post(struct nvkm_devinit *init, bool execute)
|
||||
{
|
||||
return nvbios_init(&init->subdev, execute);
|
||||
return nvbios_post(&init->subdev, execute);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -137,16 +137,11 @@ nv50_devinit_init(struct nvkm_devinit *base)
|
|||
while (init->base.post && dcb_outp_parse(bios, i, &ver, &hdr, &outp)) {
|
||||
if (nvbios_outp_match(bios, outp.hasht, outp.hashm,
|
||||
&ver, &hdr, &cnt, &len, &info)) {
|
||||
struct nvbios_init exec = {
|
||||
.subdev = subdev,
|
||||
.bios = bios,
|
||||
.offset = info.script[0],
|
||||
.outp = &outp,
|
||||
.crtc = -1,
|
||||
.execute = 1,
|
||||
};
|
||||
|
||||
nvbios_exec(&exec);
|
||||
nvbios_init(subdev, info.script[0],
|
||||
init.outp = &outp;
|
||||
init.or = ffs(outp.or) - 1;
|
||||
init.link = outp.sorconf.link == 2;
|
||||
);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
|
|
@ -1424,12 +1424,7 @@ gk104_ram_init(struct nvkm_ram *ram)
|
|||
for (i = 0; i < cnt; i++, data += 4) {
|
||||
if (i != save >> 4) {
|
||||
nvkm_mask(device, 0x10f65c, 0x000000f0, i << 4);
|
||||
nvbios_exec(&(struct nvbios_init) {
|
||||
.subdev = subdev,
|
||||
.bios = bios,
|
||||
.offset = nvbios_rd32(bios, data),
|
||||
.execute = 1,
|
||||
});
|
||||
nvbios_init(subdev, nvbios_rd32(bios, data));
|
||||
}
|
||||
}
|
||||
nvkm_mask(device, 0x10f65c, 0x000000f0, save);
|
||||
|
|
|
@ -59,12 +59,7 @@ gp100_ram_init(struct nvkm_ram *ram)
|
|||
for (i = 0; i < cnt; i++, data += 4) {
|
||||
if (i != save >> 4) {
|
||||
nvkm_mask(device, 0x9a065c, 0x000000f0, i << 4);
|
||||
nvbios_exec(&(struct nvbios_init) {
|
||||
.subdev = subdev,
|
||||
.bios = bios,
|
||||
.offset = nvbios_rd32(bios, data),
|
||||
.execute = 1,
|
||||
});
|
||||
nvbios_init(subdev, nvbios_rd32(bios, data));
|
||||
}
|
||||
}
|
||||
nvkm_mask(device, 0x9a065c, 0x000000f0, save);
|
||||
|
|
|
@ -150,16 +150,8 @@ nv40_ram_prog(struct nvkm_ram *base)
|
|||
udelay(100);
|
||||
|
||||
/* execute memory reset script from vbios */
|
||||
if (!bit_entry(bios, 'M', &M)) {
|
||||
struct nvbios_init init = {
|
||||
.subdev = subdev,
|
||||
.bios = bios,
|
||||
.offset = nvbios_rd16(bios, M.offset + 0x00),
|
||||
.execute = 1,
|
||||
};
|
||||
|
||||
nvbios_exec(&init);
|
||||
}
|
||||
if (!bit_entry(bios, 'M', &M))
|
||||
nvbios_init(subdev, nvbios_rd16(bios, M.offset + 0x00));
|
||||
|
||||
/* make sure we're in vblank (hopefully the same one as before), and
|
||||
* then re-enable crtc memory access
|
||||
|
|
|
@ -158,7 +158,7 @@ static void
|
|||
gk20a_pmu_fini(struct nvkm_pmu *pmu)
|
||||
{
|
||||
struct gk20a_pmu *gpmu = gk20a_pmu(pmu);
|
||||
nvkm_timer_alarm_cancel(pmu->subdev.device->timer, &gpmu->alarm);
|
||||
nvkm_timer_alarm(pmu->subdev.device->timer, 0, &gpmu->alarm);
|
||||
|
||||
nvkm_falcon_put(pmu->falcon, &pmu->subdev);
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode)
|
|||
|
||||
switch (mode) {
|
||||
case NVKM_THERM_CTRL_MANUAL:
|
||||
nvkm_timer_alarm_cancel(tmr, &therm->alarm);
|
||||
nvkm_timer_alarm(tmr, 0, &therm->alarm);
|
||||
duty = nvkm_therm_fan_get(therm);
|
||||
if (duty < 0)
|
||||
duty = 100;
|
||||
|
@ -142,7 +142,7 @@ nvkm_therm_update(struct nvkm_therm *therm, int mode)
|
|||
break;
|
||||
case NVKM_THERM_CTRL_NONE:
|
||||
default:
|
||||
nvkm_timer_alarm_cancel(tmr, &therm->alarm);
|
||||
nvkm_timer_alarm(tmr, 0, &therm->alarm);
|
||||
poll = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -215,7 +215,7 @@ nvkm_therm_fan_fini(struct nvkm_therm *therm, bool suspend)
|
|||
{
|
||||
struct nvkm_timer *tmr = therm->subdev.device->timer;
|
||||
if (suspend)
|
||||
nvkm_timer_alarm_cancel(tmr, &therm->fan->alarm);
|
||||
nvkm_timer_alarm(tmr, 0, &therm->fan->alarm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -220,7 +220,7 @@ nvkm_therm_sensor_fini(struct nvkm_therm *therm, bool suspend)
|
|||
{
|
||||
struct nvkm_timer *tmr = therm->subdev.device->timer;
|
||||
if (suspend)
|
||||
nvkm_timer_alarm_cancel(tmr, &therm->sensor.therm_poll_alarm);
|
||||
nvkm_timer_alarm(tmr, 0, &therm->sensor.therm_poll_alarm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -105,15 +105,6 @@ nvkm_timer_alarm(struct nvkm_timer *tmr, u32 nsec, struct nvkm_alarm *alarm)
|
|||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
}
|
||||
|
||||
void
|
||||
nvkm_timer_alarm_cancel(struct nvkm_timer *tmr, struct nvkm_alarm *alarm)
|
||||
{
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
list_del_init(&alarm->head);
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
nvkm_timer_intr(struct nvkm_subdev *subdev)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue