drm/nv50/disp: move remaining interrupt handling into core
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
Родитель
4a230fa618
Коммит
186ecad21c
|
@ -30,9 +30,15 @@
|
|||
#include <engine/software.h>
|
||||
#include <engine/disp.h>
|
||||
|
||||
#include <subdev/bios.h>
|
||||
#include <subdev/bios/dcb.h>
|
||||
#include <subdev/bios/disp.h>
|
||||
#include <subdev/bios/init.h>
|
||||
#include <subdev/bios/pll.h>
|
||||
#include <subdev/timer.h>
|
||||
#include <subdev/fb.h>
|
||||
#include <subdev/bar.h>
|
||||
#include <subdev/clock.h>
|
||||
|
||||
#include "nv50.h"
|
||||
|
||||
|
@ -729,6 +735,27 @@ nv50_disp_cclass = {
|
|||
* Display engine implementation
|
||||
******************************************************************************/
|
||||
|
||||
static void
|
||||
nv50_disp_intr_error(struct nv50_disp_priv *priv)
|
||||
{
|
||||
u32 channels = (nv_rd32(priv, 0x610020) & 0x001f0000) >> 16;
|
||||
u32 addr, data;
|
||||
int chid;
|
||||
|
||||
for (chid = 0; chid < 5; chid++) {
|
||||
if (!(channels & (1 << chid)))
|
||||
continue;
|
||||
|
||||
nv_wr32(priv, 0x610020, 0x00010000 << chid);
|
||||
addr = nv_rd32(priv, 0x610080 + (chid * 0x08));
|
||||
data = nv_rd32(priv, 0x610084 + (chid * 0x08));
|
||||
nv_wr32(priv, 0x610080 + (chid * 0x08), 0x90000000);
|
||||
|
||||
nv_error(priv, "chid %d mthd 0x%04x data 0x%08x 0x%08x\n",
|
||||
chid, addr & 0xffc, data, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
|
||||
{
|
||||
|
@ -766,24 +793,413 @@ nv50_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc)
|
|||
disp->vblank.notify(disp->vblank.data, crtc);
|
||||
}
|
||||
|
||||
static u16
|
||||
exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl,
|
||||
struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
|
||||
struct nvbios_outp *info)
|
||||
{
|
||||
struct nouveau_bios *bios = nouveau_bios(priv);
|
||||
u16 mask, type, data;
|
||||
|
||||
if (outp < 4) {
|
||||
type = DCB_OUTPUT_ANALOG;
|
||||
mask = 0;
|
||||
} else {
|
||||
outp -= 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:
|
||||
nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl);
|
||||
return 0x0000;
|
||||
}
|
||||
}
|
||||
|
||||
mask = 0x00c0 & (mask << 6);
|
||||
mask |= 0x0001 << outp;
|
||||
mask |= 0x0100 << head;
|
||||
|
||||
data = dcb_outp_match(bios, type, mask, ver, hdr, dcb);
|
||||
if (!data)
|
||||
return 0x0000;
|
||||
|
||||
return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info);
|
||||
}
|
||||
|
||||
static bool
|
||||
exec_script(struct nv50_disp_priv *priv, int head, int id)
|
||||
{
|
||||
struct nouveau_bios *bios = nouveau_bios(priv);
|
||||
struct nvbios_outp info;
|
||||
struct dcb_output dcb;
|
||||
u8 ver, hdr, cnt, len;
|
||||
u16 data;
|
||||
u32 ctrl = 0x00000000;
|
||||
int i;
|
||||
|
||||
for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
|
||||
ctrl = nv_rd32(priv, 0x610b5c + (i * 8));
|
||||
|
||||
if (nv_device(priv)->chipset < 0x90 ||
|
||||
nv_device(priv)->chipset == 0x92 ||
|
||||
nv_device(priv)->chipset == 0xa0) {
|
||||
for (i = 0; !(ctrl & (1 << head)) && i < 2; i++)
|
||||
ctrl = nv_rd32(priv, 0x610b74 + (i * 8));
|
||||
i += 3;
|
||||
} else {
|
||||
for (i = 0; !(ctrl & (1 << head)) && i < 4; i++)
|
||||
ctrl = nv_rd32(priv, 0x610798 + (i * 8));
|
||||
i += 3;
|
||||
}
|
||||
|
||||
if (!(ctrl & (1 << head)))
|
||||
return false;
|
||||
|
||||
data = exec_lookup(priv, head, i, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info);
|
||||
if (data) {
|
||||
struct nvbios_init init = {
|
||||
.subdev = nv_subdev(priv),
|
||||
.bios = bios,
|
||||
.offset = info.script[id],
|
||||
.outp = &dcb,
|
||||
.crtc = head,
|
||||
.execute = 1,
|
||||
};
|
||||
|
||||
return nvbios_exec(&init) == 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static u32
|
||||
exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk,
|
||||
struct dcb_output *outp)
|
||||
{
|
||||
struct nouveau_bios *bios = nouveau_bios(priv);
|
||||
struct nvbios_outp info1;
|
||||
struct nvbios_ocfg info2;
|
||||
u8 ver, hdr, cnt, len;
|
||||
u16 data, conf;
|
||||
u32 ctrl = 0x00000000;
|
||||
int i;
|
||||
|
||||
for (i = 0; !(ctrl & (1 << head)) && i < 3; i++)
|
||||
ctrl = nv_rd32(priv, 0x610b58 + (i * 8));
|
||||
|
||||
if (nv_device(priv)->chipset < 0x90 ||
|
||||
nv_device(priv)->chipset == 0x92 ||
|
||||
nv_device(priv)->chipset == 0xa0) {
|
||||
for (i = 0; !(ctrl & (1 << head)) && i < 2; i++)
|
||||
ctrl = nv_rd32(priv, 0x610b70 + (i * 8));
|
||||
i += 3;
|
||||
} else {
|
||||
for (i = 0; !(ctrl & (1 << head)) && i < 4; i++)
|
||||
ctrl = nv_rd32(priv, 0x610794 + (i * 8));
|
||||
i += 3;
|
||||
}
|
||||
|
||||
if (!(ctrl & (1 << head)))
|
||||
return 0x0000;
|
||||
|
||||
data = exec_lookup(priv, head, i, ctrl, outp, &ver, &hdr, &cnt, &len, &info1);
|
||||
if (!data)
|
||||
return 0x0000;
|
||||
|
||||
switch (outp->type) {
|
||||
case DCB_OUTPUT_TMDS:
|
||||
conf = (ctrl & 0x00000f00) >> 8;
|
||||
if (pclk >= 165000)
|
||||
conf |= 0x0100;
|
||||
break;
|
||||
case DCB_OUTPUT_LVDS:
|
||||
conf = priv->sor.lvdsconf;
|
||||
break;
|
||||
case DCB_OUTPUT_DP:
|
||||
conf = (ctrl & 0x00000f00) >> 8;
|
||||
break;
|
||||
case DCB_OUTPUT_ANALOG:
|
||||
default:
|
||||
conf = 0x00ff;
|
||||
break;
|
||||
}
|
||||
|
||||
data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2);
|
||||
if (data) {
|
||||
data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
|
||||
if (data) {
|
||||
struct nvbios_init init = {
|
||||
.subdev = nv_subdev(priv),
|
||||
.bios = bios,
|
||||
.offset = data,
|
||||
.outp = outp,
|
||||
.crtc = head,
|
||||
.execute = 1,
|
||||
};
|
||||
|
||||
if (nvbios_exec(&init))
|
||||
return 0x0000;
|
||||
return conf;
|
||||
}
|
||||
}
|
||||
|
||||
return 0x0000;
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_disp_intr_unk10(struct nv50_disp_priv *priv, u32 super)
|
||||
{
|
||||
int head = ffs((super & 0x00000060) >> 5) - 1;
|
||||
if (head >= 0) {
|
||||
head = ffs((super & 0x00000180) >> 7) - 1;
|
||||
if (head >= 0)
|
||||
exec_script(priv, head, 1);
|
||||
}
|
||||
|
||||
nv_wr32(priv, 0x610024, 0x00000010);
|
||||
nv_wr32(priv, 0x610030, 0x80000000);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_disp_intr_unk20_dp(struct nv50_disp_priv *priv,
|
||||
struct dcb_output *outp, u32 pclk)
|
||||
{
|
||||
const int link = !(outp->sorconf.link & 1);
|
||||
const int or = ffs(outp->or) - 1;
|
||||
const u32 soff = ( or * 0x800);
|
||||
const u32 loff = (link * 0x080) + soff;
|
||||
const u32 ctrl = nv_rd32(priv, 0x610794 + (or * 8));
|
||||
const u32 bits = ((ctrl & 0x000f0000) == 0x00020000) ? 18 : 24;
|
||||
const u32 symbol = 100000;
|
||||
u32 dpctrl = nv_rd32(priv, 0x61c10c + loff) & 0x0000f0000;
|
||||
u32 clksor = nv_rd32(priv, 0x614300 + soff);
|
||||
int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
|
||||
int TU, VTUi, VTUf, VTUa;
|
||||
u64 link_data_rate, link_ratio, unk;
|
||||
u32 best_diff = 64 * symbol;
|
||||
u32 link_nr, link_bw, r;
|
||||
|
||||
/* calculate packed data rate for each lane */
|
||||
if (dpctrl > 0x00030000) link_nr = 4;
|
||||
else if (dpctrl > 0x00010000) link_nr = 2;
|
||||
else link_nr = 1;
|
||||
|
||||
if (clksor & 0x000c0000)
|
||||
link_bw = 270000;
|
||||
else
|
||||
link_bw = 162000;
|
||||
|
||||
link_data_rate = (pclk * bits / 8) / link_nr;
|
||||
|
||||
/* calculate ratio of packed data rate to link symbol rate */
|
||||
link_ratio = link_data_rate * symbol;
|
||||
r = do_div(link_ratio, link_bw);
|
||||
|
||||
for (TU = 64; TU >= 32; TU--) {
|
||||
/* calculate average number of valid symbols in each TU */
|
||||
u32 tu_valid = link_ratio * TU;
|
||||
u32 calc, diff;
|
||||
|
||||
/* find a hw representation for the fraction.. */
|
||||
VTUi = tu_valid / symbol;
|
||||
calc = VTUi * symbol;
|
||||
diff = tu_valid - calc;
|
||||
if (diff) {
|
||||
if (diff >= (symbol / 2)) {
|
||||
VTUf = symbol / (symbol - diff);
|
||||
if (symbol - (VTUf * diff))
|
||||
VTUf++;
|
||||
|
||||
if (VTUf <= 15) {
|
||||
VTUa = 1;
|
||||
calc += symbol - (symbol / VTUf);
|
||||
} else {
|
||||
VTUa = 0;
|
||||
VTUf = 1;
|
||||
calc += symbol;
|
||||
}
|
||||
} else {
|
||||
VTUa = 0;
|
||||
VTUf = min((int)(symbol / diff), 15);
|
||||
calc += symbol / VTUf;
|
||||
}
|
||||
|
||||
diff = calc - tu_valid;
|
||||
} else {
|
||||
/* no remainder, but the hw doesn't like the fractional
|
||||
* part to be zero. decrement the integer part and
|
||||
* have the fraction add a whole symbol back
|
||||
*/
|
||||
VTUa = 0;
|
||||
VTUf = 1;
|
||||
VTUi--;
|
||||
}
|
||||
|
||||
if (diff < best_diff) {
|
||||
best_diff = diff;
|
||||
bestTU = TU;
|
||||
bestVTUa = VTUa;
|
||||
bestVTUf = VTUf;
|
||||
bestVTUi = VTUi;
|
||||
if (diff == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bestTU) {
|
||||
nv_error(priv, "unable to find suitable dp config\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* XXX close to vbios numbers, but not right */
|
||||
unk = (symbol - link_ratio) * bestTU;
|
||||
unk *= link_ratio;
|
||||
r = do_div(unk, symbol);
|
||||
r = do_div(unk, symbol);
|
||||
unk += 6;
|
||||
|
||||
nv_mask(priv, 0x61c10c + loff, 0x000001fc, bestTU << 2);
|
||||
nv_mask(priv, 0x61c128 + loff, 0x010f7f3f, bestVTUa << 24 |
|
||||
bestVTUf << 16 |
|
||||
bestVTUi << 8 | unk);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_disp_intr_unk20(struct nv50_disp_priv *priv, u32 super)
|
||||
{
|
||||
struct dcb_output outp;
|
||||
u32 addr, mask, data;
|
||||
int head;
|
||||
|
||||
/* finish detaching encoder? */
|
||||
head = ffs((super & 0x00000180) >> 7) - 1;
|
||||
if (head >= 0)
|
||||
exec_script(priv, head, 2);
|
||||
|
||||
/* check whether a vpll change is required */
|
||||
head = ffs((super & 0x00000600) >> 9) - 1;
|
||||
if (head >= 0) {
|
||||
u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
|
||||
if (pclk) {
|
||||
struct nouveau_clock *clk = nouveau_clock(priv);
|
||||
clk->pll_set(clk, PLL_VPLL0 + head, pclk);
|
||||
}
|
||||
|
||||
nv_mask(priv, 0x614200 + head * 0x800, 0x0000000f, 0x00000000);
|
||||
}
|
||||
|
||||
/* (re)attach the relevant OR to the head */
|
||||
head = ffs((super & 0x00000180) >> 7) - 1;
|
||||
if (head >= 0) {
|
||||
u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
|
||||
u32 conf = exec_clkcmp(priv, head, 0, pclk, &outp);
|
||||
if (conf) {
|
||||
if (outp.type == DCB_OUTPUT_ANALOG) {
|
||||
addr = 0x614280 + (ffs(outp.or) - 1) * 0x800;
|
||||
mask = 0xffffffff;
|
||||
data = 0x00000000;
|
||||
} else {
|
||||
if (outp.type == DCB_OUTPUT_DP)
|
||||
nv50_disp_intr_unk20_dp(priv, &outp, pclk);
|
||||
addr = 0x614300 + (ffs(outp.or) - 1) * 0x800;
|
||||
mask = 0x00000707;
|
||||
data = (conf & 0x0100) ? 0x0101 : 0x0000;
|
||||
}
|
||||
|
||||
nv_mask(priv, addr, mask, data);
|
||||
}
|
||||
}
|
||||
|
||||
nv_wr32(priv, 0x610024, 0x00000020);
|
||||
nv_wr32(priv, 0x610030, 0x80000000);
|
||||
}
|
||||
|
||||
/* If programming a TMDS output on a SOR that can also be configured for
|
||||
* DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off.
|
||||
*
|
||||
* It looks like the VBIOS TMDS scripts make an attempt at this, however,
|
||||
* the VBIOS scripts on at least one board I have only switch it off on
|
||||
* link 0, causing a blank display if the output has previously been
|
||||
* programmed for DisplayPort.
|
||||
*/
|
||||
static void
|
||||
nv50_disp_intr_unk40_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp)
|
||||
{
|
||||
struct nouveau_bios *bios = nouveau_bios(priv);
|
||||
const int link = !(outp->sorconf.link & 1);
|
||||
const int or = ffs(outp->or) - 1;
|
||||
const u32 loff = (or * 0x800) + (link * 0x80);
|
||||
const u16 mask = (outp->sorconf.link << 6) | outp->or;
|
||||
u8 ver, hdr;
|
||||
|
||||
if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, outp))
|
||||
nv_mask(priv, 0x61c10c + loff, 0x00000001, 0x00000000);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_disp_intr_unk40(struct nv50_disp_priv *priv, u32 super)
|
||||
{
|
||||
int head = ffs((super & 0x00000180) >> 7) - 1;
|
||||
if (head >= 0) {
|
||||
struct dcb_output outp;
|
||||
u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff;
|
||||
if (pclk && exec_clkcmp(priv, head, 1, pclk, &outp)) {
|
||||
if (outp.type == DCB_OUTPUT_TMDS)
|
||||
nv50_disp_intr_unk40_tmds(priv, &outp);
|
||||
}
|
||||
}
|
||||
|
||||
nv_wr32(priv, 0x610024, 0x00000040);
|
||||
nv_wr32(priv, 0x610030, 0x80000000);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_disp_intr_super(struct nv50_disp_priv *priv, u32 intr1)
|
||||
{
|
||||
u32 super = nv_rd32(priv, 0x610030);
|
||||
|
||||
nv_debug(priv, "supervisor 0x%08x 0x%08x\n", intr1, super);
|
||||
|
||||
if (intr1 & 0x00000010)
|
||||
nv50_disp_intr_unk10(priv, super);
|
||||
if (intr1 & 0x00000020)
|
||||
nv50_disp_intr_unk20(priv, super);
|
||||
if (intr1 & 0x00000040)
|
||||
nv50_disp_intr_unk40(priv, super);
|
||||
}
|
||||
|
||||
void
|
||||
nv50_disp_intr(struct nouveau_subdev *subdev)
|
||||
{
|
||||
struct nv50_disp_priv *priv = (void *)subdev;
|
||||
u32 stat1 = nv_rd32(priv, 0x610024);
|
||||
u32 intr0 = nv_rd32(priv, 0x610020);
|
||||
u32 intr1 = nv_rd32(priv, 0x610024);
|
||||
|
||||
if (stat1 & 0x00000004) {
|
||||
if (intr0 & 0x001f0000) {
|
||||
nv50_disp_intr_error(priv);
|
||||
intr0 &= ~0x001f0000;
|
||||
}
|
||||
|
||||
if (intr1 & 0x00000004) {
|
||||
nv50_disp_intr_vblank(priv, 0);
|
||||
nv_wr32(priv, 0x610024, 0x00000004);
|
||||
stat1 &= ~0x00000004;
|
||||
intr1 &= ~0x00000004;
|
||||
}
|
||||
|
||||
if (stat1 & 0x00000008) {
|
||||
if (intr1 & 0x00000008) {
|
||||
nv50_disp_intr_vblank(priv, 1);
|
||||
nv_wr32(priv, 0x610024, 0x00000008);
|
||||
stat1 &= ~0x00000008;
|
||||
intr1 &= ~0x00000008;
|
||||
}
|
||||
|
||||
if (intr1 & 0x00000070) {
|
||||
nv50_disp_intr_super(priv, intr1);
|
||||
intr1 &= ~0x00000070;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
@ -60,14 +60,6 @@ nouveau_irq_handler(DRM_IRQ_ARGS)
|
|||
return IRQ_NONE;
|
||||
|
||||
nv_subdev(pmc)->intr(nv_subdev(pmc));
|
||||
|
||||
if (dev->mode_config.num_crtc &&
|
||||
device->card_type <= NV_C0 &&
|
||||
device->card_type >= NV_50) {
|
||||
if (nv_rd32(device, 0x000100) & 0x04000000)
|
||||
nv50_display_intr(dev);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,8 +40,6 @@
|
|||
|
||||
#include <subdev/timer.h>
|
||||
|
||||
static void nv50_display_bh(unsigned long);
|
||||
|
||||
static inline int
|
||||
nv50_sor_nr(struct drm_device *dev)
|
||||
{
|
||||
|
@ -312,8 +310,6 @@ nv50_display_create(struct drm_device *dev)
|
|||
}
|
||||
}
|
||||
|
||||
tasklet_init(&priv->tasklet, nv50_display_bh, (unsigned long)dev);
|
||||
|
||||
ret = nv50_evo_create(dev);
|
||||
if (ret) {
|
||||
nv50_display_destroy(dev);
|
||||
|
@ -464,455 +460,3 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
|
|||
dispc->sem.value++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u16
|
||||
nv50_display_script_select(struct drm_device *dev, struct dcb_output *dcb,
|
||||
u32 mc, int pxclk)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_connector *nv_connector = NULL;
|
||||
struct drm_encoder *encoder;
|
||||
struct nvbios *bios = &drm->vbios;
|
||||
u32 script = 0, or;
|
||||
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
|
||||
if (nv_encoder->dcb != dcb)
|
||||
continue;
|
||||
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
break;
|
||||
}
|
||||
|
||||
or = ffs(dcb->or) - 1;
|
||||
switch (dcb->type) {
|
||||
case DCB_OUTPUT_LVDS:
|
||||
script = (mc >> 8) & 0xf;
|
||||
if (bios->fp_no_ddc) {
|
||||
if (bios->fp.dual_link)
|
||||
script |= 0x0100;
|
||||
if (bios->fp.if_is_24bit)
|
||||
script |= 0x0200;
|
||||
} else {
|
||||
/* determine number of lvds links */
|
||||
if (nv_connector && nv_connector->edid &&
|
||||
nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) {
|
||||
/* http://www.spwg.org */
|
||||
if (((u8 *)nv_connector->edid)[121] == 2)
|
||||
script |= 0x0100;
|
||||
} else
|
||||
if (pxclk >= bios->fp.duallink_transition_clk) {
|
||||
script |= 0x0100;
|
||||
}
|
||||
|
||||
/* determine panel depth */
|
||||
if (script & 0x0100) {
|
||||
if (bios->fp.strapless_is_24bit & 2)
|
||||
script |= 0x0200;
|
||||
} else {
|
||||
if (bios->fp.strapless_is_24bit & 1)
|
||||
script |= 0x0200;
|
||||
}
|
||||
|
||||
if (nv_connector && nv_connector->edid &&
|
||||
(nv_connector->edid->revision >= 4) &&
|
||||
(nv_connector->edid->input & 0x70) >= 0x20)
|
||||
script |= 0x0200;
|
||||
}
|
||||
break;
|
||||
case DCB_OUTPUT_TMDS:
|
||||
script = (mc >> 8) & 0xf;
|
||||
if (pxclk >= 165000)
|
||||
script |= 0x0100;
|
||||
break;
|
||||
case DCB_OUTPUT_DP:
|
||||
script = (mc >> 8) & 0xf;
|
||||
break;
|
||||
case DCB_OUTPUT_ANALOG:
|
||||
script = 0xff;
|
||||
break;
|
||||
default:
|
||||
NV_ERROR(drm, "modeset on unsupported output type!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_display_unk10_handler(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_device *device = nouveau_dev(dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nv50_display *disp = nv50_display(dev);
|
||||
u32 unk30 = nv_rd32(device, 0x610030), mc;
|
||||
int i, crtc, or = 0, type = DCB_OUTPUT_ANY;
|
||||
|
||||
NV_DEBUG(drm, "0x610030: 0x%08x\n", unk30);
|
||||
disp->irq.dcb = NULL;
|
||||
|
||||
nv_wr32(device, 0x619494, nv_rd32(device, 0x619494) & ~8);
|
||||
|
||||
/* Determine which CRTC we're dealing with, only 1 ever will be
|
||||
* signalled at the same time with the current nouveau code.
|
||||
*/
|
||||
crtc = ffs((unk30 & 0x00000060) >> 5) - 1;
|
||||
if (crtc < 0)
|
||||
goto ack;
|
||||
|
||||
/* Nothing needs to be done for the encoder */
|
||||
crtc = ffs((unk30 & 0x00000180) >> 7) - 1;
|
||||
if (crtc < 0)
|
||||
goto ack;
|
||||
|
||||
/* Find which encoder was connected to the CRTC */
|
||||
for (i = 0; type == DCB_OUTPUT_ANY && i < 3; i++) {
|
||||
mc = nv_rd32(device, NV50_PDISPLAY_DAC_MODE_CTRL_C(i));
|
||||
NV_DEBUG(drm, "DAC-%d mc: 0x%08x\n", i, mc);
|
||||
if (!(mc & (1 << crtc)))
|
||||
continue;
|
||||
|
||||
switch ((mc & 0x00000f00) >> 8) {
|
||||
case 0: type = DCB_OUTPUT_ANALOG; break;
|
||||
case 1: type = DCB_OUTPUT_TV; break;
|
||||
default:
|
||||
NV_ERROR(drm, "invalid mc, DAC-%d: 0x%08x\n", i, mc);
|
||||
goto ack;
|
||||
}
|
||||
|
||||
or = i;
|
||||
}
|
||||
|
||||
for (i = 0; type == DCB_OUTPUT_ANY && i < nv50_sor_nr(dev); i++) {
|
||||
if (nv_device(drm->device)->chipset < 0x90 ||
|
||||
nv_device(drm->device)->chipset == 0x92 ||
|
||||
nv_device(drm->device)->chipset == 0xa0)
|
||||
mc = nv_rd32(device, NV50_PDISPLAY_SOR_MODE_CTRL_C(i));
|
||||
else
|
||||
mc = nv_rd32(device, NV90_PDISPLAY_SOR_MODE_CTRL_C(i));
|
||||
|
||||
NV_DEBUG(drm, "SOR-%d mc: 0x%08x\n", i, mc);
|
||||
if (!(mc & (1 << crtc)))
|
||||
continue;
|
||||
|
||||
switch ((mc & 0x00000f00) >> 8) {
|
||||
case 0: type = DCB_OUTPUT_LVDS; break;
|
||||
case 1: type = DCB_OUTPUT_TMDS; break;
|
||||
case 2: type = DCB_OUTPUT_TMDS; break;
|
||||
case 5: type = DCB_OUTPUT_TMDS; break;
|
||||
case 8: type = DCB_OUTPUT_DP; break;
|
||||
case 9: type = DCB_OUTPUT_DP; break;
|
||||
default:
|
||||
NV_ERROR(drm, "invalid mc, SOR-%d: 0x%08x\n", i, mc);
|
||||
goto ack;
|
||||
}
|
||||
|
||||
or = i;
|
||||
}
|
||||
|
||||
/* There was no encoder to disable */
|
||||
if (type == DCB_OUTPUT_ANY)
|
||||
goto ack;
|
||||
|
||||
/* Disable the encoder */
|
||||
for (i = 0; i < drm->vbios.dcb.entries; i++) {
|
||||
struct dcb_output *dcb = &drm->vbios.dcb.entry[i];
|
||||
|
||||
if (dcb->type == type && (dcb->or & (1 << or))) {
|
||||
nouveau_bios_run_display_table(dev, 0, -1, dcb, -1);
|
||||
disp->irq.dcb = dcb;
|
||||
goto ack;
|
||||
}
|
||||
}
|
||||
|
||||
NV_ERROR(drm, "no dcb for %d %d 0x%08x\n", or, type, mc);
|
||||
ack:
|
||||
nv_wr32(device, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK10);
|
||||
nv_wr32(device, 0x610030, 0x80000000);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_display_unk20_handler(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_device *device = nouveau_dev(dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nv50_display *disp = nv50_display(dev);
|
||||
u32 unk30 = nv_rd32(device, 0x610030), tmp, pclk, script, mc = 0;
|
||||
struct dcb_output *dcb;
|
||||
int i, crtc, or = 0, type = DCB_OUTPUT_ANY;
|
||||
|
||||
NV_DEBUG(drm, "0x610030: 0x%08x\n", unk30);
|
||||
dcb = disp->irq.dcb;
|
||||
if (dcb) {
|
||||
nouveau_bios_run_display_table(dev, 0, -2, dcb, -1);
|
||||
disp->irq.dcb = NULL;
|
||||
}
|
||||
|
||||
/* CRTC clock change requested? */
|
||||
crtc = ffs((unk30 & 0x00000600) >> 9) - 1;
|
||||
if (crtc >= 0) {
|
||||
pclk = nv_rd32(device, NV50_PDISPLAY_CRTC_P(crtc, CLOCK));
|
||||
pclk &= 0x003fffff;
|
||||
if (pclk)
|
||||
nv50_crtc_set_clock(dev, crtc, pclk);
|
||||
|
||||
tmp = nv_rd32(device, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc));
|
||||
tmp &= ~0x000000f;
|
||||
nv_wr32(device, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc), tmp);
|
||||
}
|
||||
|
||||
/* Nothing needs to be done for the encoder */
|
||||
crtc = ffs((unk30 & 0x00000180) >> 7) - 1;
|
||||
if (crtc < 0)
|
||||
goto ack;
|
||||
pclk = nv_rd32(device, NV50_PDISPLAY_CRTC_P(crtc, CLOCK)) & 0x003fffff;
|
||||
|
||||
/* Find which encoder is connected to the CRTC */
|
||||
for (i = 0; type == DCB_OUTPUT_ANY && i < 3; i++) {
|
||||
mc = nv_rd32(device, NV50_PDISPLAY_DAC_MODE_CTRL_P(i));
|
||||
NV_DEBUG(drm, "DAC-%d mc: 0x%08x\n", i, mc);
|
||||
if (!(mc & (1 << crtc)))
|
||||
continue;
|
||||
|
||||
switch ((mc & 0x00000f00) >> 8) {
|
||||
case 0: type = DCB_OUTPUT_ANALOG; break;
|
||||
case 1: type = DCB_OUTPUT_TV; break;
|
||||
default:
|
||||
NV_ERROR(drm, "invalid mc, DAC-%d: 0x%08x\n", i, mc);
|
||||
goto ack;
|
||||
}
|
||||
|
||||
or = i;
|
||||
}
|
||||
|
||||
for (i = 0; type == DCB_OUTPUT_ANY && i < nv50_sor_nr(dev); i++) {
|
||||
if (nv_device(drm->device)->chipset < 0x90 ||
|
||||
nv_device(drm->device)->chipset == 0x92 ||
|
||||
nv_device(drm->device)->chipset == 0xa0)
|
||||
mc = nv_rd32(device, NV50_PDISPLAY_SOR_MODE_CTRL_P(i));
|
||||
else
|
||||
mc = nv_rd32(device, NV90_PDISPLAY_SOR_MODE_CTRL_P(i));
|
||||
|
||||
NV_DEBUG(drm, "SOR-%d mc: 0x%08x\n", i, mc);
|
||||
if (!(mc & (1 << crtc)))
|
||||
continue;
|
||||
|
||||
switch ((mc & 0x00000f00) >> 8) {
|
||||
case 0: type = DCB_OUTPUT_LVDS; break;
|
||||
case 1: type = DCB_OUTPUT_TMDS; break;
|
||||
case 2: type = DCB_OUTPUT_TMDS; break;
|
||||
case 5: type = DCB_OUTPUT_TMDS; break;
|
||||
case 8: type = DCB_OUTPUT_DP; break;
|
||||
case 9: type = DCB_OUTPUT_DP; break;
|
||||
default:
|
||||
NV_ERROR(drm, "invalid mc, SOR-%d: 0x%08x\n", i, mc);
|
||||
goto ack;
|
||||
}
|
||||
|
||||
or = i;
|
||||
}
|
||||
|
||||
if (type == DCB_OUTPUT_ANY)
|
||||
goto ack;
|
||||
|
||||
/* Enable the encoder */
|
||||
for (i = 0; i < drm->vbios.dcb.entries; i++) {
|
||||
dcb = &drm->vbios.dcb.entry[i];
|
||||
if (dcb->type == type && (dcb->or & (1 << or)))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == drm->vbios.dcb.entries) {
|
||||
NV_ERROR(drm, "no dcb for %d %d 0x%08x\n", or, type, mc);
|
||||
goto ack;
|
||||
}
|
||||
|
||||
script = nv50_display_script_select(dev, dcb, mc, pclk);
|
||||
nouveau_bios_run_display_table(dev, script, pclk, dcb, -1);
|
||||
|
||||
if (type == DCB_OUTPUT_DP) {
|
||||
int link = !(dcb->dpconf.sor.link & 1);
|
||||
if ((mc & 0x000f0000) == 0x00020000)
|
||||
nv50_sor_dp_calc_tu(dev, or, link, pclk, 18);
|
||||
else
|
||||
nv50_sor_dp_calc_tu(dev, or, link, pclk, 24);
|
||||
}
|
||||
|
||||
if (dcb->type != DCB_OUTPUT_ANALOG) {
|
||||
tmp = nv_rd32(device, NV50_PDISPLAY_SOR_CLK_CTRL2(or));
|
||||
tmp &= ~0x00000f0f;
|
||||
if (script & 0x0100)
|
||||
tmp |= 0x00000101;
|
||||
nv_wr32(device, NV50_PDISPLAY_SOR_CLK_CTRL2(or), tmp);
|
||||
} else {
|
||||
nv_wr32(device, NV50_PDISPLAY_DAC_CLK_CTRL2(or), 0);
|
||||
}
|
||||
|
||||
disp->irq.dcb = dcb;
|
||||
disp->irq.pclk = pclk;
|
||||
disp->irq.script = script;
|
||||
|
||||
ack:
|
||||
nv_wr32(device, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK20);
|
||||
nv_wr32(device, 0x610030, 0x80000000);
|
||||
}
|
||||
|
||||
/* If programming a TMDS output on a SOR that can also be configured for
|
||||
* DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off.
|
||||
*
|
||||
* It looks like the VBIOS TMDS scripts make an attempt at this, however,
|
||||
* the VBIOS scripts on at least one board I have only switch it off on
|
||||
* link 0, causing a blank display if the output has previously been
|
||||
* programmed for DisplayPort.
|
||||
*/
|
||||
static void
|
||||
nv50_display_unk40_dp_set_tmds(struct drm_device *dev, struct dcb_output *dcb)
|
||||
{
|
||||
struct nouveau_device *device = nouveau_dev(dev);
|
||||
int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1);
|
||||
struct drm_encoder *encoder;
|
||||
u32 tmp;
|
||||
|
||||
if (dcb->type != DCB_OUTPUT_TMDS)
|
||||
return;
|
||||
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
|
||||
if (nv_encoder->dcb->type == DCB_OUTPUT_DP &&
|
||||
nv_encoder->dcb->or & (1 << or)) {
|
||||
tmp = nv_rd32(device, NV50_SOR_DP_CTRL(or, link));
|
||||
tmp &= ~NV50_SOR_DP_CTRL_ENABLED;
|
||||
nv_wr32(device, NV50_SOR_DP_CTRL(or, link), tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_display_unk40_handler(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_device *device = nouveau_dev(dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nv50_display *disp = nv50_display(dev);
|
||||
struct dcb_output *dcb = disp->irq.dcb;
|
||||
u16 script = disp->irq.script;
|
||||
u32 unk30 = nv_rd32(device, 0x610030), pclk = disp->irq.pclk;
|
||||
|
||||
NV_DEBUG(drm, "0x610030: 0x%08x\n", unk30);
|
||||
disp->irq.dcb = NULL;
|
||||
if (!dcb)
|
||||
goto ack;
|
||||
|
||||
nouveau_bios_run_display_table(dev, script, -pclk, dcb, -1);
|
||||
nv50_display_unk40_dp_set_tmds(dev, dcb);
|
||||
|
||||
ack:
|
||||
nv_wr32(device, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK40);
|
||||
nv_wr32(device, 0x610030, 0x80000000);
|
||||
nv_wr32(device, 0x619494, nv_rd32(device, 0x619494) | 8);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_display_bh(unsigned long data)
|
||||
{
|
||||
struct drm_device *dev = (struct drm_device *)data;
|
||||
struct nouveau_device *device = nouveau_dev(dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
|
||||
for (;;) {
|
||||
uint32_t intr0 = nv_rd32(device, NV50_PDISPLAY_INTR_0);
|
||||
uint32_t intr1 = nv_rd32(device, NV50_PDISPLAY_INTR_1);
|
||||
|
||||
NV_DEBUG(drm, "PDISPLAY_INTR_BH 0x%08x 0x%08x\n", intr0, intr1);
|
||||
|
||||
if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK10)
|
||||
nv50_display_unk10_handler(dev);
|
||||
else
|
||||
if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK20)
|
||||
nv50_display_unk20_handler(dev);
|
||||
else
|
||||
if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK40)
|
||||
nv50_display_unk40_handler(dev);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
nv_wr32(device, NV03_PMC_INTR_EN_0, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_display_error_handler(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_device *device = nouveau_dev(dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
u32 channels = (nv_rd32(device, NV50_PDISPLAY_INTR_0) & 0x001f0000) >> 16;
|
||||
u32 addr, data;
|
||||
int chid;
|
||||
|
||||
for (chid = 0; chid < 5; chid++) {
|
||||
if (!(channels & (1 << chid)))
|
||||
continue;
|
||||
|
||||
nv_wr32(device, NV50_PDISPLAY_INTR_0, 0x00010000 << chid);
|
||||
addr = nv_rd32(device, NV50_PDISPLAY_TRAPPED_ADDR(chid));
|
||||
data = nv_rd32(device, NV50_PDISPLAY_TRAPPED_DATA(chid));
|
||||
NV_ERROR(drm, "EvoCh %d Mthd 0x%04x Data 0x%08x "
|
||||
"(0x%04x 0x%02x)\n", chid,
|
||||
addr & 0xffc, data, addr >> 16, (addr >> 12) & 0xf);
|
||||
|
||||
nv_wr32(device, NV50_PDISPLAY_TRAPPED_ADDR(chid), 0x90000000);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nv50_display_intr(struct drm_device *dev)
|
||||
{
|
||||
struct nouveau_device *device = nouveau_dev(dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nv50_display *disp = nv50_display(dev);
|
||||
uint32_t delayed = 0;
|
||||
|
||||
while (nv_rd32(device, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) {
|
||||
uint32_t intr0 = nv_rd32(device, NV50_PDISPLAY_INTR_0);
|
||||
uint32_t intr1 = nv_rd32(device, NV50_PDISPLAY_INTR_1);
|
||||
uint32_t clock;
|
||||
|
||||
NV_DEBUG(drm, "PDISPLAY_INTR 0x%08x 0x%08x\n", intr0, intr1);
|
||||
|
||||
if (!intr0 && !(intr1 & ~delayed))
|
||||
break;
|
||||
|
||||
if (intr0 & 0x001f0000) {
|
||||
nv50_display_error_handler(dev);
|
||||
intr0 &= ~0x001f0000;
|
||||
}
|
||||
|
||||
if (intr1 & NV50_PDISPLAY_INTR_1_VBLANK_CRTC) {
|
||||
intr1 &= ~NV50_PDISPLAY_INTR_1_VBLANK_CRTC;
|
||||
delayed |= NV50_PDISPLAY_INTR_1_VBLANK_CRTC;
|
||||
}
|
||||
|
||||
clock = (intr1 & (NV50_PDISPLAY_INTR_1_CLK_UNK10 |
|
||||
NV50_PDISPLAY_INTR_1_CLK_UNK20 |
|
||||
NV50_PDISPLAY_INTR_1_CLK_UNK40));
|
||||
if (clock) {
|
||||
nv_wr32(device, NV03_PMC_INTR_EN_0, 0);
|
||||
tasklet_schedule(&disp->tasklet);
|
||||
delayed |= clock;
|
||||
intr1 &= ~clock;
|
||||
}
|
||||
|
||||
if (intr0) {
|
||||
NV_ERROR(drm, "unknown PDISPLAY_INTR_0: 0x%08x\n", intr0);
|
||||
nv_wr32(device, NV50_PDISPLAY_INTR_0, intr0);
|
||||
}
|
||||
|
||||
if (intr1) {
|
||||
NV_ERROR(drm,
|
||||
"unknown PDISPLAY_INTR_1: 0x%08x\n", intr1);
|
||||
nv_wr32(device, NV50_PDISPLAY_INTR_1, intr1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,6 @@ int nv50_display_create(struct drm_device *dev);
|
|||
int nv50_display_init(struct drm_device *dev);
|
||||
void nv50_display_fini(struct drm_device *dev);
|
||||
void nv50_display_destroy(struct drm_device *dev);
|
||||
void nv50_display_intr(struct drm_device *);
|
||||
int nv50_crtc_blank(struct nouveau_crtc *, bool blank);
|
||||
int nv50_crtc_set_clock(struct drm_device *, int head, int pclk);
|
||||
|
||||
|
|
|
@ -73,111 +73,6 @@ nv50_sor_dp_link_set(struct drm_device *dev, struct dcb_output *dcb, int crtc,
|
|||
nv_call(disp->core, NV94_DISP_SOR_DP_LNKCTL + moff, data);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_sor_dp_link_get(struct drm_device *dev, u32 or, u32 link, u32 *nr, u32 *bw)
|
||||
{
|
||||
struct nouveau_device *device = nouveau_dev(dev);
|
||||
u32 dpctrl = nv_rd32(device, NV50_SOR_DP_CTRL(or, link)) & 0x000f0000;
|
||||
u32 clksor = nv_rd32(device, 0x614300 + (or * 0x800));
|
||||
if (clksor & 0x000c0000)
|
||||
*bw = 270000;
|
||||
else
|
||||
*bw = 162000;
|
||||
|
||||
if (dpctrl > 0x00030000) *nr = 4;
|
||||
else if (dpctrl > 0x00010000) *nr = 2;
|
||||
else *nr = 1;
|
||||
}
|
||||
|
||||
void
|
||||
nv50_sor_dp_calc_tu(struct drm_device *dev, int or, int link, u32 clk, u32 bpp)
|
||||
{
|
||||
struct nouveau_device *device = nouveau_dev(dev);
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
const u32 symbol = 100000;
|
||||
int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
|
||||
int TU, VTUi, VTUf, VTUa;
|
||||
u64 link_data_rate, link_ratio, unk;
|
||||
u32 best_diff = 64 * symbol;
|
||||
u32 link_nr, link_bw, r;
|
||||
|
||||
/* calculate packed data rate for each lane */
|
||||
nv50_sor_dp_link_get(dev, or, link, &link_nr, &link_bw);
|
||||
link_data_rate = (clk * bpp / 8) / link_nr;
|
||||
|
||||
/* calculate ratio of packed data rate to link symbol rate */
|
||||
link_ratio = link_data_rate * symbol;
|
||||
r = do_div(link_ratio, link_bw);
|
||||
|
||||
for (TU = 64; TU >= 32; TU--) {
|
||||
/* calculate average number of valid symbols in each TU */
|
||||
u32 tu_valid = link_ratio * TU;
|
||||
u32 calc, diff;
|
||||
|
||||
/* find a hw representation for the fraction.. */
|
||||
VTUi = tu_valid / symbol;
|
||||
calc = VTUi * symbol;
|
||||
diff = tu_valid - calc;
|
||||
if (diff) {
|
||||
if (diff >= (symbol / 2)) {
|
||||
VTUf = symbol / (symbol - diff);
|
||||
if (symbol - (VTUf * diff))
|
||||
VTUf++;
|
||||
|
||||
if (VTUf <= 15) {
|
||||
VTUa = 1;
|
||||
calc += symbol - (symbol / VTUf);
|
||||
} else {
|
||||
VTUa = 0;
|
||||
VTUf = 1;
|
||||
calc += symbol;
|
||||
}
|
||||
} else {
|
||||
VTUa = 0;
|
||||
VTUf = min((int)(symbol / diff), 15);
|
||||
calc += symbol / VTUf;
|
||||
}
|
||||
|
||||
diff = calc - tu_valid;
|
||||
} else {
|
||||
/* no remainder, but the hw doesn't like the fractional
|
||||
* part to be zero. decrement the integer part and
|
||||
* have the fraction add a whole symbol back
|
||||
*/
|
||||
VTUa = 0;
|
||||
VTUf = 1;
|
||||
VTUi--;
|
||||
}
|
||||
|
||||
if (diff < best_diff) {
|
||||
best_diff = diff;
|
||||
bestTU = TU;
|
||||
bestVTUa = VTUa;
|
||||
bestVTUf = VTUf;
|
||||
bestVTUi = VTUi;
|
||||
if (diff == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bestTU) {
|
||||
NV_ERROR(drm, "DP: unable to find suitable config\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* XXX close to vbios numbers, but not right */
|
||||
unk = (symbol - link_ratio) * bestTU;
|
||||
unk *= link_ratio;
|
||||
r = do_div(unk, symbol);
|
||||
r = do_div(unk, symbol);
|
||||
unk += 6;
|
||||
|
||||
nv_mask(device, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2);
|
||||
nv_mask(device, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 |
|
||||
bestVTUf << 16 |
|
||||
bestVTUi << 8 |
|
||||
unk);
|
||||
}
|
||||
static void
|
||||
nv50_sor_disconnect(struct drm_encoder *encoder)
|
||||
{
|
||||
|
@ -312,7 +207,9 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
|
|||
struct nouveau_drm *drm = nouveau_drm(encoder->dev);
|
||||
struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
|
||||
struct nouveau_connector *nv_connector;
|
||||
uint32_t mode_ctl = 0;
|
||||
struct nv50_display *disp = nv50_display(encoder->dev);
|
||||
struct nvbios *bios = &drm->vbios;
|
||||
uint32_t mode_ctl = 0, script;
|
||||
int ret;
|
||||
|
||||
NV_DEBUG(drm, "or %d type %d -> crtc %d\n",
|
||||
|
@ -331,6 +228,43 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
|
|||
|
||||
nouveau_hdmi_mode_set(encoder, mode);
|
||||
break;
|
||||
case DCB_OUTPUT_LVDS:
|
||||
script = 0x0000;
|
||||
if (bios->fp_no_ddc) {
|
||||
if (bios->fp.dual_link)
|
||||
script |= 0x0100;
|
||||
if (bios->fp.if_is_24bit)
|
||||
script |= 0x0200;
|
||||
} else {
|
||||
/* determine number of lvds links */
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
if (nv_connector && nv_connector->edid &&
|
||||
nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) {
|
||||
/* http://www.spwg.org */
|
||||
if (((u8 *)nv_connector->edid)[121] == 2)
|
||||
script |= 0x0100;
|
||||
} else
|
||||
if (mode->clock >= bios->fp.duallink_transition_clk) {
|
||||
script |= 0x0100;
|
||||
}
|
||||
|
||||
/* determine panel depth */
|
||||
if (script & 0x0100) {
|
||||
if (bios->fp.strapless_is_24bit & 2)
|
||||
script |= 0x0200;
|
||||
} else {
|
||||
if (bios->fp.strapless_is_24bit & 1)
|
||||
script |= 0x0200;
|
||||
}
|
||||
|
||||
if (nv_connector && nv_connector->edid &&
|
||||
(nv_connector->edid->revision >= 4) &&
|
||||
(nv_connector->edid->input & 0x70) >= 0x20)
|
||||
script |= 0x0200;
|
||||
}
|
||||
|
||||
nv_call(disp->core, NV50_DISP_SOR_LVDS_SCRIPT + nv_encoder->or, script);
|
||||
break;
|
||||
case DCB_OUTPUT_DP:
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
if (nv_connector && nv_connector->base.display_info.bpc == 6) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче