Porting DVO stuff
authorHong Liu <hong.liu@intel.com>
Thu, 17 Apr 2008 08:51:00 +0000 (16:51 +0800)
committerJesse Barnes <jbarnes@jbarnes-t61.(none)>
Thu, 17 Apr 2008 18:43:28 +0000 (11:43 -0700)
Ported from Xorg intel 2d driver. Changed interfaces definitions, which needed
to be changed later if other device wants to use these DVO stuff.

linux-core/Makefile.kernel
linux-core/dvo.h [new file with mode: 0644]
linux-core/dvo_ch7017.c [new file with mode: 0644]
linux-core/dvo_ch7xxx.c [new file with mode: 0644]
linux-core/dvo_ivch.c [new file with mode: 0644]
linux-core/dvo_sil164.c [new file with mode: 0644]
linux-core/dvo_tfp410.c [new file with mode: 0644]
linux-core/intel_display.c
linux-core/intel_drv.h
linux-core/intel_dvo.c [new file with mode: 0644]
shared-core/i915_drv.h

index 91decfc..28f6ec0 100644 (file)
@@ -22,7 +22,8 @@ i810-objs   := i810_drv.o i810_dma.o
 i915-objs   := i915_drv.o i915_dma.o i915_irq.o i915_mem.o i915_fence.o \
                i915_buffer.o intel_display.o intel_crt.o intel_lvds.o \
                intel_sdvo.o intel_modes.o intel_i2c.o i915_init.o intel_fb.o \
-               intel_tv.o i915_compat.o
+               intel_tv.o i915_compat.o intel_dvo.o dvo_ch7xxx.o \
+               dvo_ch7017.o dvo_ivch.o dvo_tfp410.o dvo_sil164.o
 nouveau-objs := nouveau_drv.o nouveau_state.o nouveau_fifo.o nouveau_mem.o \
                nouveau_object.o nouveau_irq.o nouveau_notifier.o nouveau_swmthd.o \
                nouveau_sgdma.o nouveau_dma.o nouveau_buffer.o nouveau_fence.o \
