aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/i915/intel_panel.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915/intel_panel.c')
-rw-r--r--drivers/gpu/drm/i915/intel_panel.c191
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
120static void
121centre_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
142static void
143centre_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
163static 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
176void 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
293out:
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
120static int is_backlight_combination_mode(struct drm_device *dev) 311static 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;