Subversion Repositories Kolibri OS

Compare Revisions

Regard whitespace Rev 2329 → Rev 2330

/drivers/video/drm/i915/dvo.h
0,0 → 1,144
/*
* 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 {
const 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;
 
const struct intel_dvo_dev_ops *dev_ops;
void *dev_priv;
struct i2c_adapter *i2c_bus;
};
 
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 i2c_adapter *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);
 
/*
* 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_connector_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);
 
/**
* 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);
};
 
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;
 
#endif /* _INTEL_DVO_H */
/drivers/video/drm/i915/dvo_ch7017.c
0,0 → 1,401
/*
* 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 dummy;
};
 
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, u8 addr, u8 *val)
{
struct i2c_msg msgs[] = {
{
.addr = dvo->slave_addr,
.flags = 0,
.len = 1,
.buf = &addr,
},
{
.addr = dvo->slave_addr,
.flags = I2C_M_RD,
.len = 1,
.buf = val,
}
};
return i2c_transfer(dvo->i2c_bus, msgs, 2) == 2;
}
 
static bool ch7017_write(struct intel_dvo_device *dvo, u8 addr, u8 val)
{
uint8_t buf[2] = { addr, val };
struct i2c_msg msg = {
.addr = dvo->slave_addr,
.flags = 0,
.len = 2,
.buf = buf,
};
return i2c_transfer(dvo->i2c_bus, &msg, 1) == 1;
}
 
/** Probes for a CH7017 on the given bus and slave address. */
static bool ch7017_init(struct intel_dvo_device *dvo,
struct i2c_adapter *adapter)
{
struct ch7017_priv *priv;
const char *str;
u8 val;
 
priv = kzalloc(sizeof(struct ch7017_priv), GFP_KERNEL);
if (priv == NULL)
return false;
 
dvo->i2c_bus = adapter;
dvo->dev_priv = priv;
 
if (!ch7017_read(dvo, CH7017_DEVICE_ID, &val))
goto fail;
 
switch (val) {
case CH7017_DEVICE_ID_VALUE:
str = "ch7017";
break;
case CH7018_DEVICE_ID_VALUE:
str = "ch7018";
break;
case CH7019_DEVICE_ID_VALUE:
str = "ch7019";
break;
default:
DRM_DEBUG_KMS("ch701x not detected, got %d: from %s "
"slave %d.\n",
val, adapter->name,dvo->slave_addr);
goto fail;
}
 
DRM_DEBUG_KMS("%s detected on %s, addr %d\n",
str, adapter->name, dvo->slave_addr);
return true;
 
fail:
kfree(priv);
return false;
}
 
static enum drm_connector_status ch7017_detect(struct intel_dvo_device *dvo)
{
return connector_status_connected;
}
 
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_KMS("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, DRM_MODE_DPMS_OFF);
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_KMS("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 == DRM_MODE_DPMS_ON) {
/* 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 */
msleep(20);
}
 
static void ch7017_dump_regs(struct intel_dvo_device *dvo)
{
uint8_t val;
 
#define DUMP(reg) \
do { \
ch7017_read(dvo, reg, &val); \
DRM_DEBUG_KMS(#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_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,
.destroy = ch7017_destroy,
};
/drivers/video/drm/i915/dvo_ch7xxx.c
0,0 → 1,331
/**************************************************************************
 
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_priv {
bool quiet;
};
 
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 i2c_adapter *adapter = dvo->i2c_bus;
u8 out_buf[2];
u8 in_buf[2];
 
struct i2c_msg msgs[] = {
{
.addr = dvo->slave_addr,
.flags = 0,
.len = 1,
.buf = out_buf,
},
{
.addr = dvo->slave_addr,
.flags = I2C_M_RD,
.len = 1,
.buf = in_buf,
}
};
 
out_buf[0] = addr;
out_buf[1] = 0;
 
if (i2c_transfer(adapter, msgs, 2) == 2) {
*ch = in_buf[0];
return true;
};
 
if (!ch7xxx->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
addr, adapter->name, dvo->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 i2c_adapter *adapter = dvo->i2c_bus;
uint8_t out_buf[2];
struct i2c_msg msg = {
.addr = dvo->slave_addr,
.flags = 0,
.len = 2,
.buf = out_buf,
};
 
out_buf[0] = addr;
out_buf[1] = ch;
 
if (i2c_transfer(adapter, &msg, 1) == 1)
return true;
 
if (!ch7xxx->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
addr, adapter->name, dvo->slave_addr);
}
 
return false;
}
 
static bool ch7xxx_init(struct intel_dvo_device *dvo,
struct i2c_adapter *adapter)
{
/* 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 = adapter;
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_KMS("ch7xxx not detected; got 0x%02x from %s "
"slave %d.\n",
vendor, adapter->name, dvo->slave_addr);
goto out;
}
 
 
if (!ch7xxx_readb(dvo, CH7xxx_REG_DID, &device))
goto out;
 
if (device != CH7xxx_DID) {
DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
"slave %d.\n",
vendor, adapter->name, dvo->slave_addr);
goto out;
}
 
ch7xxx->quiet = false;
DRM_DEBUG_KMS("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
name, vendor, device);
return true;
out:
kfree(ch7xxx);
return false;
}
 
static enum drm_connector_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 connector_status_connected;
return connector_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 & DRM_MODE_FLAG_PHSYNC)
idf |= CH7xxx_IDF_HSP;
 
if (mode->flags & DRM_MODE_FLAG_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 == DRM_MODE_DPMS_ON)
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)
{
int i;
 
for (i = 0; i < CH7xxx_NUM_REGS; i++) {
uint8_t val;
if ((i % 8) == 0 )
DRM_LOG_KMS("\n %02X: ", i);
ch7xxx_readb(dvo, i, &val);
DRM_LOG_KMS("%02X ", val);
}
}
 
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,
.destroy = ch7xxx_destroy,
};
/drivers/video/drm/i915/dvo_ivch.c
0,0 → 1,421
/*
* 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;
};
 
 
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 i2c_adapter *adapter = dvo->i2c_bus;
u8 out_buf[1];
u8 in_buf[2];
 
struct i2c_msg msgs[] = {
{
.addr = dvo->slave_addr,
.flags = I2C_M_RD,
.len = 0,
},
{
.addr = 0,
.flags = I2C_M_NOSTART,
.len = 1,
.buf = out_buf,
},
{
.addr = dvo->slave_addr,
.flags = I2C_M_RD | I2C_M_NOSTART,
.len = 2,
.buf = in_buf,
}
};
 
out_buf[0] = addr;
 
if (i2c_transfer(adapter, msgs, 3) == 3) {
*data = (in_buf[1] << 8) | in_buf[0];
return true;
};
 
if (!priv->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from "
"%s:%02x.\n",
addr, adapter->name, dvo->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 i2c_adapter *adapter = dvo->i2c_bus;
u8 out_buf[3];
struct i2c_msg msg = {
.addr = dvo->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(adapter, &msg, 1) == 1)
return true;
 
if (!priv->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
addr, adapter->name, dvo->slave_addr);
}
 
return false;
}
 
/** Probes the given bus and slave address for an ivch */
static bool ivch_init(struct intel_dvo_device *dvo,
struct i2c_adapter *adapter)
{
struct ivch_priv *priv;
uint16_t temp;
 
priv = kzalloc(sizeof(struct ivch_priv), GFP_KERNEL);
if (priv == NULL)
return false;
 
dvo->i2c_bus = adapter;
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_KMS("ivch detect failed due to address mismatch "
"(%d vs %d)\n",
(temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr);
goto out;
}
 
ivch_read(dvo, VR20, &priv->width);
ivch_read(dvo, VR21, &priv->height);
 
return true;
 
out:
kfree(priv);
return false;
}
 
static enum drm_connector_status ivch_detect(struct intel_dvo_device *dvo)
{
return connector_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 == DRM_MODE_DPMS_ON)
backlight = 1;
else
backlight = 0;
ivch_write(dvo, VR80, backlight);
 
if (mode == DRM_MODE_DPMS_ON)
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 == DRM_MODE_DPMS_ON))
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_LOG_KMS("VR00: 0x%04x\n", val);
ivch_read(dvo, VR01, &val);
DRM_LOG_KMS("VR01: 0x%04x\n", val);
ivch_read(dvo, VR30, &val);
DRM_LOG_KMS("VR30: 0x%04x\n", val);
ivch_read(dvo, VR40, &val);
DRM_LOG_KMS("VR40: 0x%04x\n", val);
 
/* GPIO registers */
ivch_read(dvo, VR80, &val);
DRM_LOG_KMS("VR80: 0x%04x\n", val);
ivch_read(dvo, VR81, &val);
DRM_LOG_KMS("VR81: 0x%04x\n", val);
ivch_read(dvo, VR82, &val);
DRM_LOG_KMS("VR82: 0x%04x\n", val);
ivch_read(dvo, VR83, &val);
DRM_LOG_KMS("VR83: 0x%04x\n", val);
ivch_read(dvo, VR84, &val);
DRM_LOG_KMS("VR84: 0x%04x\n", val);
ivch_read(dvo, VR85, &val);
DRM_LOG_KMS("VR85: 0x%04x\n", val);
ivch_read(dvo, VR86, &val);
DRM_LOG_KMS("VR86: 0x%04x\n", val);
ivch_read(dvo, VR87, &val);
DRM_LOG_KMS("VR87: 0x%04x\n", val);
ivch_read(dvo, VR88, &val);
DRM_LOG_KMS("VR88: 0x%04x\n", val);
 
/* Scratch register 0 - AIM Panel type */
ivch_read(dvo, VR8E, &val);
DRM_LOG_KMS("VR8E: 0x%04x\n", val);
 
/* Scratch register 1 - Status register */
ivch_read(dvo, VR8F, &val);
DRM_LOG_KMS("VR8F: 0x%04x\n", val);
}
 
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,
.mode_valid = ivch_mode_valid,
.mode_set = ivch_mode_set,
.detect = ivch_detect,
.dump_regs = ivch_dump_regs,
.destroy = ivch_destroy,
};
/drivers/video/drm/i915/dvo_sil164.c
0,0 → 1,263
/**************************************************************************
 
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_priv {
//I2CDevRec d;
bool quiet;
};
 
#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 i2c_adapter *adapter = dvo->i2c_bus;
u8 out_buf[2];
u8 in_buf[2];
 
struct i2c_msg msgs[] = {
{
.addr = dvo->slave_addr,
.flags = 0,
.len = 1,
.buf = out_buf,
},
{
.addr = dvo->slave_addr,
.flags = I2C_M_RD,
.len = 1,
.buf = in_buf,
}
};
 
out_buf[0] = addr;
out_buf[1] = 0;
 
if (i2c_transfer(adapter, msgs, 2) == 2) {
*ch = in_buf[0];
return true;
};
 
if (!sil->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
addr, adapter->name, dvo->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 i2c_adapter *adapter = dvo->i2c_bus;
uint8_t out_buf[2];
struct i2c_msg msg = {
.addr = dvo->slave_addr,
.flags = 0,
.len = 2,
.buf = out_buf,
};
 
out_buf[0] = addr;
out_buf[1] = ch;
 
if (i2c_transfer(adapter, &msg, 1) == 1)
return true;
 
if (!sil->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
addr, adapter->name, dvo->slave_addr);
}
 
return false;
}
 
/* Silicon Image 164 driver for chip on i2c bus */
static bool sil164_init(struct intel_dvo_device *dvo,
struct i2c_adapter *adapter)
{
/* 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 = adapter;
dvo->dev_priv = sil;
sil->quiet = true;
 
if (!sil164_readb(dvo, SIL164_VID_LO, &ch))
goto out;
 
if (ch != (SIL164_VID & 0xff)) {
DRM_DEBUG_KMS("sil164 not detected got %d: from %s Slave %d.\n",
ch, adapter->name, dvo->slave_addr);
goto out;
}
 
if (!sil164_readb(dvo, SIL164_DID_LO, &ch))
goto out;
 
if (ch != (SIL164_DID & 0xff)) {
DRM_DEBUG_KMS("sil164 not detected got %d: from %s Slave %d.\n",
ch, adapter->name, dvo->slave_addr);
goto out;
}
sil->quiet = false;
 
DRM_DEBUG_KMS("init sil164 dvo controller successfully!\n");
return true;
 
out:
kfree(sil);
return false;
}
 
static enum drm_connector_status sil164_detect(struct intel_dvo_device *dvo)
{
uint8_t reg9;
 
sil164_readb(dvo, SIL164_REG9, &reg9);
 
if (reg9 & SIL164_9_HTPLG)
return connector_status_connected;
else
return connector_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 == DRM_MODE_DPMS_ON)
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_LOG_KMS("SIL164_FREQ_LO: 0x%02x\n", val);
sil164_readb(dvo, SIL164_FREQ_HI, &val);
DRM_LOG_KMS("SIL164_FREQ_HI: 0x%02x\n", val);
sil164_readb(dvo, SIL164_REG8, &val);
DRM_LOG_KMS("SIL164_REG8: 0x%02x\n", val);
sil164_readb(dvo, SIL164_REG9, &val);
DRM_LOG_KMS("SIL164_REG9: 0x%02x\n", val);
sil164_readb(dvo, SIL164_REGC, &val);
DRM_LOG_KMS("SIL164_REGC: 0x%02x\n", val);
}
 
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,
.destroy = sil164_destroy,
};
/drivers/video/drm/i915/dvo_tfp410.c
0,0 → 1,304
/*
* 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_priv {
bool quiet;
};
 
static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
{
struct tfp410_priv *tfp = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
u8 out_buf[2];
u8 in_buf[2];
 
struct i2c_msg msgs[] = {
{
.addr = dvo->slave_addr,
.flags = 0,
.len = 1,
.buf = out_buf,
},
{
.addr = dvo->slave_addr,
.flags = I2C_M_RD,
.len = 1,
.buf = in_buf,
}
};
 
out_buf[0] = addr;
out_buf[1] = 0;
 
if (i2c_transfer(adapter, msgs, 2) == 2) {
*ch = in_buf[0];
return true;
};
 
if (!tfp->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
addr, adapter->name, dvo->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 i2c_adapter *adapter = dvo->i2c_bus;
uint8_t out_buf[2];
struct i2c_msg msg = {
.addr = dvo->slave_addr,
.flags = 0,
.len = 2,
.buf = out_buf,
};
 
out_buf[0] = addr;
out_buf[1] = ch;
 
if (i2c_transfer(adapter, &msg, 1) == 1)
return true;
 
if (!tfp->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
addr, adapter->name, dvo->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 i2c_adapter *adapter)
{
/* 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 = adapter;
dvo->dev_priv = tfp;
tfp->quiet = true;
 
if ((id = tfp410_getid(dvo, TFP410_VID_LO)) != TFP410_VID) {
DRM_DEBUG_KMS("tfp410 not detected got VID %X: from %s "
"Slave %d.\n",
id, adapter->name, dvo->slave_addr);
goto out;
}
 
if ((id = tfp410_getid(dvo, TFP410_DID_LO)) != TFP410_DID) {
DRM_DEBUG_KMS("tfp410 not detected got DID %X: from %s "
"Slave %d.\n",
id, adapter->name, dvo->slave_addr);
goto out;
}
tfp->quiet = false;
return true;
out:
kfree(tfp);
return false;
}
 
static enum drm_connector_status tfp410_detect(struct intel_dvo_device *dvo)
{
enum drm_connector_status ret = connector_status_disconnected;
uint8_t ctl2;
 
if (tfp410_readb(dvo, TFP410_CTL_2, &ctl2)) {
if (ctl2 & TFP410_CTL_2_RSEN)
ret = connector_status_connected;
else
ret = connector_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 == DRM_MODE_DPMS_ON)
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_LOG_KMS("TFP410_REV: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_CTL_1, &val);
DRM_LOG_KMS("TFP410_CTL1: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_CTL_2, &val);
DRM_LOG_KMS("TFP410_CTL2: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_CTL_3, &val);
DRM_LOG_KMS("TFP410_CTL3: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_USERCFG, &val);
DRM_LOG_KMS("TFP410_USERCFG: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_DE_DLY, &val);
DRM_LOG_KMS("TFP410_DE_DLY: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_DE_CTL, &val);
DRM_LOG_KMS("TFP410_DE_CTL: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_DE_TOP, &val);
DRM_LOG_KMS("TFP410_DE_TOP: 0x%02X\n", val);
tfp410_readb(dvo, TFP410_DE_CNT_LO, &val);
tfp410_readb(dvo, TFP410_DE_CNT_HI, &val2);
DRM_LOG_KMS("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_LOG_KMS("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_LOG_KMS("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_LOG_KMS("TFP410_V_RES: 0x%02X%02X\n", val2, val);
}
 
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,
.destroy = tfp410_destroy,
};
/drivers/video/drm/i915/i915_dma.c
31,7 → 31,7
#include "drm_crtc_helper.h"
#include "drm_fb_helper.h"
#include "intel_drv.h"
//#include "i915_drm.h"
#include "i915_drm.h"
#include "i915_drv.h"
#include <drm/intel-gtt.h>
//#include "i915_trace.h"
41,11 → 41,20
//#include <linux/acpi.h>
//#include <linux/pnp.h>
//#include <linux/vga_switcheroo.h>
//#include <linux/slab.h>
#include <linux/slab.h>
//#include <acpi/video.h>
 
void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen);
 
static inline int pci_read_config_dword(struct pci_dev *dev, int where,
u32 *val)
{
*val = PciRead32(dev->busnr, dev->devfn, where);
return 1;
}
 
 
 
static void i915_write_hws_pga(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
57,7 → 66,6
I915_WRITE(HWS_PGA, addr);
}
 
 
/**
* Sets up the hardware status page for devices that need a physical address
* in the register.
81,6 → 89,136
return 0;
}
 
 
 
 
 
 
 
 
 
 
#define MCHBAR_I915 0x44
#define MCHBAR_I965 0x48
#define MCHBAR_SIZE (4*4096)
 
#define DEVEN_REG 0x54
#define DEVEN_MCHBAR_EN (1 << 28)
 
 
 
 
/* Setup MCHBAR if possible, return true if we should disable it again */
static void
intel_setup_mchbar(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
int mchbar_reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915;
u32 temp;
bool enabled;
 
dev_priv->mchbar_need_disable = false;
 
if (IS_I915G(dev) || IS_I915GM(dev)) {
pci_read_config_dword(dev_priv->bridge_dev, DEVEN_REG, &temp);
enabled = !!(temp & DEVEN_MCHBAR_EN);
} else {
pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp);
enabled = temp & 1;
}
 
/* If it's already enabled, don't have to do anything */
if (enabled)
return;
 
dbgprintf("Epic fail\n");
 
#if 0
if (intel_alloc_mchbar_resource(dev))
return;
 
dev_priv->mchbar_need_disable = true;
 
/* Space is allocated or reserved, so enable it. */
if (IS_I915G(dev) || IS_I915GM(dev)) {
pci_write_config_dword(dev_priv->bridge_dev, DEVEN_REG,
temp | DEVEN_MCHBAR_EN);
} else {
pci_read_config_dword(dev_priv->bridge_dev, mchbar_reg, &temp);
pci_write_config_dword(dev_priv->bridge_dev, mchbar_reg, temp | 1);
}
#endif
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static int i915_load_gem_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long prealloc_size, gtt_size, mappable_size;
int ret;
 
prealloc_size = dev_priv->mm.gtt->stolen_size;
gtt_size = dev_priv->mm.gtt->gtt_total_entries << PAGE_SHIFT;
mappable_size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT;
 
dbgprintf("%s prealloc: %x gtt: %x mappable: %x\n",__FUNCTION__,
prealloc_size, gtt_size, mappable_size);
 
/* Basic memrange allocator for stolen space */
drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size);
 
//0xC00000 >> PAGE_SHIFT
 
/* Let GEM Manage all of the aperture.
*
* However, leave one page at the end still bound to the scratch page.
* There are a number of places where the hardware apparently
* prefetches past the end of the object, and we've seen multiple
* hangs with the GPU head pointer stuck in a batchbuffer bound
* at the last page of the aperture. One page should be enough to
* keep any prefetching inside of the aperture.
*/
// i915_gem_do_init(dev, 0, mappable_size, gtt_size - PAGE_SIZE);
 
// mutex_lock(&dev->struct_mutex);
// ret = i915_gem_init_ringbuffer(dev);
// mutex_unlock(&dev->struct_mutex);
// if (ret)
// return ret;
 
/* Try to set up FBC with a reasonable compressed buffer size */
// if (I915_HAS_FBC(dev) && i915_powersave) {
// int cfb_size;
 
/* Leave 1M for line length buffer & misc. */
 
/* Try to get a 32M buffer... */
// if (prealloc_size > (36*1024*1024))
// cfb_size = 32*1024*1024;
// else /* fall back to 7/8 of the stolen space */
// cfb_size = prealloc_size * 7 / 8;
// i915_setup_compression(dev, cfb_size);
// }
 
/* Allow hardware batchbuffers unless told otherwise. */
dev_priv->allow_batchbuffer = 1;
return 0;
}
 
static int i915_load_modeset_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
98,12 → 236,12
 
intel_modeset_init(dev);
 
#if 0
 
ret = i915_load_gem_init(dev);
if (ret)
goto cleanup_vga_switcheroo;
 
#if 0
 
intel_modeset_gem_init(dev);
 
ret = drm_irq_install(dev);
392,13 → 530,12
// intel_irq_init(dev);
 
/* Try to make sure MCHBAR is enabled before poking at it */
// intel_setup_mchbar(dev);
intel_setup_mchbar(dev);
intel_setup_gmbus(dev);
 
intel_opregion_setup(dev);
 
/* Make sure the bios did its job and set up vital registers */
// intel_setup_bios(dev);
intel_setup_bios(dev);
 
i915_gem_load(dev);
 
/drivers/video/drm/i915/i915_drm.h
0,0 → 1,847
/*
* Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
* 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 TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS 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.
*
*/
 
#ifndef _I915_DRM_H_
#define _I915_DRM_H_
 
#include "drm.h"
 
/* Please note that modifications to all structs defined here are
* subject to backwards-compatibility constraints.
*/
 
#ifdef __KERNEL__
/* For use by IPS driver */
extern unsigned long i915_read_mch_val(void);
extern bool i915_gpu_raise(void);
extern bool i915_gpu_lower(void);
extern bool i915_gpu_busy(void);
extern bool i915_gpu_turbo_disable(void);
#endif
 
/* Each region is a minimum of 16k, and there are at most 255 of them.
*/
#define I915_NR_TEX_REGIONS 255 /* table size 2k - maximum due to use
* of chars for next/prev indices */
#define I915_LOG_MIN_TEX_REGION_SIZE 14
 
typedef struct _drm_i915_init {
enum {
I915_INIT_DMA = 0x01,
I915_CLEANUP_DMA = 0x02,
I915_RESUME_DMA = 0x03
} func;
unsigned int mmio_offset;
int sarea_priv_offset;
unsigned int ring_start;
unsigned int ring_end;
unsigned int ring_size;
unsigned int front_offset;
unsigned int back_offset;
unsigned int depth_offset;
unsigned int w;
unsigned int h;
unsigned int pitch;
unsigned int pitch_bits;
unsigned int back_pitch;
unsigned int depth_pitch;
unsigned int cpp;
unsigned int chipset;
} drm_i915_init_t;
 
typedef struct _drm_i915_sarea {
struct drm_tex_region texList[I915_NR_TEX_REGIONS + 1];
int last_upload; /* last time texture was uploaded */
int last_enqueue; /* last time a buffer was enqueued */
int last_dispatch; /* age of the most recently dispatched buffer */
int ctxOwner; /* last context to upload state */
int texAge;
int pf_enabled; /* is pageflipping allowed? */
int pf_active;
int pf_current_page; /* which buffer is being displayed? */
int perf_boxes; /* performance boxes to be displayed */
int width, height; /* screen size in pixels */
 
drm_handle_t front_handle;
int front_offset;
int front_size;
 
drm_handle_t back_handle;
int back_offset;
int back_size;
 
drm_handle_t depth_handle;
int depth_offset;
int depth_size;
 
drm_handle_t tex_handle;
int tex_offset;
int tex_size;
int log_tex_granularity;
int pitch;
int rotation; /* 0, 90, 180 or 270 */
int rotated_offset;
int rotated_size;
int rotated_pitch;
int virtualX, virtualY;
 
unsigned int front_tiled;
unsigned int back_tiled;
unsigned int depth_tiled;
unsigned int rotated_tiled;
unsigned int rotated2_tiled;
 
int pipeA_x;
int pipeA_y;
int pipeA_w;
int pipeA_h;
int pipeB_x;
int pipeB_y;
int pipeB_w;
int pipeB_h;
 
/* fill out some space for old userspace triple buffer */
drm_handle_t unused_handle;
__u32 unused1, unused2, unused3;
 
/* buffer object handles for static buffers. May change
* over the lifetime of the client.
*/
__u32 front_bo_handle;
__u32 back_bo_handle;
__u32 unused_bo_handle;
__u32 depth_bo_handle;
 
} drm_i915_sarea_t;
 
/* due to userspace building against these headers we need some compat here */
#define planeA_x pipeA_x
#define planeA_y pipeA_y
#define planeA_w pipeA_w
#define planeA_h pipeA_h
#define planeB_x pipeB_x
#define planeB_y pipeB_y
#define planeB_w pipeB_w
#define planeB_h pipeB_h
 
/* Flags for perf_boxes
*/
#define I915_BOX_RING_EMPTY 0x1
#define I915_BOX_FLIP 0x2
#define I915_BOX_WAIT 0x4
#define I915_BOX_TEXTURE_LOAD 0x8
#define I915_BOX_LOST_CONTEXT 0x10
 
/* I915 specific ioctls
* The device specific ioctl range is 0x40 to 0x79.
*/
#define DRM_I915_INIT 0x00
#define DRM_I915_FLUSH 0x01
#define DRM_I915_FLIP 0x02
#define DRM_I915_BATCHBUFFER 0x03
#define DRM_I915_IRQ_EMIT 0x04
#define DRM_I915_IRQ_WAIT 0x05
#define DRM_I915_GETPARAM 0x06
#define DRM_I915_SETPARAM 0x07
#define DRM_I915_ALLOC 0x08
#define DRM_I915_FREE 0x09
#define DRM_I915_INIT_HEAP 0x0a
#define DRM_I915_CMDBUFFER 0x0b
#define DRM_I915_DESTROY_HEAP 0x0c
#define DRM_I915_SET_VBLANK_PIPE 0x0d
#define DRM_I915_GET_VBLANK_PIPE 0x0e
#define DRM_I915_VBLANK_SWAP 0x0f
#define DRM_I915_HWS_ADDR 0x11
#define DRM_I915_GEM_INIT 0x13
#define DRM_I915_GEM_EXECBUFFER 0x14
#define DRM_I915_GEM_PIN 0x15
#define DRM_I915_GEM_UNPIN 0x16
#define DRM_I915_GEM_BUSY 0x17
#define DRM_I915_GEM_THROTTLE 0x18
#define DRM_I915_GEM_ENTERVT 0x19
#define DRM_I915_GEM_LEAVEVT 0x1a
#define DRM_I915_GEM_CREATE 0x1b
#define DRM_I915_GEM_PREAD 0x1c
#define DRM_I915_GEM_PWRITE 0x1d
#define DRM_I915_GEM_MMAP 0x1e
#define DRM_I915_GEM_SET_DOMAIN 0x1f
#define DRM_I915_GEM_SW_FINISH 0x20
#define DRM_I915_GEM_SET_TILING 0x21
#define DRM_I915_GEM_GET_TILING 0x22
#define DRM_I915_GEM_GET_APERTURE 0x23
#define DRM_I915_GEM_MMAP_GTT 0x24
#define DRM_I915_GET_PIPE_FROM_CRTC_ID 0x25
#define DRM_I915_GEM_MADVISE 0x26
#define DRM_I915_OVERLAY_PUT_IMAGE 0x27
#define DRM_I915_OVERLAY_ATTRS 0x28
#define DRM_I915_GEM_EXECBUFFER2 0x29
 
#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
#define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
#define DRM_IOCTL_I915_FLIP DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLIP)
#define DRM_IOCTL_I915_BATCHBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_I915_BATCHBUFFER, drm_i915_batchbuffer_t)
#define DRM_IOCTL_I915_IRQ_EMIT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_IRQ_EMIT, drm_i915_irq_emit_t)
#define DRM_IOCTL_I915_IRQ_WAIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_IRQ_WAIT, drm_i915_irq_wait_t)
#define DRM_IOCTL_I915_GETPARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GETPARAM, drm_i915_getparam_t)
#define DRM_IOCTL_I915_SETPARAM DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SETPARAM, drm_i915_setparam_t)
#define DRM_IOCTL_I915_ALLOC DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_ALLOC, drm_i915_mem_alloc_t)
#define DRM_IOCTL_I915_FREE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_FREE, drm_i915_mem_free_t)
#define DRM_IOCTL_I915_INIT_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT_HEAP, drm_i915_mem_init_heap_t)
#define DRM_IOCTL_I915_CMDBUFFER DRM_IOW( DRM_COMMAND_BASE + DRM_I915_CMDBUFFER, drm_i915_cmdbuffer_t)
#define DRM_IOCTL_I915_DESTROY_HEAP DRM_IOW( DRM_COMMAND_BASE + DRM_I915_DESTROY_HEAP, drm_i915_mem_destroy_heap_t)
#define DRM_IOCTL_I915_SET_VBLANK_PIPE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SET_VBLANK_PIPE, drm_i915_vblank_pipe_t)
#define DRM_IOCTL_I915_GET_VBLANK_PIPE DRM_IOR( DRM_COMMAND_BASE + DRM_I915_GET_VBLANK_PIPE, drm_i915_vblank_pipe_t)
#define DRM_IOCTL_I915_VBLANK_SWAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t)
#define DRM_IOCTL_I915_HWS_ADDR DRM_IOW(DRM_COMMAND_BASE + DRM_I915_HWS_ADDR, struct drm_i915_gem_init)
#define DRM_IOCTL_I915_GEM_INIT DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_INIT, struct drm_i915_gem_init)
#define DRM_IOCTL_I915_GEM_EXECBUFFER DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER, struct drm_i915_gem_execbuffer)
#define DRM_IOCTL_I915_GEM_EXECBUFFER2 DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2)
#define DRM_IOCTL_I915_GEM_PIN DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_PIN, struct drm_i915_gem_pin)
#define DRM_IOCTL_I915_GEM_UNPIN DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_UNPIN, struct drm_i915_gem_unpin)
#define DRM_IOCTL_I915_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy)
#define DRM_IOCTL_I915_GEM_THROTTLE DRM_IO ( DRM_COMMAND_BASE + DRM_I915_GEM_THROTTLE)
#define DRM_IOCTL_I915_GEM_ENTERVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_ENTERVT)
#define DRM_IOCTL_I915_GEM_LEAVEVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_LEAVEVT)
#define DRM_IOCTL_I915_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_CREATE, struct drm_i915_gem_create)
#define DRM_IOCTL_I915_GEM_PREAD DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PREAD, struct drm_i915_gem_pread)
#define DRM_IOCTL_I915_GEM_PWRITE DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE, struct drm_i915_gem_pwrite)
#define DRM_IOCTL_I915_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct drm_i915_gem_mmap)
#define DRM_IOCTL_I915_GEM_MMAP_GTT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP_GTT, struct drm_i915_gem_mmap_gtt)
#define DRM_IOCTL_I915_GEM_SET_DOMAIN DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SET_DOMAIN, struct drm_i915_gem_set_domain)
#define DRM_IOCTL_I915_GEM_SW_FINISH DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SW_FINISH, struct drm_i915_gem_sw_finish)
#define DRM_IOCTL_I915_GEM_SET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling)
#define DRM_IOCTL_I915_GEM_GET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct drm_i915_gem_get_tiling)
#define DRM_IOCTL_I915_GEM_GET_APERTURE DRM_IOR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture)
#define DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_PIPE_FROM_CRTC_ID, struct drm_i915_get_pipe_from_crtc_id)
#define DRM_IOCTL_I915_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise)
#define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE DRM_IOW(DRM_COMMAND_BASE + DRM_I915_OVERLAY_PUT_IMAGE, struct drm_intel_overlay_put_image)
#define DRM_IOCTL_I915_OVERLAY_ATTRS DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs)
 
/* Allow drivers to submit batchbuffers directly to hardware, relying
* on the security mechanisms provided by hardware.
*/
typedef struct drm_i915_batchbuffer {
int start; /* agp offset */
int used; /* nr bytes in use */
int DR1; /* hw flags for GFX_OP_DRAWRECT_INFO */
int DR4; /* window origin for GFX_OP_DRAWRECT_INFO */
int num_cliprects; /* mulitpass with multiple cliprects? */
struct drm_clip_rect __user *cliprects; /* pointer to userspace cliprects */
} drm_i915_batchbuffer_t;
 
/* As above, but pass a pointer to userspace buffer which can be
* validated by the kernel prior to sending to hardware.
*/
typedef struct _drm_i915_cmdbuffer {
char __user *buf; /* pointer to userspace command buffer */
int sz; /* nr bytes in buf */
int DR1; /* hw flags for GFX_OP_DRAWRECT_INFO */
int DR4; /* window origin for GFX_OP_DRAWRECT_INFO */
int num_cliprects; /* mulitpass with multiple cliprects? */
struct drm_clip_rect __user *cliprects; /* pointer to userspace cliprects */
} drm_i915_cmdbuffer_t;
 
/* Userspace can request & wait on irq's:
*/
typedef struct drm_i915_irq_emit {
int __user *irq_seq;
} drm_i915_irq_emit_t;
 
typedef struct drm_i915_irq_wait {
int irq_seq;
} drm_i915_irq_wait_t;
 
/* Ioctl to query kernel params:
*/
#define I915_PARAM_IRQ_ACTIVE 1
#define I915_PARAM_ALLOW_BATCHBUFFER 2
#define I915_PARAM_LAST_DISPATCH 3
#define I915_PARAM_CHIPSET_ID 4
#define I915_PARAM_HAS_GEM 5
#define I915_PARAM_NUM_FENCES_AVAIL 6
#define I915_PARAM_HAS_OVERLAY 7
#define I915_PARAM_HAS_PAGEFLIPPING 8
#define I915_PARAM_HAS_EXECBUF2 9
#define I915_PARAM_HAS_BSD 10
#define I915_PARAM_HAS_BLT 11
#define I915_PARAM_HAS_RELAXED_FENCING 12
#define I915_PARAM_HAS_COHERENT_RINGS 13
#define I915_PARAM_HAS_EXEC_CONSTANTS 14
#define I915_PARAM_HAS_RELAXED_DELTA 15
 
typedef struct drm_i915_getparam {
int param;
int __user *value;
} drm_i915_getparam_t;
 
/* Ioctl to set kernel params:
*/
#define I915_SETPARAM_USE_MI_BATCHBUFFER_START 1
#define I915_SETPARAM_TEX_LRU_LOG_GRANULARITY 2
#define I915_SETPARAM_ALLOW_BATCHBUFFER 3
#define I915_SETPARAM_NUM_USED_FENCES 4
 
typedef struct drm_i915_setparam {
int param;
int value;
} drm_i915_setparam_t;
 
/* A memory manager for regions of shared memory:
*/
#define I915_MEM_REGION_AGP 1
 
typedef struct drm_i915_mem_alloc {
int region;
int alignment;
int size;
int __user *region_offset; /* offset from start of fb or agp */
} drm_i915_mem_alloc_t;
 
typedef struct drm_i915_mem_free {
int region;
int region_offset;
} drm_i915_mem_free_t;
 
typedef struct drm_i915_mem_init_heap {
int region;
int size;
int start;
} drm_i915_mem_init_heap_t;
 
/* Allow memory manager to be torn down and re-initialized (eg on
* rotate):
*/
typedef struct drm_i915_mem_destroy_heap {
int region;
} drm_i915_mem_destroy_heap_t;
 
/* Allow X server to configure which pipes to monitor for vblank signals
*/
#define DRM_I915_VBLANK_PIPE_A 1
#define DRM_I915_VBLANK_PIPE_B 2
 
typedef struct drm_i915_vblank_pipe {
int pipe;
} drm_i915_vblank_pipe_t;
 
/* Schedule buffer swap at given vertical blank:
*/
typedef struct drm_i915_vblank_swap {
drm_drawable_t drawable;
enum drm_vblank_seq_type seqtype;
unsigned int sequence;
} drm_i915_vblank_swap_t;
 
typedef struct drm_i915_hws_addr {
__u64 addr;
} drm_i915_hws_addr_t;
 
struct drm_i915_gem_init {
/**
* Beginning offset in the GTT to be managed by the DRM memory
* manager.
*/
__u64 gtt_start;
/**
* Ending offset in the GTT to be managed by the DRM memory
* manager.
*/
__u64 gtt_end;
};
 
struct drm_i915_gem_create {
/**
* Requested size for the object.
*
* The (page-aligned) allocated size for the object will be returned.
*/
__u64 size;
/**
* Returned handle for the object.
*
* Object handles are nonzero.
*/
__u32 handle;
__u32 pad;
};
 
struct drm_i915_gem_pread {
/** Handle for the object being read. */
__u32 handle;
__u32 pad;
/** Offset into the object to read from */
__u64 offset;
/** Length of data to read */
__u64 size;
/**
* Pointer to write the data into.
*
* This is a fixed-size type for 32/64 compatibility.
*/
__u64 data_ptr;
};
 
struct drm_i915_gem_pwrite {
/** Handle for the object being written to. */
__u32 handle;
__u32 pad;
/** Offset into the object to write to */
__u64 offset;
/** Length of data to write */
__u64 size;
/**
* Pointer to read the data from.
*
* This is a fixed-size type for 32/64 compatibility.
*/
__u64 data_ptr;
};
 
struct drm_i915_gem_mmap {
/** Handle for the object being mapped. */
__u32 handle;
__u32 pad;
/** Offset in the object to map. */
__u64 offset;
/**
* Length of data to map.
*
* The value will be page-aligned.
*/
__u64 size;
/**
* Returned pointer the data was mapped at.
*
* This is a fixed-size type for 32/64 compatibility.
*/
__u64 addr_ptr;
};
 
struct drm_i915_gem_mmap_gtt {
/** Handle for the object being mapped. */
__u32 handle;
__u32 pad;
/**
* Fake offset to use for subsequent mmap call
*
* This is a fixed-size type for 32/64 compatibility.
*/
__u64 offset;
};
 
struct drm_i915_gem_set_domain {
/** Handle for the object */
__u32 handle;
 
/** New read domains */
__u32 read_domains;
 
/** New write domain */
__u32 write_domain;
};
 
struct drm_i915_gem_sw_finish {
/** Handle for the object */
__u32 handle;
};
 
struct drm_i915_gem_relocation_entry {
/**
* Handle of the buffer being pointed to by this relocation entry.
*
* It's appealing to make this be an index into the mm_validate_entry
* list to refer to the buffer, but this allows the driver to create
* a relocation list for state buffers and not re-write it per
* exec using the buffer.
*/
__u32 target_handle;
 
/**
* Value to be added to the offset of the target buffer to make up
* the relocation entry.
*/
__u32 delta;
 
/** Offset in the buffer the relocation entry will be written into */
__u64 offset;
 
/**
* Offset value of the target buffer that the relocation entry was last
* written as.
*
* If the buffer has the same offset as last time, we can skip syncing
* and writing the relocation. This value is written back out by
* the execbuffer ioctl when the relocation is written.
*/
__u64 presumed_offset;
 
/**
* Target memory domains read by this operation.
*/
__u32 read_domains;
 
/**
* Target memory domains written by this operation.
*
* Note that only one domain may be written by the whole
* execbuffer operation, so that where there are conflicts,
* the application will get -EINVAL back.
*/
__u32 write_domain;
};
 
/** @{
* Intel memory domains
*
* Most of these just align with the various caches in
* the system and are used to flush and invalidate as
* objects end up cached in different domains.
*/
/** CPU cache */
#define I915_GEM_DOMAIN_CPU 0x00000001
/** Render cache, used by 2D and 3D drawing */
#define I915_GEM_DOMAIN_RENDER 0x00000002
/** Sampler cache, used by texture engine */
#define I915_GEM_DOMAIN_SAMPLER 0x00000004
/** Command queue, used to load batch buffers */
#define I915_GEM_DOMAIN_COMMAND 0x00000008
/** Instruction cache, used by shader programs */
#define I915_GEM_DOMAIN_INSTRUCTION 0x00000010
/** Vertex address cache */
#define I915_GEM_DOMAIN_VERTEX 0x00000020
/** GTT domain - aperture and scanout */
#define I915_GEM_DOMAIN_GTT 0x00000040
/** @} */
 
struct drm_i915_gem_exec_object {
/**
* User's handle for a buffer to be bound into the GTT for this
* operation.
*/
__u32 handle;
 
/** Number of relocations to be performed on this buffer */
__u32 relocation_count;
/**
* Pointer to array of struct drm_i915_gem_relocation_entry containing
* the relocations to be performed in this buffer.
*/
__u64 relocs_ptr;
 
/** Required alignment in graphics aperture */
__u64 alignment;
 
/**
* Returned value of the updated offset of the object, for future
* presumed_offset writes.
*/
__u64 offset;
};
 
struct drm_i915_gem_execbuffer {
/**
* List of buffers to be validated with their relocations to be
* performend on them.
*
* This is a pointer to an array of struct drm_i915_gem_validate_entry.
*
* These buffers must be listed in an order such that all relocations
* a buffer is performing refer to buffers that have already appeared
* in the validate list.
*/
__u64 buffers_ptr;
__u32 buffer_count;
 
/** Offset in the batchbuffer to start execution from. */
__u32 batch_start_offset;
/** Bytes used in batchbuffer from batch_start_offset */
__u32 batch_len;
__u32 DR1;
__u32 DR4;
__u32 num_cliprects;
/** This is a struct drm_clip_rect *cliprects */
__u64 cliprects_ptr;
};
 
struct drm_i915_gem_exec_object2 {
/**
* User's handle for a buffer to be bound into the GTT for this
* operation.
*/
__u32 handle;
 
/** Number of relocations to be performed on this buffer */
__u32 relocation_count;
/**
* Pointer to array of struct drm_i915_gem_relocation_entry containing
* the relocations to be performed in this buffer.
*/
__u64 relocs_ptr;
 
/** Required alignment in graphics aperture */
__u64 alignment;
 
/**
* Returned value of the updated offset of the object, for future
* presumed_offset writes.
*/
__u64 offset;
 
#define EXEC_OBJECT_NEEDS_FENCE (1<<0)
__u64 flags;
__u64 rsvd1;
__u64 rsvd2;
};
 
struct drm_i915_gem_execbuffer2 {
/**
* List of gem_exec_object2 structs
*/
__u64 buffers_ptr;
__u32 buffer_count;
 
/** Offset in the batchbuffer to start execution from. */
__u32 batch_start_offset;
/** Bytes used in batchbuffer from batch_start_offset */
__u32 batch_len;
__u32 DR1;
__u32 DR4;
__u32 num_cliprects;
/** This is a struct drm_clip_rect *cliprects */
__u64 cliprects_ptr;
#define I915_EXEC_RING_MASK (7<<0)
#define I915_EXEC_DEFAULT (0<<0)
#define I915_EXEC_RENDER (1<<0)
#define I915_EXEC_BSD (2<<0)
#define I915_EXEC_BLT (3<<0)
 
/* Used for switching the constants addressing mode on gen4+ RENDER ring.
* Gen6+ only supports relative addressing to dynamic state (default) and
* absolute addressing.
*
* These flags are ignored for the BSD and BLT rings.
*/
#define I915_EXEC_CONSTANTS_MASK (3<<6)
#define I915_EXEC_CONSTANTS_REL_GENERAL (0<<6) /* default */
#define I915_EXEC_CONSTANTS_ABSOLUTE (1<<6)
#define I915_EXEC_CONSTANTS_REL_SURFACE (2<<6) /* gen4/5 only */
__u64 flags;
__u64 rsvd1;
__u64 rsvd2;
};
 
struct drm_i915_gem_pin {
/** Handle of the buffer to be pinned. */
__u32 handle;
__u32 pad;
 
/** alignment required within the aperture */
__u64 alignment;
 
/** Returned GTT offset of the buffer. */
__u64 offset;
};
 
struct drm_i915_gem_unpin {
/** Handle of the buffer to be unpinned. */
__u32 handle;
__u32 pad;
};
 
struct drm_i915_gem_busy {
/** Handle of the buffer to check for busy */
__u32 handle;
 
/** Return busy status (1 if busy, 0 if idle) */
__u32 busy;
};
 
#define I915_TILING_NONE 0
#define I915_TILING_X 1
#define I915_TILING_Y 2
 
#define I915_BIT_6_SWIZZLE_NONE 0
#define I915_BIT_6_SWIZZLE_9 1
#define I915_BIT_6_SWIZZLE_9_10 2
#define I915_BIT_6_SWIZZLE_9_11 3
#define I915_BIT_6_SWIZZLE_9_10_11 4
/* Not seen by userland */
#define I915_BIT_6_SWIZZLE_UNKNOWN 5
/* Seen by userland. */
#define I915_BIT_6_SWIZZLE_9_17 6
#define I915_BIT_6_SWIZZLE_9_10_17 7
 
struct drm_i915_gem_set_tiling {
/** Handle of the buffer to have its tiling state updated */
__u32 handle;
 
/**
* Tiling mode for the object (I915_TILING_NONE, I915_TILING_X,
* I915_TILING_Y).
*
* This value is to be set on request, and will be updated by the
* kernel on successful return with the actual chosen tiling layout.
*
* The tiling mode may be demoted to I915_TILING_NONE when the system
* has bit 6 swizzling that can't be managed correctly by GEM.
*
* Buffer contents become undefined when changing tiling_mode.
*/
__u32 tiling_mode;
 
/**
* Stride in bytes for the object when in I915_TILING_X or
* I915_TILING_Y.
*/
__u32 stride;
 
/**
* Returned address bit 6 swizzling required for CPU access through
* mmap mapping.
*/
__u32 swizzle_mode;
};
 
struct drm_i915_gem_get_tiling {
/** Handle of the buffer to get tiling state for. */
__u32 handle;
 
/**
* Current tiling mode for the object (I915_TILING_NONE, I915_TILING_X,
* I915_TILING_Y).
*/
__u32 tiling_mode;
 
/**
* Returned address bit 6 swizzling required for CPU access through
* mmap mapping.
*/
__u32 swizzle_mode;
};
 
struct drm_i915_gem_get_aperture {
/** Total size of the aperture used by i915_gem_execbuffer, in bytes */
__u64 aper_size;
 
/**
* Available space in the aperture used by i915_gem_execbuffer, in
* bytes
*/
__u64 aper_available_size;
};
 
struct drm_i915_get_pipe_from_crtc_id {
/** ID of CRTC being requested **/
__u32 crtc_id;
 
/** pipe of requested CRTC **/
__u32 pipe;
};
 
#define I915_MADV_WILLNEED 0
#define I915_MADV_DONTNEED 1
#define __I915_MADV_PURGED 2 /* internal state */
 
struct drm_i915_gem_madvise {
/** Handle of the buffer to change the backing store advice */
__u32 handle;
 
/* Advice: either the buffer will be needed again in the near future,
* or wont be and could be discarded under memory pressure.
*/
__u32 madv;
 
/** Whether the backing store still exists. */
__u32 retained;
};
 
/* flags */
#define I915_OVERLAY_TYPE_MASK 0xff
#define I915_OVERLAY_YUV_PLANAR 0x01
#define I915_OVERLAY_YUV_PACKED 0x02
#define I915_OVERLAY_RGB 0x03
 
#define I915_OVERLAY_DEPTH_MASK 0xff00
#define I915_OVERLAY_RGB24 0x1000
#define I915_OVERLAY_RGB16 0x2000
#define I915_OVERLAY_RGB15 0x3000
#define I915_OVERLAY_YUV422 0x0100
#define I915_OVERLAY_YUV411 0x0200
#define I915_OVERLAY_YUV420 0x0300
#define I915_OVERLAY_YUV410 0x0400
 
#define I915_OVERLAY_SWAP_MASK 0xff0000
#define I915_OVERLAY_NO_SWAP 0x000000
#define I915_OVERLAY_UV_SWAP 0x010000
#define I915_OVERLAY_Y_SWAP 0x020000
#define I915_OVERLAY_Y_AND_UV_SWAP 0x030000
 
#define I915_OVERLAY_FLAGS_MASK 0xff000000
#define I915_OVERLAY_ENABLE 0x01000000
 
struct drm_intel_overlay_put_image {
/* various flags and src format description */
__u32 flags;
/* source picture description */
__u32 bo_handle;
/* stride values and offsets are in bytes, buffer relative */
__u16 stride_Y; /* stride for packed formats */
__u16 stride_UV;
__u32 offset_Y; /* offset for packet formats */
__u32 offset_U;
__u32 offset_V;
/* in pixels */
__u16 src_width;
__u16 src_height;
/* to compensate the scaling factors for partially covered surfaces */
__u16 src_scan_width;
__u16 src_scan_height;
/* output crtc description */
__u32 crtc_id;
__u16 dst_x;
__u16 dst_y;
__u16 dst_width;
__u16 dst_height;
};
 
/* flags */
#define I915_OVERLAY_UPDATE_ATTRS (1<<0)
#define I915_OVERLAY_UPDATE_GAMMA (1<<1)
struct drm_intel_overlay_attrs {
__u32 flags;
__u32 color_key;
__s32 brightness;
__u32 contrast;
__u32 saturation;
__u32 gamma0;
__u32 gamma1;
__u32 gamma2;
__u32 gamma3;
__u32 gamma4;
__u32 gamma5;
};
 
#endif /* _I915_DRM_H_ */
/drivers/video/drm/i915/i915_drv.c
28,9 → 28,14
*
*/
 
#include <drm/drmP.h>
#include <drm/drm.h>
//#include <linux/device.h>
#include "drmP.h"
#include "drm.h"
#include "i915_drm.h"
#include "i915_drv.h"
#include "intel_drv.h"
 
 
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
37,15 → 42,25
#include <errno-base.h>
#include <linux/pci.h>
 
#include "i915_drv.h"
#include <syscall.h>
 
unsigned int i915_lvds_downclock = 0;
int i915_vbt_sdvo_panel_type = -1;
unsigned int i915_panel_use_ssc = 1;
unsigned int i915_powersave = 1;
unsigned int i915_enable_fbc = 1;
#define __read_mostly
 
 
int i915_panel_ignore_lid __read_mostly = 0;
 
unsigned int i915_powersave __read_mostly = 1;
 
unsigned int i915_enable_rc6 __read_mostly = 0;
 
unsigned int i915_enable_fbc __read_mostly = 1;
 
unsigned int i915_lvds_downclock __read_mostly = 0;
 
unsigned int i915_panel_use_ssc __read_mostly = 1;
 
int i915_vbt_sdvo_panel_type __read_mostly = -1;
 
#define PCI_VENDOR_ID_INTEL 0x8086
 
#define INTEL_VGA_DEVICE(id, info) { \
59,17 → 74,14
 
static const struct intel_device_info intel_sandybridge_d_info = {
.gen = 6,
.need_gfx_hws = 1,
.has_hotplug = 1,
.need_gfx_hws = 1, .has_hotplug = 1,
.has_bsd_ring = 1,
.has_blt_ring = 1,
};
 
static const struct intel_device_info intel_sandybridge_m_info = {
.gen = 6,
.is_mobile = 1,
.need_gfx_hws = 1,
.has_hotplug = 1,
.gen = 6, .is_mobile = 1,
.need_gfx_hws = 1, .has_hotplug = 1,
.has_fbc = 1,
.has_bsd_ring = 1,
.has_blt_ring = 1,
256,8 → 268,8
mutex_init(&dev->struct_mutex);
mutex_init(&dev->ctxlist_mutex);
 
//int i915_driver_load(struct drm_device *dev, unsigned long flags)
 
 
ret = i915_driver_load(dev, ent->driver_data );
// if (ret)
// goto err_g4;
/drivers/video/drm/i915/i915_drv.h
34,7 → 34,7
#include "intel_bios.h"
#include "intel_ringbuffer.h"
//#include <linux/io-mapping.h>
//#include <linux/i2c.h>
#include <linux/i2c.h>
//#include <drm/intel-gtt.h>
//#include <linux/backlight.h>
 
119,7 → 119,10
struct intel_overlay;
struct intel_overlay_error_state;
 
 
struct drm_i915_master_private {
drm_local_map_t *sarea;
struct _drm_i915_sarea *sarea_priv;
};
#define I915_FENCE_REG_NONE -1
 
struct drm_i915_fence_reg {
323,7 → 326,7
 
/* For hangcheck timer */
#define DRM_I915_HANGCHECK_PERIOD 1500 /* in ms */
// struct timer_list hangcheck_timer;
struct timer_list hangcheck_timer;
int hangcheck_count;
uint32_t last_acthd;
uint32_t last_instdone;
547,7 → 550,7
/** Bridge to intel-gtt-ko */
const struct intel_gtt *gtt;
/** Memory allocator for GTT stolen memory */
// struct drm_mm stolen;
struct drm_mm stolen;
/** Memory allocator for GTT */
// struct drm_mm gtt_space;
/** List of all objects in gtt_space. Used to restore gtt
681,7 → 684,7
/* indicates the reduced downclock for LVDS*/
int lvds_downclock;
// struct work_struct idle_work;
// struct timer_list idle_timer;
struct timer_list idle_timer;
bool busy;
u16 orig_clock;
int child_dev_num;
704,7 → 707,7
u64 last_count1;
unsigned long last_time1;
u64 last_count2;
// struct timespec last_time2;
struct timespec last_time2;
unsigned long gfx_power;
int c_m;
int r_t;
/drivers/video/drm/i915/i915_gem.c
27,12 → 27,12
 
#include "drmP.h"
#include "drm.h"
//#include "i915_drm.h"
#include "i915_drm.h"
#include "i915_drv.h"
//#include "i915_trace.h"
#include "intel_drv.h"
//#include <linux/shmem_fs.h>
//#include <linux/slab.h>
#include <linux/slab.h>
//#include <linux/swap.h>
#include <linux/pci.h>
 
/drivers/video/drm/i915/i915_gem_tiling.c
29,7 → 29,7
#include "linux/bitops.h"
#include "drmP.h"
#include "drm.h"
//#include "i915_drm.h"
#include "i915_drm.h"
#include "i915_drv.h"
 
/** @file i915_gem_tiling.c
/drivers/video/drm/i915/intel_bios.c
27,12 → 27,10
#include <drm/drm_dp_helper.h>
#include "drmP.h"
#include "drm.h"
//#include "i915_drm.h"
#include "i915_drm.h"
#include "i915_drv.h"
#include "intel_bios.h"
 
#include <syscall.h>
 
#define SLAVE_ADDR1 0x70
#define SLAVE_ADDR2 0x72
 
/drivers/video/drm/i915/intel_crt.c
0,0 → 1,609
/*
* 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>
*/
 
#include <linux/i2c.h>
#include <linux/slab.h>
#include "drmP.h"
#include "drm.h"
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
#include "drm_edid.h"
#include "intel_drv.h"
#include "i915_drm.h"
#include "i915_drv.h"
 
/* Here's the desired hotplug mode */
#define ADPA_HOTPLUG_BITS (ADPA_CRT_HOTPLUG_PERIOD_128 | \
ADPA_CRT_HOTPLUG_WARMUP_10MS | \
ADPA_CRT_HOTPLUG_SAMPLE_4S | \
ADPA_CRT_HOTPLUG_VOLTAGE_50 | \
ADPA_CRT_HOTPLUG_VOLREF_325MV | \
ADPA_CRT_HOTPLUG_ENABLE)
 
struct intel_crt {
struct intel_encoder base;
bool force_hotplug_required;
};
 
static struct intel_crt *intel_attached_crt(struct drm_connector *connector)
{
return container_of(intel_attached_encoder(connector),
struct intel_crt, base);
}
 
static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 temp, reg;
 
if (HAS_PCH_SPLIT(dev))
reg = PCH_ADPA;
else
reg = ADPA;
 
temp = I915_READ(reg);
temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
temp &= ~ADPA_DAC_ENABLE;
 
switch(mode) {
case DRM_MODE_DPMS_ON:
temp |= ADPA_DAC_ENABLE;
break;
case DRM_MODE_DPMS_STANDBY:
temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
break;
case DRM_MODE_DPMS_SUSPEND:
temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
break;
case DRM_MODE_DPMS_OFF:
temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
break;
}
 
I915_WRITE(reg, temp);
}
 
static int intel_crt_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct drm_device *dev = connector->dev;
 
int max_clock = 0;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
 
if (mode->clock < 25000)
return MODE_CLOCK_LOW;
 
if (IS_GEN2(dev))
max_clock = 350000;
else
max_clock = 400000;
if (mode->clock > max_clock)
return MODE_CLOCK_HIGH;
 
return MODE_OK;
}
 
static bool intel_crt_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
return true;
}
 
static void intel_crt_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
 
struct drm_device *dev = encoder->dev;
struct drm_crtc *crtc = encoder->crtc;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct drm_i915_private *dev_priv = dev->dev_private;
int dpll_md_reg;
u32 adpa, dpll_md;
u32 adpa_reg;
 
dpll_md_reg = DPLL_MD(intel_crtc->pipe);
 
if (HAS_PCH_SPLIT(dev))
adpa_reg = PCH_ADPA;
else
adpa_reg = ADPA;
 
/*
* Disable separate mode multiplier used when cloning SDVO to CRT
* XXX this needs to be adjusted when we really are cloning
*/
if (INTEL_INFO(dev)->gen >= 4 && !HAS_PCH_SPLIT(dev)) {
dpll_md = I915_READ(dpll_md_reg);
I915_WRITE(dpll_md_reg,
dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
}
 
adpa = ADPA_HOTPLUG_BITS;
if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
adpa |= ADPA_HSYNC_ACTIVE_HIGH;
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
adpa |= ADPA_VSYNC_ACTIVE_HIGH;
 
if (intel_crtc->pipe == 0) {
if (HAS_PCH_CPT(dev))
adpa |= PORT_TRANS_A_SEL_CPT;
else
adpa |= ADPA_PIPE_A_SELECT;
} else {
if (HAS_PCH_CPT(dev))
adpa |= PORT_TRANS_B_SEL_CPT;
else
adpa |= ADPA_PIPE_B_SELECT;
}
 
if (!HAS_PCH_SPLIT(dev))
I915_WRITE(BCLRPAT(intel_crtc->pipe), 0);
 
I915_WRITE(adpa_reg, adpa);
}
 
static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct intel_crt *crt = intel_attached_crt(connector);
struct drm_i915_private *dev_priv = dev->dev_private;
u32 adpa;
bool ret;
 
/* The first time through, trigger an explicit detection cycle */
if (crt->force_hotplug_required) {
bool turn_off_dac = HAS_PCH_SPLIT(dev);
u32 save_adpa;
 
crt->force_hotplug_required = 0;
 
save_adpa = adpa = I915_READ(PCH_ADPA);
DRM_DEBUG_KMS("trigger hotplug detect cycle: adpa=0x%x\n", adpa);
 
adpa |= ADPA_CRT_HOTPLUG_FORCE_TRIGGER;
if (turn_off_dac)
adpa &= ~ADPA_DAC_ENABLE;
 
I915_WRITE(PCH_ADPA, adpa);
 
if (wait_for((I915_READ(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) == 0,
1000))
DRM_DEBUG_KMS("timed out waiting for FORCE_TRIGGER");
 
if (turn_off_dac) {
I915_WRITE(PCH_ADPA, save_adpa);
POSTING_READ(PCH_ADPA);
}
}
 
/* Check the status to see if both blue and green are on now */
adpa = I915_READ(PCH_ADPA);
if ((adpa & ADPA_CRT_HOTPLUG_MONITOR_MASK) != 0)
ret = true;
else
ret = false;
DRM_DEBUG_KMS("ironlake hotplug adpa=0x%x, result %d\n", adpa, ret);
 
return ret;
}
 
/**
* Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence.
*
* Not for i915G/i915GM
*
* \return true if CRT is connected.
* \return false if CRT is disconnected.
*/
static bool intel_crt_detect_hotplug(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 hotplug_en, orig, stat;
bool ret = false;
int i, tries = 0;
 
if (HAS_PCH_SPLIT(dev))
return intel_ironlake_crt_detect_hotplug(connector);
 
/*
* On 4 series desktop, CRT detect sequence need to be done twice
* to get a reliable result.
*/
 
if (IS_G4X(dev) && !IS_GM45(dev))
tries = 2;
else
tries = 1;
hotplug_en = orig = I915_READ(PORT_HOTPLUG_EN);
hotplug_en |= CRT_HOTPLUG_FORCE_DETECT;
 
for (i = 0; i < tries ; i++) {
/* turn on the FORCE_DETECT */
I915_WRITE(PORT_HOTPLUG_EN, hotplug_en);
/* wait for FORCE_DETECT to go off */
if (wait_for((I915_READ(PORT_HOTPLUG_EN) &
CRT_HOTPLUG_FORCE_DETECT) == 0,
1000))
DRM_DEBUG_KMS("timed out waiting for FORCE_DETECT to go off");
}
 
stat = I915_READ(PORT_HOTPLUG_STAT);
if ((stat & CRT_HOTPLUG_MONITOR_MASK) != CRT_HOTPLUG_MONITOR_NONE)
ret = true;
 
/* clear the interrupt we just generated, if any */
I915_WRITE(PORT_HOTPLUG_STAT, CRT_HOTPLUG_INT_STATUS);
 
/* and put the bits back */
I915_WRITE(PORT_HOTPLUG_EN, orig);
 
return ret;
}
 
static bool intel_crt_detect_ddc(struct drm_connector *connector)
{
struct intel_crt *crt = intel_attached_crt(connector);
struct drm_i915_private *dev_priv = crt->base.base.dev->dev_private;
 
/* CRT should always be at 0, but check anyway */
if (crt->base.type != INTEL_OUTPUT_ANALOG)
return false;
 
if (intel_ddc_probe(&crt->base, dev_priv->crt_ddc_pin)) {
struct edid *edid;
bool is_digital = false;
 
edid = drm_get_edid(connector,
&dev_priv->gmbus[dev_priv->crt_ddc_pin].adapter);
/*
* This may be a DVI-I connector with a shared DDC
* link between analog and digital outputs, so we
* have to check the EDID input spec of the attached device.
*
* On the other hand, what should we do if it is a broken EDID?
*/
if (edid != NULL) {
is_digital = edid->input & DRM_EDID_INPUT_DIGITAL;
connector->display_info.raw_edid = NULL;
kfree(edid);
}
 
if (!is_digital) {
DRM_DEBUG_KMS("CRT detected via DDC:0x50 [EDID]\n");
return true;
} else {
DRM_DEBUG_KMS("CRT not detected via DDC:0x50 [EDID reports a digital panel]\n");
}
}
 
return false;
}
 
static enum drm_connector_status
intel_crt_load_detect(struct intel_crt *crt)
{
struct drm_device *dev = crt->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t pipe = to_intel_crtc(crt->base.base.crtc)->pipe;
uint32_t save_bclrpat;
uint32_t save_vtotal;
uint32_t vtotal, vactive;
uint32_t vsample;
uint32_t vblank, vblank_start, vblank_end;
uint32_t dsl;
uint32_t bclrpat_reg;
uint32_t vtotal_reg;
uint32_t vblank_reg;
uint32_t vsync_reg;
uint32_t pipeconf_reg;
uint32_t pipe_dsl_reg;
uint8_t st00;
enum drm_connector_status status;
 
DRM_DEBUG_KMS("starting load-detect on CRT\n");
 
bclrpat_reg = BCLRPAT(pipe);
vtotal_reg = VTOTAL(pipe);
vblank_reg = VBLANK(pipe);
vsync_reg = VSYNC(pipe);
pipeconf_reg = PIPECONF(pipe);
pipe_dsl_reg = PIPEDSL(pipe);
 
save_bclrpat = I915_READ(bclrpat_reg);
save_vtotal = I915_READ(vtotal_reg);
vblank = I915_READ(vblank_reg);
 
vtotal = ((save_vtotal >> 16) & 0xfff) + 1;
vactive = (save_vtotal & 0x7ff) + 1;
 
vblank_start = (vblank & 0xfff) + 1;
vblank_end = ((vblank >> 16) & 0xfff) + 1;
 
/* Set the border color to purple. */
I915_WRITE(bclrpat_reg, 0x500050);
 
if (!IS_GEN2(dev)) {
uint32_t pipeconf = I915_READ(pipeconf_reg);
I915_WRITE(pipeconf_reg, pipeconf | PIPECONF_FORCE_BORDER);
POSTING_READ(pipeconf_reg);
/* Wait for next Vblank to substitue
* border color for Color info */
intel_wait_for_vblank(dev, pipe);
st00 = I915_READ8(VGA_MSR_WRITE);
status = ((st00 & (1 << 4)) != 0) ?
connector_status_connected :
connector_status_disconnected;
 
I915_WRITE(pipeconf_reg, pipeconf);
} else {
bool restore_vblank = false;
int count, detect;
 
/*
* If there isn't any border, add some.
* Yes, this will flicker
*/
if (vblank_start <= vactive && vblank_end >= vtotal) {
uint32_t vsync = I915_READ(vsync_reg);
uint32_t vsync_start = (vsync & 0xffff) + 1;
 
vblank_start = vsync_start;
I915_WRITE(vblank_reg,
(vblank_start - 1) |
((vblank_end - 1) << 16));
restore_vblank = true;
}
/* sample in the vertical border, selecting the larger one */
if (vblank_start - vactive >= vtotal - vblank_end)
vsample = (vblank_start + vactive) >> 1;
else
vsample = (vtotal + vblank_end) >> 1;
 
/*
* Wait for the border to be displayed
*/
while (I915_READ(pipe_dsl_reg) >= vactive)
;
while ((dsl = I915_READ(pipe_dsl_reg)) <= vsample)
;
/*
* Watch ST00 for an entire scanline
*/
detect = 0;
count = 0;
do {
count++;
/* Read the ST00 VGA status register */
st00 = I915_READ8(VGA_MSR_WRITE);
if (st00 & (1 << 4))
detect++;
} while ((I915_READ(pipe_dsl_reg) == dsl));
 
/* restore vblank if necessary */
if (restore_vblank)
I915_WRITE(vblank_reg, vblank);
/*
* If more than 3/4 of the scanline detected a monitor,
* then it is assumed to be present. This works even on i830,
* where there isn't any way to force the border color across
* the screen
*/
status = detect * 4 > count * 3 ?
connector_status_connected :
connector_status_disconnected;
}
 
/* Restore previous settings */
I915_WRITE(bclrpat_reg, save_bclrpat);
 
return status;
}
 
static enum drm_connector_status
intel_crt_detect(struct drm_connector *connector, bool force)
{
struct drm_device *dev = connector->dev;
struct intel_crt *crt = intel_attached_crt(connector);
struct drm_crtc *crtc;
enum drm_connector_status status;
 
if (I915_HAS_HOTPLUG(dev)) {
if (intel_crt_detect_hotplug(connector)) {
DRM_DEBUG_KMS("CRT detected via hotplug\n");
return connector_status_connected;
} else {
DRM_DEBUG_KMS("CRT not detected via hotplug\n");
return connector_status_disconnected;
}
}
 
if (intel_crt_detect_ddc(connector))
return connector_status_connected;
 
if (!force)
return connector->status;
 
/* for pre-945g platforms use load detect */
crtc = crt->base.base.crtc;
if (crtc && crtc->enabled) {
status = intel_crt_load_detect(crt);
} else {
struct intel_load_detect_pipe tmp;
 
if (intel_get_load_detect_pipe(&crt->base, connector, NULL,
&tmp)) {
if (intel_crt_detect_ddc(connector))
status = connector_status_connected;
else
status = intel_crt_load_detect(crt);
intel_release_load_detect_pipe(&crt->base, connector,
&tmp);
} else
status = connector_status_unknown;
}
 
return status;
}
 
static void intel_crt_destroy(struct drm_connector *connector)
{
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
 
static int intel_crt_get_modes(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
 
ret = intel_ddc_get_modes(connector,
&dev_priv->gmbus[dev_priv->crt_ddc_pin].adapter);
if (ret || !IS_G4X(dev))
return ret;
 
/* Try to probe digital port for output in DVI-I -> VGA mode. */
return intel_ddc_get_modes(connector,
&dev_priv->gmbus[GMBUS_PORT_DPB].adapter);
}
 
static int intel_crt_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t value)
{
return 0;
}
 
static void intel_crt_reset(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct intel_crt *crt = intel_attached_crt(connector);
 
if (HAS_PCH_SPLIT(dev))
crt->force_hotplug_required = 1;
}
 
/*
* Routines for controlling stuff on the analog port
*/
 
static const struct drm_encoder_helper_funcs intel_crt_helper_funcs = {
.dpms = intel_crt_dpms,
.mode_fixup = intel_crt_mode_fixup,
.prepare = intel_encoder_prepare,
.commit = intel_encoder_commit,
.mode_set = intel_crt_mode_set,
};
 
static const struct drm_connector_funcs intel_crt_connector_funcs = {
.reset = intel_crt_reset,
.dpms = drm_helper_connector_dpms,
.detect = intel_crt_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.destroy = intel_crt_destroy,
.set_property = intel_crt_set_property,
};
 
static const struct drm_connector_helper_funcs intel_crt_connector_helper_funcs = {
.mode_valid = intel_crt_mode_valid,
.get_modes = intel_crt_get_modes,
.best_encoder = intel_best_encoder,
};
 
static const struct drm_encoder_funcs intel_crt_enc_funcs = {
.destroy = intel_encoder_destroy,
};
 
void intel_crt_init(struct drm_device *dev)
{
struct drm_connector *connector;
struct intel_crt *crt;
struct intel_connector *intel_connector;
struct drm_i915_private *dev_priv = dev->dev_private;
 
crt = kzalloc(sizeof(struct intel_crt), GFP_KERNEL);
if (!crt)
return;
 
intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
if (!intel_connector) {
kfree(crt);
return;
}
 
connector = &intel_connector->base;
drm_connector_init(dev, &intel_connector->base,
&intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA);
 
drm_encoder_init(dev, &crt->base.base, &intel_crt_enc_funcs,
DRM_MODE_ENCODER_DAC);
 
intel_connector_attach_encoder(intel_connector, &crt->base);
 
crt->base.type = INTEL_OUTPUT_ANALOG;
crt->base.clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT |
1 << INTEL_ANALOG_CLONE_BIT |
1 << INTEL_SDVO_LVDS_CLONE_BIT);
crt->base.crtc_mask = (1 << 0) | (1 << 1);
connector->interlace_allowed = 1;
connector->doublescan_allowed = 0;
 
drm_encoder_helper_add(&crt->base.base, &intel_crt_helper_funcs);
drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
 
drm_sysfs_connector_add(connector);
 
if (I915_HAS_HOTPLUG(dev))
connector->polled = DRM_CONNECTOR_POLL_HPD;
else
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
 
/*
* Configure the automatic hotplug detection stuff
*/
crt->force_hotplug_required = 0;
if (HAS_PCH_SPLIT(dev)) {
u32 adpa;
 
adpa = I915_READ(PCH_ADPA);
adpa &= ~ADPA_CRT_HOTPLUG_MASK;
adpa |= ADPA_HOTPLUG_BITS;
I915_WRITE(PCH_ADPA, adpa);
POSTING_READ(PCH_ADPA);
 
DRM_DEBUG_KMS("pch crt adpa set to 0x%x\n", adpa);
crt->force_hotplug_required = 1;
}
 
dev_priv->hotplug_supported_mask |= CRT_HOTPLUG_INT_STATUS;
}
/drivers/video/drm/i915/intel_display.c
29,10 → 29,11
//#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
//#include <linux/slab.h>
#include <linux/slab.h>
//#include <linux/vgaarb.h>
#include "drmP.h"
#include "intel_drv.h"
#include "i915_drm.h"
#include "i915_drv.h"
//#include "i915_trace.h"
#include "drm_dp_helper.h"
39,8 → 40,6
 
#include "drm_crtc_helper.h"
 
#include <syscall.h>
 
phys_addr_t get_bus_addr(void);
 
static inline __attribute__((const))
49,6 → 48,21
return (n != 0 && ((n & (n - 1)) == 0));
}
 
#define MAX_ERRNO 4095
 
#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
 
static inline long IS_ERR(const void *ptr)
{
return IS_ERR_VALUE((unsigned long)ptr);
}
 
static inline void *ERR_PTR(long error)
{
return (void *) error;
}
 
 
static inline int pci_read_config_word(struct pci_dev *dev, int where,
u16 *val)
{
1914,21 → 1928,6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
int x, int y)
{
2070,11 → 2069,11
 
DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n",
Start, Offset, x, y, fb->pitch);
// I915_WRITE(DSPSTRIDE(plane), fb->pitch);
// I915_WRITE(DSPSURF(plane), Start);
// I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
// I915_WRITE(DSPADDR(plane), Offset);
// POSTING_READ(reg);
I915_WRITE(DSPSTRIDE(plane), fb->pitch);
I915_WRITE(DSPSURF(plane), Start);
I915_WRITE(DSPTILEOFF(plane), (y << 16) | x);
I915_WRITE(DSPADDR(plane), Offset);
POSTING_READ(reg);
 
return 0;
}
2167,22 → 2166,22
}
 
mutex_unlock(&dev->struct_mutex);
#if 0
if (!dev->primary->master)
return 0;
 
// if (!dev->primary->master)
// return 0;
master_priv = dev->primary->master->driver_priv;
if (!master_priv->sarea_priv)
return 0;
 
// master_priv = dev->primary->master->driver_priv;
// if (!master_priv->sarea_priv)
// return 0;
 
// if (intel_crtc->pipe) {
// master_priv->sarea_priv->pipeB_x = x;
// master_priv->sarea_priv->pipeB_y = y;
// } else {
// master_priv->sarea_priv->pipeA_x = x;
// master_priv->sarea_priv->pipeA_y = y;
// }
 
if (intel_crtc->pipe) {
master_priv->sarea_priv->pipeB_x = x;
master_priv->sarea_priv->pipeB_y = y;
} else {
master_priv->sarea_priv->pipeA_x = x;
master_priv->sarea_priv->pipeA_y = y;
}
#endif
return 0;
}
 
3141,23 → 3140,134
}
}
 
/**
* Sets the power management mode of the pipe and plane.
*/
static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_master_private *master_priv;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
bool enabled;
 
if (intel_crtc->dpms_mode == mode)
return;
 
intel_crtc->dpms_mode = mode;
 
dev_priv->display.dpms(crtc, mode);
 
if (!dev->primary->master)
return;
 
master_priv = dev->primary->master->driver_priv;
if (!master_priv->sarea_priv)
return;
 
enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF;
 
switch (pipe) {
case 0:
master_priv->sarea_priv->pipeA_w = enabled ? crtc->mode.hdisplay : 0;
master_priv->sarea_priv->pipeA_h = enabled ? crtc->mode.vdisplay : 0;
break;
case 1:
master_priv->sarea_priv->pipeB_w = enabled ? crtc->mode.hdisplay : 0;
master_priv->sarea_priv->pipeB_h = enabled ? crtc->mode.vdisplay : 0;
break;
default:
DRM_ERROR("Can't update pipe %c in SAREA\n", pipe_name(pipe));
break;
}
}
 
static void intel_crtc_disable(struct drm_crtc *crtc)
{
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
struct drm_device *dev = crtc->dev;
 
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
 
if (crtc->fb) {
mutex_lock(&dev->struct_mutex);
// i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj);
mutex_unlock(&dev->struct_mutex);
}
}
 
/* Prepare for a mode set.
*
* Note we could be a lot smarter here. We need to figure out which outputs
* will be enabled, which disabled (in short, how the config will changes)
* and perform the minimum necessary steps to accomplish that, e.g. updating
* watermarks, FBC configuration, making sure PLLs are programmed correctly,
* panel fitting is in the proper state, etc.
*/
static void i9xx_crtc_prepare(struct drm_crtc *crtc)
{
i9xx_crtc_disable(crtc);
}
 
static void i9xx_crtc_commit(struct drm_crtc *crtc)
{
i9xx_crtc_enable(crtc);
}
 
static void ironlake_crtc_prepare(struct drm_crtc *crtc)
{
ironlake_crtc_disable(crtc);
}
 
static void ironlake_crtc_commit(struct drm_crtc *crtc)
{
ironlake_crtc_enable(crtc);
}
 
void intel_encoder_prepare (struct drm_encoder *encoder)
{
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
/* lvds has its own version of prepare see intel_lvds_prepare */
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
}
 
void intel_encoder_commit (struct drm_encoder *encoder)
{
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
/* lvds has its own version of commit see intel_lvds_commit */
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
}
 
void intel_encoder_destroy(struct drm_encoder *encoder)
{
struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
 
drm_encoder_cleanup(encoder);
kfree(intel_encoder);
}
 
static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = crtc->dev;
 
if (HAS_PCH_SPLIT(dev)) {
/* FDI link clock is fixed at 2.7G */
if (mode->clock * 3 > IRONLAKE_FDI_FREQ * 4)
return false;
}
 
/* XXX some encoders set the crtcinfo, others don't.
* Obviously we need some form of conflict resolution here...
*/
if (adjusted_mode->crtc_htotal == 0)
drm_mode_set_crtcinfo(adjusted_mode, 0);
 
return true;
}
 
static int i945_get_display_clock_speed(struct drm_device *dev)
{
return 400000;
5473,13 → 5583,30
return ret;
}
 
static int intel_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode,
int x, int y,
struct drm_framebuffer *old_fb)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
int ret;
 
// drm_vblank_pre_modeset(dev, pipe);
 
ret = dev_priv->display.crtc_mode_set(crtc, mode, adjusted_mode,
x, y, old_fb);
 
// drm_vblank_post_modeset(dev, pipe);
 
intel_crtc->dpms_mode = DRM_MODE_DPMS_ON;
 
return ret;
}
 
 
/** Loads the palette/gamma unit for the CRTC with the prepared values */
void intel_crtc_load_lut(struct drm_crtc *crtc)
{
5545,64 → 5672,384
 
 
 
static void intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
u16 *blue, uint32_t start, uint32_t size)
{
int end = (start + size > 256) ? 256 : start + size, i;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
for (i = start; i < end; i++) {
intel_crtc->lut_r[i] = red[i] >> 8;
intel_crtc->lut_g[i] = green[i] >> 8;
intel_crtc->lut_b[i] = blue[i] >> 8;
}
 
intel_crtc_load_lut(crtc);
}
 
/**
* Get a pipe with a simple mode set on it for doing load-based monitor
* detection.
*
* It will be up to the load-detect code to adjust the pipe as appropriate for
* its requirements. The pipe will be connected to no other encoders.
*
* Currently this code will only succeed if there is a pipe with no encoders
* configured for it. In the future, it could choose to temporarily disable
* some outputs to free up a pipe for its use.
*
* \return crtc, or NULL if no pipes are available.
*/
 
/* VESA 640x480x72Hz mode to set on the pipe */
static struct drm_display_mode load_detect_mode = {
DRM_MODE("640x480", DRM_MODE_TYPE_DEFAULT, 31500, 640, 664,
704, 832, 0, 480, 489, 491, 520, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
};
 
 
 
 
 
static u32
intel_framebuffer_pitch_for_width(int width, int bpp)
{
u32 pitch = DIV_ROUND_UP(width * bpp, 8);
return ALIGN(pitch, 64);
}
 
static u32
intel_framebuffer_size_for_mode(struct drm_display_mode *mode, int bpp)
{
u32 pitch = intel_framebuffer_pitch_for_width(mode->hdisplay, bpp);
return ALIGN(pitch * mode->vdisplay, PAGE_SIZE);
}
 
static struct drm_framebuffer *
intel_framebuffer_create_for_mode(struct drm_device *dev,
struct drm_display_mode *mode,
int depth, int bpp)
{
struct drm_i915_gem_object *obj;
struct drm_mode_fb_cmd mode_cmd;
 
// obj = i915_gem_alloc_object(dev,
// intel_framebuffer_size_for_mode(mode, bpp));
// if (obj == NULL)
return ERR_PTR(-ENOMEM);
 
// mode_cmd.width = mode->hdisplay;
// mode_cmd.height = mode->vdisplay;
// mode_cmd.depth = depth;
// mode_cmd.bpp = bpp;
// mode_cmd.pitch = intel_framebuffer_pitch_for_width(mode_cmd.width, bpp);
 
// return intel_framebuffer_create(dev, &mode_cmd, obj);
}
 
static struct drm_framebuffer *
mode_fits_in_fbdev(struct drm_device *dev,
struct drm_display_mode *mode)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj;
struct drm_framebuffer *fb;
 
// if (dev_priv->fbdev == NULL)
// return NULL;
 
// obj = dev_priv->fbdev->ifb.obj;
// if (obj == NULL)
// return NULL;
 
// fb = &dev_priv->fbdev->ifb.base;
// if (fb->pitch < intel_framebuffer_pitch_for_width(mode->hdisplay,
// fb->bits_per_pixel))
return NULL;
 
// if (obj->base.size < mode->vdisplay * fb->pitch)
// return NULL;
 
// return fb;
}
 
bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
struct drm_connector *connector,
struct drm_display_mode *mode,
struct intel_load_detect_pipe *old)
{
struct intel_crtc *intel_crtc;
struct drm_crtc *possible_crtc;
struct drm_encoder *encoder = &intel_encoder->base;
struct drm_crtc *crtc = NULL;
struct drm_device *dev = encoder->dev;
struct drm_framebuffer *old_fb;
int i = -1;
 
DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
connector->base.id, drm_get_connector_name(connector),
encoder->base.id, drm_get_encoder_name(encoder));
 
/*
* Algorithm gets a little messy:
*
* - if the connector already has an assigned crtc, use it (but make
* sure it's on first)
*
* - try to find the first unused crtc that can drive this connector,
* and use that if we find one
*/
 
/* See if we already have a CRTC for this connector */
if (encoder->crtc) {
crtc = encoder->crtc;
 
intel_crtc = to_intel_crtc(crtc);
old->dpms_mode = intel_crtc->dpms_mode;
old->load_detect_temp = false;
 
/* Make sure the crtc and connector are running */
if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) {
struct drm_encoder_helper_funcs *encoder_funcs;
struct drm_crtc_helper_funcs *crtc_funcs;
 
crtc_funcs = crtc->helper_private;
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
 
encoder_funcs = encoder->helper_private;
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
}
 
return true;
}
 
/* Find an unused one (if possible) */
list_for_each_entry(possible_crtc, &dev->mode_config.crtc_list, head) {
i++;
if (!(encoder->possible_crtcs & (1 << i)))
continue;
if (!possible_crtc->enabled) {
crtc = possible_crtc;
break;
}
}
 
/*
* If we didn't find an unused CRTC, don't use any.
*/
if (!crtc) {
DRM_DEBUG_KMS("no pipe available for load-detect\n");
return false;
}
 
encoder->crtc = crtc;
connector->encoder = encoder;
 
intel_crtc = to_intel_crtc(crtc);
old->dpms_mode = intel_crtc->dpms_mode;
old->load_detect_temp = true;
old->release_fb = NULL;
 
if (!mode)
mode = &load_detect_mode;
 
old_fb = crtc->fb;
 
/* We need a framebuffer large enough to accommodate all accesses
* that the plane may generate whilst we perform load detection.
* We can not rely on the fbcon either being present (we get called
* during its initialisation to detect all boot displays, or it may
* not even exist) or that it is large enough to satisfy the
* requested mode.
*/
crtc->fb = mode_fits_in_fbdev(dev, mode);
if (crtc->fb == NULL) {
DRM_DEBUG_KMS("creating tmp fb for load-detection\n");
crtc->fb = intel_framebuffer_create_for_mode(dev, mode, 24, 32);
old->release_fb = crtc->fb;
} else
DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n");
if (IS_ERR(crtc->fb)) {
DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n");
crtc->fb = old_fb;
return false;
}
 
if (!drm_crtc_helper_set_mode(crtc, mode, 0, 0, old_fb)) {
DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n");
if (old->release_fb)
old->release_fb->funcs->destroy(old->release_fb);
crtc->fb = old_fb;
return false;
}
 
/* let the connector get through one full cycle before testing */
intel_wait_for_vblank(dev, intel_crtc->pipe);
 
return true;
}
 
void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
struct drm_connector *connector,
struct intel_load_detect_pipe *old)
{
struct drm_encoder *encoder = &intel_encoder->base;
struct drm_device *dev = encoder->dev;
struct drm_crtc *crtc = encoder->crtc;
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
 
DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
connector->base.id, drm_get_connector_name(connector),
encoder->base.id, drm_get_encoder_name(encoder));
 
if (old->load_detect_temp) {
connector->encoder = NULL;
drm_helper_disable_unused_functions(dev);
 
if (old->release_fb)
old->release_fb->funcs->destroy(old->release_fb);
 
return;
}
 
/* Switch crtc and encoder back off if necessary */
if (old->dpms_mode != DRM_MODE_DPMS_ON) {
encoder_funcs->dpms(encoder, old->dpms_mode);
crtc_funcs->dpms(crtc, old->dpms_mode);
}
}
 
/* Returns the clock of the currently programmed mode of the given pipe. */
static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
u32 dpll = I915_READ(DPLL(pipe));
u32 fp;
intel_clock_t clock;
 
if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0)
fp = I915_READ(FP0(pipe));
else
fp = I915_READ(FP1(pipe));
 
clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
if (IS_PINEVIEW(dev)) {
clock.n = ffs((fp & FP_N_PINEVIEW_DIV_MASK) >> FP_N_DIV_SHIFT) - 1;
clock.m2 = (fp & FP_M2_PINEVIEW_DIV_MASK) >> FP_M2_DIV_SHIFT;
} else {
clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT;
clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT;
}
 
if (!IS_GEN2(dev)) {
if (IS_PINEVIEW(dev))
clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW) >>
DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW);
else
clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >>
DPLL_FPA01_P1_POST_DIV_SHIFT);
 
switch (dpll & DPLL_MODE_MASK) {
case DPLLB_MODE_DAC_SERIAL:
clock.p2 = dpll & DPLL_DAC_SERIAL_P2_CLOCK_DIV_5 ?
5 : 10;
break;
case DPLLB_MODE_LVDS:
clock.p2 = dpll & DPLLB_LVDS_P2_CLOCK_DIV_7 ?
7 : 14;
break;
default:
DRM_DEBUG_KMS("Unknown DPLL mode %08x in programmed "
"mode\n", (int)(dpll & DPLL_MODE_MASK));
return 0;
}
 
/* XXX: Handle the 100Mhz refclk */
intel_clock(dev, 96000, &clock);
} else {
bool is_lvds = (pipe == 1) && (I915_READ(LVDS) & LVDS_PORT_EN);
 
if (is_lvds) {
clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >>
DPLL_FPA01_P1_POST_DIV_SHIFT);
clock.p2 = 14;
 
if ((dpll & PLL_REF_INPUT_MASK) ==
PLLB_REF_INPUT_SPREADSPECTRUMIN) {
/* XXX: might not be 66MHz */
intel_clock(dev, 66000, &clock);
} else
intel_clock(dev, 48000, &clock);
} else {
if (dpll & PLL_P1_DIVIDE_BY_TWO)
clock.p1 = 2;
else {
clock.p1 = ((dpll & DPLL_FPA01_P1_POST_DIV_MASK_I830) >>
DPLL_FPA01_P1_POST_DIV_SHIFT) + 2;
}
if (dpll & PLL_P2_DIVIDE_BY_4)
clock.p2 = 4;
else
clock.p2 = 2;
 
intel_clock(dev, 48000, &clock);
}
}
 
/* XXX: It would be nice to validate the clocks, but we can't reuse
* i830PllIsValid() because it relies on the xf86_config connector
* configuration being accurate, which it isn't necessarily.
*/
 
return clock.dot;
}
 
/** Returns the currently programmed mode of the given pipe. */
struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
struct drm_crtc *crtc)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
struct drm_display_mode *mode;
int htot = I915_READ(HTOTAL(pipe));
int hsync = I915_READ(HSYNC(pipe));
int vtot = I915_READ(VTOTAL(pipe));
int vsync = I915_READ(VSYNC(pipe));
 
mode = kzalloc(sizeof(*mode), GFP_KERNEL);
if (!mode)
return NULL;
 
mode->clock = intel_crtc_clock_get(dev, crtc);
mode->hdisplay = (htot & 0xffff) + 1;
mode->htotal = ((htot & 0xffff0000) >> 16) + 1;
mode->hsync_start = (hsync & 0xffff) + 1;
mode->hsync_end = ((hsync & 0xffff0000) >> 16) + 1;
mode->vdisplay = (vtot & 0xffff) + 1;
mode->vtotal = ((vtot & 0xffff0000) >> 16) + 1;
mode->vsync_start = (vsync & 0xffff) + 1;
mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1;
 
drm_mode_set_name(mode);
drm_mode_set_crtcinfo(mode, 0);
 
return mode;
}
 
#define GPU_IDLE_TIMEOUT 500 /* ms */
 
 
 
 
#define CRTC_IDLE_TIMEOUT 1000 /* ms */
 
 
 
 
static void intel_increase_pllclock(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
5664,10 → 6111,27
 
 
 
static void intel_crtc_destroy(struct drm_crtc *crtc)
{
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct drm_device *dev = crtc->dev;
struct intel_unpin_work *work;
unsigned long flags;
 
spin_lock_irqsave(&dev->event_lock, flags);
work = intel_crtc->unpin_work;
intel_crtc->unpin_work = NULL;
spin_unlock_irqrestore(&dev->event_lock, flags);
 
if (work) {
// cancel_work_sync(&work->work);
kfree(work);
}
 
drm_crtc_cleanup(crtc);
 
kfree(intel_crtc);
}
 
 
 
5734,30 → 6198,133
 
 
 
static void intel_sanitize_modesetting(struct drm_device *dev,
int pipe, int plane)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 reg, val;
 
if (HAS_PCH_SPLIT(dev))
return;
 
/* Who knows what state these registers were left in by the BIOS or
* grub?
*
* If we leave the registers in a conflicting state (e.g. with the
* display plane reading from the other pipe than the one we intend
* to use) then when we attempt to teardown the active mode, we will
* not disable the pipes and planes in the correct order -- leaving
* a plane reading from a disabled pipe and possibly leading to
* undefined behaviour.
*/
 
reg = DSPCNTR(plane);
val = I915_READ(reg);
 
if ((val & DISPLAY_PLANE_ENABLE) == 0)
return;
if (!!(val & DISPPLANE_SEL_PIPE_MASK) == pipe)
return;
 
/* This display plane is active and attached to the other CPU pipe. */
pipe = !pipe;
 
/* Disable the plane and wait for it to stop reading from the pipe. */
intel_disable_plane(dev_priv, plane, pipe);
intel_disable_pipe(dev_priv, pipe);
}
 
static void intel_crtc_reset(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
/* Reset flags back to the 'unknown' status so that they
* will be correctly set on the initial modeset.
*/
intel_crtc->dpms_mode = -1;
 
/* We need to fix up any BIOS configuration that conflicts with
* our expectations.
*/
intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane);
}
 
static struct drm_crtc_helper_funcs intel_helper_funcs = {
.dpms = intel_crtc_dpms,
.mode_fixup = intel_crtc_mode_fixup,
.mode_set = intel_crtc_mode_set,
.mode_set_base = intel_pipe_set_base,
.mode_set_base_atomic = intel_pipe_set_base_atomic,
.load_lut = intel_crtc_load_lut,
.disable = intel_crtc_disable,
};
 
static const struct drm_crtc_funcs intel_crtc_funcs = {
.reset = intel_crtc_reset,
// .cursor_set = intel_crtc_cursor_set,
// .cursor_move = intel_crtc_cursor_move,
.gamma_set = intel_crtc_gamma_set,
.set_config = drm_crtc_helper_set_config,
.destroy = intel_crtc_destroy,
// .page_flip = intel_crtc_page_flip,
};
 
static void intel_crtc_init(struct drm_device *dev, int pipe)
{
drm_i915_private_t *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc;
int i;
 
ENTER();
 
intel_crtc = kzalloc(sizeof(struct intel_crtc) + (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), GFP_KERNEL);
if (intel_crtc == NULL)
return;
 
drm_crtc_init(dev, &intel_crtc->base, &intel_crtc_funcs);
 
drm_mode_crtc_set_gamma_size(&intel_crtc->base, 256);
for (i = 0; i < 256; i++) {
intel_crtc->lut_r[i] = i;
intel_crtc->lut_g[i] = i;
intel_crtc->lut_b[i] = i;
}
 
/* Swap pipes & planes for FBC on pre-965 */
intel_crtc->pipe = pipe;
intel_crtc->plane = pipe;
if (IS_MOBILE(dev) && IS_GEN3(dev)) {
DRM_DEBUG_KMS("swapping pipes & planes for FBC\n");
intel_crtc->plane = !pipe;
}
 
BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL);
dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base;
dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base;
 
intel_crtc_reset(&intel_crtc->base);
intel_crtc->active = true; /* force the pipe off on setup_init_config */
intel_crtc->bpp = 24; /* default for pre-Ironlake */
 
if (HAS_PCH_SPLIT(dev)) {
intel_helper_funcs.prepare = ironlake_crtc_prepare;
intel_helper_funcs.commit = ironlake_crtc_commit;
} else {
intel_helper_funcs.prepare = i9xx_crtc_prepare;
intel_helper_funcs.commit = i9xx_crtc_commit;
}
 
drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
 
intel_crtc->busy = false;
 
LEAVE();
 
// setup_timer(&intel_crtc->idle_timer, intel_crtc_idle_timer,
// (unsigned long)intel_crtc);
}
 
 
 
5765,36 → 6332,148
 
 
 
static int intel_encoder_clones(struct drm_device *dev, int type_mask)
{
struct intel_encoder *encoder;
int index_mask = 0;
int entry = 0;
 
list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
if (type_mask & encoder->clone_mask)
index_mask |= (1 << entry);
entry++;
}
 
return index_mask;
}
 
static bool has_edp_a(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
 
if (!IS_MOBILE(dev))
return false;
 
if ((I915_READ(DP_A) & DP_DETECTED) == 0)
return false;
 
if (IS_GEN5(dev) &&
(I915_READ(ILK_DISPLAY_CHICKEN_FUSES) & ILK_eDP_A_DISABLE))
return false;
 
return true;
}
 
static void intel_setup_outputs(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_encoder *encoder;
bool dpd_is_edp = false;
bool has_lvds = false;
 
if (IS_MOBILE(dev) && !IS_I830(dev))
has_lvds = intel_lvds_init(dev);
if (!has_lvds && !HAS_PCH_SPLIT(dev)) {
/* disable the panel fitter on everything but LVDS */
I915_WRITE(PFIT_CONTROL, 0);
}
 
if (HAS_PCH_SPLIT(dev)) {
dpd_is_edp = intel_dpd_is_edp(dev);
 
if (has_edp_a(dev))
intel_dp_init(dev, DP_A);
 
if (dpd_is_edp && (I915_READ(PCH_DP_D) & DP_DETECTED))
intel_dp_init(dev, PCH_DP_D);
}
 
intel_crt_init(dev);
 
if (HAS_PCH_SPLIT(dev)) {
int found;
 
if (I915_READ(HDMIB) & PORT_DETECTED) {
/* PCH SDVOB multiplex with HDMIB */
found = intel_sdvo_init(dev, PCH_SDVOB);
if (!found)
intel_hdmi_init(dev, HDMIB);
if (!found && (I915_READ(PCH_DP_B) & DP_DETECTED))
intel_dp_init(dev, PCH_DP_B);
}
 
if (I915_READ(HDMIC) & PORT_DETECTED)
intel_hdmi_init(dev, HDMIC);
 
if (I915_READ(HDMID) & PORT_DETECTED)
intel_hdmi_init(dev, HDMID);
 
if (I915_READ(PCH_DP_C) & DP_DETECTED)
intel_dp_init(dev, PCH_DP_C);
 
if (!dpd_is_edp && (I915_READ(PCH_DP_D) & DP_DETECTED))
intel_dp_init(dev, PCH_DP_D);
 
} else if (SUPPORTS_DIGITAL_OUTPUTS(dev)) {
bool found = false;
 
if (I915_READ(SDVOB) & SDVO_DETECTED) {
DRM_DEBUG_KMS("probing SDVOB\n");
found = intel_sdvo_init(dev, SDVOB);
if (!found && SUPPORTS_INTEGRATED_HDMI(dev)) {
DRM_DEBUG_KMS("probing HDMI on SDVOB\n");
intel_hdmi_init(dev, SDVOB);
}
 
if (!found && SUPPORTS_INTEGRATED_DP(dev)) {
DRM_DEBUG_KMS("probing DP_B\n");
intel_dp_init(dev, DP_B);
}
}
 
/* Before G4X SDVOC doesn't have its own detect register */
 
if (I915_READ(SDVOB) & SDVO_DETECTED) {
DRM_DEBUG_KMS("probing SDVOC\n");
found = intel_sdvo_init(dev, SDVOC);
}
 
if (!found && (I915_READ(SDVOC) & SDVO_DETECTED)) {
 
if (SUPPORTS_INTEGRATED_HDMI(dev)) {
DRM_DEBUG_KMS("probing HDMI on SDVOC\n");
intel_hdmi_init(dev, SDVOC);
}
if (SUPPORTS_INTEGRATED_DP(dev)) {
DRM_DEBUG_KMS("probing DP_C\n");
intel_dp_init(dev, DP_C);
}
}
 
if (SUPPORTS_INTEGRATED_DP(dev) &&
(I915_READ(DP_D) & DP_DETECTED)) {
DRM_DEBUG_KMS("probing DP_D\n");
intel_dp_init(dev, DP_D);
}
} else if (IS_GEN2(dev))
intel_dvo_init(dev);
 
// if (SUPPORTS_TV(dev))
// intel_tv_init(dev);
 
list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
encoder->base.possible_crtcs = encoder->crtc_mask;
encoder->base.possible_clones =
intel_encoder_clones(dev, encoder->clone_mask);
}
 
/* disable all the possible outputs/crtcs before entering KMS mode */
// drm_helper_disable_unused_functions(dev);
}
 
 
 
 
static const struct drm_mode_config_funcs intel_mode_funcs = {
.fb_create = NULL /*intel_user_framebuffer_create*/,
.output_poll_changed = NULL /*intel_fb_output_poll_changed*/,
5831,27 → 6510,91
 
 
 
bool ironlake_set_drps(struct drm_device *dev, u8 val)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u16 rgvswctl;
 
rgvswctl = I915_READ16(MEMSWCTL);
if (rgvswctl & MEMCTL_CMD_STS) {
DRM_DEBUG("gpu busy, RCS change rejected\n");
return false; /* still busy with another command */
}
 
rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
(val << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
I915_WRITE16(MEMSWCTL, rgvswctl);
POSTING_READ16(MEMSWCTL);
 
rgvswctl |= MEMCTL_CMD_STS;
I915_WRITE16(MEMSWCTL, rgvswctl);
 
return true;
}
 
void ironlake_enable_drps(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 rgvmodectl = I915_READ(MEMMODECTL);
u8 fmax, fmin, fstart, vstart;
 
/* Enable temp reporting */
I915_WRITE16(PMMISC, I915_READ(PMMISC) | MCPPCE_EN);
I915_WRITE16(TSC1, I915_READ(TSC1) | TSE);
 
/* 100ms RC evaluation intervals */
I915_WRITE(RCUPEI, 100000);
I915_WRITE(RCDNEI, 100000);
 
/* Set max/min thresholds to 90ms and 80ms respectively */
I915_WRITE(RCBMAXAVG, 90000);
I915_WRITE(RCBMINAVG, 80000);
 
I915_WRITE(MEMIHYST, 1);
 
/* Set up min, max, and cur for interrupt handling */
fmax = (rgvmodectl & MEMMODE_FMAX_MASK) >> MEMMODE_FMAX_SHIFT;
fmin = (rgvmodectl & MEMMODE_FMIN_MASK);
fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >>
MEMMODE_FSTART_SHIFT;
 
vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >>
PXVFREQ_PX_SHIFT;
 
dev_priv->fmax = fmax; /* IPS callback will increase this */
dev_priv->fstart = fstart;
 
dev_priv->max_delay = fstart;
dev_priv->min_delay = fmin;
dev_priv->cur_delay = fstart;
 
DRM_DEBUG_DRIVER("fmax: %d, fmin: %d, fstart: %d\n",
fmax, fmin, fstart);
 
I915_WRITE(MEMINTREN, MEMINT_CX_SUPR_EN | MEMINT_EVAL_CHG_EN);
 
/*
* Interrupts will be enabled in ironlake_irq_postinstall
*/
 
I915_WRITE(VIDSTART, vstart);
POSTING_READ(VIDSTART);
 
rgvmodectl |= MEMMODE_SWMODE_EN;
I915_WRITE(MEMMODECTL, rgvmodectl);
 
if (wait_for((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 10))
DRM_ERROR("stuck trying to change perf mode\n");
msleep(1);
 
ironlake_set_drps(dev, fstart);
 
dev_priv->last_count1 = I915_READ(0x112e4) + I915_READ(0x112e8) +
I915_READ(0x112e0);
// dev_priv->last_time1 = jiffies_to_msecs(jiffies);
dev_priv->last_count2 = I915_READ(0x112f4);
// getrawmonotonic(&dev_priv->last_time2);
}
 
 
 
5867,20 → 6610,269
 
 
 
static unsigned long intel_pxfreq(u32 vidfreq)
{
unsigned long freq;
int div = (vidfreq & 0x3f0000) >> 16;
int post = (vidfreq & 0x3000) >> 12;
int pre = (vidfreq & 0x7);
 
if (!pre)
return 0;
 
freq = ((div * 133333) / ((1<<post) * pre));
 
return freq;
}
 
void intel_init_emon(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 lcfuse;
u8 pxw[16];
int i;
 
/* Disable to program */
I915_WRITE(ECR, 0);
POSTING_READ(ECR);
 
/* Program energy weights for various events */
I915_WRITE(SDEW, 0x15040d00);
I915_WRITE(CSIEW0, 0x007f0000);
I915_WRITE(CSIEW1, 0x1e220004);
I915_WRITE(CSIEW2, 0x04000004);
 
for (i = 0; i < 5; i++)
I915_WRITE(PEW + (i * 4), 0);
for (i = 0; i < 3; i++)
I915_WRITE(DEW + (i * 4), 0);
 
/* Program P-state weights to account for frequency power adjustment */
for (i = 0; i < 16; i++) {
u32 pxvidfreq = I915_READ(PXVFREQ_BASE + (i * 4));
unsigned long freq = intel_pxfreq(pxvidfreq);
unsigned long vid = (pxvidfreq & PXVFREQ_PX_MASK) >>
PXVFREQ_PX_SHIFT;
unsigned long val;
 
val = vid * vid;
val *= (freq / 1000);
val *= 255;
val /= (127*127*900);
if (val > 0xff)
DRM_ERROR("bad pxval: %ld\n", val);
pxw[i] = val;
}
/* Render standby states get 0 weight */
pxw[14] = 0;
pxw[15] = 0;
 
for (i = 0; i < 4; i++) {
u32 val = (pxw[i*4] << 24) | (pxw[(i*4)+1] << 16) |
(pxw[(i*4)+2] << 8) | (pxw[(i*4)+3]);
I915_WRITE(PXW + (i * 4), val);
}
 
/* Adjust magic regs to magic values (more experimental results) */
I915_WRITE(OGW0, 0);
I915_WRITE(OGW1, 0);
I915_WRITE(EG0, 0x00007f00);
I915_WRITE(EG1, 0x0000000e);
I915_WRITE(EG2, 0x000e0000);
I915_WRITE(EG3, 0x68000300);
I915_WRITE(EG4, 0x42000000);
I915_WRITE(EG5, 0x00140031);
I915_WRITE(EG6, 0);
I915_WRITE(EG7, 0);
 
for (i = 0; i < 8; i++)
I915_WRITE(PXWL + (i * 4), 0);
 
/* Enable PMON + select events */
I915_WRITE(ECR, 0x80000019);
 
lcfuse = I915_READ(LCFUSE02);
 
dev_priv->corr = (lcfuse & LCFUSE_HIV_MASK);
}
 
void gen6_enable_rps(struct drm_i915_private *dev_priv)
{
u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
u32 pcu_mbox, rc6_mask = 0;
int cur_freq, min_freq, max_freq;
int i;
 
/* Here begins a magic sequence of register writes to enable
* auto-downclocking.
*
* Perhaps there might be some value in exposing these to
* userspace...
*/
I915_WRITE(GEN6_RC_STATE, 0);
mutex_lock(&dev_priv->dev->struct_mutex);
gen6_gt_force_wake_get(dev_priv);
 
/* disable the counters and set deterministic thresholds */
I915_WRITE(GEN6_RC_CONTROL, 0);
 
I915_WRITE(GEN6_RC1_WAKE_RATE_LIMIT, 1000 << 16);
I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 40 << 16 | 30);
I915_WRITE(GEN6_RC6pp_WAKE_RATE_LIMIT, 30);
I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000);
I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25);
 
for (i = 0; i < I915_NUM_RINGS; i++)
I915_WRITE(RING_MAX_IDLE(dev_priv->ring[i].mmio_base), 10);
 
I915_WRITE(GEN6_RC_SLEEP, 0);
I915_WRITE(GEN6_RC1e_THRESHOLD, 1000);
I915_WRITE(GEN6_RC6_THRESHOLD, 50000);
I915_WRITE(GEN6_RC6p_THRESHOLD, 100000);
I915_WRITE(GEN6_RC6pp_THRESHOLD, 64000); /* unused */
 
if (i915_enable_rc6)
rc6_mask = GEN6_RC_CTL_RC6p_ENABLE |
GEN6_RC_CTL_RC6_ENABLE;
 
I915_WRITE(GEN6_RC_CONTROL,
rc6_mask |
GEN6_RC_CTL_EI_MODE(1) |
GEN6_RC_CTL_HW_ENABLE);
 
I915_WRITE(GEN6_RPNSWREQ,
GEN6_FREQUENCY(10) |
GEN6_OFFSET(0) |
GEN6_AGGRESSIVE_TURBO);
I915_WRITE(GEN6_RC_VIDEO_FREQ,
GEN6_FREQUENCY(12));
 
I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 1000000);
I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
18 << 24 |
6 << 16);
I915_WRITE(GEN6_RP_UP_THRESHOLD, 10000);
I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 1000000);
I915_WRITE(GEN6_RP_UP_EI, 100000);
I915_WRITE(GEN6_RP_DOWN_EI, 5000000);
I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10);
I915_WRITE(GEN6_RP_CONTROL,
GEN6_RP_MEDIA_TURBO |
GEN6_RP_USE_NORMAL_FREQ |
GEN6_RP_MEDIA_IS_GFX |
GEN6_RP_ENABLE |
GEN6_RP_UP_BUSY_AVG |
GEN6_RP_DOWN_IDLE_CONT);
 
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
500))
DRM_ERROR("timeout waiting for pcode mailbox to become idle\n");
 
I915_WRITE(GEN6_PCODE_DATA, 0);
I915_WRITE(GEN6_PCODE_MAILBOX,
GEN6_PCODE_READY |
GEN6_PCODE_WRITE_MIN_FREQ_TABLE);
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
500))
DRM_ERROR("timeout waiting for pcode mailbox to finish\n");
 
min_freq = (rp_state_cap & 0xff0000) >> 16;
max_freq = rp_state_cap & 0xff;
cur_freq = (gt_perf_status & 0xff00) >> 8;
 
/* Check for overclock support */
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
500))
DRM_ERROR("timeout waiting for pcode mailbox to become idle\n");
I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_READ_OC_PARAMS);
pcu_mbox = I915_READ(GEN6_PCODE_DATA);
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
500))
DRM_ERROR("timeout waiting for pcode mailbox to finish\n");
if (pcu_mbox & (1<<31)) { /* OC supported */
max_freq = pcu_mbox & 0xff;
DRM_DEBUG_DRIVER("overclocking supported, adjusting frequency max to %dMHz\n", pcu_mbox * 50);
}
 
/* In units of 100MHz */
dev_priv->max_delay = max_freq;
dev_priv->min_delay = min_freq;
dev_priv->cur_delay = cur_freq;
 
/* requires MSI enabled */
I915_WRITE(GEN6_PMIER,
GEN6_PM_MBOX_EVENT |
GEN6_PM_THERMAL_EVENT |
GEN6_PM_RP_DOWN_TIMEOUT |
GEN6_PM_RP_UP_THRESHOLD |
GEN6_PM_RP_DOWN_THRESHOLD |
GEN6_PM_RP_UP_EI_EXPIRED |
GEN6_PM_RP_DOWN_EI_EXPIRED);
// spin_lock_irq(&dev_priv->rps_lock);
// WARN_ON(dev_priv->pm_iir != 0);
I915_WRITE(GEN6_PMIMR, 0);
// spin_unlock_irq(&dev_priv->rps_lock);
/* enable all PM interrupts */
I915_WRITE(GEN6_PMINTRMSK, 0);
 
gen6_gt_force_wake_put(dev_priv);
mutex_unlock(&dev_priv->dev->struct_mutex);
}
 
void gen6_update_ring_freq(struct drm_i915_private *dev_priv)
{
int min_freq = 15;
int gpu_freq, ia_freq, max_ia_freq;
int scaling_factor = 180;
 
// max_ia_freq = cpufreq_quick_get_max(0);
/*
* Default to measured freq if none found, PCU will ensure we don't go
* over
*/
// if (!max_ia_freq)
max_ia_freq = 3000000; //tsc_khz;
 
/* Convert from kHz to MHz */
max_ia_freq /= 1000;
 
mutex_lock(&dev_priv->dev->struct_mutex);
 
/*
* For each potential GPU frequency, load a ring frequency we'd like
* to use for memory access. We do this by specifying the IA frequency
* the PCU should use as a reference to determine the ring frequency.
*/
for (gpu_freq = dev_priv->max_delay; gpu_freq >= dev_priv->min_delay;
gpu_freq--) {
int diff = dev_priv->max_delay - gpu_freq;
 
/*
* For GPU frequencies less than 750MHz, just use the lowest
* ring freq.
*/
if (gpu_freq < min_freq)
ia_freq = 800;
else
ia_freq = max_ia_freq - ((diff * scaling_factor) / 2);
ia_freq = DIV_ROUND_CLOSEST(ia_freq, 100);
 
I915_WRITE(GEN6_PCODE_DATA,
(ia_freq << GEN6_PCODE_FREQ_IA_RATIO_SHIFT) |
gpu_freq);
I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY |
GEN6_PCODE_WRITE_MIN_FREQ_TABLE);
if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) &
GEN6_PCODE_READY) == 0, 10)) {
DRM_ERROR("pcode write of freq table timed out\n");
continue;
}
}
 
mutex_unlock(&dev_priv->dev->struct_mutex);
}
 
static void ironlake_init_clock_gating(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
6114,6 → 7106,17
 
 
 
 
void intel_init_clock_gating(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
 
dev_priv->display.init_clock_gating(dev);
 
if (dev_priv->display.init_pch_clock_gating)
dev_priv->display.init_pch_clock_gating(dev);
}
 
/* Set up chip specific display functions */
static void intel_init_display(struct drm_device *dev)
{
6360,7 → 7363,29
}
}
 
/* Disable the VGA plane that we never use */
static void i915_disable_vga(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u8 sr1;
u32 vga_reg;
 
if (HAS_PCH_SPLIT(dev))
vga_reg = CPU_VGACNTRL;
else
vga_reg = VGACNTRL;
 
// vga_get_uninterruptible(dev->pdev, VGA_RSRC_LEGACY_IO);
out8(VGA_SR_INDEX, 1);
sr1 = in8(VGA_SR_DATA);
out8(VGA_SR_DATA,sr1 | 1<<5);
// vga_put(dev->pdev, VGA_RSRC_LEGACY_IO);
udelay(300);
 
I915_WRITE(vga_reg, VGA_DISP_DISABLE);
POSTING_READ(vga_reg);
}
 
void intel_modeset_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
6393,8 → 7418,6
DRM_DEBUG_KMS("%d display pipe%s available.\n",
dev_priv->num_pipe, dev_priv->num_pipe > 1 ? "s" : "");
 
#if 0
 
for (i = 0; i < dev_priv->num_pipe; i++) {
intel_crtc_init(dev, i);
}
6415,11 → 7438,23
gen6_update_ring_freq(dev_priv);
}
 
INIT_WORK(&dev_priv->idle_work, intel_idle_update);
setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
(unsigned long)dev);
#endif
}
 
 
/*
* Return which encoder is currently attached for connector.
*/
struct drm_encoder *intel_best_encoder(struct drm_connector *connector)
{
return &intel_attached_encoder(connector)->base;
}
 
void intel_connector_attach_encoder(struct intel_connector *connector,
struct intel_encoder *encoder)
{
connector->encoder = encoder;
drm_mode_connector_attach_encoder(&connector->base,
&encoder->base);
}
 
 
/drivers/video/drm/i915/intel_dp.c
26,13 → 26,13
*/
 
#include <linux/i2c.h>
//#include <linux/slab.h>
#include <linux/slab.h>
#include "drmP.h"
#include "drm.h"
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
#include "intel_drv.h"
//#include "i915_drm.h"
#include "i915_drm.h"
#include "i915_drv.h"
#include "drm_dp_helper.h"
 
91,10 → 91,12
return container_of(encoder, struct intel_dp, base.base);
}
 
static struct intel_dp *intel_attached_dp(struct drm_connector *connector)
{
return container_of(intel_attached_encoder(connector),
struct intel_dp, base);
}
 
 
 
 
/**
* intel_encoder_is_pch_edp - is the given encoder a PCH attached eDP?
* @encoder: DRM encoder
114,6 → 116,10
return is_pch_edp(intel_dp);
}
 
static void intel_dp_start_link_train(struct intel_dp *intel_dp);
static void intel_dp_complete_link_train(struct intel_dp *intel_dp);
static void intel_dp_link_down(struct intel_dp *intel_dp);
 
void
intel_edp_link_config (struct intel_encoder *intel_encoder,
int *lane_num, int *link_bw)
127,19 → 133,523
*link_bw = 270000;
}
 
static int
intel_dp_max_lane_count(struct intel_dp *intel_dp)
{
int max_lane_count = 4;
 
if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11) {
max_lane_count = intel_dp->dpcd[DP_MAX_LANE_COUNT] & 0x1f;
switch (max_lane_count) {
case 1: case 2: case 4:
break;
default:
max_lane_count = 4;
}
}
return max_lane_count;
}
 
static int
intel_dp_max_link_bw(struct intel_dp *intel_dp)
{
int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE];
 
switch (max_link_bw) {
case DP_LINK_BW_1_62:
case DP_LINK_BW_2_7:
break;
default:
max_link_bw = DP_LINK_BW_1_62;
break;
}
return max_link_bw;
}
 
static int
intel_dp_link_clock(uint8_t link_bw)
{
if (link_bw == DP_LINK_BW_2_7)
return 270000;
else
return 162000;
}
 
/* I think this is a fiction */
static int
intel_dp_link_required(struct drm_device *dev, struct intel_dp *intel_dp, int pixel_clock)
{
struct drm_crtc *crtc = intel_dp->base.base.crtc;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int bpp = 24;
 
if (intel_crtc)
bpp = intel_crtc->bpp;
 
return (pixel_clock * bpp + 7) / 8;
}
 
static int
intel_dp_max_data_rate(int max_link_clock, int max_lanes)
{
return (max_link_clock * max_lanes * 8) / 10;
}
 
static int
intel_dp_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct intel_dp *intel_dp = intel_attached_dp(connector);
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_dp));
int max_lanes = intel_dp_max_lane_count(intel_dp);
 
if (is_edp(intel_dp) && dev_priv->panel_fixed_mode) {
if (mode->hdisplay > dev_priv->panel_fixed_mode->hdisplay)
return MODE_PANEL;
 
if (mode->vdisplay > dev_priv->panel_fixed_mode->vdisplay)
return MODE_PANEL;
}
 
/* only refuse the mode on non eDP since we have seen some weird eDP panels
which are outside spec tolerances but somehow work by magic */
if (!is_edp(intel_dp) &&
(intel_dp_link_required(connector->dev, intel_dp, mode->clock)
> intel_dp_max_data_rate(max_link_clock, max_lanes)))
return MODE_CLOCK_HIGH;
 
if (mode->clock < 10000)
return MODE_CLOCK_LOW;
 
return MODE_OK;
}
 
static uint32_t
pack_aux(uint8_t *src, int src_bytes)
{
int i;
uint32_t v = 0;
 
if (src_bytes > 4)
src_bytes = 4;
for (i = 0; i < src_bytes; i++)
v |= ((uint32_t) src[i]) << ((3-i) * 8);
return v;
}
 
static void
unpack_aux(uint32_t src, uint8_t *dst, int dst_bytes)
{
int i;
if (dst_bytes > 4)
dst_bytes = 4;
for (i = 0; i < dst_bytes; i++)
dst[i] = src >> ((3-i) * 8);
}
 
/* hrawclock is 1/4 the FSB frequency */
static int
intel_hrawclk(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t clkcfg;
 
clkcfg = I915_READ(CLKCFG);
switch (clkcfg & CLKCFG_FSB_MASK) {
case CLKCFG_FSB_400:
return 100;
case CLKCFG_FSB_533:
return 133;
case CLKCFG_FSB_667:
return 166;
case CLKCFG_FSB_800:
return 200;
case CLKCFG_FSB_1067:
return 266;
case CLKCFG_FSB_1333:
return 333;
/* these two are just a guess; one of them might be right */
case CLKCFG_FSB_1600:
case CLKCFG_FSB_1600_ALT:
return 400;
default:
return 133;
}
}
 
static int
intel_dp_aux_ch(struct intel_dp *intel_dp,
uint8_t *send, int send_bytes,
uint8_t *recv, int recv_size)
{
uint32_t output_reg = intel_dp->output_reg;
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t ch_ctl = output_reg + 0x10;
uint32_t ch_data = ch_ctl + 4;
int i;
int recv_bytes;
uint32_t status;
uint32_t aux_clock_divider;
int try, precharge;
 
/* The clock divider is based off the hrawclk,
* and would like to run at 2MHz. So, take the
* hrawclk value and divide by 2 and use that
*
* Note that PCH attached eDP panels should use a 125MHz input
* clock divider.
*/
if (is_edp(intel_dp) && !is_pch_edp(intel_dp)) {
if (IS_GEN6(dev))
aux_clock_divider = 200; /* SNB eDP input clock at 400Mhz */
else
aux_clock_divider = 225; /* eDP input clock at 450Mhz */
} else if (HAS_PCH_SPLIT(dev))
aux_clock_divider = 62; /* IRL input clock fixed at 125Mhz */
else
aux_clock_divider = intel_hrawclk(dev) / 2;
 
if (IS_GEN6(dev))
precharge = 3;
else
precharge = 5;
 
/* Try to wait for any previous AUX channel activity */
for (try = 0; try < 3; try++) {
status = I915_READ(ch_ctl);
if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
break;
msleep(1);
}
 
if (try == 3) {
WARN(1, "dp_aux_ch not started status 0x%08x\n",
I915_READ(ch_ctl));
return -EBUSY;
}
 
/* Must try at least 3 times according to DP spec */
for (try = 0; try < 5; try++) {
/* Load the send data into the aux channel data registers */
for (i = 0; i < send_bytes; i += 4)
I915_WRITE(ch_data + i,
pack_aux(send + i, send_bytes - i));
 
/* Send the command and wait for it to complete */
I915_WRITE(ch_ctl,
DP_AUX_CH_CTL_SEND_BUSY |
DP_AUX_CH_CTL_TIME_OUT_400us |
(send_bytes << DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT) |
(precharge << DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT) |
(aux_clock_divider << DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT) |
DP_AUX_CH_CTL_DONE |
DP_AUX_CH_CTL_TIME_OUT_ERROR |
DP_AUX_CH_CTL_RECEIVE_ERROR);
for (;;) {
status = I915_READ(ch_ctl);
if ((status & DP_AUX_CH_CTL_SEND_BUSY) == 0)
break;
udelay(100);
}
 
/* Clear done status and any errors */
I915_WRITE(ch_ctl,
status |
DP_AUX_CH_CTL_DONE |
DP_AUX_CH_CTL_TIME_OUT_ERROR |
DP_AUX_CH_CTL_RECEIVE_ERROR);
if (status & DP_AUX_CH_CTL_DONE)
break;
}
 
if ((status & DP_AUX_CH_CTL_DONE) == 0) {
DRM_ERROR("dp_aux_ch not done status 0x%08x\n", status);
return -EBUSY;
}
 
/* Check for timeout or receive error.
* Timeouts occur when the sink is not connected
*/
if (status & DP_AUX_CH_CTL_RECEIVE_ERROR) {
DRM_ERROR("dp_aux_ch receive error status 0x%08x\n", status);
return -EIO;
}
 
/* Timeouts occur when the device isn't connected, so they're
* "normal" -- don't fill the kernel log with these */
if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) {
DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status);
return -ETIMEDOUT;
}
 
/* Unload any bytes sent back from the other side */
recv_bytes = ((status & DP_AUX_CH_CTL_MESSAGE_SIZE_MASK) >>
DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT);
if (recv_bytes > recv_size)
recv_bytes = recv_size;
 
for (i = 0; i < recv_bytes; i += 4)
unpack_aux(I915_READ(ch_data + i),
recv + i, recv_bytes - i);
 
return recv_bytes;
}
 
/* Write data to the aux channel in native mode */
static int
intel_dp_aux_native_write(struct intel_dp *intel_dp,
uint16_t address, uint8_t *send, int send_bytes)
{
int ret;
uint8_t msg[20];
int msg_bytes;
uint8_t ack;
 
if (send_bytes > 16)
return -1;
msg[0] = AUX_NATIVE_WRITE << 4;
msg[1] = address >> 8;
msg[2] = address & 0xff;
msg[3] = send_bytes - 1;
memcpy(&msg[4], send, send_bytes);
msg_bytes = send_bytes + 4;
for (;;) {
ret = intel_dp_aux_ch(intel_dp, msg, msg_bytes, &ack, 1);
if (ret < 0)
return ret;
if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK)
break;
else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
udelay(100);
else
return -EIO;
}
return send_bytes;
}
 
/* Write a single byte to the aux channel in native mode */
static int
intel_dp_aux_native_write_1(struct intel_dp *intel_dp,
uint16_t address, uint8_t byte)
{
return intel_dp_aux_native_write(intel_dp, address, &byte, 1);
}
 
/* read bytes from a native aux channel */
static int
intel_dp_aux_native_read(struct intel_dp *intel_dp,
uint16_t address, uint8_t *recv, int recv_bytes)
{
uint8_t msg[4];
int msg_bytes;
uint8_t reply[20];
int reply_bytes;
uint8_t ack;
int ret;
 
msg[0] = AUX_NATIVE_READ << 4;
msg[1] = address >> 8;
msg[2] = address & 0xff;
msg[3] = recv_bytes - 1;
 
msg_bytes = 4;
reply_bytes = recv_bytes + 1;
 
for (;;) {
ret = intel_dp_aux_ch(intel_dp, msg, msg_bytes,
reply, reply_bytes);
if (ret == 0)
return -EPROTO;
if (ret < 0)
return ret;
ack = reply[0];
if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_ACK) {
memcpy(recv, reply + 1, ret - 1);
return ret - 1;
}
else if ((ack & AUX_NATIVE_REPLY_MASK) == AUX_NATIVE_REPLY_DEFER)
udelay(100);
else
return -EIO;
}
}
 
static int
intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
uint8_t write_byte, uint8_t *read_byte)
{
struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
struct intel_dp *intel_dp = container_of(adapter,
struct intel_dp,
adapter);
uint16_t address = algo_data->address;
uint8_t msg[5];
uint8_t reply[2];
unsigned retry;
int msg_bytes;
int reply_bytes;
int ret;
 
/* Set up the command byte */
if (mode & MODE_I2C_READ)
msg[0] = AUX_I2C_READ << 4;
else
msg[0] = AUX_I2C_WRITE << 4;
 
if (!(mode & MODE_I2C_STOP))
msg[0] |= AUX_I2C_MOT << 4;
 
msg[1] = address >> 8;
msg[2] = address;
 
switch (mode) {
case MODE_I2C_WRITE:
msg[3] = 0;
msg[4] = write_byte;
msg_bytes = 5;
reply_bytes = 1;
break;
case MODE_I2C_READ:
msg[3] = 0;
msg_bytes = 4;
reply_bytes = 2;
break;
default:
msg_bytes = 3;
reply_bytes = 1;
break;
}
 
for (retry = 0; retry < 5; retry++) {
ret = intel_dp_aux_ch(intel_dp,
msg, msg_bytes,
reply, reply_bytes);
if (ret < 0) {
DRM_DEBUG_KMS("aux_ch failed %d\n", ret);
return ret;
}
 
switch (reply[0] & AUX_NATIVE_REPLY_MASK) {
case AUX_NATIVE_REPLY_ACK:
/* I2C-over-AUX Reply field is only valid
* when paired with AUX ACK.
*/
break;
case AUX_NATIVE_REPLY_NACK:
DRM_DEBUG_KMS("aux_ch native nack\n");
return -EREMOTEIO;
case AUX_NATIVE_REPLY_DEFER:
udelay(100);
continue;
default:
DRM_ERROR("aux_ch invalid native reply 0x%02x\n",
reply[0]);
return -EREMOTEIO;
}
 
switch (reply[0] & AUX_I2C_REPLY_MASK) {
case AUX_I2C_REPLY_ACK:
if (mode == MODE_I2C_READ) {
*read_byte = reply[1];
}
return reply_bytes - 1;
case AUX_I2C_REPLY_NACK:
DRM_DEBUG_KMS("aux_i2c nack\n");
return -EREMOTEIO;
case AUX_I2C_REPLY_DEFER:
DRM_DEBUG_KMS("aux_i2c defer\n");
udelay(100);
break;
default:
DRM_ERROR("aux_i2c invalid reply 0x%02x\n", reply[0]);
return -EREMOTEIO;
}
}
 
DRM_ERROR("too many retries, giving up\n");
return -EREMOTEIO;
}
 
static int
intel_dp_i2c_init(struct intel_dp *intel_dp,
struct intel_connector *intel_connector, const char *name)
{
DRM_DEBUG_KMS("i2c_init %s\n", name);
intel_dp->algo.running = false;
intel_dp->algo.address = 0;
intel_dp->algo.aux_ch = intel_dp_i2c_aux_ch;
 
memset(&intel_dp->adapter, '\0', sizeof (intel_dp->adapter));
// intel_dp->adapter.owner = THIS_MODULE;
intel_dp->adapter.class = I2C_CLASS_DDC;
strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1);
intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0';
intel_dp->adapter.algo_data = &intel_dp->algo;
intel_dp->adapter.dev.parent = &intel_connector->base.kdev;
 
return i2c_dp_aux_add_bus(&intel_dp->adapter);
}
 
static bool
intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
int lane_count, clock;
int max_lane_count = intel_dp_max_lane_count(intel_dp);
int max_clock = intel_dp_max_link_bw(intel_dp) == DP_LINK_BW_2_7 ? 1 : 0;
static int bws[2] = { DP_LINK_BW_1_62, DP_LINK_BW_2_7 };
 
if (is_edp(intel_dp) && dev_priv->panel_fixed_mode) {
intel_fixed_panel_mode(dev_priv->panel_fixed_mode, adjusted_mode);
intel_pch_panel_fitting(dev, DRM_MODE_SCALE_FULLSCREEN,
mode, adjusted_mode);
/*
* the mode->clock is used to calculate the Data&Link M/N
* of the pipe. For the eDP the fixed clock should be used.
*/
mode->clock = dev_priv->panel_fixed_mode->clock;
}
 
for (lane_count = 1; lane_count <= max_lane_count; lane_count <<= 1) {
for (clock = 0; clock <= max_clock; clock++) {
int link_avail = intel_dp_max_data_rate(intel_dp_link_clock(bws[clock]), lane_count);
 
if (intel_dp_link_required(encoder->dev, intel_dp, mode->clock)
<= link_avail) {
intel_dp->link_bw = bws[clock];
intel_dp->lane_count = lane_count;
adjusted_mode->clock = intel_dp_link_clock(intel_dp->link_bw);
DRM_DEBUG_KMS("Display port link bw %02x lane "
"count %d clock %d\n",
intel_dp->link_bw, intel_dp->lane_count,
adjusted_mode->clock);
return true;
}
}
}
 
if (is_edp(intel_dp)) {
/* okay we failed just pick the highest */
intel_dp->lane_count = max_lane_count;
intel_dp->link_bw = bws[max_clock];
adjusted_mode->clock = intel_dp_link_clock(intel_dp->link_bw);
DRM_DEBUG_KMS("Force picking display port link bw %02x lane "
"count %d clock %d\n",
intel_dp->link_bw, intel_dp->lane_count,
adjusted_mode->clock);
 
return true;
}
 
return false;
}
 
struct intel_dp_m_n {
uint32_t tu;
uint32_t gmch_m;
230,53 → 740,1147
}
}
 
static void
intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
struct drm_crtc *crtc = intel_dp->base.base.crtc;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
intel_dp->DP = DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
intel_dp->DP |= intel_dp->color_range;
 
if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
intel_dp->DP |= DP_SYNC_HS_HIGH;
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
intel_dp->DP |= DP_SYNC_VS_HIGH;
 
if (HAS_PCH_CPT(dev) && !is_edp(intel_dp))
intel_dp->DP |= DP_LINK_TRAIN_OFF_CPT;
else
intel_dp->DP |= DP_LINK_TRAIN_OFF;
 
switch (intel_dp->lane_count) {
case 1:
intel_dp->DP |= DP_PORT_WIDTH_1;
break;
case 2:
intel_dp->DP |= DP_PORT_WIDTH_2;
break;
case 4:
intel_dp->DP |= DP_PORT_WIDTH_4;
break;
}
if (intel_dp->has_audio)
intel_dp->DP |= DP_AUDIO_OUTPUT_ENABLE;
 
memset(intel_dp->link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
intel_dp->link_configuration[0] = intel_dp->link_bw;
intel_dp->link_configuration[1] = intel_dp->lane_count;
intel_dp->link_configuration[8] = DP_SET_ANSI_8B10B;
 
/*
* Check for DPCD version > 1.1 and enhanced framing support
*/
if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11 &&
(intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)) {
intel_dp->link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
intel_dp->DP |= DP_ENHANCED_FRAMING;
}
 
/* CPT DP's pipe select is decided in TRANS_DP_CTL */
if (intel_crtc->pipe == 1 && !HAS_PCH_CPT(dev))
intel_dp->DP |= DP_PIPEB_SELECT;
 
if (is_edp(intel_dp) && !is_pch_edp(intel_dp)) {
/* don't miss out required setting for eDP */
intel_dp->DP |= DP_PLL_ENABLE;
if (adjusted_mode->clock < 200000)
intel_dp->DP |= DP_PLL_FREQ_160MHZ;
else
intel_dp->DP |= DP_PLL_FREQ_270MHZ;
}
}
 
static void ironlake_edp_panel_vdd_on(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp;
 
/*
* If the panel wasn't on, make sure there's not a currently
* active PP sequence before enabling AUX VDD.
*/
if (!(I915_READ(PCH_PP_STATUS) & PP_ON))
msleep(dev_priv->panel_t3);
 
pp = I915_READ(PCH_PP_CONTROL);
pp |= EDP_FORCE_VDD;
I915_WRITE(PCH_PP_CONTROL, pp);
POSTING_READ(PCH_PP_CONTROL);
}
 
static void ironlake_edp_panel_vdd_off(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp;
 
pp = I915_READ(PCH_PP_CONTROL);
pp &= ~EDP_FORCE_VDD;
I915_WRITE(PCH_PP_CONTROL, pp);
POSTING_READ(PCH_PP_CONTROL);
 
/* Make sure sequencer is idle before allowing subsequent activity */
msleep(dev_priv->panel_t12);
}
 
/* Returns true if the panel was already on when called */
static bool ironlake_edp_panel_on (struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp, idle_on_mask = PP_ON | PP_SEQUENCE_STATE_ON_IDLE;
 
if (I915_READ(PCH_PP_STATUS) & PP_ON)
return true;
 
pp = I915_READ(PCH_PP_CONTROL);
 
/* ILK workaround: disable reset around power sequence */
pp &= ~PANEL_POWER_RESET;
I915_WRITE(PCH_PP_CONTROL, pp);
POSTING_READ(PCH_PP_CONTROL);
 
pp |= PANEL_UNLOCK_REGS | POWER_TARGET_ON;
I915_WRITE(PCH_PP_CONTROL, pp);
POSTING_READ(PCH_PP_CONTROL);
 
if (wait_for((I915_READ(PCH_PP_STATUS) & idle_on_mask) == idle_on_mask,
5000))
DRM_ERROR("panel on wait timed out: 0x%08x\n",
I915_READ(PCH_PP_STATUS));
 
pp |= PANEL_POWER_RESET; /* restore panel reset bit */
I915_WRITE(PCH_PP_CONTROL, pp);
POSTING_READ(PCH_PP_CONTROL);
 
return false;
}
 
static void ironlake_edp_panel_off (struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp, idle_off_mask = PP_ON | PP_SEQUENCE_MASK |
PP_CYCLE_DELAY_ACTIVE | PP_SEQUENCE_STATE_MASK;
 
pp = I915_READ(PCH_PP_CONTROL);
 
/* ILK workaround: disable reset around power sequence */
pp &= ~PANEL_POWER_RESET;
I915_WRITE(PCH_PP_CONTROL, pp);
POSTING_READ(PCH_PP_CONTROL);
 
pp &= ~POWER_TARGET_ON;
I915_WRITE(PCH_PP_CONTROL, pp);
POSTING_READ(PCH_PP_CONTROL);
 
if (wait_for((I915_READ(PCH_PP_STATUS) & idle_off_mask) == 0, 5000))
DRM_ERROR("panel off wait timed out: 0x%08x\n",
I915_READ(PCH_PP_STATUS));
 
pp |= PANEL_POWER_RESET; /* restore panel reset bit */
I915_WRITE(PCH_PP_CONTROL, pp);
POSTING_READ(PCH_PP_CONTROL);
}
 
static void ironlake_edp_backlight_on (struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp;
 
DRM_DEBUG_KMS("\n");
/*
* If we enable the backlight right away following a panel power
* on, we may see slight flicker as the panel syncs with the eDP
* link. So delay a bit to make sure the image is solid before
* allowing it to appear.
*/
msleep(300);
pp = I915_READ(PCH_PP_CONTROL);
pp |= EDP_BLC_ENABLE;
I915_WRITE(PCH_PP_CONTROL, pp);
}
 
static void ironlake_edp_backlight_off (struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 pp;
 
DRM_DEBUG_KMS("\n");
pp = I915_READ(PCH_PP_CONTROL);
pp &= ~EDP_BLC_ENABLE;
I915_WRITE(PCH_PP_CONTROL, pp);
}
 
static void ironlake_edp_pll_on(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 dpa_ctl;
 
DRM_DEBUG_KMS("\n");
dpa_ctl = I915_READ(DP_A);
dpa_ctl |= DP_PLL_ENABLE;
I915_WRITE(DP_A, dpa_ctl);
POSTING_READ(DP_A);
udelay(200);
}
 
static void ironlake_edp_pll_off(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 dpa_ctl;
 
dpa_ctl = I915_READ(DP_A);
dpa_ctl &= ~DP_PLL_ENABLE;
I915_WRITE(DP_A, dpa_ctl);
POSTING_READ(DP_A);
udelay(200);
}
 
/* If the sink supports it, try to set the power state appropriately */
static void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode)
{
int ret, i;
 
/* Should have a valid DPCD by this point */
if (intel_dp->dpcd[DP_DPCD_REV] < 0x11)
return;
 
if (mode != DRM_MODE_DPMS_ON) {
ret = intel_dp_aux_native_write_1(intel_dp, DP_SET_POWER,
DP_SET_POWER_D3);
if (ret != 1)
DRM_DEBUG_DRIVER("failed to write sink power state\n");
} else {
/*
* When turning on, we need to retry for 1ms to give the sink
* time to wake up.
*/
for (i = 0; i < 3; i++) {
ret = intel_dp_aux_native_write_1(intel_dp,
DP_SET_POWER,
DP_SET_POWER_D0);
if (ret == 1)
break;
msleep(1);
}
}
}
 
static void intel_dp_prepare(struct drm_encoder *encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
struct drm_device *dev = encoder->dev;
 
/* Wake up the sink first */
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
 
if (is_edp(intel_dp)) {
ironlake_edp_backlight_off(dev);
ironlake_edp_panel_off(dev);
if (!is_pch_edp(intel_dp))
ironlake_edp_pll_on(encoder);
else
ironlake_edp_pll_off(encoder);
}
intel_dp_link_down(intel_dp);
}
 
static void intel_dp_commit(struct drm_encoder *encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
struct drm_device *dev = encoder->dev;
 
if (is_edp(intel_dp))
ironlake_edp_panel_vdd_on(intel_dp);
 
intel_dp_start_link_train(intel_dp);
 
if (is_edp(intel_dp)) {
ironlake_edp_panel_on(intel_dp);
ironlake_edp_panel_vdd_off(intel_dp);
}
 
intel_dp_complete_link_train(intel_dp);
 
if (is_edp(intel_dp))
ironlake_edp_backlight_on(dev);
 
intel_dp->dpms_mode = DRM_MODE_DPMS_ON;
}
 
static void
intel_dp_dpms(struct drm_encoder *encoder, int mode)
{
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t dp_reg = I915_READ(intel_dp->output_reg);
 
if (mode != DRM_MODE_DPMS_ON) {
if (is_edp(intel_dp))
ironlake_edp_backlight_off(dev);
intel_dp_sink_dpms(intel_dp, mode);
intel_dp_link_down(intel_dp);
if (is_edp(intel_dp))
ironlake_edp_panel_off(dev);
if (is_edp(intel_dp) && !is_pch_edp(intel_dp))
ironlake_edp_pll_off(encoder);
} else {
if (is_edp(intel_dp))
ironlake_edp_panel_vdd_on(intel_dp);
intel_dp_sink_dpms(intel_dp, mode);
if (!(dp_reg & DP_PORT_EN)) {
intel_dp_start_link_train(intel_dp);
if (is_edp(intel_dp)) {
ironlake_edp_panel_on(intel_dp);
ironlake_edp_panel_vdd_off(intel_dp);
}
intel_dp_complete_link_train(intel_dp);
}
if (is_edp(intel_dp))
ironlake_edp_backlight_on(dev);
}
intel_dp->dpms_mode = mode;
}
 
/*
* Native read with retry for link status and receiver capability reads for
* cases where the sink may still be asleep.
*/
static bool
intel_dp_aux_native_read_retry(struct intel_dp *intel_dp, uint16_t address,
uint8_t *recv, int recv_bytes)
{
int ret, i;
 
/*
* Sinks are *supposed* to come up within 1ms from an off state,
* but we're also supposed to retry 3 times per the spec.
*/
for (i = 0; i < 3; i++) {
ret = intel_dp_aux_native_read(intel_dp, address, recv,
recv_bytes);
if (ret == recv_bytes)
return true;
msleep(1);
}
 
return false;
}
 
/*
* Fetch AUX CH registers 0x202 - 0x207 which contain
* link status information
*/
static bool
intel_dp_get_link_status(struct intel_dp *intel_dp)
{
return intel_dp_aux_native_read_retry(intel_dp,
DP_LANE0_1_STATUS,
intel_dp->link_status,
DP_LINK_STATUS_SIZE);
}
 
static uint8_t
intel_dp_link_status(uint8_t link_status[DP_LINK_STATUS_SIZE],
int r)
{
return link_status[r - DP_LANE0_1_STATUS];
}
 
static uint8_t
intel_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE],
int lane)
{
int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
int s = ((lane & 1) ?
DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
uint8_t l = intel_dp_link_status(link_status, i);
 
return ((l >> s) & 3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
}
 
static uint8_t
intel_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE],
int lane)
{
int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
int s = ((lane & 1) ?
DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
uint8_t l = intel_dp_link_status(link_status, i);
 
return ((l >> s) & 3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
}
 
 
#if 0
static char *voltage_names[] = {
"0.4V", "0.6V", "0.8V", "1.2V"
};
static char *pre_emph_names[] = {
"0dB", "3.5dB", "6dB", "9.5dB"
};
static char *link_train_names[] = {
"pattern 1", "pattern 2", "idle", "off"
};
#endif
 
/*
* These are source-specific values; current Intel hardware supports
* a maximum voltage of 800mV and a maximum pre-emphasis of 6dB
*/
#define I830_DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_800
 
static uint8_t
intel_dp_pre_emphasis_max(uint8_t voltage_swing)
{
switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
case DP_TRAIN_VOLTAGE_SWING_400:
return DP_TRAIN_PRE_EMPHASIS_6;
case DP_TRAIN_VOLTAGE_SWING_600:
return DP_TRAIN_PRE_EMPHASIS_6;
case DP_TRAIN_VOLTAGE_SWING_800:
return DP_TRAIN_PRE_EMPHASIS_3_5;
case DP_TRAIN_VOLTAGE_SWING_1200:
default:
return DP_TRAIN_PRE_EMPHASIS_0;
}
}
 
static void
intel_get_adjust_train(struct intel_dp *intel_dp)
{
uint8_t v = 0;
uint8_t p = 0;
int lane;
 
for (lane = 0; lane < intel_dp->lane_count; lane++) {
uint8_t this_v = intel_get_adjust_request_voltage(intel_dp->link_status, lane);
uint8_t this_p = intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane);
 
if (this_v > v)
v = this_v;
if (this_p > p)
p = this_p;
}
 
if (v >= I830_DP_VOLTAGE_MAX)
v = I830_DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED;
 
if (p >= intel_dp_pre_emphasis_max(v))
p = intel_dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
 
for (lane = 0; lane < 4; lane++)
intel_dp->train_set[lane] = v | p;
}
 
static uint32_t
intel_dp_signal_levels(uint8_t train_set, int lane_count)
{
uint32_t signal_levels = 0;
 
switch (train_set & DP_TRAIN_VOLTAGE_SWING_MASK) {
case DP_TRAIN_VOLTAGE_SWING_400:
default:
signal_levels |= DP_VOLTAGE_0_4;
break;
case DP_TRAIN_VOLTAGE_SWING_600:
signal_levels |= DP_VOLTAGE_0_6;
break;
case DP_TRAIN_VOLTAGE_SWING_800:
signal_levels |= DP_VOLTAGE_0_8;
break;
case DP_TRAIN_VOLTAGE_SWING_1200:
signal_levels |= DP_VOLTAGE_1_2;
break;
}
switch (train_set & DP_TRAIN_PRE_EMPHASIS_MASK) {
case DP_TRAIN_PRE_EMPHASIS_0:
default:
signal_levels |= DP_PRE_EMPHASIS_0;
break;
case DP_TRAIN_PRE_EMPHASIS_3_5:
signal_levels |= DP_PRE_EMPHASIS_3_5;
break;
case DP_TRAIN_PRE_EMPHASIS_6:
signal_levels |= DP_PRE_EMPHASIS_6;
break;
case DP_TRAIN_PRE_EMPHASIS_9_5:
signal_levels |= DP_PRE_EMPHASIS_9_5;
break;
}
return signal_levels;
}
 
/* Gen6's DP voltage swing and pre-emphasis control */
static uint32_t
intel_gen6_edp_signal_levels(uint8_t train_set)
{
int signal_levels = train_set & (DP_TRAIN_VOLTAGE_SWING_MASK |
DP_TRAIN_PRE_EMPHASIS_MASK);
switch (signal_levels) {
case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_0:
case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_0:
return EDP_LINK_TRAIN_400_600MV_0DB_SNB_B;
case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_3_5:
return EDP_LINK_TRAIN_400MV_3_5DB_SNB_B;
case DP_TRAIN_VOLTAGE_SWING_400 | DP_TRAIN_PRE_EMPHASIS_6:
case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_6:
return EDP_LINK_TRAIN_400_600MV_6DB_SNB_B;
case DP_TRAIN_VOLTAGE_SWING_600 | DP_TRAIN_PRE_EMPHASIS_3_5:
case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_3_5:
return EDP_LINK_TRAIN_600_800MV_3_5DB_SNB_B;
case DP_TRAIN_VOLTAGE_SWING_800 | DP_TRAIN_PRE_EMPHASIS_0:
case DP_TRAIN_VOLTAGE_SWING_1200 | DP_TRAIN_PRE_EMPHASIS_0:
return EDP_LINK_TRAIN_800_1200MV_0DB_SNB_B;
default:
DRM_DEBUG_KMS("Unsupported voltage swing/pre-emphasis level:"
"0x%x\n", signal_levels);
return EDP_LINK_TRAIN_400_600MV_0DB_SNB_B;
}
}
 
static uint8_t
intel_get_lane_status(uint8_t link_status[DP_LINK_STATUS_SIZE],
int lane)
{
int i = DP_LANE0_1_STATUS + (lane >> 1);
int s = (lane & 1) * 4;
uint8_t l = intel_dp_link_status(link_status, i);
 
return (l >> s) & 0xf;
}
 
/* Check for clock recovery is done on all channels */
static bool
intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count)
{
int lane;
uint8_t lane_status;
 
for (lane = 0; lane < lane_count; lane++) {
lane_status = intel_get_lane_status(link_status, lane);
if ((lane_status & DP_LANE_CR_DONE) == 0)
return false;
}
return true;
}
 
/* Check to see if channel eq is done on all channels */
#define CHANNEL_EQ_BITS (DP_LANE_CR_DONE|\
DP_LANE_CHANNEL_EQ_DONE|\
DP_LANE_SYMBOL_LOCKED)
static bool
intel_channel_eq_ok(struct intel_dp *intel_dp)
{
uint8_t lane_align;
uint8_t lane_status;
int lane;
 
lane_align = intel_dp_link_status(intel_dp->link_status,
DP_LANE_ALIGN_STATUS_UPDATED);
if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
return false;
for (lane = 0; lane < intel_dp->lane_count; lane++) {
lane_status = intel_get_lane_status(intel_dp->link_status, lane);
if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS)
return false;
}
return true;
}
 
static bool
intel_dp_set_link_train(struct intel_dp *intel_dp,
uint32_t dp_reg_value,
uint8_t dp_train_pat)
{
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
 
I915_WRITE(intel_dp->output_reg, dp_reg_value);
POSTING_READ(intel_dp->output_reg);
 
intel_dp_aux_native_write_1(intel_dp,
DP_TRAINING_PATTERN_SET,
dp_train_pat);
 
ret = intel_dp_aux_native_write(intel_dp,
DP_TRAINING_LANE0_SET,
intel_dp->train_set, 4);
if (ret != 4)
return false;
 
return true;
}
 
/* Enable corresponding port and start training pattern 1 */
static void
intel_dp_start_link_train(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc);
int i;
uint8_t voltage;
bool clock_recovery = false;
int tries;
u32 reg;
uint32_t DP = intel_dp->DP;
 
/*
* On CPT we have to enable the port in training pattern 1, which
* will happen below in intel_dp_set_link_train. Otherwise, enable
* the port and wait for it to become active.
*/
if (!HAS_PCH_CPT(dev)) {
I915_WRITE(intel_dp->output_reg, intel_dp->DP);
POSTING_READ(intel_dp->output_reg);
intel_wait_for_vblank(dev, intel_crtc->pipe);
}
 
/* Write the link configuration data */
intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET,
intel_dp->link_configuration,
DP_LINK_CONFIGURATION_SIZE);
 
DP |= DP_PORT_EN;
if (HAS_PCH_CPT(dev) && !is_edp(intel_dp))
DP &= ~DP_LINK_TRAIN_MASK_CPT;
else
DP &= ~DP_LINK_TRAIN_MASK;
memset(intel_dp->train_set, 0, 4);
voltage = 0xff;
tries = 0;
clock_recovery = false;
for (;;) {
/* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
uint32_t signal_levels;
if (IS_GEN6(dev) && is_edp(intel_dp)) {
signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]);
DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels;
} else {
signal_levels = intel_dp_signal_levels(intel_dp->train_set[0], intel_dp->lane_count);
DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
}
 
if (HAS_PCH_CPT(dev) && !is_edp(intel_dp))
reg = DP | DP_LINK_TRAIN_PAT_1_CPT;
else
reg = DP | DP_LINK_TRAIN_PAT_1;
 
if (!intel_dp_set_link_train(intel_dp, reg,
DP_TRAINING_PATTERN_1 |
DP_LINK_SCRAMBLING_DISABLE))
break;
/* Set training pattern 1 */
 
udelay(100);
if (!intel_dp_get_link_status(intel_dp))
break;
 
if (intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) {
clock_recovery = true;
break;
}
 
/* Check to see if we've tried the max voltage */
for (i = 0; i < intel_dp->lane_count; i++)
if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
break;
if (i == intel_dp->lane_count)
break;
 
/* Check to see if we've tried the same voltage 5 times */
if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
++tries;
if (tries == 5)
break;
} else
tries = 0;
voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
 
/* Compute new intel_dp->train_set as requested by target */
intel_get_adjust_train(intel_dp);
}
 
intel_dp->DP = DP;
}
 
static void
intel_dp_complete_link_train(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
bool channel_eq = false;
int tries, cr_tries;
u32 reg;
uint32_t DP = intel_dp->DP;
 
/* channel equalization */
tries = 0;
cr_tries = 0;
channel_eq = false;
for (;;) {
/* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
uint32_t signal_levels;
 
if (cr_tries > 5) {
DRM_ERROR("failed to train DP, aborting\n");
intel_dp_link_down(intel_dp);
break;
}
 
if (IS_GEN6(dev) && is_edp(intel_dp)) {
signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]);
DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels;
} else {
signal_levels = intel_dp_signal_levels(intel_dp->train_set[0], intel_dp->lane_count);
DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
}
 
if (HAS_PCH_CPT(dev) && !is_edp(intel_dp))
reg = DP | DP_LINK_TRAIN_PAT_2_CPT;
else
reg = DP | DP_LINK_TRAIN_PAT_2;
 
/* channel eq pattern */
if (!intel_dp_set_link_train(intel_dp, reg,
DP_TRAINING_PATTERN_2 |
DP_LINK_SCRAMBLING_DISABLE))
break;
 
udelay(400);
if (!intel_dp_get_link_status(intel_dp))
break;
 
/* Make sure clock is still ok */
if (!intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) {
intel_dp_start_link_train(intel_dp);
cr_tries++;
continue;
}
 
if (intel_channel_eq_ok(intel_dp)) {
channel_eq = true;
break;
}
 
/* Try 5 times, then try clock recovery if that fails */
if (tries > 5) {
intel_dp_link_down(intel_dp);
intel_dp_start_link_train(intel_dp);
tries = 0;
cr_tries++;
continue;
}
 
/* Compute new intel_dp->train_set as requested by target */
intel_get_adjust_train(intel_dp);
++tries;
}
 
if (HAS_PCH_CPT(dev) && !is_edp(intel_dp))
reg = DP | DP_LINK_TRAIN_OFF_CPT;
else
reg = DP | DP_LINK_TRAIN_OFF;
 
I915_WRITE(intel_dp->output_reg, reg);
POSTING_READ(intel_dp->output_reg);
intel_dp_aux_native_write_1(intel_dp,
DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE);
}
 
static void
intel_dp_link_down(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t DP = intel_dp->DP;
 
if ((I915_READ(intel_dp->output_reg) & DP_PORT_EN) == 0)
return;
 
DRM_DEBUG_KMS("\n");
 
if (is_edp(intel_dp)) {
DP &= ~DP_PLL_ENABLE;
I915_WRITE(intel_dp->output_reg, DP);
POSTING_READ(intel_dp->output_reg);
udelay(100);
}
 
if (HAS_PCH_CPT(dev) && !is_edp(intel_dp)) {
DP &= ~DP_LINK_TRAIN_MASK_CPT;
I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE_CPT);
} else {
DP &= ~DP_LINK_TRAIN_MASK;
I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE);
}
POSTING_READ(intel_dp->output_reg);
 
msleep(17);
 
if (is_edp(intel_dp))
DP |= DP_LINK_TRAIN_OFF;
 
if (!HAS_PCH_CPT(dev) &&
I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) {
struct drm_crtc *crtc = intel_dp->base.base.crtc;
 
/* Hardware workaround: leaving our transcoder select
* set to transcoder B while it's off will prevent the
* corresponding HDMI output on transcoder A.
*
* Combine this with another hardware workaround:
* transcoder select bit can only be cleared while the
* port is enabled.
*/
DP &= ~DP_PIPEB_SELECT;
I915_WRITE(intel_dp->output_reg, DP);
 
/* Changes to enable or select take place the vblank
* after being written.
*/
if (crtc == NULL) {
/* We can arrive here never having been attached
* to a CRTC, for instance, due to inheriting
* random state from the BIOS.
*
* If the pipe is not running, play safe and
* wait for the clocks to stabilise before
* continuing.
*/
POSTING_READ(intel_dp->output_reg);
msleep(50);
} else
intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe);
}
 
I915_WRITE(intel_dp->output_reg, DP & ~DP_PORT_EN);
POSTING_READ(intel_dp->output_reg);
}
 
static bool
intel_dp_get_dpcd(struct intel_dp *intel_dp)
{
if (intel_dp_aux_native_read_retry(intel_dp, 0x000, intel_dp->dpcd,
sizeof (intel_dp->dpcd)) &&
(intel_dp->dpcd[DP_DPCD_REV] != 0)) {
return true;
}
 
return false;
}
 
/*
* According to DP spec
* 5.1.2:
* 1. Read DPCD
* 2. Configure link according to Receiver Capabilities
* 3. Use Link Training from 2.5.3.3 and 3.5.1.3
* 4. Check link status on receipt of hot-plug interrupt
*/
 
static void
intel_dp_check_link_status(struct intel_dp *intel_dp)
{
if (intel_dp->dpms_mode != DRM_MODE_DPMS_ON)
return;
 
if (!intel_dp->base.base.crtc)
return;
 
/* Try to read receiver status if the link appears to be up */
if (!intel_dp_get_link_status(intel_dp)) {
intel_dp_link_down(intel_dp);
return;
}
 
/* Now read the DPCD to see if it's actually running */
if (!intel_dp_get_dpcd(intel_dp)) {
intel_dp_link_down(intel_dp);
return;
}
 
if (!intel_channel_eq_ok(intel_dp)) {
DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n",
drm_get_encoder_name(&intel_dp->base.base));
intel_dp_start_link_train(intel_dp);
intel_dp_complete_link_train(intel_dp);
}
}
 
static enum drm_connector_status
intel_dp_detect_dpcd(struct intel_dp *intel_dp)
{
if (intel_dp_get_dpcd(intel_dp))
return connector_status_connected;
return connector_status_disconnected;
}
 
static enum drm_connector_status
ironlake_dp_detect(struct intel_dp *intel_dp)
{
enum drm_connector_status status;
 
/* Can't disconnect eDP, but you can close the lid... */
if (is_edp(intel_dp)) {
status = intel_panel_detect(intel_dp->base.base.dev);
if (status == connector_status_unknown)
status = connector_status_connected;
return status;
}
 
return intel_dp_detect_dpcd(intel_dp);
}
 
static enum drm_connector_status
g4x_dp_detect(struct intel_dp *intel_dp)
{
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t temp, bit;
 
switch (intel_dp->output_reg) {
case DP_B:
bit = DPB_HOTPLUG_INT_STATUS;
break;
case DP_C:
bit = DPC_HOTPLUG_INT_STATUS;
break;
case DP_D:
bit = DPD_HOTPLUG_INT_STATUS;
break;
default:
return connector_status_unknown;
}
 
temp = I915_READ(PORT_HOTPLUG_STAT);
 
if ((temp & bit) == 0)
return connector_status_disconnected;
 
return intel_dp_detect_dpcd(intel_dp);
}
 
/**
* Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect DP connection.
*
* \return true if DP port is connected.
* \return false if DP port is disconnected.
*/
static enum drm_connector_status
intel_dp_detect(struct drm_connector *connector, bool force)
{
struct intel_dp *intel_dp = intel_attached_dp(connector);
struct drm_device *dev = intel_dp->base.base.dev;
enum drm_connector_status status;
struct edid *edid = NULL;
 
intel_dp->has_audio = false;
 
if (HAS_PCH_SPLIT(dev))
status = ironlake_dp_detect(intel_dp);
else
status = g4x_dp_detect(intel_dp);
 
DRM_DEBUG_KMS("DPCD: %02hx%02hx%02hx%02hx%02hx%02hx%02hx%02hx\n",
intel_dp->dpcd[0], intel_dp->dpcd[1], intel_dp->dpcd[2],
intel_dp->dpcd[3], intel_dp->dpcd[4], intel_dp->dpcd[5],
intel_dp->dpcd[6], intel_dp->dpcd[7]);
 
if (status != connector_status_connected)
return status;
/*
if (intel_dp->force_audio) {
intel_dp->has_audio = intel_dp->force_audio > 0;
} else {
edid = drm_get_edid(connector, &intel_dp->adapter);
if (edid) {
intel_dp->has_audio = drm_detect_monitor_audio(edid);
connector->display_info.raw_edid = NULL;
kfree(edid);
}
}
*/
return connector_status_connected;
}
 
static int intel_dp_get_modes(struct drm_connector *connector)
{
struct intel_dp *intel_dp = intel_attached_dp(connector);
struct drm_device *dev = intel_dp->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
 
/* We should parse the EDID data and find out if it has an audio sink
*/
 
ret = intel_ddc_get_modes(connector, &intel_dp->adapter);
if (ret) {
if (is_edp(intel_dp) && !dev_priv->panel_fixed_mode) {
struct drm_display_mode *newmode;
list_for_each_entry(newmode, &connector->probed_modes,
head) {
if (newmode->type & DRM_MODE_TYPE_PREFERRED) {
dev_priv->panel_fixed_mode =
drm_mode_duplicate(dev, newmode);
break;
}
}
}
 
return ret;
}
 
/* if eDP has no EDID, try to use fixed panel mode from VBT */
if (is_edp(intel_dp)) {
if (dev_priv->panel_fixed_mode != NULL) {
struct drm_display_mode *mode;
mode = drm_mode_duplicate(dev, dev_priv->panel_fixed_mode);
drm_mode_probed_add(connector, mode);
return 1;
}
}
return 0;
}
 
 
 
 
 
static int
intel_dp_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t val)
{
struct drm_i915_private *dev_priv = connector->dev->dev_private;
struct intel_dp *intel_dp = intel_attached_dp(connector);
int ret;
 
ret = drm_connector_property_set_value(connector, property, val);
if (ret)
return ret;
#if 0
if (property == dev_priv->force_audio_property) {
int i = val;
bool has_audio;
 
if (i == intel_dp->force_audio)
return 0;
 
intel_dp->force_audio = i;
 
if (i == 0)
has_audio = intel_dp_detect_audio(connector);
else
has_audio = i > 0;
 
if (has_audio == intel_dp->has_audio)
return 0;
 
intel_dp->has_audio = has_audio;
goto done;
}
 
if (property == dev_priv->broadcast_rgb_property) {
if (val == !!intel_dp->color_range)
return 0;
 
intel_dp->color_range = val ? DP_COLOR_RANGE_16_235 : 0;
goto done;
}
#endif
 
return -EINVAL;
 
done:
if (intel_dp->base.base.crtc) {
struct drm_crtc *crtc = intel_dp->base.base.crtc;
drm_crtc_helper_set_mode(crtc, &crtc->mode,
crtc->x, crtc->y,
crtc->fb);
}
 
return 0;
}
 
static void
intel_dp_destroy (struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
 
if (intel_dpd_is_edp(dev))
intel_panel_destroy_backlight(dev);
 
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
 
static void intel_dp_encoder_destroy(struct drm_encoder *encoder)
{
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
 
// i2c_del_adapter(&intel_dp->adapter);
drm_encoder_cleanup(encoder);
kfree(intel_dp);
}
 
static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = {
.dpms = intel_dp_dpms,
.mode_fixup = intel_dp_mode_fixup,
.prepare = intel_dp_prepare,
.mode_set = intel_dp_mode_set,
.commit = intel_dp_commit,
};
 
static const struct drm_connector_funcs intel_dp_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.detect = intel_dp_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_dp_set_property,
.destroy = intel_dp_destroy,
};
 
static const struct drm_connector_helper_funcs intel_dp_connector_helper_funcs = {
.get_modes = intel_dp_get_modes,
.mode_valid = intel_dp_mode_valid,
.best_encoder = intel_best_encoder,
};
 
static const struct drm_encoder_funcs intel_dp_enc_funcs = {
.destroy = intel_dp_encoder_destroy,
};
 
static void
intel_dp_hot_plug(struct intel_encoder *intel_encoder)
{
struct intel_dp *intel_dp = container_of(intel_encoder, struct intel_dp, base);
 
intel_dp_check_link_status(intel_dp);
}
 
/* Return which DP Port should be selected for Transcoder DP control */
int
intel_trans_dp_port_sel (struct drm_crtc *crtc)
298,3 → 1902,180
 
return -1;
}
 
/* check the VBT to see whether the eDP is on DP-D port */
bool intel_dpd_is_edp(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct child_device_config *p_child;
int i;
 
if (!dev_priv->child_dev_num)
return false;
 
for (i = 0; i < dev_priv->child_dev_num; i++) {
p_child = dev_priv->child_dev + i;
 
if (p_child->dvo_port == PORT_IDPD &&
p_child->device_type == DEVICE_TYPE_eDP)
return true;
}
return false;
}
 
static void
intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector)
{
intel_attach_force_audio_property(connector);
intel_attach_broadcast_rgb_property(connector);
}
 
void
intel_dp_init(struct drm_device *dev, int output_reg)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_connector *connector;
struct intel_dp *intel_dp;
struct intel_encoder *intel_encoder;
struct intel_connector *intel_connector;
const char *name = NULL;
int type;
 
intel_dp = kzalloc(sizeof(struct intel_dp), GFP_KERNEL);
if (!intel_dp)
return;
 
intel_dp->output_reg = output_reg;
intel_dp->dpms_mode = -1;
 
intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
if (!intel_connector) {
kfree(intel_dp);
return;
}
intel_encoder = &intel_dp->base;
 
if (HAS_PCH_SPLIT(dev) && output_reg == PCH_DP_D)
if (intel_dpd_is_edp(dev))
intel_dp->is_pch_edp = true;
 
if (output_reg == DP_A || is_pch_edp(intel_dp)) {
type = DRM_MODE_CONNECTOR_eDP;
intel_encoder->type = INTEL_OUTPUT_EDP;
} else {
type = DRM_MODE_CONNECTOR_DisplayPort;
intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
}
 
connector = &intel_connector->base;
drm_connector_init(dev, connector, &intel_dp_connector_funcs, type);
drm_connector_helper_add(connector, &intel_dp_connector_helper_funcs);
 
connector->polled = DRM_CONNECTOR_POLL_HPD;
 
if (output_reg == DP_B || output_reg == PCH_DP_B)
intel_encoder->clone_mask = (1 << INTEL_DP_B_CLONE_BIT);
else if (output_reg == DP_C || output_reg == PCH_DP_C)
intel_encoder->clone_mask = (1 << INTEL_DP_C_CLONE_BIT);
else if (output_reg == DP_D || output_reg == PCH_DP_D)
intel_encoder->clone_mask = (1 << INTEL_DP_D_CLONE_BIT);
 
if (is_edp(intel_dp))
intel_encoder->clone_mask = (1 << INTEL_EDP_CLONE_BIT);
 
intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
connector->interlace_allowed = true;
connector->doublescan_allowed = 0;
 
drm_encoder_init(dev, &intel_encoder->base, &intel_dp_enc_funcs,
DRM_MODE_ENCODER_TMDS);
drm_encoder_helper_add(&intel_encoder->base, &intel_dp_helper_funcs);
 
intel_connector_attach_encoder(intel_connector, intel_encoder);
drm_sysfs_connector_add(connector);
 
/* Set up the DDC bus. */
switch (output_reg) {
case DP_A:
name = "DPDDC-A";
break;
case DP_B:
case PCH_DP_B:
dev_priv->hotplug_supported_mask |=
HDMIB_HOTPLUG_INT_STATUS;
name = "DPDDC-B";
break;
case DP_C:
case PCH_DP_C:
dev_priv->hotplug_supported_mask |=
HDMIC_HOTPLUG_INT_STATUS;
name = "DPDDC-C";
break;
case DP_D:
case PCH_DP_D:
dev_priv->hotplug_supported_mask |=
HDMID_HOTPLUG_INT_STATUS;
name = "DPDDC-D";
break;
}
 
intel_dp_i2c_init(intel_dp, intel_connector, name);
 
/* Cache some DPCD data in the eDP case */
if (is_edp(intel_dp)) {
bool ret;
u32 pp_on, pp_div;
 
pp_on = I915_READ(PCH_PP_ON_DELAYS);
pp_div = I915_READ(PCH_PP_DIVISOR);
 
/* Get T3 & T12 values (note: VESA not bspec terminology) */
dev_priv->panel_t3 = (pp_on & 0x1fff0000) >> 16;
dev_priv->panel_t3 /= 10; /* t3 in 100us units */
dev_priv->panel_t12 = pp_div & 0xf;
dev_priv->panel_t12 *= 100; /* t12 in 100ms units */
 
ironlake_edp_panel_vdd_on(intel_dp);
ret = intel_dp_get_dpcd(intel_dp);
ironlake_edp_panel_vdd_off(intel_dp);
if (ret) {
if (intel_dp->dpcd[DP_DPCD_REV] >= 0x11)
dev_priv->no_aux_handshake =
intel_dp->dpcd[DP_MAX_DOWNSPREAD] &
DP_NO_AUX_HANDSHAKE_LINK_TRAINING;
} else {
/* if this fails, presume the device is a ghost */
DRM_INFO("failed to retrieve link info, disabling eDP\n");
intel_dp_encoder_destroy(&intel_dp->base.base);
intel_dp_destroy(&intel_connector->base);
return;
}
}
 
intel_encoder->hot_plug = intel_dp_hot_plug;
 
if (is_edp(intel_dp)) {
/* initialize panel mode from VBT if available for eDP */
if (dev_priv->lfp_lvds_vbt_mode) {
dev_priv->panel_fixed_mode =
drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
if (dev_priv->panel_fixed_mode) {
dev_priv->panel_fixed_mode->type |=
DRM_MODE_TYPE_PREFERRED;
}
}
dev_priv->int_edp_connector = connector;
intel_panel_setup_backlight(dev);
}
 
intel_dp_add_properties(intel_dp, connector);
 
/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
* 0xd. Failure to do so will result in spurious interrupts being
* generated on the port when a cable is not attached.
*/
if (IS_G4X(dev) && !IS_GM45(dev)) {
u32 temp = I915_READ(PEG_BAND_GAP_DATA);
I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
}
}
/drivers/video/drm/i915/intel_drv.h
30,6 → 30,7
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
#include "drm_fb_helper.h"
#include <syscall.h>
 
#define _wait_for(COND, MS, W) ({ \
unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \
39,7 → 40,7
ret__ = -ETIMEDOUT; \
break; \
} \
if (W && !(/*in_atomic()||*/ in_dbg_master())) msleep(W); \
if (W) msleep(W); \
} \
ret__; \
})
/drivers/video/drm/i915/intel_dvo.c
0,0 → 1,448
/*
* Copyright 2006 Dave Airlie <airlied@linux.ie>
* 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>
*/
#include <linux/i2c.h>
#include <linux/slab.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
 
static const 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 = GMBUS_PORT_DPB,
.dev_ops = &ch7017_ops,
}
};
 
struct intel_dvo {
struct intel_encoder base;
 
struct intel_dvo_device dev;
 
struct drm_display_mode *panel_fixed_mode;
bool panel_wants_dither;
};
 
static struct intel_dvo *enc_to_intel_dvo(struct drm_encoder *encoder)
{
return container_of(encoder, struct intel_dvo, base.base);
}
 
static struct intel_dvo *intel_attached_dvo(struct drm_connector *connector)
{
return container_of(intel_attached_encoder(connector),
struct intel_dvo, base);
}
 
static void intel_dvo_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_i915_private *dev_priv = encoder->dev->dev_private;
struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder);
u32 dvo_reg = intel_dvo->dev.dvo_reg;
u32 temp = I915_READ(dvo_reg);
 
if (mode == DRM_MODE_DPMS_ON) {
I915_WRITE(dvo_reg, temp | DVO_ENABLE);
I915_READ(dvo_reg);
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, mode);
} else {
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, mode);
I915_WRITE(dvo_reg, temp & ~DVO_ENABLE);
I915_READ(dvo_reg);
}
}
 
static int intel_dvo_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
 
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
 
/* XXX: Validate clock range */
 
if (intel_dvo->panel_fixed_mode) {
if (mode->hdisplay > intel_dvo->panel_fixed_mode->hdisplay)
return MODE_PANEL;
if (mode->vdisplay > intel_dvo->panel_fixed_mode->vdisplay)
return MODE_PANEL;
}
 
return intel_dvo->dev.dev_ops->mode_valid(&intel_dvo->dev, mode);
}
 
static bool intel_dvo_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder);
 
/* 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 (intel_dvo->panel_fixed_mode != NULL) {
#define C(x) adjusted_mode->x = intel_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 (intel_dvo->dev.dev_ops->mode_fixup)
return intel_dvo->dev.dev_ops->mode_fixup(&intel_dvo->dev, mode, adjusted_mode);
 
return true;
}
 
static void intel_dvo_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder);
int pipe = intel_crtc->pipe;
u32 dvo_val;
u32 dvo_reg = intel_dvo->dev.dvo_reg, dvo_srcdim_reg;
int dpll_reg = DPLL(pipe);
 
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;
}
 
intel_dvo->dev.dev_ops->mode_set(&intel_dvo->dev, 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 & DRM_MODE_FLAG_PHSYNC)
dvo_val |= DVO_HSYNC_ACTIVE_HIGH;
if (adjusted_mode->flags & DRM_MODE_FLAG_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_connector_status
intel_dvo_detect(struct drm_connector *connector, bool force)
{
struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
return intel_dvo->dev.dev_ops->detect(&intel_dvo->dev);
}
 
static int intel_dvo_get_modes(struct drm_connector *connector)
{
struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
struct drm_i915_private *dev_priv = connector->dev->dev_private;
 
/* 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(connector,
&dev_priv->gmbus[GMBUS_PORT_DPC].adapter);
if (!list_empty(&connector->probed_modes))
return 1;
 
if (intel_dvo->panel_fixed_mode != NULL) {
struct drm_display_mode *mode;
mode = drm_mode_duplicate(connector->dev, intel_dvo->panel_fixed_mode);
if (mode) {
drm_mode_probed_add(connector, mode);
return 1;
}
}
 
return 0;
}
 
static void intel_dvo_destroy(struct drm_connector *connector)
{
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
 
static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = {
.dpms = intel_dvo_dpms,
.mode_fixup = intel_dvo_mode_fixup,
.prepare = intel_encoder_prepare,
.mode_set = intel_dvo_mode_set,
.commit = intel_encoder_commit,
};
 
static const struct drm_connector_funcs intel_dvo_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.detect = intel_dvo_detect,
.destroy = intel_dvo_destroy,
.fill_modes = drm_helper_probe_single_connector_modes,
};
 
static const struct drm_connector_helper_funcs intel_dvo_connector_helper_funcs = {
.mode_valid = intel_dvo_mode_valid,
.get_modes = intel_dvo_get_modes,
.best_encoder = intel_best_encoder,
};
 
static void intel_dvo_enc_destroy(struct drm_encoder *encoder)
{
struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder);
 
if (intel_dvo->dev.dev_ops->destroy)
intel_dvo->dev.dev_ops->destroy(&intel_dvo->dev);
 
kfree(intel_dvo->panel_fixed_mode);
 
intel_encoder_destroy(encoder);
}
 
static const struct drm_encoder_funcs intel_dvo_enc_funcs = {
.destroy = intel_dvo_enc_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_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
uint32_t dvo_val = I915_READ(intel_dvo->dev.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_for_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 |= DRM_MODE_FLAG_PHSYNC;
if (dvo_val & DVO_VSYNC_ACTIVE_HIGH)
mode->flags |= DRM_MODE_FLAG_PVSYNC;
}
}
}
 
return mode;
}
 
void intel_dvo_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_encoder *intel_encoder;
struct intel_dvo *intel_dvo;
struct intel_connector *intel_connector;
int i;
int encoder_type = DRM_MODE_ENCODER_NONE;
 
intel_dvo = kzalloc(sizeof(struct intel_dvo), GFP_KERNEL);
if (!intel_dvo)
return;
 
intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
if (!intel_connector) {
kfree(intel_dvo);
return;
}
 
intel_encoder = &intel_dvo->base;
drm_encoder_init(dev, &intel_encoder->base,
&intel_dvo_enc_funcs, encoder_type);
 
/* Now, try to find a controller */
for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) {
struct drm_connector *connector = &intel_connector->base;
const struct intel_dvo_device *dvo = &intel_dvo_devices[i];
struct i2c_adapter *i2c;
int gpio;
 
/* 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 = GMBUS_PORT_SSC;
else
gpio = GMBUS_PORT_DPB;
 
/* 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).
*/
i2c = &dev_priv->gmbus[gpio].adapter;
 
intel_dvo->dev = *dvo;
if (!dvo->dev_ops->init(&intel_dvo->dev, i2c))
continue;
 
intel_encoder->type = INTEL_OUTPUT_DVO;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
switch (dvo->type) {
case INTEL_DVO_CHIP_TMDS:
intel_encoder->clone_mask =
(1 << INTEL_DVO_TMDS_CLONE_BIT) |
(1 << INTEL_ANALOG_CLONE_BIT);
drm_connector_init(dev, connector,
&intel_dvo_connector_funcs,
DRM_MODE_CONNECTOR_DVII);
encoder_type = DRM_MODE_ENCODER_TMDS;
break;
case INTEL_DVO_CHIP_LVDS:
intel_encoder->clone_mask =
(1 << INTEL_DVO_LVDS_CLONE_BIT);
drm_connector_init(dev, connector,
&intel_dvo_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
encoder_type = DRM_MODE_ENCODER_LVDS;
break;
}
 
drm_connector_helper_add(connector,
&intel_dvo_connector_helper_funcs);
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
 
drm_encoder_helper_add(&intel_encoder->base,
&intel_dvo_helper_funcs);
 
intel_connector_attach_encoder(intel_connector, intel_encoder);
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.
*/
intel_dvo->panel_fixed_mode =
intel_dvo_get_current_mode(connector);
intel_dvo->panel_wants_dither = true;
}
 
drm_sysfs_connector_add(connector);
return;
}
 
drm_encoder_cleanup(&intel_encoder->base);
kfree(intel_dvo);
kfree(intel_connector);
}
/drivers/video/drm/i915/intel_hdmi.c
0,0 → 1,561
/*
* Copyright 2006 Dave Airlie <airlied@linux.ie>
* Copyright © 2006-2009 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>
* Jesse Barnes <jesse.barnes@intel.com>
*/
 
#include <linux/i2c.h>
#include <linux/slab.h>
//#include <linux/delay.h>
#include "drmP.h"
#include "drm.h"
#include "drm_crtc.h"
#include "drm_edid.h"
#include "intel_drv.h"
#include "i915_drm.h"
#include "i915_drv.h"
 
struct intel_hdmi {
struct intel_encoder base;
u32 sdvox_reg;
int ddc_bus;
uint32_t color_range;
bool has_hdmi_sink;
bool has_audio;
int force_audio;
void (*write_infoframe)(struct drm_encoder *encoder,
struct dip_infoframe *frame);
};
 
static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder)
{
return container_of(encoder, struct intel_hdmi, base.base);
}
 
static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector)
{
return container_of(intel_attached_encoder(connector),
struct intel_hdmi, base);
}
 
void intel_dip_infoframe_csum(struct dip_infoframe *frame)
{
uint8_t *data = (uint8_t *)frame;
uint8_t sum = 0;
unsigned i;
 
frame->checksum = 0;
frame->ecc = 0;
 
/* Header isn't part of the checksum */
for (i = 5; i < frame->len; i++)
sum += data[i];
 
frame->checksum = 0x100 - sum;
}
 
static u32 intel_infoframe_index(struct dip_infoframe *frame)
{
u32 flags = 0;
 
switch (frame->type) {
case DIP_TYPE_AVI:
flags |= VIDEO_DIP_SELECT_AVI;
break;
case DIP_TYPE_SPD:
flags |= VIDEO_DIP_SELECT_SPD;
break;
default:
DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
break;
}
 
return flags;
}
 
static u32 intel_infoframe_flags(struct dip_infoframe *frame)
{
u32 flags = 0;
 
switch (frame->type) {
case DIP_TYPE_AVI:
flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC;
break;
case DIP_TYPE_SPD:
flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_2VSYNC;
break;
default:
DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
break;
}
 
return flags;
}
 
static void i9xx_write_infoframe(struct drm_encoder *encoder,
struct dip_infoframe *frame)
{
uint32_t *data = (uint32_t *)frame;
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
u32 port, flags, val = I915_READ(VIDEO_DIP_CTL);
unsigned i, len = DIP_HEADER_SIZE + frame->len;
 
 
/* XXX first guess at handling video port, is this corrent? */
if (intel_hdmi->sdvox_reg == SDVOB)
port = VIDEO_DIP_PORT_B;
else if (intel_hdmi->sdvox_reg == SDVOC)
port = VIDEO_DIP_PORT_C;
else
return;
 
flags = intel_infoframe_index(frame);
 
val &= ~VIDEO_DIP_SELECT_MASK;
 
I915_WRITE(VIDEO_DIP_CTL, val | port | flags);
 
for (i = 0; i < len; i += 4) {
I915_WRITE(VIDEO_DIP_DATA, *data);
data++;
}
 
flags |= intel_infoframe_flags(frame);
 
I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags);
}
 
static void ironlake_write_infoframe(struct drm_encoder *encoder,
struct dip_infoframe *frame)
{
uint32_t *data = (uint32_t *)frame;
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc = encoder->crtc;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
unsigned i, len = DIP_HEADER_SIZE + frame->len;
u32 flags, val = I915_READ(reg);
 
intel_wait_for_vblank(dev, intel_crtc->pipe);
 
flags = intel_infoframe_index(frame);
 
val &= ~VIDEO_DIP_SELECT_MASK;
 
I915_WRITE(reg, val | flags);
 
for (i = 0; i < len; i += 4) {
I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
data++;
}
 
flags |= intel_infoframe_flags(frame);
 
I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags);
}
static void intel_set_infoframe(struct drm_encoder *encoder,
struct dip_infoframe *frame)
{
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
 
if (!intel_hdmi->has_hdmi_sink)
return;
 
intel_dip_infoframe_csum(frame);
intel_hdmi->write_infoframe(encoder, frame);
}
 
static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder)
{
struct dip_infoframe avi_if = {
.type = DIP_TYPE_AVI,
.ver = DIP_VERSION_AVI,
.len = DIP_LEN_AVI,
};
 
intel_set_infoframe(encoder, &avi_if);
}
 
static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
{
struct dip_infoframe spd_if;
 
memset(&spd_if, 0, sizeof(spd_if));
spd_if.type = DIP_TYPE_SPD;
spd_if.ver = DIP_VERSION_SPD;
spd_if.len = DIP_LEN_SPD;
strcpy(spd_if.body.spd.vn, "Intel");
strcpy(spd_if.body.spd.pd, "Integrated gfx");
spd_if.body.spd.sdi = DIP_SPD_PC;
 
intel_set_infoframe(encoder, &spd_if);
}
 
static void intel_hdmi_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc = encoder->crtc;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
u32 sdvox;
 
sdvox = SDVO_ENCODING_HDMI | SDVO_BORDER_ENABLE;
if (!HAS_PCH_SPLIT(dev))
sdvox |= intel_hdmi->color_range;
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
sdvox |= SDVO_VSYNC_ACTIVE_HIGH;
if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
sdvox |= SDVO_HSYNC_ACTIVE_HIGH;
 
if (intel_crtc->bpp > 24)
sdvox |= COLOR_FORMAT_12bpc;
else
sdvox |= COLOR_FORMAT_8bpc;
 
/* Required on CPT */
if (intel_hdmi->has_hdmi_sink && HAS_PCH_CPT(dev))
sdvox |= HDMI_MODE_SELECT;
 
if (intel_hdmi->has_audio) {
sdvox |= SDVO_AUDIO_ENABLE;
sdvox |= SDVO_NULL_PACKETS_DURING_VSYNC;
}
 
if (intel_crtc->pipe == 1) {
if (HAS_PCH_CPT(dev))
sdvox |= PORT_TRANS_B_SEL_CPT;
else
sdvox |= SDVO_PIPE_B_SELECT;
}
 
I915_WRITE(intel_hdmi->sdvox_reg, sdvox);
POSTING_READ(intel_hdmi->sdvox_reg);
 
intel_hdmi_set_avi_infoframe(encoder);
intel_hdmi_set_spd_infoframe(encoder);
}
 
static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
u32 temp;
 
temp = I915_READ(intel_hdmi->sdvox_reg);
 
/* HW workaround, need to toggle enable bit off and on for 12bpc, but
* we do this anyway which shows more stable in testing.
*/
if (HAS_PCH_SPLIT(dev)) {
I915_WRITE(intel_hdmi->sdvox_reg, temp & ~SDVO_ENABLE);
POSTING_READ(intel_hdmi->sdvox_reg);
}
 
if (mode != DRM_MODE_DPMS_ON) {
temp &= ~SDVO_ENABLE;
} else {
temp |= SDVO_ENABLE;
}
 
I915_WRITE(intel_hdmi->sdvox_reg, temp);
POSTING_READ(intel_hdmi->sdvox_reg);
 
/* HW workaround, need to write this twice for issue that may result
* in first write getting masked.
*/
if (HAS_PCH_SPLIT(dev)) {
I915_WRITE(intel_hdmi->sdvox_reg, temp);
POSTING_READ(intel_hdmi->sdvox_reg);
}
}
 
static int intel_hdmi_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
if (mode->clock > 165000)
return MODE_CLOCK_HIGH;
if (mode->clock < 20000)
return MODE_CLOCK_LOW;
 
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
 
return MODE_OK;
}
 
static bool intel_hdmi_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
return true;
}
 
static enum drm_connector_status
intel_hdmi_detect(struct drm_connector *connector, bool force)
{
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
struct drm_i915_private *dev_priv = connector->dev->dev_private;
struct edid *edid;
enum drm_connector_status status = connector_status_disconnected;
 
intel_hdmi->has_hdmi_sink = false;
intel_hdmi->has_audio = false;
edid = drm_get_edid(connector,
&dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);
 
if (edid) {
if (edid->input & DRM_EDID_INPUT_DIGITAL) {
status = connector_status_connected;
intel_hdmi->has_hdmi_sink = drm_detect_hdmi_monitor(edid);
intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
}
connector->display_info.raw_edid = NULL;
kfree(edid);
}
 
if (status == connector_status_connected) {
if (intel_hdmi->force_audio)
intel_hdmi->has_audio = intel_hdmi->force_audio > 0;
}
 
return status;
}
 
static int intel_hdmi_get_modes(struct drm_connector *connector)
{
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
struct drm_i915_private *dev_priv = connector->dev->dev_private;
 
/* We should parse the EDID data and find out if it's an HDMI sink so
* we can send audio to it.
*/
 
return intel_ddc_get_modes(connector,
&dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);
}
 
static bool
intel_hdmi_detect_audio(struct drm_connector *connector)
{
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
struct drm_i915_private *dev_priv = connector->dev->dev_private;
struct edid *edid;
bool has_audio = false;
 
edid = drm_get_edid(connector,
&dev_priv->gmbus[intel_hdmi->ddc_bus].adapter);
if (edid) {
if (edid->input & DRM_EDID_INPUT_DIGITAL)
has_audio = drm_detect_monitor_audio(edid);
 
connector->display_info.raw_edid = NULL;
kfree(edid);
}
 
return has_audio;
}
 
static int
intel_hdmi_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t val)
{
struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector);
struct drm_i915_private *dev_priv = connector->dev->dev_private;
int ret;
 
ret = drm_connector_property_set_value(connector, property, val);
if (ret)
return ret;
#if 0
if (property == dev_priv->force_audio_property) {
int i = val;
bool has_audio;
 
if (i == intel_hdmi->force_audio)
return 0;
 
intel_hdmi->force_audio = i;
 
if (i == 0)
has_audio = intel_hdmi_detect_audio(connector);
else
has_audio = i > 0;
 
if (has_audio == intel_hdmi->has_audio)
return 0;
 
intel_hdmi->has_audio = has_audio;
goto done;
}
 
if (property == dev_priv->broadcast_rgb_property) {
if (val == !!intel_hdmi->color_range)
return 0;
 
intel_hdmi->color_range = val ? SDVO_COLOR_RANGE_16_235 : 0;
goto done;
}
#endif
return -EINVAL;
 
done:
if (intel_hdmi->base.base.crtc) {
struct drm_crtc *crtc = intel_hdmi->base.base.crtc;
drm_crtc_helper_set_mode(crtc, &crtc->mode,
crtc->x, crtc->y,
crtc->fb);
}
 
return 0;
}
 
static void intel_hdmi_destroy(struct drm_connector *connector)
{
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
 
static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = {
.dpms = intel_hdmi_dpms,
.mode_fixup = intel_hdmi_mode_fixup,
.prepare = intel_encoder_prepare,
.mode_set = intel_hdmi_mode_set,
.commit = intel_encoder_commit,
};
 
static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.detect = intel_hdmi_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_hdmi_set_property,
.destroy = intel_hdmi_destroy,
};
 
static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = {
.get_modes = intel_hdmi_get_modes,
.mode_valid = intel_hdmi_mode_valid,
.best_encoder = intel_best_encoder,
};
 
static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
.destroy = intel_encoder_destroy,
};
 
static void
intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *connector)
{
intel_attach_force_audio_property(connector);
intel_attach_broadcast_rgb_property(connector);
}
 
void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_connector *connector;
struct intel_encoder *intel_encoder;
struct intel_connector *intel_connector;
struct intel_hdmi *intel_hdmi;
 
intel_hdmi = kzalloc(sizeof(struct intel_hdmi), GFP_KERNEL);
if (!intel_hdmi)
return;
 
intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
if (!intel_connector) {
kfree(intel_hdmi);
return;
}
 
intel_encoder = &intel_hdmi->base;
drm_encoder_init(dev, &intel_encoder->base, &intel_hdmi_enc_funcs,
DRM_MODE_ENCODER_TMDS);
 
connector = &intel_connector->base;
drm_connector_init(dev, connector, &intel_hdmi_connector_funcs,
DRM_MODE_CONNECTOR_HDMIA);
drm_connector_helper_add(connector, &intel_hdmi_connector_helper_funcs);
 
intel_encoder->type = INTEL_OUTPUT_HDMI;
 
connector->polled = DRM_CONNECTOR_POLL_HPD;
connector->interlace_allowed = 0;
connector->doublescan_allowed = 0;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
 
/* Set up the DDC bus. */
if (sdvox_reg == SDVOB) {
intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT);
intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
} else if (sdvox_reg == SDVOC) {
intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT);
intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
} else if (sdvox_reg == HDMIB) {
intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT);
intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
} else if (sdvox_reg == HDMIC) {
intel_encoder->clone_mask = (1 << INTEL_HDMIE_CLONE_BIT);
intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
} else if (sdvox_reg == HDMID) {
intel_encoder->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT);
intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS;
}
 
intel_hdmi->sdvox_reg = sdvox_reg;
 
if (!HAS_PCH_SPLIT(dev))
intel_hdmi->write_infoframe = i9xx_write_infoframe;
else
intel_hdmi->write_infoframe = ironlake_write_infoframe;
 
drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);
 
intel_hdmi_add_properties(intel_hdmi, connector);
 
intel_connector_attach_encoder(intel_connector, intel_encoder);
drm_sysfs_connector_add(connector);
 
/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
* 0xd. Failure to do so will result in spurious interrupts being
* generated on the port when a cable is not attached.
*/
if (IS_G4X(dev) && !IS_GM45(dev)) {
u32 temp = I915_READ(PEG_BAND_GAP_DATA);
I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd);
}
}
/drivers/video/drm/i915/intel_i2c.c
1,4 → 1,4
/*
/*
* Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
* Copyright © 2006-2008,2010 Intel Corporation
* Jesse Barnes <jesse.barnes@intel.com>
31,9 → 31,8
#include "drmP.h"
#include "drm.h"
#include "intel_drv.h"
//#include "i915_drm.h"
#include "i915_drm.h"
#include "i915_drv.h"
#include <syscall.h>
 
#define MSEC_PER_SEC 1000L
#define USEC_PER_MSEC 1000L
/drivers/video/drm/i915/intel_lvds.c
0,0 → 1,1051
/*
* Copyright © 2006-2007 Intel Corporation
* Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
*
* 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>
* Dave Airlie <airlied@linux.ie>
* Jesse Barnes <jesse.barnes@intel.com>
*/
 
//#include <acpi/button.h>
//#include <linux/dmi.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include "drmP.h"
#include "drm.h"
#include "drm_crtc.h"
#include "drm_edid.h"
#include "intel_drv.h"
#include "i915_drm.h"
#include "i915_drv.h"
//#include <linux/acpi.h>
 
/* Private structure for the integrated LVDS support */
struct intel_lvds {
struct intel_encoder base;
 
struct edid *edid;
 
int fitting_mode;
u32 pfit_control;
u32 pfit_pgm_ratios;
bool pfit_dirty;
 
struct drm_display_mode *fixed_mode;
};
 
static struct intel_lvds *to_intel_lvds(struct drm_encoder *encoder)
{
return container_of(encoder, struct intel_lvds, base.base);
}
 
static struct intel_lvds *intel_attached_lvds(struct drm_connector *connector)
{
return container_of(intel_attached_encoder(connector),
struct intel_lvds, base);
}
 
/**
* Sets the power state for the panel.
*/
static void intel_lvds_enable(struct intel_lvds *intel_lvds)
{
struct drm_device *dev = intel_lvds->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 ctl_reg, lvds_reg, stat_reg;
 
if (HAS_PCH_SPLIT(dev)) {
ctl_reg = PCH_PP_CONTROL;
lvds_reg = PCH_LVDS;
stat_reg = PCH_PP_STATUS;
} else {
ctl_reg = PP_CONTROL;
lvds_reg = LVDS;
stat_reg = PP_STATUS;
}
 
I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN);
 
if (intel_lvds->pfit_dirty) {
/*
* Enable automatic panel scaling so that non-native modes
* fill the screen. The panel fitter should only be
* adjusted whilst the pipe is disabled, according to
* register description and PRM.
*/
DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n",
intel_lvds->pfit_control,
intel_lvds->pfit_pgm_ratios);
 
I915_WRITE(PFIT_PGM_RATIOS, intel_lvds->pfit_pgm_ratios);
I915_WRITE(PFIT_CONTROL, intel_lvds->pfit_control);
intel_lvds->pfit_dirty = false;
}
 
I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON);
POSTING_READ(lvds_reg);
if (wait_for((I915_READ(stat_reg) & PP_ON) != 0, 1000))
DRM_ERROR("timed out waiting for panel to power on\n");
 
intel_panel_enable_backlight(dev);
}
 
static void intel_lvds_disable(struct intel_lvds *intel_lvds)
{
struct drm_device *dev = intel_lvds->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 ctl_reg, lvds_reg, stat_reg;
 
if (HAS_PCH_SPLIT(dev)) {
ctl_reg = PCH_PP_CONTROL;
lvds_reg = PCH_LVDS;
stat_reg = PCH_PP_STATUS;
} else {
ctl_reg = PP_CONTROL;
lvds_reg = LVDS;
stat_reg = PP_STATUS;
}
 
intel_panel_disable_backlight(dev);
 
I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON);
if (wait_for((I915_READ(stat_reg) & PP_ON) == 0, 1000))
DRM_ERROR("timed out waiting for panel to power off\n");
 
if (intel_lvds->pfit_control) {
I915_WRITE(PFIT_CONTROL, 0);
intel_lvds->pfit_dirty = true;
}
 
I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN);
POSTING_READ(lvds_reg);
}
 
static void intel_lvds_dpms(struct drm_encoder *encoder, int mode)
{
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
 
if (mode == DRM_MODE_DPMS_ON)
intel_lvds_enable(intel_lvds);
else
intel_lvds_disable(intel_lvds);
 
/* XXX: We never power down the LVDS pairs. */
}
 
static int intel_lvds_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct intel_lvds *intel_lvds = intel_attached_lvds(connector);
struct drm_display_mode *fixed_mode = intel_lvds->fixed_mode;
 
if (mode->hdisplay > fixed_mode->hdisplay)
return MODE_PANEL;
if (mode->vdisplay > fixed_mode->vdisplay)
return MODE_PANEL;
 
return MODE_OK;
}
 
static void
centre_horizontally(struct drm_display_mode *mode,
int width)
{
u32 border, sync_pos, blank_width, sync_width;
 
/* keep the hsync and hblank widths constant */
sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start;
blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start;
sync_pos = (blank_width - sync_width + 1) / 2;
 
border = (mode->hdisplay - width + 1) / 2;
border += border & 1; /* make the border even */
 
mode->crtc_hdisplay = width;
mode->crtc_hblank_start = width + border;
mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width;
 
mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos;
mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width;
}
 
static void
centre_vertically(struct drm_display_mode *mode,
int height)
{
u32 border, sync_pos, blank_width, sync_width;
 
/* keep the vsync and vblank widths constant */
sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start;
blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start;
sync_pos = (blank_width - sync_width + 1) / 2;
 
border = (mode->vdisplay - height + 1) / 2;
 
mode->crtc_vdisplay = height;
mode->crtc_vblank_start = height + border;
mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width;
 
mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos;
mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width;
}
 
static inline u32 panel_fitter_scaling(u32 source, u32 target)
{
/*
* Floating point operation is not supported. So the FACTOR
* is defined, which can avoid the floating point computation
* when calculating the panel ratio.
*/
#define ACCURACY 12
#define FACTOR (1 << ACCURACY)
u32 ratio = source * FACTOR / target;
return (FACTOR * ratio + FACTOR/2) / FACTOR;
}
 
static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
struct drm_encoder *tmp_encoder;
u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
int pipe;
 
/* Should never happen!! */
if (INTEL_INFO(dev)->gen < 4 && intel_crtc->pipe == 0) {
DRM_ERROR("Can't support LVDS on pipe A\n");
return false;
}
 
/* Should never happen!! */
list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, head) {
if (tmp_encoder != encoder && tmp_encoder->crtc == encoder->crtc) {
DRM_ERROR("Can't enable LVDS and another "
"encoder on the same pipe\n");
return false;
}
}
 
/*
* 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.
*/
intel_fixed_panel_mode(intel_lvds->fixed_mode, adjusted_mode);
 
if (HAS_PCH_SPLIT(dev)) {
intel_pch_panel_fitting(dev, intel_lvds->fitting_mode,
mode, adjusted_mode);
return true;
}
 
/* Native modes don't need fitting */
if (adjusted_mode->hdisplay == mode->hdisplay &&
adjusted_mode->vdisplay == mode->vdisplay)
goto out;
 
/* 965+ wants fuzzy fitting */
if (INTEL_INFO(dev)->gen >= 4)
pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) |
PFIT_FILTER_FUZZY);
 
/*
* Enable automatic panel scaling for non-native modes so that they fill
* the screen. Should be enabled before the pipe is enabled, according
* to register description and PRM.
* Change the value here to see the borders for debugging
*/
for_each_pipe(pipe)
I915_WRITE(BCLRPAT(pipe), 0);
 
switch (intel_lvds->fitting_mode) {
case DRM_MODE_SCALE_CENTER:
/*
* For centered modes, we have to calculate border widths &
* heights and modify the values programmed into the CRTC.
*/
centre_horizontally(adjusted_mode, mode->hdisplay);
centre_vertically(adjusted_mode, mode->vdisplay);
border = LVDS_BORDER_ENABLE;
break;
 
case DRM_MODE_SCALE_ASPECT:
/* Scale but preserve the aspect ratio */
if (INTEL_INFO(dev)->gen >= 4) {
u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
 
/* 965+ is easy, it does everything in hw */
if (scaled_width > scaled_height)
pfit_control |= PFIT_ENABLE | PFIT_SCALING_PILLAR;
else if (scaled_width < scaled_height)
pfit_control |= PFIT_ENABLE | PFIT_SCALING_LETTER;
else if (adjusted_mode->hdisplay != mode->hdisplay)
pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO;
} else {
u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
/*
* For earlier chips we have to calculate the scaling
* ratio by hand and program it into the
* PFIT_PGM_RATIO register
*/
if (scaled_width > scaled_height) { /* pillar */
centre_horizontally(adjusted_mode, scaled_height / mode->vdisplay);
 
border = LVDS_BORDER_ENABLE;
if (mode->vdisplay != adjusted_mode->vdisplay) {
u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay);
pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
bits << PFIT_VERT_SCALE_SHIFT);
pfit_control |= (PFIT_ENABLE |
VERT_INTERP_BILINEAR |
HORIZ_INTERP_BILINEAR);
}
} else if (scaled_width < scaled_height) { /* letter */
centre_vertically(adjusted_mode, scaled_width / mode->hdisplay);
 
border = LVDS_BORDER_ENABLE;
if (mode->hdisplay != adjusted_mode->hdisplay) {
u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay);
pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT |
bits << PFIT_VERT_SCALE_SHIFT);
pfit_control |= (PFIT_ENABLE |
VERT_INTERP_BILINEAR |
HORIZ_INTERP_BILINEAR);
}
} else
/* Aspects match, Let hw scale both directions */
pfit_control |= (PFIT_ENABLE |
VERT_AUTO_SCALE | HORIZ_AUTO_SCALE |
VERT_INTERP_BILINEAR |
HORIZ_INTERP_BILINEAR);
}
break;
 
case DRM_MODE_SCALE_FULLSCREEN:
/*
* Full scaling, even if it changes the aspect ratio.
* Fortunately this is all done for us in hw.
*/
if (mode->vdisplay != adjusted_mode->vdisplay ||
mode->hdisplay != adjusted_mode->hdisplay) {
pfit_control |= PFIT_ENABLE;
if (INTEL_INFO(dev)->gen >= 4)
pfit_control |= PFIT_SCALING_AUTO;
else
pfit_control |= (VERT_AUTO_SCALE |
VERT_INTERP_BILINEAR |
HORIZ_AUTO_SCALE |
HORIZ_INTERP_BILINEAR);
}
break;
 
default:
break;
}
 
out:
/* If not enabling scaling, be consistent and always use 0. */
if ((pfit_control & PFIT_ENABLE) == 0) {
pfit_control = 0;
pfit_pgm_ratios = 0;
}
 
/* Make sure pre-965 set dither correctly */
if (INTEL_INFO(dev)->gen < 4 && dev_priv->lvds_dither)
pfit_control |= PANEL_8TO6_DITHER_ENABLE;
 
if (pfit_control != intel_lvds->pfit_control ||
pfit_pgm_ratios != intel_lvds->pfit_pgm_ratios) {
intel_lvds->pfit_control = pfit_control;
intel_lvds->pfit_pgm_ratios = pfit_pgm_ratios;
intel_lvds->pfit_dirty = true;
}
dev_priv->lvds_border_bits = border;
 
/*
* XXX: It would be nice to support lower refresh rates on the
* panels to reduce power consumption, and perhaps match the
* user's requested refresh rate.
*/
 
return true;
}
 
static void intel_lvds_prepare(struct drm_encoder *encoder)
{
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
 
/*
* Prior to Ironlake, we must disable the pipe if we want to adjust
* the panel fitter. However at all other times we can just reset
* the registers regardless.
*/
if (!HAS_PCH_SPLIT(encoder->dev) && intel_lvds->pfit_dirty)
intel_lvds_disable(intel_lvds);
}
 
static void intel_lvds_commit(struct drm_encoder *encoder)
{
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
 
/* Always do a full power on as we do not know what state
* we were left in.
*/
intel_lvds_enable(intel_lvds);
}
 
static void intel_lvds_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
/*
* The LVDS pin pair will already have been turned on in the
* intel_crtc_mode_set since it has a large impact on the DPLL
* settings.
*/
}
 
/**
* Detect the LVDS connection.
*
* Since LVDS doesn't have hotlug, we use the lid as a proxy. Open means
* connected and closed means disconnected. We also send hotplug events as
* needed, using lid status notification from the input layer.
*/
static enum drm_connector_status
intel_lvds_detect(struct drm_connector *connector, bool force)
{
struct drm_device *dev = connector->dev;
enum drm_connector_status status;
 
status = intel_panel_detect(dev);
if (status != connector_status_unknown)
return status;
 
return connector_status_connected;
}
 
/**
* Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
*/
static int intel_lvds_get_modes(struct drm_connector *connector)
{
struct intel_lvds *intel_lvds = intel_attached_lvds(connector);
struct drm_device *dev = connector->dev;
struct drm_display_mode *mode;
 
if (intel_lvds->edid)
return drm_add_edid_modes(connector, intel_lvds->edid);
 
mode = drm_mode_duplicate(dev, intel_lvds->fixed_mode);
if (mode == NULL)
return 0;
 
drm_mode_probed_add(connector, mode);
return 1;
}
 
static int intel_no_modeset_on_lid_dmi_callback(const struct dmi_system_id *id)
{
DRM_DEBUG_KMS("Skipping forced modeset for %s\n", id->ident);
return 1;
}
 
/* The GPU hangs up on these systems if modeset is performed on LID open */
static const struct dmi_system_id intel_no_modeset_on_lid[] = {
{
.callback = intel_no_modeset_on_lid_dmi_callback,
.ident = "Toshiba Tecra A11",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
DMI_MATCH(DMI_PRODUCT_NAME, "TECRA A11"),
},
},
 
{ } /* terminating entry */
};
 
#if 0
/*
* Lid events. Note the use of 'modeset_on_lid':
* - we set it on lid close, and reset it on open
* - we use it as a "only once" bit (ie we ignore
* duplicate events where it was already properly
* set/reset)
* - the suspend/resume paths will also set it to
* zero, since they restore the mode ("lid open").
*/
static int intel_lid_notify(struct notifier_block *nb, unsigned long val,
void *unused)
{
struct drm_i915_private *dev_priv =
container_of(nb, struct drm_i915_private, lid_notifier);
struct drm_device *dev = dev_priv->dev;
struct drm_connector *connector = dev_priv->int_lvds_connector;
 
if (dev->switch_power_state != DRM_SWITCH_POWER_ON)
return NOTIFY_OK;
 
/*
* check and update the status of LVDS connector after receiving
* the LID nofication event.
*/
if (connector)
connector->status = connector->funcs->detect(connector,
false);
 
/* Don't force modeset on machines where it causes a GPU lockup */
if (dmi_check_system(intel_no_modeset_on_lid))
return NOTIFY_OK;
if (!acpi_lid_open()) {
dev_priv->modeset_on_lid = 1;
return NOTIFY_OK;
}
 
if (!dev_priv->modeset_on_lid)
return NOTIFY_OK;
 
dev_priv->modeset_on_lid = 0;
 
mutex_lock(&dev->mode_config.mutex);
drm_helper_resume_force_mode(dev);
mutex_unlock(&dev->mode_config.mutex);
 
return NOTIFY_OK;
}
#endif
 
/**
* intel_lvds_destroy - unregister and free LVDS structures
* @connector: connector to free
*
* Unregister the DDC bus for this connector then free the driver private
* structure.
*/
static void intel_lvds_destroy(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
 
intel_panel_destroy_backlight(dev);
 
// if (dev_priv->lid_notifier.notifier_call)
// acpi_lid_notifier_unregister(&dev_priv->lid_notifier);
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
 
static int intel_lvds_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t value)
{
struct intel_lvds *intel_lvds = intel_attached_lvds(connector);
struct drm_device *dev = connector->dev;
 
if (property == dev->mode_config.scaling_mode_property) {
struct drm_crtc *crtc = intel_lvds->base.base.crtc;
 
if (value == DRM_MODE_SCALE_NONE) {
DRM_DEBUG_KMS("no scaling not supported\n");
return -EINVAL;
}
 
if (intel_lvds->fitting_mode == value) {
/* the LVDS scaling property is not changed */
return 0;
}
intel_lvds->fitting_mode = value;
if (crtc && crtc->enabled) {
/*
* If the CRTC is enabled, the display will be changed
* according to the new panel fitting mode.
*/
drm_crtc_helper_set_mode(crtc, &crtc->mode,
crtc->x, crtc->y, crtc->fb);
}
}
 
return 0;
}
 
static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = {
.dpms = intel_lvds_dpms,
.mode_fixup = intel_lvds_mode_fixup,
.prepare = intel_lvds_prepare,
.mode_set = intel_lvds_mode_set,
.commit = intel_lvds_commit,
};
 
static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
.get_modes = intel_lvds_get_modes,
.mode_valid = intel_lvds_mode_valid,
.best_encoder = intel_best_encoder,
};
 
static const struct drm_connector_funcs intel_lvds_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.detect = intel_lvds_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_lvds_set_property,
.destroy = intel_lvds_destroy,
};
 
static const struct drm_encoder_funcs intel_lvds_enc_funcs = {
.destroy = intel_encoder_destroy,
};
 
static int intel_no_lvds_dmi_callback(const struct dmi_system_id *id)
{
DRM_DEBUG_KMS("Skipping LVDS initialization for %s\n", id->ident);
return 1;
}
 
/* These systems claim to have LVDS, but really don't */
static const struct dmi_system_id intel_no_lvds[] = {
{
.callback = intel_no_lvds_dmi_callback,
.ident = "Apple Mac Mini (Core series)",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Apple"),
DMI_MATCH(DMI_PRODUCT_NAME, "Macmini1,1"),
},
},
{
.callback = intel_no_lvds_dmi_callback,
.ident = "Apple Mac Mini (Core 2 series)",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Apple"),
DMI_MATCH(DMI_PRODUCT_NAME, "Macmini2,1"),
},
},
{
.callback = intel_no_lvds_dmi_callback,
.ident = "MSI IM-945GSE-A",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "MSI"),
DMI_MATCH(DMI_PRODUCT_NAME, "A9830IMS"),
},
},
{
.callback = intel_no_lvds_dmi_callback,
.ident = "Dell Studio Hybrid",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Studio Hybrid 140g"),
},
},
{
.callback = intel_no_lvds_dmi_callback,
.ident = "Dell OptiPlex FX170",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex FX170"),
},
},
{
.callback = intel_no_lvds_dmi_callback,
.ident = "AOpen Mini PC",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "AOpen"),
DMI_MATCH(DMI_PRODUCT_NAME, "i965GMx-IF"),
},
},
{
.callback = intel_no_lvds_dmi_callback,
.ident = "AOpen Mini PC MP915",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
DMI_MATCH(DMI_BOARD_NAME, "i915GMx-F"),
},
},
{
.callback = intel_no_lvds_dmi_callback,
.ident = "AOpen i915GMm-HFS",
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AOpen"),
DMI_MATCH(DMI_BOARD_NAME, "i915GMm-HFS"),
},
},
{
.callback = intel_no_lvds_dmi_callback,
.ident = "Aopen i945GTt-VFA",
.matches = {
DMI_MATCH(DMI_PRODUCT_VERSION, "AO00001JW"),
},
},
{
.callback = intel_no_lvds_dmi_callback,
.ident = "Clientron U800",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Clientron"),
DMI_MATCH(DMI_PRODUCT_NAME, "U800"),
},
},
{
.callback = intel_no_lvds_dmi_callback,
.ident = "Asus EeeBox PC EB1007",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "EB1007"),
},
},
 
{ } /* terminating entry */
};
 
/**
* intel_find_lvds_downclock - find the reduced downclock for LVDS in EDID
* @dev: drm device
* @connector: LVDS connector
*
* Find the reduced downclock for LVDS in EDID.
*/
static void intel_find_lvds_downclock(struct drm_device *dev,
struct drm_display_mode *fixed_mode,
struct drm_connector *connector)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_display_mode *scan;
int temp_downclock;
 
temp_downclock = fixed_mode->clock;
list_for_each_entry(scan, &connector->probed_modes, head) {
/*
* If one mode has the same resolution with the fixed_panel
* mode while they have the different refresh rate, it means
* that the reduced downclock is found for the LVDS. In such
* case we can set the different FPx0/1 to dynamically select
* between low and high frequency.
*/
if (scan->hdisplay == fixed_mode->hdisplay &&
scan->hsync_start == fixed_mode->hsync_start &&
scan->hsync_end == fixed_mode->hsync_end &&
scan->htotal == fixed_mode->htotal &&
scan->vdisplay == fixed_mode->vdisplay &&
scan->vsync_start == fixed_mode->vsync_start &&
scan->vsync_end == fixed_mode->vsync_end &&
scan->vtotal == fixed_mode->vtotal) {
if (scan->clock < temp_downclock) {
/*
* The downclock is already found. But we
* expect to find the lower downclock.
*/
temp_downclock = scan->clock;
}
}
}
if (temp_downclock < fixed_mode->clock && i915_lvds_downclock) {
/* We found the downclock for LVDS. */
dev_priv->lvds_downclock_avail = 1;
dev_priv->lvds_downclock = temp_downclock;
DRM_DEBUG_KMS("LVDS downclock is found in EDID. "
"Normal clock %dKhz, downclock %dKhz\n",
fixed_mode->clock, temp_downclock);
}
}
 
/*
* Enumerate the child dev array parsed from VBT to check whether
* the LVDS is present.
* If it is present, return 1.
* If it is not present, return false.
* If no child dev is parsed from VBT, it assumes that the LVDS is present.
*/
static bool lvds_is_present_in_vbt(struct drm_device *dev,
u8 *i2c_pin)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int i;
 
if (!dev_priv->child_dev_num)
return true;
 
for (i = 0; i < dev_priv->child_dev_num; i++) {
struct child_device_config *child = dev_priv->child_dev + i;
 
/* If the device type is not LFP, continue.
* We have to check both the new identifiers as well as the
* old for compatibility with some BIOSes.
*/
if (child->device_type != DEVICE_TYPE_INT_LFP &&
child->device_type != DEVICE_TYPE_LFP)
continue;
 
if (child->i2c_pin)
*i2c_pin = child->i2c_pin;
 
/* However, we cannot trust the BIOS writers to populate
* the VBT correctly. Since LVDS requires additional
* information from AIM blocks, a non-zero addin offset is
* a good indicator that the LVDS is actually present.
*/
if (child->addin_offset)
return true;
 
/* But even then some BIOS writers perform some black magic
* and instantiate the device without reference to any
* additional data. Trust that if the VBT was written into
* the OpRegion then they have validated the LVDS's existence.
*/
if (dev_priv->opregion.vbt)
return true;
}
 
return false;
}
 
/**
* intel_lvds_init - setup LVDS connectors on this device
* @dev: drm device
*
* Create the connector, register the LVDS DDC bus, and try to figure out what
* modes we can display on the LVDS panel (if present).
*/
bool intel_lvds_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_lvds *intel_lvds;
struct intel_encoder *intel_encoder;
struct intel_connector *intel_connector;
struct drm_connector *connector;
struct drm_encoder *encoder;
struct drm_display_mode *scan; /* *modes, *bios_mode; */
struct drm_crtc *crtc;
u32 lvds;
int pipe;
u8 pin;
 
/* Skip init on machines we know falsely report LVDS */
// if (dmi_check_system(intel_no_lvds))
// return false;
 
pin = GMBUS_PORT_PANEL;
if (!lvds_is_present_in_vbt(dev, &pin)) {
DRM_DEBUG_KMS("LVDS is not present in VBT\n");
return false;
}
 
if (HAS_PCH_SPLIT(dev)) {
if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0)
return false;
if (dev_priv->edp.support) {
DRM_DEBUG_KMS("disable LVDS for eDP support\n");
return false;
}
}
 
intel_lvds = kzalloc(sizeof(struct intel_lvds), GFP_KERNEL);
if (!intel_lvds) {
return false;
}
 
intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
if (!intel_connector) {
kfree(intel_lvds);
return false;
}
 
if (!HAS_PCH_SPLIT(dev)) {
intel_lvds->pfit_control = I915_READ(PFIT_CONTROL);
}
 
intel_encoder = &intel_lvds->base;
encoder = &intel_encoder->base;
connector = &intel_connector->base;
drm_connector_init(dev, &intel_connector->base, &intel_lvds_connector_funcs,
DRM_MODE_CONNECTOR_LVDS);
 
drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs,
DRM_MODE_ENCODER_LVDS);
 
intel_connector_attach_encoder(intel_connector, intel_encoder);
intel_encoder->type = INTEL_OUTPUT_LVDS;
 
intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT);
intel_encoder->crtc_mask = (1 << 1);
if (INTEL_INFO(dev)->gen >= 5)
intel_encoder->crtc_mask |= (1 << 0);
drm_encoder_helper_add(encoder, &intel_lvds_helper_funcs);
drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs);
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
 
/* create the scaling mode property */
drm_mode_create_scaling_mode_property(dev);
/*
* the initial panel fitting mode will be FULL_SCREEN.
*/
 
drm_connector_attach_property(&intel_connector->base,
dev->mode_config.scaling_mode_property,
DRM_MODE_SCALE_ASPECT);
intel_lvds->fitting_mode = DRM_MODE_SCALE_ASPECT;
/*
* LVDS discovery:
* 1) check for EDID on DDC
* 2) check for VBT data
* 3) check to see if LVDS is already on
* if none of the above, no panel
* 4) make sure lid is open
* if closed, act like it's not there for now
*/
 
/*
* Attempt to get the fixed panel mode from DDC. Assume that the
* preferred mode is the right one.
*/
intel_lvds->edid = drm_get_edid(connector,
&dev_priv->gmbus[pin].adapter);
if (intel_lvds->edid) {
if (drm_add_edid_modes(connector,
intel_lvds->edid)) {
drm_mode_connector_update_edid_property(connector,
intel_lvds->edid);
} else {
kfree(intel_lvds->edid);
intel_lvds->edid = NULL;
}
}
if (!intel_lvds->edid) {
/* Didn't get an EDID, so
* Set wide sync ranges so we get all modes
* handed to valid_mode for checking
*/
connector->display_info.min_vfreq = 0;
connector->display_info.max_vfreq = 200;
connector->display_info.min_hfreq = 0;
connector->display_info.max_hfreq = 200;
}
 
list_for_each_entry(scan, &connector->probed_modes, head) {
if (scan->type & DRM_MODE_TYPE_PREFERRED) {
intel_lvds->fixed_mode =
drm_mode_duplicate(dev, scan);
intel_find_lvds_downclock(dev,
intel_lvds->fixed_mode,
connector);
goto out;
}
}
 
/* Failed to get EDID, what about VBT? */
if (dev_priv->lfp_lvds_vbt_mode) {
intel_lvds->fixed_mode =
drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
if (intel_lvds->fixed_mode) {
intel_lvds->fixed_mode->type |=
DRM_MODE_TYPE_PREFERRED;
goto out;
}
}
 
/*
* If we didn't get EDID, try checking if the panel is already turned
* on. If so, assume that whatever is currently programmed is the
* correct mode.
*/
 
/* Ironlake: FIXME if still fail, not try pipe mode now */
if (HAS_PCH_SPLIT(dev))
goto failed;
 
lvds = I915_READ(LVDS);
pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
crtc = intel_get_crtc_for_pipe(dev, pipe);
 
if (crtc && (lvds & LVDS_PORT_EN)) {
intel_lvds->fixed_mode = intel_crtc_mode_get(dev, crtc);
if (intel_lvds->fixed_mode) {
intel_lvds->fixed_mode->type |=
DRM_MODE_TYPE_PREFERRED;
goto out;
}
}
 
/* If we still don't have a mode after all that, give up. */
if (!intel_lvds->fixed_mode)
goto failed;
 
out:
if (HAS_PCH_SPLIT(dev)) {
u32 pwm;
 
pipe = (I915_READ(PCH_LVDS) & LVDS_PIPEB_SELECT) ? 1 : 0;
 
/* make sure PWM is enabled and locked to the LVDS pipe */
pwm = I915_READ(BLC_PWM_CPU_CTL2);
if (pipe == 0 && (pwm & PWM_PIPE_B))
I915_WRITE(BLC_PWM_CPU_CTL2, pwm & ~PWM_ENABLE);
if (pipe)
pwm |= PWM_PIPE_B;
else
pwm &= ~PWM_PIPE_B;
I915_WRITE(BLC_PWM_CPU_CTL2, pwm | PWM_ENABLE);
 
pwm = I915_READ(BLC_PWM_PCH_CTL1);
pwm |= PWM_PCH_ENABLE;
I915_WRITE(BLC_PWM_PCH_CTL1, pwm);
/*
* Unlock registers and just
* leave them unlocked
*/
I915_WRITE(PCH_PP_CONTROL,
I915_READ(PCH_PP_CONTROL) | PANEL_UNLOCK_REGS);
} else {
/*
* Unlock registers and just
* leave them unlocked
*/
I915_WRITE(PP_CONTROL,
I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
}
// dev_priv->lid_notifier.notifier_call = intel_lid_notify;
// if (acpi_lid_notifier_register(&dev_priv->lid_notifier)) {
// DRM_DEBUG_KMS("lid notifier registration failed\n");
// dev_priv->lid_notifier.notifier_call = NULL;
// }
/* keep the LVDS connector */
dev_priv->int_lvds_connector = connector;
drm_sysfs_connector_add(connector);
 
intel_panel_setup_backlight(dev);
 
return true;
 
failed:
DRM_DEBUG_KMS("No LVDS modes found, disabling.\n");
drm_connector_cleanup(connector);
drm_encoder_cleanup(encoder);
kfree(intel_lvds);
kfree(intel_connector);
return false;
}
/drivers/video/drm/i915/intel_modes.c
0,0 → 1,144
/*
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
* Copyright (c) 2007, 2010 Intel Corporation
* Jesse Barnes <jesse.barnes@intel.com>
*
* 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.
*/
 
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/fb.h>
#include "drmP.h"
#include "intel_drv.h"
#include "i915_drv.h"
 
/**
* intel_ddc_probe
*
*/
bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus)
{
struct drm_i915_private *dev_priv = intel_encoder->base.dev->dev_private;
u8 out_buf[] = { 0x0, 0x0};
u8 buf[2];
struct i2c_msg msgs[] = {
{
.addr = 0x50,
.flags = 0,
.len = 1,
.buf = out_buf,
},
{
.addr = 0x50,
.flags = I2C_M_RD,
.len = 1,
.buf = buf,
}
};
 
return i2c_transfer(&dev_priv->gmbus[ddc_bus].adapter, msgs, 2) == 2;
}
 
/**
* intel_ddc_get_modes - get modelist from monitor
* @connector: DRM connector device to use
* @adapter: i2c adapter
*
* Fetch the EDID information from @connector using the DDC bus.
*/
int intel_ddc_get_modes(struct drm_connector *connector,
struct i2c_adapter *adapter)
{
struct edid *edid;
int ret = 0;
 
edid = drm_get_edid(connector, adapter);
if (edid) {
drm_mode_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid);
connector->display_info.raw_edid = NULL;
kfree(edid);
}
 
return ret;
}
 
static const char *force_audio_names[] = {
"off",
"auto",
"on",
};
 
void
intel_attach_force_audio_property(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_property *prop;
int i;
#if 0
prop = dev_priv->force_audio_property;
if (prop == NULL) {
prop = drm_property_create(dev, DRM_MODE_PROP_ENUM,
"audio",
ARRAY_SIZE(force_audio_names));
if (prop == NULL)
return;
 
for (i = 0; i < ARRAY_SIZE(force_audio_names); i++)
drm_property_add_enum(prop, i, i-1, force_audio_names[i]);
 
dev_priv->force_audio_property = prop;
}
drm_connector_attach_property(connector, prop, 0);
#endif
}
 
static const char *broadcast_rgb_names[] = {
"Full",
"Limited 16:235",
};
 
void
intel_attach_broadcast_rgb_property(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_property *prop;
int i;
#if 0
prop = dev_priv->broadcast_rgb_property;
if (prop == NULL) {
prop = drm_property_create(dev, DRM_MODE_PROP_ENUM,
"Broadcast RGB",
ARRAY_SIZE(broadcast_rgb_names));
if (prop == NULL)
return;
 
for (i = 0; i < ARRAY_SIZE(broadcast_rgb_names); i++)
drm_property_add_enum(prop, i, i, broadcast_rgb_names[i]);
 
dev_priv->broadcast_rgb_property = prop;
}
 
drm_connector_attach_property(connector, prop, 0);
#endif
}
/drivers/video/drm/i915/intel_opregion.c
30,12 → 30,10
//#include <acpi/video.h>
#include <linux/errno.h>
#include "drmP.h"
//#include "i915_drm.h"
#include "i915_drm.h"
#include "i915_drv.h"
#include "intel_drv.h"
 
#include <syscall.h>
 
#define PCI_ASLE 0xe4
#define PCI_ASLS 0xfc
 
/drivers/video/drm/i915/intel_panel.c
0,0 → 1,396
/*
* Copyright © 2006-2010 Intel Corporation
* Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
*
* 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>
* Dave Airlie <airlied@linux.ie>
* Jesse Barnes <jesse.barnes@intel.com>
* Chris Wilson <chris@chris-wilson.co.uk>
*/
 
#include "intel_drv.h"
 
static inline int pci_read_config_byte(struct pci_dev *dev, int where,
u8 *val)
{
*val = PciRead8(dev->busnr, dev->devfn, where);
return 1;
}
 
static inline int pci_write_config_byte(struct pci_dev *dev, int where,
u8 val)
{
PciWrite8(dev->busnr, dev->devfn, where, val);
return 1;
}
 
 
#define PCI_LBPC 0xf4 /* legacy/combination backlight modes */
 
void
intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
struct drm_display_mode *adjusted_mode)
{
adjusted_mode->hdisplay = fixed_mode->hdisplay;
adjusted_mode->hsync_start = fixed_mode->hsync_start;
adjusted_mode->hsync_end = fixed_mode->hsync_end;
adjusted_mode->htotal = fixed_mode->htotal;
 
adjusted_mode->vdisplay = fixed_mode->vdisplay;
adjusted_mode->vsync_start = fixed_mode->vsync_start;
adjusted_mode->vsync_end = fixed_mode->vsync_end;
adjusted_mode->vtotal = fixed_mode->vtotal;
 
adjusted_mode->clock = fixed_mode->clock;
 
drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
}
 
/* adjusted_mode has been preset to be the panel's fixed mode */
void
intel_pch_panel_fitting(struct drm_device *dev,
int fitting_mode,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int x, y, width, height;
 
x = y = width = height = 0;
 
/* Native modes don't need fitting */
if (adjusted_mode->hdisplay == mode->hdisplay &&
adjusted_mode->vdisplay == mode->vdisplay)
goto done;
 
switch (fitting_mode) {
case DRM_MODE_SCALE_CENTER:
width = mode->hdisplay;
height = mode->vdisplay;
x = (adjusted_mode->hdisplay - width + 1)/2;
y = (adjusted_mode->vdisplay - height + 1)/2;
break;
 
case DRM_MODE_SCALE_ASPECT:
/* Scale but preserve the aspect ratio */
{
u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
if (scaled_width > scaled_height) { /* pillar */
width = scaled_height / mode->vdisplay;
if (width & 1)
width++;
x = (adjusted_mode->hdisplay - width + 1) / 2;
y = 0;
height = adjusted_mode->vdisplay;
} else if (scaled_width < scaled_height) { /* letter */
height = scaled_width / mode->hdisplay;
if (height & 1)
height++;
y = (adjusted_mode->vdisplay - height + 1) / 2;
x = 0;
width = adjusted_mode->hdisplay;
} else {
x = y = 0;
width = adjusted_mode->hdisplay;
height = adjusted_mode->vdisplay;
}
}
break;
 
default:
case DRM_MODE_SCALE_FULLSCREEN:
x = y = 0;
width = adjusted_mode->hdisplay;
height = adjusted_mode->vdisplay;
break;
}
 
done:
dev_priv->pch_pf_pos = (x << 16) | y;
dev_priv->pch_pf_size = (width << 16) | height;
}
 
static int is_backlight_combination_mode(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
 
if (INTEL_INFO(dev)->gen >= 4)
return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE;
 
if (IS_GEN2(dev))
return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE;
 
return 0;
}
 
static u32 i915_read_blc_pwm_ctl(struct drm_i915_private *dev_priv)
{
u32 val;
 
/* Restore the CTL value if it lost, e.g. GPU reset */
 
if (HAS_PCH_SPLIT(dev_priv->dev)) {
val = I915_READ(BLC_PWM_PCH_CTL2);
if (dev_priv->saveBLC_PWM_CTL2 == 0) {
dev_priv->saveBLC_PWM_CTL2 = val;
} else if (val == 0) {
I915_WRITE(BLC_PWM_PCH_CTL2,
dev_priv->saveBLC_PWM_CTL);
val = dev_priv->saveBLC_PWM_CTL;
}
} else {
val = I915_READ(BLC_PWM_CTL);
if (dev_priv->saveBLC_PWM_CTL == 0) {
dev_priv->saveBLC_PWM_CTL = val;
dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2);
} else if (val == 0) {
I915_WRITE(BLC_PWM_CTL,
dev_priv->saveBLC_PWM_CTL);
I915_WRITE(BLC_PWM_CTL2,
dev_priv->saveBLC_PWM_CTL2);
val = dev_priv->saveBLC_PWM_CTL;
}
}
 
return val;
}
 
u32 intel_panel_get_max_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 max;
 
max = i915_read_blc_pwm_ctl(dev_priv);
if (max == 0) {
/* XXX add code here to query mode clock or hardware clock
* and program max PWM appropriately.
*/
printk(KERN_WARNING "fixme: max PWM is zero.\n");
return 1;
}
 
if (HAS_PCH_SPLIT(dev)) {
max >>= 16;
} else {
if (IS_PINEVIEW(dev)) {
max >>= 17;
} else {
max >>= 16;
if (INTEL_INFO(dev)->gen < 4)
max &= ~1;
}
 
if (is_backlight_combination_mode(dev))
max *= 0xff;
}
 
DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max);
return max;
}
 
u32 intel_panel_get_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 val;
 
if (HAS_PCH_SPLIT(dev)) {
val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
} else {
val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
if (IS_PINEVIEW(dev))
val >>= 1;
 
if (is_backlight_combination_mode(dev)){
u8 lbpc;
 
val &= ~1;
pci_read_config_byte(dev->pdev, PCI_LBPC, &lbpc);
val *= lbpc;
}
}
 
DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val);
return val;
}
 
static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
I915_WRITE(BLC_PWM_CPU_CTL, val | level);
}
 
void intel_panel_set_backlight(struct drm_device *dev, u32 level)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 tmp;
 
DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);
 
if (HAS_PCH_SPLIT(dev))
return intel_pch_panel_set_backlight(dev, level);
 
if (is_backlight_combination_mode(dev)){
u32 max = intel_panel_get_max_backlight(dev);
u8 lbpc;
 
lbpc = level * 0xfe / max + 1;
level /= lbpc;
pci_write_config_byte(dev->pdev, PCI_LBPC, lbpc);
}
 
tmp = I915_READ(BLC_PWM_CTL);
if (IS_PINEVIEW(dev)) {
tmp &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1);
level <<= 1;
} else
tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK;
I915_WRITE(BLC_PWM_CTL, tmp | level);
}
 
void intel_panel_disable_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
 
if (dev_priv->backlight_enabled) {
dev_priv->backlight_level = intel_panel_get_backlight(dev);
dev_priv->backlight_enabled = false;
}
 
intel_panel_set_backlight(dev, 0);
}
 
void intel_panel_enable_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
 
if (dev_priv->backlight_level == 0)
dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
 
intel_panel_set_backlight(dev, dev_priv->backlight_level);
dev_priv->backlight_enabled = true;
}
 
static void intel_panel_init_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
 
dev_priv->backlight_level = intel_panel_get_backlight(dev);
dev_priv->backlight_enabled = dev_priv->backlight_level != 0;
}
 
enum drm_connector_status
intel_panel_detect(struct drm_device *dev)
{
#if 0
struct drm_i915_private *dev_priv = dev->dev_private;
#endif
 
if (i915_panel_ignore_lid)
return i915_panel_ignore_lid > 0 ?
connector_status_connected :
connector_status_disconnected;
 
/* opregion lid state on HP 2540p is wrong at boot up,
* appears to be either the BIOS or Linux ACPI fault */
#if 0
/* Assume that the BIOS does not lie through the OpRegion... */
if (dev_priv->opregion.lid_state)
return ioread32(dev_priv->opregion.lid_state) & 0x1 ?
connector_status_connected :
connector_status_disconnected;
#endif
 
return connector_status_unknown;
}
 
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
static int intel_panel_update_status(struct backlight_device *bd)
{
struct drm_device *dev = bl_get_data(bd);
intel_panel_set_backlight(dev, bd->props.brightness);
return 0;
}
 
static int intel_panel_get_brightness(struct backlight_device *bd)
{
struct drm_device *dev = bl_get_data(bd);
return intel_panel_get_backlight(dev);
}
 
static const struct backlight_ops intel_panel_bl_ops = {
.update_status = intel_panel_update_status,
.get_brightness = intel_panel_get_brightness,
};
 
int intel_panel_setup_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct backlight_properties props;
struct drm_connector *connector;
 
intel_panel_init_backlight(dev);
 
if (dev_priv->int_lvds_connector)
connector = dev_priv->int_lvds_connector;
else if (dev_priv->int_edp_connector)
connector = dev_priv->int_edp_connector;
else
return -ENODEV;
 
props.type = BACKLIGHT_RAW;
props.max_brightness = intel_panel_get_max_backlight(dev);
dev_priv->backlight =
backlight_device_register("intel_backlight",
&connector->kdev, dev,
&intel_panel_bl_ops, &props);
 
if (IS_ERR(dev_priv->backlight)) {
DRM_ERROR("Failed to register backlight: %ld\n",
PTR_ERR(dev_priv->backlight));
dev_priv->backlight = NULL;
return -ENODEV;
}
dev_priv->backlight->props.brightness = intel_panel_get_backlight(dev);
return 0;
}
 
void intel_panel_destroy_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
if (dev_priv->backlight)
backlight_device_unregister(dev_priv->backlight);
}
#else
int intel_panel_setup_backlight(struct drm_device *dev)
{
intel_panel_init_backlight(dev);
return 0;
}
 
void intel_panel_destroy_backlight(struct drm_device *dev)
{
return;
}
#endif
/drivers/video/drm/i915/intel_ringbuffer.h
109,7 → 109,7
u32 outstanding_lazy_request;
 
// wait_queue_head_t irq_queue;
// drm_local_map_t map;
drm_local_map_t map;
 
void *private;
};
/drivers/video/drm/i915/intel_sdvo.c
0,0 → 1,2592
/*
* Copyright 2006 Dave Airlie <airlied@linux.ie>
* Copyright © 2006-2007 Intel Corporation
* Jesse Barnes <jesse.barnes@intel.com>
*
* 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 <linux/i2c.h>
#include <linux/slab.h>
//#include <linux/delay.h>
#include "drmP.h"
#include "drm.h"
#include "drm_crtc.h"
#include "drm_edid.h"
#include "intel_drv.h"
#include "i915_drm.h"
#include "i915_drv.h"
#include "intel_sdvo_regs.h"
 
unsigned int hweight16(unsigned int w)
{
unsigned int res = w - ((w >> 1) & 0x5555);
res = (res & 0x3333) + ((res >> 2) & 0x3333);
res = (res + (res >> 4)) & 0x0F0F;
return (res + (res >> 8)) & 0x00FF;
}
 
 
#define SDVO_TMDS_MASK (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_TMDS1)
#define SDVO_RGB_MASK (SDVO_OUTPUT_RGB0 | SDVO_OUTPUT_RGB1)
#define SDVO_LVDS_MASK (SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_LVDS1)
#define SDVO_TV_MASK (SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_SVID0)
 
#define SDVO_OUTPUT_MASK (SDVO_TMDS_MASK | SDVO_RGB_MASK | SDVO_LVDS_MASK |\
SDVO_TV_MASK)
 
#define IS_TV(c) (c->output_flag & SDVO_TV_MASK)
#define IS_TMDS(c) (c->output_flag & SDVO_TMDS_MASK)
#define IS_LVDS(c) (c->output_flag & SDVO_LVDS_MASK)
#define IS_TV_OR_LVDS(c) (c->output_flag & (SDVO_TV_MASK | SDVO_LVDS_MASK))
 
 
static const char *tv_format_names[] = {
"NTSC_M" , "NTSC_J" , "NTSC_443",
"PAL_B" , "PAL_D" , "PAL_G" ,
"PAL_H" , "PAL_I" , "PAL_M" ,
"PAL_N" , "PAL_NC" , "PAL_60" ,
"SECAM_B" , "SECAM_D" , "SECAM_G" ,
"SECAM_K" , "SECAM_K1", "SECAM_L" ,
"SECAM_60"
};
 
#define TV_FORMAT_NUM (sizeof(tv_format_names) / sizeof(*tv_format_names))
 
struct intel_sdvo {
struct intel_encoder base;
 
struct i2c_adapter *i2c;
u8 slave_addr;
 
struct i2c_adapter ddc;
 
/* Register for the SDVO device: SDVOB or SDVOC */
int sdvo_reg;
 
/* Active outputs controlled by this SDVO output */
uint16_t controlled_output;
 
/*
* Capabilities of the SDVO device returned by
* i830_sdvo_get_capabilities()
*/
struct intel_sdvo_caps caps;
 
/* Pixel clock limitations reported by the SDVO device, in kHz */
int pixel_clock_min, pixel_clock_max;
 
/*
* For multiple function SDVO device,
* this is for current attached outputs.
*/
uint16_t attached_output;
 
/**
* This is used to select the color range of RBG outputs in HDMI mode.
* It is only valid when using TMDS encoding and 8 bit per color mode.
*/
uint32_t color_range;
 
/**
* This is set if we're going to treat the device as TV-out.
*
* While we have these nice friendly flags for output types that ought
* to decide this for us, the S-Video output on our HDMI+S-Video card
* shows up as RGB1 (VGA).
*/
bool is_tv;
 
/* This is for current tv format name */
int tv_format_index;
 
/**
* This is set if we treat the device as HDMI, instead of DVI.
*/
bool is_hdmi;
bool has_hdmi_monitor;
bool has_hdmi_audio;
 
/**
* This is set if we detect output of sdvo device as LVDS and
* have a valid fixed mode to use with the panel.
*/
bool is_lvds;
 
/**
* This is sdvo fixed pannel mode pointer
*/
struct drm_display_mode *sdvo_lvds_fixed_mode;
 
/* DDC bus used by this SDVO encoder */
uint8_t ddc_bus;
 
/* Input timings for adjusted_mode */
struct intel_sdvo_dtd input_dtd;
};
 
struct intel_sdvo_connector {
struct intel_connector base;
 
/* Mark the type of connector */
uint16_t output_flag;
 
int force_audio;
 
/* This contains all current supported TV format */
u8 tv_format_supported[TV_FORMAT_NUM];
int format_supported_num;
struct drm_property *tv_format;
 
/* add the property for the SDVO-TV */
struct drm_property *left;
struct drm_property *right;
struct drm_property *top;
struct drm_property *bottom;
struct drm_property *hpos;
struct drm_property *vpos;
struct drm_property *contrast;
struct drm_property *saturation;
struct drm_property *hue;
struct drm_property *sharpness;
struct drm_property *flicker_filter;
struct drm_property *flicker_filter_adaptive;
struct drm_property *flicker_filter_2d;
struct drm_property *tv_chroma_filter;
struct drm_property *tv_luma_filter;
struct drm_property *dot_crawl;
 
/* add the property for the SDVO-TV/LVDS */
struct drm_property *brightness;
 
/* Add variable to record current setting for the above property */
u32 left_margin, right_margin, top_margin, bottom_margin;
 
/* this is to get the range of margin.*/
u32 max_hscan, max_vscan;
u32 max_hpos, cur_hpos;
u32 max_vpos, cur_vpos;
u32 cur_brightness, max_brightness;
u32 cur_contrast, max_contrast;
u32 cur_saturation, max_saturation;
u32 cur_hue, max_hue;
u32 cur_sharpness, max_sharpness;
u32 cur_flicker_filter, max_flicker_filter;
u32 cur_flicker_filter_adaptive, max_flicker_filter_adaptive;
u32 cur_flicker_filter_2d, max_flicker_filter_2d;
u32 cur_tv_chroma_filter, max_tv_chroma_filter;
u32 cur_tv_luma_filter, max_tv_luma_filter;
u32 cur_dot_crawl, max_dot_crawl;
};
 
static struct intel_sdvo *to_intel_sdvo(struct drm_encoder *encoder)
{
return container_of(encoder, struct intel_sdvo, base.base);
}
 
static struct intel_sdvo *intel_attached_sdvo(struct drm_connector *connector)
{
return container_of(intel_attached_encoder(connector),
struct intel_sdvo, base);
}
 
static struct intel_sdvo_connector *to_intel_sdvo_connector(struct drm_connector *connector)
{
return container_of(to_intel_connector(connector), struct intel_sdvo_connector, base);
}
 
static bool
intel_sdvo_output_setup(struct intel_sdvo *intel_sdvo, uint16_t flags);
static bool
intel_sdvo_tv_create_property(struct intel_sdvo *intel_sdvo,
struct intel_sdvo_connector *intel_sdvo_connector,
int type);
static bool
intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo,
struct intel_sdvo_connector *intel_sdvo_connector);
 
/**
* Writes the SDVOB or SDVOC with the given value, but always writes both
* SDVOB and SDVOC to work around apparent hardware issues (according to
* comments in the BIOS).
*/
static void intel_sdvo_write_sdvox(struct intel_sdvo *intel_sdvo, u32 val)
{
struct drm_device *dev = intel_sdvo->base.base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
u32 bval = val, cval = val;
int i;
 
if (intel_sdvo->sdvo_reg == PCH_SDVOB) {
I915_WRITE(intel_sdvo->sdvo_reg, val);
I915_READ(intel_sdvo->sdvo_reg);
return;
}
 
if (intel_sdvo->sdvo_reg == SDVOB) {
cval = I915_READ(SDVOC);
} else {
bval = I915_READ(SDVOB);
}
/*
* Write the registers twice for luck. Sometimes,
* writing them only once doesn't appear to 'stick'.
* The BIOS does this too. Yay, magic
*/
for (i = 0; i < 2; i++)
{
I915_WRITE(SDVOB, bval);
I915_READ(SDVOB);
I915_WRITE(SDVOC, cval);
I915_READ(SDVOC);
}
}
 
static bool intel_sdvo_read_byte(struct intel_sdvo *intel_sdvo, u8 addr, u8 *ch)
{
struct i2c_msg msgs[] = {
{
.addr = intel_sdvo->slave_addr,
.flags = 0,
.len = 1,
.buf = &addr,
},
{
.addr = intel_sdvo->slave_addr,
.flags = I2C_M_RD,
.len = 1,
.buf = ch,
}
};
int ret;
 
if ((ret = i2c_transfer(intel_sdvo->i2c, msgs, 2)) == 2)
return true;
 
DRM_DEBUG_KMS("i2c transfer returned %d\n", ret);
return false;
}
 
#define SDVO_CMD_NAME_ENTRY(cmd) {cmd, #cmd}
/** Mapping of command numbers to names, for debug output */
static const struct _sdvo_cmd_name {
u8 cmd;
const char *name;
} sdvo_cmd_names[] = {
SDVO_CMD_NAME_ENTRY(SDVO_CMD_RESET),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DEVICE_CAPS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FIRMWARE_REV),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TRAINED_INPUTS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_OUTPUTS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_OUTPUTS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_IN_OUT_MAP),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_IN_OUT_MAP),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ATTACHED_DISPLAYS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HOT_PLUG_SUPPORT),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ACTIVE_HOT_PLUG),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ACTIVE_HOT_PLUG),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_INPUT),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TARGET_OUTPUT),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART1),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_TIMINGS_PART2),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART2),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_INPUT_TIMINGS_PART1),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART1),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OUTPUT_TIMINGS_PART2),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART1),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_TIMINGS_PART2),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CLOCK_RATE_MULT),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CLOCK_RATE_MULT),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_TV_FORMATS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_FORMAT),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_FORMAT),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_POWER_STATES),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_POWER_STATE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODER_POWER_STATE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DISPLAY_POWER_STATE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTROL_BUS_SWITCH),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS),
 
/* Add the op code for SDVO enhancements */
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HPOS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HPOS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HPOS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_VPOS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_VPOS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_VPOS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SATURATION),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SATURATION),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SATURATION),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_HUE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HUE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HUE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_CONTRAST),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_CONTRAST),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_CONTRAST),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_BRIGHTNESS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_BRIGHTNESS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_BRIGHTNESS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_H),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_H),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_H),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_OVERSCAN_V),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_OVERSCAN_V),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_OVERSCAN_V),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_ADAPTIVE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_ADAPTIVE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_ADAPTIVE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_FLICKER_FILTER_2D),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_FLICKER_FILTER_2D),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_FLICKER_FILTER_2D),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_SHARPNESS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SHARPNESS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_SHARPNESS),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_DOT_CRAWL),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_DOT_CRAWL),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_CHROMA_FILTER),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_CHROMA_FILTER),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_CHROMA_FILTER),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_MAX_TV_LUMA_FILTER),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_TV_LUMA_FILTER),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_TV_LUMA_FILTER),
 
/* HDMI op code */
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_SUPP_ENCODE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_ENCODE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_ENCODE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_PIXEL_REPLI),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_PIXEL_REPLI),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY_CAP),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_COLORIMETRY),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_COLORIMETRY),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_AUDIO_STAT),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_AUDIO_STAT),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INDEX),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_INDEX),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_INFO),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_AV_SPLIT),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_AV_SPLIT),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_TXRATE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_TXRATE),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_SET_HBUF_DATA),
SDVO_CMD_NAME_ENTRY(SDVO_CMD_GET_HBUF_DATA),
};
 
#define IS_SDVOB(reg) (reg == SDVOB || reg == PCH_SDVOB)
#define SDVO_NAME(svdo) (IS_SDVOB((svdo)->sdvo_reg) ? "SDVOB" : "SDVOC")
 
static void intel_sdvo_debug_write(struct intel_sdvo *intel_sdvo, u8 cmd,
const void *args, int args_len)
{
int i;
 
DRM_DEBUG_KMS("%s: W: %02X ",
SDVO_NAME(intel_sdvo), cmd);
for (i = 0; i < args_len; i++)
DRM_LOG_KMS("%02X ", ((u8 *)args)[i]);
for (; i < 8; i++)
DRM_LOG_KMS(" ");
for (i = 0; i < ARRAY_SIZE(sdvo_cmd_names); i++) {
if (cmd == sdvo_cmd_names[i].cmd) {
DRM_LOG_KMS("(%s)", sdvo_cmd_names[i].name);
break;
}
}
if (i == ARRAY_SIZE(sdvo_cmd_names))
DRM_LOG_KMS("(%02X)", cmd);
DRM_LOG_KMS("\n");
}
 
static const char *cmd_status_names[] = {
"Power on",
"Success",
"Not supported",
"Invalid arg",
"Pending",
"Target not specified",
"Scaling not supported"
};
 
static bool intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd,
const void *args, int args_len)
{
u8 buf[args_len*2 + 2], status;
struct i2c_msg msgs[args_len + 3];
int i, ret;
 
intel_sdvo_debug_write(intel_sdvo, cmd, args, args_len);
 
for (i = 0; i < args_len; i++) {
msgs[i].addr = intel_sdvo->slave_addr;
msgs[i].flags = 0;
msgs[i].len = 2;
msgs[i].buf = buf + 2 *i;
buf[2*i + 0] = SDVO_I2C_ARG_0 - i;
buf[2*i + 1] = ((u8*)args)[i];
}
msgs[i].addr = intel_sdvo->slave_addr;
msgs[i].flags = 0;
msgs[i].len = 2;
msgs[i].buf = buf + 2*i;
buf[2*i + 0] = SDVO_I2C_OPCODE;
buf[2*i + 1] = cmd;
 
/* the following two are to read the response */
status = SDVO_I2C_CMD_STATUS;
msgs[i+1].addr = intel_sdvo->slave_addr;
msgs[i+1].flags = 0;
msgs[i+1].len = 1;
msgs[i+1].buf = &status;
 
msgs[i+2].addr = intel_sdvo->slave_addr;
msgs[i+2].flags = I2C_M_RD;
msgs[i+2].len = 1;
msgs[i+2].buf = &status;
 
ret = i2c_transfer(intel_sdvo->i2c, msgs, i+3);
if (ret < 0) {
DRM_DEBUG_KMS("I2c transfer returned %d\n", ret);
return false;
}
if (ret != i+3) {
/* failure in I2C transfer */
DRM_DEBUG_KMS("I2c transfer returned %d/%d\n", ret, i+3);
return false;
}
 
return true;
}
 
static bool intel_sdvo_read_response(struct intel_sdvo *intel_sdvo,
void *response, int response_len)
{
u8 retry = 5;
u8 status;
int i;
 
DRM_DEBUG_KMS("%s: R: ", SDVO_NAME(intel_sdvo));
 
/*
* The documentation states that all commands will be
* processed within 15µs, and that we need only poll
* the status byte a maximum of 3 times in order for the
* command to be complete.
*
* Check 5 times in case the hardware failed to read the docs.
*/
if (!intel_sdvo_read_byte(intel_sdvo,
SDVO_I2C_CMD_STATUS,
&status))
goto log_fail;
 
while (status == SDVO_CMD_STATUS_PENDING && retry--) {
udelay(15);
if (!intel_sdvo_read_byte(intel_sdvo,
SDVO_I2C_CMD_STATUS,
&status))
goto log_fail;
}
 
if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP)
DRM_LOG_KMS("(%s)", cmd_status_names[status]);
else
DRM_LOG_KMS("(??? %d)", status);
 
if (status != SDVO_CMD_STATUS_SUCCESS)
goto log_fail;
 
/* Read the command response */
for (i = 0; i < response_len; i++) {
if (!intel_sdvo_read_byte(intel_sdvo,
SDVO_I2C_RETURN_0 + i,
&((u8 *)response)[i]))
goto log_fail;
DRM_LOG_KMS(" %02X", ((u8 *)response)[i]);
}
DRM_LOG_KMS("\n");
return true;
 
log_fail:
DRM_LOG_KMS("... failed\n");
return false;
}
 
static int intel_sdvo_get_pixel_multiplier(struct drm_display_mode *mode)
{
if (mode->clock >= 100000)
return 1;
else if (mode->clock >= 50000)
return 2;
else
return 4;
}
 
static bool intel_sdvo_set_control_bus_switch(struct intel_sdvo *intel_sdvo,
u8 ddc_bus)
{
/* This must be the immediately preceding write before the i2c xfer */
return intel_sdvo_write_cmd(intel_sdvo,
SDVO_CMD_SET_CONTROL_BUS_SWITCH,
&ddc_bus, 1);
}
 
static bool intel_sdvo_set_value(struct intel_sdvo *intel_sdvo, u8 cmd, const void *data, int len)
{
if (!intel_sdvo_write_cmd(intel_sdvo, cmd, data, len))
return false;
 
return intel_sdvo_read_response(intel_sdvo, NULL, 0);
}
 
static bool
intel_sdvo_get_value(struct intel_sdvo *intel_sdvo, u8 cmd, void *value, int len)
{
if (!intel_sdvo_write_cmd(intel_sdvo, cmd, NULL, 0))
return false;
 
return intel_sdvo_read_response(intel_sdvo, value, len);
}
 
static bool intel_sdvo_set_target_input(struct intel_sdvo *intel_sdvo)
{
struct intel_sdvo_set_target_input_args targets = {0};
return intel_sdvo_set_value(intel_sdvo,
SDVO_CMD_SET_TARGET_INPUT,
&targets, sizeof(targets));
}
 
/**
* Return whether each input is trained.
*
* This function is making an assumption about the layout of the response,
* which should be checked against the docs.
*/
static bool intel_sdvo_get_trained_inputs(struct intel_sdvo *intel_sdvo, bool *input_1, bool *input_2)
{
struct intel_sdvo_get_trained_inputs_response response;
 
BUILD_BUG_ON(sizeof(response) != 1);
if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_TRAINED_INPUTS,
&response, sizeof(response)))
return false;
 
*input_1 = response.input0_trained;
*input_2 = response.input1_trained;
return true;
}
 
static bool intel_sdvo_set_active_outputs(struct intel_sdvo *intel_sdvo,
u16 outputs)
{
return intel_sdvo_set_value(intel_sdvo,
SDVO_CMD_SET_ACTIVE_OUTPUTS,
&outputs, sizeof(outputs));
}
 
 
 
 
static bool intel_sdvo_get_input_pixel_clock_range(struct intel_sdvo *intel_sdvo,
int *clock_min,
int *clock_max)
{
struct intel_sdvo_pixel_clock_range clocks;
 
BUILD_BUG_ON(sizeof(clocks) != 4);
if (!intel_sdvo_get_value(intel_sdvo,
SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE,
&clocks, sizeof(clocks)))
return false;
 
/* Convert the values from units of 10 kHz to kHz. */
*clock_min = clocks.min * 10;
*clock_max = clocks.max * 10;
return true;
}
 
static bool intel_sdvo_set_target_output(struct intel_sdvo *intel_sdvo,
u16 outputs)
{
return intel_sdvo_set_value(intel_sdvo,
SDVO_CMD_SET_TARGET_OUTPUT,
&outputs, sizeof(outputs));
}
 
static bool intel_sdvo_set_timing(struct intel_sdvo *intel_sdvo, u8 cmd,
struct intel_sdvo_dtd *dtd)
{
return intel_sdvo_set_value(intel_sdvo, cmd, &dtd->part1, sizeof(dtd->part1)) &&
intel_sdvo_set_value(intel_sdvo, cmd + 1, &dtd->part2, sizeof(dtd->part2));
}
 
static bool intel_sdvo_set_input_timing(struct intel_sdvo *intel_sdvo,
struct intel_sdvo_dtd *dtd)
{
return intel_sdvo_set_timing(intel_sdvo,
SDVO_CMD_SET_INPUT_TIMINGS_PART1, dtd);
}
 
static bool intel_sdvo_set_output_timing(struct intel_sdvo *intel_sdvo,
struct intel_sdvo_dtd *dtd)
{
return intel_sdvo_set_timing(intel_sdvo,
SDVO_CMD_SET_OUTPUT_TIMINGS_PART1, dtd);
}
 
static bool
intel_sdvo_create_preferred_input_timing(struct intel_sdvo *intel_sdvo,
uint16_t clock,
uint16_t width,
uint16_t height)
{
struct intel_sdvo_preferred_input_timing_args args;
 
memset(&args, 0, sizeof(args));
args.clock = clock;
args.width = width;
args.height = height;
args.interlace = 0;
 
if (intel_sdvo->is_lvds &&
(intel_sdvo->sdvo_lvds_fixed_mode->hdisplay != width ||
intel_sdvo->sdvo_lvds_fixed_mode->vdisplay != height))
args.scaled = 1;
 
return intel_sdvo_set_value(intel_sdvo,
SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING,
&args, sizeof(args));
}
 
static bool intel_sdvo_get_preferred_input_timing(struct intel_sdvo *intel_sdvo,
struct intel_sdvo_dtd *dtd)
{
BUILD_BUG_ON(sizeof(dtd->part1) != 8);
BUILD_BUG_ON(sizeof(dtd->part2) != 8);
return intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1,
&dtd->part1, sizeof(dtd->part1)) &&
intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2,
&dtd->part2, sizeof(dtd->part2));
}
 
static bool intel_sdvo_set_clock_rate_mult(struct intel_sdvo *intel_sdvo, u8 val)
{
return intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_CLOCK_RATE_MULT, &val, 1);
}
 
static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd,
const struct drm_display_mode *mode)
{
uint16_t width, height;
uint16_t h_blank_len, h_sync_len, v_blank_len, v_sync_len;
uint16_t h_sync_offset, v_sync_offset;
 
width = mode->crtc_hdisplay;
height = mode->crtc_vdisplay;
 
/* do some mode translations */
h_blank_len = mode->crtc_hblank_end - mode->crtc_hblank_start;
h_sync_len = mode->crtc_hsync_end - mode->crtc_hsync_start;
 
v_blank_len = mode->crtc_vblank_end - mode->crtc_vblank_start;
v_sync_len = mode->crtc_vsync_end - mode->crtc_vsync_start;
 
h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start;
v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start;
 
dtd->part1.clock = mode->clock / 10;
dtd->part1.h_active = width & 0xff;
dtd->part1.h_blank = h_blank_len & 0xff;
dtd->part1.h_high = (((width >> 8) & 0xf) << 4) |
((h_blank_len >> 8) & 0xf);
dtd->part1.v_active = height & 0xff;
dtd->part1.v_blank = v_blank_len & 0xff;
dtd->part1.v_high = (((height >> 8) & 0xf) << 4) |
((v_blank_len >> 8) & 0xf);
 
dtd->part2.h_sync_off = h_sync_offset & 0xff;
dtd->part2.h_sync_width = h_sync_len & 0xff;
dtd->part2.v_sync_off_width = (v_sync_offset & 0xf) << 4 |
(v_sync_len & 0xf);
dtd->part2.sync_off_width_high = ((h_sync_offset & 0x300) >> 2) |
((h_sync_len & 0x300) >> 4) | ((v_sync_offset & 0x30) >> 2) |
((v_sync_len & 0x30) >> 4);
 
dtd->part2.dtd_flags = 0x18;
if (mode->flags & DRM_MODE_FLAG_PHSYNC)
dtd->part2.dtd_flags |= 0x2;
if (mode->flags & DRM_MODE_FLAG_PVSYNC)
dtd->part2.dtd_flags |= 0x4;
 
dtd->part2.sdvo_flags = 0;
dtd->part2.v_sync_off_high = v_sync_offset & 0xc0;
dtd->part2.reserved = 0;
}
 
static void intel_sdvo_get_mode_from_dtd(struct drm_display_mode * mode,
const struct intel_sdvo_dtd *dtd)
{
mode->hdisplay = dtd->part1.h_active;
mode->hdisplay += ((dtd->part1.h_high >> 4) & 0x0f) << 8;
mode->hsync_start = mode->hdisplay + dtd->part2.h_sync_off;
mode->hsync_start += (dtd->part2.sync_off_width_high & 0xc0) << 2;
mode->hsync_end = mode->hsync_start + dtd->part2.h_sync_width;
mode->hsync_end += (dtd->part2.sync_off_width_high & 0x30) << 4;
mode->htotal = mode->hdisplay + dtd->part1.h_blank;
mode->htotal += (dtd->part1.h_high & 0xf) << 8;
 
mode->vdisplay = dtd->part1.v_active;
mode->vdisplay += ((dtd->part1.v_high >> 4) & 0x0f) << 8;
mode->vsync_start = mode->vdisplay;
mode->vsync_start += (dtd->part2.v_sync_off_width >> 4) & 0xf;
mode->vsync_start += (dtd->part2.sync_off_width_high & 0x0c) << 2;
mode->vsync_start += dtd->part2.v_sync_off_high & 0xc0;
mode->vsync_end = mode->vsync_start +
(dtd->part2.v_sync_off_width & 0xf);
mode->vsync_end += (dtd->part2.sync_off_width_high & 0x3) << 4;
mode->vtotal = mode->vdisplay + dtd->part1.v_blank;
mode->vtotal += (dtd->part1.v_high & 0xf) << 8;
 
mode->clock = dtd->part1.clock * 10;
 
mode->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
if (dtd->part2.dtd_flags & 0x2)
mode->flags |= DRM_MODE_FLAG_PHSYNC;
if (dtd->part2.dtd_flags & 0x4)
mode->flags |= DRM_MODE_FLAG_PVSYNC;
}
 
static bool intel_sdvo_check_supp_encode(struct intel_sdvo *intel_sdvo)
{
struct intel_sdvo_encode encode;
 
BUILD_BUG_ON(sizeof(encode) != 2);
return intel_sdvo_get_value(intel_sdvo,
SDVO_CMD_GET_SUPP_ENCODE,
&encode, sizeof(encode));
}
 
static bool intel_sdvo_set_encode(struct intel_sdvo *intel_sdvo,
uint8_t mode)
{
return intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_ENCODE, &mode, 1);
}
 
static bool intel_sdvo_set_colorimetry(struct intel_sdvo *intel_sdvo,
uint8_t mode)
{
return intel_sdvo_set_value(intel_sdvo, SDVO_CMD_SET_COLORIMETRY, &mode, 1);
}
 
#if 0
static void intel_sdvo_dump_hdmi_buf(struct intel_sdvo *intel_sdvo)
{
int i, j;
uint8_t set_buf_index[2];
uint8_t av_split;
uint8_t buf_size;
uint8_t buf[48];
uint8_t *pos;
 
intel_sdvo_get_value(encoder, SDVO_CMD_GET_HBUF_AV_SPLIT, &av_split, 1);
 
for (i = 0; i <= av_split; i++) {
set_buf_index[0] = i; set_buf_index[1] = 0;
intel_sdvo_write_cmd(encoder, SDVO_CMD_SET_HBUF_INDEX,
set_buf_index, 2);
intel_sdvo_write_cmd(encoder, SDVO_CMD_GET_HBUF_INFO, NULL, 0);
intel_sdvo_read_response(encoder, &buf_size, 1);
 
pos = buf;
for (j = 0; j <= buf_size; j += 8) {
intel_sdvo_write_cmd(encoder, SDVO_CMD_GET_HBUF_DATA,
NULL, 0);
intel_sdvo_read_response(encoder, pos, 8);
pos += 8;
}
}
}
#endif
 
static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo)
{
struct dip_infoframe avi_if = {
.type = DIP_TYPE_AVI,
.ver = DIP_VERSION_AVI,
.len = DIP_LEN_AVI,
};
uint8_t tx_rate = SDVO_HBUF_TX_VSYNC;
uint8_t set_buf_index[2] = { 1, 0 };
uint64_t *data = (uint64_t *)&avi_if;
unsigned i;
 
intel_dip_infoframe_csum(&avi_if);
 
if (!intel_sdvo_set_value(intel_sdvo,
SDVO_CMD_SET_HBUF_INDEX,
set_buf_index, 2))
return false;
 
for (i = 0; i < sizeof(avi_if); i += 8) {
if (!intel_sdvo_set_value(intel_sdvo,
SDVO_CMD_SET_HBUF_DATA,
data, 8))
return false;
data++;
}
 
return intel_sdvo_set_value(intel_sdvo,
SDVO_CMD_SET_HBUF_TXRATE,
&tx_rate, 1);
}
 
static bool intel_sdvo_set_tv_format(struct intel_sdvo *intel_sdvo)
{
struct intel_sdvo_tv_format format;
uint32_t format_map;
 
format_map = 1 << intel_sdvo->tv_format_index;
memset(&format, 0, sizeof(format));
memcpy(&format, &format_map, min(sizeof(format), sizeof(format_map)));
 
BUILD_BUG_ON(sizeof(format) != 6);
return intel_sdvo_set_value(intel_sdvo,
SDVO_CMD_SET_TV_FORMAT,
&format, sizeof(format));
}
 
static bool
intel_sdvo_set_output_timings_from_mode(struct intel_sdvo *intel_sdvo,
struct drm_display_mode *mode)
{
struct intel_sdvo_dtd output_dtd;
 
if (!intel_sdvo_set_target_output(intel_sdvo,
intel_sdvo->attached_output))
return false;
 
intel_sdvo_get_dtd_from_mode(&output_dtd, mode);
if (!intel_sdvo_set_output_timing(intel_sdvo, &output_dtd))
return false;
 
return true;
}
 
static bool
intel_sdvo_set_input_timings_for_mode(struct intel_sdvo *intel_sdvo,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
/* Reset the input timing to the screen. Assume always input 0. */
if (!intel_sdvo_set_target_input(intel_sdvo))
return false;
 
if (!intel_sdvo_create_preferred_input_timing(intel_sdvo,
mode->clock / 10,
mode->hdisplay,
mode->vdisplay))
return false;
 
if (!intel_sdvo_get_preferred_input_timing(intel_sdvo,
&intel_sdvo->input_dtd))
return false;
 
intel_sdvo_get_mode_from_dtd(adjusted_mode, &intel_sdvo->input_dtd);
 
drm_mode_set_crtcinfo(adjusted_mode, 0);
return true;
}
 
static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder);
int multiplier;
 
/* We need to construct preferred input timings based on our
* output timings. To do that, we have to set the output
* timings, even though this isn't really the right place in
* the sequence to do it. Oh well.
*/
if (intel_sdvo->is_tv) {
if (!intel_sdvo_set_output_timings_from_mode(intel_sdvo, mode))
return false;
 
(void) intel_sdvo_set_input_timings_for_mode(intel_sdvo,
mode,
adjusted_mode);
} else if (intel_sdvo->is_lvds) {
if (!intel_sdvo_set_output_timings_from_mode(intel_sdvo,
intel_sdvo->sdvo_lvds_fixed_mode))
return false;
 
(void) intel_sdvo_set_input_timings_for_mode(intel_sdvo,
mode,
adjusted_mode);
}
 
/* Make the CRTC code factor in the SDVO pixel multiplier. The
* SDVO device will factor out the multiplier during mode_set.
*/
multiplier = intel_sdvo_get_pixel_multiplier(adjusted_mode);
intel_mode_set_pixel_multiplier(adjusted_mode, multiplier);
 
return true;
}
 
static void intel_sdvo_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc = encoder->crtc;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder);
u32 sdvox;
struct intel_sdvo_in_out_map in_out;
struct intel_sdvo_dtd input_dtd;
int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
int rate;
 
if (!mode)
return;
 
/* First, set the input mapping for the first input to our controlled
* output. This is only correct if we're a single-input device, in
* which case the first input is the output from the appropriate SDVO
* channel on the motherboard. In a two-input device, the first input
* will be SDVOB and the second SDVOC.
*/
in_out.in0 = intel_sdvo->attached_output;
in_out.in1 = 0;
 
intel_sdvo_set_value(intel_sdvo,
SDVO_CMD_SET_IN_OUT_MAP,
&in_out, sizeof(in_out));
 
/* Set the output timings to the screen */
if (!intel_sdvo_set_target_output(intel_sdvo,
intel_sdvo->attached_output))
return;
 
/* We have tried to get input timing in mode_fixup, and filled into
* adjusted_mode.
*/
if (intel_sdvo->is_tv || intel_sdvo->is_lvds) {
input_dtd = intel_sdvo->input_dtd;
} else {
/* Set the output timing to the screen */
if (!intel_sdvo_set_target_output(intel_sdvo,
intel_sdvo->attached_output))
return;
 
intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode);
(void) intel_sdvo_set_output_timing(intel_sdvo, &input_dtd);
}
 
/* Set the input timing to the screen. Assume always input 0. */
if (!intel_sdvo_set_target_input(intel_sdvo))
return;
 
if (intel_sdvo->has_hdmi_monitor) {
intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_HDMI);
intel_sdvo_set_colorimetry(intel_sdvo,
SDVO_COLORIMETRY_RGB256);
intel_sdvo_set_avi_infoframe(intel_sdvo);
} else
intel_sdvo_set_encode(intel_sdvo, SDVO_ENCODE_DVI);
 
if (intel_sdvo->is_tv &&
!intel_sdvo_set_tv_format(intel_sdvo))
return;
 
(void) intel_sdvo_set_input_timing(intel_sdvo, &input_dtd);
 
switch (pixel_multiplier) {
default:
case 1: rate = SDVO_CLOCK_RATE_MULT_1X; break;
case 2: rate = SDVO_CLOCK_RATE_MULT_2X; break;
case 4: rate = SDVO_CLOCK_RATE_MULT_4X; break;
}
if (!intel_sdvo_set_clock_rate_mult(intel_sdvo, rate))
return;
 
/* Set the SDVO control regs. */
if (INTEL_INFO(dev)->gen >= 4) {
sdvox = 0;
if (intel_sdvo->is_hdmi)
sdvox |= intel_sdvo->color_range;
if (INTEL_INFO(dev)->gen < 5)
sdvox |= SDVO_BORDER_ENABLE;
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
sdvox |= SDVO_VSYNC_ACTIVE_HIGH;
if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
sdvox |= SDVO_HSYNC_ACTIVE_HIGH;
} else {
sdvox = I915_READ(intel_sdvo->sdvo_reg);
switch (intel_sdvo->sdvo_reg) {
case SDVOB:
sdvox &= SDVOB_PRESERVE_MASK;
break;
case SDVOC:
sdvox &= SDVOC_PRESERVE_MASK;
break;
}
sdvox |= (9 << 19) | SDVO_BORDER_ENABLE;
}
if (intel_crtc->pipe == 1)
sdvox |= SDVO_PIPE_B_SELECT;
if (intel_sdvo->has_hdmi_audio)
sdvox |= SDVO_AUDIO_ENABLE;
 
if (INTEL_INFO(dev)->gen >= 4) {
/* done in crtc_mode_set as the dpll_md reg must be written early */
} else if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) {
/* done in crtc_mode_set as it lives inside the dpll register */
} else {
sdvox |= (pixel_multiplier - 1) << SDVO_PORT_MULTIPLY_SHIFT;
}
 
if (input_dtd.part2.sdvo_flags & SDVO_NEED_TO_STALL &&
INTEL_INFO(dev)->gen < 5)
sdvox |= SDVO_STALL_SELECT;
intel_sdvo_write_sdvox(intel_sdvo, sdvox);
}
 
static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder);
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
u32 temp;
 
if (mode != DRM_MODE_DPMS_ON) {
intel_sdvo_set_active_outputs(intel_sdvo, 0);
if (0)
intel_sdvo_set_encoder_power_state(intel_sdvo, mode);
 
if (mode == DRM_MODE_DPMS_OFF) {
temp = I915_READ(intel_sdvo->sdvo_reg);
if ((temp & SDVO_ENABLE) != 0) {
intel_sdvo_write_sdvox(intel_sdvo, temp & ~SDVO_ENABLE);
}
}
} else {
bool input1, input2;
int i;
u8 status;
 
temp = I915_READ(intel_sdvo->sdvo_reg);
if ((temp & SDVO_ENABLE) == 0)
intel_sdvo_write_sdvox(intel_sdvo, temp | SDVO_ENABLE);
for (i = 0; i < 2; i++)
intel_wait_for_vblank(dev, intel_crtc->pipe);
 
status = intel_sdvo_get_trained_inputs(intel_sdvo, &input1, &input2);
/* Warn if the device reported failure to sync.
* A lot of SDVO devices fail to notify of sync, but it's
* a given it the status is a success, we succeeded.
*/
if (status == SDVO_CMD_STATUS_SUCCESS && !input1) {
DRM_DEBUG_KMS("First %s output reported failure to "
"sync\n", SDVO_NAME(intel_sdvo));
}
 
if (0)
intel_sdvo_set_encoder_power_state(intel_sdvo, mode);
intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output);
}
return;
}
 
static int intel_sdvo_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
 
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
 
if (intel_sdvo->pixel_clock_min > mode->clock)
return MODE_CLOCK_LOW;
 
if (intel_sdvo->pixel_clock_max < mode->clock)
return MODE_CLOCK_HIGH;
 
if (intel_sdvo->is_lvds) {
if (mode->hdisplay > intel_sdvo->sdvo_lvds_fixed_mode->hdisplay)
return MODE_PANEL;
 
if (mode->vdisplay > intel_sdvo->sdvo_lvds_fixed_mode->vdisplay)
return MODE_PANEL;
}
 
return MODE_OK;
}
 
static bool intel_sdvo_get_capabilities(struct intel_sdvo *intel_sdvo, struct intel_sdvo_caps *caps)
{
BUILD_BUG_ON(sizeof(*caps) != 8);
if (!intel_sdvo_get_value(intel_sdvo,
SDVO_CMD_GET_DEVICE_CAPS,
caps, sizeof(*caps)))
return false;
 
DRM_DEBUG_KMS("SDVO capabilities:\n"
" vendor_id: %d\n"
" device_id: %d\n"
" device_rev_id: %d\n"
" sdvo_version_major: %d\n"
" sdvo_version_minor: %d\n"
" sdvo_inputs_mask: %d\n"
" smooth_scaling: %d\n"
" sharp_scaling: %d\n"
" up_scaling: %d\n"
" down_scaling: %d\n"
" stall_support: %d\n"
" output_flags: %d\n",
caps->vendor_id,
caps->device_id,
caps->device_rev_id,
caps->sdvo_version_major,
caps->sdvo_version_minor,
caps->sdvo_inputs_mask,
caps->smooth_scaling,
caps->sharp_scaling,
caps->up_scaling,
caps->down_scaling,
caps->stall_support,
caps->output_flags);
 
return true;
}
 
/* No use! */
#if 0
struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB)
{
struct drm_connector *connector = NULL;
struct intel_sdvo *iout = NULL;
struct intel_sdvo *sdvo;
 
/* find the sdvo connector */
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
iout = to_intel_sdvo(connector);
 
if (iout->type != INTEL_OUTPUT_SDVO)
continue;
 
sdvo = iout->dev_priv;
 
if (sdvo->sdvo_reg == SDVOB && sdvoB)
return connector;
 
if (sdvo->sdvo_reg == SDVOC && !sdvoB)
return connector;
 
}
 
return NULL;
}
 
int intel_sdvo_supports_hotplug(struct drm_connector *connector)
{
u8 response[2];
u8 status;
struct intel_sdvo *intel_sdvo;
DRM_DEBUG_KMS("\n");
 
if (!connector)
return 0;
 
intel_sdvo = to_intel_sdvo(connector);
 
return intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT,
&response, 2) && response[0];
}
 
void intel_sdvo_set_hotplug(struct drm_connector *connector, int on)
{
u8 response[2];
u8 status;
struct intel_sdvo *intel_sdvo = to_intel_sdvo(connector);
 
intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0);
intel_sdvo_read_response(intel_sdvo, &response, 2);
 
if (on) {
intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT, NULL, 0);
status = intel_sdvo_read_response(intel_sdvo, &response, 2);
 
intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2);
} else {
response[0] = 0;
response[1] = 0;
intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &response, 2);
}
 
intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_GET_ACTIVE_HOT_PLUG, NULL, 0);
intel_sdvo_read_response(intel_sdvo, &response, 2);
}
#endif
 
static bool
intel_sdvo_multifunc_encoder(struct intel_sdvo *intel_sdvo)
{
/* Is there more than one type of output? */
int caps = intel_sdvo->caps.output_flags & 0xf;
return caps & -caps;
}
 
static struct edid *
intel_sdvo_get_edid(struct drm_connector *connector)
{
struct intel_sdvo *sdvo = intel_attached_sdvo(connector);
return drm_get_edid(connector, &sdvo->ddc);
}
 
/* Mac mini hack -- use the same DDC as the analog connector */
static struct edid *
intel_sdvo_get_analog_edid(struct drm_connector *connector)
{
struct drm_i915_private *dev_priv = connector->dev->dev_private;
 
return drm_get_edid(connector,
&dev_priv->gmbus[dev_priv->crt_ddc_pin].adapter);
}
 
enum drm_connector_status
intel_sdvo_hdmi_sink_detect(struct drm_connector *connector)
{
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
enum drm_connector_status status;
struct edid *edid;
 
edid = intel_sdvo_get_edid(connector);
 
if (edid == NULL && intel_sdvo_multifunc_encoder(intel_sdvo)) {
u8 ddc, saved_ddc = intel_sdvo->ddc_bus;
 
/*
* Don't use the 1 as the argument of DDC bus switch to get
* the EDID. It is used for SDVO SPD ROM.
*/
for (ddc = intel_sdvo->ddc_bus >> 1; ddc > 1; ddc >>= 1) {
intel_sdvo->ddc_bus = ddc;
edid = intel_sdvo_get_edid(connector);
if (edid)
break;
}
/*
* If we found the EDID on the other bus,
* assume that is the correct DDC bus.
*/
if (edid == NULL)
intel_sdvo->ddc_bus = saved_ddc;
}
 
/*
* When there is no edid and no monitor is connected with VGA
* port, try to use the CRT ddc to read the EDID for DVI-connector.
*/
if (edid == NULL)
edid = intel_sdvo_get_analog_edid(connector);
 
status = connector_status_unknown;
if (edid != NULL) {
/* DDC bus is shared, match EDID to connector type */
if (edid->input & DRM_EDID_INPUT_DIGITAL) {
status = connector_status_connected;
if (intel_sdvo->is_hdmi) {
intel_sdvo->has_hdmi_monitor = drm_detect_hdmi_monitor(edid);
intel_sdvo->has_hdmi_audio = drm_detect_monitor_audio(edid);
}
} else
status = connector_status_disconnected;
connector->display_info.raw_edid = NULL;
kfree(edid);
}
 
if (status == connector_status_connected) {
struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
if (intel_sdvo_connector->force_audio)
intel_sdvo->has_hdmi_audio = intel_sdvo_connector->force_audio > 0;
}
 
return status;
}
 
static enum drm_connector_status
intel_sdvo_detect(struct drm_connector *connector, bool force)
{
uint16_t response;
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
enum drm_connector_status ret;
 
if (!intel_sdvo_write_cmd(intel_sdvo,
SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0))
return connector_status_unknown;
 
/* add 30ms delay when the output type might be TV */
if (intel_sdvo->caps.output_flags &
(SDVO_OUTPUT_SVID0 | SDVO_OUTPUT_CVBS0))
mdelay(30);
 
if (!intel_sdvo_read_response(intel_sdvo, &response, 2))
return connector_status_unknown;
 
DRM_DEBUG_KMS("SDVO response %d %d [%x]\n",
response & 0xff, response >> 8,
intel_sdvo_connector->output_flag);
 
if (response == 0)
return connector_status_disconnected;
 
intel_sdvo->attached_output = response;
 
intel_sdvo->has_hdmi_monitor = false;
intel_sdvo->has_hdmi_audio = false;
 
if ((intel_sdvo_connector->output_flag & response) == 0)
ret = connector_status_disconnected;
else if (IS_TMDS(intel_sdvo_connector))
ret = intel_sdvo_hdmi_sink_detect(connector);
else {
struct edid *edid;
 
/* if we have an edid check it matches the connection */
edid = intel_sdvo_get_edid(connector);
if (edid == NULL)
edid = intel_sdvo_get_analog_edid(connector);
if (edid != NULL) {
if (edid->input & DRM_EDID_INPUT_DIGITAL)
ret = connector_status_disconnected;
else
ret = connector_status_connected;
connector->display_info.raw_edid = NULL;
kfree(edid);
} else
ret = connector_status_connected;
}
 
/* May update encoder flag for like clock for SDVO TV, etc.*/
if (ret == connector_status_connected) {
intel_sdvo->is_tv = false;
intel_sdvo->is_lvds = false;
intel_sdvo->base.needs_tv_clock = false;
 
if (response & SDVO_TV_MASK) {
intel_sdvo->is_tv = true;
intel_sdvo->base.needs_tv_clock = true;
}
if (response & SDVO_LVDS_MASK)
intel_sdvo->is_lvds = intel_sdvo->sdvo_lvds_fixed_mode != NULL;
}
 
return ret;
}
 
static void intel_sdvo_get_ddc_modes(struct drm_connector *connector)
{
struct edid *edid;
 
/* set the bus switch and get the modes */
edid = intel_sdvo_get_edid(connector);
 
/*
* Mac mini hack. On this device, the DVI-I connector shares one DDC
* link between analog and digital outputs. So, if the regular SDVO
* DDC fails, check to see if the analog output is disconnected, in
* which case we'll look there for the digital DDC data.
*/
if (edid == NULL)
edid = intel_sdvo_get_analog_edid(connector);
 
if (edid != NULL) {
struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
bool monitor_is_digital = !!(edid->input & DRM_EDID_INPUT_DIGITAL);
bool connector_is_digital = !!IS_TMDS(intel_sdvo_connector);
 
if (connector_is_digital == monitor_is_digital) {
drm_mode_connector_update_edid_property(connector, edid);
drm_add_edid_modes(connector, edid);
}
 
connector->display_info.raw_edid = NULL;
kfree(edid);
}
}
 
/*
* Set of SDVO TV modes.
* Note! This is in reply order (see loop in get_tv_modes).
* XXX: all 60Hz refresh?
*/
static const struct drm_display_mode sdvo_tv_modes[] = {
{ DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 5815, 320, 321, 384,
416, 0, 200, 201, 232, 233, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 6814, 320, 321, 384,
416, 0, 240, 241, 272, 273, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 9910, 400, 401, 464,
496, 0, 300, 301, 332, 333, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("640x350", DRM_MODE_TYPE_DRIVER, 16913, 640, 641, 704,
736, 0, 350, 351, 382, 383, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("640x400", DRM_MODE_TYPE_DRIVER, 19121, 640, 641, 704,
736, 0, 400, 401, 432, 433, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 22654, 640, 641, 704,
736, 0, 480, 481, 512, 513, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("704x480", DRM_MODE_TYPE_DRIVER, 24624, 704, 705, 768,
800, 0, 480, 481, 512, 513, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("704x576", DRM_MODE_TYPE_DRIVER, 29232, 704, 705, 768,
800, 0, 576, 577, 608, 609, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("720x350", DRM_MODE_TYPE_DRIVER, 18751, 720, 721, 784,
816, 0, 350, 351, 382, 383, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("720x400", DRM_MODE_TYPE_DRIVER, 21199, 720, 721, 784,
816, 0, 400, 401, 432, 433, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 25116, 720, 721, 784,
816, 0, 480, 481, 512, 513, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("720x540", DRM_MODE_TYPE_DRIVER, 28054, 720, 721, 784,
816, 0, 540, 541, 572, 573, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 29816, 720, 721, 784,
816, 0, 576, 577, 608, 609, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("768x576", DRM_MODE_TYPE_DRIVER, 31570, 768, 769, 832,
864, 0, 576, 577, 608, 609, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 34030, 800, 801, 864,
896, 0, 600, 601, 632, 633, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 36581, 832, 833, 896,
928, 0, 624, 625, 656, 657, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("920x766", DRM_MODE_TYPE_DRIVER, 48707, 920, 921, 984,
1016, 0, 766, 767, 798, 799, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 53827, 1024, 1025, 1088,
1120, 0, 768, 769, 800, 801, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 87265, 1280, 1281, 1344,
1376, 0, 1024, 1025, 1056, 1057, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
};
 
static void intel_sdvo_get_tv_modes(struct drm_connector *connector)
{
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
struct intel_sdvo_sdtv_resolution_request tv_res;
uint32_t reply = 0, format_map = 0;
int i;
 
/* Read the list of supported input resolutions for the selected TV
* format.
*/
format_map = 1 << intel_sdvo->tv_format_index;
memcpy(&tv_res, &format_map,
min(sizeof(format_map), sizeof(struct intel_sdvo_sdtv_resolution_request)));
 
if (!intel_sdvo_set_target_output(intel_sdvo, intel_sdvo->attached_output))
return;
 
BUILD_BUG_ON(sizeof(tv_res) != 3);
if (!intel_sdvo_write_cmd(intel_sdvo,
SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT,
&tv_res, sizeof(tv_res)))
return;
if (!intel_sdvo_read_response(intel_sdvo, &reply, 3))
return;
 
for (i = 0; i < ARRAY_SIZE(sdvo_tv_modes); i++)
if (reply & (1 << i)) {
struct drm_display_mode *nmode;
nmode = drm_mode_duplicate(connector->dev,
&sdvo_tv_modes[i]);
if (nmode)
drm_mode_probed_add(connector, nmode);
}
}
 
static void intel_sdvo_get_lvds_modes(struct drm_connector *connector)
{
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
struct drm_i915_private *dev_priv = connector->dev->dev_private;
struct drm_display_mode *newmode;
 
/*
* Attempt to get the mode list from DDC.
* Assume that the preferred modes are
* arranged in priority order.
*/
intel_ddc_get_modes(connector, intel_sdvo->i2c);
if (list_empty(&connector->probed_modes) == false)
goto end;
 
/* Fetch modes from VBT */
if (dev_priv->sdvo_lvds_vbt_mode != NULL) {
newmode = drm_mode_duplicate(connector->dev,
dev_priv->sdvo_lvds_vbt_mode);
if (newmode != NULL) {
/* Guarantee the mode is preferred */
newmode->type = (DRM_MODE_TYPE_PREFERRED |
DRM_MODE_TYPE_DRIVER);
drm_mode_probed_add(connector, newmode);
}
}
 
end:
list_for_each_entry(newmode, &connector->probed_modes, head) {
if (newmode->type & DRM_MODE_TYPE_PREFERRED) {
intel_sdvo->sdvo_lvds_fixed_mode =
drm_mode_duplicate(connector->dev, newmode);
 
drm_mode_set_crtcinfo(intel_sdvo->sdvo_lvds_fixed_mode,
0);
 
intel_sdvo->is_lvds = true;
break;
}
}
 
}
 
static int intel_sdvo_get_modes(struct drm_connector *connector)
{
struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
 
if (IS_TV(intel_sdvo_connector))
intel_sdvo_get_tv_modes(connector);
else if (IS_LVDS(intel_sdvo_connector))
intel_sdvo_get_lvds_modes(connector);
else
intel_sdvo_get_ddc_modes(connector);
 
return !list_empty(&connector->probed_modes);
}
 
static void
intel_sdvo_destroy_enhance_property(struct drm_connector *connector)
{
struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
struct drm_device *dev = connector->dev;
 
if (intel_sdvo_connector->left)
drm_property_destroy(dev, intel_sdvo_connector->left);
if (intel_sdvo_connector->right)
drm_property_destroy(dev, intel_sdvo_connector->right);
if (intel_sdvo_connector->top)
drm_property_destroy(dev, intel_sdvo_connector->top);
if (intel_sdvo_connector->bottom)
drm_property_destroy(dev, intel_sdvo_connector->bottom);
if (intel_sdvo_connector->hpos)
drm_property_destroy(dev, intel_sdvo_connector->hpos);
if (intel_sdvo_connector->vpos)
drm_property_destroy(dev, intel_sdvo_connector->vpos);
if (intel_sdvo_connector->saturation)
drm_property_destroy(dev, intel_sdvo_connector->saturation);
if (intel_sdvo_connector->contrast)
drm_property_destroy(dev, intel_sdvo_connector->contrast);
if (intel_sdvo_connector->hue)
drm_property_destroy(dev, intel_sdvo_connector->hue);
if (intel_sdvo_connector->sharpness)
drm_property_destroy(dev, intel_sdvo_connector->sharpness);
if (intel_sdvo_connector->flicker_filter)
drm_property_destroy(dev, intel_sdvo_connector->flicker_filter);
if (intel_sdvo_connector->flicker_filter_2d)
drm_property_destroy(dev, intel_sdvo_connector->flicker_filter_2d);
if (intel_sdvo_connector->flicker_filter_adaptive)
drm_property_destroy(dev, intel_sdvo_connector->flicker_filter_adaptive);
if (intel_sdvo_connector->tv_luma_filter)
drm_property_destroy(dev, intel_sdvo_connector->tv_luma_filter);
if (intel_sdvo_connector->tv_chroma_filter)
drm_property_destroy(dev, intel_sdvo_connector->tv_chroma_filter);
if (intel_sdvo_connector->dot_crawl)
drm_property_destroy(dev, intel_sdvo_connector->dot_crawl);
if (intel_sdvo_connector->brightness)
drm_property_destroy(dev, intel_sdvo_connector->brightness);
}
 
static void intel_sdvo_destroy(struct drm_connector *connector)
{
struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
 
if (intel_sdvo_connector->tv_format)
drm_property_destroy(connector->dev,
intel_sdvo_connector->tv_format);
 
intel_sdvo_destroy_enhance_property(connector);
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
kfree(connector);
}
 
 
 
 
 
static int
intel_sdvo_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t val)
{
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
struct drm_i915_private *dev_priv = connector->dev->dev_private;
uint16_t temp_value;
uint8_t cmd;
int ret;
 
ret = drm_connector_property_set_value(connector, property, val);
if (ret)
return ret;
 
#if 0
if (property == dev_priv->force_audio_property) {
int i = val;
bool has_audio;
 
if (i == intel_sdvo_connector->force_audio)
return 0;
 
intel_sdvo_connector->force_audio = i;
 
if (i == 0)
has_audio = intel_sdvo_detect_hdmi_audio(connector);
else
has_audio = i > 0;
 
if (has_audio == intel_sdvo->has_hdmi_audio)
return 0;
 
intel_sdvo->has_hdmi_audio = has_audio;
goto done;
}
 
if (property == dev_priv->broadcast_rgb_property) {
if (val == !!intel_sdvo->color_range)
return 0;
 
intel_sdvo->color_range = val ? SDVO_COLOR_RANGE_16_235 : 0;
goto done;
}
#endif
 
#define CHECK_PROPERTY(name, NAME) \
if (intel_sdvo_connector->name == property) { \
if (intel_sdvo_connector->cur_##name == temp_value) return 0; \
if (intel_sdvo_connector->max_##name < temp_value) return -EINVAL; \
cmd = SDVO_CMD_SET_##NAME; \
intel_sdvo_connector->cur_##name = temp_value; \
goto set_value; \
}
 
if (property == intel_sdvo_connector->tv_format) {
if (val >= TV_FORMAT_NUM)
return -EINVAL;
 
if (intel_sdvo->tv_format_index ==
intel_sdvo_connector->tv_format_supported[val])
return 0;
 
intel_sdvo->tv_format_index = intel_sdvo_connector->tv_format_supported[val];
goto done;
} else if (IS_TV_OR_LVDS(intel_sdvo_connector)) {
temp_value = val;
if (intel_sdvo_connector->left == property) {
drm_connector_property_set_value(connector,
intel_sdvo_connector->right, val);
if (intel_sdvo_connector->left_margin == temp_value)
return 0;
 
intel_sdvo_connector->left_margin = temp_value;
intel_sdvo_connector->right_margin = temp_value;
temp_value = intel_sdvo_connector->max_hscan -
intel_sdvo_connector->left_margin;
cmd = SDVO_CMD_SET_OVERSCAN_H;
goto set_value;
} else if (intel_sdvo_connector->right == property) {
drm_connector_property_set_value(connector,
intel_sdvo_connector->left, val);
if (intel_sdvo_connector->right_margin == temp_value)
return 0;
 
intel_sdvo_connector->left_margin = temp_value;
intel_sdvo_connector->right_margin = temp_value;
temp_value = intel_sdvo_connector->max_hscan -
intel_sdvo_connector->left_margin;
cmd = SDVO_CMD_SET_OVERSCAN_H;
goto set_value;
} else if (intel_sdvo_connector->top == property) {
drm_connector_property_set_value(connector,
intel_sdvo_connector->bottom, val);
if (intel_sdvo_connector->top_margin == temp_value)
return 0;
 
intel_sdvo_connector->top_margin = temp_value;
intel_sdvo_connector->bottom_margin = temp_value;
temp_value = intel_sdvo_connector->max_vscan -
intel_sdvo_connector->top_margin;
cmd = SDVO_CMD_SET_OVERSCAN_V;
goto set_value;
} else if (intel_sdvo_connector->bottom == property) {
drm_connector_property_set_value(connector,
intel_sdvo_connector->top, val);
if (intel_sdvo_connector->bottom_margin == temp_value)
return 0;
 
intel_sdvo_connector->top_margin = temp_value;
intel_sdvo_connector->bottom_margin = temp_value;
temp_value = intel_sdvo_connector->max_vscan -
intel_sdvo_connector->top_margin;
cmd = SDVO_CMD_SET_OVERSCAN_V;
goto set_value;
}
CHECK_PROPERTY(hpos, HPOS)
CHECK_PROPERTY(vpos, VPOS)
CHECK_PROPERTY(saturation, SATURATION)
CHECK_PROPERTY(contrast, CONTRAST)
CHECK_PROPERTY(hue, HUE)
CHECK_PROPERTY(brightness, BRIGHTNESS)
CHECK_PROPERTY(sharpness, SHARPNESS)
CHECK_PROPERTY(flicker_filter, FLICKER_FILTER)
CHECK_PROPERTY(flicker_filter_2d, FLICKER_FILTER_2D)
CHECK_PROPERTY(flicker_filter_adaptive, FLICKER_FILTER_ADAPTIVE)
CHECK_PROPERTY(tv_chroma_filter, TV_CHROMA_FILTER)
CHECK_PROPERTY(tv_luma_filter, TV_LUMA_FILTER)
CHECK_PROPERTY(dot_crawl, DOT_CRAWL)
}
 
return -EINVAL; /* unknown property */
 
set_value:
if (!intel_sdvo_set_value(intel_sdvo, cmd, &temp_value, 2))
return -EIO;
 
 
done:
if (intel_sdvo->base.base.crtc) {
struct drm_crtc *crtc = intel_sdvo->base.base.crtc;
drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x,
crtc->y, crtc->fb);
}
 
return 0;
#undef CHECK_PROPERTY
}
 
static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = {
.dpms = intel_sdvo_dpms,
.mode_fixup = intel_sdvo_mode_fixup,
.prepare = intel_encoder_prepare,
.mode_set = intel_sdvo_mode_set,
.commit = intel_encoder_commit,
};
 
static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.detect = intel_sdvo_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = intel_sdvo_set_property,
.destroy = intel_sdvo_destroy,
};
 
static const struct drm_connector_helper_funcs intel_sdvo_connector_helper_funcs = {
.get_modes = intel_sdvo_get_modes,
.mode_valid = intel_sdvo_mode_valid,
.best_encoder = intel_best_encoder,
};
 
static void intel_sdvo_enc_destroy(struct drm_encoder *encoder)
{
struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder);
 
if (intel_sdvo->sdvo_lvds_fixed_mode != NULL)
drm_mode_destroy(encoder->dev,
intel_sdvo->sdvo_lvds_fixed_mode);
 
// i2c_del_adapter(&intel_sdvo->ddc);
intel_encoder_destroy(encoder);
}
 
static const struct drm_encoder_funcs intel_sdvo_enc_funcs = {
.destroy = intel_sdvo_enc_destroy,
};
 
static void
intel_sdvo_guess_ddc_bus(struct intel_sdvo *sdvo)
{
uint16_t mask = 0;
unsigned int num_bits;
 
/* Make a mask of outputs less than or equal to our own priority in the
* list.
*/
switch (sdvo->controlled_output) {
case SDVO_OUTPUT_LVDS1:
mask |= SDVO_OUTPUT_LVDS1;
case SDVO_OUTPUT_LVDS0:
mask |= SDVO_OUTPUT_LVDS0;
case SDVO_OUTPUT_TMDS1:
mask |= SDVO_OUTPUT_TMDS1;
case SDVO_OUTPUT_TMDS0:
mask |= SDVO_OUTPUT_TMDS0;
case SDVO_OUTPUT_RGB1:
mask |= SDVO_OUTPUT_RGB1;
case SDVO_OUTPUT_RGB0:
mask |= SDVO_OUTPUT_RGB0;
break;
}
 
/* Count bits to find what number we are in the priority list. */
mask &= sdvo->caps.output_flags;
num_bits = hweight16(mask);
/* If more than 3 outputs, default to DDC bus 3 for now. */
if (num_bits > 3)
num_bits = 3;
 
/* Corresponds to SDVO_CONTROL_BUS_DDCx */
sdvo->ddc_bus = 1 << num_bits;
}
 
/**
* Choose the appropriate DDC bus for control bus switch command for this
* SDVO output based on the controlled output.
*
* DDC bus number assignment is in a priority order of RGB outputs, then TMDS
* outputs, then LVDS outputs.
*/
static void
intel_sdvo_select_ddc_bus(struct drm_i915_private *dev_priv,
struct intel_sdvo *sdvo, u32 reg)
{
struct sdvo_device_mapping *mapping;
 
if (IS_SDVOB(reg))
mapping = &(dev_priv->sdvo_mappings[0]);
else
mapping = &(dev_priv->sdvo_mappings[1]);
 
if (mapping->initialized)
sdvo->ddc_bus = 1 << ((mapping->ddc_pin & 0xf0) >> 4);
else
intel_sdvo_guess_ddc_bus(sdvo);
}
 
static void
intel_sdvo_select_i2c_bus(struct drm_i915_private *dev_priv,
struct intel_sdvo *sdvo, u32 reg)
{
struct sdvo_device_mapping *mapping;
u8 pin, speed;
 
if (IS_SDVOB(reg))
mapping = &dev_priv->sdvo_mappings[0];
else
mapping = &dev_priv->sdvo_mappings[1];
 
pin = GMBUS_PORT_DPB;
speed = GMBUS_RATE_1MHZ >> 8;
if (mapping->initialized) {
pin = mapping->i2c_pin;
speed = mapping->i2c_speed;
}
 
if (pin < GMBUS_NUM_PORTS) {
sdvo->i2c = &dev_priv->gmbus[pin].adapter;
intel_gmbus_set_speed(sdvo->i2c, speed);
intel_gmbus_force_bit(sdvo->i2c, true);
} else
sdvo->i2c = &dev_priv->gmbus[GMBUS_PORT_DPB].adapter;
}
 
static bool
intel_sdvo_is_hdmi_connector(struct intel_sdvo *intel_sdvo, int device)
{
return intel_sdvo_check_supp_encode(intel_sdvo);
}
 
static u8
intel_sdvo_get_slave_addr(struct drm_device *dev, int sdvo_reg)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct sdvo_device_mapping *my_mapping, *other_mapping;
 
if (IS_SDVOB(sdvo_reg)) {
my_mapping = &dev_priv->sdvo_mappings[0];
other_mapping = &dev_priv->sdvo_mappings[1];
} else {
my_mapping = &dev_priv->sdvo_mappings[1];
other_mapping = &dev_priv->sdvo_mappings[0];
}
 
/* If the BIOS described our SDVO device, take advantage of it. */
if (my_mapping->slave_addr)
return my_mapping->slave_addr;
 
/* If the BIOS only described a different SDVO device, use the
* address that it isn't using.
*/
if (other_mapping->slave_addr) {
if (other_mapping->slave_addr == 0x70)
return 0x72;
else
return 0x70;
}
 
/* No SDVO device info is found for another DVO port,
* so use mapping assumption we had before BIOS parsing.
*/
if (IS_SDVOB(sdvo_reg))
return 0x70;
else
return 0x72;
}
 
static void
intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
struct intel_sdvo *encoder)
{
drm_connector_init(encoder->base.base.dev,
&connector->base.base,
&intel_sdvo_connector_funcs,
connector->base.base.connector_type);
 
drm_connector_helper_add(&connector->base.base,
&intel_sdvo_connector_helper_funcs);
 
connector->base.base.interlace_allowed = 0;
connector->base.base.doublescan_allowed = 0;
connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
 
intel_connector_attach_encoder(&connector->base, &encoder->base);
drm_sysfs_connector_add(&connector->base.base);
}
 
static void
intel_sdvo_add_hdmi_properties(struct intel_sdvo_connector *connector)
{
struct drm_device *dev = connector->base.base.dev;
 
intel_attach_force_audio_property(&connector->base.base);
if (INTEL_INFO(dev)->gen >= 4 && IS_MOBILE(dev))
intel_attach_broadcast_rgb_property(&connector->base.base);
}
 
static bool
intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
{
struct drm_encoder *encoder = &intel_sdvo->base.base;
struct drm_connector *connector;
struct intel_connector *intel_connector;
struct intel_sdvo_connector *intel_sdvo_connector;
 
intel_sdvo_connector = kzalloc(sizeof(struct intel_sdvo_connector), GFP_KERNEL);
if (!intel_sdvo_connector)
return false;
 
if (device == 0) {
intel_sdvo->controlled_output |= SDVO_OUTPUT_TMDS0;
intel_sdvo_connector->output_flag = SDVO_OUTPUT_TMDS0;
} else if (device == 1) {
intel_sdvo->controlled_output |= SDVO_OUTPUT_TMDS1;
intel_sdvo_connector->output_flag = SDVO_OUTPUT_TMDS1;
}
 
intel_connector = &intel_sdvo_connector->base;
connector = &intel_connector->base;
connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
encoder->encoder_type = DRM_MODE_ENCODER_TMDS;
connector->connector_type = DRM_MODE_CONNECTOR_DVID;
 
if (intel_sdvo_is_hdmi_connector(intel_sdvo, device)) {
connector->connector_type = DRM_MODE_CONNECTOR_HDMIA;
intel_sdvo->is_hdmi = true;
}
intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
(1 << INTEL_ANALOG_CLONE_BIT));
 
intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
if (intel_sdvo->is_hdmi)
intel_sdvo_add_hdmi_properties(intel_sdvo_connector);
 
return true;
}
 
static bool
intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type)
{
struct drm_encoder *encoder = &intel_sdvo->base.base;
struct drm_connector *connector;
struct intel_connector *intel_connector;
struct intel_sdvo_connector *intel_sdvo_connector;
 
intel_sdvo_connector = kzalloc(sizeof(struct intel_sdvo_connector), GFP_KERNEL);
if (!intel_sdvo_connector)
return false;
 
intel_connector = &intel_sdvo_connector->base;
connector = &intel_connector->base;
encoder->encoder_type = DRM_MODE_ENCODER_TVDAC;
connector->connector_type = DRM_MODE_CONNECTOR_SVIDEO;
 
intel_sdvo->controlled_output |= type;
intel_sdvo_connector->output_flag = type;
 
intel_sdvo->is_tv = true;
intel_sdvo->base.needs_tv_clock = true;
intel_sdvo->base.clone_mask = 1 << INTEL_SDVO_TV_CLONE_BIT;
 
intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
 
if (!intel_sdvo_tv_create_property(intel_sdvo, intel_sdvo_connector, type))
goto err;
 
if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector))
goto err;
 
return true;
 
err:
intel_sdvo_destroy(connector);
return false;
}
 
static bool
intel_sdvo_analog_init(struct intel_sdvo *intel_sdvo, int device)
{
struct drm_encoder *encoder = &intel_sdvo->base.base;
struct drm_connector *connector;
struct intel_connector *intel_connector;
struct intel_sdvo_connector *intel_sdvo_connector;
 
intel_sdvo_connector = kzalloc(sizeof(struct intel_sdvo_connector), GFP_KERNEL);
if (!intel_sdvo_connector)
return false;
 
intel_connector = &intel_sdvo_connector->base;
connector = &intel_connector->base;
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
encoder->encoder_type = DRM_MODE_ENCODER_DAC;
connector->connector_type = DRM_MODE_CONNECTOR_VGA;
 
if (device == 0) {
intel_sdvo->controlled_output |= SDVO_OUTPUT_RGB0;
intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB0;
} else if (device == 1) {
intel_sdvo->controlled_output |= SDVO_OUTPUT_RGB1;
intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB1;
}
 
intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
(1 << INTEL_ANALOG_CLONE_BIT));
 
intel_sdvo_connector_init(intel_sdvo_connector,
intel_sdvo);
return true;
}
 
static bool
intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device)
{
struct drm_encoder *encoder = &intel_sdvo->base.base;
struct drm_connector *connector;
struct intel_connector *intel_connector;
struct intel_sdvo_connector *intel_sdvo_connector;
 
intel_sdvo_connector = kzalloc(sizeof(struct intel_sdvo_connector), GFP_KERNEL);
if (!intel_sdvo_connector)
return false;
 
intel_connector = &intel_sdvo_connector->base;
connector = &intel_connector->base;
encoder->encoder_type = DRM_MODE_ENCODER_LVDS;
connector->connector_type = DRM_MODE_CONNECTOR_LVDS;
 
if (device == 0) {
intel_sdvo->controlled_output |= SDVO_OUTPUT_LVDS0;
intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS0;
} else if (device == 1) {
intel_sdvo->controlled_output |= SDVO_OUTPUT_LVDS1;
intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS1;
}
 
intel_sdvo->base.clone_mask = ((1 << INTEL_ANALOG_CLONE_BIT) |
(1 << INTEL_SDVO_LVDS_CLONE_BIT));
 
intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector))
goto err;
 
return true;
 
err:
intel_sdvo_destroy(connector);
return false;
}
 
static bool
intel_sdvo_output_setup(struct intel_sdvo *intel_sdvo, uint16_t flags)
{
intel_sdvo->is_tv = false;
intel_sdvo->base.needs_tv_clock = false;
intel_sdvo->is_lvds = false;
 
/* SDVO requires XXX1 function may not exist unless it has XXX0 function.*/
 
if (flags & SDVO_OUTPUT_TMDS0)
if (!intel_sdvo_dvi_init(intel_sdvo, 0))
return false;
 
if ((flags & SDVO_TMDS_MASK) == SDVO_TMDS_MASK)
if (!intel_sdvo_dvi_init(intel_sdvo, 1))
return false;
 
/* TV has no XXX1 function block */
if (flags & SDVO_OUTPUT_SVID0)
if (!intel_sdvo_tv_init(intel_sdvo, SDVO_OUTPUT_SVID0))
return false;
 
if (flags & SDVO_OUTPUT_CVBS0)
if (!intel_sdvo_tv_init(intel_sdvo, SDVO_OUTPUT_CVBS0))
return false;
 
if (flags & SDVO_OUTPUT_RGB0)
if (!intel_sdvo_analog_init(intel_sdvo, 0))
return false;
 
if ((flags & SDVO_RGB_MASK) == SDVO_RGB_MASK)
if (!intel_sdvo_analog_init(intel_sdvo, 1))
return false;
 
if (flags & SDVO_OUTPUT_LVDS0)
if (!intel_sdvo_lvds_init(intel_sdvo, 0))
return false;
 
if ((flags & SDVO_LVDS_MASK) == SDVO_LVDS_MASK)
if (!intel_sdvo_lvds_init(intel_sdvo, 1))
return false;
 
if ((flags & SDVO_OUTPUT_MASK) == 0) {
unsigned char bytes[2];
 
intel_sdvo->controlled_output = 0;
memcpy(bytes, &intel_sdvo->caps.output_flags, 2);
DRM_DEBUG_KMS("%s: Unknown SDVO output type (0x%02x%02x)\n",
SDVO_NAME(intel_sdvo),
bytes[0], bytes[1]);
return false;
}
intel_sdvo->base.crtc_mask = (1 << 0) | (1 << 1);
 
return true;
}
 
static bool intel_sdvo_tv_create_property(struct intel_sdvo *intel_sdvo,
struct intel_sdvo_connector *intel_sdvo_connector,
int type)
{
struct drm_device *dev = intel_sdvo->base.base.dev;
struct intel_sdvo_tv_format format;
uint32_t format_map, i;
 
if (!intel_sdvo_set_target_output(intel_sdvo, type))
return false;
 
BUILD_BUG_ON(sizeof(format) != 6);
if (!intel_sdvo_get_value(intel_sdvo,
SDVO_CMD_GET_SUPPORTED_TV_FORMATS,
&format, sizeof(format)))
return false;
 
memcpy(&format_map, &format, min(sizeof(format_map), sizeof(format)));
 
if (format_map == 0)
return false;
 
intel_sdvo_connector->format_supported_num = 0;
for (i = 0 ; i < TV_FORMAT_NUM; i++)
if (format_map & (1 << i))
intel_sdvo_connector->tv_format_supported[intel_sdvo_connector->format_supported_num++] = i;
 
 
intel_sdvo_connector->tv_format =
drm_property_create(dev, DRM_MODE_PROP_ENUM,
"mode", intel_sdvo_connector->format_supported_num);
if (!intel_sdvo_connector->tv_format)
return false;
 
for (i = 0; i < intel_sdvo_connector->format_supported_num; i++)
drm_property_add_enum(
intel_sdvo_connector->tv_format, i,
i, tv_format_names[intel_sdvo_connector->tv_format_supported[i]]);
 
intel_sdvo->tv_format_index = intel_sdvo_connector->tv_format_supported[0];
drm_connector_attach_property(&intel_sdvo_connector->base.base,
intel_sdvo_connector->tv_format, 0);
return true;
 
}
 
#define ENHANCEMENT(name, NAME) do { \
if (enhancements.name) { \
if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_MAX_##NAME, &data_value, 4) || \
!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_##NAME, &response, 2)) \
return false; \
intel_sdvo_connector->max_##name = data_value[0]; \
intel_sdvo_connector->cur_##name = response; \
intel_sdvo_connector->name = \
drm_property_create(dev, DRM_MODE_PROP_RANGE, #name, 2); \
if (!intel_sdvo_connector->name) return false; \
intel_sdvo_connector->name->values[0] = 0; \
intel_sdvo_connector->name->values[1] = data_value[0]; \
drm_connector_attach_property(connector, \
intel_sdvo_connector->name, \
intel_sdvo_connector->cur_##name); \
DRM_DEBUG_KMS(#name ": max %d, default %d, current %d\n", \
data_value[0], data_value[1], response); \
} \
} while(0)
 
static bool
intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo,
struct intel_sdvo_connector *intel_sdvo_connector,
struct intel_sdvo_enhancements_reply enhancements)
{
struct drm_device *dev = intel_sdvo->base.base.dev;
struct drm_connector *connector = &intel_sdvo_connector->base.base;
uint16_t response, data_value[2];
 
/* when horizontal overscan is supported, Add the left/right property */
if (enhancements.overscan_h) {
if (!intel_sdvo_get_value(intel_sdvo,
SDVO_CMD_GET_MAX_OVERSCAN_H,
&data_value, 4))
return false;
 
if (!intel_sdvo_get_value(intel_sdvo,
SDVO_CMD_GET_OVERSCAN_H,
&response, 2))
return false;
 
intel_sdvo_connector->max_hscan = data_value[0];
intel_sdvo_connector->left_margin = data_value[0] - response;
intel_sdvo_connector->right_margin = intel_sdvo_connector->left_margin;
intel_sdvo_connector->left =
drm_property_create(dev, DRM_MODE_PROP_RANGE,
"left_margin", 2);
if (!intel_sdvo_connector->left)
return false;
 
intel_sdvo_connector->left->values[0] = 0;
intel_sdvo_connector->left->values[1] = data_value[0];
drm_connector_attach_property(connector,
intel_sdvo_connector->left,
intel_sdvo_connector->left_margin);
 
intel_sdvo_connector->right =
drm_property_create(dev, DRM_MODE_PROP_RANGE,
"right_margin", 2);
if (!intel_sdvo_connector->right)
return false;
 
intel_sdvo_connector->right->values[0] = 0;
intel_sdvo_connector->right->values[1] = data_value[0];
drm_connector_attach_property(connector,
intel_sdvo_connector->right,
intel_sdvo_connector->right_margin);
DRM_DEBUG_KMS("h_overscan: max %d, "
"default %d, current %d\n",
data_value[0], data_value[1], response);
}
 
if (enhancements.overscan_v) {
if (!intel_sdvo_get_value(intel_sdvo,
SDVO_CMD_GET_MAX_OVERSCAN_V,
&data_value, 4))
return false;
 
if (!intel_sdvo_get_value(intel_sdvo,
SDVO_CMD_GET_OVERSCAN_V,
&response, 2))
return false;
 
intel_sdvo_connector->max_vscan = data_value[0];
intel_sdvo_connector->top_margin = data_value[0] - response;
intel_sdvo_connector->bottom_margin = intel_sdvo_connector->top_margin;
intel_sdvo_connector->top =
drm_property_create(dev, DRM_MODE_PROP_RANGE,
"top_margin", 2);
if (!intel_sdvo_connector->top)
return false;
 
intel_sdvo_connector->top->values[0] = 0;
intel_sdvo_connector->top->values[1] = data_value[0];
drm_connector_attach_property(connector,
intel_sdvo_connector->top,
intel_sdvo_connector->top_margin);
 
intel_sdvo_connector->bottom =
drm_property_create(dev, DRM_MODE_PROP_RANGE,
"bottom_margin", 2);
if (!intel_sdvo_connector->bottom)
return false;
 
intel_sdvo_connector->bottom->values[0] = 0;
intel_sdvo_connector->bottom->values[1] = data_value[0];
drm_connector_attach_property(connector,
intel_sdvo_connector->bottom,
intel_sdvo_connector->bottom_margin);
DRM_DEBUG_KMS("v_overscan: max %d, "
"default %d, current %d\n",
data_value[0], data_value[1], response);
}
 
ENHANCEMENT(hpos, HPOS);
ENHANCEMENT(vpos, VPOS);
ENHANCEMENT(saturation, SATURATION);
ENHANCEMENT(contrast, CONTRAST);
ENHANCEMENT(hue, HUE);
ENHANCEMENT(sharpness, SHARPNESS);
ENHANCEMENT(brightness, BRIGHTNESS);
ENHANCEMENT(flicker_filter, FLICKER_FILTER);
ENHANCEMENT(flicker_filter_adaptive, FLICKER_FILTER_ADAPTIVE);
ENHANCEMENT(flicker_filter_2d, FLICKER_FILTER_2D);
ENHANCEMENT(tv_chroma_filter, TV_CHROMA_FILTER);
ENHANCEMENT(tv_luma_filter, TV_LUMA_FILTER);
 
if (enhancements.dot_crawl) {
if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_DOT_CRAWL, &response, 2))
return false;
 
intel_sdvo_connector->max_dot_crawl = 1;
intel_sdvo_connector->cur_dot_crawl = response & 0x1;
intel_sdvo_connector->dot_crawl =
drm_property_create(dev, DRM_MODE_PROP_RANGE, "dot_crawl", 2);
if (!intel_sdvo_connector->dot_crawl)
return false;
 
intel_sdvo_connector->dot_crawl->values[0] = 0;
intel_sdvo_connector->dot_crawl->values[1] = 1;
drm_connector_attach_property(connector,
intel_sdvo_connector->dot_crawl,
intel_sdvo_connector->cur_dot_crawl);
DRM_DEBUG_KMS("dot crawl: current %d\n", response);
}
 
return true;
}
 
static bool
intel_sdvo_create_enhance_property_lvds(struct intel_sdvo *intel_sdvo,
struct intel_sdvo_connector *intel_sdvo_connector,
struct intel_sdvo_enhancements_reply enhancements)
{
struct drm_device *dev = intel_sdvo->base.base.dev;
struct drm_connector *connector = &intel_sdvo_connector->base.base;
uint16_t response, data_value[2];
 
ENHANCEMENT(brightness, BRIGHTNESS);
 
return true;
}
#undef ENHANCEMENT
 
static bool intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo,
struct intel_sdvo_connector *intel_sdvo_connector)
{
union {
struct intel_sdvo_enhancements_reply reply;
uint16_t response;
} enhancements;
 
BUILD_BUG_ON(sizeof(enhancements) != 2);
 
enhancements.response = 0;
intel_sdvo_get_value(intel_sdvo,
SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS,
&enhancements, sizeof(enhancements));
if (enhancements.response == 0) {
DRM_DEBUG_KMS("No enhancement is supported\n");
return true;
}
 
if (IS_TV(intel_sdvo_connector))
return intel_sdvo_create_enhance_property_tv(intel_sdvo, intel_sdvo_connector, enhancements.reply);
else if(IS_LVDS(intel_sdvo_connector))
return intel_sdvo_create_enhance_property_lvds(intel_sdvo, intel_sdvo_connector, enhancements.reply);
else
return true;
}
 
static int intel_sdvo_ddc_proxy_xfer(struct i2c_adapter *adapter,
struct i2c_msg *msgs,
int num)
{
struct intel_sdvo *sdvo = adapter->algo_data;
 
if (!intel_sdvo_set_control_bus_switch(sdvo, sdvo->ddc_bus))
return -EIO;
 
return sdvo->i2c->algo->master_xfer(sdvo->i2c, msgs, num);
}
 
static u32 intel_sdvo_ddc_proxy_func(struct i2c_adapter *adapter)
{
struct intel_sdvo *sdvo = adapter->algo_data;
return sdvo->i2c->algo->functionality(sdvo->i2c);
}
 
static const struct i2c_algorithm intel_sdvo_ddc_proxy = {
.master_xfer = intel_sdvo_ddc_proxy_xfer,
.functionality = intel_sdvo_ddc_proxy_func
};
 
static bool
intel_sdvo_init_ddc_proxy(struct intel_sdvo *sdvo,
struct drm_device *dev)
{
// sdvo->ddc.owner = THIS_MODULE;
sdvo->ddc.class = I2C_CLASS_DDC;
snprintf(sdvo->ddc.name, I2C_NAME_SIZE, "SDVO DDC proxy");
sdvo->ddc.dev.parent = &dev->pdev->dev;
sdvo->ddc.algo_data = sdvo;
sdvo->ddc.algo = &intel_sdvo_ddc_proxy;
 
return 1; //i2c_add_adapter(&sdvo->ddc) == 0;
}
 
bool intel_sdvo_init(struct drm_device *dev, int sdvo_reg)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_encoder *intel_encoder;
struct intel_sdvo *intel_sdvo;
int i;
 
intel_sdvo = kzalloc(sizeof(struct intel_sdvo), GFP_KERNEL);
if (!intel_sdvo)
return false;
 
intel_sdvo->sdvo_reg = sdvo_reg;
intel_sdvo->slave_addr = intel_sdvo_get_slave_addr(dev, sdvo_reg) >> 1;
intel_sdvo_select_i2c_bus(dev_priv, intel_sdvo, sdvo_reg);
if (!intel_sdvo_init_ddc_proxy(intel_sdvo, dev)) {
kfree(intel_sdvo);
return false;
}
 
/* encoder type will be decided later */
intel_encoder = &intel_sdvo->base;
intel_encoder->type = INTEL_OUTPUT_SDVO;
drm_encoder_init(dev, &intel_encoder->base, &intel_sdvo_enc_funcs, 0);
 
/* Read the regs to test if we can talk to the device */
for (i = 0; i < 0x40; i++) {
u8 byte;
 
if (!intel_sdvo_read_byte(intel_sdvo, i, &byte)) {
DRM_DEBUG_KMS("No SDVO device found on SDVO%c\n",
IS_SDVOB(sdvo_reg) ? 'B' : 'C');
goto err;
}
}
 
if (IS_SDVOB(sdvo_reg))
dev_priv->hotplug_supported_mask |= SDVOB_HOTPLUG_INT_STATUS;
else
dev_priv->hotplug_supported_mask |= SDVOC_HOTPLUG_INT_STATUS;
 
drm_encoder_helper_add(&intel_encoder->base, &intel_sdvo_helper_funcs);
 
/* In default case sdvo lvds is false */
if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps))
goto err;
 
if (intel_sdvo_output_setup(intel_sdvo,
intel_sdvo->caps.output_flags) != true) {
DRM_DEBUG_KMS("SDVO output failed to setup on SDVO%c\n",
IS_SDVOB(sdvo_reg) ? 'B' : 'C');
goto err;
}
 
intel_sdvo_select_ddc_bus(dev_priv, intel_sdvo, sdvo_reg);
 
/* Set the input timing to the screen. Assume always input 0. */
if (!intel_sdvo_set_target_input(intel_sdvo))
goto err;
 
if (!intel_sdvo_get_input_pixel_clock_range(intel_sdvo,
&intel_sdvo->pixel_clock_min,
&intel_sdvo->pixel_clock_max))
goto err;
 
DRM_DEBUG_KMS("%s device VID/DID: %02X:%02X.%02X, "
"clock range %dMHz - %dMHz, "
"input 1: %c, input 2: %c, "
"output 1: %c, output 2: %c\n",
SDVO_NAME(intel_sdvo),
intel_sdvo->caps.vendor_id, intel_sdvo->caps.device_id,
intel_sdvo->caps.device_rev_id,
intel_sdvo->pixel_clock_min / 1000,
intel_sdvo->pixel_clock_max / 1000,
(intel_sdvo->caps.sdvo_inputs_mask & 0x1) ? 'Y' : 'N',
(intel_sdvo->caps.sdvo_inputs_mask & 0x2) ? 'Y' : 'N',
/* check currently supported outputs */
intel_sdvo->caps.output_flags &
(SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0) ? 'Y' : 'N',
intel_sdvo->caps.output_flags &
(SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1) ? 'Y' : 'N');
return true;
 
err:
drm_encoder_cleanup(&intel_encoder->base);
// i2c_del_adapter(&intel_sdvo->ddc);
kfree(intel_sdvo);
 
return false;
}
/drivers/video/drm/i915/intel_sdvo_regs.h
0,0 → 1,723
/*
* 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>
*/
 
/**
* @file SDVO command definitions and structures.
*/
 
#define SDVO_OUTPUT_FIRST (0)
#define SDVO_OUTPUT_TMDS0 (1 << 0)
#define SDVO_OUTPUT_RGB0 (1 << 1)
#define SDVO_OUTPUT_CVBS0 (1 << 2)
#define SDVO_OUTPUT_SVID0 (1 << 3)
#define SDVO_OUTPUT_YPRPB0 (1 << 4)
#define SDVO_OUTPUT_SCART0 (1 << 5)
#define SDVO_OUTPUT_LVDS0 (1 << 6)
#define SDVO_OUTPUT_TMDS1 (1 << 8)
#define SDVO_OUTPUT_RGB1 (1 << 9)
#define SDVO_OUTPUT_CVBS1 (1 << 10)
#define SDVO_OUTPUT_SVID1 (1 << 11)
#define SDVO_OUTPUT_YPRPB1 (1 << 12)
#define SDVO_OUTPUT_SCART1 (1 << 13)
#define SDVO_OUTPUT_LVDS1 (1 << 14)
#define SDVO_OUTPUT_LAST (14)
 
struct intel_sdvo_caps {
u8 vendor_id;
u8 device_id;
u8 device_rev_id;
u8 sdvo_version_major;
u8 sdvo_version_minor;
unsigned int sdvo_inputs_mask:2;
unsigned int smooth_scaling:1;
unsigned int sharp_scaling:1;
unsigned int up_scaling:1;
unsigned int down_scaling:1;
unsigned int stall_support:1;
unsigned int pad:1;
u16 output_flags;
} __attribute__((packed));
 
/** This matches the EDID DTD structure, more or less */
struct intel_sdvo_dtd {
struct {
u16 clock; /**< pixel clock, in 10kHz units */
u8 h_active; /**< lower 8 bits (pixels) */
u8 h_blank; /**< lower 8 bits (pixels) */
u8 h_high; /**< upper 4 bits each h_active, h_blank */
u8 v_active; /**< lower 8 bits (lines) */
u8 v_blank; /**< lower 8 bits (lines) */
u8 v_high; /**< upper 4 bits each v_active, v_blank */
} part1;
 
struct {
u8 h_sync_off; /**< lower 8 bits, from hblank start */
u8 h_sync_width; /**< lower 8 bits (pixels) */
/** lower 4 bits each vsync offset, vsync width */
u8 v_sync_off_width;
/**
* 2 high bits of hsync offset, 2 high bits of hsync width,
* bits 4-5 of vsync offset, and 2 high bits of vsync width.
*/
u8 sync_off_width_high;
u8 dtd_flags;
u8 sdvo_flags;
/** bits 6-7 of vsync offset at bits 6-7 */
u8 v_sync_off_high;
u8 reserved;
} part2;
} __attribute__((packed));
 
struct intel_sdvo_pixel_clock_range {
u16 min; /**< pixel clock, in 10kHz units */
u16 max; /**< pixel clock, in 10kHz units */
} __attribute__((packed));
 
struct intel_sdvo_preferred_input_timing_args {
u16 clock;
u16 width;
u16 height;
u8 interlace:1;
u8 scaled:1;
u8 pad:6;
} __attribute__((packed));
 
/* I2C registers for SDVO */
#define SDVO_I2C_ARG_0 0x07
#define SDVO_I2C_ARG_1 0x06
#define SDVO_I2C_ARG_2 0x05
#define SDVO_I2C_ARG_3 0x04
#define SDVO_I2C_ARG_4 0x03
#define SDVO_I2C_ARG_5 0x02
#define SDVO_I2C_ARG_6 0x01
#define SDVO_I2C_ARG_7 0x00
#define SDVO_I2C_OPCODE 0x08
#define SDVO_I2C_CMD_STATUS 0x09
#define SDVO_I2C_RETURN_0 0x0a
#define SDVO_I2C_RETURN_1 0x0b
#define SDVO_I2C_RETURN_2 0x0c
#define SDVO_I2C_RETURN_3 0x0d
#define SDVO_I2C_RETURN_4 0x0e
#define SDVO_I2C_RETURN_5 0x0f
#define SDVO_I2C_RETURN_6 0x10
#define SDVO_I2C_RETURN_7 0x11
#define SDVO_I2C_VENDOR_BEGIN 0x20
 
/* Status results */
#define SDVO_CMD_STATUS_POWER_ON 0x0
#define SDVO_CMD_STATUS_SUCCESS 0x1
#define SDVO_CMD_STATUS_NOTSUPP 0x2
#define SDVO_CMD_STATUS_INVALID_ARG 0x3
#define SDVO_CMD_STATUS_PENDING 0x4
#define SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED 0x5
#define SDVO_CMD_STATUS_SCALING_NOT_SUPP 0x6
 
/* SDVO commands, argument/result registers */
 
#define SDVO_CMD_RESET 0x01
 
/** Returns a struct intel_sdvo_caps */
#define SDVO_CMD_GET_DEVICE_CAPS 0x02
 
#define SDVO_CMD_GET_FIRMWARE_REV 0x86
# define SDVO_DEVICE_FIRMWARE_MINOR SDVO_I2C_RETURN_0
# define SDVO_DEVICE_FIRMWARE_MAJOR SDVO_I2C_RETURN_1
# define SDVO_DEVICE_FIRMWARE_PATCH SDVO_I2C_RETURN_2
 
/**
* Reports which inputs are trained (managed to sync).
*
* Devices must have trained within 2 vsyncs of a mode change.
*/
#define SDVO_CMD_GET_TRAINED_INPUTS 0x03
struct intel_sdvo_get_trained_inputs_response {
unsigned int input0_trained:1;
unsigned int input1_trained:1;
unsigned int pad:6;
} __attribute__((packed));
 
/** Returns a struct intel_sdvo_output_flags of active outputs. */
#define SDVO_CMD_GET_ACTIVE_OUTPUTS 0x04
 
/**
* Sets the current set of active outputs.
*
* Takes a struct intel_sdvo_output_flags. Must be preceded by a SET_IN_OUT_MAP
* on multi-output devices.
*/
#define SDVO_CMD_SET_ACTIVE_OUTPUTS 0x05
 
/**
* Returns the current mapping of SDVO inputs to outputs on the device.
*
* Returns two struct intel_sdvo_output_flags structures.
*/
#define SDVO_CMD_GET_IN_OUT_MAP 0x06
struct intel_sdvo_in_out_map {
u16 in0, in1;
};
 
/**
* Sets the current mapping of SDVO inputs to outputs on the device.
*
* Takes two struct i380_sdvo_output_flags structures.
*/
#define SDVO_CMD_SET_IN_OUT_MAP 0x07
 
/**
* Returns a struct intel_sdvo_output_flags of attached displays.
*/
#define SDVO_CMD_GET_ATTACHED_DISPLAYS 0x0b
 
/**
* Returns a struct intel_sdvo_ouptut_flags of displays supporting hot plugging.
*/
#define SDVO_CMD_GET_HOT_PLUG_SUPPORT 0x0c
 
/**
* Takes a struct intel_sdvo_output_flags.
*/
#define SDVO_CMD_SET_ACTIVE_HOT_PLUG 0x0d
 
/**
* Returns a struct intel_sdvo_output_flags of displays with hot plug
* interrupts enabled.
*/
#define SDVO_CMD_GET_ACTIVE_HOT_PLUG 0x0e
 
#define SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE 0x0f
struct intel_sdvo_get_interrupt_event_source_response {
u16 interrupt_status;
unsigned int ambient_light_interrupt:1;
unsigned int hdmi_audio_encrypt_change:1;
unsigned int pad:6;
} __attribute__((packed));
 
/**
* Selects which input is affected by future input commands.
*
* Commands affected include SET_INPUT_TIMINGS_PART[12],
* GET_INPUT_TIMINGS_PART[12], GET_PREFERRED_INPUT_TIMINGS_PART[12],
* GET_INPUT_PIXEL_CLOCK_RANGE, and CREATE_PREFERRED_INPUT_TIMINGS.
*/
#define SDVO_CMD_SET_TARGET_INPUT 0x10
struct intel_sdvo_set_target_input_args {
unsigned int target_1:1;
unsigned int pad:7;
} __attribute__((packed));
 
/**
* Takes a struct intel_sdvo_output_flags of which outputs are targeted by
* future output commands.
*
* Affected commands inclue SET_OUTPUT_TIMINGS_PART[12],
* GET_OUTPUT_TIMINGS_PART[12], and GET_OUTPUT_PIXEL_CLOCK_RANGE.
*/
#define SDVO_CMD_SET_TARGET_OUTPUT 0x11
 
#define SDVO_CMD_GET_INPUT_TIMINGS_PART1 0x12
#define SDVO_CMD_GET_INPUT_TIMINGS_PART2 0x13
#define SDVO_CMD_SET_INPUT_TIMINGS_PART1 0x14
#define SDVO_CMD_SET_INPUT_TIMINGS_PART2 0x15
#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART1 0x16
#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART2 0x17
#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART1 0x18
#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART2 0x19
/* Part 1 */
# define SDVO_DTD_CLOCK_LOW SDVO_I2C_ARG_0
# define SDVO_DTD_CLOCK_HIGH SDVO_I2C_ARG_1
# define SDVO_DTD_H_ACTIVE SDVO_I2C_ARG_2
# define SDVO_DTD_H_BLANK SDVO_I2C_ARG_3
# define SDVO_DTD_H_HIGH SDVO_I2C_ARG_4
# define SDVO_DTD_V_ACTIVE SDVO_I2C_ARG_5
# define SDVO_DTD_V_BLANK SDVO_I2C_ARG_6
# define SDVO_DTD_V_HIGH SDVO_I2C_ARG_7
/* Part 2 */
# define SDVO_DTD_HSYNC_OFF SDVO_I2C_ARG_0
# define SDVO_DTD_HSYNC_WIDTH SDVO_I2C_ARG_1
# define SDVO_DTD_VSYNC_OFF_WIDTH SDVO_I2C_ARG_2
# define SDVO_DTD_SYNC_OFF_WIDTH_HIGH SDVO_I2C_ARG_3
# define SDVO_DTD_DTD_FLAGS SDVO_I2C_ARG_4
# define SDVO_DTD_DTD_FLAG_INTERLACED (1 << 7)
# define SDVO_DTD_DTD_FLAG_STEREO_MASK (3 << 5)
# define SDVO_DTD_DTD_FLAG_INPUT_MASK (3 << 3)
# define SDVO_DTD_DTD_FLAG_SYNC_MASK (3 << 1)
# define SDVO_DTD_SDVO_FLAS SDVO_I2C_ARG_5
# define SDVO_DTD_SDVO_FLAG_STALL (1 << 7)
# define SDVO_DTD_SDVO_FLAG_CENTERED (0 << 6)
# define SDVO_DTD_SDVO_FLAG_UPPER_LEFT (1 << 6)
# define SDVO_DTD_SDVO_FLAG_SCALING_MASK (3 << 4)
# define SDVO_DTD_SDVO_FLAG_SCALING_NONE (0 << 4)
# define SDVO_DTD_SDVO_FLAG_SCALING_SHARP (1 << 4)
# define SDVO_DTD_SDVO_FLAG_SCALING_SMOOTH (2 << 4)
# define SDVO_DTD_VSYNC_OFF_HIGH SDVO_I2C_ARG_6
 
/**
* Generates a DTD based on the given width, height, and flags.
*
* This will be supported by any device supporting scaling or interlaced
* modes.
*/
#define SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING 0x1a
# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_LOW SDVO_I2C_ARG_0
# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_HIGH SDVO_I2C_ARG_1
# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_LOW SDVO_I2C_ARG_2
# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_HIGH SDVO_I2C_ARG_3
# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_LOW SDVO_I2C_ARG_4
# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_HIGH SDVO_I2C_ARG_5
# define SDVO_PREFERRED_INPUT_TIMING_FLAGS SDVO_I2C_ARG_6
# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_INTERLACED (1 << 0)
# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_SCALED (1 << 1)
 
#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1 0x1b
#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2 0x1c
 
/** Returns a struct intel_sdvo_pixel_clock_range */
#define SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE 0x1d
/** Returns a struct intel_sdvo_pixel_clock_range */
#define SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE 0x1e
 
/** Returns a byte bitfield containing SDVO_CLOCK_RATE_MULT_* flags */
#define SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS 0x1f
 
/** Returns a byte containing a SDVO_CLOCK_RATE_MULT_* flag */
#define SDVO_CMD_GET_CLOCK_RATE_MULT 0x20
/** Takes a byte containing a SDVO_CLOCK_RATE_MULT_* flag */
#define SDVO_CMD_SET_CLOCK_RATE_MULT 0x21
# define SDVO_CLOCK_RATE_MULT_1X (1 << 0)
# define SDVO_CLOCK_RATE_MULT_2X (1 << 1)
# define SDVO_CLOCK_RATE_MULT_4X (1 << 3)
 
#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS 0x27
/** 6 bytes of bit flags for TV formats shared by all TV format functions */
struct intel_sdvo_tv_format {
unsigned int ntsc_m:1;
unsigned int ntsc_j:1;
unsigned int ntsc_443:1;
unsigned int pal_b:1;
unsigned int pal_d:1;
unsigned int pal_g:1;
unsigned int pal_h:1;
unsigned int pal_i:1;
 
unsigned int pal_m:1;
unsigned int pal_n:1;
unsigned int pal_nc:1;
unsigned int pal_60:1;
unsigned int secam_b:1;
unsigned int secam_d:1;
unsigned int secam_g:1;
unsigned int secam_k:1;
 
unsigned int secam_k1:1;
unsigned int secam_l:1;
unsigned int secam_60:1;
unsigned int hdtv_std_smpte_240m_1080i_59:1;
unsigned int hdtv_std_smpte_240m_1080i_60:1;
unsigned int hdtv_std_smpte_260m_1080i_59:1;
unsigned int hdtv_std_smpte_260m_1080i_60:1;
unsigned int hdtv_std_smpte_274m_1080i_50:1;
 
unsigned int hdtv_std_smpte_274m_1080i_59:1;
unsigned int hdtv_std_smpte_274m_1080i_60:1;
unsigned int hdtv_std_smpte_274m_1080p_23:1;
unsigned int hdtv_std_smpte_274m_1080p_24:1;
unsigned int hdtv_std_smpte_274m_1080p_25:1;
unsigned int hdtv_std_smpte_274m_1080p_29:1;
unsigned int hdtv_std_smpte_274m_1080p_30:1;
unsigned int hdtv_std_smpte_274m_1080p_50:1;
 
unsigned int hdtv_std_smpte_274m_1080p_59:1;
unsigned int hdtv_std_smpte_274m_1080p_60:1;
unsigned int hdtv_std_smpte_295m_1080i_50:1;
unsigned int hdtv_std_smpte_295m_1080p_50:1;
unsigned int hdtv_std_smpte_296m_720p_59:1;
unsigned int hdtv_std_smpte_296m_720p_60:1;
unsigned int hdtv_std_smpte_296m_720p_50:1;
unsigned int hdtv_std_smpte_293m_480p_59:1;
 
unsigned int hdtv_std_smpte_170m_480i_59:1;
unsigned int hdtv_std_iturbt601_576i_50:1;
unsigned int hdtv_std_iturbt601_576p_50:1;
unsigned int hdtv_std_eia_7702a_480i_60:1;
unsigned int hdtv_std_eia_7702a_480p_60:1;
unsigned int pad:3;
} __attribute__((packed));
 
#define SDVO_CMD_GET_TV_FORMAT 0x28
 
#define SDVO_CMD_SET_TV_FORMAT 0x29
 
/** Returns the resolutiosn that can be used with the given TV format */
#define SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT 0x83
struct intel_sdvo_sdtv_resolution_request {
unsigned int ntsc_m:1;
unsigned int ntsc_j:1;
unsigned int ntsc_443:1;
unsigned int pal_b:1;
unsigned int pal_d:1;
unsigned int pal_g:1;
unsigned int pal_h:1;
unsigned int pal_i:1;
 
unsigned int pal_m:1;
unsigned int pal_n:1;
unsigned int pal_nc:1;
unsigned int pal_60:1;
unsigned int secam_b:1;
unsigned int secam_d:1;
unsigned int secam_g:1;
unsigned int secam_k:1;
 
unsigned int secam_k1:1;
unsigned int secam_l:1;
unsigned int secam_60:1;
unsigned int pad:5;
} __attribute__((packed));
 
struct intel_sdvo_sdtv_resolution_reply {
unsigned int res_320x200:1;
unsigned int res_320x240:1;
unsigned int res_400x300:1;
unsigned int res_640x350:1;
unsigned int res_640x400:1;
unsigned int res_640x480:1;
unsigned int res_704x480:1;
unsigned int res_704x576:1;
 
unsigned int res_720x350:1;
unsigned int res_720x400:1;
unsigned int res_720x480:1;
unsigned int res_720x540:1;
unsigned int res_720x576:1;
unsigned int res_768x576:1;
unsigned int res_800x600:1;
unsigned int res_832x624:1;
 
unsigned int res_920x766:1;
unsigned int res_1024x768:1;
unsigned int res_1280x1024:1;
unsigned int pad:5;
} __attribute__((packed));
 
/* Get supported resolution with squire pixel aspect ratio that can be
scaled for the requested HDTV format */
#define SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT 0x85
 
struct intel_sdvo_hdtv_resolution_request {
unsigned int hdtv_std_smpte_240m_1080i_59:1;
unsigned int hdtv_std_smpte_240m_1080i_60:1;
unsigned int hdtv_std_smpte_260m_1080i_59:1;
unsigned int hdtv_std_smpte_260m_1080i_60:1;
unsigned int hdtv_std_smpte_274m_1080i_50:1;
unsigned int hdtv_std_smpte_274m_1080i_59:1;
unsigned int hdtv_std_smpte_274m_1080i_60:1;
unsigned int hdtv_std_smpte_274m_1080p_23:1;
 
unsigned int hdtv_std_smpte_274m_1080p_24:1;
unsigned int hdtv_std_smpte_274m_1080p_25:1;
unsigned int hdtv_std_smpte_274m_1080p_29:1;
unsigned int hdtv_std_smpte_274m_1080p_30:1;
unsigned int hdtv_std_smpte_274m_1080p_50:1;
unsigned int hdtv_std_smpte_274m_1080p_59:1;
unsigned int hdtv_std_smpte_274m_1080p_60:1;
unsigned int hdtv_std_smpte_295m_1080i_50:1;
 
unsigned int hdtv_std_smpte_295m_1080p_50:1;
unsigned int hdtv_std_smpte_296m_720p_59:1;
unsigned int hdtv_std_smpte_296m_720p_60:1;
unsigned int hdtv_std_smpte_296m_720p_50:1;
unsigned int hdtv_std_smpte_293m_480p_59:1;
unsigned int hdtv_std_smpte_170m_480i_59:1;
unsigned int hdtv_std_iturbt601_576i_50:1;
unsigned int hdtv_std_iturbt601_576p_50:1;
 
unsigned int hdtv_std_eia_7702a_480i_60:1;
unsigned int hdtv_std_eia_7702a_480p_60:1;
unsigned int pad:6;
} __attribute__((packed));
 
struct intel_sdvo_hdtv_resolution_reply {
unsigned int res_640x480:1;
unsigned int res_800x600:1;
unsigned int res_1024x768:1;
unsigned int res_1280x960:1;
unsigned int res_1400x1050:1;
unsigned int res_1600x1200:1;
unsigned int res_1920x1440:1;
unsigned int res_2048x1536:1;
 
unsigned int res_2560x1920:1;
unsigned int res_3200x2400:1;
unsigned int res_3840x2880:1;
unsigned int pad1:5;
 
unsigned int res_848x480:1;
unsigned int res_1064x600:1;
unsigned int res_1280x720:1;
unsigned int res_1360x768:1;
unsigned int res_1704x960:1;
unsigned int res_1864x1050:1;
unsigned int res_1920x1080:1;
unsigned int res_2128x1200:1;
 
unsigned int res_2560x1400:1;
unsigned int res_2728x1536:1;
unsigned int res_3408x1920:1;
unsigned int res_4264x2400:1;
unsigned int res_5120x2880:1;
unsigned int pad2:3;
 
unsigned int res_768x480:1;
unsigned int res_960x600:1;
unsigned int res_1152x720:1;
unsigned int res_1124x768:1;
unsigned int res_1536x960:1;
unsigned int res_1680x1050:1;
unsigned int res_1728x1080:1;
unsigned int res_1920x1200:1;
 
unsigned int res_2304x1440:1;
unsigned int res_2456x1536:1;
unsigned int res_3072x1920:1;
unsigned int res_3840x2400:1;
unsigned int res_4608x2880:1;
unsigned int pad3:3;
 
unsigned int res_1280x1024:1;
unsigned int pad4:7;
 
unsigned int res_1280x768:1;
unsigned int pad5:7;
} __attribute__((packed));
 
/* Get supported power state returns info for encoder and monitor, rely on
last SetTargetInput and SetTargetOutput calls */
#define SDVO_CMD_GET_SUPPORTED_POWER_STATES 0x2a
/* Get power state returns info for encoder and monitor, rely on last
SetTargetInput and SetTargetOutput calls */
#define SDVO_CMD_GET_POWER_STATE 0x2b
#define SDVO_CMD_GET_ENCODER_POWER_STATE 0x2b
#define SDVO_CMD_SET_ENCODER_POWER_STATE 0x2c
# define SDVO_ENCODER_STATE_ON (1 << 0)
# define SDVO_ENCODER_STATE_STANDBY (1 << 1)
# define SDVO_ENCODER_STATE_SUSPEND (1 << 2)
# define SDVO_ENCODER_STATE_OFF (1 << 3)
# define SDVO_MONITOR_STATE_ON (1 << 4)
# define SDVO_MONITOR_STATE_STANDBY (1 << 5)
# define SDVO_MONITOR_STATE_SUSPEND (1 << 6)
# define SDVO_MONITOR_STATE_OFF (1 << 7)
 
#define SDVO_CMD_GET_MAX_PANEL_POWER_SEQUENCING 0x2d
#define SDVO_CMD_GET_PANEL_POWER_SEQUENCING 0x2e
#define SDVO_CMD_SET_PANEL_POWER_SEQUENCING 0x2f
/**
* The panel power sequencing parameters are in units of milliseconds.
* The high fields are bits 8:9 of the 10-bit values.
*/
struct sdvo_panel_power_sequencing {
u8 t0;
u8 t1;
u8 t2;
u8 t3;
u8 t4;
 
unsigned int t0_high:2;
unsigned int t1_high:2;
unsigned int t2_high:2;
unsigned int t3_high:2;
 
unsigned int t4_high:2;
unsigned int pad:6;
} __attribute__((packed));
 
#define SDVO_CMD_GET_MAX_BACKLIGHT_LEVEL 0x30
struct sdvo_max_backlight_reply {
u8 max_value;
u8 default_value;
} __attribute__((packed));
 
#define SDVO_CMD_GET_BACKLIGHT_LEVEL 0x31
#define SDVO_CMD_SET_BACKLIGHT_LEVEL 0x32
 
#define SDVO_CMD_GET_AMBIENT_LIGHT 0x33
struct sdvo_get_ambient_light_reply {
u16 trip_low;
u16 trip_high;
u16 value;
} __attribute__((packed));
#define SDVO_CMD_SET_AMBIENT_LIGHT 0x34
struct sdvo_set_ambient_light_reply {
u16 trip_low;
u16 trip_high;
unsigned int enable:1;
unsigned int pad:7;
} __attribute__((packed));
 
/* Set display power state */
#define SDVO_CMD_SET_DISPLAY_POWER_STATE 0x7d
# define SDVO_DISPLAY_STATE_ON (1 << 0)
# define SDVO_DISPLAY_STATE_STANDBY (1 << 1)
# define SDVO_DISPLAY_STATE_SUSPEND (1 << 2)
# define SDVO_DISPLAY_STATE_OFF (1 << 3)
 
#define SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS 0x84
struct intel_sdvo_enhancements_reply {
unsigned int flicker_filter:1;
unsigned int flicker_filter_adaptive:1;
unsigned int flicker_filter_2d:1;
unsigned int saturation:1;
unsigned int hue:1;
unsigned int brightness:1;
unsigned int contrast:1;
unsigned int overscan_h:1;
 
unsigned int overscan_v:1;
unsigned int hpos:1;
unsigned int vpos:1;
unsigned int sharpness:1;
unsigned int dot_crawl:1;
unsigned int dither:1;
unsigned int tv_chroma_filter:1;
unsigned int tv_luma_filter:1;
} __attribute__((packed));
 
/* Picture enhancement limits below are dependent on the current TV format,
* and thus need to be queried and set after it.
*/
#define SDVO_CMD_GET_MAX_FLICKER_FILTER 0x4d
#define SDVO_CMD_GET_MAX_FLICKER_FILTER_ADAPTIVE 0x7b
#define SDVO_CMD_GET_MAX_FLICKER_FILTER_2D 0x52
#define SDVO_CMD_GET_MAX_SATURATION 0x55
#define SDVO_CMD_GET_MAX_HUE 0x58
#define SDVO_CMD_GET_MAX_BRIGHTNESS 0x5b
#define SDVO_CMD_GET_MAX_CONTRAST 0x5e
#define SDVO_CMD_GET_MAX_OVERSCAN_H 0x61
#define SDVO_CMD_GET_MAX_OVERSCAN_V 0x64
#define SDVO_CMD_GET_MAX_HPOS 0x67
#define SDVO_CMD_GET_MAX_VPOS 0x6a
#define SDVO_CMD_GET_MAX_SHARPNESS 0x6d
#define SDVO_CMD_GET_MAX_TV_CHROMA_FILTER 0x74
#define SDVO_CMD_GET_MAX_TV_LUMA_FILTER 0x77
struct intel_sdvo_enhancement_limits_reply {
u16 max_value;
u16 default_value;
} __attribute__((packed));
 
#define SDVO_CMD_GET_LVDS_PANEL_INFORMATION 0x7f
#define SDVO_CMD_SET_LVDS_PANEL_INFORMATION 0x80
# define SDVO_LVDS_COLOR_DEPTH_18 (0 << 0)
# define SDVO_LVDS_COLOR_DEPTH_24 (1 << 0)
# define SDVO_LVDS_CONNECTOR_SPWG (0 << 2)
# define SDVO_LVDS_CONNECTOR_OPENLDI (1 << 2)
# define SDVO_LVDS_SINGLE_CHANNEL (0 << 4)
# define SDVO_LVDS_DUAL_CHANNEL (1 << 4)
 
#define SDVO_CMD_GET_FLICKER_FILTER 0x4e
#define SDVO_CMD_SET_FLICKER_FILTER 0x4f
#define SDVO_CMD_GET_FLICKER_FILTER_ADAPTIVE 0x50
#define SDVO_CMD_SET_FLICKER_FILTER_ADAPTIVE 0x51
#define SDVO_CMD_GET_FLICKER_FILTER_2D 0x53
#define SDVO_CMD_SET_FLICKER_FILTER_2D 0x54
#define SDVO_CMD_GET_SATURATION 0x56
#define SDVO_CMD_SET_SATURATION 0x57
#define SDVO_CMD_GET_HUE 0x59
#define SDVO_CMD_SET_HUE 0x5a
#define SDVO_CMD_GET_BRIGHTNESS 0x5c
#define SDVO_CMD_SET_BRIGHTNESS 0x5d
#define SDVO_CMD_GET_CONTRAST 0x5f
#define SDVO_CMD_SET_CONTRAST 0x60
#define SDVO_CMD_GET_OVERSCAN_H 0x62
#define SDVO_CMD_SET_OVERSCAN_H 0x63
#define SDVO_CMD_GET_OVERSCAN_V 0x65
#define SDVO_CMD_SET_OVERSCAN_V 0x66
#define SDVO_CMD_GET_HPOS 0x68
#define SDVO_CMD_SET_HPOS 0x69
#define SDVO_CMD_GET_VPOS 0x6b
#define SDVO_CMD_SET_VPOS 0x6c
#define SDVO_CMD_GET_SHARPNESS 0x6e
#define SDVO_CMD_SET_SHARPNESS 0x6f
#define SDVO_CMD_GET_TV_CHROMA_FILTER 0x75
#define SDVO_CMD_SET_TV_CHROMA_FILTER 0x76
#define SDVO_CMD_GET_TV_LUMA_FILTER 0x78
#define SDVO_CMD_SET_TV_LUMA_FILTER 0x79
struct intel_sdvo_enhancements_arg {
u16 value;
}__attribute__((packed));
 
#define SDVO_CMD_GET_DOT_CRAWL 0x70
#define SDVO_CMD_SET_DOT_CRAWL 0x71
# define SDVO_DOT_CRAWL_ON (1 << 0)
# define SDVO_DOT_CRAWL_DEFAULT_ON (1 << 1)
 
#define SDVO_CMD_GET_DITHER 0x72
#define SDVO_CMD_SET_DITHER 0x73
# define SDVO_DITHER_ON (1 << 0)
# define SDVO_DITHER_DEFAULT_ON (1 << 1)
 
#define SDVO_CMD_SET_CONTROL_BUS_SWITCH 0x7a
# define SDVO_CONTROL_BUS_PROM (1 << 0)
# define SDVO_CONTROL_BUS_DDC1 (1 << 1)
# define SDVO_CONTROL_BUS_DDC2 (1 << 2)
# define SDVO_CONTROL_BUS_DDC3 (1 << 3)
 
/* HDMI op codes */
#define SDVO_CMD_GET_SUPP_ENCODE 0x9d
#define SDVO_CMD_GET_ENCODE 0x9e
#define SDVO_CMD_SET_ENCODE 0x9f
#define SDVO_ENCODE_DVI 0x0
#define SDVO_ENCODE_HDMI 0x1
#define SDVO_CMD_SET_PIXEL_REPLI 0x8b
#define SDVO_CMD_GET_PIXEL_REPLI 0x8c
#define SDVO_CMD_GET_COLORIMETRY_CAP 0x8d
#define SDVO_CMD_SET_COLORIMETRY 0x8e
#define SDVO_COLORIMETRY_RGB256 0x0
#define SDVO_COLORIMETRY_RGB220 0x1
#define SDVO_COLORIMETRY_YCrCb422 0x3
#define SDVO_COLORIMETRY_YCrCb444 0x4
#define SDVO_CMD_GET_COLORIMETRY 0x8f
#define SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER 0x90
#define SDVO_CMD_SET_AUDIO_STAT 0x91
#define SDVO_CMD_GET_AUDIO_STAT 0x92
#define SDVO_CMD_SET_HBUF_INDEX 0x93
#define SDVO_CMD_GET_HBUF_INDEX 0x94
#define SDVO_CMD_GET_HBUF_INFO 0x95
#define SDVO_CMD_SET_HBUF_AV_SPLIT 0x96
#define SDVO_CMD_GET_HBUF_AV_SPLIT 0x97
#define SDVO_CMD_SET_HBUF_DATA 0x98
#define SDVO_CMD_GET_HBUF_DATA 0x99
#define SDVO_CMD_SET_HBUF_TXRATE 0x9a
#define SDVO_CMD_GET_HBUF_TXRATE 0x9b
#define SDVO_HBUF_TX_DISABLED (0 << 6)
#define SDVO_HBUF_TX_ONCE (2 << 6)
#define SDVO_HBUF_TX_VSYNC (3 << 6)
#define SDVO_CMD_GET_AUDIO_TX_INFO 0x9c
#define SDVO_NEED_TO_STALL (1 << 7)
 
struct intel_sdvo_encode{
u8 dvi_rev;
u8 hdmi_rev;
} __attribute__ ((packed));
/drivers/video/drm/i915/main.c
26,7 → 26,7
 
if(!dbg_open(log))
{
strcpy(log, "/RD/1/DRIVERS/i915.log");
strcpy(log, "/HD1/2/i915.log");
 
if(!dbg_open(log))
{