16a227d5fSAlan Cox /* 26a227d5fSAlan Cox * Copyright © 2006-2007 Intel Corporation 36a227d5fSAlan Cox * 46a227d5fSAlan Cox * Permission is hereby granted, free of charge, to any person obtaining a 56a227d5fSAlan Cox * copy of this software and associated documentation files (the "Software"), 66a227d5fSAlan Cox * to deal in the Software without restriction, including without limitation 76a227d5fSAlan Cox * the rights to use, copy, modify, merge, publish, distribute, sublicense, 86a227d5fSAlan Cox * and/or sell copies of the Software, and to permit persons to whom the 96a227d5fSAlan Cox * Software is furnished to do so, subject to the following conditions: 106a227d5fSAlan Cox * 116a227d5fSAlan Cox * The above copyright notice and this permission notice (including the next 126a227d5fSAlan Cox * paragraph) shall be included in all copies or substantial portions of the 136a227d5fSAlan Cox * Software. 146a227d5fSAlan Cox * 156a227d5fSAlan Cox * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 166a227d5fSAlan Cox * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 176a227d5fSAlan Cox * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 186a227d5fSAlan Cox * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 196a227d5fSAlan Cox * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 206a227d5fSAlan Cox * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 216a227d5fSAlan Cox * DEALINGS IN THE SOFTWARE. 226a227d5fSAlan Cox * 236a227d5fSAlan Cox * Authors: 246a227d5fSAlan Cox * Eric Anholt <eric@anholt.net> 256a227d5fSAlan Cox */ 266a227d5fSAlan Cox 276a227d5fSAlan Cox #include <linux/i2c.h> 286a227d5fSAlan Cox #include <drm/drmP.h> 296a227d5fSAlan Cox 306a227d5fSAlan Cox #include "intel_bios.h" 316a227d5fSAlan Cox #include "psb_drv.h" 326a227d5fSAlan Cox #include "psb_intel_drv.h" 336a227d5fSAlan Cox #include "psb_intel_reg.h" 346a227d5fSAlan Cox #include "power.h" 352acdc9faSKirill A. Shutemov #include "cdv_device.h" 366a227d5fSAlan Cox #include <linux/pm_runtime.h> 376a227d5fSAlan Cox 386a227d5fSAlan Cox 396a227d5fSAlan Cox static void cdv_intel_crt_dpms(struct drm_encoder *encoder, int mode) 406a227d5fSAlan Cox { 416a227d5fSAlan Cox struct drm_device *dev = encoder->dev; 426a227d5fSAlan Cox u32 temp, reg; 436a227d5fSAlan Cox reg = ADPA; 446a227d5fSAlan Cox 456a227d5fSAlan Cox temp = REG_READ(reg); 466a227d5fSAlan Cox temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); 476a227d5fSAlan Cox temp &= ~ADPA_DAC_ENABLE; 486a227d5fSAlan Cox 496a227d5fSAlan Cox switch (mode) { 506a227d5fSAlan Cox case DRM_MODE_DPMS_ON: 516a227d5fSAlan Cox temp |= ADPA_DAC_ENABLE; 526a227d5fSAlan Cox break; 536a227d5fSAlan Cox case DRM_MODE_DPMS_STANDBY: 546a227d5fSAlan Cox temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE; 556a227d5fSAlan Cox break; 566a227d5fSAlan Cox case DRM_MODE_DPMS_SUSPEND: 576a227d5fSAlan Cox temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE; 586a227d5fSAlan Cox break; 596a227d5fSAlan Cox case DRM_MODE_DPMS_OFF: 606a227d5fSAlan Cox temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE; 616a227d5fSAlan Cox break; 626a227d5fSAlan Cox } 636a227d5fSAlan Cox 646a227d5fSAlan Cox REG_WRITE(reg, temp); 656a227d5fSAlan Cox } 666a227d5fSAlan Cox 676a227d5fSAlan Cox static int cdv_intel_crt_mode_valid(struct drm_connector *connector, 686a227d5fSAlan Cox struct drm_display_mode *mode) 696a227d5fSAlan Cox { 706a227d5fSAlan Cox if (mode->flags & DRM_MODE_FLAG_DBLSCAN) 716a227d5fSAlan Cox return MODE_NO_DBLESCAN; 726a227d5fSAlan Cox 736a227d5fSAlan Cox /* The lowest clock for CDV is 20000KHz */ 746a227d5fSAlan Cox if (mode->clock < 20000) 756a227d5fSAlan Cox return MODE_CLOCK_LOW; 766a227d5fSAlan Cox 776a227d5fSAlan Cox /* The max clock for CDV is 355 instead of 400 */ 78*b60bfb65SAlan Cox if (mode->clock > 355000) 796a227d5fSAlan Cox return MODE_CLOCK_HIGH; 806a227d5fSAlan Cox 816a227d5fSAlan Cox if (mode->hdisplay > 1680 || mode->vdisplay > 1050) 826a227d5fSAlan Cox return MODE_PANEL; 836a227d5fSAlan Cox 846a227d5fSAlan Cox return MODE_OK; 856a227d5fSAlan Cox } 866a227d5fSAlan Cox 876a227d5fSAlan Cox static bool cdv_intel_crt_mode_fixup(struct drm_encoder *encoder, 886a227d5fSAlan Cox struct drm_display_mode *mode, 896a227d5fSAlan Cox struct drm_display_mode *adjusted_mode) 906a227d5fSAlan Cox { 916a227d5fSAlan Cox return true; 926a227d5fSAlan Cox } 936a227d5fSAlan Cox 946a227d5fSAlan Cox static void cdv_intel_crt_mode_set(struct drm_encoder *encoder, 956a227d5fSAlan Cox struct drm_display_mode *mode, 966a227d5fSAlan Cox struct drm_display_mode *adjusted_mode) 976a227d5fSAlan Cox { 986a227d5fSAlan Cox 996a227d5fSAlan Cox struct drm_device *dev = encoder->dev; 1006a227d5fSAlan Cox struct drm_crtc *crtc = encoder->crtc; 1016a227d5fSAlan Cox struct psb_intel_crtc *psb_intel_crtc = 1026a227d5fSAlan Cox to_psb_intel_crtc(crtc); 1036a227d5fSAlan Cox int dpll_md_reg; 1046a227d5fSAlan Cox u32 adpa, dpll_md; 1056a227d5fSAlan Cox u32 adpa_reg; 1066a227d5fSAlan Cox 1076a227d5fSAlan Cox if (psb_intel_crtc->pipe == 0) 1086a227d5fSAlan Cox dpll_md_reg = DPLL_A_MD; 1096a227d5fSAlan Cox else 1106a227d5fSAlan Cox dpll_md_reg = DPLL_B_MD; 1116a227d5fSAlan Cox 1126a227d5fSAlan Cox adpa_reg = ADPA; 1136a227d5fSAlan Cox 1146a227d5fSAlan Cox /* 1156a227d5fSAlan Cox * Disable separate mode multiplier used when cloning SDVO to CRT 1166a227d5fSAlan Cox * XXX this needs to be adjusted when we really are cloning 1176a227d5fSAlan Cox */ 1186a227d5fSAlan Cox { 1196a227d5fSAlan Cox dpll_md = REG_READ(dpll_md_reg); 1206a227d5fSAlan Cox REG_WRITE(dpll_md_reg, 1216a227d5fSAlan Cox dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); 1226a227d5fSAlan Cox } 1236a227d5fSAlan Cox 1246a227d5fSAlan Cox adpa = 0; 1256a227d5fSAlan Cox if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) 1266a227d5fSAlan Cox adpa |= ADPA_HSYNC_ACTIVE_HIGH; 1276a227d5fSAlan Cox if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) 1286a227d5fSAlan Cox adpa |= ADPA_VSYNC_ACTIVE_HIGH; 1296a227d5fSAlan Cox 1306a227d5fSAlan Cox if (psb_intel_crtc->pipe == 0) 1316a227d5fSAlan Cox adpa |= ADPA_PIPE_A_SELECT; 1326a227d5fSAlan Cox else 1336a227d5fSAlan Cox adpa |= ADPA_PIPE_B_SELECT; 1346a227d5fSAlan Cox 1356a227d5fSAlan Cox REG_WRITE(adpa_reg, adpa); 1366a227d5fSAlan Cox } 1376a227d5fSAlan Cox 1386a227d5fSAlan Cox 1396a227d5fSAlan Cox /** 1406a227d5fSAlan Cox * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. 1416a227d5fSAlan Cox * 1426a227d5fSAlan Cox * \return true if CRT is connected. 1436a227d5fSAlan Cox * \return false if CRT is disconnected. 1446a227d5fSAlan Cox */ 1456a227d5fSAlan Cox static bool cdv_intel_crt_detect_hotplug(struct drm_connector *connector, 1466a227d5fSAlan Cox bool force) 1476a227d5fSAlan Cox { 1486a227d5fSAlan Cox struct drm_device *dev = connector->dev; 1496a227d5fSAlan Cox u32 hotplug_en; 1506a227d5fSAlan Cox int i, tries = 0, ret = false; 1516a227d5fSAlan Cox u32 adpa_orig; 1526a227d5fSAlan Cox 1536a227d5fSAlan Cox /* disable the DAC when doing the hotplug detection */ 1546a227d5fSAlan Cox 1556a227d5fSAlan Cox adpa_orig = REG_READ(ADPA); 1566a227d5fSAlan Cox 1576a227d5fSAlan Cox REG_WRITE(ADPA, adpa_orig & ~(ADPA_DAC_ENABLE)); 1586a227d5fSAlan Cox 1596a227d5fSAlan Cox /* 1606a227d5fSAlan Cox * On a CDV thep, CRT detect sequence need to be done twice 1616a227d5fSAlan Cox * to get a reliable result. 1626a227d5fSAlan Cox */ 1636a227d5fSAlan Cox tries = 2; 1646a227d5fSAlan Cox 1656a227d5fSAlan Cox hotplug_en = REG_READ(PORT_HOTPLUG_EN); 1666a227d5fSAlan Cox hotplug_en &= ~(CRT_HOTPLUG_DETECT_MASK); 1676a227d5fSAlan Cox hotplug_en |= CRT_HOTPLUG_FORCE_DETECT; 1686a227d5fSAlan Cox 1696a227d5fSAlan Cox hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64; 1706a227d5fSAlan Cox hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; 1716a227d5fSAlan Cox 1726a227d5fSAlan Cox for (i = 0; i < tries ; i++) { 1736a227d5fSAlan Cox unsigned long timeout; 1746a227d5fSAlan Cox /* turn on the FORCE_DETECT */ 1756a227d5fSAlan Cox REG_WRITE(PORT_HOTPLUG_EN, hotplug_en); 1766a227d5fSAlan Cox timeout = jiffies + msecs_to_jiffies(1000); 1776a227d5fSAlan Cox /* wait for FORCE_DETECT to go off */ 1786a227d5fSAlan Cox do { 1796a227d5fSAlan Cox if (!(REG_READ(PORT_HOTPLUG_EN) & 1806a227d5fSAlan Cox CRT_HOTPLUG_FORCE_DETECT)) 1816a227d5fSAlan Cox break; 1826a227d5fSAlan Cox msleep(1); 1836a227d5fSAlan Cox } while (time_after(timeout, jiffies)); 1846a227d5fSAlan Cox } 1856a227d5fSAlan Cox 1866a227d5fSAlan Cox if ((REG_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) != 1876a227d5fSAlan Cox CRT_HOTPLUG_MONITOR_NONE) 1886a227d5fSAlan Cox ret = true; 1896a227d5fSAlan Cox 1906a227d5fSAlan Cox /* Restore the saved ADPA */ 1916a227d5fSAlan Cox REG_WRITE(ADPA, adpa_orig); 1926a227d5fSAlan Cox return ret; 1936a227d5fSAlan Cox } 1946a227d5fSAlan Cox 1956a227d5fSAlan Cox static enum drm_connector_status cdv_intel_crt_detect( 1966a227d5fSAlan Cox struct drm_connector *connector, bool force) 1976a227d5fSAlan Cox { 1986a227d5fSAlan Cox if (cdv_intel_crt_detect_hotplug(connector, force)) 1996a227d5fSAlan Cox return connector_status_connected; 2006a227d5fSAlan Cox else 2016a227d5fSAlan Cox return connector_status_disconnected; 2026a227d5fSAlan Cox } 2036a227d5fSAlan Cox 2046a227d5fSAlan Cox static void cdv_intel_crt_destroy(struct drm_connector *connector) 2056a227d5fSAlan Cox { 206a12d6a07SPatrik Jakobsson struct psb_intel_encoder *psb_intel_encoder = 207a12d6a07SPatrik Jakobsson psb_intel_attached_encoder(connector); 2086a227d5fSAlan Cox 209a12d6a07SPatrik Jakobsson psb_intel_i2c_destroy(psb_intel_encoder->ddc_bus); 2106a227d5fSAlan Cox drm_sysfs_connector_remove(connector); 2116a227d5fSAlan Cox drm_connector_cleanup(connector); 2126a227d5fSAlan Cox kfree(connector); 2136a227d5fSAlan Cox } 2146a227d5fSAlan Cox 2156a227d5fSAlan Cox static int cdv_intel_crt_get_modes(struct drm_connector *connector) 2166a227d5fSAlan Cox { 217a12d6a07SPatrik Jakobsson struct psb_intel_encoder *psb_intel_encoder = 218a12d6a07SPatrik Jakobsson psb_intel_attached_encoder(connector); 219a12d6a07SPatrik Jakobsson return psb_intel_ddc_get_modes(connector, &psb_intel_encoder->ddc_bus->adapter); 2206a227d5fSAlan Cox } 2216a227d5fSAlan Cox 2226a227d5fSAlan Cox static int cdv_intel_crt_set_property(struct drm_connector *connector, 2236a227d5fSAlan Cox struct drm_property *property, 2246a227d5fSAlan Cox uint64_t value) 2256a227d5fSAlan Cox { 2266a227d5fSAlan Cox return 0; 2276a227d5fSAlan Cox } 2286a227d5fSAlan Cox 2296a227d5fSAlan Cox /* 2306a227d5fSAlan Cox * Routines for controlling stuff on the analog port 2316a227d5fSAlan Cox */ 2326a227d5fSAlan Cox 2336a227d5fSAlan Cox static const struct drm_encoder_helper_funcs cdv_intel_crt_helper_funcs = { 2346a227d5fSAlan Cox .dpms = cdv_intel_crt_dpms, 2356a227d5fSAlan Cox .mode_fixup = cdv_intel_crt_mode_fixup, 2366a227d5fSAlan Cox .prepare = psb_intel_encoder_prepare, 2376a227d5fSAlan Cox .commit = psb_intel_encoder_commit, 2386a227d5fSAlan Cox .mode_set = cdv_intel_crt_mode_set, 2396a227d5fSAlan Cox }; 2406a227d5fSAlan Cox 2416a227d5fSAlan Cox static const struct drm_connector_funcs cdv_intel_crt_connector_funcs = { 2426a227d5fSAlan Cox .dpms = drm_helper_connector_dpms, 2436a227d5fSAlan Cox .detect = cdv_intel_crt_detect, 2446a227d5fSAlan Cox .fill_modes = drm_helper_probe_single_connector_modes, 2456a227d5fSAlan Cox .destroy = cdv_intel_crt_destroy, 2466a227d5fSAlan Cox .set_property = cdv_intel_crt_set_property, 2476a227d5fSAlan Cox }; 2486a227d5fSAlan Cox 2496a227d5fSAlan Cox static const struct drm_connector_helper_funcs 2506a227d5fSAlan Cox cdv_intel_crt_connector_helper_funcs = { 2516a227d5fSAlan Cox .mode_valid = cdv_intel_crt_mode_valid, 2526a227d5fSAlan Cox .get_modes = cdv_intel_crt_get_modes, 2536a227d5fSAlan Cox .best_encoder = psb_intel_best_encoder, 2546a227d5fSAlan Cox }; 2556a227d5fSAlan Cox 2566a227d5fSAlan Cox static void cdv_intel_crt_enc_destroy(struct drm_encoder *encoder) 2576a227d5fSAlan Cox { 2586a227d5fSAlan Cox drm_encoder_cleanup(encoder); 2596a227d5fSAlan Cox } 2606a227d5fSAlan Cox 2616a227d5fSAlan Cox static const struct drm_encoder_funcs cdv_intel_crt_enc_funcs = { 2626a227d5fSAlan Cox .destroy = cdv_intel_crt_enc_destroy, 2636a227d5fSAlan Cox }; 2646a227d5fSAlan Cox 2656a227d5fSAlan Cox void cdv_intel_crt_init(struct drm_device *dev, 2666a227d5fSAlan Cox struct psb_intel_mode_device *mode_dev) 2676a227d5fSAlan Cox { 2686a227d5fSAlan Cox 269a12d6a07SPatrik Jakobsson struct psb_intel_connector *psb_intel_connector; 270a12d6a07SPatrik Jakobsson struct psb_intel_encoder *psb_intel_encoder; 2716a227d5fSAlan Cox struct drm_connector *connector; 2726a227d5fSAlan Cox struct drm_encoder *encoder; 2736a227d5fSAlan Cox 2746a227d5fSAlan Cox u32 i2c_reg; 2756a227d5fSAlan Cox 276a12d6a07SPatrik Jakobsson psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder), GFP_KERNEL); 277a12d6a07SPatrik Jakobsson if (!psb_intel_encoder) 2786a227d5fSAlan Cox return; 2796a227d5fSAlan Cox 280a12d6a07SPatrik Jakobsson psb_intel_connector = kzalloc(sizeof(struct psb_intel_connector), GFP_KERNEL); 281a12d6a07SPatrik Jakobsson if (!psb_intel_connector) 282a12d6a07SPatrik Jakobsson goto failed_connector; 283a12d6a07SPatrik Jakobsson 284a12d6a07SPatrik Jakobsson connector = &psb_intel_connector->base; 2856a227d5fSAlan Cox drm_connector_init(dev, connector, 2866a227d5fSAlan Cox &cdv_intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); 2876a227d5fSAlan Cox 288a12d6a07SPatrik Jakobsson encoder = &psb_intel_encoder->base; 2896a227d5fSAlan Cox drm_encoder_init(dev, encoder, 2906a227d5fSAlan Cox &cdv_intel_crt_enc_funcs, DRM_MODE_ENCODER_DAC); 2916a227d5fSAlan Cox 292a12d6a07SPatrik Jakobsson psb_intel_connector_attach_encoder(psb_intel_connector, 293a12d6a07SPatrik Jakobsson psb_intel_encoder); 2946a227d5fSAlan Cox 2956a227d5fSAlan Cox /* Set up the DDC bus. */ 2966a227d5fSAlan Cox i2c_reg = GPIOA; 2976a227d5fSAlan Cox /* Remove the following code for CDV */ 2986a227d5fSAlan Cox /* 2996a227d5fSAlan Cox if (dev_priv->crt_ddc_bus != 0) 3006a227d5fSAlan Cox i2c_reg = dev_priv->crt_ddc_bus; 3016a227d5fSAlan Cox }*/ 302a12d6a07SPatrik Jakobsson psb_intel_encoder->ddc_bus = psb_intel_i2c_create(dev, 3036a227d5fSAlan Cox i2c_reg, "CRTDDC_A"); 304a12d6a07SPatrik Jakobsson if (!psb_intel_encoder->ddc_bus) { 3056a227d5fSAlan Cox dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " 3066a227d5fSAlan Cox "failed.\n"); 3076a227d5fSAlan Cox goto failed_ddc; 3086a227d5fSAlan Cox } 3096a227d5fSAlan Cox 310a12d6a07SPatrik Jakobsson psb_intel_encoder->type = INTEL_OUTPUT_ANALOG; 3116a227d5fSAlan Cox /* 3126a227d5fSAlan Cox psb_intel_output->clone_mask = (1 << INTEL_ANALOG_CLONE_BIT); 3136a227d5fSAlan Cox psb_intel_output->crtc_mask = (1 << 0) | (1 << 1); 3146a227d5fSAlan Cox */ 3156a227d5fSAlan Cox connector->interlace_allowed = 0; 3166a227d5fSAlan Cox connector->doublescan_allowed = 0; 3176a227d5fSAlan Cox 3186a227d5fSAlan Cox drm_encoder_helper_add(encoder, &cdv_intel_crt_helper_funcs); 3196a227d5fSAlan Cox drm_connector_helper_add(connector, 3206a227d5fSAlan Cox &cdv_intel_crt_connector_helper_funcs); 3216a227d5fSAlan Cox 3226a227d5fSAlan Cox drm_sysfs_connector_add(connector); 3236a227d5fSAlan Cox 3246a227d5fSAlan Cox return; 3256a227d5fSAlan Cox failed_ddc: 326a12d6a07SPatrik Jakobsson drm_encoder_cleanup(&psb_intel_encoder->base); 327a12d6a07SPatrik Jakobsson drm_connector_cleanup(&psb_intel_connector->base); 328a12d6a07SPatrik Jakobsson kfree(psb_intel_connector); 329a12d6a07SPatrik Jakobsson failed_connector: 330a12d6a07SPatrik Jakobsson kfree(psb_intel_encoder); 3316a227d5fSAlan Cox return; 3326a227d5fSAlan Cox } 333