drivers/gpu/drm/ttm/ttm_bo.c | 2 drivers/gpu/drm/via/Makefile | 2 drivers/gpu/drm/via/via_analog.c | 10 drivers/gpu/drm/via/via_crtc.c | 431 ++++++++++++++++++++- drivers/gpu/drm/via/via_disp_reg.h | 20 drivers/gpu/drm/via/via_display.c | 85 ++++ drivers/gpu/drm/via/via_display.h | 23 + drivers/gpu/drm/via/via_drv.h | 1 drivers/gpu/drm/via/via_lvds.c | 746 +++++++++++++++++++++++++++++++++++++ 9 files changed, 1289 insertions(+), 31 deletions(-)
New commits: commit c45cef24757677199a1ae3f77ae4b2f6a76f253e Merge: 15220435 0953e76 Author: James Simmons <jsimm...@infradead.org> Date: Thu Dec 20 19:53:24 2012 -0500 Merge branch 'drm-next' commit 15220435da3c2ebfc7c6ac86ba325be86e10e8f3 Author: James Simmons <jsimm...@infradead.org> Date: Thu Dec 20 19:51:30 2012 -0500 Initial LVDS support. You can resize but xrandr scale is still broken. Will fix scaling at a latter time but in general internal LVDS is supported. diff --git a/drivers/gpu/drm/via/Makefile b/drivers/gpu/drm/via/Makefile index 6eb45fa..2912e41 100644 --- a/drivers/gpu/drm/via/Makefile +++ b/drivers/gpu/drm/via/Makefile @@ -7,6 +7,6 @@ via-y := via_drv.o via_pm.o via_i2c.o via_irq.o via_verifier.o via_ioc32.o \ init_ttm.o ttm_gem.o via_ttm.o via_fb.o via_sgdma.o \ via_h1_dma.o via_h1_cmdbuf.o via_video.o \ via_display.o via_crtc.o crtc_hw.o via_clocks.o \ - via_analog.o + via_analog.o via_lvds.o obj-$(CONFIG_DRM_VIA) += via.o diff --git a/drivers/gpu/drm/via/via_analog.c b/drivers/gpu/drm/via/via_analog.c index 286ec51..1ea889d 100644 --- a/drivers/gpu/drm/via/via_analog.c +++ b/drivers/gpu/drm/via/via_analog.c @@ -69,19 +69,11 @@ via_dac_mode_fixup(struct drm_encoder *encoder, return true; } -static void -via_dac_prepare(struct drm_encoder *encoder) -{ - struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; - - encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); -} - static const struct drm_encoder_helper_funcs via_dac_enc_helper_funcs = { .dpms = via_dac_dpms, .mode_fixup = via_dac_mode_fixup, - .prepare = via_dac_prepare, .mode_set = via_set_sync_polarity, + .prepare = via_encoder_prepare, .commit = via_encoder_commit, .disable = via_encoder_disable, }; diff --git a/drivers/gpu/drm/via/via_crtc.c b/drivers/gpu/drm/via/via_crtc.c index 8222ea1..864a69d 100644 --- a/drivers/gpu/drm/via/via_crtc.c +++ b/drivers/gpu/drm/via/via_crtc.c @@ -475,6 +475,328 @@ void via_load_crtc_timing(struct via_crtc *iga, struct drm_display_mode *mode) } } +/* + * This function changes the destination of scaling up/down + * and CRTC timing registers + * crtc : which IGA + * scale_type : upscaling(VIA_EXPAND) or downscaling(VIA_SHRINK) + */ +void via_set_scale_path(struct drm_crtc *crtc, u32 scale_type) +{ + struct via_crtc *iga = container_of(crtc, struct via_crtc, base); + struct drm_via_private *dev_priv = crtc->dev->dev_private; + u8 reg_cr_fd = vga_rcrt(VGABASE, 0xFD); + struct drm_device *dev = crtc->dev; + + if (!iga->index) + /* register reuse: select IGA1 path */ + reg_cr_fd |= BIT(7); + else + /* register reuse: select IGA2 path */ + reg_cr_fd &= ~BIT(7); + + /* only IGA1 up scaling need to clear this bit CRFD.5. */ + if (dev->pci_device == PCI_DEVICE_ID_VIA_VX900) { + if (!iga->index && ((VIA_HOR_EXPAND & scale_type) || + (VIA_VER_EXPAND & scale_type))) + reg_cr_fd &= ~BIT(5); + } + + /* CRFD.0 = 0 : common IGA2, = 1 : downscaling IGA */ + switch (scale_type) { + case VIA_NO_SCALING: + case VIA_EXPAND: + case VIA_HOR_EXPAND: + case VIA_VER_EXPAND: + /* register reuse: as common IGA2 */ + reg_cr_fd &= ~BIT(0); + break; + + case VIA_SHRINK: + /* register reuse: as downscaling IGA */ + reg_cr_fd |= BIT(0); + break; + + default: + break; + } + vga_wcrt(VGABASE, 0xFD, reg_cr_fd); +} + +/* disable IGA scaling */ +static void +via_disable_iga_scaling(struct drm_crtc *crtc) +{ + struct via_crtc *iga = container_of(crtc, struct via_crtc, base); + struct drm_via_private *dev_priv = crtc->dev->dev_private; + + if (iga->index) { + /* IGA2 scalings disable */ + via_set_scale_path(crtc, VIA_SHRINK); + /* disable IGA down scaling and buffer sharing. */ + svga_wcrt_mask(VGABASE, 0x89, 0x00, BIT(7) | BIT(0)); + /* Horizontal and Vertical scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, 0x00, BIT(7) | BIT(3)); + + /* Disable scale up as well */ + via_set_scale_path(crtc, VIA_EXPAND); + /* disable IGA up scaling */ + svga_wcrt_mask(VGABASE, 0x79, 0, BIT(0)); + /* Horizontal and Vertical scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, 0x00, BIT(7) | BIT(3)); + } else { + /* IGA1 scalings disable */ + via_set_scale_path(crtc, VIA_SHRINK); + /* disable IGA down scaling and buffer sharing. */ + svga_wcrt_mask(VGABASE, 0x89, 0x00, BIT(7) | BIT(0)); + /* Horizontal and Vertical scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, 0x00, BIT(7) | BIT(3)); + + /* Disable scale up as well */ + via_set_scale_path(crtc, VIA_EXPAND); + /* disable IGA up scaling */ + svga_wcrt_mask(VGABASE, 0x79, 0, BIT(0)); + /* Horizontal and Vertical scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, 0x00, BIT(7) | BIT(3)); + } +} + +/* + * Enable IGA scale functions. + * + * input : iga_path = IGA1 or IGA2 or + * IGA1+IGA2 + * + * scale_type = VIA_HOR_EXPAND or VIA_VER_EXPAND or VIA_EXPAND or + * VIA_SHRINK or VIA_SHRINK + VIA_EXPAND + */ +bool +via_set_iga_scale_function(struct drm_crtc *crtc, u32 scale_type) +{ + struct via_crtc *iga = container_of(crtc, struct via_crtc, base); + struct drm_via_private *dev_priv = crtc->dev->dev_private; + + if (!(scale_type & (VIA_SHRINK + VIA_EXPAND))) + return false; + + if (iga->index) { + /* IGA2 scalings enable */ + if (VIA_SHRINK & scale_type) { + via_set_scale_path(crtc, VIA_SHRINK); + /* Horizontal and Vertical scaling enable */ + svga_wcrt_mask(VGABASE, 0xA2, + BIT(7) | BIT(3), BIT(7) | BIT(3)); + /* enable IGA down scaling */ + svga_wcrt_mask(VGABASE, 0x89, BIT(0), BIT(0)); + /* hor and ver scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, + BIT(2) | BIT(1), BIT(2) | BIT(1)); + } + + if (VIA_EXPAND & scale_type) { + via_set_scale_path(crtc, VIA_EXPAND); + /* enable IGA up scaling */ + svga_wcrt_mask(VGABASE, 0x79, BIT(0), BIT(0)); + } + + if ((VIA_EXPAND & scale_type) == VIA_EXPAND) { + /* Horizontal and Vertical scaling enable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(7) | BIT(3), + BIT(7) | BIT(3)); + /* hor and ver scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, BIT(2) | BIT(1), + BIT(2) | BIT(1)); + } else if (VIA_HOR_EXPAND & scale_type) { + /* Horizontal scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(7), BIT(7)); + /* hor scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, BIT(1), BIT(1)); + } else if (VIA_VER_EXPAND & scale_type) { + /* Vertical scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(3), BIT(3)); + /* ver scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, BIT(2), BIT(2)); + } + } else { + /* IGA1 scalings enable */ + if (VIA_SHRINK & scale_type) { + via_set_scale_path(crtc, VIA_SHRINK); + + /* Horizontal and Vertical scaling enable */ + svga_wcrt_mask(VGABASE, 0xA2, + BIT(7) | BIT(3), BIT(7) | BIT(3)); + /* enable IGA down scaling */ + svga_wcrt_mask(VGABASE, 0x89, BIT(0), BIT(0)); + /* hor and ver scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, + BIT(2) | BIT(1), BIT(2) | BIT(1)); + } + + if (VIA_EXPAND & scale_type) { + via_set_scale_path(crtc, VIA_EXPAND); + /* enable IGA up scaling */ + svga_wcrt_mask(VGABASE, 0x79, BIT(0), BIT(0)); + } + + if ((VIA_EXPAND & scale_type) == VIA_EXPAND) { + /* Horizontal and Vertical scaling enable */ + svga_wcrt_mask(VGABASE, 0xA2, + BIT(7) | BIT(3), BIT(7) | BIT(3)); + /* hor and ver scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, + BIT(2) | BIT(1), BIT(2) | BIT(1)); + } else if (VIA_HOR_EXPAND & scale_type) { + /* Horizontal scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(7), BIT(7)); + /* hor scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, BIT(1), BIT(1)); + } else if (VIA_VER_EXPAND & scale_type) { + /* Vertical scaling disable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(3), BIT(3)); + /* ver scaling : Interpolation */ + svga_wcrt_mask(VGABASE, 0x79, BIT(2), BIT(2)); + } + } + return true; +} + +/* + * 1. get scale factors from source and dest H & V size + * 2. load scale factors into registers + * 3. enable H or V scale ( set CRA2 bit7 or bit3 ) + */ +bool +via_load_iga_scale_factor_regs(struct drm_via_private *dev_priv, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, + u32 scale_type, u32 is_hor_or_ver) +{ + u32 dst_hor_regs = adjusted_mode->crtc_hdisplay; + u32 dst_ver_regs = adjusted_mode->crtc_vdisplay; + u32 src_hor_regs = mode->crtc_hdisplay; + u32 src_ver_regs = mode->crtc_vdisplay; + u32 hor_factor = 0, ver_factor = 0; + struct vga_registers reg; + + if ((0 == src_hor_regs) || (0 == src_ver_regs) || + (0 == dst_hor_regs) || (0 == dst_ver_regs)) + return false; + + if (VIA_EXPAND == scale_type) { + if (HOR_SCALE & is_hor_or_ver) { + hor_factor = ((src_hor_regs - 1) * 4096) / + (dst_hor_regs - 1); + reg.count = ARRAY_SIZE(lcd_hor_scaling); + reg.regs = lcd_hor_scaling; + load_value_to_registers(VGABASE, ®, hor_factor); + /* Horizontal scaling enable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(7), BIT(7)); + } + + if (VER_SCALE & is_hor_or_ver) { + ver_factor = ((src_ver_regs - 1) * 2048) / + (dst_ver_regs - 1); + reg.count = ARRAY_SIZE(lcd_ver_scaling); + reg.regs = lcd_ver_scaling; + load_value_to_registers(VGABASE, ®, ver_factor); + /* Vertical scaling enable */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(3), BIT(3)); + } + + } else if (VIA_SHRINK == scale_type) { + + if (src_hor_regs > dst_hor_regs) + hor_factor = + ((src_hor_regs - dst_hor_regs) * 4096) / + dst_hor_regs; + + if (src_ver_regs > dst_ver_regs) + ver_factor = + ((src_ver_regs - dst_ver_regs) * 2048) / + dst_ver_regs; + + reg.count = ARRAY_SIZE(lcd_hor_scaling); + reg.regs = lcd_hor_scaling; + load_value_to_registers(VGABASE, ®, hor_factor); + + reg.count = ARRAY_SIZE(lcd_ver_scaling); + reg.regs = lcd_ver_scaling; + load_value_to_registers(VGABASE, ®, ver_factor); + + /* set buffer sharing enable bit . */ + if (hor_factor || ver_factor) { + if (dst_hor_regs > 1024) + svga_wcrt_mask(VGABASE, 0x89, + BIT(7), BIT(7)); + else + svga_wcrt_mask(VGABASE, 0x89, + 0x00, BIT(7)); + } + + if (hor_factor) + /* CRA2[7]:1 Enable Hor scaling + CRA2[6]:1 Linear Mode */ + svga_wcrt_mask(VGABASE, 0xA2, BIT(7) | BIT(6), + BIT(7) | BIT(6)); + else + svga_wcrt_mask(VGABASE, 0xA2, 0, BIT(7)); + + if (ver_factor) + svga_wcrt_mask(VGABASE, 0xA2, BIT(3), BIT(3)); + else + svga_wcrt_mask(VGABASE, 0xA2, 0, BIT(3)); + } + return true; +} + +void +via_set_iga2_downscale_source_timing(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + unsigned int viewx = adjusted_mode->hdisplay, viewy = adjusted_mode->vdisplay; + unsigned int srcx = mode->crtc_hdisplay, srcy = mode->crtc_vdisplay; + struct via_crtc *iga = container_of(crtc, struct via_crtc, base); + struct drm_display_mode *src_timing; + + src_timing = drm_mode_duplicate(crtc->dev, adjusted_mode); + /* derived source timing */ + if (srcx <= viewx) { + src_timing->crtc_htotal = adjusted_mode->crtc_htotal; + src_timing->crtc_hdisplay = adjusted_mode->crtc_hdisplay; + } else { + unsigned int htotal = adjusted_mode->crtc_htotal - adjusted_mode->crtc_hdisplay; + + src_timing->crtc_htotal = htotal + srcx; + src_timing->crtc_hdisplay = srcx; + } + src_timing->crtc_hblank_start = src_timing->crtc_hdisplay; + src_timing->crtc_hblank_end = src_timing->crtc_htotal; + src_timing->crtc_hsync_start = src_timing->crtc_hdisplay + 2; + src_timing->crtc_hsync_end = src_timing->crtc_hsync_start + 1; + + if (srcy <= viewy) { + src_timing->crtc_vtotal = adjusted_mode->crtc_vtotal; + src_timing->crtc_vdisplay = adjusted_mode->crtc_vdisplay; + } else { + unsigned int vtotal = adjusted_mode->crtc_vtotal - adjusted_mode->crtc_vdisplay; + + src_timing->crtc_vtotal = vtotal + srcy; + src_timing->crtc_vdisplay = srcy; + } + src_timing->crtc_vblank_start = src_timing->crtc_vdisplay; + src_timing->crtc_vblank_end = src_timing->crtc_vtotal; + src_timing->crtc_vsync_start = src_timing->crtc_vdisplay + 2; + src_timing->crtc_vsync_end = src_timing->crtc_vsync_start + 1; + + via_set_scale_path(crtc, VIA_NO_SCALING); + /* load src timing */ + via_load_crtc_timing(iga, src_timing); + + /* Cleanup up source timings */ + drm_mode_destroy(crtc->dev, src_timing); +} + static void drm_mode_crtc_load_lut(struct drm_crtc *crtc) { @@ -585,28 +907,97 @@ via_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, svga_wcrt_mask(VGABASE, 0x11, 0x00, BIT(6)); } - /* Write CRTC */ - via_load_crtc_timing(iga, adjusted_mode); + /* disable IGA scales first */ + via_disable_iga_scaling(crtc); - /* Patch the IGA1 ECK clock */ - if (!iga->index) { - u8 reg_value = 0; + /* Load crtc timing and IGA scaling */ + if (iga->scaling_mode & VIA_SHRINK) { + /* I. down scaling */ + if (iga->index) { + /* enable IGA2 down scaling and set Interpolation */ + via_set_iga_scale_function(crtc, VIA_SHRINK); + + /* load hor and ver downscaling factor */ + /** + * interlace modes scaling support(example 1080I): + * we should use mode->crtc_vdisplay here, + * because crtc_vdisplay=540, vdisplay=1080, + * we need 540 here, not 1080. + */ + via_load_iga_scale_factor_regs(dev_priv, mode, + adjusted_mode, + VIA_SHRINK, + HOR_VER_SCALE); + /* load src timing to timing registers */ + /** + * interlace modes scaling support(example 1080I): + * we should use mode->crtc_vdisplay here, + * because crtc_vdisplay=540, vdisplay=1080, + * we need 540 here, not 1080. + */ + via_set_iga2_downscale_source_timing(crtc, mode, + adjusted_mode); + + /* Download dst timing */ + via_set_scale_path(crtc, VIA_SHRINK); + via_load_crtc_timing(iga, adjusted_mode); + /* very necessary to set IGA to none scaling status */ + /* need to fix why so need. */ + via_set_scale_path(crtc, VIA_NO_SCALING); + } else { + /* IGA1 downscaling not supported */ + } + } else { + /* when not down scaling, we only need load one timing. */ + via_load_crtc_timing(iga, adjusted_mode); + + /* Patch the IGA1 ECK clock */ + if (!iga->index) { + u8 reg_value = 0; + + switch (adjusted_mode->crtc_htotal % 8) { + case 0: + default: + break; + case 2: + reg_value = BIT(7); + break; + case 4: + reg_value = BIT(6); + break; + case 6: + reg_value = BIT(3); + break; + } + svga_wcrt_mask(VGABASE, 0x47, reg_value, BIT(7) | BIT(6) | BIT(3)); + } - switch (adjusted_mode->crtc_htotal % 8) { - case 0: - default: - break; - case 2: - reg_value = BIT(7); - break; - case 4: - reg_value = BIT(6); - break; - case 6: - reg_value = BIT(3); - break; + /* II. up scaling */ + if (iga->scaling_mode & VIA_EXPAND) { + if (iga->index) { + /* Horizontal scaling */ + if (iga->scaling_mode & VIA_HOR_EXPAND) { + via_set_iga_scale_function(crtc, + VIA_HOR_EXPAND); + via_load_iga_scale_factor_regs(dev_priv, + mode, adjusted_mode, + VIA_EXPAND, HOR_SCALE); + } + + /* Vertical scaling */ + if (iga->scaling_mode & VIA_VER_EXPAND) { + via_set_iga_scale_function(crtc, + VIA_VER_EXPAND); + via_load_iga_scale_factor_regs(dev_priv, + mode, adjusted_mode, + VIA_EXPAND, VER_SCALE); + } + } else { + /* IGA1 Upscaling not supported */ + } + } else { + /* III. no scaling */ } - svga_wcrt_mask(VGABASE, 0x47, reg_value, BIT(7) | BIT(6) | BIT(3)); } /* Relock */ @@ -641,6 +1032,8 @@ via_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, if (adjusted_mode->clock) { u32 clock = adjusted_mode->clock * 1000, pll_regs; + if (iga->scaling_mode & VIA_SHRINK) + clock *= 2; pll_regs = via_get_clk_value(crtc->dev, clock); via_set_vclock(crtc, pll_regs); } diff --git a/drivers/gpu/drm/via/via_disp_reg.h b/drivers/gpu/drm/via/via_disp_reg.h index 71ccf35..ebe65fe 100644 --- a/drivers/gpu/drm/via/via_disp_reg.h +++ b/drivers/gpu/drm/via/via_disp_reg.h @@ -214,6 +214,24 @@ static struct vga_regset iga2_fetch_count[] __devinitdata = { { VGA_CRT_IC, 0x67, 2, 3 } }; +/************************************************/ +/*********** IGA Scaling Factor Registers *******/ +/************************************************/ +#define LCD_HOR_SCALE_FACTOR_FORMULA(x, y) (((x - 1) * 4096) / (y - 1)) +#define LCD_VER_SCALE_FACTOR_FORMULA(x, y) (((x - 1) * 2048) / (y - 1)) + +static struct vga_regset lcd_hor_scaling[] = { + { VGA_CRT_IC, 0x9F, 0, 1 }, + { VGA_CRT_IC, 0x77, 0, 7 }, + { VGA_CRT_IC, 0x79, 4, 5 } +}; + +static struct vga_regset lcd_ver_scaling[] = { + { VGA_CRT_IC, 0x79, 3, 3 }, + { VGA_CRT_IC, 0x78, 0, 7 }, + { VGA_CRT_IC, 0x79, 6, 7 } +}; + /***********************************************/ /*********** CRTC timing register **************/ /***********************************************/ @@ -269,7 +287,7 @@ static struct vga_regset iga2_fetch_count[] __devinitdata = { #define IGA1_PIXELTIMING_VER_SYNC_START_FORMULA(x) (x - 1) #define IGA1_PIXELTIMING_VER_SYNC_END_FORMULA(x) (x - 1) -#define IGA1_PIXELTIMING_HVSYNC_OFFSET_END_FORMULA(x,y) \ +#define IGA1_PIXELTIMING_HVSYNC_OFFSET_END_FORMULA(x, y) \ ((x / 2) - 1 - (x - y)) /************************************************/ diff --git a/drivers/gpu/drm/via/via_display.c b/drivers/gpu/drm/via/via_display.c index 2446aa6..6b257c6 100644 --- a/drivers/gpu/drm/via/via_display.c +++ b/drivers/gpu/drm/via/via_display.c @@ -244,6 +244,14 @@ via_set_sync_polarity(struct drm_encoder *encoder, struct drm_display_mode *mode } } +void +via_encoder_prepare(struct drm_encoder *encoder) +{ + struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; + + encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); +} + struct drm_encoder * via_best_encoder(struct drm_connector *connector) { @@ -281,6 +289,61 @@ via_connector_destroy(struct drm_connector *connector) kfree(con); } +/* Power sequence relations */ +struct td_timer { + struct vga_regset tdRegs[2]; +}; + +static struct td_timer td_timer_regs[] = { + /* td_timer0 */ + { { { VGA_CRT_IC, 0x8B, 0, 7 }, { VGA_CRT_IC, 0x8F, 0, 3 } } }, + /* td_timer1 */ + { { { VGA_CRT_IC, 0x8C, 0, 7 }, { VGA_CRT_IC, 0x8F, 4, 7 } } }, + /* td_timer2 */ + { { { VGA_CRT_IC, 0x8D, 0, 7 }, { VGA_CRT_IC, 0x90, 0, 3 } } }, + /* td_timer3 */ + { { { VGA_CRT_IC, 0x8E, 0, 7 }, { VGA_CRT_IC, 0x90, 4, 7 } } } +}; + +/* + * Function Name: via_init_td_timing_regs + * Description: Init TD timing register (power sequence) + */ +static void +via_init_td_timing_regs(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = dev->dev_private; + unsigned int td_timer[4] = { 500, 50, 0, 510 }, i; + struct vga_registers timings; + u32 reg_value; + + /* Fill primary power sequence */ + for (i = 0; i < 4; i++) { + /* Calculate TD Timer, every step is 572.1uSec */ + reg_value = td_timer[i] * 10000 / 5721; + + timings.count = ARRAY_SIZE(td_timer_regs[i].tdRegs); + timings.regs = td_timer_regs[i].tdRegs; + load_value_to_registers(VGABASE, &timings, reg_value); + } + + /* Note: VT3353 have two hardware power sequences + * other chips only have one hardware power sequence */ + if (dev->pci_device == PCI_DEVICE_ID_VIA_VT1122) { + /* set CRD4[0] to "1" to select 2nd LCD power sequence. */ + svga_wcrt_mask(VGABASE, 0xD4, BIT(0), BIT(0)); + /* Fill secondary power sequence */ + for (i = 0; i < 4; i++) { + /* Calculate TD Timer, every step is 572.1uSec */ + reg_value = td_timer[i] * 10000 / 5721; + + timings.count = ARRAY_SIZE(td_timer_regs[i].tdRegs); + timings.regs = td_timer_regs[i].tdRegs; + load_value_to_registers(VGABASE, &timings, reg_value); + } + } +} + static void via_i2c_reg_init(struct drm_via_private *dev_priv) { @@ -355,10 +418,30 @@ static void via_display_init(struct drm_device *dev) { struct drm_via_private *dev_priv = dev->dev_private; + u8 index = 0x3D, value; + + /* Check if spread spectrum is enable */ + if (dev->pci_device == PCI_DEVICE_ID_VIA_VX900) + index = 0x2C; + + value = vga_rseq(VGABASE, 0x1E); + if (value & BIT(3)) { + value = vga_rseq(VGABASE, index); + vga_wseq(VGABASE, index, value & 0xFE); + + value = vga_rseq(VGABASE, 0x1E); + vga_wseq(VGABASE, 0x1E, value & 0xF7); + + dev_priv->spread_spectrum = true; + } else + dev_priv->spread_spectrum = false; /* load fixed CRTC timing registers */ via_init_crtc_regs(dev); + /* 3. Init TD timing register (power sequence) */ + via_init_td_timing_regs(dev); + /* I/O address bit to be 1. Enable access */ /* to frame buffer at A0000-BFFFFh */ svga_wmisc_mask(VGABASE, BIT(0), BIT(0)); @@ -392,6 +475,8 @@ via_modeset_init(struct drm_device *dev) (dev_priv->revision == CX700_REVISION_700M)) via_analog_init(dev); + via_lvds_init(dev); + /* * Set up the framebuffer device */ diff --git a/drivers/gpu/drm/via/via_display.h b/drivers/gpu/drm/via/via_display.h index 6f5128a..f233ad2 100644 --- a/drivers/gpu/drm/via/via_display.h +++ b/drivers/gpu/drm/via/via_display.h @@ -30,6 +30,27 @@ #include "drm_crtc_helper.h" #include "drm_fb_helper.h" +/* IGA Scaling disable */ +#define VIA_NO_SCALING 0 + +/* IGA Scaling down */ +#define VIA_HOR_SHRINK BIT(0) +#define VIA_VER_SHRINK BIT(1) +#define VIA_SHRINK (BIT(0) | BIT(1)) + +/* IGA Scaling up */ +#define VIA_HOR_EXPAND BIT(2) +#define VIA_VER_EXPAND BIT(3) +#define VIA_EXPAND (BIT(2) | BIT(3)) + +/* Define IGA Scaling up/down status : Horizontal or Vertical */ +/* Is IGA Hor scaling up/down status */ +#define HOR_SCALE BIT(0) +/* Is IGA Ver scaling up/down status */ +#define VER_SCALE BIT(1) +/* Is IGA Hor and Ver scaling up/down status */ +#define HOR_VER_SCALE (BIT(0) | BIT(1)) + struct via_crtc { struct drm_crtc base; struct ttm_bo_kmap_obj cursor_kmap; @@ -44,6 +65,7 @@ struct via_crtc { struct vga_registers fifo_depth; struct vga_registers offset; struct vga_registers fetch; + int scaling_mode; uint8_t index; }; @@ -121,6 +143,7 @@ extern void via_set_sync_polarity(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); extern struct drm_encoder* via_best_encoder(struct drm_connector *connector); +extern void via_encoder_prepare(struct drm_encoder *encoder); extern void via_encoder_disable(struct drm_encoder *encoder); extern void via_encoder_commit(struct drm_encoder *encoder); diff --git a/drivers/gpu/drm/via/via_drv.h b/drivers/gpu/drm/via/via_drv.h index b4d4d01..2faa473 100644 --- a/drivers/gpu/drm/via/via_drv.h +++ b/drivers/gpu/drm/via/via_drv.h @@ -141,6 +141,7 @@ struct drm_via_private { wait_queue_head_t decoder_queue[VIA_NR_XVMC_LOCKS]; struct via_crtc iga[2]; + bool spread_spectrum; drm_via_sarea_t *sarea_priv; drm_local_map_t *sarea; diff --git a/drivers/gpu/drm/via/via_lvds.c b/drivers/gpu/drm/via/via_lvds.c new file mode 100644 index 0000000..ea5c847 --- /dev/null +++ b/drivers/gpu/drm/via/via_lvds.c @@ -0,0 +1,746 @@ +/* + * 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, sub license, + * 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) OR COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +#include <linux/dmi.h> +#include <asm/olpc.h> + +#include "via_drv.h" + +/* Encoder flags for LVDS */ +#define LVDS_DUAL_CHANNEL 1 + +/* caculate the cetering timing using mode and adjusted_mode */ +static void +via_centering_timing(const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + int panel_hsync_time = adjusted_mode->hsync_end - + adjusted_mode->hsync_start; + int panel_vsync_time = adjusted_mode->vsync_end - + adjusted_mode->vsync_start; + int panel_hblank_start = adjusted_mode->hdisplay; + int panel_vbank_start = adjusted_mode->vdisplay; + int hborder = (adjusted_mode->hdisplay - mode->hdisplay) / 2; + int vborder = (adjusted_mode->vdisplay - mode->vdisplay) / 2; + int new_hblank_start = hborder + mode->hdisplay; + int new_vblank_start = vborder + mode->vdisplay; + + adjusted_mode->hdisplay = mode->hdisplay; + adjusted_mode->hsync_start = (adjusted_mode->hsync_start - + panel_hblank_start) + new_hblank_start; + adjusted_mode->hsync_end = adjusted_mode->hsync_start + + panel_hsync_time; + adjusted_mode->vdisplay = mode->vdisplay; + adjusted_mode->vsync_start = (adjusted_mode->vsync_start - + panel_vbank_start) + new_vblank_start; + adjusted_mode->vsync_end = adjusted_mode->vsync_start + + panel_vsync_time; + /* Adjust Crtc H and V */ + adjusted_mode->crtc_hdisplay = adjusted_mode->hdisplay; + adjusted_mode->crtc_hblank_start = new_hblank_start; + adjusted_mode->crtc_hblank_end = adjusted_mode->crtc_htotal - hborder; + adjusted_mode->crtc_hsync_start = adjusted_mode->hsync_start; + adjusted_mode->crtc_hsync_end = adjusted_mode->hsync_end; + adjusted_mode->crtc_vdisplay = adjusted_mode->vdisplay; + adjusted_mode->crtc_vblank_start = new_vblank_start; + adjusted_mode->crtc_vblank_end = adjusted_mode->crtc_vtotal - vborder; + adjusted_mode->crtc_vsync_start = adjusted_mode->vsync_start; + adjusted_mode->crtc_vsync_end = adjusted_mode->vsync_end; +} + +static void +via_enable_internal_lvds(struct drm_encoder *encoder) +{ + struct via_encoder *enc = container_of(encoder, struct via_encoder, base); + struct drm_via_private *dev_priv = encoder->dev->dev_private; + struct drm_device *dev = encoder->dev; + + /* Turn on LCD panel */ + if ((enc->diPort & DISP_DI_DFPL) || (enc->diPort == DISP_DI_DVP1)) { + /* Software control power sequence */ + if ((dev->pci_device == PCI_DEVICE_ID_VIA_VT1122) || + (dev->pci_device == PCI_DEVICE_ID_VIA_CLE266)) { + /* Software control power sequence ON */ + svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(7)); + svga_wcrt_mask(VGABASE, 0x91, BIT(0), BIT(0)); + /* Delay td0 msec. */ + mdelay(200); + /* VDD ON */ + svga_wcrt_mask(VGABASE, 0x91, BIT(4), BIT(4)); + /* Delay td1 msec. */ + mdelay(25); + /* DATA ON */ + svga_wcrt_mask(VGABASE, 0x91, BIT(3), BIT(3)); + /* VEE ON (unused on vt3353) */ + svga_wcrt_mask(VGABASE, 0x91, BIT(2), BIT(2)); + /* Delay td3 msec. */ + mdelay(250); + /* Back-Light ON */ + svga_wcrt_mask(VGABASE, 0x91, BIT(1), BIT(1)); + } else { + /* Use first power sequence control: * + * Use hardware control power sequence. */ + svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(0)); + /* Turn on back light and panel path. */ + svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(7) | BIT(6)); + /* Turn on hardware power sequence. */ + svga_wcrt_mask(VGABASE, 0x6A, BIT(3), BIT(3)); + } + } + + if (enc->diPort & DISP_DI_DFPH) { + if ((dev->pci_device == PCI_DEVICE_ID_VIA_VT1122) || + (dev->pci_device == PCI_DEVICE_ID_VIA_CLE266)) { + /* Software control power sequence ON */ + svga_wcrt_mask(VGABASE, 0xD4, 0x00, BIT(1)); + svga_wcrt_mask(VGABASE, 0xD3, BIT(0), BIT(0)); + /* Delay td0 msec. */ + mdelay(200); + /* VDD ON */ + svga_wcrt_mask(VGABASE, 0xD3, BIT(4), BIT(4)); + /* Delay td1 msec. */ + mdelay(25); + /* DATA ON */ + svga_wcrt_mask(VGABASE, 0xD3, BIT(3), BIT(3)); + /* VEE ON (unused on vt3353) */ + svga_wcrt_mask(VGABASE, 0xD3, BIT(2), BIT(2)); + /* Delay td3 msec. */ + mdelay(250); + /* Back-Light ON */ + svga_wcrt_mask(VGABASE, 0xD3, BIT(1), BIT(1)); + } else { + /* Use hardware control power sequence. */ + svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(0)); + /* Turn on back light and panel path. */ + svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(7) | BIT(6)); + /* Turn on hardware power sequence. */ + svga_wcrt_mask(VGABASE, 0xD4, BIT(1), BIT(1)); + } + } + + /* Power on LVDS channel. */ + if (enc->flags & LVDS_DUAL_CHANNEL) { + /* For high resolution LCD (internal), + * power on both LVDS0 and LVDS1 */ + svga_wcrt_mask(VGABASE, 0xD2, 0x00, BIT(7) | BIT(6)); + } else { + if (enc->diPort & DISP_DI_DFPL) + svga_wcrt_mask(VGABASE, 0xD2, 0x00, BIT(7)); + else if (enc->diPort & DISP_DI_DFPH) + svga_wcrt_mask(VGABASE, 0xD2, 0x00, BIT(6)); + else + DRM_ERROR("invalid diPort\n"); + } +} + +static void +via_disable_internal_lvds(struct drm_encoder *encoder) +{ + struct via_encoder *enc = container_of(encoder, struct via_encoder, base); + struct drm_via_private *dev_priv = encoder->dev->dev_private; + struct drm_device *dev = encoder->dev; + + /* Turn off LCD panel */ + if ((enc->diPort & DISP_DI_DFPL) || (enc->diPort == DISP_DI_DVP1)) { + /* Set LCD software power sequence off */ + if ((dev->pci_device == PCI_DEVICE_ID_VIA_VT1122) || + (dev->pci_device == PCI_DEVICE_ID_VIA_CLE266)) { + /* Back-Light OFF */ + svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(1)); + /* Delay td3 msec. */ + mdelay(250); + /* VEE OFF (unused on vt3353) */ + svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(2)); + /* DATA OFF */ + svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(3)); + /* Delay td1 msec. */ + mdelay(25); + /* VDD OFF */ + svga_wcrt_mask(VGABASE, 0x91, 0x00, BIT(4)); + } else { + /* Use first power sequence control: * + * Turn off power sequence. */ + svga_wcrt_mask(VGABASE, 0x6A, 0x00, BIT(3)); + + /* Turn off back light and panel path. */ + svga_wcrt_mask(VGABASE, 0x91, 0xC0, BIT(7) | BIT(6)); + } + } + + if (enc->diPort & DISP_DI_DFPH) { + /* Set LCD software power sequence off */ + if ((dev->pci_device == PCI_DEVICE_ID_VIA_VT1122) || + (dev->pci_device == PCI_DEVICE_ID_VIA_CLE266)) { + /* Back-Light OFF */ + svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(1)); + /* Delay td3 msec. */ + mdelay(250); + /* VEE OFF */ + svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(2)); + /* DATA OFF */ + svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(3)); + /* Delay td1 msec. */ + mdelay(25); + /* VDD OFF */ + svga_wcrt_mask(VGABASE, 0xD3, 0x00, BIT(4)); + } else { + /* Use second power sequence control: * + * Turn off power sequence. */ + svga_wcrt_mask(VGABASE, 0xD4, 0x00, BIT(1)); + /* Turn off back light and panel path. */ + svga_wcrt_mask(VGABASE, 0xD3, 0xC0, BIT(7) | BIT(6)); + } + } + + /* Power off LVDS channel. */ + if (enc->flags & LVDS_DUAL_CHANNEL) { + /* For high resolution LCD (internal) we + * power off both LVDS0 and LVDS1 */ + svga_wcrt_mask(VGABASE, 0xD2, 0xC0, BIT(7) | BIT(6)); + } else { + if (enc->diPort & DISP_DI_DFPL) + svga_wcrt_mask(VGABASE, 0xD2, BIT(7), BIT(7)); + else if (enc->diPort & DISP_DI_DFPH) + svga_wcrt_mask(VGABASE, 0xD2, BIT(6), BIT(6)); + else + DRM_ERROR("invalid diPort\n"); + } +} + +static void +via_lvds_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_via_private *dev_priv = encoder->dev->dev_private; + struct drm_device *dev = encoder->dev; + struct via_crtc *iga = NULL; + + switch (mode) { + case DRM_MODE_DPMS_ON: + if (encoder->crtc == NULL) + return; + iga = container_of(encoder->crtc, struct via_crtc, base); + + /* when using the EPIA-EX board, if we do not set this bit, + * light LCD will failed in nonRandR structure, + * So, when light LCD this bit is always setted */ + svga_wcrt_mask(VGABASE, 0x6A, BIT(3), BIT(3)); + + if (dev_priv->spread_spectrum) { + if ((dev->pci_device == PCI_DEVICE_ID_VIA_VT1122) || + (dev->pci_device == PCI_DEVICE_ID_VIA_VX875) || + (dev->pci_device == PCI_DEVICE_ID_VIA_VX900)) { + /* GPIO-4/5 are used for spread spectrum, + * we must clear SR3D[7:6] to disable + * GPIO-4/5 output */ + svga_wseq_mask(VGABASE, 0x3D, BIT(0), 0xC1); + } else { + svga_wseq_mask(VGABASE, 0x2C, BIT(0), BIT(0)); + } + svga_wseq_mask(VGABASE, 0x1E, BIT(3), BIT(3)); + } + via_enable_internal_lvds(encoder); + break; + + case DRM_MODE_DPMS_STANDBY: + case DRM_MODE_DPMS_SUSPEND: + case DRM_MODE_DPMS_OFF: + via_disable_internal_lvds(encoder); + break; + } +} + +static bool +via_lvds_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_property *prop = encoder->dev->mode_config.scaling_mode_property; + struct via_crtc *iga = container_of(encoder->crtc, struct via_crtc, base); + struct drm_display_mode *native_mode = NULL, *tmp, *t; + struct drm_connector *connector = NULL, *con; + u64 scale_mode = DRM_MODE_SCALE_CENTER; + struct drm_device *dev = encoder->dev; + + list_for_each_entry(con, &dev->mode_config.connector_list, head) { + if (encoder == con->encoder) { + connector = con; + break; + } + } + + if (!connector) { + DRM_INFO("LVDS encoder is not used by any connector\n"); + return false; + } + + list_for_each_entry_safe(tmp, t, &connector->modes, head) { + if (tmp->type & (DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER)) { + native_mode = tmp; + break; + } + } + + if (!native_mode) { + DRM_INFO("No native mode for LVDS\n"); + return false; + } + + drm_object_property_get_value(&connector->base, prop, &scale_mode); + + if ((mode->hdisplay != native_mode->hdisplay) || + (mode->vdisplay != native_mode->vdisplay)) { + if (scale_mode == DRM_MODE_SCALE_NONE) + return false; + drm_mode_copy(adjusted_mode, native_mode); + } + drm_mode_set_crtcinfo(adjusted_mode, 0); + + iga->scaling_mode = VIA_NO_SCALING; + /* Take care of 410 downscaling */ + if ((mode->hdisplay > native_mode->hdisplay) || + (mode->vdisplay > native_mode->vdisplay)) { + iga->scaling_mode = VIA_SHRINK; + } else { + if (!iga->index || scale_mode == DRM_MODE_SCALE_CENTER) { + /* Do centering according to mode and adjusted_mode */ + via_centering_timing(mode, adjusted_mode); + } else { + if (mode->hdisplay < native_mode->hdisplay) + iga->scaling_mode |= VIA_HOR_EXPAND; + if (mode->vdisplay < native_mode->vdisplay) + iga->scaling_mode |= VIA_VER_EXPAND; + } + } + return true; +} + +const struct drm_encoder_helper_funcs via_lvds_helper_funcs = { + .dpms = via_lvds_dpms, + .mode_fixup = via_lvds_mode_fixup, + .mode_set = via_set_sync_polarity, + .prepare = via_encoder_prepare, + .commit = via_encoder_commit, + .disable = via_encoder_disable, +}; + +const struct drm_encoder_funcs via_lvds_enc_funcs = { + .destroy = drm_encoder_cleanup, +}; + +/* detect this connector connect status */ +static enum drm_connector_status +via_lcd_detect(struct drm_connector *connector, bool force) +{ + struct via_connector *con = container_of(connector, struct via_connector, base); + struct edid *edid = drm_get_edid(&con->base, con->ddc_bus); + + if (edid) { + drm_mode_connector_update_edid_property(&con->base, edid); + kfree(edid); + return connector_status_connected; + } else { + if (!list_empty(&connector->probed_modes)) + return connector_status_connected; + } + return connector_status_disconnected; +} + +static int +via_lcd_set_property(struct drm_connector *connector, + struct drm_property *property, uint64_t value) +{ + struct drm_via_private *dev_priv = connector->dev->dev_private; + struct drm_device *dev = connector->dev; + uint64_t orig; + int ret; + + ret = drm_object_property_get_value(&connector->base, property, &orig); + if (!ret && (orig != value)) { + if (property == dev->mode_config.dithering_mode_property) { + u8 reg_value; + + switch (value) { + case DRM_MODE_DITHERING_AUTO: + case DRM_MODE_DITHERING_ON: + reg_value = BIT(0); + break; + + case DRM_MODE_DITHERING_OFF: + reg_value = 0x00; + break; + + default: + return -EINVAL; + } + svga_wcrt_mask(VGABASE, 0x88, reg_value, BIT(0)); + + } else if (property == dev->mode_config.scaling_mode_property) { + switch (value) { + case DRM_MODE_SCALE_NONE: + break; + + case DRM_MODE_SCALE_CENTER: + break; + + case DRM_MODE_SCALE_ASPECT: + break; + + case DRM_MODE_SCALE_FULLSCREEN: + break; + + default: + return -EINVAL; + } + } + } + return 0; +} + +struct drm_connector_funcs via_lcd_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = via_lcd_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = via_lcd_set_property, + .destroy = via_connector_destroy, +}; + +int via_lcd_get_modes(struct drm_connector *connector) +{ + int count = via_get_edid_modes(connector); + + if (!count) { + struct drm_display_mode *tmp, *t; + + list_for_each_entry_safe(tmp, t, &connector->probed_modes, head) + count++; + } + return count; +} + +int via_lcd_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct drm_property *prop = connector->dev->mode_config.scaling_mode_property; + struct drm_display_mode *native_mode = NULL, *tmp, *t; + struct drm_device *dev = connector->dev; + u64 scale_mode = DRM_MODE_SCALE_CENTER; + + list_for_each_entry_safe(tmp, t, &connector->modes, head) { + if (tmp->type & (DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER)) { + native_mode = tmp; + break; + } + } + + drm_object_property_get_value(&connector->base, prop, &scale_mode); + + if ((scale_mode == DRM_MODE_SCALE_NONE) && + ((mode->hdisplay != native_mode->hdisplay) || + (mode->vdisplay != native_mode->vdisplay))) + return MODE_PANEL; + + /* Don't support mode larger than physical size */ + if (dev->pci_device != PCI_DEVICE_ID_VIA_VX900) { + if (mode->hdisplay > native_mode->hdisplay) + return MODE_PANEL; + if (mode->vdisplay > native_mode->vdisplay) + return MODE_PANEL; + } else { + /* HW limitation 410 only can + * do <= 1.33 scaling */ + if (mode->hdisplay * 100 > native_mode->hdisplay * 133) + return MODE_PANEL; + if (mode->vdisplay * 100 > native_mode->vdisplay * 133) + return MODE_PANEL; + + /* Now we can not support H V different scale */ + if ((mode->hdisplay > native_mode->hdisplay) && + (mode->vdisplay < native_mode->vdisplay)) + return MODE_PANEL; + if ((mode->hdisplay < native_mode->hdisplay) && + (mode->vdisplay > native_mode->vdisplay)) + return MODE_PANEL; + } + return MODE_OK; +} + +struct drm_connector_helper_funcs via_lcd_connector_helper_funcs = { + .get_modes = via_lcd_get_modes, + .mode_valid = via_lcd_mode_valid, + .best_encoder = via_best_encoder, +}; + +static int __init via_ttl_lvds_dmi_callback(const struct dmi_system_id *id) +{ + DRM_INFO("LVDS is TTL type for %s\n", id->ident); + return 1; +} + +static const struct dmi_system_id via_ttl_lvds[] = { + { + .callback = via_ttl_lvds_dmi_callback, + .ident = "VIA Quanta Netbook", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "QCI"), + DMI_MATCH(DMI_PRODUCT_VERSION, "VT6413A"), + }, + }, + + { } +}; + +void +via_lvds_init(struct drm_device *dev) +{ + struct drm_via_private *dev_priv = dev->dev_private; + bool dual_channel = false, is_msb = false; + uint64_t dither = DRM_MODE_DITHERING_OFF; + struct via_connector *con; + struct via_encoder *enc; + struct edid *edid; + u8 reg_value; + + con = kzalloc(sizeof(*con), GFP_KERNEL); + if (!con) { + DRM_INFO("allocate the connector error\n"); + return; + } + con->base.interlace_allowed = false; + con->base.doublescan_allowed = false; + drm_connector_init(dev, &con->base, &via_lcd_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + drm_connector_helper_add(&con->base, &via_lcd_connector_helper_funcs); + + switch (dev->pci_device) { + case PCI_DEVICE_ID_VIA_VX875: + case PCI_DEVICE_ID_VIA_VX900: + con->ddc_bus = via_find_ddc_bus(0x2C); + break; + default: + con->ddc_bus = via_find_ddc_bus(0x31); + break; + } + + edid = drm_get_edid(&con->base, con->ddc_bus); + if (!edid) { + struct drm_display_mode *native_mode = NULL; + + /* OLPC is very special */ + if (machine_is_olpc()) { + native_mode = drm_mode_create(dev); + + native_mode->clock = 56519; + native_mode->hdisplay = 1200; + native_mode->hsync_start = 1211; + native_mode->hsync_end = 1243; + native_mode->htotal = 1264; + native_mode->hskew = 0; + native_mode->vdisplay = 900; + native_mode->vsync_start = 901; + native_mode->vsync_end = 911; + native_mode->vtotal = 912; + native_mode->vscan = 0; + native_mode->vrefresh = 50; + native_mode->hsync = 0; + } else { + int hdisplay = 0, vdisplay = 0; + + /* First we have to make sure a LVDS is present */ + reg_value = (vga_rcrt(VGABASE, 0x3B) & BIT(1)); + if (!reg_value) + goto no_device; + + /* If no edid then we detect the mode using + * the scratch pad registers. */ + reg_value = (vga_rcrt(VGABASE, 0x3F) & 0x0F); + + DRM_DEBUG("panel index %x detected\n", reg_value); + switch (reg_value) { + case 0x00: + hdisplay = 640; + vdisplay = 480; + break; + + case 0x01: + hdisplay = 800; + vdisplay = 600; + break; + + case 0x02: + hdisplay = 1024; + vdisplay = 768; + break; + + case 0x03: + hdisplay = 1152; + vdisplay = 864; + break; + + case 0x04: + dual_channel = true; + hdisplay = 1280; + vdisplay = 1024; + break; + + case 0x05: + dual_channel = true; + hdisplay = 1400; + vdisplay = 1050; + break; + + case 0x06: + dual_channel = true; + hdisplay = 1600; + vdisplay = 1200; + break; + + case 0x08: + hdisplay = 800; + vdisplay = 480; + break; + + case 0x09: + dual_channel = true; + hdisplay = 1024; + vdisplay = 768; + break; + + case 0x0A: + hdisplay = 1368; + vdisplay = 768; + break; + + case 0x0B: + dual_channel = true; + hdisplay = 1024; + vdisplay = 768; + break; + + case 0x0D: + dual_channel = true; + hdisplay = 1280; + vdisplay = 1024; + break; + + case 0x0E: + dual_channel = true; + hdisplay = 1400; + vdisplay = 1050; + break; + + case 0x0F: + dual_channel = true; + hdisplay = 1600; + vdisplay = 1200; + break; + + default: + break; + } + + if (hdisplay && vdisplay) + native_mode = drm_cvt_mode(dev, hdisplay, vdisplay, + 60, false, false, false); + + if (reg_value < 0x0A) + dither = DRM_MODE_DITHERING_ON; + } + if (!native_mode) + goto no_device; + + native_mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER; + drm_mode_set_name(native_mode); + drm_mode_probed_add(&con->base, native_mode); + } else { + drm_mode_connector_update_edid_property(&con->base, edid); + kfree(edid); + + /* 00 LVDS1 + LVDS2 10 = Dual channel. Other are reserved */ + if ((vga_rseq(VGABASE, 0x13) >> 6) == 2) + dual_channel = true; + } + + drm_mode_create_scaling_mode_property(dev); + drm_object_attach_property(&con->base.base, + dev->mode_config.scaling_mode_property, + DRM_MODE_SCALE_CENTER); + + drm_mode_create_dithering_property(dev); + drm_object_attach_property(&con->base.base, + dev->mode_config.dithering_mode_property, + dither); + via_lcd_set_property(&con->base, dev->mode_config.dithering_mode_property, + dither); + + drm_sysfs_connector_add(&con->base); + + /* Now setup the encoder */ + enc = kzalloc(sizeof(*enc), GFP_KERNEL); + if (!enc) { + DRM_INFO("allocate the encoder error\n"); + goto no_device; + } + enc->base.possible_crtcs = BIT(1) | BIT(0); + + switch (dev->pci_device) { + case PCI_DEVICE_ID_VIA_CLE266: + enc->diPort = DISP_DI_DVP1; + break; + + case PCI_DEVICE_ID_VIA_VX875: + case PCI_DEVICE_ID_VIA_VX900: + enc->diPort = DISP_DI_DFPL; + break; + + default: + enc->diPort = DISP_DI_DFPH; + break; + } + + /* There has to be a way to detect TTL LVDS + * For now we use the DMI to handle this */ + if (dmi_check_system(via_ttl_lvds)) + enc->diPort = DISP_DI_DFPL | DISP_DI_DVP1; + + reg_value = 0x00; + if (enc->diPort == DISP_DI_DFPH) { + if (!is_msb) + reg_value = BIT(0); + svga_wcrt_mask(VGABASE, 0xD2, reg_value, BIT(0)); + } else if (enc->diPort == DISP_DI_DFPL) { + if (!is_msb) + reg_value = BIT(1); + svga_wcrt_mask(VGABASE, 0xD2, reg_value, BIT(1)); + } + + if (dual_channel) + enc->flags |= LVDS_DUAL_CHANNEL; + + drm_encoder_init(dev, &enc->base, &via_lvds_enc_funcs, + DRM_MODE_ENCODER_LVDS); + drm_encoder_helper_add(&enc->base, &via_lvds_helper_funcs); + + drm_mode_connector_attach_encoder(&con->base, &enc->base); + return; + +no_device: + drm_connector_cleanup(&con->base); + kfree(con); +} commit 0953e76e91f4b6206cef50bd680696dc6bf1ef99 Author: Maarten Lankhorst <maarten.lankho...@canonical.com> Date: Wed Dec 19 18:21:10 2012 +0100 drm/ttm: fix delayed ttm_bo_cleanup_refs_and_unlock delayed handling Fix regression introduced by 85b144f860176 "drm/ttm: call ttm_bo_cleanup_refs with reservation and lru lock held, v3" Slowpath ttm_bo_cleanup_refs_and_unlock accidentally tried to increase refcount on &bo->sync_obj instead of bo->sync_obj. The compiler didn't complain since sync_obj_ref takes a void pointer, so it was still valid c. This could result in lockups, memory corruptions, and warnings like these when graphics card VRAM usage is high: ------------[ cut here ]------------ WARNING: at include/linux/kref.h:42 radeon_fence_ref+0x2c/0x40() Hardware name: System Product Name Pid: 157, comm: X Not tainted 3.7.0-rc7-00520-g85b144f-dirty #174 Call Trace: [<ffffffff81058c84>] ? warn_slowpath_common+0x74/0xb0 [<ffffffff8129273c>] ? radeon_fence_ref+0x2c/0x40 [<ffffffff8125e95c>] ? ttm_bo_cleanup_refs_and_unlock+0x18c/0x2d0 [<ffffffff8125f17c>] ? ttm_mem_evict_first+0x1dc/0x2a0 [<ffffffff81264452>] ? ttm_bo_man_get_node+0x62/0xb0 [<ffffffff8125f4ce>] ? ttm_bo_mem_space+0x28e/0x340 [<ffffffff8125fb0c>] ? ttm_bo_move_buffer+0xfc/0x170 [<ffffffff810de172>] ? kmem_cache_alloc+0xb2/0xc0 [<ffffffff8125fc15>] ? ttm_bo_validate+0x95/0x110 [<ffffffff8125ff7c>] ? ttm_bo_init+0x2ec/0x3b0 [<ffffffff8129419a>] ? radeon_bo_create+0x18a/0x200 [<ffffffff81293e80>] ? radeon_bo_clear_va+0x40/0x40 [<ffffffff812a5342>] ? radeon_gem_object_create+0x92/0x160 [<ffffffff812a575c>] ? radeon_gem_create_ioctl+0x6c/0x150 [<ffffffff812a529f>] ? radeon_gem_object_free+0x2f/0x40 [<ffffffff81246b60>] ? drm_ioctl+0x420/0x4f0 [<ffffffff812a56f0>] ? radeon_gem_pwrite_ioctl+0x20/0x20 [<ffffffff810f53a4>] ? do_vfs_ioctl+0x2e4/0x4e0 [<ffffffff810e5588>] ? vfs_read+0x118/0x160 [<ffffffff810f55ec>] ? sys_ioctl+0x4c/0xa0 [<ffffffff810e5851>] ? sys_read+0x51/0xa0 [<ffffffff814b0612>] ? system_call_fastpath+0x16/0x1b Signed-off-by: Maarten Lankhorst <maarten.lankho...@canonical.com> Reported-by: Markus Trippelsdorf <mar...@trippelsdorf.de> Acked-by: Paul Menzel <paulepan...@users.sourceforge.net> Signed-off-by: Dave Airlie <airl...@redhat.com> diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index a915133..33d20be 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -579,7 +579,7 @@ static int ttm_bo_cleanup_refs_and_unlock(struct ttm_buffer_object *bo, * at this point the buffer should be dead, so * no new sync objects can be attached. */ - sync_obj = driver->sync_obj_ref(&bo->sync_obj); + sync_obj = driver->sync_obj_ref(bo->sync_obj); spin_unlock(&bdev->fence_lock); atomic_set(&bo->reserved, 0); _______________________________________________ Openchrome-devel mailing list Openchrome-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/openchrome-devel