linux/drivers/gpu/drm/i915/dvo_ivch.c
<<
>>
Prefs
   1/*
   2 * Copyright © 2006 Intel Corporation
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice (including the next
  12 * paragraph) shall be included in all copies or substantial portions of the
  13 * Software.
  14 *
  15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21 * DEALINGS IN THE SOFTWARE.
  22 *
  23 * Authors:
  24 *    Eric Anholt <eric@anholt.net>
  25 *
  26 */
  27
  28#include "dvo.h"
  29
  30/*
  31 * register definitions for the i82807aa.
  32 *
  33 * Documentation on this chipset can be found in datasheet #29069001 at
  34 * intel.com.
  35 */
  36
  37/*
  38 * VCH Revision & GMBus Base Addr
  39 */
  40#define VR00            0x00
  41# define VR00_BASE_ADDRESS_MASK         0x007f
  42
  43/*
  44 * Functionality Enable
  45 */
  46#define VR01            0x01
  47
  48/*
  49 * Enable the panel fitter
  50 */
  51# define VR01_PANEL_FIT_ENABLE          (1 << 3)
  52/*
  53 * Enables the LCD display.
  54 *
  55 * This must not be set while VR01_DVO_BYPASS_ENABLE is set.
  56 */
  57# define VR01_LCD_ENABLE                (1 << 2)
  58/** Enables the DVO repeater. */
  59# define VR01_DVO_BYPASS_ENABLE         (1 << 1)
  60/** Enables the DVO clock */
  61# define VR01_DVO_ENABLE                (1 << 0)
  62
  63/*
  64 * LCD Interface Format
  65 */
  66#define VR10            0x10
  67/** Enables LVDS output instead of CMOS */
  68# define VR10_LVDS_ENABLE               (1 << 4)
  69/** Enables 18-bit LVDS output. */
  70# define VR10_INTERFACE_1X18            (0 << 2)
  71/** Enables 24-bit LVDS or CMOS output */
  72# define VR10_INTERFACE_1X24            (1 << 2)
  73/** Enables 2x18-bit LVDS or CMOS output. */
  74# define VR10_INTERFACE_2X18            (2 << 2)
  75/** Enables 2x24-bit LVDS output */
  76# define VR10_INTERFACE_2X24            (3 << 2)
  77
  78/*
  79 * VR20 LCD Horizontal Display Size
  80 */
  81#define VR20    0x20
  82
  83/*
  84 * LCD Vertical Display Size
  85 */
  86#define VR21    0x20
  87
  88/*
  89 * Panel power down status
  90 */
  91#define VR30            0x30
  92/** Read only bit indicating that the panel is not in a safe poweroff state. */
  93# define VR30_PANEL_ON                  (1 << 15)
  94
  95#define VR40            0x40
  96# define VR40_STALL_ENABLE              (1 << 13)
  97# define VR40_VERTICAL_INTERP_ENABLE    (1 << 12)
  98# define VR40_ENHANCED_PANEL_FITTING    (1 << 11)
  99# define VR40_HORIZONTAL_INTERP_ENABLE  (1 << 10)
 100# define VR40_AUTO_RATIO_ENABLE         (1 << 9)
 101# define VR40_CLOCK_GATING_ENABLE       (1 << 8)
 102
 103/*
 104 * Panel Fitting Vertical Ratio
 105 * (((image_height - 1) << 16) / ((panel_height - 1))) >> 2
 106 */
 107#define VR41            0x41
 108
 109/*
 110 * Panel Fitting Horizontal Ratio
 111 * (((image_width - 1) << 16) / ((panel_width - 1))) >> 2
 112 */
 113#define VR42            0x42
 114
 115/*
 116 * Horizontal Image Size
 117 */
 118#define VR43            0x43
 119
 120/* VR80 GPIO 0
 121 */
 122#define VR80        0x80
 123#define VR81        0x81
 124#define VR82        0x82
 125#define VR83        0x83
 126#define VR84        0x84
 127#define VR85        0x85
 128#define VR86        0x86
 129#define VR87        0x87
 130
 131/* VR88 GPIO 8
 132 */
 133#define VR88        0x88
 134
 135/* Graphics BIOS scratch 0
 136 */
 137#define VR8E        0x8E
 138# define VR8E_PANEL_TYPE_MASK           (0xf << 0)
 139# define VR8E_PANEL_INTERFACE_CMOS      (0 << 4)
 140# define VR8E_PANEL_INTERFACE_LVDS      (1 << 4)
 141# define VR8E_FORCE_DEFAULT_PANEL       (1 << 5)
 142
 143/* Graphics BIOS scratch 1
 144 */
 145#define VR8F        0x8F
 146# define VR8F_VCH_PRESENT               (1 << 0)
 147# define VR8F_DISPLAY_CONN              (1 << 1)
 148# define VR8F_POWER_MASK                (0x3c)
 149# define VR8F_POWER_POS                 (2)
 150
 151
 152struct ivch_priv {
 153        bool quiet;
 154
 155        uint16_t width, height;
 156};
 157
 158
 159static void ivch_dump_regs(struct intel_dvo_device *dvo);
 160
 161/**
 162 * Reads a register on the ivch.
 163 *
 164 * Each of the 256 registers are 16 bits long.
 165 */
 166static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
 167{
 168        struct ivch_priv *priv = dvo->dev_priv;
 169        struct i2c_adapter *adapter = dvo->i2c_bus;
 170        u8 out_buf[1];
 171        u8 in_buf[2];
 172
 173        struct i2c_msg msgs[] = {
 174                {
 175                        .addr = dvo->slave_addr,
 176                        .flags = I2C_M_RD,
 177                        .len = 0,
 178                },
 179                {
 180                        .addr = 0,
 181                        .flags = I2C_M_NOSTART,
 182                        .len = 1,
 183                        .buf = out_buf,
 184                },
 185                {
 186                        .addr = dvo->slave_addr,
 187                        .flags = I2C_M_RD | I2C_M_NOSTART,
 188                        .len = 2,
 189                        .buf = in_buf,
 190                }
 191        };
 192
 193        out_buf[0] = addr;
 194
 195        if (i2c_transfer(adapter, msgs, 3) == 3) {
 196                *data = (in_buf[1] << 8) | in_buf[0];
 197                return true;
 198        };
 199
 200        if (!priv->quiet) {
 201                DRM_DEBUG_KMS("Unable to read register 0x%02x from "
 202                                "%s:%02x.\n",
 203                          addr, adapter->name, dvo->slave_addr);
 204        }
 205        return false;
 206}
 207
 208/** Writes a 16-bit register on the ivch */
 209static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data)
 210{
 211        struct ivch_priv *priv = dvo->dev_priv;
 212        struct i2c_adapter *adapter = dvo->i2c_bus;
 213        u8 out_buf[3];
 214        struct i2c_msg msg = {
 215                .addr = dvo->slave_addr,
 216                .flags = 0,
 217                .len = 3,
 218                .buf = out_buf,
 219        };
 220
 221        out_buf[0] = addr;
 222        out_buf[1] = data & 0xff;
 223        out_buf[2] = data >> 8;
 224
 225        if (i2c_transfer(adapter, &msg, 1) == 1)
 226                return true;
 227
 228        if (!priv->quiet) {
 229                DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
 230                          addr, adapter->name, dvo->slave_addr);
 231        }
 232
 233        return false;
 234}
 235
 236/** Probes the given bus and slave address for an ivch */
 237static bool ivch_init(struct intel_dvo_device *dvo,
 238                      struct i2c_adapter *adapter)
 239{
 240        struct ivch_priv *priv;
 241        uint16_t temp;
 242
 243        priv = kzalloc(sizeof(struct ivch_priv), GFP_KERNEL);
 244        if (priv == NULL)
 245                return false;
 246
 247        dvo->i2c_bus = adapter;
 248        dvo->dev_priv = priv;
 249        priv->quiet = true;
 250
 251        if (!ivch_read(dvo, VR00, &temp))
 252                goto out;
 253        priv->quiet = false;
 254
 255        /* Since the identification bits are probably zeroes, which doesn't seem
 256         * very unique, check that the value in the base address field matches
 257         * the address it's responding on.
 258         */
 259        if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) {
 260                DRM_DEBUG_KMS("ivch detect failed due to address mismatch "
 261                          "(%d vs %d)\n",
 262                          (temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr);
 263                goto out;
 264        }
 265
 266        ivch_read(dvo, VR20, &priv->width);
 267        ivch_read(dvo, VR21, &priv->height);
 268
 269        return true;
 270
 271out:
 272        kfree(priv);
 273        return false;
 274}
 275
 276static enum drm_connector_status ivch_detect(struct intel_dvo_device *dvo)
 277{
 278        return connector_status_connected;
 279}
 280
 281static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo,
 282                                            struct drm_display_mode *mode)
 283{
 284        if (mode->clock > 112000)
 285                return MODE_CLOCK_HIGH;
 286
 287        return MODE_OK;
 288}
 289
 290/** Sets the power state of the panel connected to the ivch */
 291static void ivch_dpms(struct intel_dvo_device *dvo, bool enable)
 292{
 293        int i;
 294        uint16_t vr01, vr30, backlight;
 295
 296        /* Set the new power state of the panel. */
 297        if (!ivch_read(dvo, VR01, &vr01))
 298                return;
 299
 300        if (enable)
 301                backlight = 1;
 302        else
 303                backlight = 0;
 304        ivch_write(dvo, VR80, backlight);
 305
 306        if (enable)
 307                vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE;
 308        else
 309                vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE);
 310
 311        ivch_write(dvo, VR01, vr01);
 312
 313        /* Wait for the panel to make its state transition */
 314        for (i = 0; i < 100; i++) {
 315                if (!ivch_read(dvo, VR30, &vr30))
 316                        break;
 317
 318                if (((vr30 & VR30_PANEL_ON) != 0) == enable)
 319                        break;
 320                udelay(1000);
 321        }
 322        /* wait some more; vch may fail to resync sometimes without this */
 323        udelay(16 * 1000);
 324}
 325
 326static bool ivch_get_hw_state(struct intel_dvo_device *dvo)
 327{
 328        uint16_t vr01;
 329
 330        /* Set the new power state of the panel. */
 331        if (!ivch_read(dvo, VR01, &vr01))
 332                return false;
 333
 334        if (vr01 & VR01_LCD_ENABLE)
 335                return true;
 336        else
 337                return false;
 338}
 339
 340static void ivch_mode_set(struct intel_dvo_device *dvo,
 341                          struct drm_display_mode *mode,
 342                          struct drm_display_mode *adjusted_mode)
 343{
 344        uint16_t vr40 = 0;
 345        uint16_t vr01;
 346
 347        vr01 = 0;
 348        vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE |
 349                VR40_HORIZONTAL_INTERP_ENABLE);
 350
 351        if (mode->hdisplay != adjusted_mode->hdisplay ||
 352            mode->vdisplay != adjusted_mode->vdisplay) {
 353                uint16_t x_ratio, y_ratio;
 354
 355                vr01 |= VR01_PANEL_FIT_ENABLE;
 356                vr40 |= VR40_CLOCK_GATING_ENABLE;
 357                x_ratio = (((mode->hdisplay - 1) << 16) /
 358                           (adjusted_mode->hdisplay - 1)) >> 2;
 359                y_ratio = (((mode->vdisplay - 1) << 16) /
 360                           (adjusted_mode->vdisplay - 1)) >> 2;
 361                ivch_write(dvo, VR42, x_ratio);
 362                ivch_write(dvo, VR41, y_ratio);
 363        } else {
 364                vr01 &= ~VR01_PANEL_FIT_ENABLE;
 365                vr40 &= ~VR40_CLOCK_GATING_ENABLE;
 366        }
 367        vr40 &= ~VR40_AUTO_RATIO_ENABLE;
 368
 369        ivch_write(dvo, VR01, vr01);
 370        ivch_write(dvo, VR40, vr40);
 371
 372        ivch_dump_regs(dvo);
 373}
 374
 375static void ivch_dump_regs(struct intel_dvo_device *dvo)
 376{
 377        uint16_t val;
 378
 379        ivch_read(dvo, VR00, &val);
 380        DRM_LOG_KMS("VR00: 0x%04x\n", val);
 381        ivch_read(dvo, VR01, &val);
 382        DRM_LOG_KMS("VR01: 0x%04x\n", val);
 383        ivch_read(dvo, VR30, &val);
 384        DRM_LOG_KMS("VR30: 0x%04x\n", val);
 385        ivch_read(dvo, VR40, &val);
 386        DRM_LOG_KMS("VR40: 0x%04x\n", val);
 387
 388        /* GPIO registers */
 389        ivch_read(dvo, VR80, &val);
 390        DRM_LOG_KMS("VR80: 0x%04x\n", val);
 391        ivch_read(dvo, VR81, &val);
 392        DRM_LOG_KMS("VR81: 0x%04x\n", val);
 393        ivch_read(dvo, VR82, &val);
 394        DRM_LOG_KMS("VR82: 0x%04x\n", val);
 395        ivch_read(dvo, VR83, &val);
 396        DRM_LOG_KMS("VR83: 0x%04x\n", val);
 397        ivch_read(dvo, VR84, &val);
 398        DRM_LOG_KMS("VR84: 0x%04x\n", val);
 399        ivch_read(dvo, VR85, &val);
 400        DRM_LOG_KMS("VR85: 0x%04x\n", val);
 401        ivch_read(dvo, VR86, &val);
 402        DRM_LOG_KMS("VR86: 0x%04x\n", val);
 403        ivch_read(dvo, VR87, &val);
 404        DRM_LOG_KMS("VR87: 0x%04x\n", val);
 405        ivch_read(dvo, VR88, &val);
 406        DRM_LOG_KMS("VR88: 0x%04x\n", val);
 407
 408        /* Scratch register 0 - AIM Panel type */
 409        ivch_read(dvo, VR8E, &val);
 410        DRM_LOG_KMS("VR8E: 0x%04x\n", val);
 411
 412        /* Scratch register 1 - Status register */
 413        ivch_read(dvo, VR8F, &val);
 414        DRM_LOG_KMS("VR8F: 0x%04x\n", val);
 415}
 416
 417static void ivch_destroy(struct intel_dvo_device *dvo)
 418{
 419        struct ivch_priv *priv = dvo->dev_priv;
 420
 421        if (priv) {
 422                kfree(priv);
 423                dvo->dev_priv = NULL;
 424        }
 425}
 426
 427struct intel_dvo_dev_ops ivch_ops = {
 428        .init = ivch_init,
 429        .dpms = ivch_dpms,
 430        .get_hw_state = ivch_get_hw_state,
 431        .mode_valid = ivch_mode_valid,
 432        .mode_set = ivch_mode_set,
 433        .detect = ivch_detect,
 434        .dump_regs = ivch_dump_regs,
 435        .destroy = ivch_destroy,
 436};
 437