diff --git a/linux-core/dvo.h b/linux-core/dvo.h
new file mode 100644 (file)
index 0000000..c6c1dbd
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright © 2006 Eric Anholt
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#ifndef _INTEL_DVO_H 
+#define _INTEL_DVO_H 
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+
+struct intel_dvo_device {
+       char *name;
+       int type;
+       /* DVOA/B/C output register */
+       u32 dvo_reg;
+       /* GPIO register used for i2c bus to control this device */
+       u32 gpio;
+       int slave_addr;
+       struct intel_i2c_chan *i2c_bus;
+
+       const struct intel_dvo_dev_ops *dev_ops;
+       void *dev_priv;
+
+       struct drm_display_mode *panel_fixed_mode;
+       bool panel_wants_dither;
+};
+
+struct intel_dvo_dev_ops {
+       /*
+        * Initialize the device at startup time.
+        * Returns NULL if the device does not exist.
+        */
+       bool (*init)(struct intel_dvo_device *dvo,
+                    struct intel_i2c_chan *i2cbus);
+
+       /*
+        * Called to allow the output a chance to create properties after the
+        * RandR objects have been created.
+        */
+       void (*create_resources)(struct intel_dvo_device *dvo);
+
+       /*
+        * Turn on/off output or set intermediate power levels if available.
+        *
+        * Unsupported intermediate modes drop to the lower power setting.
+        * If the  mode is DPMSModeOff, the output must be disabled,
+        * as the DPLL may be disabled afterwards.
+        */
+       void (*dpms)(struct intel_dvo_device *dvo, int mode);
+
+       /*
+        * Saves the output's state for restoration on VT switch.
+        */
+       void (*save)(struct intel_dvo_device *dvo);
+
+       /*
+        * Restore's the output's state at VT switch.
+        */
+       void (*restore)(struct intel_dvo_device *dvo);
+
+       /*
+        * Callback for testing a video mode for a given output.
+        *
+        * This function should only check for cases where a mode can't
+        * be supported on the output specifically, and not represent
+        * generic CRTC limitations.
+        *
+        * \return MODE_OK if the mode is valid, or another MODE_* otherwise.
+        */
+       int (*mode_valid)(struct intel_dvo_device *dvo,
+                         struct drm_display_mode *mode);
+
+       /*
+        * Callback to adjust the mode to be set in the CRTC.
+        *
+        * This allows an output to adjust the clock or even the entire set of
+        * timings, which is used for panels with fixed timings or for
+        * buses with clock limitations.
+        */
+       bool (*mode_fixup)(struct intel_dvo_device *dvo,
+                          struct drm_display_mode *mode,
+                          struct drm_display_mode *adjusted_mode);
+
+       /*
+        * Callback for preparing mode changes on an output
+        */
+       void (*prepare)(struct intel_dvo_device *dvo);
+
+       /*
+        * Callback for committing mode changes on an output
+        */
+       void (*commit)(struct intel_dvo_device *dvo);
+
+       /*
+        * Callback for setting up a video mode after fixups have been made.
+        *
+        * This is only called while the output is disabled.  The dpms callback
+        * must be all that's necessary for the output, to turn the output on
+        * after this function is called.
+        */
+       void (*mode_set)(struct intel_dvo_device *dvo,
+                        struct drm_display_mode *mode,
+                        struct drm_display_mode *adjusted_mode);
+
+       /*
+        * Probe for a connected output, and return detect_status.
+        */
+       enum drm_output_status (*detect)(struct intel_dvo_device *dvo);
+
+       /**
+        * Query the device for the modes it provides.
+        *
+        * This function may also update MonInfo, mm_width, and mm_height.
+        *
+        * \return singly-linked list of modes or NULL if no modes found.
+        */
+       struct drm_display_mode *(*get_modes)(struct intel_dvo_device *dvo);
+
+#ifdef RANDR_12_INTERFACE
+       /**
+        * Callback when an output's property has changed.
+        */
+       bool (*set_property)(struct intel_dvo_device *dvo,
+                            struct drm_property *property, uint64_t val);
+#endif
+
+       /**
+        * Clean up driver-specific bits of the output
+        */
+       void (*destroy) (struct intel_dvo_device *dvo);
+
+       /**
+        * Debugging hook to dump device registers to log file
+        */
+       void (*dump_regs)(struct intel_dvo_device *dvo);
+};
+
+#endif /* _INTEL_DVO_H */
diff --git a/linux-core/dvo_ch7017.c b/linux-core/dvo_ch7017.c
new file mode 100644 (file)
index 0000000..8349da1
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Eric Anholt <eric@anholt.net>
+ *
+ */
+
+#include "dvo.h"
+
+#define CH7017_TV_DISPLAY_MODE         0x00
+#define CH7017_FLICKER_FILTER          0x01
+#define CH7017_VIDEO_BANDWIDTH         0x02
+#define CH7017_TEXT_ENHANCEMENT                0x03
+#define CH7017_START_ACTIVE_VIDEO      0x04
+#define CH7017_HORIZONTAL_POSITION     0x05
+#define CH7017_VERTICAL_POSITION       0x06
+#define CH7017_BLACK_LEVEL             0x07
+#define CH7017_CONTRAST_ENHANCEMENT    0x08
+#define CH7017_TV_PLL                  0x09
+#define CH7017_TV_PLL_M                        0x0a
+#define CH7017_TV_PLL_N                        0x0b
+#define CH7017_SUB_CARRIER_0           0x0c
+#define CH7017_CIV_CONTROL             0x10
+#define CH7017_CIV_0                   0x11
+#define CH7017_CHROMA_BOOST            0x14
+#define CH7017_CLOCK_MODE              0x1c
+#define CH7017_INPUT_CLOCK             0x1d
+#define CH7017_GPIO_CONTROL            0x1e
+#define CH7017_INPUT_DATA_FORMAT       0x1f
+#define CH7017_CONNECTION_DETECT       0x20
+#define CH7017_DAC_CONTROL             0x21
+#define CH7017_BUFFERED_CLOCK_OUTPUT   0x22
+#define CH7017_DEFEAT_VSYNC            0x47
+#define CH7017_TEST_PATTERN            0x48
+
+#define CH7017_POWER_MANAGEMENT                0x49
+/** Enables the TV output path. */
+#define CH7017_TV_EN                   (1 << 0)
+#define CH7017_DAC0_POWER_DOWN         (1 << 1)
+#define CH7017_DAC1_POWER_DOWN         (1 << 2)
+#define CH7017_DAC2_POWER_DOWN         (1 << 3)
+#define CH7017_DAC3_POWER_DOWN         (1 << 4)
+/** Powers down the TV out block, and DAC0-3 */
+#define CH7017_TV_POWER_DOWN_EN                (1 << 5)
+
+#define CH7017_VERSION_ID              0x4a
+
+#define CH7017_DEVICE_ID               0x4b
+#define CH7017_DEVICE_ID_VALUE         0x1b
+#define CH7018_DEVICE_ID_VALUE         0x1a
+#define CH7019_DEVICE_ID_VALUE         0x19
+
+#define CH7017_XCLK_D2_ADJUST          0x53
+#define CH7017_UP_SCALER_COEFF_0       0x55
+#define CH7017_UP_SCALER_COEFF_1       0x56
+#define CH7017_UP_SCALER_COEFF_2       0x57
+#define CH7017_UP_SCALER_COEFF_3       0x58
+#define CH7017_UP_SCALER_COEFF_4       0x59
+#define CH7017_UP_SCALER_VERTICAL_INC_0        0x5a
+#define CH7017_UP_SCALER_VERTICAL_INC_1        0x5b
+#define CH7017_GPIO_INVERT             0x5c
+#define CH7017_UP_SCALER_HORIZONTAL_INC_0      0x5d
+#define CH7017_UP_SCALER_HORIZONTAL_INC_1      0x5e
+
+#define CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT   0x5f
+/**< Low bits of horizontal active pixel input */
+
+#define CH7017_ACTIVE_INPUT_LINE_OUTPUT        0x60
+/** High bits of horizontal active pixel input */
+#define CH7017_LVDS_HAP_INPUT_MASK     (0x7 << 0)
+/** High bits of vertical active line output */
+#define CH7017_LVDS_VAL_HIGH_MASK      (0x7 << 3)
+
+#define CH7017_VERTICAL_ACTIVE_LINE_OUTPUT     0x61
+/**< Low bits of vertical active line output */
+
+#define CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT  0x62
+/**< Low bits of horizontal active pixel output */
+
+#define CH7017_LVDS_POWER_DOWN         0x63
+/** High bits of horizontal active pixel output */
+#define CH7017_LVDS_HAP_HIGH_MASK      (0x7 << 0)
+/** Enables the LVDS power down state transition */
+#define CH7017_LVDS_POWER_DOWN_EN      (1 << 6)
+/** Enables the LVDS upscaler */
+#define CH7017_LVDS_UPSCALER_EN                (1 << 7)
+#define CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED 0x08
+
+#define CH7017_LVDS_ENCODING           0x64
+#define CH7017_LVDS_DITHER_2D          (1 << 2)
+#define CH7017_LVDS_DITHER_DIS         (1 << 3)
+#define CH7017_LVDS_DUAL_CHANNEL_EN    (1 << 4)
+#define CH7017_LVDS_24_BIT             (1 << 5)
+
+#define CH7017_LVDS_ENCODING_2         0x65
+
+#define CH7017_LVDS_PLL_CONTROL                0x66
+/** Enables the LVDS panel output path */
+#define CH7017_LVDS_PANEN              (1 << 0)
+/** Enables the LVDS panel backlight */
+#define CH7017_LVDS_BKLEN              (1 << 3)
+
+#define CH7017_POWER_SEQUENCING_T1     0x67
+#define CH7017_POWER_SEQUENCING_T2     0x68
+#define CH7017_POWER_SEQUENCING_T3     0x69
+#define CH7017_POWER_SEQUENCING_T4     0x6a
+#define CH7017_POWER_SEQUENCING_T5     0x6b
+#define CH7017_GPIO_DRIVER_TYPE                0x6c
+#define CH7017_GPIO_DATA               0x6d
+#define CH7017_GPIO_DIRECTION_CONTROL  0x6e
+
+#define CH7017_LVDS_PLL_FEEDBACK_DIV   0x71
+# define CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT 4
+# define CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT 0
+# define CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED 0x80
+
+#define CH7017_LVDS_PLL_VCO_CONTROL    0x72
+# define CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED 0x80
+# define CH7017_LVDS_PLL_VCO_SHIFT     4
+# define CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT 0
+
+#define CH7017_OUTPUTS_ENABLE          0x73
+# define CH7017_CHARGE_PUMP_LOW                0x0
+# define CH7017_CHARGE_PUMP_HIGH       0x3
+# define CH7017_LVDS_CHANNEL_A         (1 << 3)
+# define CH7017_LVDS_CHANNEL_B         (1 << 4)
+# define CH7017_TV_DAC_A               (1 << 5)
+# define CH7017_TV_DAC_B               (1 << 6)
+# define CH7017_DDC_SELECT_DC2         (1 << 7)
+
+#define CH7017_LVDS_OUTPUT_AMPLITUDE   0x74
+#define CH7017_LVDS_PLL_EMI_REDUCTION  0x75
+#define CH7017_LVDS_POWER_DOWN_FLICKER 0x76
+
+#define CH7017_LVDS_CONTROL_2          0x78
+# define CH7017_LOOP_FILTER_SHIFT      5
+# define CH7017_PHASE_DETECTOR_SHIFT   0
+
+#define CH7017_BANG_LIMIT_CONTROL      0x7f
+
+struct ch7017_priv {
+       uint8_t save_hapi;
+       uint8_t save_vali;
+       uint8_t save_valo;
+       uint8_t save_ailo;
+       uint8_t save_lvds_pll_vco;
+       uint8_t save_feedback_div;
+       uint8_t save_lvds_control_2;
+       uint8_t save_outputs_enable;
+       uint8_t save_lvds_power_down;
+       uint8_t save_power_management;
+};
+
+static void ch7017_dump_regs(struct intel_dvo_device *dvo);
+static void ch7017_dpms(struct intel_dvo_device *dvo, int mode);
+
+static bool ch7017_read(struct intel_dvo_device *dvo, int addr, uint8_t *val)
+{
+       struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+       u8 out_buf[2];
+       u8 in_buf[2];
+
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = i2cbus->slave_addr,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = out_buf,
+               },
+               {
+                       .addr = i2cbus->slave_addr,
+                       .flags = I2C_M_RD,
+                       .len = 1,
+                       .buf = in_buf,
+               }
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = 0;
+
+       if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+               *val= in_buf[0];
+               return true;
+       };
+
+       return false;
+}
+
+static bool ch7017_write(struct intel_dvo_device *dvo, int addr, uint8_t val)
+{
+       struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+       uint8_t out_buf[2];
+       struct i2c_msg msg = {
+               .addr = i2cbus->slave_addr,
+               .flags = 0,
+               .len = 2,
+               .buf = out_buf,
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = val;
+
+       if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+               return true;
+
+       return false;
+}
+
+/** Probes for a CH7017 on the given bus and slave address. */
+static bool ch7017_init(struct intel_dvo_device *dvo,
+                       struct intel_i2c_chan *i2cbus)
+{
+       struct ch7017_priv *priv;
+       uint8_t val;
+
+       priv = kzalloc(sizeof(struct ch7017_priv), GFP_KERNEL);
+       if (priv == NULL)
+               return false;
+
+       dvo->i2c_bus = i2cbus;
+       dvo->i2c_bus->slave_addr = dvo->slave_addr;
+       dvo->dev_priv = priv;
+
+       if (!ch7017_read(dvo, CH7017_DEVICE_ID, &val))
+               goto fail;
+
+       if (val != CH7017_DEVICE_ID_VALUE &&
+           val != CH7018_DEVICE_ID_VALUE &&
+           val != CH7019_DEVICE_ID_VALUE) {
+               DRM_DEBUG("ch701x not detected, got %d: from %s Slave %d.\n",
+                         val, i2cbus->adapter.name,i2cbus->slave_addr); 
+               goto fail;
+       }
+
+       return true;
+fail:
+       kfree(priv);
+       return false;
+}
+
+static enum drm_output_status ch7017_detect(struct intel_dvo_device *dvo)
+{
+       return output_status_unknown;
+}
+
+static enum drm_mode_status ch7017_mode_valid(struct intel_dvo_device *dvo,
+                                             struct drm_display_mode *mode)
+{
+       if (mode->clock > 160000)
+               return MODE_CLOCK_HIGH;
+
+       return MODE_OK;
+}
+
+static void ch7017_mode_set(struct intel_dvo_device *dvo,
+                           struct drm_display_mode *mode,
+                           struct drm_display_mode *adjusted_mode)
+{
+       uint8_t lvds_pll_feedback_div, lvds_pll_vco_control;
+       uint8_t outputs_enable, lvds_control_2, lvds_power_down;
+       uint8_t horizontal_active_pixel_input;
+       uint8_t horizontal_active_pixel_output, vertical_active_line_output;
+       uint8_t active_input_line_output;
+
+       DRM_DEBUG("Registers before mode setting\n");
+       ch7017_dump_regs(dvo);
+
+       /* LVDS PLL settings from page 75 of 7017-7017ds.pdf*/
+       if (mode->clock < 100000) {
+               outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_LOW;
+               lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |
+                       (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) |
+                       (13 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT);
+               lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
+                       (2 << CH7017_LVDS_PLL_VCO_SHIFT) |
+                       (3 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
+               lvds_control_2 = (1 << CH7017_LOOP_FILTER_SHIFT) |
+                       (0 << CH7017_PHASE_DETECTOR_SHIFT);
+       } else {
+               outputs_enable = CH7017_LVDS_CHANNEL_A | CH7017_CHARGE_PUMP_HIGH;
+               lvds_pll_feedback_div = CH7017_LVDS_PLL_FEEDBACK_DEFAULT_RESERVED |
+                       (2 << CH7017_LVDS_PLL_FEED_BACK_DIVIDER_SHIFT) |
+                       (3 << CH7017_LVDS_PLL_FEED_FORWARD_DIVIDER_SHIFT);
+               lvds_pll_feedback_div = 35;
+               lvds_control_2 = (3 << CH7017_LOOP_FILTER_SHIFT) |
+                       (0 << CH7017_PHASE_DETECTOR_SHIFT);
+               if (1) { /* XXX: dual channel panel detection.  Assume yes for now. */
+                       outputs_enable |= CH7017_LVDS_CHANNEL_B;
+                       lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
+                               (2 << CH7017_LVDS_PLL_VCO_SHIFT) |
+                               (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
+               } else {
+                       lvds_pll_vco_control = CH7017_LVDS_PLL_VCO_DEFAULT_RESERVED |
+                               (1 << CH7017_LVDS_PLL_VCO_SHIFT) |
+                               (13 << CH7017_LVDS_PLL_POST_SCALE_DIV_SHIFT);
+               }
+       }
+
+       horizontal_active_pixel_input = mode->hdisplay & 0x00ff;
+
+       vertical_active_line_output = mode->vdisplay & 0x00ff;
+       horizontal_active_pixel_output = mode->hdisplay & 0x00ff;
+
+       active_input_line_output = ((mode->hdisplay & 0x0700) >> 8) |
+                                  (((mode->vdisplay & 0x0700) >> 8) << 3);
+
+       lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED |
+                         (mode->hdisplay & 0x0700) >> 8;
+
+       ch7017_dpms(dvo, DPMSModeOff);
+       ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT,
+                       horizontal_active_pixel_input);
+       ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT,
+                       horizontal_active_pixel_output);
+       ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT,
+                       vertical_active_line_output);
+       ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT,
+                       active_input_line_output);
+       ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, lvds_pll_vco_control);
+       ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, lvds_pll_feedback_div);
+       ch7017_write(dvo, CH7017_LVDS_CONTROL_2, lvds_control_2);
+       ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, outputs_enable);
+
+       /* Turn the LVDS back on with new settings. */
+       ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, lvds_power_down);
+
+       DRM_DEBUG("Registers after mode setting\n");
+       ch7017_dump_regs(dvo);
+}
+
+/* set the CH7017 power state */
+static void ch7017_dpms(struct intel_dvo_device *dvo, int mode)
+{
+       uint8_t val;
+
+       ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val);
+
+       /* Turn off TV/VGA, and never turn it on since we don't support it. */
+       ch7017_write(dvo, CH7017_POWER_MANAGEMENT,
+                       CH7017_DAC0_POWER_DOWN |
+                       CH7017_DAC1_POWER_DOWN |
+                       CH7017_DAC2_POWER_DOWN |
+                       CH7017_DAC3_POWER_DOWN |
+                       CH7017_TV_POWER_DOWN_EN);
+
+       if (mode == DPMSModeOn) {
+               /* Turn on the LVDS */
+               ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,
+                            val & ~CH7017_LVDS_POWER_DOWN_EN);
+       } else {
+               /* Turn off the LVDS */
+               ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,
+                            val | CH7017_LVDS_POWER_DOWN_EN);
+       }
+
+       /* XXX: Should actually wait for update power status somehow */
+       udelay(20000);
+}
+
+static void ch7017_dump_regs(struct intel_dvo_device *dvo)
+{
+       uint8_t val;
+
+#define DUMP(reg)                                      \
+do {                                                   \
+       ch7017_read(dvo, reg, &val);                    \
+       DRM_DEBUG(#reg ": %02x\n", val);                \
+} while (0)
+
+       DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT);
+       DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT);
+       DUMP(CH7017_VERTICAL_ACTIVE_LINE_OUTPUT);
+       DUMP(CH7017_ACTIVE_INPUT_LINE_OUTPUT);
+       DUMP(CH7017_LVDS_PLL_VCO_CONTROL);
+       DUMP(CH7017_LVDS_PLL_FEEDBACK_DIV);
+       DUMP(CH7017_LVDS_CONTROL_2);
+       DUMP(CH7017_OUTPUTS_ENABLE);
+       DUMP(CH7017_LVDS_POWER_DOWN);
+}
+
+static void ch7017_save(struct intel_dvo_device *dvo)
+{
+       struct ch7017_priv *priv = dvo->dev_priv;
+
+       ch7017_read(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, &priv->save_hapi);
+       ch7017_read(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, &priv->save_valo);
+       ch7017_read(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, &priv->save_ailo);
+       ch7017_read(dvo, CH7017_LVDS_PLL_VCO_CONTROL, &priv->save_lvds_pll_vco);
+       ch7017_read(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, &priv->save_feedback_div);
+       ch7017_read(dvo, CH7017_LVDS_CONTROL_2, &priv->save_lvds_control_2);
+       ch7017_read(dvo, CH7017_OUTPUTS_ENABLE, &priv->save_outputs_enable);
+       ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &priv->save_lvds_power_down);
+       ch7017_read(dvo, CH7017_POWER_MANAGEMENT, &priv->save_power_management);
+}
+
+static void ch7017_restore(struct intel_dvo_device *dvo)
+{
+       struct ch7017_priv *priv = dvo->dev_priv;
+
+       /* Power down before changing mode */
+       ch7017_dpms(dvo, DPMSModeOff);
+
+       ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT, priv->save_hapi);
+       ch7017_write(dvo, CH7017_VERTICAL_ACTIVE_LINE_OUTPUT, priv->save_valo);
+       ch7017_write(dvo, CH7017_ACTIVE_INPUT_LINE_OUTPUT, priv->save_ailo);
+       ch7017_write(dvo, CH7017_LVDS_PLL_VCO_CONTROL, priv->save_lvds_pll_vco);
+       ch7017_write(dvo, CH7017_LVDS_PLL_FEEDBACK_DIV, priv->save_feedback_div);
+       ch7017_write(dvo, CH7017_LVDS_CONTROL_2, priv->save_lvds_control_2);
+       ch7017_write(dvo, CH7017_OUTPUTS_ENABLE, priv->save_outputs_enable);
+       ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, priv->save_lvds_power_down);
+       ch7017_write(dvo, CH7017_POWER_MANAGEMENT, priv->save_power_management);
+}
+
+static void ch7017_destroy(struct intel_dvo_device *dvo)
+{
+       struct ch7017_priv *priv = dvo->dev_priv;
+
+       if (priv) {
+               kfree(priv);
+               dvo->dev_priv = NULL;
+       }
+}
+
+struct intel_dvo_dev_ops ch7017_ops = {
+       .init = ch7017_init,
+       .detect = ch7017_detect,
+       .mode_valid = ch7017_mode_valid,
+       .mode_set = ch7017_mode_set,
+       .dpms = ch7017_dpms,
+       .dump_regs = ch7017_dump_regs,
+       .save = ch7017_save,
+       .restore = ch7017_restore,
+       .destroy = ch7017_destroy,
+};
diff --git a/linux-core/dvo_ch7xxx.c b/linux-core/dvo_ch7xxx.c
new file mode 100644 (file)
index 0000000..69827a7
--- /dev/null
@@ -0,0 +1,368 @@
+/**************************************************************************
+
+Copyright © 2006 Dave Airlie
+
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sub license, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice (including the
+next paragraph) shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+**************************************************************************/
+
+#include "dvo.h"
+
+#define CH7xxx_REG_VID         0x4a
+#define CH7xxx_REG_DID         0x4b
+
+#define CH7011_VID             0x83 /* 7010 as well */
+#define CH7009A_VID            0x84
+#define CH7009B_VID            0x85
+#define CH7301_VID             0x95
+
+#define CH7xxx_VID             0x84
+#define CH7xxx_DID             0x17
+
+#define CH7xxx_NUM_REGS                0x4c
+
+#define CH7xxx_CM              0x1c
+#define CH7xxx_CM_XCM          (1<<0)
+#define CH7xxx_CM_MCP          (1<<2)
+#define CH7xxx_INPUT_CLOCK     0x1d
+#define CH7xxx_GPIO            0x1e
+#define CH7xxx_GPIO_HPIR       (1<<3)
+#define CH7xxx_IDF             0x1f
+
+#define CH7xxx_IDF_HSP         (1<<3)
+#define CH7xxx_IDF_VSP         (1<<4)
+
+#define CH7xxx_CONNECTION_DETECT 0x20
+#define CH7xxx_CDET_DVI                (1<<5)
+
+#define CH7301_DAC_CNTL                0x21
+#define CH7301_HOTPLUG         0x23
+#define CH7xxx_TCTL            0x31
+#define CH7xxx_TVCO            0x32
+#define CH7xxx_TPCP            0x33
+#define CH7xxx_TPD             0x34
+#define CH7xxx_TPVT            0x35
+#define CH7xxx_TLPF            0x36
+#define CH7xxx_TCT             0x37
+#define CH7301_TEST_PATTERN    0x48
+
+#define CH7xxx_PM              0x49
+#define CH7xxx_PM_FPD          (1<<0)
+#define CH7301_PM_DACPD0       (1<<1)
+#define CH7301_PM_DACPD1       (1<<2)
+#define CH7301_PM_DACPD2       (1<<3)
+#define CH7xxx_PM_DVIL         (1<<6)
+#define CH7xxx_PM_DVIP         (1<<7)
+
+#define CH7301_SYNC_POLARITY   0x56
+#define CH7301_SYNC_RGB_YUV    (1<<0)
+#define CH7301_SYNC_POL_DVI    (1<<5)
+
+/** @file
+ * driver for the Chrontel 7xxx DVI chip over DVO.
+ */
+
+static struct ch7xxx_id_struct {
+       uint8_t vid;
+       char *name;
+} ch7xxx_ids[] = { 
+       { CH7011_VID, "CH7011" },
+       { CH7009A_VID, "CH7009A" },
+       { CH7009B_VID, "CH7009B" },
+       { CH7301_VID, "CH7301" },
+};
+
+struct ch7xxx_reg_state {
+    uint8_t regs[CH7xxx_NUM_REGS];
+};
+
+struct ch7xxx_priv {
+       bool quiet;
+
+       struct ch7xxx_reg_state save_reg;
+       struct ch7xxx_reg_state mode_reg;
+       uint8_t save_TCTL, save_TPCP, save_TPD, save_TPVT;
+       uint8_t save_TLPF, save_TCT, save_PM, save_IDF;
+};
+
+static void ch7xxx_save(struct intel_dvo_device *dvo);
+
+static char *ch7xxx_get_id(uint8_t vid)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(ch7xxx_ids); i++) {
+               if (ch7xxx_ids[i].vid == vid)
+                       return ch7xxx_ids[i].name;
+       }
+
+       return NULL;
+}
+
+/** Reads an 8 bit register */
+static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
+{
+       struct ch7xxx_priv *ch7xxx= dvo->dev_priv;
+       struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+       u8 out_buf[2];
+       u8 in_buf[2];
+
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = i2cbus->slave_addr,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = out_buf,
+               },
+               {
+                       .addr = i2cbus->slave_addr,
+                       .flags = I2C_M_RD,
+                       .len = 1,
+                       .buf = in_buf,
+               }
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = 0;
+
+       if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+               *ch = in_buf[0];
+               return true;
+       };
+
+       if (!ch7xxx->quiet) {
+               DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+                         addr, i2cbus->adapter.name, i2cbus->slave_addr);
+       }
+       return false;
+}
+
+/** Writes an 8 bit register */
+static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+{
+       struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+       struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+       uint8_t out_buf[2];
+       struct i2c_msg msg = {
+               .addr = i2cbus->slave_addr,
+               .flags = 0,
+               .len = 2,
+               .buf = out_buf,
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = ch;
+
+       if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+               return true;
+
+       if (!ch7xxx->quiet) {
+               DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+                         addr, i2cbus->adapter.name, i2cbus->slave_addr);
+       }
+
+       return false;
+}
+
+static bool ch7xxx_init(struct intel_dvo_device *dvo,
+                       struct intel_i2c_chan *i2cbus)
+{
+       /* this will detect the CH7xxx chip on the specified i2c bus */
+       struct ch7xxx_priv *ch7xxx;
+       uint8_t vendor, device;
+       char *name;
+
+       ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL);
+       if (ch7xxx == NULL)
+               return false;
+
+       dvo->i2c_bus = i2cbus;
+       dvo->i2c_bus->slave_addr = dvo->slave_addr;
+       dvo->dev_priv = ch7xxx;
+       ch7xxx->quiet = true;
+
+       if (!ch7xxx_readb(dvo, CH7xxx_REG_VID, &vendor))
+               goto out;
+
+       name = ch7xxx_get_id(vendor);
+       if (!name) {
+               DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
+                         vendor, i2cbus->adapter.name, i2cbus->slave_addr);
+               goto out;
+       }
+
+
+       if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device))
+               goto out;
+
+       if (device != CH7xxx_DID) {
+               DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
+                         vendor, i2cbus->adapter.name, i2cbus->slave_addr);
+               goto out;
+       }
+
+       ch7xxx->quiet = FALSE;
+       DRM_DEBUG("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
+                 name, vendor, device);
+       return true;
+out:
+       kfree(ch7xxx);
+       return false;
+}
+
+static enum drm_output_status ch7xxx_detect(struct intel_dvo_device *dvo)
+{
+       uint8_t cdet, orig_pm, pm;
+
+       ch7xxx_readb(dvo, CH7xxx_PM, &orig_pm);
+
+       pm = orig_pm;
+       pm &= ~CH7xxx_PM_FPD;
+       pm |= CH7xxx_PM_DVIL | CH7xxx_PM_DVIP;
+
+       ch7xxx_writeb(dvo, CH7xxx_PM, pm);
+
+       ch7xxx_readb(dvo, CH7xxx_CONNECTION_DETECT, &cdet);
+
+       ch7xxx_writeb(dvo, CH7xxx_PM, orig_pm);
+
+       if (cdet & CH7xxx_CDET_DVI) 
+               return output_status_connected;
+       return output_status_disconnected;
+}
+
+static enum drm_mode_status ch7xxx_mode_valid(struct intel_dvo_device *dvo,
+                                             struct drm_display_mode *mode)
+{
+       if (mode->clock > 165000)
+               return MODE_CLOCK_HIGH;
+
+       return MODE_OK;
+}
+
+static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
+                           struct drm_display_mode *mode,
+                           struct drm_display_mode *adjusted_mode)
+{
+       uint8_t tvco, tpcp, tpd, tlpf, idf;
+
+       if (mode->clock <= 65000) {
+               tvco = 0x23;
+               tpcp = 0x08;
+               tpd = 0x16;
+               tlpf = 0x60;
+       } else {
+               tvco = 0x2d;
+               tpcp = 0x06;
+               tpd = 0x26;
+               tlpf = 0xa0;
+       }
+
+       ch7xxx_writeb(dvo, CH7xxx_TCTL, 0x00);
+       ch7xxx_writeb(dvo, CH7xxx_TVCO, tvco);
+       ch7xxx_writeb(dvo, CH7xxx_TPCP, tpcp);
+       ch7xxx_writeb(dvo, CH7xxx_TPD, tpd);
+       ch7xxx_writeb(dvo, CH7xxx_TPVT, 0x30);
+       ch7xxx_writeb(dvo, CH7xxx_TLPF, tlpf);
+       ch7xxx_writeb(dvo, CH7xxx_TCT, 0x00);
+
+       ch7xxx_readb(dvo, CH7xxx_IDF, &idf);
+
+       idf &= ~(CH7xxx_IDF_HSP | CH7xxx_IDF_VSP);
+       if (mode->flags & V_PHSYNC)
+               idf |= CH7xxx_IDF_HSP;
+
+       if (mode->flags & V_PVSYNC)
+               idf |= CH7xxx_IDF_HSP;
+
+       ch7xxx_writeb(dvo, CH7xxx_IDF, idf);
+}
+
+/* set the CH7xxx power state */
+static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode)
+{
+       if (mode == DPMSModeOn)
+               ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);
+       else
+               ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);
+}
+
+static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
+{
+       struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+       int i;
+
+       for (i = 0; i < CH7xxx_NUM_REGS; i++) {
+               if ((i % 8) == 0 )
+                       DRM_DEBUG("\n %02X: ", i);
+               DRM_DEBUG("%02X ", ch7xxx->mode_reg.regs[i]);
+       }
+}
+
+static void ch7xxx_save(struct intel_dvo_device *dvo)
+{
+       struct ch7xxx_priv *ch7xxx= dvo->dev_priv;
+
+       ch7xxx_readb(dvo, CH7xxx_TCTL, &ch7xxx->save_TCTL);
+       ch7xxx_readb(dvo, CH7xxx_TPCP, &ch7xxx->save_TPCP);
+       ch7xxx_readb(dvo, CH7xxx_TPD, &ch7xxx->save_TPD);
+       ch7xxx_readb(dvo, CH7xxx_TPVT, &ch7xxx->save_TPVT);
+       ch7xxx_readb(dvo, CH7xxx_TLPF, &ch7xxx->save_TLPF);
+       ch7xxx_readb(dvo, CH7xxx_PM, &ch7xxx->save_PM);
+       ch7xxx_readb(dvo, CH7xxx_IDF, &ch7xxx->save_IDF);
+}
+
+static void ch7xxx_restore(struct intel_dvo_device *dvo)
+{
+       struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+
+       ch7xxx_writeb(dvo, CH7xxx_TCTL, ch7xxx->save_TCTL);
+       ch7xxx_writeb(dvo, CH7xxx_TPCP, ch7xxx->save_TPCP);
+       ch7xxx_writeb(dvo, CH7xxx_TPD, ch7xxx->save_TPD);
+       ch7xxx_writeb(dvo, CH7xxx_TPVT, ch7xxx->save_TPVT);
+       ch7xxx_writeb(dvo, CH7xxx_TLPF, ch7xxx->save_TLPF);
+       ch7xxx_writeb(dvo, CH7xxx_IDF, ch7xxx->save_IDF);
+       ch7xxx_writeb(dvo, CH7xxx_PM, ch7xxx->save_PM);
+}
+
+static void ch7xxx_destroy(struct intel_dvo_device *dvo)
+{
+       struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
+
+       if (ch7xxx) {
+               kfree(ch7xxx);
+               dvo->dev_priv = NULL;
+       }
+}
+
+struct intel_dvo_dev_ops ch7xxx_ops = {
+       .init = ch7xxx_init,
+       .detect = ch7xxx_detect,
+       .mode_valid = ch7xxx_mode_valid,
+       .mode_set = ch7xxx_mode_set,
+       .dpms = ch7xxx_dpms,
+       .dump_regs = ch7xxx_dump_regs,
+       .save = ch7xxx_save,
+       .restore = ch7xxx_restore,
+       .destroy = ch7xxx_destroy,
+};
diff --git a/linux-core/dvo_ivch.c b/linux-core/dvo_ivch.c
new file mode 100644 (file)
index 0000000..5fce246
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ * Copyright © 2006 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Eric Anholt <eric@anholt.net>
+ *
+ */
+
+#include "dvo.h"
+
+/* 
+ * register definitions for the i82807aa.
+ *
+ * Documentation on this chipset can be found in datasheet #29069001 at
+ * intel.com.
+ */
+
+/* 
+ * VCH Revision & GMBus Base Addr
+ */
+#define VR00           0x00
+# define VR00_BASE_ADDRESS_MASK                0x007f
+
+/* 
+ * Functionality Enable
+ */
+#define VR01           0x01
+
+/*
+ * Enable the panel fitter
+ */
+# define VR01_PANEL_FIT_ENABLE         (1 << 3)
+/*
+ * Enables the LCD display.
+ *
+ * This must not be set while VR01_DVO_BYPASS_ENABLE is set.
+ */
+# define VR01_LCD_ENABLE               (1 << 2)
+/** Enables the DVO repeater. */
+# define VR01_DVO_BYPASS_ENABLE                (1 << 1)
+/** Enables the DVO clock */
+# define VR01_DVO_ENABLE               (1 << 0)
+
+/* 
+ * LCD Interface Format
+ */
+#define VR10           0x10
+/** Enables LVDS output instead of CMOS */
+# define VR10_LVDS_ENABLE              (1 << 4)
+/** Enables 18-bit LVDS output. */
+# define VR10_INTERFACE_1X18           (0 << 2)
+/** Enables 24-bit LVDS or CMOS output */
+# define VR10_INTERFACE_1X24           (1 << 2)
+/** Enables 2x18-bit LVDS or CMOS output. */
+# define VR10_INTERFACE_2X18           (2 << 2)
+/** Enables 2x24-bit LVDS output */
+# define VR10_INTERFACE_2X24           (3 << 2)
+
+/*
+ * VR20 LCD Horizontal Display Size
+ */
+#define VR20   0x20
+
+/* 
+ * LCD Vertical Display Size
+ */
+#define VR21   0x20
+
+/* 
+ * Panel power down status
+ */
+#define VR30           0x30
+/** Read only bit indicating that the panel is not in a safe poweroff state. */
+# define VR30_PANEL_ON                 (1 << 15)
+
+#define VR40           0x40
+# define VR40_STALL_ENABLE             (1 << 13)
+# define VR40_VERTICAL_INTERP_ENABLE   (1 << 12)
+# define VR40_ENHANCED_PANEL_FITTING   (1 << 11)
+# define VR40_HORIZONTAL_INTERP_ENABLE (1 << 10)
+# define VR40_AUTO_RATIO_ENABLE                (1 << 9)
+# define VR40_CLOCK_GATING_ENABLE      (1 << 8)
+
+/*
+ * Panel Fitting Vertical Ratio
+ * (((image_height - 1) << 16) / ((panel_height - 1))) >> 2
+ */
+#define VR41           0x41
+
+/* 
+ * Panel Fitting Horizontal Ratio
+ * (((image_width - 1) << 16) / ((panel_width - 1))) >> 2
+ */
+#define VR42           0x42
+
+/*
+ * Horizontal Image Size
+ */
+#define VR43           0x43
+
+/* VR80 GPIO 0
+ */
+#define VR80       0x80
+#define VR81       0x81
+#define VR82       0x82
+#define VR83       0x83
+#define VR84       0x84
+#define VR85       0x85
+#define VR86       0x86
+#define VR87       0x87
+    
+/* VR88 GPIO 8
+ */
+#define VR88       0x88
+
+/* Graphics BIOS scratch 0
+ */
+#define VR8E       0x8E
+# define VR8E_PANEL_TYPE_MASK          (0xf << 0)
+# define VR8E_PANEL_INTERFACE_CMOS     (0 << 4)
+# define VR8E_PANEL_INTERFACE_LVDS     (1 << 4)
+# define VR8E_FORCE_DEFAULT_PANEL      (1 << 5)
+
+/* Graphics BIOS scratch 1
+ */
+#define VR8F       0x8F
+# define VR8F_VCH_PRESENT              (1 << 0)
+# define VR8F_DISPLAY_CONN             (1 << 1)
+# define VR8F_POWER_MASK               (0x3c)
+# define VR8F_POWER_POS                        (2)
+
+
+struct ivch_priv {
+       bool quiet;
+
+       uint16_t width, height;
+
+       uint16_t save_VR01;
+       uint16_t save_VR40;
+};
+
+#if 0
+struct vch_capabilities {
+    struct aimdb_block aimdb_block;
+    uint8_t            panel_type;
+    uint8_t            set_panel_type;
+    uint8_t            slave_address;
+    uint8_t            capabilities;
+#define VCH_PANEL_FITTING_SUPPORT      (0x3 << 0)
+#define VCH_PANEL_FITTING_TEXT         (1 << 2)
+#define VCH_PANEL_FITTING_GRAPHICS     (1 << 3)
+#define VCH_PANEL_FITTING_RATIO                (1 << 4)
+#define VCH_DITHERING                  (1 << 5)
+    uint8_t            backlight_gpio;
+    uint8_t            set_panel_type_us_gpios;
+} __attribute__ ((packed));
+#endif
+
+static void ivch_dump_regs(struct intel_dvo_device *dvo);
+
+/**
+ * Reads a register on the ivch.
+ *
+ * Each of the 256 registers are 16 bits long.
+ */
+static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
+{
+       struct ivch_priv *priv = dvo->dev_priv;
+       struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+       u8 out_buf[2];
+       u8 in_buf[2];
+
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = i2cbus->slave_addr,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = out_buf,
+               },
+               {
+                       .addr = i2cbus->slave_addr,
+                       .flags = I2C_M_RD,
+                       .len = 2,
+                       .buf = in_buf,
+               }
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = 0;
+
+       if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+               *data = (in_buf[1] << 8) | in_buf[0];
+               return true;
+       };
+
+       if (!priv->quiet) {
+               DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+                         addr, i2cbus->adapter.name, i2cbus->slave_addr);
+       }
+       return false;
+}
+/** Writes a 16-bit register on the ivch */
+static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data)
+{
+       struct ivch_priv *priv = dvo->dev_priv;
+       struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+       u8 out_buf[3];
+       struct i2c_msg msg = {
+               .addr = i2cbus->slave_addr,
+               .flags = 0,
+               .len = 3,
+               .buf = out_buf,
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = data & 0xff;
+       out_buf[2] = data >> 8;
+
+       if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+               return true;
+
+       if (!priv->quiet) {
+               DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+                         addr, i2cbus->adapter.name, i2cbus->slave_addr);
+       }
+
+       return false;
+}
+
+/** Probes the given bus and slave address for an ivch */
+static bool ivch_init(struct intel_dvo_device *dvo,
+                     struct intel_i2c_chan *i2cbus)
+{
+       struct ivch_priv *priv;
+       uint16_t temp;
+
+       priv = kzalloc(sizeof(struct ivch_priv), GFP_KERNEL);
+       if (priv == NULL)
+               return false;
+
+       dvo->i2c_bus = i2cbus;
+       dvo->i2c_bus->slave_addr = dvo->slave_addr;
+       dvo->dev_priv = priv;
+       priv->quiet = TRUE;
+
+       if (!ivch_read(dvo, VR00, &temp))
+               goto out;
+       priv->quiet = false;
+
+       /* Since the identification bits are probably zeroes, which doesn't seem
+        * very unique, check that the value in the base address field matches
+        * the address it's responding on.
+        */
+       if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) {
+               DRM_DEBUG("ivch detect failed due to address mismatch "
+                         "(%d vs %d)\n",
+                         (temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr);
+               goto out;
+       }
+
+#if 0
+       if (!xf86I2CDevInit(&priv->d)) {
+               goto out;
+       }
+#endif
+       ivch_read(dvo, VR20, &priv->width);
+       ivch_read(dvo, VR21, &priv->height);
+
+       return true;
+
+out:
+       kfree(priv);
+       return false;
+}
+
+static enum drm_output_status ivch_detect(struct intel_dvo_device *dvo)
+{
+       return output_status_connected;
+}
+
+static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo,
+                                           struct drm_display_mode *mode)
+{
+       if (mode->clock > 112000)
+               return MODE_CLOCK_HIGH;
+
+       return MODE_OK;
+}
+
+/** Sets the power state of the panel connected to the ivch */
+static void ivch_dpms(struct intel_dvo_device *dvo, int mode)
+{
+       int i;
+       uint16_t vr01, vr30, backlight;
+
+       /* Set the new power state of the panel. */
+       if (!ivch_read(dvo, VR01, &vr01))
+               return;
+
+       if (mode == DPMSModeOn)
+               backlight = 1;
+       else
+               backlight = 0;
+       ivch_write(dvo, VR80, backlight);
+
+       if (mode == DPMSModeOn)
+               vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE;
+       else
+               vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE);
+
+       ivch_write(dvo, VR01, vr01);
+
+       /* Wait for the panel to make its state transition */
+       for (i = 0; i < 100; i++) {
+               if (!ivch_read(dvo, VR30, &vr30))
+                       break;
+
+               if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DPMSModeOn))
+                       break;
+               udelay(1000);
+       }
+       /* wait some more; vch may fail to resync sometimes without this */
+       udelay(16 * 1000);
+}
+
+static void ivch_mode_set(struct intel_dvo_device *dvo,
+                         struct drm_display_mode *mode,
+                         struct drm_display_mode *adjusted_mode)
+{
+       uint16_t vr40 = 0;
+       uint16_t vr01;
+
+       vr01 = 0;
+       vr40 = (VR40_STALL_ENABLE | VR40_VERTICAL_INTERP_ENABLE |
+               VR40_HORIZONTAL_INTERP_ENABLE);
+
+       if (mode->hdisplay != adjusted_mode->hdisplay || 
+           mode->vdisplay != adjusted_mode->vdisplay) {
+               uint16_t x_ratio, y_ratio;
+
+               vr01 |= VR01_PANEL_FIT_ENABLE;
+               vr40 |= VR40_CLOCK_GATING_ENABLE;
+               x_ratio = (((mode->hdisplay - 1) << 16) /
+                          (adjusted_mode->hdisplay - 1)) >> 2;
+               y_ratio = (((mode->vdisplay - 1) << 16) /
+                          (adjusted_mode->vdisplay - 1)) >> 2;
+               ivch_write (dvo, VR42, x_ratio);
+               ivch_write (dvo, VR41, y_ratio);
+       } else {
+               vr01 &= ~VR01_PANEL_FIT_ENABLE;
+               vr40 &= ~VR40_CLOCK_GATING_ENABLE;
+       }
+       vr40 &= ~VR40_AUTO_RATIO_ENABLE;
+
+       ivch_write(dvo, VR01, vr01);
+       ivch_write(dvo, VR40, vr40);
+
+       ivch_dump_regs(dvo);
+}
+
+static void ivch_dump_regs(struct intel_dvo_device *dvo)
+{
+       uint16_t val;
+
+       ivch_read(dvo, VR00, &val);
+       DRM_DEBUG("VR00: 0x%04x\n", val);
+       ivch_read(dvo, VR01, &val);
+       DRM_DEBUG("VR01: 0x%04x\n", val);
+       ivch_read(dvo, VR30, &val);
+       DRM_DEBUG("VR30: 0x%04x\n", val);
+       ivch_read(dvo, VR40, &val);
+       DRM_DEBUG("VR40: 0x%04x\n", val);
+
+       /* GPIO registers */
+       ivch_read(dvo, VR80, &val);
+       DRM_DEBUG("VR80: 0x%04x\n", val);
+       ivch_read(dvo, VR81, &val);
+       DRM_DEBUG("VR81: 0x%04x\n", val);
+       ivch_read(dvo, VR82, &val);
+       DRM_DEBUG("VR82: 0x%04x\n", val);
+       ivch_read(dvo, VR83, &val);
+       DRM_DEBUG("VR83: 0x%04x\n", val);
+       ivch_read(dvo, VR84, &val);
+       DRM_DEBUG("VR84: 0x%04x\n", val);
+       ivch_read(dvo, VR85, &val);
+       DRM_DEBUG("VR85: 0x%04x\n", val);
+       ivch_read(dvo, VR86, &val);
+       DRM_DEBUG("VR86: 0x%04x\n", val);
+       ivch_read(dvo, VR87, &val);
+       DRM_DEBUG("VR87: 0x%04x\n", val);
+       ivch_read(dvo, VR88, &val);
+       DRM_DEBUG("VR88: 0x%04x\n", val);
+
+       /* Scratch register 0 - AIM Panel type */
+       ivch_read(dvo, VR8E, &val);
+       DRM_DEBUG("VR8E: 0x%04x\n", val);
+
+       /* Scratch register 1 - Status register */
+       ivch_read(dvo, VR8F, &val);
+       DRM_DEBUG("VR8F: 0x%04x\n", val);
+}
+
+static void ivch_save(struct intel_dvo_device *dvo)
+{
+       struct ivch_priv *priv = dvo->dev_priv;
+
+       ivch_read(dvo, VR01, &priv->save_VR01);
+       ivch_read(dvo, VR40, &priv->save_VR40);
+}
+
+static void ivch_restore(struct intel_dvo_device *dvo)
+{
+       struct ivch_priv *priv = dvo->dev_priv;
+
+       ivch_write(dvo, VR01, priv->save_VR01);
+       ivch_write(dvo, VR40, priv->save_VR40);
+}
+
+static void ivch_destroy(struct intel_dvo_device *dvo)
+{
+       struct ivch_priv *priv = dvo->dev_priv;
+
+       if (priv) {
+               kfree(priv);
+               dvo->dev_priv = NULL;
+       }
+}
+
+struct intel_dvo_dev_ops ivch_ops= {
+       .init = ivch_init,
+       .dpms = ivch_dpms,
+       .save = ivch_save,
+       .restore = ivch_restore,
+       .mode_valid = ivch_mode_valid,
+       .mode_set = ivch_mode_set,
+       .detect = ivch_detect,
+       .dump_regs = ivch_dump_regs,
+       .destroy = ivch_destroy,
+};
diff --git a/linux-core/dvo_sil164.c b/linux-core/dvo_sil164.c
new file mode 100644 (file)
index 0000000..0cee59b
--- /dev/null
@@ -0,0 +1,302 @@
+/**************************************************************************
+
+Copyright © 2006 Dave Airlie
+
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sub license, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice (including the
+next paragraph) shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+**************************************************************************/
+
+#include "dvo.h"
+
+#define SIL164_VID 0x0001
+#define SIL164_DID 0x0006
+
+#define SIL164_VID_LO 0x00
+#define SIL164_VID_HI 0x01
+#define SIL164_DID_LO 0x02
+#define SIL164_DID_HI 0x03
+#define SIL164_REV    0x04
+#define SIL164_RSVD   0x05
+#define SIL164_FREQ_LO 0x06
+#define SIL164_FREQ_HI 0x07
+
+#define SIL164_REG8 0x08
+#define SIL164_8_VEN (1<<5)
+#define SIL164_8_HEN (1<<4)
+#define SIL164_8_DSEL (1<<3)
+#define SIL164_8_BSEL (1<<2)
+#define SIL164_8_EDGE (1<<1)
+#define SIL164_8_PD   (1<<0)
+
+#define SIL164_REG9 0x09
+#define SIL164_9_VLOW (1<<7)
+#define SIL164_9_MSEL_MASK (0x7<<4)
+#define SIL164_9_TSEL (1<<3)
+#define SIL164_9_RSEN (1<<2)
+#define SIL164_9_HTPLG (1<<1)
+#define SIL164_9_MDI (1<<0)
+
+#define SIL164_REGC 0x0c
+
+struct sil164_save_rec {
+       uint8_t reg8;
+       uint8_t reg9;
+       uint8_t regc;
+};
+
+struct sil164_priv {
+       //I2CDevRec d;
+       bool quiet;
+       struct sil164_save_rec save_regs;
+       struct sil164_save_rec mode_regs;
+};
+
+#define SILPTR(d) ((SIL164Ptr)(d->DriverPrivate.ptr))
+
+static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
+{
+       struct sil164_priv *sil = dvo->dev_priv;
+       struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+       u8 out_buf[2];
+       u8 in_buf[2];
+
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = i2cbus->slave_addr,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = out_buf,
+               },
+               {
+                       .addr = i2cbus->slave_addr,
+                       .flags = I2C_M_RD,
+                       .len = 1,
+                       .buf = in_buf,
+               }
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = 0;
+
+       if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+               *ch = in_buf[0];
+               return true;
+       };
+
+       if (!sil->quiet) {
+               DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+                         addr, i2cbus->adapter.name, i2cbus->slave_addr);
+       }
+       return false;
+}
+
+static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+{
+       struct sil164_priv *sil= dvo->dev_priv;
+       struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+       uint8_t out_buf[2];
+       struct i2c_msg msg = {
+               .addr = i2cbus->slave_addr,
+               .flags = 0,
+               .len = 2,
+               .buf = out_buf,
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = ch;
+
+       if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+               return true;
+
+       if (!sil->quiet) {
+               DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+                         addr, i2cbus->adapter.name, i2cbus->slave_addr);
+       }
+
+       return false;
+}
+
+/* Silicon Image 164 driver for chip on i2c bus */
+static bool sil164_init(struct intel_dvo_device *dvo,
+                       struct intel_i2c_chan *i2cbus)
+{
+       /* this will detect the SIL164 chip on the specified i2c bus */
+       struct sil164_priv *sil;
+       unsigned char ch;
+
+       sil = kzalloc(sizeof(struct sil164_priv), GFP_KERNEL);
+       if (sil == NULL)
+               return false;
+
+       dvo->i2c_bus = i2cbus;
+       dvo->i2c_bus->slave_addr = dvo->slave_addr;
+       dvo->dev_priv = sil;
+       sil->quiet = true;
+
+       if (!sil164_readb(dvo, SIL164_VID_LO, &ch))
+               goto out;
+
+       if (ch != (SIL164_VID & 0xff)) {
+               DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n",
+                         ch, i2cbus->adapter.name, i2cbus->slave_addr);
+               goto out;
+       }
+
+       if (!sil164_readb(dvo, SIL164_DID_LO, &ch))
+               goto out;
+
+       if (ch != (SIL164_DID & 0xff)) {
+               DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n",
+                         ch, i2cbus->adapter.name, i2cbus->slave_addr);
+               goto out;
+       }
+       sil->quiet = false;
+
+       DRM_DEBUG("init sil164 dvo controller successfully!\n");
+       return true;
+
+out:
+       kfree(sil);
+       return false;
+}
+
+static enum drm_output_status sil164_detect(struct intel_dvo_device *dvo)
+{
+       uint8_t reg9;
+
+       sil164_readb(dvo, SIL164_REG9, &reg9);
+
+       if (reg9 & SIL164_9_HTPLG)
+               return output_status_connected;
+       else
+               return output_status_disconnected;
+}
+
+static enum drm_mode_status sil164_mode_valid(struct intel_dvo_device *dvo,
+                                             struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static void sil164_mode_set(struct intel_dvo_device *dvo,
+                           struct drm_display_mode *mode,
+                           struct drm_display_mode *adjusted_mode)
+{
+       /* As long as the basics are set up, since we don't have clock
+        * dependencies in the mode setup, we can just leave the
+        * registers alone and everything will work fine.
+        */
+       /* recommended programming sequence from doc */
+       /*sil164_writeb(sil, 0x08, 0x30);
+         sil164_writeb(sil, 0x09, 0x00);
+         sil164_writeb(sil, 0x0a, 0x90);
+         sil164_writeb(sil, 0x0c, 0x89);
+         sil164_writeb(sil, 0x08, 0x31);*/
+       /* don't do much */
+       return;
+}
+
+/* set the SIL164 power state */
+static void sil164_dpms(struct intel_dvo_device *dvo, int mode)
+{
+       int ret;
+       unsigned char ch;
+
+       ret = sil164_readb(dvo, SIL164_REG8, &ch);
+       if (ret == false)
+               return;
+
+       if (mode == DPMSModeOn)
+               ch |= SIL164_8_PD;
+       else
+               ch &= ~SIL164_8_PD;
+
+       sil164_writeb(dvo, SIL164_REG8, ch);
+       return;
+}
+
+static void sil164_dump_regs(struct intel_dvo_device *dvo)
+{
+       uint8_t val;
+
+       sil164_readb(dvo, SIL164_FREQ_LO, &val);
+       DRM_DEBUG("SIL164_FREQ_LO: 0x%02x\n", val);
+       sil164_readb(dvo, SIL164_FREQ_HI, &val);
+       DRM_DEBUG("SIL164_FREQ_HI: 0x%02x\n", val);
+       sil164_readb(dvo, SIL164_REG8, &val);
+       DRM_DEBUG("SIL164_REG8: 0x%02x\n", val);
+       sil164_readb(dvo, SIL164_REG9, &val);
+       DRM_DEBUG("SIL164_REG9: 0x%02x\n", val);
+       sil164_readb(dvo, SIL164_REGC, &val);
+       DRM_DEBUG("SIL164_REGC: 0x%02x\n", val);
+}
+
+static void sil164_save(struct intel_dvo_device *dvo)
+{
+       struct sil164_priv *sil= dvo->dev_priv;
+
+       if (!sil164_readb(dvo, SIL164_REG8, &sil->save_regs.reg8))
+               return;
+
+       if (!sil164_readb(dvo, SIL164_REG9, &sil->save_regs.reg9))
+               return;
+
+       if (!sil164_readb(dvo, SIL164_REGC, &sil->save_regs.regc))
+               return;
+
+       return;
+}
+
+static void sil164_restore(struct intel_dvo_device *dvo)
+{
+       struct sil164_priv *sil = dvo->dev_priv;
+
+       /* Restore it powered down initially */
+       sil164_writeb(dvo, SIL164_REG8, sil->save_regs.reg8 & ~0x1);
+
+       sil164_writeb(dvo, SIL164_REG9, sil->save_regs.reg9);
+       sil164_writeb(dvo, SIL164_REGC, sil->save_regs.regc);
+       sil164_writeb(dvo, SIL164_REG8, sil->save_regs.reg8);
+}
+
+static void sil164_destroy(struct intel_dvo_device *dvo)
+{
+       struct sil164_priv *sil = dvo->dev_priv;
+
+       if (sil) {
+               kfree(sil);
+               dvo->dev_priv = NULL;
+       }
+}
+
+struct intel_dvo_dev_ops sil164_ops = {
+       .init = sil164_init,
+       .detect = sil164_detect,
+       .mode_valid = sil164_mode_valid,
+       .mode_set = sil164_mode_set,
+       .dpms = sil164_dpms,
+       .dump_regs = sil164_dump_regs,
+       .save = sil164_save,
+       .restore = sil164_restore,
+       .destroy = sil164_destroy,
+};
diff --git a/linux-core/dvo_tfp410.c b/linux-core/dvo_tfp410.c
new file mode 100644 (file)
index 0000000..6444850
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * Copyright © 2007 Dave Mueller
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Dave Mueller <dave.mueller@gmx.ch>
+ *
+ */
+
+#include "dvo.h"
+
+/* register definitions according to the TFP410 data sheet */
+#define TFP410_VID             0x014C
+#define TFP410_DID             0x0410
+
+#define TFP410_VID_LO          0x00
+#define TFP410_VID_HI          0x01
+#define TFP410_DID_LO          0x02
+#define TFP410_DID_HI          0x03
+#define TFP410_REV             0x04
+
+#define TFP410_CTL_1           0x08
+#define TFP410_CTL_1_TDIS      (1<<6)
+#define TFP410_CTL_1_VEN       (1<<5)
+#define TFP410_CTL_1_HEN       (1<<4)
+#define TFP410_CTL_1_DSEL      (1<<3)
+#define TFP410_CTL_1_BSEL      (1<<2)
+#define TFP410_CTL_1_EDGE      (1<<1)
+#define TFP410_CTL_1_PD                (1<<0)
+
+#define TFP410_CTL_2           0x09
+#define TFP410_CTL_2_VLOW      (1<<7)
+#define TFP410_CTL_2_MSEL_MASK (0x7<<4)
+#define TFP410_CTL_2_MSEL      (1<<4)
+#define TFP410_CTL_2_TSEL      (1<<3)
+#define TFP410_CTL_2_RSEN      (1<<2)
+#define TFP410_CTL_2_HTPLG     (1<<1)
+#define TFP410_CTL_2_MDI       (1<<0)
+
+#define TFP410_CTL_3           0x0A
+#define TFP410_CTL_3_DK_MASK   (0x7<<5)
+#define TFP410_CTL_3_DK                (1<<5)
+#define TFP410_CTL_3_DKEN      (1<<4)
+#define TFP410_CTL_3_CTL_MASK  (0x7<<1)
+#define TFP410_CTL_3_CTL       (1<<1)
+
+#define TFP410_USERCFG         0x0B
+
+#define TFP410_DE_DLY          0x32
+
+#define TFP410_DE_CTL          0x33
+#define TFP410_DE_CTL_DEGEN    (1<<6)
+#define TFP410_DE_CTL_VSPOL    (1<<5)
+#define TFP410_DE_CTL_HSPOL    (1<<4)
+#define TFP410_DE_CTL_DEDLY8   (1<<0)
+
+#define TFP410_DE_TOP          0x34
+
+#define TFP410_DE_CNT_LO       0x36
+#define TFP410_DE_CNT_HI       0x37
+
+#define TFP410_DE_LIN_LO       0x38
+#define TFP410_DE_LIN_HI       0x39
+
+#define TFP410_H_RES_LO                0x3A
+#define TFP410_H_RES_HI                0x3B
+
+#define TFP410_V_RES_LO                0x3C
+#define TFP410_V_RES_HI                0x3D
+
+struct tfp410_save_rec {
+       uint8_t ctl1;
+       uint8_t ctl2;
+};
+
+struct tfp410_priv {
+       bool quiet;
+
+       struct tfp410_save_rec saved_reg;
+       struct tfp410_save_rec mode_reg;
+};
+
+static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
+{
+       struct tfp410_priv *tfp = dvo->dev_priv;
+       struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+       u8 out_buf[2];
+       u8 in_buf[2];
+
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = i2cbus->slave_addr,
+                       .flags = 0,
+                       .len = 1,
+                       .buf = out_buf,
+               },
+               {
+                       .addr = i2cbus->slave_addr,
+                       .flags = I2C_M_RD,
+                       .len = 1,
+                       .buf = in_buf,
+               }
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = 0;
+
+       if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
+               *ch = in_buf[0];
+               return true;
+       };
+
+       if (!tfp->quiet) {
+               DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
+                         addr, i2cbus->adapter.name, i2cbus->slave_addr);
+       }
+       return false;
+}
+
+static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+{
+       struct tfp410_priv *tfp = dvo->dev_priv;
+       struct intel_i2c_chan *i2cbus = dvo->i2c_bus;
+       uint8_t out_buf[2];
+       struct i2c_msg msg = {
+               .addr = i2cbus->slave_addr,
+               .flags = 0,
+               .len = 2,
+               .buf = out_buf,
+       };
+
+       out_buf[0] = addr;
+       out_buf[1] = ch;
+
+       if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
+               return true;
+
+       if (!tfp->quiet) {
+               DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
+                         addr, i2cbus->adapter.name, i2cbus->slave_addr);
+       }
+
+       return false;
+}
+
+static int tfp410_getid(struct intel_dvo_device *dvo, int addr)
+{
+       uint8_t ch1, ch2;
+
+       if (tfp410_readb(dvo, addr+0, &ch1) &&
+           tfp410_readb(dvo, addr+1, &ch2))
+               return ((ch2 << 8) & 0xFF00) | (ch1 & 0x00FF);
+
+       return -1;
+}
+
+/* Ti TFP410 driver for chip on i2c bus */
+static bool tfp410_init(struct intel_dvo_device *dvo,
+                       struct intel_i2c_chan *i2cbus)
+{
+       /* this will detect the tfp410 chip on the specified i2c bus */
+       struct tfp410_priv *tfp;
+       int id;
+
+       tfp = kzalloc(sizeof(struct tfp410_priv), GFP_KERNEL);
+       if (tfp == NULL)
+               return false;
+
+       dvo->i2c_bus = i2cbus;
+       dvo->i2c_bus->slave_addr = dvo->slave_addr;
+       dvo->dev_priv = tfp;
+       tfp->quiet = TRUE;
+
+       if ((id = tfp410_getid(dvo, TFP410_VID_LO)) != TFP410_VID) {
+               DRM_DEBUG("tfp410 not detected got VID %X: from %s Slave %d.\n",
+                         id, i2cbus->adapter.name, i2cbus->slave_addr);
+               goto out;
+       }
+
+       if ((id = tfp410_getid(dvo, TFP410_DID_LO)) != TFP410_DID) {
+               DRM_DEBUG("tfp410 not detected got DID %X: from %s Slave %d.\n",
+                         id, i2cbus->adapter.name, i2cbus->slave_addr);
+               goto out;
+       }
+       tfp->quiet = FALSE;
+       return true;
+out:
+       kfree(tfp);
+       return false;
+}
+
+static enum drm_output_status tfp410_detect(struct intel_dvo_device *dvo)
+{
+       enum drm_output_status ret = output_status_disconnected;
+       uint8_t ctl2;
+
+       if (tfp410_readb(dvo, TFP410_CTL_2, &ctl2)) {
+               if (ctl2 & TFP410_CTL_2_HTPLG)
+                       ret = output_status_connected;
+               else
+                       ret = output_status_disconnected;
+       }
+
+       return ret;
+}
+
+static enum drm_mode_status tfp410_mode_valid(struct intel_dvo_device *dvo,
+                                             struct drm_display_mode *mode)
+{
+       return MODE_OK;
+}
+
+static void tfp410_mode_set(struct intel_dvo_device *dvo,
+                           struct drm_display_mode *mode,
+                           struct drm_display_mode *adjusted_mode)
+{
+    /* As long as the basics are set up, since we don't have clock dependencies
+     * in the mode setup, we can just leave the registers alone and everything
+     * will work fine.
+     */
+    /* don't do much */
+    return;
+}
+
+/* set the tfp410 power state */
+static void tfp410_dpms(struct intel_dvo_device *dvo, int mode)
+{
+       uint8_t ctl1;
+
+       if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1))
+               return;
+
+       if (mode == DPMSModeOn)
+               ctl1 |= TFP410_CTL_1_PD;
+       else
+               ctl1 &= ~TFP410_CTL_1_PD;
+
+       tfp410_writeb(dvo, TFP410_CTL_1, ctl1);
+}
+
+static void tfp410_dump_regs(struct intel_dvo_device *dvo)
+{
+       uint8_t val, val2;
+
+       tfp410_readb(dvo, TFP410_REV, &val);
+       DRM_DEBUG("TFP410_REV: 0x%02X\n", val);
+       tfp410_readb(dvo, TFP410_CTL_1, &val);
+       DRM_DEBUG("TFP410_CTL1: 0x%02X\n", val);
+       tfp410_readb(dvo, TFP410_CTL_2, &val);
+       DRM_DEBUG("TFP410_CTL2: 0x%02X\n", val);
+       tfp410_readb(dvo, TFP410_CTL_3, &val);
+       DRM_DEBUG("TFP410_CTL3: 0x%02X\n", val);
+       tfp410_readb(dvo, TFP410_USERCFG, &val);
+       DRM_DEBUG("TFP410_USERCFG: 0x%02X\n", val);
+       tfp410_readb(dvo, TFP410_DE_DLY, &val);
+       DRM_DEBUG("TFP410_DE_DLY: 0x%02X\n", val);
+       tfp410_readb(dvo, TFP410_DE_CTL, &val);
+       DRM_DEBUG("TFP410_DE_CTL: 0x%02X\n", val);
+       tfp410_readb(dvo, TFP410_DE_TOP, &val);
+       DRM_DEBUG("TFP410_DE_TOP: 0x%02X\n", val);
+       tfp410_readb(dvo, TFP410_DE_CNT_LO, &val);
+       tfp410_readb(dvo, TFP410_DE_CNT_HI, &val2);
+       DRM_DEBUG("TFP410_DE_CNT: 0x%02X%02X\n", val2, val);
+       tfp410_readb(dvo, TFP410_DE_LIN_LO, &val);
+       tfp410_readb(dvo, TFP410_DE_LIN_HI, &val2);
+       DRM_DEBUG("TFP410_DE_LIN: 0x%02X%02X\n", val2, val);
+       tfp410_readb(dvo, TFP410_H_RES_LO, &val);
+       tfp410_readb(dvo, TFP410_H_RES_HI, &val2);
+       DRM_DEBUG("TFP410_H_RES: 0x%02X%02X\n", val2, val);
+       tfp410_readb(dvo, TFP410_V_RES_LO, &val);
+       tfp410_readb(dvo, TFP410_V_RES_HI, &val2);
+       DRM_DEBUG("TFP410_V_RES: 0x%02X%02X\n", val2, val);
+}
+
+static void tfp410_save(struct intel_dvo_device *dvo)
+{
+       struct tfp410_priv *tfp = dvo->dev_priv;
+
+       if (!tfp410_readb(dvo, TFP410_CTL_1, &tfp->saved_reg.ctl1))
+               return;
+
+       if (!tfp410_readb(dvo, TFP410_CTL_2, &tfp->saved_reg.ctl2))
+               return;
+}
+
+static void tfp410_restore(struct intel_dvo_device *dvo)
+{
+       struct tfp410_priv *tfp = dvo->dev_priv;
+
+       /* Restore it powered down initially */
+       tfp410_writeb(dvo, TFP410_CTL_1, tfp->saved_reg.ctl1 & ~0x1);
+
+       tfp410_writeb(dvo, TFP410_CTL_2, tfp->saved_reg.ctl2);
+       tfp410_writeb(dvo, TFP410_CTL_1, tfp->saved_reg.ctl1);
+}
+
+static void tfp410_destroy(struct intel_dvo_device *dvo)
+{
+       struct tfp410_priv *tfp = dvo->dev_priv;
+
+       if (tfp) {
+               kfree(tfp);
+               dvo->dev_priv = NULL;
+       }
+}
+
+struct intel_dvo_dev_ops tfp410_ops = {
+       .init = tfp410_init,
+       .detect = tfp410_detect,
+       .mode_valid = tfp410_mode_valid,
+       .mode_set = tfp410_mode_set,
+       .dpms = tfp410_dpms,
+       .dump_regs = tfp410_dump_regs,
+       .save = tfp410_save,
+       .restore = tfp410_restore,
+       .destroy = tfp410_destroy,
+};
index 6aa6125..46abb90 100644 (file)
@@ -1396,7 +1396,8 @@ static void intel_setup_outputs(struct drm_device *dev)
        if (IS_I9XX(dev)) {
                intel_sdvo_init(dev, SDVOB);
                intel_sdvo_init(dev, SDVOC);
-       }
+       } else
+               intel_dvo_init(dev);
 
        if (IS_I9XX(dev) && !IS_I915G(dev))
                intel_tv_init(dev);
