diff options
Diffstat (limited to 'drivers/gpu/drm/i915/intel_panel.c')
-rw-r--r-- | drivers/gpu/drm/i915/intel_panel.c | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index 7f6141d9a06d..0f32f6498ad3 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c | |||
@@ -117,6 +117,197 @@ done: | |||
117 | dev_priv->pch_pf_size = (width << 16) | height; | 117 | dev_priv->pch_pf_size = (width << 16) | height; |
118 | } | 118 | } |
119 | 119 | ||
120 | static void | ||
121 | centre_horizontally(struct drm_display_mode *mode, | ||
122 | int width) | ||
123 | { | ||
124 | u32 border, sync_pos, blank_width, sync_width; | ||
125 | |||
126 | /* keep the hsync and hblank widths constant */ | ||
127 | sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start; | ||
128 | blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start; | ||
129 | sync_pos = (blank_width - sync_width + 1) / 2; | ||
130 | |||
131 | border = (mode->hdisplay - width + 1) / 2; | ||
132 | border += border & 1; /* make the border even */ | ||
133 | |||
134 | mode->crtc_hdisplay = width; | ||
135 | mode->crtc_hblank_start = width + border; | ||
136 | mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width; | ||
137 | |||
138 | mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos; | ||
139 | mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width; | ||
140 | } | ||
141 | |||
142 | static void | ||
143 | centre_vertically(struct drm_display_mode *mode, | ||
144 | int height) | ||
145 | { | ||
146 | u32 border, sync_pos, blank_width, sync_width; | ||
147 | |||
148 | /* keep the vsync and vblank widths constant */ | ||
149 | sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start; | ||
150 | blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start; | ||
151 | sync_pos = (blank_width - sync_width + 1) / 2; | ||
152 | |||
153 | border = (mode->vdisplay - height + 1) / 2; | ||
154 | |||
155 | mode->crtc_vdisplay = height; | ||
156 | mode->crtc_vblank_start = height + border; | ||
157 | mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width; | ||
158 | |||
159 | mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos; | ||
160 | mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width; | ||
161 | } | ||
162 | |||
163 | static inline u32 panel_fitter_scaling(u32 source, u32 target) | ||
164 | { | ||
165 | /* | ||
166 | * Floating point operation is not supported. So the FACTOR | ||
167 | * is defined, which can avoid the floating point computation | ||
168 | * when calculating the panel ratio. | ||
169 | */ | ||
170 | #define ACCURACY 12 | ||
171 | #define FACTOR (1 << ACCURACY) | ||
172 | u32 ratio = source * FACTOR / target; | ||
173 | return (FACTOR * ratio + FACTOR/2) / FACTOR; | ||
174 | } | ||
175 | |||
176 | void intel_gmch_panel_fitting(struct intel_crtc *intel_crtc, | ||
177 | struct intel_crtc_config *pipe_config, | ||
178 | int fitting_mode) | ||
179 | { | ||
180 | struct drm_device *dev = intel_crtc->base.dev; | ||
181 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
182 | u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0; | ||
183 | struct drm_display_mode *mode, *adjusted_mode; | ||
184 | |||
185 | mode = &pipe_config->requested_mode; | ||
186 | adjusted_mode = &pipe_config->adjusted_mode; | ||
187 | |||
188 | /* Native modes don't need fitting */ | ||
189 | if (adjusted_mode->hdisplay == mode->hdisplay && | ||
190 | adjusted_mode->vdisplay == mode->vdisplay) | ||
191 | goto out; | ||
192 | |||
193 | switch (fitting_mode) { | ||
194 | case DRM_MODE_SCALE_CENTER: | ||
195 | /* | ||
196 | * For centered modes, we have to calculate border widths & | ||
197 | * heights and modify the values programmed into the CRTC. | ||
198 | */ | ||
199 | centre_horizontally(adjusted_mode, mode->hdisplay); | ||
200 | centre_vertically(adjusted_mode, mode->vdisplay); | ||
201 | border = LVDS_BORDER_ENABLE; | ||
202 | break; | ||
203 | case DRM_MODE_SCALE_ASPECT: | ||
204 | /* Scale but preserve the aspect ratio */ | ||
205 | if (INTEL_INFO(dev)->gen >= 4) { | ||
206 | u32 scaled_width = adjusted_mode->hdisplay * | ||
207 | mode->vdisplay; | ||
208 | u32 scaled_height = mode->hdisplay * | ||
209 | adjusted_mode->vdisplay; | ||
210 | |||
211 | /* 965+ is easy, it does everything in hw */ | ||
212 | if (scaled_width > scaled_height) | ||
213 | pfit_control |= PFIT_ENABLE | | ||
214 | PFIT_SCALING_PILLAR; | ||
215 | else if (scaled_width < scaled_height) | ||
216 | pfit_control |= PFIT_ENABLE | | ||
217 | PFIT_SCALING_LETTER; | ||
218 | else if (adjusted_mode->hdisplay != mode->hdisplay) | ||
219 | pfit_control |= PFIT_ENABLE | PFIT_SCALING_AUTO; | ||
220 | } else { | ||
221 | u32 scaled_width = adjusted_mode->hdisplay * | ||
222 | mode->vdisplay; | ||
223 | u32 scaled_height = mode->hdisplay * | ||
224 | adjusted_mode->vdisplay; | ||
225 | /* | ||
226 | * For earlier chips we have to calculate the scaling | ||
227 | * ratio by hand and program it into the | ||
228 | * PFIT_PGM_RATIO register | ||
229 | */ | ||
230 | if (scaled_width > scaled_height) { /* pillar */ | ||
231 | centre_horizontally(adjusted_mode, | ||
232 | scaled_height / | ||
233 | mode->vdisplay); | ||
234 | |||
235 | border = LVDS_BORDER_ENABLE; | ||
236 | if (mode->vdisplay != adjusted_mode->vdisplay) { | ||
237 | u32 bits = panel_fitter_scaling(mode->vdisplay, adjusted_mode->vdisplay); | ||
238 | pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | | ||
239 | bits << PFIT_VERT_SCALE_SHIFT); | ||
240 | pfit_control |= (PFIT_ENABLE | | ||
241 | VERT_INTERP_BILINEAR | | ||
242 | HORIZ_INTERP_BILINEAR); | ||
243 | } | ||
244 | } else if (scaled_width < scaled_height) { /* letter */ | ||
245 | centre_vertically(adjusted_mode, | ||
246 | scaled_width / | ||
247 | mode->hdisplay); | ||
248 | |||
249 | border = LVDS_BORDER_ENABLE; | ||
250 | if (mode->hdisplay != adjusted_mode->hdisplay) { | ||
251 | u32 bits = panel_fitter_scaling(mode->hdisplay, adjusted_mode->hdisplay); | ||
252 | pfit_pgm_ratios |= (bits << PFIT_HORIZ_SCALE_SHIFT | | ||
253 | bits << PFIT_VERT_SCALE_SHIFT); | ||
254 | pfit_control |= (PFIT_ENABLE | | ||
255 | VERT_INTERP_BILINEAR | | ||
256 | HORIZ_INTERP_BILINEAR); | ||
257 | } | ||
258 | } else { | ||
259 | /* Aspects match, Let hw scale both directions */ | ||
260 | pfit_control |= (PFIT_ENABLE | | ||
261 | VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | | ||
262 | VERT_INTERP_BILINEAR | | ||
263 | HORIZ_INTERP_BILINEAR); | ||
264 | } | ||
265 | } | ||
266 | break; | ||
267 | default: | ||
268 | case DRM_MODE_SCALE_FULLSCREEN: | ||
269 | /* | ||
270 | * Full scaling, even if it changes the aspect ratio. | ||
271 | * Fortunately this is all done for us in hw. | ||
272 | */ | ||
273 | if (mode->vdisplay != adjusted_mode->vdisplay || | ||
274 | mode->hdisplay != adjusted_mode->hdisplay) { | ||
275 | pfit_control |= PFIT_ENABLE; | ||
276 | if (INTEL_INFO(dev)->gen >= 4) | ||
277 | pfit_control |= PFIT_SCALING_AUTO; | ||
278 | else | ||
279 | pfit_control |= (VERT_AUTO_SCALE | | ||
280 | VERT_INTERP_BILINEAR | | ||
281 | HORIZ_AUTO_SCALE | | ||
282 | HORIZ_INTERP_BILINEAR); | ||
283 | } | ||
284 | break; | ||
285 | } | ||
286 | |||
287 | /* 965+ wants fuzzy fitting */ | ||
288 | /* FIXME: handle multiple panels by failing gracefully */ | ||
289 | if (INTEL_INFO(dev)->gen >= 4) | ||
290 | pfit_control |= ((intel_crtc->pipe << PFIT_PIPE_SHIFT) | | ||
291 | PFIT_FILTER_FUZZY); | ||
292 | |||
293 | out: | ||
294 | if ((pfit_control & PFIT_ENABLE) == 0) { | ||
295 | pfit_control = 0; | ||
296 | pfit_pgm_ratios = 0; | ||
297 | } | ||
298 | |||
299 | /* Make sure pre-965 set dither correctly for 18bpp panels. */ | ||
300 | if (INTEL_INFO(dev)->gen < 4 && pipe_config->pipe_bpp == 18) | ||
301 | pfit_control |= PANEL_8TO6_DITHER_ENABLE; | ||
302 | |||
303 | if (pfit_control != pipe_config->pfit_control || | ||
304 | pfit_pgm_ratios != pipe_config->pfit_pgm_ratios) { | ||
305 | pipe_config->pfit_control = pfit_control; | ||
306 | pipe_config->pfit_pgm_ratios = pfit_pgm_ratios; | ||
307 | } | ||
308 | dev_priv->lvds_border_bits = border; | ||
309 | } | ||
310 | |||
120 | static int is_backlight_combination_mode(struct drm_device *dev) | 311 | static int is_backlight_combination_mode(struct drm_device *dev) |
121 | { | 312 | { |
122 | struct drm_i915_private *dev_priv = dev->dev_private; | 313 | struct drm_i915_private *dev_priv = dev->dev_private; |