From: Francisco J. <cur...@ri...> - 2009-08-02 02:19:56
|
Signed-off-by: Francisco Jerez <cur...@ri...> --- 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 | 266 +++++++++++++++++++++++++++++ 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, 587 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..e2142e3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv04_tv.c @@ -0,0 +1,266 @@ +/* + * 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); + + 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, + }; + + /* Set the slave encoder configuration */ + sfuncs->set_config(encoder, nv04_tv_encoder_info[type].params); + + 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 |