diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_hdmi.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_hdmi.c | 158 |
1 files changed, 134 insertions, 24 deletions
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 1ed8e6903915..226ba830f383 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c | |||
@@ -45,6 +45,8 @@ struct intel_hdmi { | |||
45 | bool has_hdmi_sink; | 45 | bool has_hdmi_sink; |
46 | bool has_audio; | 46 | bool has_audio; |
47 | int force_audio; | 47 | int force_audio; |
48 | void (*write_infoframe)(struct drm_encoder *encoder, | ||
49 | struct dip_infoframe *frame); | ||
48 | }; | 50 | }; |
49 | 51 | ||
50 | static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder) | 52 | static struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder) |
@@ -58,37 +60,70 @@ static struct intel_hdmi *intel_attached_hdmi(struct drm_connector *connector) | |||
58 | struct intel_hdmi, base); | 60 | struct intel_hdmi, base); |
59 | } | 61 | } |
60 | 62 | ||
61 | void intel_dip_infoframe_csum(struct dip_infoframe *avi_if) | 63 | void intel_dip_infoframe_csum(struct dip_infoframe *frame) |
62 | { | 64 | { |
63 | uint8_t *data = (uint8_t *)avi_if; | 65 | uint8_t *data = (uint8_t *)frame; |
64 | uint8_t sum = 0; | 66 | uint8_t sum = 0; |
65 | unsigned i; | 67 | unsigned i; |
66 | 68 | ||
67 | avi_if->checksum = 0; | 69 | frame->checksum = 0; |
68 | avi_if->ecc = 0; | 70 | frame->ecc = 0; |
69 | 71 | ||
70 | for (i = 0; i < sizeof(*avi_if); i++) | 72 | /* Header isn't part of the checksum */ |
73 | for (i = 5; i < frame->len; i++) | ||
71 | sum += data[i]; | 74 | sum += data[i]; |
72 | 75 | ||
73 | avi_if->checksum = 0x100 - sum; | 76 | frame->checksum = 0x100 - sum; |
74 | } | 77 | } |
75 | 78 | ||
76 | static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) | 79 | static u32 intel_infoframe_index(struct dip_infoframe *frame) |
77 | { | 80 | { |
78 | struct dip_infoframe avi_if = { | 81 | u32 flags = 0; |
79 | .type = DIP_TYPE_AVI, | 82 | |
80 | .ver = DIP_VERSION_AVI, | 83 | switch (frame->type) { |
81 | .len = DIP_LEN_AVI, | 84 | case DIP_TYPE_AVI: |
82 | }; | 85 | flags |= VIDEO_DIP_SELECT_AVI; |
83 | uint32_t *data = (uint32_t *)&avi_if; | 86 | break; |
87 | case DIP_TYPE_SPD: | ||
88 | flags |= VIDEO_DIP_SELECT_SPD; | ||
89 | break; | ||
90 | default: | ||
91 | DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); | ||
92 | break; | ||
93 | } | ||
94 | |||
95 | return flags; | ||
96 | } | ||
97 | |||
98 | static u32 intel_infoframe_flags(struct dip_infoframe *frame) | ||
99 | { | ||
100 | u32 flags = 0; | ||
101 | |||
102 | switch (frame->type) { | ||
103 | case DIP_TYPE_AVI: | ||
104 | flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC; | ||
105 | break; | ||
106 | case DIP_TYPE_SPD: | ||
107 | flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_2VSYNC; | ||
108 | break; | ||
109 | default: | ||
110 | DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); | ||
111 | break; | ||
112 | } | ||
113 | |||
114 | return flags; | ||
115 | } | ||
116 | |||
117 | static void i9xx_write_infoframe(struct drm_encoder *encoder, | ||
118 | struct dip_infoframe *frame) | ||
119 | { | ||
120 | uint32_t *data = (uint32_t *)frame; | ||
84 | struct drm_device *dev = encoder->dev; | 121 | struct drm_device *dev = encoder->dev; |
85 | struct drm_i915_private *dev_priv = dev->dev_private; | 122 | struct drm_i915_private *dev_priv = dev->dev_private; |
86 | struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); | 123 | struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); |
87 | u32 port; | 124 | u32 port, flags, val = I915_READ(VIDEO_DIP_CTL); |
88 | unsigned i; | 125 | unsigned i, len = DIP_HEADER_SIZE + frame->len; |
89 | 126 | ||
90 | if (!intel_hdmi->has_hdmi_sink) | ||
91 | return; | ||
92 | 127 | ||
93 | /* XXX first guess at handling video port, is this corrent? */ | 128 | /* XXX first guess at handling video port, is this corrent? */ |
94 | if (intel_hdmi->sdvox_reg == SDVOB) | 129 | if (intel_hdmi->sdvox_reg == SDVOB) |
@@ -98,18 +133,87 @@ static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) | |||
98 | else | 133 | else |
99 | return; | 134 | return; |
100 | 135 | ||
101 | I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port | | 136 | flags = intel_infoframe_index(frame); |
102 | VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC); | 137 | |
138 | val &= ~VIDEO_DIP_SELECT_MASK; | ||
103 | 139 | ||
104 | intel_dip_infoframe_csum(&avi_if); | 140 | I915_WRITE(VIDEO_DIP_CTL, val | port | flags); |
105 | for (i = 0; i < sizeof(avi_if); i += 4) { | 141 | |
142 | for (i = 0; i < len; i += 4) { | ||
106 | I915_WRITE(VIDEO_DIP_DATA, *data); | 143 | I915_WRITE(VIDEO_DIP_DATA, *data); |
107 | data++; | 144 | data++; |
108 | } | 145 | } |
109 | 146 | ||
110 | I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | port | | 147 | flags |= intel_infoframe_flags(frame); |
111 | VIDEO_DIP_SELECT_AVI | VIDEO_DIP_FREQ_VSYNC | | 148 | |
112 | VIDEO_DIP_ENABLE_AVI); | 149 | I915_WRITE(VIDEO_DIP_CTL, VIDEO_DIP_ENABLE | val | port | flags); |
150 | } | ||
151 | |||
152 | static void ironlake_write_infoframe(struct drm_encoder *encoder, | ||
153 | struct dip_infoframe *frame) | ||
154 | { | ||
155 | uint32_t *data = (uint32_t *)frame; | ||
156 | struct drm_device *dev = encoder->dev; | ||
157 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
158 | struct drm_crtc *crtc = encoder->crtc; | ||
159 | struct intel_crtc *intel_crtc = to_intel_crtc(crtc); | ||
160 | int reg = TVIDEO_DIP_CTL(intel_crtc->pipe); | ||
161 | unsigned i, len = DIP_HEADER_SIZE + frame->len; | ||
162 | u32 flags, val = I915_READ(reg); | ||
163 | |||
164 | intel_wait_for_vblank(dev, intel_crtc->pipe); | ||
165 | |||
166 | flags = intel_infoframe_index(frame); | ||
167 | |||
168 | val &= ~VIDEO_DIP_SELECT_MASK; | ||
169 | |||
170 | I915_WRITE(reg, val | flags); | ||
171 | |||
172 | for (i = 0; i < len; i += 4) { | ||
173 | I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data); | ||
174 | data++; | ||
175 | } | ||
176 | |||
177 | flags |= intel_infoframe_flags(frame); | ||
178 | |||
179 | I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags); | ||
180 | } | ||
181 | static void intel_set_infoframe(struct drm_encoder *encoder, | ||
182 | struct dip_infoframe *frame) | ||
183 | { | ||
184 | struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); | ||
185 | |||
186 | if (!intel_hdmi->has_hdmi_sink) | ||
187 | return; | ||
188 | |||
189 | intel_dip_infoframe_csum(frame); | ||
190 | intel_hdmi->write_infoframe(encoder, frame); | ||
191 | } | ||
192 | |||
193 | static void intel_hdmi_set_avi_infoframe(struct drm_encoder *encoder) | ||
194 | { | ||
195 | struct dip_infoframe avi_if = { | ||
196 | .type = DIP_TYPE_AVI, | ||
197 | .ver = DIP_VERSION_AVI, | ||
198 | .len = DIP_LEN_AVI, | ||
199 | }; | ||
200 | |||
201 | intel_set_infoframe(encoder, &avi_if); | ||
202 | } | ||
203 | |||
204 | static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder) | ||
205 | { | ||
206 | struct dip_infoframe spd_if; | ||
207 | |||
208 | memset(&spd_if, 0, sizeof(spd_if)); | ||
209 | spd_if.type = DIP_TYPE_SPD; | ||
210 | spd_if.ver = DIP_VERSION_SPD; | ||
211 | spd_if.len = DIP_LEN_SPD; | ||
212 | strcpy(spd_if.body.spd.vn, "Intel"); | ||
213 | strcpy(spd_if.body.spd.pd, "Integrated gfx"); | ||
214 | spd_if.body.spd.sdi = DIP_SPD_PC; | ||
215 | |||
216 | intel_set_infoframe(encoder, &spd_if); | ||
113 | } | 217 | } |
114 | 218 | ||
115 | static void intel_hdmi_mode_set(struct drm_encoder *encoder, | 219 | static void intel_hdmi_mode_set(struct drm_encoder *encoder, |
@@ -156,6 +260,7 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder, | |||
156 | POSTING_READ(intel_hdmi->sdvox_reg); | 260 | POSTING_READ(intel_hdmi->sdvox_reg); |
157 | 261 | ||
158 | intel_hdmi_set_avi_infoframe(encoder); | 262 | intel_hdmi_set_avi_infoframe(encoder); |
263 | intel_hdmi_set_spd_infoframe(encoder); | ||
159 | } | 264 | } |
160 | 265 | ||
161 | static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) | 266 | static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode) |
@@ -433,6 +538,11 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) | |||
433 | 538 | ||
434 | intel_hdmi->sdvox_reg = sdvox_reg; | 539 | intel_hdmi->sdvox_reg = sdvox_reg; |
435 | 540 | ||
541 | if (!HAS_PCH_SPLIT(dev)) | ||
542 | intel_hdmi->write_infoframe = i9xx_write_infoframe; | ||
543 | else | ||
544 | intel_hdmi->write_infoframe = ironlake_write_infoframe; | ||
545 | |||
436 | drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); | 546 | drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); |
437 | 547 | ||
438 | intel_hdmi_add_properties(intel_hdmi, connector); | 548 | intel_hdmi_add_properties(intel_hdmi, connector); |