Path: blob/master/drivers/gpu/drm/i915/dvo_ch7xxx.c
15112 views
/**************************************************************************12Copyright © 2006 Dave Airlie34All Rights Reserved.56Permission is hereby granted, free of charge, to any person obtaining a7copy of this software and associated documentation files (the8"Software"), to deal in the Software without restriction, including9without limitation the rights to use, copy, modify, merge, publish,10distribute, sub license, and/or sell copies of the Software, and to11permit persons to whom the Software is furnished to do so, subject to12the following conditions:1314The above copyright notice and this permission notice (including the15next paragraph) shall be included in all copies or substantial portions16of the Software.1718THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS19OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.21IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR22ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,23TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE24SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.2526**************************************************************************/2728#include "dvo.h"2930#define CH7xxx_REG_VID 0x4a31#define CH7xxx_REG_DID 0x4b3233#define CH7011_VID 0x83 /* 7010 as well */34#define CH7009A_VID 0x8435#define CH7009B_VID 0x8536#define CH7301_VID 0x953738#define CH7xxx_VID 0x8439#define CH7xxx_DID 0x174041#define CH7xxx_NUM_REGS 0x4c4243#define CH7xxx_CM 0x1c44#define CH7xxx_CM_XCM (1<<0)45#define CH7xxx_CM_MCP (1<<2)46#define CH7xxx_INPUT_CLOCK 0x1d47#define CH7xxx_GPIO 0x1e48#define CH7xxx_GPIO_HPIR (1<<3)49#define CH7xxx_IDF 0x1f5051#define CH7xxx_IDF_HSP (1<<3)52#define CH7xxx_IDF_VSP (1<<4)5354#define CH7xxx_CONNECTION_DETECT 0x2055#define CH7xxx_CDET_DVI (1<<5)5657#define CH7301_DAC_CNTL 0x2158#define CH7301_HOTPLUG 0x2359#define CH7xxx_TCTL 0x3160#define CH7xxx_TVCO 0x3261#define CH7xxx_TPCP 0x3362#define CH7xxx_TPD 0x3463#define CH7xxx_TPVT 0x3564#define CH7xxx_TLPF 0x3665#define CH7xxx_TCT 0x3766#define CH7301_TEST_PATTERN 0x486768#define CH7xxx_PM 0x4969#define CH7xxx_PM_FPD (1<<0)70#define CH7301_PM_DACPD0 (1<<1)71#define CH7301_PM_DACPD1 (1<<2)72#define CH7301_PM_DACPD2 (1<<3)73#define CH7xxx_PM_DVIL (1<<6)74#define CH7xxx_PM_DVIP (1<<7)7576#define CH7301_SYNC_POLARITY 0x5677#define CH7301_SYNC_RGB_YUV (1<<0)78#define CH7301_SYNC_POL_DVI (1<<5)7980/** @file81* driver for the Chrontel 7xxx DVI chip over DVO.82*/8384static struct ch7xxx_id_struct {85uint8_t vid;86char *name;87} ch7xxx_ids[] = {88{ CH7011_VID, "CH7011" },89{ CH7009A_VID, "CH7009A" },90{ CH7009B_VID, "CH7009B" },91{ CH7301_VID, "CH7301" },92};9394struct ch7xxx_priv {95bool quiet;96};9798static char *ch7xxx_get_id(uint8_t vid)99{100int i;101102for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) {103if (ch7xxx_ids[i].vid == vid)104return ch7xxx_ids[i].name;105}106107return NULL;108}109110/** Reads an 8 bit register */111static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)112{113struct ch7xxx_priv *ch7xxx= dvo->dev_priv;114struct i2c_adapter *adapter = dvo->i2c_bus;115u8 out_buf[2];116u8 in_buf[2];117118struct i2c_msg msgs[] = {119{120.addr = dvo->slave_addr,121.flags = 0,122.len = 1,123.buf = out_buf,124},125{126.addr = dvo->slave_addr,127.flags = I2C_M_RD,128.len = 1,129.buf = in_buf,130}131};132133out_buf[0] = addr;134out_buf[1] = 0;135136if (i2c_transfer(adapter, msgs, 2) == 2) {137*ch = in_buf[0];138return true;139};140141if (!ch7xxx->quiet) {142DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",143addr, adapter->name, dvo->slave_addr);144}145return false;146}147148/** Writes an 8 bit register */149static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)150{151struct ch7xxx_priv *ch7xxx = dvo->dev_priv;152struct i2c_adapter *adapter = dvo->i2c_bus;153uint8_t out_buf[2];154struct i2c_msg msg = {155.addr = dvo->slave_addr,156.flags = 0,157.len = 2,158.buf = out_buf,159};160161out_buf[0] = addr;162out_buf[1] = ch;163164if (i2c_transfer(adapter, &msg, 1) == 1)165return true;166167if (!ch7xxx->quiet) {168DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",169addr, adapter->name, dvo->slave_addr);170}171172return false;173}174175static bool ch7xxx_init(struct intel_dvo_device *dvo,176struct i2c_adapter *adapter)177{178/* this will detect the CH7xxx chip on the specified i2c bus */179struct ch7xxx_priv *ch7xxx;180uint8_t vendor, device;181char *name;182183ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL);184if (ch7xxx == NULL)185return false;186187dvo->i2c_bus = adapter;188dvo->dev_priv = ch7xxx;189ch7xxx->quiet = true;190191if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor))192goto out;193194name = ch7xxx_get_id(vendor);195if (!name) {196DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "197"slave %d.\n",198vendor, adapter->name, dvo->slave_addr);199goto out;200}201202203if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device))204goto out;205206if (device != CH7xxx_DID) {207DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "208"slave %d.\n",209vendor, adapter->name, dvo->slave_addr);210goto out;211}212213ch7xxx->quiet = false;214DRM_DEBUG_KMS("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",215name, vendor, device);216return true;217out:218kfree(ch7xxx);219return false;220}221222static enum drm_connector_status ch7xxx_detect(struct intel_dvo_device *dvo)223{224uint8_t cdet, orig_pm, pm;225226ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm);227228pm = orig_pm;229pm &= ~CH7xxx_PM_FPD;230pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP;231232ch7xxx_writeb(dvo, CH7xxx_PM, pm);233234ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet);235236ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm);237238if (cdet & CH7xxx_CDET_DVI)239return connector_status_connected;240return connector_status_disconnected;241}242243static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo,244struct drm_display_mode *mode)245{246if (mode->clock > 165000)247return MODE_CLOCK_HIGH;248249return MODE_OK;250}251252static void ch7xxx_mode_set(struct intel_dvo_device *dvo,253struct drm_display_mode *mode,254struct drm_display_mode *adjusted_mode)255{256uint8_t tvco, tpcp, tpd, tlpf, idf;257258if (mode->clock <= 65000) {259tvco = 0x23;260tpcp = 0x08;261tpd = 0x16;262tlpf = 0x60;263} else {264tvco = 0x2d;265tpcp = 0x06;266tpd = 0x26;267tlpf = 0xa0;268}269270ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00);271ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco);272ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp);273ch7xxx_writeb(dvo, CH7xxx_TPD, tpd);274ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30);275ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf);276ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00);277278ch7xxx_readb(dvo, CH7xxx_IDF, &idf);279280idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP);281if (mode->flags & DRM_MODE_FLAG_PHSYNC)282idf |= CH7xxx_IDF_HSP;283284if (mode->flags & DRM_MODE_FLAG_PVSYNC)285idf |= CH7xxx_IDF_HSP;286287ch7xxx_writeb(dvo, CH7xxx_IDF, idf);288}289290/* set the CH7xxx power state */291static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode)292{293if (mode == DRM_MODE_DPMS_ON)294ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);295else296ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);297}298299static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)300{301int i;302303for (i = 0; i < CH7xxx_NUM_REGS; i++) {304uint8_t val;305if ((i % 8) == 0 )306DRM_LOG_KMS("\n %02X: ", i);307ch7xxx_readb(dvo, i, &val);308DRM_LOG_KMS("%02X ", val);309}310}311312static void ch7xxx_destroy(struct intel_dvo_device *dvo)313{314struct ch7xxx_priv *ch7xxx = dvo->dev_priv;315316if (ch7xxx) {317kfree(ch7xxx);318dvo->dev_priv = NULL;319}320}321322struct intel_dvo_dev_ops ch7xxx_ops = {323.init = ch7xxx_init,324.detect = ch7xxx_detect,325.mode_valid = ch7xxx_mode_valid,326.mode_set = ch7xxx_mode_set,327.dpms = ch7xxx_dpms,328.dump_regs = ch7xxx_dump_regs,329.destroy = ch7xxx_destroy,330};331332333