linux/drivers/gpu/drm/i915/dvo_ch7xxx.c
<<
>>
Prefs
   1/**************************************************************************
   2
   3Copyright © 2006 Dave Airlie
   4
   5All Rights Reserved.
   6
   7Permission is hereby granted, free of charge, to any person obtaining a
   8copy of this software and associated documentation files (the
   9"Software"), to deal in the Software without restriction, including
  10without limitation the rights to use, copy, modify, merge, publish,
  11distribute, sub license, and/or sell copies of the Software, and to
  12permit persons to whom the Software is furnished to do so, subject to
  13the following conditions:
  14
  15The above copyright notice and this permission notice (including the
  16next paragraph) shall be included in all copies or substantial portions
  17of the Software.
  18
  19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  20OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
  22IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  23ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  24TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  25SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  26
  27**************************************************************************/
  28
  29#include "dvo.h"
  30
  31#define CH7xxx_REG_VID          0x4a
  32#define CH7xxx_REG_DID          0x4b
  33
  34#define CH7011_VID              0x83 /* 7010 as well */
  35#define CH7009A_VID             0x84
  36#define CH7009B_VID             0x85
  37#define CH7301_VID              0x95
  38
  39#define CH7xxx_VID              0x84
  40#define CH7xxx_DID              0x17
  41
  42#define CH7xxx_NUM_REGS         0x4c
  43
  44#define CH7xxx_CM               0x1c
  45#define CH7xxx_CM_XCM           (1<<0)
  46#define CH7xxx_CM_MCP           (1<<2)
  47#define CH7xxx_INPUT_CLOCK      0x1d
  48#define CH7xxx_GPIO             0x1e
  49#define CH7xxx_GPIO_HPIR        (1<<3)
  50#define CH7xxx_IDF              0x1f
  51
  52#define CH7xxx_IDF_HSP          (1<<3)
  53#define CH7xxx_IDF_VSP          (1<<4)
  54
  55#define CH7xxx_CONNECTION_DETECT 0x20
  56#define CH7xxx_CDET_DVI         (1<<5)
  57
  58#define CH7301_DAC_CNTL         0x21
  59#define CH7301_HOTPLUG          0x23
  60#define CH7xxx_TCTL             0x31
  61#define CH7xxx_TVCO             0x32
  62#define CH7xxx_TPCP             0x33
  63#define CH7xxx_TPD              0x34
  64#define CH7xxx_TPVT             0x35
  65#define CH7xxx_TLPF             0x36
  66#define CH7xxx_TCT              0x37
  67#define CH7301_TEST_PATTERN     0x48
  68
  69#define CH7xxx_PM               0x49
  70#define CH7xxx_PM_FPD           (1<<0)
  71#define CH7301_PM_DACPD0        (1<<1)
  72#define CH7301_PM_DACPD1        (1<<2)
  73#define CH7301_PM_DACPD2        (1<<3)
  74#define CH7xxx_PM_DVIL          (1<<6)
  75#define CH7xxx_PM_DVIP          (1<<7)
  76
  77#define CH7301_SYNC_POLARITY    0x56
  78#define CH7301_SYNC_RGB_YUV     (1<<0)
  79#define CH7301_SYNC_POL_DVI     (1<<5)
  80
  81/** @file
  82 * driver for the Chrontel 7xxx DVI chip over DVO.
  83 */
  84
  85static struct ch7xxx_id_struct {
  86        uint8_t vid;
  87        char *name;
  88} ch7xxx_ids[] = {
  89        { CH7011_VID, "CH7011" },
  90        { CH7009A_VID, "CH7009A" },
  91        { CH7009B_VID, "CH7009B" },
  92        { CH7301_VID, "CH7301" },
  93};
  94
  95struct ch7xxx_priv {
  96        bool quiet;
  97};
  98
  99static char *ch7xxx_get_id(uint8_t vid)
 100{
 101        int i;
 102
 103        for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) {
 104                if (ch7xxx_ids[i].vid == vid)
 105                        return ch7xxx_ids[i].name;
 106        }
 107
 108        return NULL;
 109}
 110
 111/** Reads an 8 bit register */
 112static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
 113{
 114        struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
 115        struct i2c_adapter *adapter = dvo->i2c_bus;
 116        u8 out_buf[2];
 117        u8 in_buf[2];
 118
 119        struct i2c_msg msgs[] = {
 120                {
 121                        .addr = dvo->slave_addr,
 122                        .flags = 0,
 123                        .len = 1,
 124                        .buf = out_buf,
 125                },
 126                {
 127                        .addr = dvo->slave_addr,
 128                        .flags = I2C_M_RD,
 129                        .len = 1,
 130                        .buf = in_buf,
 131                }
 132        };
 133
 134        out_buf[0] = addr;
 135        out_buf[1] = 0;
 136
 137        if (i2c_transfer(adapter, msgs, 2) == 2) {
 138                *ch = in_buf[0];
 139                return true;
 140        };
 141
 142        if (!ch7xxx->quiet) {
 143                DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
 144                          addr, adapter->name, dvo->slave_addr);
 145        }
 146        return false;
 147}
 148
 149/** Writes an 8 bit register */
 150static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
 151{
 152        struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
 153        struct i2c_adapter *adapter = dvo->i2c_bus;
 154        uint8_t out_buf[2];
 155        struct i2c_msg msg = {
 156                .addr = dvo->slave_addr,
 157                .flags = 0,
 158                .len = 2,
 159                .buf = out_buf,
 160        };
 161
 162        out_buf[0] = addr;
 163        out_buf[1] = ch;
 164
 165        if (i2c_transfer(adapter, &msg, 1) == 1)
 166                return true;
 167
 168        if (!ch7xxx->quiet) {
 169                DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
 170                          addr, adapter->name, dvo->slave_addr);
 171        }
 172
 173        return false;
 174}
 175
 176static bool ch7xxx_init(struct intel_dvo_device *dvo,
 177                        struct i2c_adapter *adapter)
 178{
 179        /* this will detect the CH7xxx chip on the specified i2c bus */
 180        struct ch7xxx_priv *ch7xxx;
 181        uint8_t vendor, device;
 182        char *name;
 183
 184        ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL);
 185        if (ch7xxx == NULL)
 186                return false;
 187
 188        dvo->i2c_bus = adapter;
 189        dvo->dev_priv = ch7xxx;
 190        ch7xxx->quiet = true;
 191
 192        if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor))
 193                goto out;
 194
 195        name = ch7xxx_get_id(vendor);
 196        if (!name) {
 197                DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
 198                                "slave %d.\n",
 199                          vendor, adapter->name, dvo->slave_addr);
 200                goto out;
 201        }
 202
 203
 204        if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device))
 205                goto out;
 206
 207        if (device != CH7xxx_DID) {
 208                DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
 209                                "slave %d.\n",
 210                          vendor, adapter->name, dvo->slave_addr);
 211                goto out;
 212        }
 213
 214        ch7xxx->quiet = false;
 215        DRM_DEBUG_KMS("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
 216                  name, vendor, device);
 217        return true;
 218out:
 219        kfree(ch7xxx);
 220        return false;
 221}
 222
 223static enum drm_connector_status ch7xxx_detect(struct intel_dvo_device *dvo)
 224{
 225        uint8_t cdet, orig_pm, pm;
 226
 227        ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm);
 228
 229        pm = orig_pm;
 230        pm &= ~CH7xxx_PM_FPD;
 231        pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP;
 232
 233        ch7xxx_writeb(dvo, CH7xxx_PM, pm);
 234
 235        ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet);
 236
 237        ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm);
 238
 239        if (cdet & CH7xxx_CDET_DVI)
 240                return connector_status_connected;
 241        return connector_status_disconnected;
 242}
 243
 244static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo,
 245                                              struct drm_display_mode *mode)
 246{
 247        if (mode->clock > 165000)
 248                return MODE_CLOCK_HIGH;
 249
 250        return MODE_OK;
 251}
 252
 253static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
 254                            struct drm_display_mode *mode,
 255                            struct drm_display_mode *adjusted_mode)
 256{
 257        uint8_t tvco, tpcp, tpd, tlpf, idf;
 258
 259        if (mode->clock <= 65000) {
 260                tvco = 0x23;
 261                tpcp = 0x08;
 262                tpd = 0x16;
 263                tlpf = 0x60;
 264        } else {
 265                tvco = 0x2d;
 266                tpcp = 0x06;
 267                tpd = 0x26;
 268                tlpf = 0xa0;
 269        }
 270
 271        ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00);
 272        ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco);
 273        ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp);
 274        ch7xxx_writeb(dvo, CH7xxx_TPD, tpd);
 275        ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30);
 276        ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf);
 277        ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00);
 278
 279        ch7xxx_readb(dvo, CH7xxx_IDF, &idf);
 280
 281        idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP);
 282        if (mode->flags & DRM_MODE_FLAG_PHSYNC)
 283                idf |= CH7xxx_IDF_HSP;
 284
 285        if (mode->flags & DRM_MODE_FLAG_PVSYNC)
 286                idf |= CH7xxx_IDF_HSP;
 287
 288        ch7xxx_writeb(dvo, CH7xxx_IDF, idf);
 289}
 290
 291/* set the CH7xxx power state */
 292static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode)
 293{
 294        if (mode == DRM_MODE_DPMS_ON)
 295                ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);
 296        else
 297                ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);
 298}
 299
 300static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
 301{
 302        int i;
 303
 304        for (i = 0; i < CH7xxx_NUM_REGS; i++) {
 305                uint8_t val;
 306                if ((i % 8) == 0)
 307                        DRM_LOG_KMS("\n %02X: ", i);
 308                ch7xxx_readb(dvo, i, &val);
 309                DRM_LOG_KMS("%02X ", val);
 310        }
 311}
 312
 313static void ch7xxx_destroy(struct intel_dvo_device *dvo)
 314{
 315        struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
 316
 317        if (ch7xxx) {
 318                kfree(ch7xxx);
 319                dvo->dev_priv = NULL;
 320        }
 321}
 322
 323struct intel_dvo_dev_ops ch7xxx_ops = {
 324        .init = ch7xxx_init,
 325        .detect = ch7xxx_detect,
 326        .mode_valid = ch7xxx_mode_valid,
 327        .mode_set = ch7xxx_mode_set,
 328        .dpms = ch7xxx_dpms,
 329        .dump_regs = ch7xxx_dump_regs,
 330        .destroy = ch7xxx_destroy,
 331};
 332