Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
a4fc5ed6
Commit
a4fc5ed6
authored
Apr 07, 2009
by
Keith Packard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
drm/i915: Add Display Port support
Signed-off-by:
Keith Packard
<
keithp@keithp.com
>
parent
c31c4ba3
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
1668 additions
and
6 deletions
+1668
-6
drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/Makefile
+2
-0
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_drv.h
+12
-0
drivers/gpu/drm/i915/i915_suspend.c
drivers/gpu/drm/i915/i915_suspend.c
+33
-1
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_display.c
+102
-5
drivers/gpu/drm/i915/intel_dp.c
drivers/gpu/drm/i915/intel_dp.c
+1098
-0
drivers/gpu/drm/i915/intel_dp.h
drivers/gpu/drm/i915/intel_dp.h
+144
-0
drivers/gpu/drm/i915/intel_dp_i2c.c
drivers/gpu/drm/i915/intel_dp_i2c.c
+272
-0
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_drv.h
+5
-0
No files found.
drivers/gpu/drm/i915/Makefile
View file @
a4fc5ed6
...
...
@@ -13,6 +13,8 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
intel_crt.o
\
intel_lvds.o
\
intel_bios.o
\
intel_dp.o
\
intel_dp_i2c.o
\
intel_hdmi.o
\
intel_sdvo.o
\
intel_modes.o
\
...
...
drivers/gpu/drm/i915/i915_drv.h
View file @
a4fc5ed6
...
...
@@ -306,6 +306,17 @@ typedef struct drm_i915_private {
u32
saveCURBPOS
;
u32
saveCURBBASE
;
u32
saveCURSIZE
;
u32
saveDP_B
;
u32
saveDP_C
;
u32
saveDP_D
;
u32
savePIPEA_GMCH_DATA_M
;
u32
savePIPEB_GMCH_DATA_M
;
u32
savePIPEA_GMCH_DATA_N
;
u32
savePIPEB_GMCH_DATA_N
;
u32
savePIPEA_DP_LINK_M
;
u32
savePIPEB_DP_LINK_M
;
u32
savePIPEA_DP_LINK_N
;
u32
savePIPEB_DP_LINK_N
;
struct
{
struct
drm_mm
gtt_space
;
...
...
@@ -857,6 +868,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
#define HAS_128_BYTE_Y_TILING(dev) (IS_I9XX(dev) && !(IS_I915G(dev) || \
IS_I915GM(dev)))
#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_IGDNG(dev))
#define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev) || IS_IGDNG(dev))
#define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_I965G(dev))
#define PRIMARY_RINGBUFFER_SIZE (128*1024)
...
...
drivers/gpu/drm/i915/i915_suspend.c
View file @
a4fc5ed6
...
...
@@ -322,6 +322,20 @@ int i915_save_state(struct drm_device *dev)
dev_priv
->
savePP_OFF_DELAYS
=
I915_READ
(
PP_OFF_DELAYS
);
dev_priv
->
savePP_DIVISOR
=
I915_READ
(
PP_DIVISOR
);
/* Display Port state */
if
(
SUPPORTS_INTEGRATED_DP
(
dev
))
{
dev_priv
->
saveDP_B
=
I915_READ
(
DP_B
);
dev_priv
->
saveDP_C
=
I915_READ
(
DP_C
);
dev_priv
->
saveDP_D
=
I915_READ
(
DP_D
);
dev_priv
->
savePIPEA_GMCH_DATA_M
=
I915_READ
(
PIPEA_GMCH_DATA_M
);
dev_priv
->
savePIPEB_GMCH_DATA_M
=
I915_READ
(
PIPEB_GMCH_DATA_M
);
dev_priv
->
savePIPEA_GMCH_DATA_N
=
I915_READ
(
PIPEA_GMCH_DATA_N
);
dev_priv
->
savePIPEB_GMCH_DATA_N
=
I915_READ
(
PIPEB_GMCH_DATA_N
);
dev_priv
->
savePIPEA_DP_LINK_M
=
I915_READ
(
PIPEA_DP_LINK_M
);
dev_priv
->
savePIPEB_DP_LINK_M
=
I915_READ
(
PIPEB_DP_LINK_M
);
dev_priv
->
savePIPEA_DP_LINK_N
=
I915_READ
(
PIPEA_DP_LINK_N
);
dev_priv
->
savePIPEB_DP_LINK_N
=
I915_READ
(
PIPEB_DP_LINK_N
);
}
/* FIXME: save TV & SDVO state */
/* FBC state */
...
...
@@ -404,7 +418,19 @@ int i915_restore_state(struct drm_device *dev)
for
(
i
=
0
;
i
<
8
;
i
++
)
I915_WRITE
(
FENCE_REG_945_8
+
(
i
*
4
),
dev_priv
->
saveFENCE
[
i
+
8
]);
}
/* Display port ratios (must be done before clock is set) */
if
(
SUPPORTS_INTEGRATED_DP
(
dev
))
{
I915_WRITE
(
PIPEA_GMCH_DATA_M
,
dev_priv
->
savePIPEA_GMCH_DATA_M
);
I915_WRITE
(
PIPEB_GMCH_DATA_M
,
dev_priv
->
savePIPEB_GMCH_DATA_M
);
I915_WRITE
(
PIPEA_GMCH_DATA_N
,
dev_priv
->
savePIPEA_GMCH_DATA_N
);
I915_WRITE
(
PIPEB_GMCH_DATA_N
,
dev_priv
->
savePIPEB_GMCH_DATA_N
);
I915_WRITE
(
PIPEA_DP_LINK_M
,
dev_priv
->
savePIPEA_DP_LINK_M
);
I915_WRITE
(
PIPEB_DP_LINK_M
,
dev_priv
->
savePIPEB_DP_LINK_M
);
I915_WRITE
(
PIPEA_DP_LINK_N
,
dev_priv
->
savePIPEA_DP_LINK_N
);
I915_WRITE
(
PIPEB_DP_LINK_N
,
dev_priv
->
savePIPEB_DP_LINK_N
);
}
/* Pipe & plane A info */
/* Prime the clock */
if
(
dev_priv
->
saveDPLL_A
&
DPLL_VCO_ENABLE
)
{
...
...
@@ -518,6 +544,12 @@ int i915_restore_state(struct drm_device *dev)
I915_WRITE
(
PP_DIVISOR
,
dev_priv
->
savePP_DIVISOR
);
I915_WRITE
(
PP_CONTROL
,
dev_priv
->
savePP_CONTROL
);
/* Display Port state */
if
(
SUPPORTS_INTEGRATED_DP
(
dev
))
{
I915_WRITE
(
DP_B
,
dev_priv
->
saveDP_B
);
I915_WRITE
(
DP_C
,
dev_priv
->
saveDP_C
);
I915_WRITE
(
DP_D
,
dev_priv
->
saveDP_D
);
}
/* FIXME: restore TV & SDVO state */
/* FBC info */
...
...
drivers/gpu/drm/i915/intel_display.c
View file @
a4fc5ed6
...
...
@@ -29,6 +29,7 @@
#include "intel_drv.h"
#include "i915_drm.h"
#include "i915_drv.h"
#include "intel_dp.h"
#include "drm_crtc_helper.h"
...
...
@@ -135,10 +136,11 @@ struct intel_limit {
#define INTEL_LIMIT_G4X_HDMI_DAC 5
#define INTEL_LIMIT_G4X_SINGLE_CHANNEL_LVDS 6
#define INTEL_LIMIT_G4X_DUAL_CHANNEL_LVDS 7
#define INTEL_LIMIT_IGD_SDVO_DAC 8
#define INTEL_LIMIT_IGD_LVDS 9
#define INTEL_LIMIT_IGDNG_SDVO_DAC 10
#define INTEL_LIMIT_IGDNG_LVDS 11
#define INTEL_LIMIT_G4X_DISPLAY_PORT 8
#define INTEL_LIMIT_IGD_SDVO_DAC 9
#define INTEL_LIMIT_IGD_LVDS 10
#define INTEL_LIMIT_IGDNG_SDVO_DAC 11
#define INTEL_LIMIT_IGDNG_LVDS 12
/*The parameter is for SDVO on G4x platform*/
#define G4X_DOT_SDVO_MIN 25000
...
...
@@ -218,6 +220,25 @@ struct intel_limit {
#define G4X_P2_DUAL_CHANNEL_LVDS_FAST 7
#define G4X_P2_DUAL_CHANNEL_LVDS_LIMIT 0
/*The parameter is for DISPLAY PORT on G4x platform*/
#define G4X_DOT_DISPLAY_PORT_MIN 161670
#define G4X_DOT_DISPLAY_PORT_MAX 227000
#define G4X_N_DISPLAY_PORT_MIN 1
#define G4X_N_DISPLAY_PORT_MAX 2
#define G4X_M_DISPLAY_PORT_MIN 97
#define G4X_M_DISPLAY_PORT_MAX 108
#define G4X_M1_DISPLAY_PORT_MIN 0x10
#define G4X_M1_DISPLAY_PORT_MAX 0x12
#define G4X_M2_DISPLAY_PORT_MIN 0x05
#define G4X_M2_DISPLAY_PORT_MAX 0x06
#define G4X_P_DISPLAY_PORT_MIN 10
#define G4X_P_DISPLAY_PORT_MAX 20
#define G4X_P1_DISPLAY_PORT_MIN 1
#define G4X_P1_DISPLAY_PORT_MAX 2
#define G4X_P2_DISPLAY_PORT_SLOW 10
#define G4X_P2_DISPLAY_PORT_FAST 10
#define G4X_P2_DISPLAY_PORT_LIMIT 0
/* IGDNG */
/* as we calculate clock using (register_value + 2) for
N/M1/M2, so here the range value for them is (actual_value-2).
...
...
@@ -256,6 +277,10 @@ static bool
intel_igdng_find_best_PLL
(
const
intel_limit_t
*
limit
,
struct
drm_crtc
*
crtc
,
int
target
,
int
refclk
,
intel_clock_t
*
best_clock
);
static
bool
intel_find_pll_g4x_dp
(
const
intel_limit_t
*
,
struct
drm_crtc
*
crtc
,
int
target
,
int
refclk
,
intel_clock_t
*
best_clock
);
static
const
intel_limit_t
intel_limits
[]
=
{
{
/* INTEL_LIMIT_I8XX_DVO_DAC */
.
dot
=
{
.
min
=
I8XX_DOT_MIN
,
.
max
=
I8XX_DOT_MAX
},
...
...
@@ -389,6 +414,28 @@ static const intel_limit_t intel_limits[] = {
},
.
find_pll
=
intel_g4x_find_best_PLL
,
},
{
/* INTEL_LIMIT_G4X_DISPLAY_PORT */
.
dot
=
{
.
min
=
G4X_DOT_DISPLAY_PORT_MIN
,
.
max
=
G4X_DOT_DISPLAY_PORT_MAX
},
.
vco
=
{
.
min
=
G4X_VCO_MIN
,
.
max
=
G4X_VCO_MAX
},
.
n
=
{
.
min
=
G4X_N_DISPLAY_PORT_MIN
,
.
max
=
G4X_N_DISPLAY_PORT_MAX
},
.
m
=
{
.
min
=
G4X_M_DISPLAY_PORT_MIN
,
.
max
=
G4X_M_DISPLAY_PORT_MAX
},
.
m1
=
{
.
min
=
G4X_M1_DISPLAY_PORT_MIN
,
.
max
=
G4X_M1_DISPLAY_PORT_MAX
},
.
m2
=
{
.
min
=
G4X_M2_DISPLAY_PORT_MIN
,
.
max
=
G4X_M2_DISPLAY_PORT_MAX
},
.
p
=
{
.
min
=
G4X_P_DISPLAY_PORT_MIN
,
.
max
=
G4X_P_DISPLAY_PORT_MAX
},
.
p1
=
{
.
min
=
G4X_P1_DISPLAY_PORT_MIN
,
.
max
=
G4X_P1_DISPLAY_PORT_MAX
},
.
p2
=
{
.
dot_limit
=
G4X_P2_DISPLAY_PORT_LIMIT
,
.
p2_slow
=
G4X_P2_DISPLAY_PORT_SLOW
,
.
p2_fast
=
G4X_P2_DISPLAY_PORT_FAST
},
.
find_pll
=
intel_find_pll_g4x_dp
,
},
{
/* INTEL_LIMIT_IGD_SDVO */
.
dot
=
{
.
min
=
I9XX_DOT_MIN
,
.
max
=
I9XX_DOT_MAX
},
.
vco
=
{
.
min
=
IGD_VCO_MIN
,
.
max
=
IGD_VCO_MAX
},
...
...
@@ -478,6 +525,8 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
limit
=
&
intel_limits
[
INTEL_LIMIT_G4X_HDMI_DAC
];
}
else
if
(
intel_pipe_has_type
(
crtc
,
INTEL_OUTPUT_SDVO
))
{
limit
=
&
intel_limits
[
INTEL_LIMIT_G4X_SDVO
];
}
else
if
(
intel_pipe_has_type
(
crtc
,
INTEL_OUTPUT_DISPLAYPORT
))
{
limit
=
&
intel_limits
[
INTEL_LIMIT_G4X_DISPLAY_PORT
];
}
else
/* The option is for other outputs */
limit
=
&
intel_limits
[
INTEL_LIMIT_I9XX_SDVO_DAC
];
...
...
@@ -764,6 +813,35 @@ intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
return
found
;
}
/* DisplayPort has only two frequencies, 162MHz and 270MHz */
static
bool
intel_find_pll_g4x_dp
(
const
intel_limit_t
*
limit
,
struct
drm_crtc
*
crtc
,
int
target
,
int
refclk
,
intel_clock_t
*
best_clock
)
{
intel_clock_t
clock
;
if
(
target
<
200000
)
{
clock
.
dot
=
161670
;
clock
.
p
=
20
;
clock
.
p1
=
2
;
clock
.
p2
=
10
;
clock
.
n
=
0x01
;
clock
.
m
=
97
;
clock
.
m1
=
0x10
;
clock
.
m2
=
0x05
;
}
else
{
clock
.
dot
=
270000
;
clock
.
p
=
10
;
clock
.
p1
=
1
;
clock
.
p2
=
10
;
clock
.
n
=
0x02
;
clock
.
m
=
108
;
clock
.
m1
=
0x12
;
clock
.
m2
=
0x06
;
}
memcpy
(
best_clock
,
&
clock
,
sizeof
(
intel_clock_t
));
return
true
;
}
void
intel_wait_for_vblank
(
struct
drm_device
*
dev
)
{
...
...
@@ -1541,7 +1619,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
intel_clock_t
clock
;
u32
dpll
=
0
,
fp
=
0
,
dspcntr
,
pipeconf
;
bool
ok
,
is_sdvo
=
false
,
is_dvo
=
false
;
bool
is_crt
=
false
,
is_lvds
=
false
,
is_tv
=
false
;
bool
is_crt
=
false
,
is_lvds
=
false
,
is_tv
=
false
,
is_dp
=
false
;
struct
drm_mode_config
*
mode_config
=
&
dev
->
mode_config
;
struct
drm_connector
*
connector
;
const
intel_limit_t
*
limit
;
...
...
@@ -1585,6 +1663,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
case
INTEL_OUTPUT_ANALOG
:
is_crt
=
true
;
break
;
case
INTEL_OUTPUT_DISPLAYPORT
:
is_dp
=
true
;
break
;
}
num_outputs
++
;
...
...
@@ -1600,6 +1681,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
}
else
{
refclk
=
48000
;
}
/*
* Returns a set of divisors for the desired target clock with the given
...
...
@@ -1662,6 +1744,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
else
if
(
IS_IGDNG
(
dev
))
dpll
|=
(
sdvo_pixel_multiply
-
1
)
<<
PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT
;
}
if
(
is_dp
)
dpll
|=
DPLL_DVO_HIGH_SPEED
;
/* compute bitmask from p1 value */
if
(
IS_IGD
(
dev
))
...
...
@@ -1809,6 +1893,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
I915_WRITE
(
lvds_reg
,
lvds
);
I915_READ
(
lvds_reg
);
}
if
(
is_dp
)
intel_dp_set_m_n
(
crtc
,
mode
,
adjusted_mode
);
I915_WRITE
(
fp_reg
,
fp
);
I915_WRITE
(
dpll_reg
,
dpll
);
...
...
@@ -2475,6 +2561,8 @@ static void intel_setup_outputs(struct drm_device *dev)
found
=
intel_sdvo_init
(
dev
,
SDVOB
);
if
(
!
found
&&
SUPPORTS_INTEGRATED_HDMI
(
dev
))
intel_hdmi_init
(
dev
,
SDVOB
);
if
(
!
found
&&
SUPPORTS_INTEGRATED_DP
(
dev
))
intel_dp_init
(
dev
,
DP_B
);
}
/* Before G4X SDVOC doesn't have its own detect register */
...
...
@@ -2487,7 +2575,11 @@ static void intel_setup_outputs(struct drm_device *dev)
found
=
intel_sdvo_init
(
dev
,
SDVOC
);
if
(
!
found
&&
SUPPORTS_INTEGRATED_HDMI
(
dev
))
intel_hdmi_init
(
dev
,
SDVOC
);
if
(
!
found
&&
SUPPORTS_INTEGRATED_DP
(
dev
))
intel_dp_init
(
dev
,
DP_C
);
}
if
(
SUPPORTS_INTEGRATED_DP
(
dev
)
&&
(
I915_READ
(
DP_D
)
&
DP_DETECTED
))
intel_dp_init
(
dev
,
DP_D
);
}
else
intel_dvo_init
(
dev
);
...
...
@@ -2530,6 +2622,11 @@ static void intel_setup_outputs(struct drm_device *dev)
(
1
<<
1
));
clone_mask
=
(
1
<<
INTEL_OUTPUT_TVOUT
);
break
;
case
INTEL_OUTPUT_DISPLAYPORT
:
crtc_mask
=
((
1
<<
0
)
|
(
1
<<
1
));
clone_mask
=
(
1
<<
INTEL_OUTPUT_DISPLAYPORT
);
break
;
}
encoder
->
possible_crtcs
=
crtc_mask
;
encoder
->
possible_clones
=
intel_connector_clones
(
dev
,
clone_mask
);
...
...
drivers/gpu/drm/i915/intel_dp.c
0 → 100644
View file @
a4fc5ed6
/*
* Copyright © 2008 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:
* Keith Packard <keithp@keithp.com>
*
*/
#include <linux/i2c.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_drv.h"
#include "intel_dp.h"
#define DP_LINK_STATUS_SIZE 6
#define DP_LINK_CHECK_TIMEOUT (10 * 1000)
#define DP_LINK_CONFIGURATION_SIZE 9
struct
intel_dp_priv
{
uint32_t
output_reg
;
uint32_t
DP
;
uint8_t
link_configuration
[
DP_LINK_CONFIGURATION_SIZE
];
uint32_t
save_DP
;
uint8_t
save_link_configuration
[
DP_LINK_CONFIGURATION_SIZE
];
bool
has_audio
;
uint8_t
link_bw
;
uint8_t
lane_count
;
uint8_t
dpcd
[
4
];
struct
intel_output
*
intel_output
;
struct
i2c_adapter
adapter
;
struct
i2c_algo_dp_aux_data
algo
;
};
static
void
intel_dp_link_train
(
struct
intel_output
*
intel_output
,
uint32_t
DP
,
uint8_t
link_configuration
[
DP_LINK_CONFIGURATION_SIZE
]);
static
void
intel_dp_link_down
(
struct
intel_output
*
intel_output
,
uint32_t
DP
);
static
int
intel_dp_max_lane_count
(
struct
intel_output
*
intel_output
)
{
struct
intel_dp_priv
*
dp_priv
=
intel_output
->
dev_priv
;
int
max_lane_count
=
4
;
if
(
dp_priv
->
dpcd
[
0
]
>=
0x11
)
{
max_lane_count
=
dp_priv
->
dpcd
[
2
]
&
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_output
*
intel_output
)
{
struct
intel_dp_priv
*
dp_priv
=
intel_output
->
dev_priv
;
int
max_link_bw
=
dp_priv
->
dpcd
[
1
];
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
(
int
pixel_clock
)
{
return
pixel_clock
*
3
;
}
static
int
intel_dp_mode_valid
(
struct
drm_connector
*
connector
,
struct
drm_display_mode
*
mode
)
{
struct
intel_output
*
intel_output
=
to_intel_output
(
connector
);
int
max_link_clock
=
intel_dp_link_clock
(
intel_dp_max_link_bw
(
intel_output
));
int
max_lanes
=
intel_dp_max_lane_count
(
intel_output
);
if
(
intel_dp_link_required
(
mode
->
clock
)
>
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
);
}
static
int
intel_dp_aux_ch
(
struct
intel_output
*
intel_output
,
uint8_t
*
send
,
int
send_bytes
,
uint8_t
*
recv
,
int
recv_size
)
{
struct
intel_dp_priv
*
dp_priv
=
intel_output
->
dev_priv
;
uint32_t
output_reg
=
dp_priv
->
output_reg
;
struct
drm_device
*
dev
=
intel_output
->
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
ctl
;
uint32_t
status
;
/* Load the send data into the aux channel data registers */
for
(
i
=
0
;
i
<
send_bytes
;
i
+=
4
)
{
uint32_t
d
=
pack_aux
(
send
+
i
,
send_bytes
-
i
);;
I915_WRITE
(
ch_data
+
i
,
d
);
}
/* The clock divider is based off the hrawclk,
* and would like to run at 2MHz. The 133 below assumes
* a 266MHz hrawclk; need to figure out how we're supposed
* to know what hrawclk is...
*/
ctl
=
(
DP_AUX_CH_CTL_SEND_BUSY
|
DP_AUX_CH_CTL_TIME_OUT_1600us
|
(
send_bytes
<<
DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT
)
|
(
5
<<
DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT
)
|
(
133
<<
DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT
)
|
DP_AUX_CH_CTL_TIME_OUT_ERROR
|
DP_AUX_CH_CTL_RECEIVE_ERROR
);
/* Send the command and wait for it to complete */
I915_WRITE
(
ch_ctl
,
ctl
);
(
void
)
I915_READ
(
ch_ctl
);
for
(;;)
{
udelay
(
100
);
status
=
I915_READ
(
ch_ctl
);
if
((
status
&
DP_AUX_CH_CTL_SEND_BUSY
)
==
0
)
break
;
}
/* Clear done status and any errors */
I915_WRITE
(
ch_ctl
,
(
ctl
|
DP_AUX_CH_CTL_DONE
|
DP_AUX_CH_CTL_TIME_OUT_ERROR
|
DP_AUX_CH_CTL_RECEIVE_ERROR
));
(
void
)
I915_READ
(
ch_ctl
);
if
((
status
&
DP_AUX_CH_CTL_DONE
)
==
0
)
{
printk
(
KERN_ERR
"dp_aux_ch not done status 0x%08x
\n
"
,
status
);
return
-
1
;
}
/* Check for timeout or receive error.
* Timeouts occur when the sink is not connected
*/
if
(
status
&
(
DP_AUX_CH_CTL_TIME_OUT_ERROR
|
DP_AUX_CH_CTL_RECEIVE_ERROR
))
{
printk
(
KERN_ERR
"dp_aux_ch error status 0x%08x
\n
"
,
status
);
return
-
1
;
}
/* 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
)
{
uint32_t
d
=
I915_READ
(
ch_data
+
i
);
unpack_aux
(
d
,
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_output
*
intel_output
,
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
;
msg
[
3
]
=
send_bytes
-
1
;
memcpy
(
&
msg
[
4
],
send
,
send_bytes
);
msg_bytes
=
send_bytes
+
4
;
for
(;;)
{
ret
=
intel_dp_aux_ch
(
intel_output
,
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
-
1
;
}
return
send_bytes
;
}
/* Write a single byte to the aux channel in native mode */
static
int
intel_dp_aux_native_write_1
(
struct
intel_output
*
intel_output
,
uint16_t
address
,
uint8_t
byte
)
{
return
intel_dp_aux_native_write
(
intel_output
,
address
,
&
byte
,
1
);
}
/* read bytes from a native aux channel */
static
int
intel_dp_aux_native_read
(
struct
intel_output
*
intel_output
,
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_output
,
msg
,
msg_bytes
,
reply
,
reply_bytes
);
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
-
1
;
}
}
static
int
intel_dp_i2c_aux_ch
(
struct
i2c_adapter
*
adapter
,
uint8_t
*
send
,
int
send_bytes
,
uint8_t
*
recv
,
int
recv_bytes
)
{
struct
intel_dp_priv
*
dp_priv
=
container_of
(
adapter
,
struct
intel_dp_priv
,
adapter
);
struct
intel_output
*
intel_output
=
dp_priv
->
intel_output
;
return
intel_dp_aux_ch
(
intel_output
,
send
,
send_bytes
,
recv
,
recv_bytes
);
}
static
int
intel_dp_i2c_init
(
struct
intel_output
*
intel_output
,
const
char
*
name
)
{
struct
intel_dp_priv
*
dp_priv
=
intel_output
->
dev_priv
;
DRM_ERROR
(
"i2c_init %s
\n
"
,
name
);
dp_priv
->
algo
.
running
=
false
;
dp_priv
->
algo
.
address
=
0
;
dp_priv
->
algo
.
aux_ch
=
intel_dp_i2c_aux_ch
;
memset
(
&
dp_priv
->
adapter
,
'\0'
,
sizeof
(
dp_priv
->
adapter
));
dp_priv
->
adapter
.
owner
=
THIS_MODULE
;
dp_priv
->
adapter
.
class
=
I2C_CLASS_DDC
;
strncpy
(
dp_priv
->
adapter
.
name
,
name
,
sizeof
dp_priv
->
adapter
.
name
-
1
);
dp_priv
->
adapter
.
name
[
sizeof
dp_priv
->
adapter
.
name
-
1
]
=
'\0'
;
dp_priv
->
adapter
.
algo_data
=
&
dp_priv
->
algo
;
dp_priv
->
adapter
.
dev
.
parent
=
&
intel_output
->
base
.
kdev
;
return
i2c_dp_aux_add_bus
(
&
dp_priv
->
adapter
);
}
static
bool
intel_dp_mode_fixup
(
struct
drm_encoder
*
encoder
,
struct
drm_display_mode
*
mode
,
struct
drm_display_mode
*
adjusted_mode
)
{
struct
intel_output
*
intel_output
=
enc_to_intel_output
(
encoder
);
struct
intel_dp_priv
*
dp_priv
=
intel_output
->
dev_priv
;
int
lane_count
,
clock
;
int
max_lane_count
=
intel_dp_max_lane_count
(
intel_output
);
int
max_clock
=
intel_dp_max_link_bw
(
intel_output
)
==
DP_LINK_BW_2_7
?
1
:
0
;
static
int
bws
[
2
]
=
{
DP_LINK_BW_1_62
,
DP_LINK_BW_2_7
};
for
(
lane_count
=
1
;
lane_count
<=
max_lane_count
;
lane_count
<<=
1
)
{
for
(
clock
=
0
;
clock
<=
max_clock
;
clock
++
)
{
int
link_avail
=
intel_dp_link_clock
(
bws
[
clock
])
*
lane_count
;
if
(
intel_dp_link_required
(
mode
->
clock
)
<=
link_avail
)
{
dp_priv
->
link_bw
=
bws
[
clock
];
dp_priv
->
lane_count
=
lane_count
;
adjusted_mode
->
clock
=
intel_dp_link_clock
(
dp_priv
->
link_bw
);
printk
(
KERN_ERR
"link bw %02x lane count %d clock %d
\n
"
,
dp_priv
->
link_bw
,
dp_priv
->
lane_count
,
adjusted_mode
->
clock
);
return
true
;
}
}
}
return
false
;
}
struct
intel_dp_m_n
{
uint32_t
tu
;
uint32_t
gmch_m
;
uint32_t
gmch_n
;
uint32_t
link_m
;
uint32_t
link_n
;
};
static
void
intel_reduce_ratio
(
uint32_t
*
num
,
uint32_t
*
den
)
{
while
(
*
num
>
0xffffff
||
*
den
>
0xffffff
)
{
*
num
>>=
1
;
*
den
>>=
1
;
}
}
static
void
intel_dp_compute_m_n
(
int
bytes_per_pixel
,
int
nlanes
,
int
pixel_clock
,
int
link_clock
,
struct
intel_dp_m_n
*
m_n
)
{
m_n
->
tu
=
64
;
m_n
->
gmch_m
=
pixel_clock
*
bytes_per_pixel
;
m_n
->
gmch_n
=
link_clock
*
nlanes
;
intel_reduce_ratio
(
&
m_n
->
gmch_m
,
&
m_n
->
gmch_n
);
m_n
->
link_m
=
pixel_clock
;
m_n
->
link_n
=
link_clock
;
intel_reduce_ratio
(
&
m_n
->
link_m
,
&
m_n
->
link_n
);
}
void
intel_dp_set_m_n
(
struct
drm_crtc
*
crtc
,
struct
drm_display_mode
*
mode
,
struct
drm_display_mode
*
adjusted_mode
)
{
struct
drm_device
*
dev
=
crtc
->
dev
;
struct
drm_mode_config
*
mode_config
=
&
dev
->
mode_config
;
struct
drm_connector
*
connector
;
struct
drm_i915_private
*
dev_priv
=
dev
->
dev_private
;
struct
intel_crtc
*
intel_crtc
=
to_intel_crtc
(
crtc
);
int
lane_count
=
4
;
struct
intel_dp_m_n
m_n
;
/*
* Find the lane count in the intel_output private
*/
list_for_each_entry
(
connector
,
&
mode_config
->
connector_list
,
head
)
{
struct
intel_output
*
intel_output
=
to_intel_output
(
connector
);
struct
intel_dp_priv
*
dp_priv
=
intel_output
->
dev_priv
;
if
(
!
connector
->
encoder
||
connector
->
encoder
->
crtc
!=
crtc
)
continue
;
if
(
intel_output
->
type
==
INTEL_OUTPUT_DISPLAYPORT
)
{
lane_count
=
dp_priv
->
lane_count
;
break
;
}
}
/*
* Compute the GMCH and Link ratios. The '3' here is
* the number of bytes_per_pixel post-LUT, which we always
* set up for 8-bits of R/G/B, or 3 bytes total.
*/
intel_dp_compute_m_n
(
3
,
lane_count
,
mode
->
clock
,
adjusted_mode
->
clock
,
&
m_n
);
if
(
intel_crtc
->
pipe
==
0
)
{
I915_WRITE
(
PIPEA_GMCH_DATA_M
,
((
m_n
.
tu
-
1
)
<<
PIPE_GMCH_DATA_M_TU_SIZE_SHIFT
)
|
m_n
.
gmch_m
);
I915_WRITE
(
PIPEA_GMCH_DATA_N
,
m_n
.
gmch_n
);
I915_WRITE
(
PIPEA_DP_LINK_M
,
m_n
.
link_m
);
I915_WRITE
(
PIPEA_DP_LINK_N
,
m_n
.
link_n
);
}
else
{
I915_WRITE
(
PIPEB_GMCH_DATA_M
,
((
m_n
.
tu
-
1
)
<<
PIPE_GMCH_DATA_M_TU_SIZE_SHIFT
)
|
m_n
.
gmch_m
);
I915_WRITE
(
PIPEB_GMCH_DATA_N
,
m_n
.
gmch_n
);
I915_WRITE
(
PIPEB_DP_LINK_M
,
m_n
.
link_m
);
I915_WRITE
(
PIPEB_DP_LINK_N
,
m_n
.
link_n
);
}
}
static
void
intel_dp_mode_set
(
struct
drm_encoder
*
encoder
,
struct
drm_display_mode
*
mode
,
struct
drm_display_mode
*
adjusted_mode
)
{
struct
intel_output
*
intel_output
=
enc_to_intel_output
(
encoder
);
struct
intel_dp_priv
*
dp_priv
=
intel_output
->
dev_priv
;
struct
drm_crtc
*
crtc
=
intel_output
->
enc
.
crtc
;
struct
intel_crtc
*
intel_crtc
=
to_intel_crtc
(
crtc
);
dp_priv
->
DP
=
(
DP_LINK_TRAIN_OFF
|
DP_VOLTAGE_0_4
|
DP_PRE_EMPHASIS_0
|
DP_SYNC_VS_HIGH
|
DP_SYNC_HS_HIGH
);
switch
(
dp_priv
->
lane_count
)
{
case
1
:
dp_priv
->
DP
|=
DP_PORT_WIDTH_1
;
break
;
case
2
:
dp_priv
->
DP
|=
DP_PORT_WIDTH_2
;
break
;
case
4
:
dp_priv
->
DP
|=
DP_PORT_WIDTH_4
;
break
;
}
if
(
dp_priv
->
has_audio
)
dp_priv
->
DP
|=
DP_AUDIO_OUTPUT_ENABLE
;
memset
(
dp_priv
->
link_configuration
,
0
,
DP_LINK_CONFIGURATION_SIZE
);
dp_priv
->
link_configuration
[
0
]
=
dp_priv
->
link_bw
;
dp_priv
->
link_configuration
[
1
]
=
dp_priv
->
lane_count
;
/*
* Check for DPCD version > 1.1,
* enable enahanced frame stuff in that case
*/
if
(
dp_priv
->
dpcd
[
0
]
>=
0x11
)
{
dp_priv
->
link_configuration
[
1
]
|=
DP_LANE_COUNT_ENHANCED_FRAME_EN
;
dp_priv
->
DP
|=
DP_ENHANCED_FRAMING
;
}
if
(
intel_crtc
->
pipe
==
1
)
dp_priv
->
DP
|=
DP_PIPEB_SELECT
;
}
static
void
intel_dp_dpms
(
struct
drm_encoder
*
encoder
,
int
mode
)
{
struct
intel_output
*
intel_output
=
enc_to_intel_output
(
encoder
);
struct
intel_dp_priv
*
dp_priv
=
intel_output
->
dev_priv
;
struct
drm_device
*
dev
=
intel_output
->
base
.
dev
;
struct
drm_i915_private
*
dev_priv
=
dev
->
dev_private
;
uint32_t
dp_reg
=
I915_READ
(
dp_priv
->
output_reg
);
if
(
mode
!=
DRM_MODE_DPMS_ON
)
{
if
(
dp_reg
&
DP_PORT_EN
)
intel_dp_link_down
(
intel_output
,
dp_priv
->
DP
);
}
else
{
if
(
!
(
dp_reg
&
DP_PORT_EN
))
intel_dp_link_train
(
intel_output
,
dp_priv
->
DP
,
dp_priv
->
link_configuration
);
}
}
/*
* Fetch AUX CH registers 0x202 - 0x207 which contain
* link status information
*/
static
bool
intel_dp_get_link_status
(
struct
intel_output
*
intel_output
,
uint8_t
link_status
[
DP_LINK_STATUS_SIZE
])
{
int
ret
;
ret
=
intel_dp_aux_native_read
(
intel_output
,
DP_LANE0_1_STATUS
,
link_status
,
DP_LINK_STATUS_SIZE
);
if
(
ret
!=
DP_LINK_STATUS_SIZE
)
return
false
;
return
true
;
}
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
void
intel_dp_save
(
struct
drm_connector
*
connector
)
{
struct
intel_output
*
intel_output
=
to_intel_output
(
connector
);
struct
drm_device
*
dev
=
intel_output
->
base
.
dev
;
struct
drm_i915_private
*
dev_priv
=
dev
->
dev_private
;
struct
intel_dp_priv
*
dp_priv
=
intel_output
->
dev_priv
;
dp_priv
->
save_DP
=
I915_READ
(
dp_priv
->
output_reg
);
intel_dp_aux_native_read
(
intel_output
,
DP_LINK_BW_SET
,
dp_priv
->
save_link_configuration
,
sizeof
(
dp_priv
->
save_link_configuration
));
}
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_output
*
intel_output
,
uint8_t
link_status
[
DP_LINK_STATUS_SIZE
],
int
lane_count
,
uint8_t
train_set
[
4
])
{
uint8_t
v
=
0
;
uint8_t
p
=
0
;
int
lane
;
for
(
lane
=
0
;
lane
<
lane_count
;
lane
++
)
{
uint8_t
this_v
=
intel_get_adjust_request_voltage
(
link_status
,
lane
);
uint8_t
this_p
=
intel_get_adjust_request_pre_emphasis
(
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
++
)
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
;
}
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
(
uint8_t
link_status
[
DP_LINK_STATUS_SIZE
],
int
lane_count
)
{
uint8_t
lane_align
;
uint8_t
lane_status
;
int
lane
;
lane_align
=
intel_dp_link_status
(
link_status
,
DP_LANE_ALIGN_STATUS_UPDATED
);
if
((
lane_align
&
DP_INTERLANE_ALIGN_DONE
)
==
0
)
return
false
;
for
(
lane
=
0
;
lane
<
lane_count
;
lane
++
)
{
lane_status
=
intel_get_lane_status
(
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_output
*
intel_output
,
uint32_t
dp_reg_value
,
uint8_t
dp_train_pat
,
uint8_t
train_set
[
4
],
bool
first
)
{
struct
drm_device
*
dev
=
intel_output
->
base
.
dev
;
struct
drm_i915_private
*
dev_priv
=
dev
->
dev_private
;
struct
intel_dp_priv
*
dp_priv
=
intel_output
->
dev_priv
;
int
ret
;
I915_WRITE
(
dp_priv
->
output_reg
,
dp_reg_value
);
POSTING_READ
(
dp_priv
->
output_reg
);
if
(
first
)
intel_wait_for_vblank
(
dev
);
intel_dp_aux_native_write_1
(
intel_output
,
DP_TRAINING_PATTERN_SET
,
dp_train_pat
);
ret
=
intel_dp_aux_native_write
(
intel_output
,
DP_TRAINING_LANE0_SET
,
train_set
,
4
);
if
(
ret
!=
4
)
return
false
;
return
true
;
}
static
void
intel_dp_link_train
(
struct
intel_output
*
intel_output
,
uint32_t
DP
,
uint8_t
link_configuration
[
DP_LINK_CONFIGURATION_SIZE
])
{
struct
drm_device
*
dev
=
intel_output
->
base
.
dev
;
struct
drm_i915_private
*
dev_priv
=
dev
->
dev_private
;
struct
intel_dp_priv
*
dp_priv
=
intel_output
->
dev_priv
;
uint8_t
train_set
[
4
];
uint8_t
link_status
[
DP_LINK_STATUS_SIZE
];
int
i
;
uint8_t
voltage
;
bool
clock_recovery
=
false
;
bool
channel_eq
=
false
;
bool
first
=
true
;
int
tries
;
/* Write the link configuration data */
intel_dp_aux_native_write
(
intel_output
,
0x100
,
link_configuration
,
DP_LINK_CONFIGURATION_SIZE
);
DP
|=
DP_PORT_EN
;
DP
&=
~
DP_LINK_TRAIN_MASK
;
memset
(
train_set
,
0
,
4
);
voltage
=
0xff
;
tries
=
0
;
clock_recovery
=
false
;
for
(;;)
{
/* Use train_set[0] to set the voltage and pre emphasis values */
uint32_t
signal_levels
=
intel_dp_signal_levels
(
train_set
[
0
],
dp_priv
->
lane_count
);
DP
=
(
DP
&
~
(
DP_VOLTAGE_MASK
|
DP_PRE_EMPHASIS_MASK
))
|
signal_levels
;
if
(
!
intel_dp_set_link_train
(
intel_output
,
DP
|
DP_LINK_TRAIN_PAT_1
,
DP_TRAINING_PATTERN_1
,
train_set
,
first
))
break
;
first
=
false
;
/* Set training pattern 1 */
udelay
(
100
);
if
(
!
intel_dp_get_link_status
(
intel_output
,
link_status
))
break
;
if
(
intel_clock_recovery_ok
(
link_status
,
dp_priv
->
lane_count
))
{
clock_recovery
=
true
;
break
;
}
/* Check to see if we've tried the max voltage */
for
(
i
=
0
;
i
<
dp_priv
->
lane_count
;
i
++
)
if
((
train_set
[
i
]
&
DP_TRAIN_MAX_SWING_REACHED
)
==
0
)
break
;
if
(
i
==
dp_priv
->
lane_count
)
break
;
/* Check to see if we've tried the same voltage 5 times */
if
((
train_set
[
0
]
&
DP_TRAIN_VOLTAGE_SWING_MASK
)
==
voltage
)
{
++
tries
;
if
(
tries
==
5
)
break
;
}
else
tries
=
0
;
voltage
=
train_set
[
0
]
&
DP_TRAIN_VOLTAGE_SWING_MASK
;
/* Compute new train_set as requested by target */
intel_get_adjust_train
(
intel_output
,
link_status
,
dp_priv
->
lane_count
,
train_set
);
}
/* channel equalization */
tries
=
0
;
channel_eq
=
false
;
for
(;;)
{
/* Use train_set[0] to set the voltage and pre emphasis values */
uint32_t
signal_levels
=
intel_dp_signal_levels
(
train_set
[
0
],
dp_priv
->
lane_count
);
DP
=
(
DP
&
~
(
DP_VOLTAGE_MASK
|
DP_PRE_EMPHASIS_MASK
))
|
signal_levels
;
/* channel eq pattern */
if
(
!
intel_dp_set_link_train
(
intel_output
,
DP
|
DP_LINK_TRAIN_PAT_2
,
DP_TRAINING_PATTERN_2
,
train_set
,
false
))
break
;
udelay
(
400
);
if
(
!
intel_dp_get_link_status
(
intel_output
,
link_status
))
break
;
if
(
intel_channel_eq_ok
(
link_status
,
dp_priv
->
lane_count
))
{
channel_eq
=
true
;
break
;
}
/* Try 5 times */
if
(
tries
>
5
)
break
;
/* Compute new train_set as requested by target */
intel_get_adjust_train
(
intel_output
,
link_status
,
dp_priv
->
lane_count
,
train_set
);
++
tries
;
}
I915_WRITE
(
dp_priv
->
output_reg
,
DP
|
DP_LINK_TRAIN_OFF
);
POSTING_READ
(
dp_priv
->
output_reg
);
intel_dp_aux_native_write_1
(
intel_output
,
DP_TRAINING_PATTERN_SET
,
DP_TRAINING_PATTERN_DISABLE
);
}
static
void
intel_dp_link_down
(
struct
intel_output
*
intel_output
,
uint32_t
DP
)
{
struct
drm_device
*
dev
=
intel_output
->
base
.
dev
;
struct
drm_i915_private
*
dev_priv
=
dev
->
dev_private
;
struct
intel_dp_priv
*
dp_priv
=
intel_output
->
dev_priv
;
I915_WRITE
(
dp_priv
->
output_reg
,
DP
&
~
DP_PORT_EN
);
POSTING_READ
(
dp_priv
->
output_reg
);
}
static
void
intel_dp_restore
(
struct
drm_connector
*
connector
)
{
struct
intel_output
*
intel_output
=
to_intel_output
(
connector
);
struct
intel_dp_priv
*
dp_priv
=
intel_output
->
dev_priv
;
if
(
dp_priv
->
save_DP
&
DP_PORT_EN
)
intel_dp_link_train
(
intel_output
,
dp_priv
->
save_DP
,
dp_priv
->
save_link_configuration
);
else
intel_dp_link_down
(
intel_output
,
dp_priv
->
save_DP
);
}
#if 0
/*
* 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_output *intel_output)
{
struct intel_dp_priv *dp_priv = intel_output->dev_priv;
uint8_t link_status[DP_LINK_STATUS_SIZE];
if (!intel_output->enc.crtc)
return;
if (!intel_dp_get_link_status(intel_output, link_status)) {
intel_dp_link_down(intel_output, dp_priv->DP);
return;
}
if (!intel_channel_eq_ok(link_status, dp_priv->lane_count))
intel_dp_link_train(intel_output, dp_priv->DP, dp_priv->link_configuration);
}
#endif
/**
* 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
)
{
struct
intel_output
*
intel_output
=
to_intel_output
(
connector
);
struct
drm_device
*
dev
=
intel_output
->
base
.
dev
;
struct
drm_i915_private
*
dev_priv
=
dev
->
dev_private
;
struct
intel_dp_priv
*
dp_priv
=
intel_output
->
dev_priv
;
uint32_t
temp
,
bit
;
enum
drm_connector_status
status
;
dp_priv
->
has_audio
=
false
;
temp
=
I915_READ
(
PORT_HOTPLUG_EN
);
I915_WRITE
(
PORT_HOTPLUG_EN
,
temp
|
DPB_HOTPLUG_INT_EN
|
DPC_HOTPLUG_INT_EN
|
DPD_HOTPLUG_INT_EN
);
POSTING_READ
(
PORT_HOTPLUG_EN
);
switch
(
dp_priv
->
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
;
status
=
connector_status_disconnected
;
if
(
intel_dp_aux_native_read
(
intel_output
,
0x000
,
dp_priv
->
dpcd
,
sizeof
(
dp_priv
->
dpcd
))
==
sizeof
(
dp_priv
->
dpcd
))
{
if
(
dp_priv
->
dpcd
[
0
]
!=
0
)
status
=
connector_status_connected
;
}
return
status
;
}
static
int
intel_dp_get_modes
(
struct
drm_connector
*
connector
)
{
struct
intel_output
*
intel_output
=
to_intel_output
(
connector
);
/* We should parse the EDID data and find out if it has an audio sink
*/
return
intel_ddc_get_modes
(
intel_output
);
}
static
void
intel_dp_destroy
(
struct
drm_connector
*
connector
)
{
struct
intel_output
*
intel_output
=
to_intel_output
(
connector
);
if
(
intel_output
->
i2c_bus
)
intel_i2c_destroy
(
intel_output
->
i2c_bus
);
drm_sysfs_connector_remove
(
connector
);
drm_connector_cleanup
(
connector
);
kfree
(
intel_output
);
}
static
const
struct
drm_encoder_helper_funcs
intel_dp_helper_funcs
=
{
.
dpms
=
intel_dp_dpms
,
.
mode_fixup
=
intel_dp_mode_fixup
,
.
prepare
=
intel_encoder_prepare
,
.
mode_set
=
intel_dp_mode_set
,
.
commit
=
intel_encoder_commit
,
};
static
const
struct
drm_connector_funcs
intel_dp_connector_funcs
=
{
.
dpms
=
drm_helper_connector_dpms
,
.
save
=
intel_dp_save
,
.
restore
=
intel_dp_restore
,
.
detect
=
intel_dp_detect
,
.
fill_modes
=
drm_helper_probe_single_connector_modes
,
.
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
void
intel_dp_enc_destroy
(
struct
drm_encoder
*
encoder
)
{
drm_encoder_cleanup
(
encoder
);
}
static
const
struct
drm_encoder_funcs
intel_dp_enc_funcs
=
{
.
destroy
=
intel_dp_enc_destroy
,
};
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_output
*
intel_output
;
struct
intel_dp_priv
*
dp_priv
;
intel_output
=
kcalloc
(
sizeof
(
struct
intel_output
)
+
sizeof
(
struct
intel_dp_priv
),
1
,
GFP_KERNEL
);
if
(
!
intel_output
)
return
;
dp_priv
=
(
struct
intel_dp_priv
*
)(
intel_output
+
1
);
connector
=
&
intel_output
->
base
;
drm_connector_init
(
dev
,
connector
,
&
intel_dp_connector_funcs
,
DRM_MODE_CONNECTOR_DisplayPort
);
drm_connector_helper_add
(
connector
,
&
intel_dp_connector_helper_funcs
);
intel_output
->
type
=
INTEL_OUTPUT_DISPLAYPORT
;
connector
->
interlace_allowed
=
true
;
connector
->
doublescan_allowed
=
0
;
dp_priv
->
intel_output
=
intel_output
;
dp_priv
->
output_reg
=
output_reg
;
dp_priv
->
has_audio
=
false
;
intel_output
->
dev_priv
=
dp_priv
;
drm_encoder_init
(
dev
,
&
intel_output
->
enc
,
&
intel_dp_enc_funcs
,
DRM_MODE_ENCODER_TMDS
);
drm_encoder_helper_add
(
&
intel_output
->
enc
,
&
intel_dp_helper_funcs
);
drm_mode_connector_attach_encoder
(
&
intel_output
->
base
,
&
intel_output
->
enc
);
drm_sysfs_connector_add
(
connector
);
/* Set up the DDC bus. */
intel_dp_i2c_init
(
intel_output
,
(
output_reg
==
DP_B
)
?
"DPDDC-B"
:
(
output_reg
==
DP_C
)
?
"DPDDC-C"
:
"DPDDC-D"
);
intel_output
->
ddc_bus
=
&
dp_priv
->
adapter
;
/* 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/gpu/drm/i915/intel_dp.h
0 → 100644
View file @
a4fc5ed6
/*
* Copyright © 2008 Keith Packard
*
* 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_DP_H_
#define _INTEL_DP_H_
/* From the VESA DisplayPort spec */
#define AUX_NATIVE_WRITE 0x8
#define AUX_NATIVE_READ 0x9
#define AUX_I2C_WRITE 0x0
#define AUX_I2C_READ 0x1
#define AUX_I2C_STATUS 0x2
#define AUX_I2C_MOT 0x4
#define AUX_NATIVE_REPLY_ACK (0x0 << 4)
#define AUX_NATIVE_REPLY_NACK (0x1 << 4)
#define AUX_NATIVE_REPLY_DEFER (0x2 << 4)
#define AUX_NATIVE_REPLY_MASK (0x3 << 4)
#define AUX_I2C_REPLY_ACK (0x0 << 6)
#define AUX_I2C_REPLY_NACK (0x1 << 6)
#define AUX_I2C_REPLY_DEFER (0x2 << 6)
#define AUX_I2C_REPLY_MASK (0x3 << 6)
/* AUX CH addresses */
#define DP_LINK_BW_SET 0x100
# define DP_LINK_BW_1_62 0x06
# define DP_LINK_BW_2_7 0x0a
#define DP_LANE_COUNT_SET 0x101
# define DP_LANE_COUNT_MASK 0x0f
# define DP_LANE_COUNT_ENHANCED_FRAME_EN (1 << 7)
#define DP_TRAINING_PATTERN_SET 0x102
# define DP_TRAINING_PATTERN_DISABLE 0
# define DP_TRAINING_PATTERN_1 1
# define DP_TRAINING_PATTERN_2 2
# define DP_TRAINING_PATTERN_MASK 0x3
# define DP_LINK_QUAL_PATTERN_DISABLE (0 << 2)
# define DP_LINK_QUAL_PATTERN_D10_2 (1 << 2)
# define DP_LINK_QUAL_PATTERN_ERROR_RATE (2 << 2)
# define DP_LINK_QUAL_PATTERN_PRBS7 (3 << 2)
# define DP_LINK_QUAL_PATTERN_MASK (3 << 2)
# define DP_RECOVERED_CLOCK_OUT_EN (1 << 4)
# define DP_LINK_SCRAMBLING_DISABLE (1 << 5)
# define DP_SYMBOL_ERROR_COUNT_BOTH (0 << 6)
# define DP_SYMBOL_ERROR_COUNT_DISPARITY (1 << 6)
# define DP_SYMBOL_ERROR_COUNT_SYMBOL (2 << 6)
# define DP_SYMBOL_ERROR_COUNT_MASK (3 << 6)
#define DP_TRAINING_LANE0_SET 0x103
#define DP_TRAINING_LANE1_SET 0x104
#define DP_TRAINING_LANE2_SET 0x105
#define DP_TRAINING_LANE3_SET 0x106
# define DP_TRAIN_VOLTAGE_SWING_MASK 0x3
# define DP_TRAIN_VOLTAGE_SWING_SHIFT 0
# define DP_TRAIN_MAX_SWING_REACHED (1 << 2)
# define DP_TRAIN_VOLTAGE_SWING_400 (0 << 0)
# define DP_TRAIN_VOLTAGE_SWING_600 (1 << 0)
# define DP_TRAIN_VOLTAGE_SWING_800 (2 << 0)
# define DP_TRAIN_VOLTAGE_SWING_1200 (3 << 0)
# define DP_TRAIN_PRE_EMPHASIS_MASK (3 << 3)
# define DP_TRAIN_PRE_EMPHASIS_0 (0 << 3)
# define DP_TRAIN_PRE_EMPHASIS_3_5 (1 << 3)
# define DP_TRAIN_PRE_EMPHASIS_6 (2 << 3)
# define DP_TRAIN_PRE_EMPHASIS_9_5 (3 << 3)
# define DP_TRAIN_PRE_EMPHASIS_SHIFT 3
# define DP_TRAIN_MAX_PRE_EMPHASIS_REACHED (1 << 5)
#define DP_DOWNSPREAD_CTRL 0x107
# define DP_SPREAD_AMP_0_5 (1 << 4)
#define DP_MAIN_LINK_CHANNEL_CODING_SET 0x108
# define DP_SET_ANSI_8B10B (1 << 0)
#define DP_LANE0_1_STATUS 0x202
#define DP_LANE2_3_STATUS 0x203
# define DP_LANE_CR_DONE (1 << 0)
# define DP_LANE_CHANNEL_EQ_DONE (1 << 1)
# define DP_LANE_SYMBOL_LOCKED (1 << 2)
#define DP_LANE_ALIGN_STATUS_UPDATED 0x204
#define DP_INTERLANE_ALIGN_DONE (1 << 0)
#define DP_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6)
#define DP_LINK_STATUS_UPDATED (1 << 7)
#define DP_SINK_STATUS 0x205
#define DP_RECEIVE_PORT_0_STATUS (1 << 0)
#define DP_RECEIVE_PORT_1_STATUS (1 << 1)
#define DP_ADJUST_REQUEST_LANE0_1 0x206
#define DP_ADJUST_REQUEST_LANE2_3 0x207
#define DP_ADJUST_VOLTAGE_SWING_LANE0_MASK 0x03
#define DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT 0
#define DP_ADJUST_PRE_EMPHASIS_LANE0_MASK 0x0c
#define DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT 2
#define DP_ADJUST_VOLTAGE_SWING_LANE1_MASK 0x30
#define DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT 4
#define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK 0xc0
#define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT 6
struct
i2c_algo_dp_aux_data
{
bool
running
;
u16
address
;
int
(
*
aux_ch
)
(
struct
i2c_adapter
*
adapter
,
uint8_t
*
send
,
int
send_bytes
,
uint8_t
*
recv
,
int
recv_bytes
);
};
int
i2c_dp_aux_add_bus
(
struct
i2c_adapter
*
adapter
);
#endif
/* _INTEL_DP_H_ */
drivers/gpu/drm/i915/intel_dp_i2c.c
0 → 100644
View file @
a4fc5ed6
/*
* Copyright © 2009 Keith Packard
*
* 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.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/i2c.h>
#include "intel_dp.h"
/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */
#define MODE_I2C_START 1
#define MODE_I2C_WRITE 2
#define MODE_I2C_READ 4
#define MODE_I2C_STOP 8
static
int
i2c_algo_dp_aux_transaction
(
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
;
uint16_t
address
=
algo_data
->
address
;
uint8_t
msg
[
5
];
uint8_t
reply
[
2
];
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
(;;)
{
ret
=
(
*
algo_data
->
aux_ch
)(
adapter
,
msg
,
msg_bytes
,
reply
,
reply_bytes
);
if
(
ret
<
0
)
{
printk
(
KERN_ERR
"aux_ch failed %d
\n
"
,
ret
);
return
ret
;
}
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
:
printk
(
KERN_ERR
"aux_ch nack
\n
"
);
return
-
EREMOTEIO
;
case
AUX_I2C_REPLY_DEFER
:
printk
(
KERN_ERR
"aux_ch defer
\n
"
);
udelay
(
100
);
break
;
default:
printk
(
KERN_ERR
"aux_ch invalid reply 0x%02x
\n
"
,
reply
[
0
]);
return
-
EREMOTEIO
;
}
}
}
/*
* I2C over AUX CH
*/
/*
* Send the address. If the I2C link is running, this 'restarts'
* the connection with the new address, this is used for doing
* a write followed by a read (as needed for DDC)
*/
static
int
i2c_algo_dp_aux_address
(
struct
i2c_adapter
*
adapter
,
u16
address
,
bool
reading
)
{
struct
i2c_algo_dp_aux_data
*
algo_data
=
adapter
->
algo_data
;
int
mode
=
MODE_I2C_START
;
int
ret
;
if
(
reading
)
mode
|=
MODE_I2C_READ
;
else
mode
|=
MODE_I2C_WRITE
;
algo_data
->
address
=
address
;
algo_data
->
running
=
true
;
ret
=
i2c_algo_dp_aux_transaction
(
adapter
,
mode
,
0
,
NULL
);
return
ret
;
}
/*
* Stop the I2C transaction. This closes out the link, sending
* a bare address packet with the MOT bit turned off
*/
static
void
i2c_algo_dp_aux_stop
(
struct
i2c_adapter
*
adapter
,
bool
reading
)
{
struct
i2c_algo_dp_aux_data
*
algo_data
=
adapter
->
algo_data
;
int
mode
=
MODE_I2C_STOP
;
if
(
reading
)
mode
|=
MODE_I2C_READ
;
else
mode
|=
MODE_I2C_WRITE
;
if
(
algo_data
->
running
)
{
(
void
)
i2c_algo_dp_aux_transaction
(
adapter
,
mode
,
0
,
NULL
);
algo_data
->
running
=
false
;
}
}
/*
* Write a single byte to the current I2C address, the
* the I2C link must be running or this returns -EIO
*/
static
int
i2c_algo_dp_aux_put_byte
(
struct
i2c_adapter
*
adapter
,
u8
byte
)
{
struct
i2c_algo_dp_aux_data
*
algo_data
=
adapter
->
algo_data
;
int
ret
;
if
(
!
algo_data
->
running
)
return
-
EIO
;
ret
=
i2c_algo_dp_aux_transaction
(
adapter
,
MODE_I2C_WRITE
,
byte
,
NULL
);
return
ret
;
}
/*
* Read a single byte from the current I2C address, the
* I2C link must be running or this returns -EIO
*/
static
int
i2c_algo_dp_aux_get_byte
(
struct
i2c_adapter
*
adapter
,
u8
*
byte_ret
)
{
struct
i2c_algo_dp_aux_data
*
algo_data
=
adapter
->
algo_data
;
int
ret
;
if
(
!
algo_data
->
running
)
return
-
EIO
;
ret
=
i2c_algo_dp_aux_transaction
(
adapter
,
MODE_I2C_READ
,
0
,
byte_ret
);
return
ret
;
}
static
int
i2c_algo_dp_aux_xfer
(
struct
i2c_adapter
*
adapter
,
struct
i2c_msg
*
msgs
,
int
num
)
{
int
ret
=
0
;
bool
reading
=
false
;
int
m
;
int
b
;
for
(
m
=
0
;
m
<
num
;
m
++
)
{
u16
len
=
msgs
[
m
].
len
;
u8
*
buf
=
msgs
[
m
].
buf
;
reading
=
(
msgs
[
m
].
flags
&
I2C_M_RD
)
!=
0
;
ret
=
i2c_algo_dp_aux_address
(
adapter
,
msgs
[
m
].
addr
,
reading
);
if
(
ret
<
0
)
break
;
if
(
reading
)
{
for
(
b
=
0
;
b
<
len
;
b
++
)
{
ret
=
i2c_algo_dp_aux_get_byte
(
adapter
,
&
buf
[
b
]);
if
(
ret
<
0
)
break
;
}
}
else
{
for
(
b
=
0
;
b
<
len
;
b
++
)
{
ret
=
i2c_algo_dp_aux_put_byte
(
adapter
,
buf
[
b
]);
if
(
ret
<
0
)
break
;
}
}
if
(
ret
<
0
)
break
;
}
if
(
ret
>=
0
)
ret
=
num
;
i2c_algo_dp_aux_stop
(
adapter
,
reading
);
printk
(
KERN_ERR
"dp_aux_xfer return %d
\n
"
,
ret
);
return
ret
;
}
static
u32
i2c_algo_dp_aux_functionality
(
struct
i2c_adapter
*
adapter
)
{
return
I2C_FUNC_I2C
|
I2C_FUNC_SMBUS_EMUL
|
I2C_FUNC_SMBUS_READ_BLOCK_DATA
|
I2C_FUNC_SMBUS_BLOCK_PROC_CALL
|
I2C_FUNC_10BIT_ADDR
;
}
static
const
struct
i2c_algorithm
i2c_dp_aux_algo
=
{
.
master_xfer
=
i2c_algo_dp_aux_xfer
,
.
functionality
=
i2c_algo_dp_aux_functionality
,
};
static
void
i2c_dp_aux_reset_bus
(
struct
i2c_adapter
*
adapter
)
{
(
void
)
i2c_algo_dp_aux_address
(
adapter
,
0
,
false
);
(
void
)
i2c_algo_dp_aux_stop
(
adapter
,
false
);
}
static
int
i2c_dp_aux_prepare_bus
(
struct
i2c_adapter
*
adapter
)
{
adapter
->
algo
=
&
i2c_dp_aux_algo
;
adapter
->
retries
=
3
;
i2c_dp_aux_reset_bus
(
adapter
);
return
0
;
}
int
i2c_dp_aux_add_bus
(
struct
i2c_adapter
*
adapter
)
{
int
error
;
error
=
i2c_dp_aux_prepare_bus
(
adapter
);
if
(
error
)
return
error
;
error
=
i2c_add_adapter
(
adapter
);
return
error
;
}
EXPORT_SYMBOL
(
i2c_dp_aux_add_bus
);
drivers/gpu/drm/i915/intel_drv.h
View file @
a4fc5ed6
...
...
@@ -54,6 +54,7 @@
#define INTEL_OUTPUT_LVDS 4
#define INTEL_OUTPUT_TVOUT 5
#define INTEL_OUTPUT_HDMI 6
#define INTEL_OUTPUT_DISPLAYPORT 7
#define INTEL_DVO_CHIP_NONE 0
#define INTEL_DVO_CHIP_LVDS 1
...
...
@@ -116,6 +117,10 @@ extern bool intel_sdvo_init(struct drm_device *dev, int output_device);
extern
void
intel_dvo_init
(
struct
drm_device
*
dev
);
extern
void
intel_tv_init
(
struct
drm_device
*
dev
);
extern
void
intel_lvds_init
(
struct
drm_device
*
dev
);
extern
void
intel_dp_init
(
struct
drm_device
*
dev
,
int
dp_reg
);
void
intel_dp_set_m_n
(
struct
drm_crtc
*
crtc
,
struct
drm_display_mode
*
mode
,
struct
drm_display_mode
*
adjusted_mode
);
extern
void
intel_crtc_load_lut
(
struct
drm_crtc
*
crtc
);
extern
void
intel_encoder_prepare
(
struct
drm_encoder
*
encoder
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment