Commit 7d57382e65994ab7d01741373bd1c420370aed9f
Committed by
Dave Airlie
1 parent
3f8bc370ac
Exists in
master
and in
20 other branches
drm/i915: Add support for integrated HDMI on G4X hardware.
This is ported directly from the userland 2D driver code. The HDMI audio bits aren't hooked up yet. Signed-off-by: Eric Anholt <eric@anholt.net> Signed-off-by: Dave Airlie <airlied@linux.ie>
Showing 7 changed files with 322 additions and 7 deletions Side-by-side Diff
drivers/gpu/drm/i915/Makefile
drivers/gpu/drm/i915/i915_drv.h
... | ... | @@ -664,6 +664,7 @@ |
664 | 664 | writel(upper_32_bits(val), dev_priv->regs + \ |
665 | 665 | (reg) + 4)) |
666 | 666 | #endif |
667 | +#define POSTING_READ(reg) (void)I915_READ(reg) | |
667 | 668 | |
668 | 669 | #define I915_VERBOSE 0 |
669 | 670 | |
... | ... | @@ -760,6 +761,7 @@ |
760 | 761 | IS_I945GM(dev) || IS_I965GM(dev) || IS_GM45(dev)) |
761 | 762 | |
762 | 763 | #define I915_NEED_GFX_HWS(dev) (IS_G33(dev) || IS_GM45(dev) || IS_G4X(dev)) |
764 | +#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev)) | |
763 | 765 | |
764 | 766 | #define PRIMARY_RINGBUFFER_SIZE (128*1024) |
765 | 767 |
drivers/gpu/drm/i915/i915_reg.h
... | ... | @@ -549,6 +549,8 @@ |
549 | 549 | /** GM965 GM45 render standby register */ |
550 | 550 | #define MCHBAR_RENDER_STANDBY 0x111B8 |
551 | 551 | |
552 | +#define PEG_BAND_GAP_DATA 0x14d68 | |
553 | + | |
552 | 554 | /* |
553 | 555 | * Overlay regs |
554 | 556 | */ |
... | ... | @@ -612,6 +614,9 @@ |
612 | 614 | |
613 | 615 | /* Hotplug control (945+ only) */ |
614 | 616 | #define PORT_HOTPLUG_EN 0x61110 |
617 | +#define HDMIB_HOTPLUG_INT_EN (1 << 29) | |
618 | +#define HDMIC_HOTPLUG_INT_EN (1 << 28) | |
619 | +#define HDMID_HOTPLUG_INT_EN (1 << 27) | |
615 | 620 | #define SDVOB_HOTPLUG_INT_EN (1 << 26) |
616 | 621 | #define SDVOC_HOTPLUG_INT_EN (1 << 25) |
617 | 622 | #define TV_HOTPLUG_INT_EN (1 << 18) |
... | ... | @@ -619,6 +624,9 @@ |
619 | 624 | #define CRT_HOTPLUG_FORCE_DETECT (1 << 3) |
620 | 625 | |
621 | 626 | #define PORT_HOTPLUG_STAT 0x61114 |
627 | +#define HDMIB_HOTPLUG_INT_STATUS (1 << 29) | |
628 | +#define HDMIC_HOTPLUG_INT_STATUS (1 << 28) | |
629 | +#define HDMID_HOTPLUG_INT_STATUS (1 << 27) | |
622 | 630 | #define CRT_HOTPLUG_INT_STATUS (1 << 11) |
623 | 631 | #define TV_HOTPLUG_INT_STATUS (1 << 10) |
624 | 632 | #define CRT_HOTPLUG_MONITOR_MASK (3 << 8) |
625 | 633 | |
... | ... | @@ -648,7 +656,16 @@ |
648 | 656 | #define SDVO_PHASE_SELECT_DEFAULT (6 << 19) |
649 | 657 | #define SDVO_CLOCK_OUTPUT_INVERT (1 << 18) |
650 | 658 | #define SDVOC_GANG_MODE (1 << 16) |
659 | +#define SDVO_ENCODING_SDVO (0x0 << 10) | |
660 | +#define SDVO_ENCODING_HDMI (0x2 << 10) | |
661 | +/** Requird for HDMI operation */ | |
662 | +#define SDVO_NULL_PACKETS_DURING_VSYNC (1 << 9) | |
651 | 663 | #define SDVO_BORDER_ENABLE (1 << 7) |
664 | +#define SDVO_AUDIO_ENABLE (1 << 6) | |
665 | +/** New with 965, default is to be set */ | |
666 | +#define SDVO_VSYNC_ACTIVE_HIGH (1 << 4) | |
667 | +/** New with 965, default is to be set */ | |
668 | +#define SDVO_HSYNC_ACTIVE_HIGH (1 << 3) | |
652 | 669 | #define SDVOB_PCIE_CONCURRENCY (1 << 3) |
653 | 670 | #define SDVO_DETECTED (1 << 2) |
654 | 671 | /* Bits to be preserved when writing */ |
drivers/gpu/drm/i915/intel_display.c
... | ... | @@ -751,6 +751,7 @@ |
751 | 751 | is_lvds = true; |
752 | 752 | break; |
753 | 753 | case INTEL_OUTPUT_SDVO: |
754 | + case INTEL_OUTPUT_HDMI: | |
754 | 755 | is_sdvo = true; |
755 | 756 | break; |
756 | 757 | case INTEL_OUTPUT_DVO: |
... | ... | @@ -1443,8 +1444,15 @@ |
1443 | 1444 | intel_lvds_init(dev); |
1444 | 1445 | |
1445 | 1446 | if (IS_I9XX(dev)) { |
1446 | - intel_sdvo_init(dev, SDVOB); | |
1447 | - intel_sdvo_init(dev, SDVOC); | |
1447 | + int found; | |
1448 | + | |
1449 | + found = intel_sdvo_init(dev, SDVOB); | |
1450 | + if (!found && SUPPORTS_INTEGRATED_HDMI(dev)) | |
1451 | + intel_hdmi_init(dev, SDVOB); | |
1452 | + | |
1453 | + found = intel_sdvo_init(dev, SDVOC); | |
1454 | + if (!found && SUPPORTS_INTEGRATED_HDMI(dev)) | |
1455 | + intel_hdmi_init(dev, SDVOC); | |
1448 | 1456 | } else |
1449 | 1457 | intel_dvo_init(dev); |
1450 | 1458 | |
... | ... | @@ -1458,6 +1466,11 @@ |
1458 | 1466 | |
1459 | 1467 | /* valid crtcs */ |
1460 | 1468 | switch(intel_output->type) { |
1469 | + case INTEL_OUTPUT_HDMI: | |
1470 | + crtc_mask = ((1 << 0)| | |
1471 | + (1 << 1)); | |
1472 | + clone_mask = ((1 << INTEL_OUTPUT_HDMI)); | |
1473 | + break; | |
1461 | 1474 | case INTEL_OUTPUT_DVO: |
1462 | 1475 | case INTEL_OUTPUT_SDVO: |
1463 | 1476 | crtc_mask = ((1 << 0)| |
drivers/gpu/drm/i915/intel_drv.h
... | ... | @@ -53,6 +53,7 @@ |
53 | 53 | #define INTEL_OUTPUT_SDVO 3 |
54 | 54 | #define INTEL_OUTPUT_LVDS 4 |
55 | 55 | #define INTEL_OUTPUT_TVOUT 5 |
56 | +#define INTEL_OUTPUT_HDMI 6 | |
56 | 57 | |
57 | 58 | #define INTEL_DVO_CHIP_NONE 0 |
58 | 59 | #define INTEL_DVO_CHIP_LVDS 1 |
... | ... | @@ -109,7 +110,8 @@ |
109 | 110 | extern bool intel_ddc_probe(struct intel_output *intel_output); |
110 | 111 | |
111 | 112 | extern void intel_crt_init(struct drm_device *dev); |
112 | -extern void intel_sdvo_init(struct drm_device *dev, int output_device); | |
113 | +extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg); | |
114 | +extern bool intel_sdvo_init(struct drm_device *dev, int output_device); | |
113 | 115 | extern void intel_dvo_init(struct drm_device *dev); |
114 | 116 | extern void intel_tv_init(struct drm_device *dev); |
115 | 117 | extern void intel_lvds_init(struct drm_device *dev); |
drivers/gpu/drm/i915/intel_hdmi.c
1 | +/* | |
2 | + * Copyright 2006 Dave Airlie <airlied@linux.ie> | |
3 | + * Copyright ยฉ 2006-2009 Intel Corporation | |
4 | + * | |
5 | + * Permission is hereby granted, free of charge, to any person obtaining a | |
6 | + * copy of this software and associated documentation files (the "Software"), | |
7 | + * to deal in the Software without restriction, including without limitation | |
8 | + * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
9 | + * and/or sell copies of the Software, and to permit persons to whom the | |
10 | + * Software is furnished to do so, subject to the following conditions: | |
11 | + * | |
12 | + * The above copyright notice and this permission notice (including the next | |
13 | + * paragraph) shall be included in all copies or substantial portions of the | |
14 | + * Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
21 | + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
22 | + * DEALINGS IN THE SOFTWARE. | |
23 | + * | |
24 | + * Authors: | |
25 | + * Eric Anholt <eric@anholt.net> | |
26 | + * Jesse Barnes <jesse.barnes@intel.com> | |
27 | + */ | |
28 | + | |
29 | +#include <linux/i2c.h> | |
30 | +#include <linux/delay.h> | |
31 | +#include "drmP.h" | |
32 | +#include "drm.h" | |
33 | +#include "drm_crtc.h" | |
34 | +#include "intel_drv.h" | |
35 | +#include "i915_drm.h" | |
36 | +#include "i915_drv.h" | |
37 | + | |
38 | +struct intel_hdmi_priv { | |
39 | + u32 sdvox_reg; | |
40 | + u32 save_SDVOX; | |
41 | + int has_hdmi_sink; | |
42 | +}; | |
43 | + | |
44 | +static void intel_hdmi_mode_set(struct drm_encoder *encoder, | |
45 | + struct drm_display_mode *mode, | |
46 | + struct drm_display_mode *adjusted_mode) | |
47 | +{ | |
48 | + struct drm_device *dev = encoder->dev; | |
49 | + struct drm_i915_private *dev_priv = dev->dev_private; | |
50 | + struct drm_crtc *crtc = encoder->crtc; | |
51 | + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); | |
52 | + struct intel_output *intel_output = enc_to_intel_output(encoder); | |
53 | + struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv; | |
54 | + u32 sdvox; | |
55 | + | |
56 | + sdvox = SDVO_ENCODING_HDMI | | |
57 | + SDVO_BORDER_ENABLE | | |
58 | + SDVO_VSYNC_ACTIVE_HIGH | | |
59 | + SDVO_HSYNC_ACTIVE_HIGH; | |
60 | + | |
61 | + if (hdmi_priv->has_hdmi_sink) | |
62 | + sdvox |= SDVO_AUDIO_ENABLE; | |
63 | + | |
64 | + if (intel_crtc->pipe == 1) | |
65 | + sdvox |= SDVO_PIPE_B_SELECT; | |
66 | + | |
67 | + I915_WRITE(hdmi_priv->sdvox_reg, sdvox); | |
68 | + POSTING_READ(hdmi_priv->sdvox_reg); | |
69 | +} | |
70 | + | |
71 | +static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) | |
72 | +{ | |
73 | + struct drm_device *dev = encoder->dev; | |
74 | + struct drm_i915_private *dev_priv = dev->dev_private; | |
75 | + struct intel_output *intel_output = enc_to_intel_output(encoder); | |
76 | + struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv; | |
77 | + u32 temp; | |
78 | + | |
79 | + if (mode != DRM_MODE_DPMS_ON) { | |
80 | + temp = I915_READ(hdmi_priv->sdvox_reg); | |
81 | + I915_WRITE(hdmi_priv->sdvox_reg, temp & ~SDVO_ENABLE); | |
82 | + } else { | |
83 | + temp = I915_READ(hdmi_priv->sdvox_reg); | |
84 | + I915_WRITE(hdmi_priv->sdvox_reg, temp | SDVO_ENABLE); | |
85 | + } | |
86 | + POSTING_READ(hdmi_priv->sdvox_reg); | |
87 | +} | |
88 | + | |
89 | +static void intel_hdmi_save(struct drm_connector *connector) | |
90 | +{ | |
91 | + struct drm_device *dev = connector->dev; | |
92 | + struct drm_i915_private *dev_priv = dev->dev_private; | |
93 | + struct intel_output *intel_output = to_intel_output(connector); | |
94 | + struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv; | |
95 | + | |
96 | + hdmi_priv->save_SDVOX = I915_READ(hdmi_priv->sdvox_reg); | |
97 | +} | |
98 | + | |
99 | +static void intel_hdmi_restore(struct drm_connector *connector) | |
100 | +{ | |
101 | + struct drm_device *dev = connector->dev; | |
102 | + struct drm_i915_private *dev_priv = dev->dev_private; | |
103 | + struct intel_output *intel_output = to_intel_output(connector); | |
104 | + struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv; | |
105 | + | |
106 | + I915_WRITE(hdmi_priv->sdvox_reg, hdmi_priv->save_SDVOX); | |
107 | + POSTING_READ(hdmi_priv->sdvox_reg); | |
108 | +} | |
109 | + | |
110 | +static int intel_hdmi_mode_valid(struct drm_connector *connector, | |
111 | + struct drm_display_mode *mode) | |
112 | +{ | |
113 | + if (mode->clock > 165000) | |
114 | + return MODE_CLOCK_HIGH; | |
115 | + if (mode->clock < 20000) | |
116 | + return MODE_CLOCK_HIGH; | |
117 | + | |
118 | + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) | |
119 | + return MODE_NO_DBLESCAN; | |
120 | + | |
121 | + return MODE_OK; | |
122 | +} | |
123 | + | |
124 | +static bool intel_hdmi_mode_fixup(struct drm_encoder *encoder, | |
125 | + struct drm_display_mode *mode, | |
126 | + struct drm_display_mode *adjusted_mode) | |
127 | +{ | |
128 | + return true; | |
129 | +} | |
130 | + | |
131 | +static enum drm_connector_status | |
132 | +intel_hdmi_detect(struct drm_connector *connector) | |
133 | +{ | |
134 | + struct drm_device *dev = connector->dev; | |
135 | + struct drm_i915_private *dev_priv = dev->dev_private; | |
136 | + struct intel_output *intel_output = to_intel_output(connector); | |
137 | + struct intel_hdmi_priv *hdmi_priv = intel_output->dev_priv; | |
138 | + u32 temp, bit; | |
139 | + | |
140 | + temp = I915_READ(PORT_HOTPLUG_EN); | |
141 | + | |
142 | + I915_WRITE(PORT_HOTPLUG_EN, | |
143 | + temp | | |
144 | + HDMIB_HOTPLUG_INT_EN | | |
145 | + HDMIC_HOTPLUG_INT_EN | | |
146 | + HDMID_HOTPLUG_INT_EN); | |
147 | + | |
148 | + POSTING_READ(PORT_HOTPLUG_EN); | |
149 | + | |
150 | + switch (hdmi_priv->sdvox_reg) { | |
151 | + case SDVOB: | |
152 | + bit = HDMIB_HOTPLUG_INT_STATUS; | |
153 | + break; | |
154 | + case SDVOC: | |
155 | + bit = HDMIC_HOTPLUG_INT_STATUS; | |
156 | + break; | |
157 | + default: | |
158 | + return connector_status_unknown; | |
159 | + } | |
160 | + | |
161 | + if ((I915_READ(PORT_HOTPLUG_STAT) & bit) != 0) | |
162 | + return connector_status_connected; | |
163 | + else | |
164 | + return connector_status_disconnected; | |
165 | +} | |
166 | + | |
167 | +static int intel_hdmi_get_modes(struct drm_connector *connector) | |
168 | +{ | |
169 | + struct intel_output *intel_output = to_intel_output(connector); | |
170 | + | |
171 | + /* We should parse the EDID data and find out if it's an HDMI sink so | |
172 | + * we can send audio to it. | |
173 | + */ | |
174 | + | |
175 | + return intel_ddc_get_modes(intel_output); | |
176 | +} | |
177 | + | |
178 | +static void intel_hdmi_destroy(struct drm_connector *connector) | |
179 | +{ | |
180 | + struct intel_output *intel_output = to_intel_output(connector); | |
181 | + | |
182 | + if (intel_output->i2c_bus) | |
183 | + intel_i2c_destroy(intel_output->i2c_bus); | |
184 | + drm_sysfs_connector_remove(connector); | |
185 | + drm_connector_cleanup(connector); | |
186 | + kfree(intel_output); | |
187 | +} | |
188 | + | |
189 | +static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = { | |
190 | + .dpms = intel_hdmi_dpms, | |
191 | + .mode_fixup = intel_hdmi_mode_fixup, | |
192 | + .prepare = intel_encoder_prepare, | |
193 | + .mode_set = intel_hdmi_mode_set, | |
194 | + .commit = intel_encoder_commit, | |
195 | +}; | |
196 | + | |
197 | +static const struct drm_connector_funcs intel_hdmi_connector_funcs = { | |
198 | + .save = intel_hdmi_save, | |
199 | + .restore = intel_hdmi_restore, | |
200 | + .detect = intel_hdmi_detect, | |
201 | + .fill_modes = drm_helper_probe_single_connector_modes, | |
202 | + .destroy = intel_hdmi_destroy, | |
203 | +}; | |
204 | + | |
205 | +static const struct drm_connector_helper_funcs intel_hdmi_connector_helper_funcs = { | |
206 | + .get_modes = intel_hdmi_get_modes, | |
207 | + .mode_valid = intel_hdmi_mode_valid, | |
208 | + .best_encoder = intel_best_encoder, | |
209 | +}; | |
210 | + | |
211 | +static void intel_hdmi_enc_destroy(struct drm_encoder *encoder) | |
212 | +{ | |
213 | + drm_encoder_cleanup(encoder); | |
214 | +} | |
215 | + | |
216 | +static const struct drm_encoder_funcs intel_hdmi_enc_funcs = { | |
217 | + .destroy = intel_hdmi_enc_destroy, | |
218 | +}; | |
219 | + | |
220 | + | |
221 | +void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) | |
222 | +{ | |
223 | + struct drm_i915_private *dev_priv = dev->dev_private; | |
224 | + struct drm_connector *connector; | |
225 | + struct intel_output *intel_output; | |
226 | + struct intel_hdmi_priv *hdmi_priv; | |
227 | + | |
228 | + intel_output = kcalloc(sizeof(struct intel_output) + | |
229 | + sizeof(struct intel_hdmi_priv), 1, GFP_KERNEL); | |
230 | + if (!intel_output) | |
231 | + return; | |
232 | + hdmi_priv = (struct intel_hdmi_priv *)(intel_output + 1); | |
233 | + | |
234 | + connector = &intel_output->base; | |
235 | + drm_connector_init(dev, connector, &intel_hdmi_connector_funcs, | |
236 | + DRM_MODE_CONNECTOR_DVID); | |
237 | + drm_connector_helper_add(connector, &intel_hdmi_connector_helper_funcs); | |
238 | + | |
239 | + intel_output->type = INTEL_OUTPUT_HDMI; | |
240 | + | |
241 | + connector->interlace_allowed = 0; | |
242 | + connector->doublescan_allowed = 0; | |
243 | + | |
244 | + /* Set up the DDC bus. */ | |
245 | + if (sdvox_reg == SDVOB) | |
246 | + intel_output->ddc_bus = intel_i2c_create(dev, GPIOE, "HDMIB"); | |
247 | + else | |
248 | + intel_output->ddc_bus = intel_i2c_create(dev, GPIOD, "HDMIC"); | |
249 | + | |
250 | + if (!intel_output->ddc_bus) | |
251 | + goto err_connector; | |
252 | + | |
253 | + hdmi_priv->sdvox_reg = sdvox_reg; | |
254 | + intel_output->dev_priv = hdmi_priv; | |
255 | + | |
256 | + drm_encoder_init(dev, &intel_output->enc, &intel_hdmi_enc_funcs, | |
257 | + DRM_MODE_ENCODER_TMDS); | |
258 | + drm_encoder_helper_add(&intel_output->enc, &intel_hdmi_helper_funcs); | |
259 | + | |
260 | + drm_mode_connector_attach_encoder(&intel_output->base, | |
261 | + &intel_output->enc); | |
262 | + drm_sysfs_connector_add(connector); | |
263 | + | |
264 | + /* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written | |
265 | + * 0xd. Failure to do so will result in spurious interrupts being | |
266 | + * generated on the port when a cable is not attached. | |
267 | + */ | |
268 | + if (IS_G4X(dev) && !IS_GM45(dev)) { | |
269 | + u32 temp = I915_READ(PEG_BAND_GAP_DATA); | |
270 | + I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd); | |
271 | + } | |
272 | + | |
273 | + return; | |
274 | + | |
275 | +err_connector: | |
276 | + drm_connector_cleanup(connector); | |
277 | + kfree(intel_output); | |
278 | + | |
279 | + return; | |
280 | +} |
drivers/gpu/drm/i915/intel_sdvo.c
... | ... | @@ -978,7 +978,7 @@ |
978 | 978 | }; |
979 | 979 | |
980 | 980 | |
981 | -void intel_sdvo_init(struct drm_device *dev, int output_device) | |
981 | +bool intel_sdvo_init(struct drm_device *dev, int output_device) | |
982 | 982 | { |
983 | 983 | struct drm_connector *connector; |
984 | 984 | struct intel_output *intel_output; |
... | ... | @@ -991,7 +991,7 @@ |
991 | 991 | |
992 | 992 | intel_output = kcalloc(sizeof(struct intel_output)+sizeof(struct intel_sdvo_priv), 1, GFP_KERNEL); |
993 | 993 | if (!intel_output) { |
994 | - return; | |
994 | + return false; | |
995 | 995 | } |
996 | 996 | |
997 | 997 | connector = &intel_output->base; |
... | ... | @@ -1116,7 +1116,7 @@ |
1116 | 1116 | |
1117 | 1117 | intel_output->ddc_bus = i2cbus; |
1118 | 1118 | |
1119 | - return; | |
1119 | + return true; | |
1120 | 1120 | |
1121 | 1121 | err_i2c: |
1122 | 1122 | intel_i2c_destroy(intel_output->i2c_bus); |
... | ... | @@ -1124,6 +1124,6 @@ |
1124 | 1124 | drm_connector_cleanup(connector); |
1125 | 1125 | kfree(intel_output); |
1126 | 1126 | |
1127 | - return; | |
1127 | + return false; | |
1128 | 1128 | } |