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 27*0c7b178aSSam Ravnborg #include <linux/delay.h> 286a227d5fSAlan Cox #include <linux/i2c.h> 29*0c7b178aSSam Ravnborg #include <linux/pm_runtime.h> 306a227d5fSAlan Cox 31*0c7b178aSSam Ravnborg #include "cdv_device.h" 326a227d5fSAlan Cox #include "intel_bios.h" 33*0c7b178aSSam Ravnborg #include "power.h" 346a227d5fSAlan Cox #include "psb_drv.h" 356a227d5fSAlan Cox #include "psb_intel_drv.h" 366a227d5fSAlan Cox #include "psb_intel_reg.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 6767772782SLuc Van Oostenryck static enum drm_mode_status 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 */ 78b60bfb65SAlan Cox if (mode->clock > 355000) 796a227d5fSAlan Cox return MODE_CLOCK_HIGH; 806a227d5fSAlan Cox 816a227d5fSAlan Cox return MODE_OK; 826a227d5fSAlan Cox } 836a227d5fSAlan Cox 846a227d5fSAlan Cox static void cdv_intel_crt_mode_set(struct drm_encoder *encoder, 856a227d5fSAlan Cox struct drm_display_mode *mode, 866a227d5fSAlan Cox struct drm_display_mode *adjusted_mode) 876a227d5fSAlan Cox { 886a227d5fSAlan Cox 896a227d5fSAlan Cox struct drm_device *dev = encoder->dev; 906a227d5fSAlan Cox struct drm_crtc *crtc = encoder->crtc; 916306865dSPatrik Jakobsson struct gma_crtc *gma_crtc = to_gma_crtc(crtc); 926a227d5fSAlan Cox int dpll_md_reg; 936a227d5fSAlan Cox u32 adpa, dpll_md; 946a227d5fSAlan Cox u32 adpa_reg; 956a227d5fSAlan Cox 966306865dSPatrik Jakobsson if (gma_crtc->pipe == 0) 976a227d5fSAlan Cox dpll_md_reg = DPLL_A_MD; 986a227d5fSAlan Cox else 996a227d5fSAlan Cox dpll_md_reg = DPLL_B_MD; 1006a227d5fSAlan Cox 1016a227d5fSAlan Cox adpa_reg = ADPA; 1026a227d5fSAlan Cox 1036a227d5fSAlan Cox /* 1046a227d5fSAlan Cox * Disable separate mode multiplier used when cloning SDVO to CRT 1056a227d5fSAlan Cox * XXX this needs to be adjusted when we really are cloning 1066a227d5fSAlan Cox */ 1076a227d5fSAlan Cox { 1086a227d5fSAlan Cox dpll_md = REG_READ(dpll_md_reg); 1096a227d5fSAlan Cox REG_WRITE(dpll_md_reg, 1106a227d5fSAlan Cox dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK); 1116a227d5fSAlan Cox } 1126a227d5fSAlan Cox 1136a227d5fSAlan Cox adpa = 0; 1146a227d5fSAlan Cox if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) 1156a227d5fSAlan Cox adpa |= ADPA_HSYNC_ACTIVE_HIGH; 1166a227d5fSAlan Cox if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) 1176a227d5fSAlan Cox adpa |= ADPA_VSYNC_ACTIVE_HIGH; 1186a227d5fSAlan Cox 1196306865dSPatrik Jakobsson if (gma_crtc->pipe == 0) 1206a227d5fSAlan Cox adpa |= ADPA_PIPE_A_SELECT; 1216a227d5fSAlan Cox else 1226a227d5fSAlan Cox adpa |= ADPA_PIPE_B_SELECT; 1236a227d5fSAlan Cox 1246a227d5fSAlan Cox REG_WRITE(adpa_reg, adpa); 1256a227d5fSAlan Cox } 1266a227d5fSAlan Cox 1276a227d5fSAlan Cox 1286a227d5fSAlan Cox /** 1296a227d5fSAlan Cox * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence. 1306a227d5fSAlan Cox * 1316a227d5fSAlan Cox * \return true if CRT is connected. 1326a227d5fSAlan Cox * \return false if CRT is disconnected. 1336a227d5fSAlan Cox */ 1346a227d5fSAlan Cox static bool cdv_intel_crt_detect_hotplug(struct drm_connector *connector, 1356a227d5fSAlan Cox bool force) 1366a227d5fSAlan Cox { 1376a227d5fSAlan Cox struct drm_device *dev = connector->dev; 1386a227d5fSAlan Cox u32 hotplug_en; 1396a227d5fSAlan Cox int i, tries = 0, ret = false; 140d235e64aSAlan Cox u32 orig; 1416a227d5fSAlan Cox 1426a227d5fSAlan Cox /* 1436a227d5fSAlan Cox * On a CDV thep, CRT detect sequence need to be done twice 1446a227d5fSAlan Cox * to get a reliable result. 1456a227d5fSAlan Cox */ 1466a227d5fSAlan Cox tries = 2; 1476a227d5fSAlan Cox 148d235e64aSAlan Cox orig = hotplug_en = REG_READ(PORT_HOTPLUG_EN); 1496a227d5fSAlan Cox hotplug_en &= ~(CRT_HOTPLUG_DETECT_MASK); 1506a227d5fSAlan Cox hotplug_en |= CRT_HOTPLUG_FORCE_DETECT; 1516a227d5fSAlan Cox 1526a227d5fSAlan Cox hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64; 1536a227d5fSAlan Cox hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50; 1546a227d5fSAlan Cox 1556a227d5fSAlan Cox for (i = 0; i < tries ; i++) { 1566a227d5fSAlan Cox unsigned long timeout; 1576a227d5fSAlan Cox /* turn on the FORCE_DETECT */ 1586a227d5fSAlan Cox REG_WRITE(PORT_HOTPLUG_EN, hotplug_en); 1596a227d5fSAlan Cox timeout = jiffies + msecs_to_jiffies(1000); 1606a227d5fSAlan Cox /* wait for FORCE_DETECT to go off */ 1616a227d5fSAlan Cox do { 1626a227d5fSAlan Cox if (!(REG_READ(PORT_HOTPLUG_EN) & 1636a227d5fSAlan Cox CRT_HOTPLUG_FORCE_DETECT)) 1646a227d5fSAlan Cox break; 1656a227d5fSAlan Cox msleep(1); 1666a227d5fSAlan Cox } while (time_after(timeout, jiffies)); 1676a227d5fSAlan Cox } 1686a227d5fSAlan Cox 1696a227d5fSAlan Cox if ((REG_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) != 1706a227d5fSAlan Cox CRT_HOTPLUG_MONITOR_NONE) 1716a227d5fSAlan Cox ret = true; 1726a227d5fSAlan Cox 173d235e64aSAlan Cox /* clear the interrupt we just generated, if any */ 174d235e64aSAlan Cox REG_WRITE(PORT_HOTPLUG_STAT, CRT_HOTPLUG_INT_STATUS); 175d235e64aSAlan Cox 176d235e64aSAlan Cox /* and put the bits back */ 177d235e64aSAlan Cox REG_WRITE(PORT_HOTPLUG_EN, orig); 1786a227d5fSAlan Cox return ret; 1796a227d5fSAlan Cox } 1806a227d5fSAlan Cox 1816a227d5fSAlan Cox static enum drm_connector_status cdv_intel_crt_detect( 1826a227d5fSAlan Cox struct drm_connector *connector, bool force) 1836a227d5fSAlan Cox { 1846a227d5fSAlan Cox if (cdv_intel_crt_detect_hotplug(connector, force)) 1856a227d5fSAlan Cox return connector_status_connected; 1866a227d5fSAlan Cox else 1876a227d5fSAlan Cox return connector_status_disconnected; 1886a227d5fSAlan Cox } 1896a227d5fSAlan Cox 1906a227d5fSAlan Cox static void cdv_intel_crt_destroy(struct drm_connector *connector) 1916a227d5fSAlan Cox { 192367e4408SPatrik Jakobsson struct gma_encoder *gma_encoder = gma_attached_encoder(connector); 1936a227d5fSAlan Cox 194367e4408SPatrik Jakobsson psb_intel_i2c_destroy(gma_encoder->ddc_bus); 19534ea3d38SThomas Wood drm_connector_unregister(connector); 1966a227d5fSAlan Cox drm_connector_cleanup(connector); 1976a227d5fSAlan Cox kfree(connector); 1986a227d5fSAlan Cox } 1996a227d5fSAlan Cox 2006a227d5fSAlan Cox static int cdv_intel_crt_get_modes(struct drm_connector *connector) 2016a227d5fSAlan Cox { 202367e4408SPatrik Jakobsson struct gma_encoder *gma_encoder = gma_attached_encoder(connector); 203367e4408SPatrik Jakobsson return psb_intel_ddc_get_modes(connector, 204367e4408SPatrik Jakobsson &gma_encoder->ddc_bus->adapter); 2056a227d5fSAlan Cox } 2066a227d5fSAlan Cox 2076a227d5fSAlan Cox static int cdv_intel_crt_set_property(struct drm_connector *connector, 2086a227d5fSAlan Cox struct drm_property *property, 2096a227d5fSAlan Cox uint64_t value) 2106a227d5fSAlan Cox { 2116a227d5fSAlan Cox return 0; 2126a227d5fSAlan Cox } 2136a227d5fSAlan Cox 2146a227d5fSAlan Cox /* 2156a227d5fSAlan Cox * Routines for controlling stuff on the analog port 2166a227d5fSAlan Cox */ 2176a227d5fSAlan Cox 2186a227d5fSAlan Cox static const struct drm_encoder_helper_funcs cdv_intel_crt_helper_funcs = { 2196a227d5fSAlan Cox .dpms = cdv_intel_crt_dpms, 220c9d49590SPatrik Jakobsson .prepare = gma_encoder_prepare, 221c9d49590SPatrik Jakobsson .commit = gma_encoder_commit, 2226a227d5fSAlan Cox .mode_set = cdv_intel_crt_mode_set, 2236a227d5fSAlan Cox }; 2246a227d5fSAlan Cox 2256a227d5fSAlan Cox static const struct drm_connector_funcs cdv_intel_crt_connector_funcs = { 2266a227d5fSAlan Cox .dpms = drm_helper_connector_dpms, 2276a227d5fSAlan Cox .detect = cdv_intel_crt_detect, 2286a227d5fSAlan Cox .fill_modes = drm_helper_probe_single_connector_modes, 2296a227d5fSAlan Cox .destroy = cdv_intel_crt_destroy, 2306a227d5fSAlan Cox .set_property = cdv_intel_crt_set_property, 2316a227d5fSAlan Cox }; 2326a227d5fSAlan Cox 2336a227d5fSAlan Cox static const struct drm_connector_helper_funcs 2346a227d5fSAlan Cox cdv_intel_crt_connector_helper_funcs = { 2356a227d5fSAlan Cox .mode_valid = cdv_intel_crt_mode_valid, 2366a227d5fSAlan Cox .get_modes = cdv_intel_crt_get_modes, 237c9d49590SPatrik Jakobsson .best_encoder = gma_best_encoder, 2386a227d5fSAlan Cox }; 2396a227d5fSAlan Cox 2406a227d5fSAlan Cox static void cdv_intel_crt_enc_destroy(struct drm_encoder *encoder) 2416a227d5fSAlan Cox { 2426a227d5fSAlan Cox drm_encoder_cleanup(encoder); 2436a227d5fSAlan Cox } 2446a227d5fSAlan Cox 2456a227d5fSAlan Cox static const struct drm_encoder_funcs cdv_intel_crt_enc_funcs = { 2466a227d5fSAlan Cox .destroy = cdv_intel_crt_enc_destroy, 2476a227d5fSAlan Cox }; 2486a227d5fSAlan Cox 2496a227d5fSAlan Cox void cdv_intel_crt_init(struct drm_device *dev, 2506a227d5fSAlan Cox struct psb_intel_mode_device *mode_dev) 2516a227d5fSAlan Cox { 2526a227d5fSAlan Cox 253a3d5d75fSPatrik Jakobsson struct gma_connector *gma_connector; 254367e4408SPatrik Jakobsson struct gma_encoder *gma_encoder; 2556a227d5fSAlan Cox struct drm_connector *connector; 2566a227d5fSAlan Cox struct drm_encoder *encoder; 2576a227d5fSAlan Cox 2586a227d5fSAlan Cox u32 i2c_reg; 2596a227d5fSAlan Cox 260367e4408SPatrik Jakobsson gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); 261367e4408SPatrik Jakobsson if (!gma_encoder) 2626a227d5fSAlan Cox return; 2636a227d5fSAlan Cox 264a3d5d75fSPatrik Jakobsson gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); 265a3d5d75fSPatrik Jakobsson if (!gma_connector) 266a12d6a07SPatrik Jakobsson goto failed_connector; 267a12d6a07SPatrik Jakobsson 268a3d5d75fSPatrik Jakobsson connector = &gma_connector->base; 269bda50031SKero van Gelder connector->polled = DRM_CONNECTOR_POLL_HPD; 2706a227d5fSAlan Cox drm_connector_init(dev, connector, 2716a227d5fSAlan Cox &cdv_intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); 2726a227d5fSAlan Cox 273367e4408SPatrik Jakobsson encoder = &gma_encoder->base; 2746a227d5fSAlan Cox drm_encoder_init(dev, encoder, 27513a3d91fSVille Syrjälä &cdv_intel_crt_enc_funcs, DRM_MODE_ENCODER_DAC, NULL); 2766a227d5fSAlan Cox 277367e4408SPatrik Jakobsson gma_connector_attach_encoder(gma_connector, gma_encoder); 2786a227d5fSAlan Cox 2796a227d5fSAlan Cox /* Set up the DDC bus. */ 2806a227d5fSAlan Cox i2c_reg = GPIOA; 2816a227d5fSAlan Cox /* Remove the following code for CDV */ 2826a227d5fSAlan Cox /* 2836a227d5fSAlan Cox if (dev_priv->crt_ddc_bus != 0) 2846a227d5fSAlan Cox i2c_reg = dev_priv->crt_ddc_bus; 2856a227d5fSAlan Cox }*/ 286367e4408SPatrik Jakobsson gma_encoder->ddc_bus = psb_intel_i2c_create(dev, 2876a227d5fSAlan Cox i2c_reg, "CRTDDC_A"); 288367e4408SPatrik Jakobsson if (!gma_encoder->ddc_bus) { 2896a227d5fSAlan Cox dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " 2906a227d5fSAlan Cox "failed.\n"); 2916a227d5fSAlan Cox goto failed_ddc; 2926a227d5fSAlan Cox } 2936a227d5fSAlan Cox 294367e4408SPatrik Jakobsson gma_encoder->type = INTEL_OUTPUT_ANALOG; 2956a227d5fSAlan Cox /* 2966a227d5fSAlan Cox psb_intel_output->clone_mask = (1 << INTEL_ANALOG_CLONE_BIT); 2976a227d5fSAlan Cox psb_intel_output->crtc_mask = (1 << 0) | (1 << 1); 2986a227d5fSAlan Cox */ 2996a227d5fSAlan Cox connector->interlace_allowed = 0; 3006a227d5fSAlan Cox connector->doublescan_allowed = 0; 3016a227d5fSAlan Cox 3026a227d5fSAlan Cox drm_encoder_helper_add(encoder, &cdv_intel_crt_helper_funcs); 3036a227d5fSAlan Cox drm_connector_helper_add(connector, 3046a227d5fSAlan Cox &cdv_intel_crt_connector_helper_funcs); 3056a227d5fSAlan Cox 30634ea3d38SThomas Wood drm_connector_register(connector); 3076a227d5fSAlan Cox 3086a227d5fSAlan Cox return; 3096a227d5fSAlan Cox failed_ddc: 310367e4408SPatrik Jakobsson drm_encoder_cleanup(&gma_encoder->base); 311a3d5d75fSPatrik Jakobsson drm_connector_cleanup(&gma_connector->base); 312a3d5d75fSPatrik Jakobsson kfree(gma_connector); 313a12d6a07SPatrik Jakobsson failed_connector: 314367e4408SPatrik Jakobsson kfree(gma_encoder); 3156a227d5fSAlan Cox return; 3166a227d5fSAlan Cox } 317