Signed-off-by: Francisco Jerez <[email protected]>
---
drivers/gpu/drm/nouveau/Makefile | 2 +-
drivers/gpu/drm/nouveau/nouveau_bios.c | 36 ++++-
drivers/gpu/drm/nouveau/nouveau_drv.h | 14 ++
drivers/gpu/drm/nouveau/nouveau_encoder.h | 7 +-
drivers/gpu/drm/nouveau/nouveau_hw.c | 29 +++
drivers/gpu/drm/nouveau/nouveau_i2c.c | 6 +-
drivers/gpu/drm/nouveau/nouveau_i2c.h | 1 +
drivers/gpu/drm/nouveau/nv04_crtc.c | 59 +++++--
drivers/gpu/drm/nouveau/nv04_display.c | 29 +++-
drivers/gpu/drm/nouveau/nv04_output.c | 192 ++++++++++++++--------
drivers/gpu/drm/nouveau/nv04_tv.c | 264 +++++++++++++++++++++++++++++
drivers/gpu/drm/nouveau/nv50_connector.c | 8 +-
drivers/gpu/drm/nouveau/nv50_crtc.c | 2 +-
drivers/gpu/drm/nouveau/nv50_dac.c | 18 +-
drivers/gpu/drm/nouveau/nv50_sor.c | 18 +-
drivers/gpu/drm/nouveau/nvreg.h | 21 ++-
16 files changed, 585 insertions(+), 121 deletions(-)
create mode 100644 drivers/gpu/drm/nouveau/nv04_tv.c
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index 67a9582..8f32853 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -19,7 +19,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_fifo.o
nouveau_mem.o \
nv50_crtc.o nv50_dac.o nv50_sor.o nv50_connector.o \
nv50_cursor.o nv50_display.o nv50_fbcon.o \
nv04_display.o nv04_output.o nv04_crtc.o nv04_cursor.o \
- nv04_fbcon.o
+ nv04_fbcon.o nv04_tv.o
nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c
b/drivers/gpu/drm/nouveau/nouveau_bios.c
index 5914560..6e03c84 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -33,6 +33,7 @@
#define FEATURE_MOBILE 0x10 /* also FEATURE_QUADRO for BMP */
#define LEGACY_I2C_CRT 0x80
#define LEGACY_I2C_PANEL 0x81
+#define LEGACY_I2C_TV 0x82
#define EDID1_LEN 128
@@ -4518,6 +4519,16 @@ static void fabricate_dvi_i_output(struct parsed_dcb
*dcb, bool twoHeads)
#endif
}
+static void fabricate_tv_output(struct parsed_dcb *dcb, bool twoHeads)
+{
+ struct dcb_entry *entry = new_dcb_entry(dcb);
+
+ entry->type = 1;
+ entry->i2c_index = LEGACY_I2C_TV;
+ entry->heads = twoHeads ? 3 : 1;
+ entry->location = !DCB_LOC_ON_CHIP; /* ie OFF CHIP */
+}
+
static bool
parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
uint32_t conn, uint32_t conf, struct dcb_entry *entry)
@@ -4737,6 +4748,11 @@ static int parse_dcb_table(struct drm_device *dev,
struct nvbios *bios, bool two
"assuming a CRT output exists\n");
/* this situation likely means a really old card, pre DCB */
fabricate_vga_output(dcb, LEGACY_I2C_CRT, 1);
+
+ if (nv04_tv_identify(dev,
+ bios->legacy.i2c_indices.tv) >= 0)
+ fabricate_tv_output(dcb, twoHeads);
+
return 0;
}
@@ -4797,9 +4813,19 @@ static int parse_dcb_table(struct drm_device *dev,
struct nvbios *bios, bool two
NV_TRACEWARN(dev, "No useful information in BIOS output table; "
"adding all possible outputs\n");
fabricate_vga_output(dcb, LEGACY_I2C_CRT, 1);
- if (bios->tmds.output0_script_ptr ||
- bios->tmds.output1_script_ptr)
+
+ /* Attempt to detect TV before DVI because the test
+ * for the former is more accurate and it rules the
+ * latter out.
+ */
+ if (nv04_tv_identify(dev,
+ bios->legacy.i2c_indices.tv) >= 0)
+ fabricate_tv_output(dcb, twoHeads);
+
+ else if (bios->tmds.output0_script_ptr ||
+ bios->tmds.output1_script_ptr)
fabricate_dvi_i_output(dcb, twoHeads);
+
return 0;
}
@@ -4853,6 +4879,8 @@ static void fixup_legacy_i2c(struct nvbios *bios)
dcb->entry[i].i2c_index = bios->legacy.i2c_indices.crt;
if (dcb->entry[i].i2c_index == LEGACY_I2C_PANEL)
dcb->entry[i].i2c_index =
bios->legacy.i2c_indices.panel;
+ if (dcb->entry[i].i2c_index == LEGACY_I2C_TV)
+ dcb->entry[i].i2c_index = bios->legacy.i2c_indices.tv;
}
}
@@ -5032,6 +5060,8 @@ nouveau_bios_init(struct drm_device *dev)
uint32_t saved_nv_pextdev_boot_0;
int ret;
+ dev_priv->vbios = &bios->pub;
+
if (!NVInitVBIOS(dev))
return -ENODEV;
@@ -5067,8 +5097,6 @@ nouveau_bios_init(struct drm_device *dev)
bios_wr32(dev, NV_PEXTDEV_BOOT_0, saved_nv_pextdev_boot_0);
- dev_priv->vbios = &bios->pub;
-
ret = nouveau_run_vbios_init(dev);
if (ret) {
dev_priv->vbios = NULL;
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h
b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 5477dc0..837e515 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -375,6 +375,14 @@ struct nv04_crtc_reg {
uint32_t ramdac_gen_ctrl;
uint32_t ramdac_630;
uint32_t ramdac_634;
+ uint32_t tv_setup;
+ uint32_t tv_vtotal;
+ uint32_t tv_vskew;
+ uint32_t tv_vsync_delay;
+ uint32_t tv_htotal;
+ uint32_t tv_hskew;
+ uint32_t tv_hsync_delay;
+ uint32_t tv_hsync_delay2;
uint32_t fp_horiz_regs[7];
uint32_t fp_vert_regs[7];
uint32_t dither;
@@ -909,6 +917,8 @@ extern void nv04_display_restore(struct drm_device *);
extern int nv04_encoder_create(struct drm_device *, struct dcb_entry *);
extern int nv04_connector_create(struct drm_device *, int i2c_index,
uint16_t encoders, int type);
+extern void nv04_encoder_commit(struct drm_encoder *encoder);
+extern void nv04_fp_encoder_unbind(struct drm_device *dev, int head);
/* nv04_crtc.c */
extern int nv04_crtc_create(struct drm_device *, int index);
@@ -969,6 +979,10 @@ extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *,
void *,
extern int nouveau_gem_ioctl_info(struct drm_device *, void *,
struct drm_file *);
+/* nv04_tv.c */
+extern int nv04_tv_identify(struct drm_device *dev, int i2c_index);
+extern int nv04_tv_encoder_create(struct drm_device *dev, struct dcb_entry
*entry);
+
#ifndef ioread32_native
#ifdef __BIG_ENDIAN
#define ioread32_native ioread32be
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h
b/drivers/gpu/drm/nouveau/nouveau_encoder.h
index 934a672..5936a07 100644
--- a/drivers/gpu/drm/nouveau/nouveau_encoder.h
+++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h
@@ -27,12 +27,13 @@
#ifndef __NOUVEAU_OUTPUT_H__
#define __NOUVEAU_OUTPUT_H__
+#include "drm_encoder_slave.h"
#include "nouveau_drv.h"
#define NV_DPMS_CLEARED 0x80
struct nouveau_encoder {
- struct drm_encoder base;
+ struct drm_encoder_slave base;
struct dcb_entry *dcb;
int or;
@@ -43,7 +44,9 @@ struct nouveau_encoder {
struct nv04_output_reg restore;
};
-#define nouveau_encoder(x) container_of((x), struct nouveau_encoder, base)
+#define nouveau_encoder(x) container_of(to_encoder_slave(x), \
+ struct nouveau_encoder, base)
+#define to_drm_encoder(x) (&(x)->base.base)
int nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry);
int nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry);
diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c
b/drivers/gpu/drm/nouveau/nouveau_hw.c
index 295b876..182243b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hw.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hw.c
@@ -87,8 +87,17 @@ NVSetOwner(struct drm_device *dev, int owner)
if (owner == 1)
owner *= 3;
+ if (dev_priv->chipset == 0x11) {
+ /* This might seem stupid, but the blob does it and
+ * omitting it often locks the system up.
+ */
+ NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX);
+ NVReadVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX);
+ }
+
/* CR44 is always changed on CRTC0 */
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_44, owner);
+
if (dev_priv->chipset == 0x11) { /* set me harder */
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner);
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner);
@@ -665,6 +674,15 @@ nv_save_state_ramdac(struct drm_device *dev, int head,
if (dev_priv->chipset >= 0x30)
regp->ramdac_634 = NVReadRAMDAC(dev, head, NV_PRAMDAC_634);
+ regp->tv_setup = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP);
+ regp->tv_vtotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL);
+ regp->tv_vskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW);
+ regp->tv_vsync_delay = NVReadRAMDAC(dev, head,
NV_PRAMDAC_TV_VSYNC_DELAY);
+ regp->tv_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL);
+ regp->tv_hskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW);
+ regp->tv_hsync_delay = NVReadRAMDAC(dev, head,
NV_PRAMDAC_TV_HSYNC_DELAY);
+ regp->tv_hsync_delay2 = NVReadRAMDAC(dev, head,
NV_PRAMDAC_TV_HSYNC_DELAY2);
+
for (i = 0; i < 7; i++) {
uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4);
regp->fp_vert_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg);
@@ -723,6 +741,15 @@ nv_load_state_ramdac(struct drm_device *dev, int head,
if (dev_priv->chipset >= 0x30)
NVWriteRAMDAC(dev, head, NV_PRAMDAC_634, regp->ramdac_634);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, regp->tv_setup);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL, regp->tv_vtotal);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW, regp->tv_vskew);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY,
regp->tv_vsync_delay);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL, regp->tv_htotal);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW, regp->tv_hskew);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY,
regp->tv_hsync_delay);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2,
regp->tv_hsync_delay2);
+
for (i = 0; i < 7; i++) {
uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4);
@@ -820,6 +847,7 @@ nv_save_state_ext(struct drm_device *dev, int head,
rd_cio_state(dev, head, regp, NV_CIO_CRE_21);
if (nv_arch(dev) >= NV_30)
rd_cio_state(dev, head, regp, NV_CIO_CRE_47);
+ rd_cio_state(dev, head, regp, NV_CIO_CRE_49);
rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
@@ -921,6 +949,7 @@ nv_load_state_ext(struct drm_device *dev, int head,
if (nv_arch(dev) >= NV_30)
wr_cio_state(dev, head, regp, NV_CIO_CRE_47);
+ wr_cio_state(dev, head, regp, NV_CIO_CRE_49);
wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c
b/drivers/gpu/drm/nouveau/nouveau_i2c.c
index ce264d6..d44d9f6 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.c
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
@@ -181,6 +181,7 @@ nouveau_i2c_new(struct drm_device *dev, const char *name,
unsigned index,
i2c->adapter.owner = THIS_MODULE;
i2c->adapter.algo_data = &i2c->algo;
i2c->dev = dev;
+ i2c->index = index;
switch (dcbi2c->port_type) {
case 0:
@@ -235,11 +236,14 @@ void
nouveau_i2c_del(struct nouveau_i2c_chan **pi2c)
{
struct nouveau_i2c_chan *i2c = *pi2c;
+ struct drm_nouveau_private *dev_priv;
if (!i2c)
return;
- *pi2c = NULL;
+ dev_priv = i2c->dev->dev_private;
+
+ dev_priv->vbios->dcb->i2c[i2c->index].chan = *pi2c = NULL;
i2c_del_adapter(&i2c->adapter);
kfree(i2c);
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h
b/drivers/gpu/drm/nouveau/nouveau_i2c.h
index 70b1a54..4690a12 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.h
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h
@@ -33,6 +33,7 @@ struct nouveau_i2c_chan {
struct drm_device *dev;
struct i2c_adapter adapter;
struct i2c_algo_bit_data algo;
+ unsigned index;
unsigned rd;
unsigned wr;
unsigned data;
diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c
b/drivers/gpu/drm/nouveau/nv04_crtc.c
index c732fbe..6f2570a 100644
--- a/drivers/gpu/drm/nouveau/nv04_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
@@ -74,6 +74,18 @@ static void nv_crtc_set_image_sharpening(struct drm_crtc
*crtc, int level)
NVWriteRAMDAC(crtc->dev, nv_crtc->index, NV_PRAMDAC_634,
regp->ramdac_634);
}
+#define PLLSEL_VPLL1_MASK \
+ (NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL \
+ | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2)
+#define PLLSEL_VPLL2_MASK \
+ (NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 \
+ | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2)
+#define PLLSEL_TV_MASK \
+ (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 \
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2)
+
/* NV4x 0x40.. pll notes:
* gpu pll: 0x4000 + 0x4004
* ?gpu? pll: 0x4008 + 0x400c
@@ -122,17 +134,16 @@ static void nv_crtc_calc_state_ext(struct drm_crtc *crtc,
struct drm_display_mod
if (!(vclk = nouveau_calc_pll_mnp(dev, &pll_lim, dot_clock, pv)))
return;
+ state->pllsel &= PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK;
+
/* The blob uses this always, so let's do the same */
if (nv_arch(dev) == NV_40)
- state->pllsel |= NV_RAMDAC_PLL_SELECT_USE_VPLL2_TRUE;
+ state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE;
/* again nv40 and some nv43 act more like nv3x as described above */
if (dev_priv->chipset < 0x41)
state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL |
NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL;
- state->pllsel |= (nv_crtc->index ?
NV_RAMDAC_PLL_SELECT_PLL_SOURCE_VPLL2 |
- NV_RAMDAC_PLL_SELECT_VCLK2_RATIO_DB2 :
-
NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL |
-
NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2);
+ state->pllsel |= nv_crtc->index ? PLLSEL_VPLL2_MASK : PLLSEL_VPLL1_MASK;
if (pv->NM2)
NV_TRACE(dev, "vpll: n1 %d n2 %d m1 %d m2 %d log2p %d\n",
@@ -441,7 +452,9 @@ nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct
drm_display_mode *mode,
* be easily turned on/off after this.
*/
static void
-nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
+nv_crtc_mode_set_regs(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = crtc->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -449,7 +462,8 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct
drm_display_mode * mode)
struct nv04_crtc_reg *regp =
&dev_priv->mode_reg.crtc_reg[nv_crtc->index];
struct nv04_crtc_reg *savep =
&dev_priv->saved_reg.crtc_reg[nv_crtc->index];
struct drm_encoder *encoder;
- bool lvds_output = false, tmds_output = false, off_chip_digital = false;
+ bool lvds_output = false, tmds_output = false,
+ off_chip_digital = false, tv_output = false;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
@@ -460,6 +474,8 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct
drm_display_mode * mode)
if (nv_encoder->dcb->type == OUTPUT_LVDS)
digital = lvds_output = true;
+ if (nv_encoder->dcb->type == OUTPUT_TV)
+ tv_output = true;
if (nv_encoder->dcb->type == OUTPUT_TMDS)
digital = tmds_output = true;
if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && digital)
@@ -550,7 +566,7 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct
drm_display_mode * mode)
regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (crtc->fb->depth + 1) / 8;
/* Enable slaved mode (called MODE_TV in nv4ref.h) */
- if (lvds_output || tmds_output)
+ if (lvds_output || tmds_output || tv_output)
regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (1 << 7);
/* Generic PRAMDAC regs */
@@ -575,6 +591,18 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct
drm_display_mode * mode)
regp->ramdac_a20 = 0x0;
regp->ramdac_a24 = 0xfffff;
regp->ramdac_a34 = 0x1;
+
+ if (tv_output) {
+ regp->tv_htotal = adjusted_mode->htotal;
+ regp->tv_hskew = 1;
+ regp->tv_hsync_delay = 1;
+ regp->tv_hsync_delay2 = 64 + adjusted_mode->hsync_start
+ - mode->hsync_start - adjusted_mode->htotal +
mode->htotal;
+
+ regp->tv_vtotal = adjusted_mode->vtotal;
+ regp->tv_vskew = 1;
+ regp->tv_vsync_delay = 1;
+ }
}
enum fp_display_regs {
@@ -774,12 +802,11 @@ nv_crtc_mode_set(struct drm_crtc *crtc, struct
drm_display_mode *mode,
/* calculated in output_prepare, nv40 needs it written before
calculating PLLs */
if (nv_arch(dev) == NV_40)
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK,
dev_priv->mode_reg.sel_clk);
- nv_crtc_mode_set_regs(crtc, mode);
+ nv_crtc_mode_set_regs(crtc, mode, adjusted_mode);
nv_crtc_mode_set_fp_regs(crtc, mode, adjusted_mode);
nv_crtc_calc_state_ext(crtc, mode, adjusted_mode->clock);
nouveau_hw_load_state(dev, nv_crtc->index, &dev_priv->mode_reg);
-
nv04_crtc_mode_set_base(crtc, x, y, NULL);
#ifdef __BIG_ENDIAN
@@ -798,15 +825,21 @@ static void nv_crtc_save(struct drm_crtc *crtc)
{
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
+ struct nv04_mode_state *state = &dev_priv->mode_reg;
+ struct nv04_mode_state *saved = &dev_priv->saved_reg;
if (nv_two_heads(crtc->dev))
NVSetOwner(crtc->dev, nv_crtc->index);
- nouveau_hw_save_state(crtc->dev, nv_crtc->index, &dev_priv->saved_reg);
+ nouveau_hw_save_state(crtc->dev, nv_crtc->index, saved);
/* init some state to saved value */
- dev_priv->mode_reg.sel_clk = dev_priv->saved_reg.sel_clk & ~(0x5 << 16);
- dev_priv->mode_reg.crtc_reg[nv_crtc->index].CRTC[NV_CIO_CRE_LCD__INDEX]
= dev_priv->saved_reg.crtc_reg[nv_crtc->index].CRTC[NV_CIO_CRE_LCD__INDEX];
+ state->sel_clk = saved->sel_clk & ~(0x5 << 16);
+ state->crtc_reg[nv_crtc->index].CRTC[NV_CIO_CRE_LCD__INDEX] =
saved->crtc_reg[nv_crtc->index].CRTC[NV_CIO_CRE_LCD__INDEX];
+ state->pllsel = saved->pllsel & ~(PLLSEL_VPLL1_MASK
+ | PLLSEL_VPLL2_MASK
+ | PLLSEL_TV_MASK);
+
}
static void nv_crtc_restore(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/nouveau/nv04_display.c
b/drivers/gpu/drm/nouveau/nv04_display.c
index 6a05a0a..db337b3 100644
--- a/drivers/gpu/drm/nouveau/nv04_display.c
+++ b/drivers/gpu/drm/nouveau/nv04_display.c
@@ -96,7 +96,7 @@ nv04_display_create(struct drm_device *dev)
struct drm_encoder *encoder;
struct drm_crtc *crtc;
uint16_t connectors[16] = { 0 };
- int i;
+ int i, ret;
NV_DEBUG(dev, "\n");
@@ -130,16 +130,25 @@ nv04_display_create(struct drm_device *dev)
for (i = 0; i < dcb->entries; i++) {
struct dcb_entry *dcbent = &dcb->entry[i];
- if (dcbent->type == OUTPUT_TV)
- continue;
if (dcbent->type > 3) {
NV_WARN(dev, "DCB type %d not known\n", dcbent->type);
continue;
}
- connectors[dcbent->i2c_index] |= 1 << i;
+ if (dcbent->type == OUTPUT_TV){
+ if(dcbent->location == DCB_LOC_ON_CHIP)
+ continue;
+ /* TODO - ret = nv17_tv_encoder_create(dev,
entry); */
+ else
+ ret = nv04_tv_encoder_create(dev, dcbent);
+ }else{
+ ret = nv04_encoder_create(dev, dcbent);
+ }
+
+ if(ret)
+ continue;
- nv04_encoder_create(dev, dcbent);
+ connectors[dcbent->i2c_index] |= 1 << i;
}
for (i = 0; i < dcb->entries; i++) {
@@ -171,13 +180,21 @@ nv04_display_create(struct drm_device *dev)
dev_priv->vbios->fp_no_ddc)
i2c_index = 0xf;
break;
+ case OUTPUT_TV:
+ type = DRM_MODE_CONNECTOR_TV;
+ break;
default:
type = DRM_MODE_CONNECTOR_Unknown;
continue;
}
+ if (i2c_index == 0xf)
+ encoders = 1 << dcbent->index; /* allow multiple
connectors with the
+ same dummy i2c index
*/
+ else
+ connectors[i2c_index] = 0; /* avoid connectors being
added multiply */
+
nv04_connector_create(dev, i2c_index, encoders, type);
- connectors[i2c_index] = 0; /* avoid connectors being added
multiply */
}
/* Save previous state */
diff --git a/drivers/gpu/drm/nouveau/nv04_output.c
b/drivers/gpu/drm/nouveau/nv04_output.c
index 790d933..eae7d2b 100644
--- a/drivers/gpu/drm/nouveau/nv04_output.c
+++ b/drivers/gpu/drm/nouveau/nv04_output.c
@@ -34,6 +34,10 @@
#include "nouveau_hw.h"
#include "nvreg.h"
+#define get_slave_funcs(nv_encoder) ((nv_encoder) ? \
+
to_encoder_slave(to_drm_encoder(nv_encoder))->slave_funcs \
+ : NULL)
+
static int
nv_output_ramdac_offset(struct nouveau_encoder *nv_encoder)
{
@@ -416,6 +420,9 @@ nv_output_detect(struct drm_connector *connector)
nv_connector->edid = (struct edid
*)nouveau_bios_embedded_edid(dev);
ret = connector_status_connected;
}
+ } else if((det_encoder = find_encoder_by_type(connector,
DRM_MODE_ENCODER_TVDAC))) {
+ ret =
get_slave_funcs(det_encoder)->detect(to_drm_encoder(det_encoder),
+ connector);
}
if (ret != connector_status_disconnected)
@@ -489,11 +496,15 @@ nv_output_get_edid_modes(struct drm_connector *connector)
ret = 1;
}
+ if (enctype == OUTPUT_TV)
+ return
get_slave_funcs(nv_encoder)->get_modes(to_drm_encoder(nv_encoder),
+ connector);
+
return ret;
}
static int
-nv_output_get_modes(struct drm_connector *connector)
+nv_output_get_lvds_modes(struct drm_connector *connector)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
@@ -503,9 +514,6 @@ nv_output_get_modes(struct drm_connector *connector)
bool dl, if_is_24bit = false;
int ret;
- if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS)
- return nv_output_get_edid_modes(connector);
-
/* panels only have one mode, and it doesn't change */
if (nv_connector->native_mode) {
drm_mode_probed_add(connector, drm_mode_duplicate(dev,
@@ -581,6 +589,10 @@ nv_output_mode_valid(struct drm_connector *connector,
return MODE_CLOCK_HIGH;
}
+ if (nv_encoder->dcb->type == OUTPUT_TV)
+ return
get_slave_funcs(nv_encoder)->mode_valid(to_drm_encoder(nv_encoder),
+ mode);
+
return MODE_OK;
}
@@ -662,8 +674,27 @@ static inline bool is_fpc_off(uint32_t fpc)
FP_TG_CONTROL_OFF);
}
+void nv04_fp_encoder_unbind(struct drm_device *dev, int head)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
+
+ if (NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL) &
+ FP_TG_CONTROL_ON) {
+ /* digital remnants must be cleaned before new crtc
+ * values programmed. delay is time for the vga stuff
+ * to realise it's in control again
+ */
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL,
+ FP_TG_CONTROL_OFF);
+ msleep(50);
+ }
+ /* don't inadvertently turn it on when state written later */
+ crtcstate[head].fp_control = FP_TG_CONTROL_OFF;
+}
+
static void
-nv_output_prepare(struct drm_encoder *encoder)
+nv04_encoder_prepare(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_encoder_helper_funcs *helper = encoder->helper_private;
@@ -678,20 +709,8 @@ nv_output_prepare(struct drm_encoder *encoder)
helper->dpms(encoder, DRM_MODE_DPMS_OFF);
- if (nv_encoder->dcb->type == OUTPUT_ANALOG) {
- if (NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL) &
- FP_TG_CONTROL_ON) {
- /* digital remnants must be cleaned before new crtc
- * values programmed. delay is time for the vga stuff
- * to realise it's in control again
- */
- NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL,
- FP_TG_CONTROL_OFF);
- msleep(50);
- }
- /* don't inadvertently turn it on when state written later */
- crtcstate[head].fp_control = FP_TG_CONTROL_OFF;
- }
+ if (nv_encoder->dcb->type == OUTPUT_ANALOG)
+ nv04_fp_encoder_unbind(dev, head);
/* calculate some output specific CRTC regs now, so that they can be
* written in nv_crtc_set_mode
@@ -705,6 +724,7 @@ nv_output_prepare(struct drm_encoder *encoder)
*/
if (!(*cr_lcd & 0x44)) {
*cr_lcd = digital_op ? 0x3 : 0x0;
+
if (digital_op && nv_two_heads(dev)) {
if (nv_encoder->dcb->location == DCB_LOC_ON_CHIP)
*cr_lcd |= head ? 0x0 : 0x8;
@@ -776,8 +796,8 @@ nv_output_mode_set(struct drm_encoder *encoder, struct
drm_display_mode *mode,
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
nv_output_ramdac_offset(nv_encoder), 0x00100000);
}
-static void
-nv_output_commit(struct drm_encoder *encoder)
+void
+nv04_encoder_commit(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
@@ -838,9 +858,11 @@ static inline bool is_powersaving_dpms(int mode)
}
static void
-lvds_encoder_dpms(struct drm_device *dev, struct nouveau_encoder *nv_encoder,
- struct drm_crtc * crtc, int mode)
+lvds_encoder_dpms(struct drm_encoder *encoder, int mode)
{
+ struct drm_device *dev = encoder->dev;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_nouveau_private *dev_priv = dev->dev_private;
bool was_powersaving = is_powersaving_dpms(nv_encoder->last_dpms);
@@ -886,9 +908,11 @@ lvds_encoder_dpms(struct drm_device *dev, struct
nouveau_encoder *nv_encoder,
}
static void
-vga_encoder_dpms(struct drm_device *dev, struct nouveau_encoder *nv_encoder,
- struct drm_crtc * crtc, int mode)
+vga_encoder_dpms(struct drm_encoder *encoder, int mode)
{
+ struct drm_device *dev = encoder->dev;
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
if (nv_encoder->last_dpms == mode)
return;
nv_encoder->last_dpms = mode;
@@ -909,9 +933,11 @@ vga_encoder_dpms(struct drm_device *dev, struct
nouveau_encoder *nv_encoder,
}
static void
-tmds_encoder_dpms(struct drm_device *dev, struct nouveau_encoder *nv_encoder,
- struct drm_crtc * crtc, int mode)
+tmds_encoder_dpms(struct drm_encoder *encoder, int mode)
{
+ struct drm_device *dev = encoder->dev;
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
if (nv_encoder->last_dpms == mode)
return;
nv_encoder->last_dpms = mode;
@@ -919,20 +945,7 @@ tmds_encoder_dpms(struct drm_device *dev, struct
nouveau_encoder *nv_encoder,
NV_INFO(dev, "Setting dpms mode %d on tmds encoder (output %d)\n",
mode, nv_encoder->dcb->index);
- dpms_update_fp_control(dev, nv_encoder, crtc, mode);
-}
-
-static void
-nv_output_dpms(struct drm_encoder *encoder, int mode)
-{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct drm_device *dev = encoder->dev;
- struct drm_crtc *crtc = encoder->crtc;
- void (* const encoder_dpms[4])(struct drm_device *, struct
nouveau_encoder *, struct drm_crtc *, int) =
- /* index matches DCB type */
- { vga_encoder_dpms, NULL, tmds_encoder_dpms, lvds_encoder_dpms
};
-
- encoder_dpms[nv_encoder->dcb->type](dev, nv_encoder, crtc, mode);
+ dpms_update_fp_control(dev, nv_encoder, encoder->crtc, mode);
}
static void
@@ -982,13 +995,35 @@ nv04_encoder_restore(struct drm_encoder *encoder)
nv_encoder->last_dpms = NV_DPMS_CLEARED;
}
-static const struct drm_encoder_helper_funcs nv04_encoder_helper_funcs = {
- .dpms = nv_output_dpms,
+static const struct drm_encoder_helper_funcs nv04_encoder_hfuncs_vga = {
+ .dpms = vga_encoder_dpms,
.save = nv04_encoder_save,
.restore = nv04_encoder_restore,
.mode_fixup = nv_output_mode_fixup,
- .prepare = nv_output_prepare,
- .commit = nv_output_commit,
+ .prepare = nv04_encoder_prepare,
+ .commit = nv04_encoder_commit,
+ .mode_set = nv_output_mode_set,
+ .detect = NULL
+};
+
+static const struct drm_encoder_helper_funcs nv04_encoder_hfuncs_tmds = {
+ .dpms = tmds_encoder_dpms,
+ .save = nv04_encoder_save,
+ .restore = nv04_encoder_restore,
+ .mode_fixup = nv_output_mode_fixup,
+ .prepare = nv04_encoder_prepare,
+ .commit = nv04_encoder_commit,
+ .mode_set = nv_output_mode_set,
+ .detect = NULL
+};
+
+static const struct drm_encoder_helper_funcs nv04_encoder_hfuncs_lvds = {
+ .dpms = lvds_encoder_dpms,
+ .save = nv04_encoder_save,
+ .restore = nv04_encoder_restore,
+ .mode_fixup = nv_output_mode_fixup,
+ .prepare = nv04_encoder_prepare,
+ .commit = nv04_encoder_commit,
.mode_set = nv_output_mode_set,
.detect = NULL
};
@@ -1011,18 +1046,23 @@ static const struct drm_encoder_funcs
nv04_encoder_funcs = {
int
nv04_encoder_create(struct drm_device *dev, struct dcb_entry *entry)
{
+ struct drm_encoder *encoder;
struct nouveau_encoder *nv_encoder = NULL;
+ const struct drm_encoder_helper_funcs *hfuncs;
int type;
switch (entry->type) {
case OUTPUT_TMDS:
type = DRM_MODE_ENCODER_TMDS;
+ hfuncs = &nv04_encoder_hfuncs_tmds;
break;
case OUTPUT_LVDS:
type = DRM_MODE_ENCODER_LVDS;
+ hfuncs = &nv04_encoder_hfuncs_lvds;
break;
case OUTPUT_ANALOG:
type = DRM_MODE_ENCODER_DAC;
+ hfuncs = &nv04_encoder_hfuncs_vga;
break;
default:
return -EINVAL;
@@ -1032,14 +1072,16 @@ nv04_encoder_create(struct drm_device *dev, struct
dcb_entry *entry)
if (!nv_encoder)
return -ENOMEM;
+ encoder = to_drm_encoder(nv_encoder);
+
nv_encoder->dcb = entry;
nv_encoder->or = ffs(entry->or) - 1;
- drm_encoder_init(dev, &nv_encoder->base, &nv04_encoder_funcs, type);
- drm_encoder_helper_add(&nv_encoder->base, &nv04_encoder_helper_funcs);
+ drm_encoder_init(dev, encoder, &nv04_encoder_funcs, type);
+ drm_encoder_helper_add(encoder, hfuncs);
- nv_encoder->base.possible_crtcs = entry->heads;
- nv_encoder->base.possible_clones = 0;
+ encoder->possible_crtcs = entry->heads;
+ encoder->possible_clones = 0;
return 0;
}
@@ -1049,11 +1091,17 @@ nv_output_best_encoder(struct drm_connector *connector)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
- return &nv_connector->detected_encoder->base;
+ return to_drm_encoder(nv_connector->detected_encoder);
}
-static const struct drm_connector_helper_funcs nv04_connector_helper_funcs = {
- .get_modes = nv_output_get_modes,
+static const struct drm_connector_helper_funcs nv04_connector_hfuncs = {
+ .get_modes = nv_output_get_edid_modes,
+ .mode_valid = nv_output_mode_valid,
+ .best_encoder = nv_output_best_encoder,
+};
+
+static const struct drm_connector_helper_funcs nv04_connector_hfuncs_lvds = {
+ .get_modes = nv_output_get_lvds_modes,
.mode_valid = nv_output_mode_valid,
.best_encoder = nv_output_best_encoder,
};
@@ -1064,11 +1112,12 @@ nv04_connector_set_property(struct drm_connector
*connector,
{
struct drm_device *dev = connector->dev;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
+ struct drm_encoder *encoder = connector->encoder;
+ struct nouveau_encoder *nv_encoder = encoder? nouveau_encoder(encoder)
: NULL;
/* Scaling mode */
if (property == dev->mode_config.scaling_mode_property) {
- struct drm_crtc *crtc = connector->encoder ?
- connector->encoder->crtc : NULL;
+ struct drm_crtc *crtc = encoder ? encoder->crtc : NULL;
int ret;
switch (value) {
@@ -1103,8 +1152,13 @@ nv04_connector_set_property(struct drm_connector
*connector,
return 0;
}
+ if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV)
+ return get_slave_funcs(nv_encoder)->set_property(encoder,
connector,
+ property,
value);
+
return -EINVAL;
}
+
static void
nv04_connector_destroy(struct drm_connector *connector)
{
@@ -1136,6 +1190,7 @@ nv04_connector_create(struct drm_device *dev, int
i2c_index, uint16_t encoders,
struct nouveau_connector *nv_connector;
struct drm_connector *connector;
struct drm_encoder *encoder;
+ const struct drm_connector_helper_funcs *hfuncs;
int ret;
nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL);
@@ -1143,19 +1198,13 @@ nv04_connector_create(struct drm_device *dev, int
i2c_index, uint16_t encoders,
return -ENOMEM;
connector = &nv_connector->base;
- switch (type) {
- case DRM_MODE_CONNECTOR_DVII:
- case DRM_MODE_CONNECTOR_DVID:
- case DRM_MODE_CONNECTOR_LVDS:
- nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;
- break;
- default:
- nv_connector->scaling_mode = DRM_MODE_SCALE_NON_GPU;
- break;
- }
+ if (type == DRM_MODE_CONNECTOR_LVDS)
+ hfuncs = &nv04_connector_hfuncs_lvds;
+ else
+ hfuncs = &nv04_connector_hfuncs;
drm_connector_init(dev, connector, &nv04_connector_funcs, type);
- drm_connector_helper_add(connector, &nv04_connector_helper_funcs);
+ drm_connector_helper_add(connector, hfuncs);
if (i2c_index < 0xf) {
ret = nouveau_i2c_new(dev, drm_get_connector_name(connector),
@@ -1172,18 +1221,25 @@ nv04_connector_create(struct drm_device *dev, int
i2c_index, uint16_t encoders,
drm_connector_attach_property(connector,
dev->mode_config.dvi_i_select_subconnector_property, 0);
}
- if (type != DRM_MODE_CONNECTOR_VGA) {
+
+ if (type == DRM_MODE_CONNECTOR_DVID ||
+ type == DRM_MODE_CONNECTOR_DVII ||
+ type == DRM_MODE_CONNECTOR_LVDS) {
+ nv_connector->scaling_mode = DRM_MODE_SCALE_NON_GPU;
+
drm_connector_attach_property(connector,
dev->mode_config.scaling_mode_property, nv_connector->scaling_mode);
+ drm_connector_attach_property(connector,
dev->mode_config.dithering_mode_property, nv_connector->use_dithering ?
DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF);
}
- drm_connector_attach_property(connector,
dev->mode_config.dithering_mode_property, nv_connector->use_dithering ?
DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF);
-
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
if (nv_encoder->dcb->i2c_index != i2c_index)
continue;
+ if (get_slave_funcs(nv_encoder))
+ get_slave_funcs(nv_encoder)->create_resources(encoder,
connector);
+
drm_mode_connector_attach_encoder(connector, encoder);
}
diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c
b/drivers/gpu/drm/nouveau/nv04_tv.c
new file mode 100644
index 0000000..9bab262
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv04_tv.c
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2009 Francisco Jerez.
+ * All Rights Reserved.
+ *
+ * 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 (including the
+ * next paragraph) 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 OWNER(S) AND/OR ITS SUPPLIERS 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 "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_encoder.h"
+#include "nouveau_crtc.h"
+#include "nouveau_hw.h"
+#include "drm_crtc_helper.h"
+
+#include <drm/i2c/ch7006.h>
+
+static struct {
+ struct i2c_board_info board_info;
+ struct drm_encoder_funcs funcs;
+ struct drm_encoder_helper_funcs hfuncs;
+ void *params;
+
+} nv04_tv_encoder_info[] = {
+ {
+ .board_info = { I2C_BOARD_INFO("ch7006", 0x75) },
+ .params = &(struct ch7006_encoder_params) {
+ CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER,
+ 0, 0, 0,
+ CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED,
+ CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC
+ },
+ },
+};
+
+static bool probe_i2c_addr(struct i2c_adapter *adapter, int addr)
+{
+ struct i2c_msg msg = {
+ .addr = addr,
+ .len = 0,
+ };
+
+ return i2c_transfer(adapter, &msg, 1) == 1;
+}
+
+int nv04_tv_identify(struct drm_device *dev, int i2c_index)
+{
+ char adaptername[11];
+ struct nouveau_i2c_chan *i2c;
+ bool was_locked;
+ int i,ret;
+
+ NV_TRACE(dev, "Probing TV encoders on I2C bus: %d\n", i2c_index);
+
+ snprintf(adaptername, 11, "DCB-I2C-%d", i2c_index);
+ if (nouveau_i2c_new(dev, adaptername, i2c_index, &i2c))
+ return -ENODEV;
+
+ was_locked = NVLockVgaCrtcs(dev, false);
+
+ for (i = 0; i < ARRAY_SIZE(nv04_tv_encoder_info); i++) {
+ if (probe_i2c_addr(&i2c->adapter,
+ nv04_tv_encoder_info[i].board_info.addr)) {
+ ret = i;
+ break;
+ }
+ }
+
+ if (i < ARRAY_SIZE(nv04_tv_encoder_info)) {
+ NV_TRACE(dev, "Detected TV encoder: %s\n",
+ nv04_tv_encoder_info[i].board_info.type);
+
+ } else {
+ NV_TRACE(dev, "No TV encoders found.\n");
+
+ nouveau_i2c_del(&i2c);
+ i = -ENODEV;
+ }
+
+ NVLockVgaCrtcs(dev, was_locked);
+ return i;
+}
+
+static void nv04_tv_encoder_bind(struct drm_device *dev, int head, bool bind)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv04_crtc_reg *state = &dev_priv->mode_reg.crtc_reg[head];
+
+ state->tv_setup = 0;
+
+ if (bind) {
+ state->CRTC[NV_CIO_CRE_LCD__INDEX] = 0;
+ state->CRTC[NV_CIO_CRE_49] |= 0x10;
+ } else {
+ state->CRTC[NV_CIO_CRE_49] &= ~0x10;
+ }
+
+ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_LCD__INDEX,
+ state->CRTC[NV_CIO_CRE_LCD__INDEX]);
+ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_49,
+ state->CRTC[NV_CIO_CRE_49]);
+ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP,
+ state->tv_setup);
+}
+
+static void nv04_tv_encoder_prepare(struct drm_encoder *encoder)
+{
+ struct drm_device *dev = encoder->dev;
+ int head = nouveau_crtc(encoder->crtc)->index;
+ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
+
+ helper->dpms(encoder, DRM_MODE_DPMS_OFF);
+
+ nv04_fp_encoder_unbind(dev, head);
+
+ if (nv_two_heads(dev))
+ nv04_tv_encoder_bind(dev, head ^ 1, false);
+
+ nv04_tv_encoder_bind(dev, head, true);
+}
+
+#define PLLSEL_TV_CRTC1_MASK \
+ (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1)
+#define PLLSEL_TV_CRTC2_MASK \
+ (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \
+ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2)
+
+static void nv04_tv_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+ struct drm_device *dev = encoder->dev;
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv04_mode_state *state = &dev_priv->mode_reg;
+ uint8_t crtc1A;
+
+ NV_INFO(dev, "Setting dpms mode %d on TV encoder (output %d)\n",
+ mode, nv_encoder->dcb->index);
+
+ state->pllsel &= ~(PLLSEL_TV_CRTC1_MASK | PLLSEL_TV_CRTC2_MASK);
+
+ if (mode == DRM_MODE_DPMS_ON) {
+ int head = nouveau_crtc(encoder->crtc)->index;
+ crtc1A = NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX);
+
+ state->pllsel |= head? PLLSEL_TV_CRTC2_MASK :
PLLSEL_TV_CRTC1_MASK;
+
+ /* Inhibit hsync */
+ crtc1A |= 0x80;
+
+ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX, crtc1A);
+ }
+
+ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel);
+
+ to_encoder_slave(encoder)->slave_funcs->dpms(encoder, mode);
+}
+
+static void nv04_tv_encoder_destroy(struct drm_encoder *encoder)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+
+ to_encoder_slave(encoder)->slave_funcs->destroy(encoder);
+
+ drm_encoder_cleanup(encoder);
+
+ kfree(nv_encoder);
+}
+
+int nv04_tv_encoder_create(struct drm_device *dev, struct dcb_entry *entry)
+{
+ struct nouveau_encoder *nv_encoder;
+ struct drm_encoder *encoder;
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct i2c_adapter *adap;
+ struct drm_encoder_funcs *funcs = NULL;
+ struct drm_encoder_helper_funcs *hfuncs = NULL;
+ struct drm_encoder_slave_funcs *sfuncs = NULL;
+ int i2c_index = entry->i2c_index;
+ int type, ret;
+ bool was_locked;
+
+ /* Ensure that we can talk to this encoder */
+ type = nv04_tv_identify(dev, i2c_index);
+ if (type < 0)
+ return type;
+
+ /* Allocate the necessary memory */
+ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+ if (!nv_encoder)
+ return -ENOMEM;
+
+ /* Initialize the common members */
+ encoder = to_drm_encoder(nv_encoder);
+
+ funcs = &nv04_tv_encoder_info[type].funcs;
+ hfuncs = &nv04_tv_encoder_info[type].hfuncs;
+
+ drm_encoder_init(dev, encoder, funcs, DRM_MODE_ENCODER_TVDAC);
+ drm_encoder_helper_add(encoder, hfuncs);
+
+ encoder->possible_crtcs = entry->heads;
+ encoder->possible_clones = 0;
+
+ nv_encoder->dcb = entry;
+ nv_encoder->or = ffs(entry->or) - 1;
+
+ /* Run the slave-specific initialization */
+ adap = &dev_priv->vbios->dcb->i2c[i2c_index].chan->adapter;
+
+ was_locked = NVLockVgaCrtcs(dev, false);
+
+ ret = drm_i2c_encoder_init(encoder->dev, to_encoder_slave(encoder),
adap,
+ &nv04_tv_encoder_info[type].board_info,
+ nv04_tv_encoder_info[type].params);
+
+ NVLockVgaCrtcs(dev, was_locked);
+
+ if (ret < 0)
+ goto fail;
+
+ /* Fill the function pointers */
+ sfuncs = to_encoder_slave(encoder)->slave_funcs;
+
+ *funcs = (struct drm_encoder_funcs) {
+ .destroy = nv04_tv_encoder_destroy,
+ };
+
+ *hfuncs = (struct drm_encoder_helper_funcs) {
+ .dpms = nv04_tv_encoder_dpms,
+ .save = sfuncs->save,
+ .restore = sfuncs->restore,
+ .mode_fixup = sfuncs->mode_fixup,
+ .prepare = nv04_tv_encoder_prepare,
+ .commit = nv04_encoder_commit,
+ .mode_set = sfuncs->mode_set,
+ };
+
+ return 0;
+
+fail:
+ drm_encoder_cleanup(encoder);
+
+ kfree(nv_encoder);
+ return ret;
+}
diff --git a/drivers/gpu/drm/nouveau/nv50_connector.c
b/drivers/gpu/drm/nouveau/nv50_connector.c
index 28bcf9f..135a50f 100644
--- a/drivers/gpu/drm/nouveau/nv50_connector.c
+++ b/drivers/gpu/drm/nouveau/nv50_connector.c
@@ -140,9 +140,9 @@ nv50_connector_detect(struct drm_connector *drm_connector)
encoder = nv50_connector_to_encoder(connector, false);
if (encoder)
- helper = encoder->base.helper_private;
+ helper = to_drm_encoder(encoder)->helper_private;
- if (helper && helper->detect(&encoder->base, &connector->base) ==
+ if (helper && helper->detect(to_drm_encoder(encoder), &connector->base)
==
connector_status_connected) {
nv50_connector_set_digital(connector, false);
return connector_status_connected;
@@ -335,7 +335,7 @@ static int nv50_connector_mode_valid(struct drm_connector
*drm_connector,
min_clock = 25000;
- switch (encoder->base.encoder_type) {
+ switch (to_drm_encoder(encoder)->encoder_type) {
case DRM_MODE_ENCODER_LVDS:
if (!connector->native_mode) {
NV_ERROR(dev, "AIIII no native mode\n");
@@ -373,7 +373,7 @@ nv50_connector_best_encoder(struct drm_connector
*drm_connector)
{
struct nouveau_connector *connector = nouveau_connector(drm_connector);
- return &nv50_connector_to_encoder(connector, connector->digital)->base;
+ return to_drm_encoder(nv50_connector_to_encoder(connector,
connector->digital));
}
static const struct drm_connector_helper_funcs nv50_connector_helper_funcs = {
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c
b/drivers/gpu/drm/nouveau/nv50_crtc.c
index 81c53c4..4c0e633 100644
--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
@@ -209,7 +209,7 @@ nouveau_crtc_connector_get(struct nouveau_crtc *crtc)
return NULL;
list_for_each_entry(drm_connector, &dev->mode_config.connector_list,
head) {
- if (drm_connector->encoder == &encoder->base)
+ if (drm_connector->encoder == to_drm_encoder(encoder))
return nouveau_connector(drm_connector);
}
diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c
b/drivers/gpu/drm/nouveau/nv50_dac.c
index f07b92a..67b1457 100644
--- a/drivers/gpu/drm/nouveau/nv50_dac.c
+++ b/drivers/gpu/drm/nouveau/nv50_dac.c
@@ -36,7 +36,7 @@
static void
nv50_dac_disconnect(struct nouveau_encoder *encoder)
{
- struct drm_device *dev = encoder->base.dev;
+ struct drm_device *dev = to_drm_encoder(encoder)->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
int ret;
@@ -57,7 +57,7 @@ nv50_dac_detect(struct drm_encoder *drm_encoder,
struct drm_connector *drm_connector)
{
struct nouveau_encoder *encoder = nouveau_encoder(drm_encoder);
- struct drm_device *dev = encoder->base.dev;
+ struct drm_device *dev = to_drm_encoder(encoder)->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
enum drm_connector_status status = connector_status_disconnected;
uint32_t dpms_state, load_pattern, load_state;
@@ -207,10 +207,10 @@ static void nv50_dac_mode_set(struct drm_encoder
*drm_encoder,
mode_ctl |= NV50_EVO_DAC_MODE_CTRL_CRTC0;
/* Lacking a working tv-out, this is not a 100% sure. */
- if (encoder->base.encoder_type == DRM_MODE_ENCODER_DAC) {
+ if (to_drm_encoder(encoder)->encoder_type == DRM_MODE_ENCODER_DAC) {
mode_ctl |= 0x40;
} else
- if (encoder->base.encoder_type == DRM_MODE_ENCODER_TVDAC) {
+ if (to_drm_encoder(encoder)->encoder_type == DRM_MODE_ENCODER_TVDAC) {
mode_ctl |= 0x100;
}
@@ -250,7 +250,7 @@ static void nv50_dac_destroy(struct drm_encoder
*drm_encoder)
if (!drm_encoder)
return;
- drm_encoder_cleanup(&encoder->base);
+ drm_encoder_cleanup(to_drm_encoder(encoder));
kfree(encoder);
}
@@ -273,12 +273,12 @@ int nv50_dac_create(struct drm_device *dev, struct
dcb_entry *entry)
encoder->dcb = entry;
encoder->or = ffs(entry->or) - 1;
- drm_encoder_init(dev, &encoder->base, &nv50_dac_encoder_funcs,
+ drm_encoder_init(dev, to_drm_encoder(encoder), &nv50_dac_encoder_funcs,
DRM_MODE_ENCODER_DAC);
- drm_encoder_helper_add(&encoder->base, &nv50_dac_helper_funcs);
+ drm_encoder_helper_add(to_drm_encoder(encoder), &nv50_dac_helper_funcs);
- encoder->base.possible_crtcs = entry->heads;
- encoder->base.possible_clones = 0;
+ to_drm_encoder(encoder)->possible_crtcs = entry->heads;
+ to_drm_encoder(encoder)->possible_clones = 0;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c
b/drivers/gpu/drm/nouveau/nv50_sor.c
index c8f1fe9..f731d10 100644
--- a/drivers/gpu/drm/nouveau/nv50_sor.c
+++ b/drivers/gpu/drm/nouveau/nv50_sor.c
@@ -39,7 +39,7 @@ extern int nouveau_duallink;
static void
nv50_sor_disconnect(struct nouveau_encoder *encoder)
{
- struct drm_device *dev = encoder->base.dev;
+ struct drm_device *dev = to_drm_encoder(encoder)->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
int ret;
@@ -108,11 +108,11 @@ static void nv50_sor_restore(struct drm_encoder
*drm_encoder)
struct nouveau_connector *
nouveau_encoder_connector_get(struct nouveau_encoder *encoder)
{
- struct drm_device *dev = encoder->base.dev;
+ struct drm_device *dev = to_drm_encoder(encoder)->dev;
struct drm_connector *drm_connector;
list_for_each_entry(drm_connector, &dev->mode_config.connector_list,
head) {
- if (drm_connector->encoder == &encoder->base)
+ if (drm_connector->encoder == to_drm_encoder(encoder))
return nouveau_connector(drm_connector);
}
@@ -168,7 +168,7 @@ static void nv50_sor_mode_set(struct drm_encoder
*drm_encoder,
nv50_sor_dpms(drm_encoder, DRM_MODE_DPMS_ON);
dev_priv->in_modeset = ret;
- if (encoder->base.encoder_type != DRM_MODE_ENCODER_LVDS) {
+ if (to_drm_encoder(encoder)->encoder_type != DRM_MODE_ENCODER_LVDS) {
mode_ctl |= NV50_EVO_SOR_MODE_CTRL_TMDS;
if (adjusted_mode->clock > 165000)
mode_ctl |= NV50_EVO_SOR_MODE_CTRL_TMDS_DUAL_LINK;
@@ -214,7 +214,7 @@ static void nv50_sor_destroy(struct drm_encoder
*drm_encoder)
if (!drm_encoder)
return;
- drm_encoder_cleanup(&encoder->base);
+ drm_encoder_cleanup(to_drm_encoder(encoder));
kfree(encoder);
}
@@ -258,11 +258,11 @@ int nv50_sor_create(struct drm_device *dev, struct
dcb_entry *entry)
encoder->dual_link = nouveau_duallink;
- drm_encoder_init(dev, &encoder->base, &nv50_sor_encoder_funcs, type);
- drm_encoder_helper_add(&encoder->base, &nv50_sor_helper_funcs);
+ drm_encoder_init(dev, to_drm_encoder(encoder), &nv50_sor_encoder_funcs,
type);
+ drm_encoder_helper_add(to_drm_encoder(encoder), &nv50_sor_helper_funcs);
- encoder->base.possible_crtcs = entry->heads;
- encoder->base.possible_clones = 0;
+ to_drm_encoder(encoder)->possible_crtcs = entry->heads;
+ to_drm_encoder(encoder)->possible_clones = 0;
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvreg.h b/drivers/gpu/drm/nouveau/nvreg.h
index 90623b0..a013b60 100644
--- a/drivers/gpu/drm/nouveau/nvreg.h
+++ b/drivers/gpu/drm/nouveau/nvreg.h
@@ -293,6 +293,7 @@
# define NV_CIO_CRE_RCR 0x46
# define NV_CIO_CRE_RCR_ENDIAN_BIG 7:7
# define NV_CIO_CRE_47 0x47 /* extended fifo lwm,
used on nv30+ */
+# define NV_CIO_CRE_49 0x49
# define NV_CIO_CRE_4B 0x4b /* given patterns in
0x[2-3][a-c] regs, probably scratch 6 */
# define NV_CIO_CRE_TVOUT_LATENCY 0x52
# define NV_CIO_CRE_53 0x53 /* `fp_htiming'
according to Haiku */
@@ -316,13 +317,18 @@
# define NV30_RAMDAC_ENABLE_VCO2 (8 << 4)
#define NV_PRAMDAC_PLL_COEFF_SELECT 0x0068050c
-# define NV_RAMDAC_PLL_SELECT_USE_VPLL2_TRUE (4 << 0)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE (4 << 0)
# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL (1 << 8)
# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL (2 << 8)
# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL (4 << 8)
-# define NV_RAMDAC_PLL_SELECT_PLL_SOURCE_VPLL2 (8 << 8)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 (8 << 8)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 (1 << 16)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 (2 << 16)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 (4 << 16)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2 (8 << 16)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_CLK_SOURCE_VIP (1 << 20)
# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2 (1 << 28)
-# define NV_RAMDAC_PLL_SELECT_VCLK2_RATIO_DB2 (2 << 28)
+# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2 (2 << 28)
#define NV_PRAMDAC_PLL_SETUP_CONTROL 0x00680510
#define NV_RAMDAC_VPLL2 0x00680520
@@ -356,6 +362,15 @@
#define NV_PRAMDAC_630 0x00680630
#define NV_PRAMDAC_634 0x00680634
+#define NV_PRAMDAC_TV_SETUP 0x00680700
+#define NV_PRAMDAC_TV_VTOTAL 0x00680720
+#define NV_PRAMDAC_TV_VSKEW 0x00680724
+#define NV_PRAMDAC_TV_VSYNC_DELAY 0x00680728
+#define NV_PRAMDAC_TV_HTOTAL 0x0068072c
+#define NV_PRAMDAC_TV_HSKEW 0x00680730
+#define NV_PRAMDAC_TV_HSYNC_DELAY 0x00680734
+#define NV_PRAMDAC_TV_HSYNC_DELAY2 0x00680738
+
#define NV_PRAMDAC_FP_VDISPLAY_END 0x00680800
#define NV_PRAMDAC_FP_VTOTAL 0x00680804
#define NV_PRAMDAC_FP_VCRTC 0x00680808
--
1.6.3.3
------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now. http://p.sf.net/sfu/bobj-july
--
_______________________________________________
Dri-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/dri-devel