index 51c52c8..e0e6b79 100644 (file)
@@ -69,6 +69,7 @@ extern bool intel_ddc_probe(struct drm_output *output);
 
 extern void intel_crt_init(struct drm_device *dev);
 extern void intel_sdvo_init(struct drm_device *dev, int output_device);
+extern void intel_dvo_init(struct drm_device *dev);
 extern void intel_tv_init(struct drm_device *dev);
 extern void intel_lvds_init(struct drm_device *dev);
 
diff --git a/linux-core/intel_dvo.c b/linux-core/intel_dvo.c
new file mode 100644 (file)
index 0000000..423b751
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *     Eric Anholt <eric@anholt.net>
+ */
+/*
+ * Copyright 2006 Dave Airlie <airlied@linux.ie>
+ */
+
+#include <linux/i2c.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "intel_drv.h"
+#include "i915_drm.h"
+#include "i915_drv.h"
+#include "dvo.h"
+
+#define SIL164_ADDR    0x38
+#define CH7xxx_ADDR    0x76
+#define TFP410_ADDR    0x38
+
+extern struct intel_dvo_dev_ops sil164_ops;
+extern struct intel_dvo_dev_ops ch7xxx_ops;
+extern struct intel_dvo_dev_ops ivch_ops;
+extern struct intel_dvo_dev_ops tfp410_ops;
+extern struct intel_dvo_dev_ops ch7017_ops;
+
+struct intel_dvo_device intel_dvo_devices[] = {
+       {
+               .type = INTEL_DVO_CHIP_TMDS,
+               .name = "sil164",
+               .dvo_reg = DVOC,
+               .slave_addr = SIL164_ADDR,
+               .dev_ops = &sil164_ops,
+       },
+       {
+               .type = INTEL_DVO_CHIP_TMDS,
+               .name = "ch7xxx",
+               .dvo_reg = DVOC,
+               .slave_addr = CH7xxx_ADDR,
+               .dev_ops = &ch7xxx_ops,
+       },
+       {
+               .type = INTEL_DVO_CHIP_LVDS,
+               .name = "ivch",
+               .dvo_reg = DVOA,
+               .slave_addr = 0x02, /* Might also be 0x44, 0x84, 0xc4 */
+               .dev_ops = &ivch_ops,
+       },
+       {
+               .type = INTEL_DVO_CHIP_TMDS,
+               .name = "tfp410",
+               .dvo_reg = DVOC,
+               .slave_addr = TFP410_ADDR,
+               .dev_ops = &tfp410_ops,
+       },
+       {
+               .type = INTEL_DVO_CHIP_LVDS,
+               .name = "ch7017",
+               .dvo_reg = DVOC,
+               .slave_addr = 0x75,
+               .gpio = GPIOE,
+               .dev_ops = &ch7017_ops,
+       }
+};
+
+static void intel_dvo_dpms(struct drm_output *output, int mode)
+{
+       struct drm_i915_private *dev_priv = output->dev->dev_private;
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_dvo_device *dvo = intel_output->dev_priv;
+       u32 dvo_reg = dvo->dvo_reg;
+       u32 temp = I915_READ(dvo_reg);
+
+       if (mode == DPMSModeOn) {
+               I915_WRITE(dvo_reg, temp | DVO_ENABLE);
+               I915_READ(dvo_reg);
+               dvo->dev_ops->dpms(dvo, mode);
+       } else {
+               dvo->dev_ops->dpms(dvo, mode);
+               I915_WRITE(dvo_reg, temp & ~DVO_ENABLE);
+               I915_READ(dvo_reg);
+       }
+}
+
+static void intel_dvo_save(struct drm_output *output)
+{
+       struct drm_i915_private *dev_priv = output->dev->dev_private;
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+       /* Each output should probably just save the registers it touches,
+        * but for now, use more overkill.
+        */
+       dev_priv->saveDVOA = I915_READ(DVOA);
+       dev_priv->saveDVOB = I915_READ(DVOB);
+       dev_priv->saveDVOC = I915_READ(DVOC);
+
+       dvo->dev_ops->save(dvo);
+}
+
+static void intel_dvo_restore(struct drm_output *output)
+{
+       struct drm_i915_private *dev_priv = output->dev->dev_private;
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+       dvo->dev_ops->restore(dvo);
+
+       I915_WRITE(DVOA, dev_priv->saveDVOA);
+       I915_WRITE(DVOB, dev_priv->saveDVOB);
+       I915_WRITE(DVOC, dev_priv->saveDVOC);
+}
+
+static int intel_dvo_mode_valid(struct drm_output *output,
+                               struct drm_display_mode *mode)
+{
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+       if (mode->flags & V_DBLSCAN)
+               return MODE_NO_DBLESCAN;
+
+       /* XXX: Validate clock range */
+
+       if (dvo->panel_fixed_mode) {
+               if (mode->hdisplay > dvo->panel_fixed_mode->hdisplay)
+                       return MODE_PANEL;
+               if (mode->vdisplay > dvo->panel_fixed_mode->vdisplay)
+                       return MODE_PANEL;
+       }
+
+       return dvo->dev_ops->mode_valid(dvo, mode);
+}
+
+static bool intel_dvo_mode_fixup(struct drm_output *output,
+                                struct drm_display_mode *mode,
+                                struct drm_display_mode *adjusted_mode)
+{
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+       /* If we have timings from the BIOS for the panel, put them in
+        * to the adjusted mode.  The CRTC will be set up for this mode,
+        * with the panel scaling set up to source from the H/VDisplay
+        * of the original mode.
+        */
+       if (dvo->panel_fixed_mode != NULL) {
+#define C(x) adjusted_mode->x = dvo->panel_fixed_mode->x
+               C(hdisplay);
+               C(hsync_start);
+               C(hsync_end);
+               C(htotal);
+               C(vdisplay);
+               C(vsync_start);
+               C(vsync_end);
+               C(vtotal);
+               C(clock);
+               drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
+#undef C
+       }
+
+       if (dvo->dev_ops->mode_fixup)
+               return dvo->dev_ops->mode_fixup(dvo, mode, adjusted_mode);
+
+       return true;
+}
+
+static void intel_dvo_mode_set(struct drm_output *output,
+                              struct drm_display_mode *mode,
+                              struct drm_display_mode *adjusted_mode)
+{
+       struct drm_device *dev = output->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = output->crtc->driver_private;
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_dvo_device *dvo = intel_output->dev_priv;
+       int pipe = intel_crtc->pipe;
+       u32 dvo_val;
+       u32 dvo_reg = dvo->dvo_reg, dvo_srcdim_reg;
+       int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+
+       switch (dvo_reg) {
+       case DVOA:
+       default:
+               dvo_srcdim_reg = DVOA_SRCDIM;
+               break;
+       case DVOB:
+               dvo_srcdim_reg = DVOB_SRCDIM;
+               break;
+       case DVOC:
+               dvo_srcdim_reg = DVOC_SRCDIM;
+               break;
+       }
+
+       dvo->dev_ops->mode_set(dvo, mode, adjusted_mode);
+
+       /* Save the data order, since I don't know what it should be set to. */
+       dvo_val = I915_READ(dvo_reg) &
+                 (DVO_PRESERVE_MASK | DVO_DATA_ORDER_GBRG);
+       dvo_val |= DVO_DATA_ORDER_FP | DVO_BORDER_ENABLE |
+                  DVO_BLANK_ACTIVE_HIGH;
+
+       if (pipe == 1)
+               dvo_val |= DVO_PIPE_B_SELECT;
+       dvo_val |= DVO_PIPE_STALL;
+       if (adjusted_mode->flags & V_PHSYNC)
+               dvo_val |= DVO_HSYNC_ACTIVE_HIGH;
+       if (adjusted_mode->flags & V_PVSYNC)
+               dvo_val |= DVO_VSYNC_ACTIVE_HIGH;
+
+       I915_WRITE(dpll_reg, I915_READ(dpll_reg) | DPLL_DVO_HIGH_SPEED);
+
+       /*I915_WRITE(DVOB_SRCDIM,
+         (adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) |
+         (adjusted_mode->VDisplay << DVO_SRCDIM_VERTICAL_SHIFT));*/
+       I915_WRITE(dvo_srcdim_reg,
+                  (adjusted_mode->hdisplay << DVO_SRCDIM_HORIZONTAL_SHIFT) |
+                  (adjusted_mode->vdisplay << DVO_SRCDIM_VERTICAL_SHIFT));
+       /*I915_WRITE(DVOB, dvo_val);*/
+       I915_WRITE(dvo_reg, dvo_val);
+}
+
+/**
+ * Detect the output connection on our DVO device.
+ *
+ * Unimplemented.
+ */
+static enum drm_output_status intel_dvo_detect(struct drm_output *output)
+{
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+       return dvo->dev_ops->detect(dvo);
+}
+
+static int intel_dvo_get_modes(struct drm_output *output)
+{
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+       /* We should probably have an i2c driver get_modes function for those
+        * devices which will have a fixed set of modes determined by the chip
+        * (TV-out, for example), but for now with just TMDS and LVDS,
+        * that's not the case.
+        */
+       intel_ddc_get_modes(output);
+       if (!list_empty(&output->probed_modes))
+               return 1;
+
+#if 0
+       if (intel_output->i2c_drv->vid_rec->get_modes)
+       {
+               modes = intel_output->i2c_drv->vid_rec->get_modes (intel_output->i2c_drv->dev_priv);
+               if (modes != NULL)
+                       return modes;
+       }
+#endif
+
+       if (dvo->panel_fixed_mode != NULL) {
+               struct drm_display_mode *mode;
+               mode = drm_mode_duplicate(output->dev, dvo->panel_fixed_mode);
+               if (mode) {
+                       drm_mode_probed_add(output, mode);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static void intel_dvo_destroy (struct drm_output *output)
+{
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_dvo_device *dvo = intel_output->dev_priv;
+
+       if (dvo) {
+               if (dvo->dev_ops->destroy)
+                       dvo->dev_ops->destroy(dvo);
+               if (dvo->panel_fixed_mode)
+                       kfree(dvo->panel_fixed_mode);
+               /* no need, in i830_dvoices[] now */
+               //kfree(dvo);
+       }
+       if (intel_output) {
+               if (intel_output->i2c_bus)
+                       intel_i2c_destroy(intel_output->i2c_bus);
+               if (intel_output->ddc_bus)
+                       intel_i2c_destroy(intel_output->ddc_bus);
+               kfree(intel_output);
+               output->driver_private = NULL;
+       }
+}
+
+#ifdef RANDR_GET_CRTC_INTERFACE
+static struct drm_crtc *intel_dvo_get_crtc(struct drm_output *output)
+{
+       struct drm_device *dev = output->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_dvo_device *dvo = intel_output->dev_priv;
+       int pipe = !!(I915_READ(dvo->dvo_reg) & SDVO_PIPE_B_SELECT);
+
+       return intel_pipe_to_crtc(pScrn, pipe);
+}
+#endif
+
+
+static const struct drm_output_funcs intel_dvo_output_funcs = {
+       .dpms = intel_dvo_dpms,
+       .save = intel_dvo_save,
+       .restore = intel_dvo_restore,
+       .mode_valid = intel_dvo_mode_valid,
+       .mode_fixup = intel_dvo_mode_fixup,
+       .prepare = intel_output_prepare,
+       .mode_set = intel_dvo_mode_set,
+       .commit = intel_output_commit,
+       .detect = intel_dvo_detect,
+       .get_modes = intel_dvo_get_modes,
+       .cleanup = intel_dvo_destroy
+};
+
+/**
+ * Attempts to get a fixed panel timing for LVDS (currently only the i830).
+ *
+ * Other chips with DVO LVDS will need to extend this to deal with the LVDS
+ * chip being on DVOB/C and having multiple pipes.
+ */
+static struct drm_display_mode *
+intel_dvo_get_current_mode (struct drm_output *output)
+{
+       struct drm_device *dev = output->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_output *intel_output = output->driver_private;
+       struct intel_dvo_device *dvo = intel_output->dev_priv;
+       uint32_t dvo_reg = dvo->dvo_reg;
+       uint32_t dvo_val = I915_READ(dvo_reg);
+       struct drm_display_mode *mode = NULL;
+
+       /* If the DVO port is active, that'll be the LVDS, so we can pull out
+        * its timings to get how the BIOS set up the panel.
+        */
+       if (dvo_val & DVO_ENABLE) {
+               struct drm_crtc *crtc;
+               int pipe = (dvo_val & DVO_PIPE_B_SELECT) ? 1 : 0;
+
+               crtc = intel_get_crtc_from_pipe(dev, pipe);
+               if (crtc) {
+                       mode = intel_crtc_mode_get(dev, crtc);
+
+                       if (mode) {
+                               mode->type |= DRM_MODE_TYPE_PREFERRED;
+                               if (dvo_val & DVO_HSYNC_ACTIVE_HIGH)
+                                       mode->flags |= V_PHSYNC;
+                               if (dvo_val & DVO_VSYNC_ACTIVE_HIGH)
+                                       mode->flags |= V_PVSYNC;
+                       }
+               }
+       }
+       return mode;
+}
+
+void intel_dvo_init(struct drm_device *dev)
+{
+       struct intel_output *intel_output;
+       struct intel_dvo_device *dvo;
+       struct intel_i2c_chan *i2cbus = NULL;
+       int ret = 0;
+       int i;
+       int gpio_inited = 0;
+       int connector = ConnectorUnknown;
+
+       intel_output = kzalloc (sizeof(struct intel_output), GFP_KERNEL);
+       if (!intel_output)
+               return;
+
+       /* Set up the DDC bus */
+       intel_output->ddc_bus = intel_i2c_create(dev, GPIOD, "DVODDC_D");
+       if (!intel_output->ddc_bus)
+               goto free_intel;
+
+       /* Now, try to find a controller */
+       for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) {
+               struct drm_output *output = NULL;
+               int gpio;
+
+               dvo = &intel_dvo_devices[i];
+
+               /* Allow the I2C driver info to specify the GPIO to be used in
+                * special cases, but otherwise default to what's defined
+                * in the spec.
+                */
+               if (dvo->gpio != 0)
+                       gpio = dvo->gpio;
+               else if (dvo->type == INTEL_DVO_CHIP_LVDS)
+                       gpio = GPIOB;
+               else
+                       gpio = GPIOE;
+
+               /* Set up the I2C bus necessary for the chip we're probing.
+                * It appears that everything is on GPIOE except for panels
+                * on i830 laptops, which are on GPIOB (DVOA).
+                */
+               if (gpio_inited != gpio) {
+                       if (i2cbus != NULL)
+                               intel_i2c_destroy(i2cbus);
+                       if (!(i2cbus = intel_i2c_create(dev, gpio,
+                               gpio == GPIOB ? "DVOI2C_B" : "DVOI2C_E"))) {
+                               continue;
+                       }
+                       gpio_inited = gpio;
+               }
+
+               if (dvo->dev_ops!= NULL)
+                       ret = dvo->dev_ops->init(dvo, i2cbus);
+               else
+                       ret = false;
+
+               if (!ret)
+                       continue;
+
+               intel_output->type = INTEL_OUTPUT_DVO;
+               switch (dvo->type) {
+               case INTEL_DVO_CHIP_TMDS:
+                       connector = ConnectorDVID;
+                       output = drm_output_create(dev, &intel_dvo_output_funcs,
+                                       DRM_MODE_OUTPUT_TMDS);
+                       break;
+               case INTEL_DVO_CHIP_LVDS:
+                       connector = ConnectorLVDS;
+                       output = drm_output_create(dev, &intel_dvo_output_funcs,
+                                       DRM_MODE_OUTPUT_LVDS);
+                       break;
+               }
+               if (output == NULL) {
+                       dvo->dev_ops->destroy(dvo);
+                       goto free_i2c;
+               }
+
+               output->driver_private = intel_output;
+               output->display_info.subpixel_order = SubPixelHorizontalRGB;
+               output->interlace_allowed = false;
+               output->doublescan_allowed = false;
+
+               intel_output->dev_priv = dvo;
+               intel_output->i2c_bus = i2cbus;
+
+               if (dvo->type == INTEL_DVO_CHIP_LVDS) {
+                       /* For our LVDS chipsets, we should hopefully be able
+                        * to dig the fixed panel mode out of the BIOS data.
+                        * However, it's in a different format from the BIOS
+                        * data on chipsets with integrated LVDS (stored in AIM
+                        * headers, likely), so for now, just get the current
+                        * mode being output through DVO.
+                        */
+                       dvo->panel_fixed_mode = intel_dvo_get_current_mode(output);
+                       dvo->panel_wants_dither = true;
+               }
+
+               drm_output_attach_property(output,
+                               dev->mode_config.connector_type_property,
+                               connector);
+               return;
+       }
+
+free_i2c:
+       intel_i2c_destroy(intel_output->ddc_bus);
+       /* Didn't find a chip, so tear down. */
+       if (i2cbus != NULL)
+               intel_i2c_destroy(i2cbus);
+free_intel:
+       kfree(intel_output);
+}
index e32c36f..80d8d46 100644 (file)
@@ -685,11 +685,39 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
 #define PPCR_ON                        (1<<0)
 
 #define DVOA                   0x61120
-#define DVOA_ON                        (1<<31)
 #define DVOB                   0x61140
-#define DVOB_ON                        (1<<31)
 #define DVOC                   0x61160
-#define DVOC_ON                        (1<<31)
+#define DVO_ENABLE                     (1 << 31)
+#define DVO_PIPE_B_SELECT              (1 << 30)
+#define DVO_PIPE_STALL_UNUSED          (0 << 28)
+#define DVO_PIPE_STALL                 (1 << 28)
+#define DVO_PIPE_STALL_TV              (2 << 28)
+#define DVO_PIPE_STALL_MASK            (3 << 28)
+#define DVO_USE_VGA_SYNC               (1 << 15)
+#define DVO_DATA_ORDER_I740            (0 << 14)
+#define DVO_DATA_ORDER_FP              (1 << 14)
+#define DVO_VSYNC_DISABLE              (1 << 11)
+#define DVO_HSYNC_DISABLE              (1 << 10)
+#define DVO_VSYNC_TRISTATE             (1 << 9)
+#define DVO_HSYNC_TRISTATE             (1 << 8)
+#define DVO_BORDER_ENABLE              (1 << 7)
+#define DVO_DATA_ORDER_GBRG            (1 << 6)
+#define DVO_DATA_ORDER_RGGB            (0 << 6)
+#define DVO_DATA_ORDER_GBRG_ERRATA     (0 << 6)
+#define DVO_DATA_ORDER_RGGB_ERRATA     (1 << 6)
+#define DVO_VSYNC_ACTIVE_HIGH          (1 << 4)
+#define DVO_HSYNC_ACTIVE_HIGH          (1 << 3)
+#define DVO_BLANK_ACTIVE_HIGH          (1 << 2)
+#define DVO_OUTPUT_CSTATE_PIXELS       (1 << 1)        /* SDG only */
+#define DVO_OUTPUT_SOURCE_SIZE_PIXELS  (1 << 0)        /* SDG only */
+#define DVO_PRESERVE_MASK      (0x7<<24)
+
+#define DVOA_SRCDIM            0x61124
+#define DVOB_SRCDIM            0x61144
+#define DVOC_SRCDIM            0x61164
+#define DVO_SRCDIM_HORIZONTAL_SHIFT    12
+#define DVO_SRCDIM_VERTICAL_SHIFT      0
+
 #define LVDS                   0x61180
 #define LVDS_ON                        (1<<31)