58ceece6086c15577cb05aa667ddc77a01954e6a
[profile/ivi/intel-emgd-kmod.git] / emgd / drm / emgd_encoder.c
1 /*
2  *-----------------------------------------------------------------------------
3  * Filename: emgd_encoder.c
4  * $Revision: 1.4 $
5  *-----------------------------------------------------------------------------
6  * Copyright (c) 2002-2011, Intel Corporation.
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24  * THE SOFTWARE.
25  *
26  *-----------------------------------------------------------------------------
27  * Description:
28  *  Encoder / kenrel mode setting functions.
29  *-----------------------------------------------------------------------------
30  */
31 #define MODULE_NAME hal.oal
32
33 #include <drmP.h>
34 #include <drm_crtc_helper.h>
35 #include <linux/version.h>
36
37 #include <mode_dispatch.h>
38 #include "drm_emgd_private.h"
39
40 #include <igd_pwr.h>
41
42
43
44 /*------------------------------------------------------------------------------
45  * External Functions
46  *------------------------------------------------------------------------------
47  */
48 extern int calculate_eld(igd_display_port_t *port,
49                                 igd_timing_info_t *timing_info);
50
51
52
53 static void emgd_encoder_destroy(struct drm_encoder *encoder);
54 static void emgd_encoder_dpms(struct drm_encoder *encoder, int mode);
55 static bool emgd_encoder_mode_fixup(struct drm_encoder *encoder,
56                                 struct drm_display_mode *mode,
57                                 struct drm_display_mode *adjusted_mode);
58 static void emgd_encoder_prepare(struct drm_encoder *encoder);
59 static void emgd_encoder_mode_set(struct drm_encoder *encoder,
60                                 struct drm_display_mode *mode,
61                                 struct drm_display_mode *adjusted_mode);
62 static void emgd_encoder_commit(struct drm_encoder *encoder);
63
64
65 const struct drm_encoder_funcs emgd_encoder_funcs = {
66         .destroy = emgd_encoder_destroy,
67 };
68
69 const struct drm_encoder_helper_funcs emgd_encoder_helper_funcs = {
70         .dpms       = emgd_encoder_dpms,
71         .mode_fixup = emgd_encoder_mode_fixup,
72         .prepare    = emgd_encoder_prepare,
73         .mode_set   = emgd_encoder_mode_set,
74         .commit     = emgd_encoder_commit,
75 };
76
77
78 /**
79  * emgd_encoder_dpms
80  *
81  * This function will put the encoder to either an ON or OFF state.  Anything
82  * that is not DRM_MODE_DPMS_ON is treated as an off-state.
83  *
84  * @param encoder (IN) Encoder
85  * @param mode    (IN) power mode
86  *
87  * @return None
88  */
89 static void emgd_encoder_dpms(struct drm_encoder *encoder, int mode)
90 {
91         emgd_crtc_t *emgd_crtc = container_of(encoder->crtc, emgd_crtc_t, base);
92         emgd_encoder_t *emgd_encoder = container_of(encoder, emgd_encoder_t, base);
93         igd_display_port_t *igd_port = emgd_encoder->igd_port;
94
95         EMGD_TRACE_ENTER;
96
97         /* The following check is a work around for KMS tries to
98          * program both the crtcs and ports (LVDS and SDVO)
99          * even if it is in single mode. It results in a SIGSEGV.
100          * By putting this check we ensure that it moves forward
101          * only if there is a valid context associated  with the
102          * port.
103          */
104         if(emgd_crtc->igd_pipe->owner) {
105
106                 EMGD_DEBUG("Setting port %lx power to %d",
107                                         igd_port->port_number, mode);
108
109                 switch(mode) {
110
111                         case DRM_MODE_DPMS_ON:
112                                 mode_context->kms_dispatch->kms_program_port(emgd_encoder,
113                                                 IGD_DISPLAY_ENABLE);
114                                 mode_context->kms_dispatch->kms_post_program_port(emgd_encoder,
115                                                 TRUE);
116                                 break;
117
118                         case DRM_MODE_DPMS_STANDBY:
119                         case DRM_MODE_DPMS_SUSPEND:
120                         case DRM_MODE_DPMS_OFF:
121                                 mode_context->kms_dispatch->kms_program_port(emgd_encoder,
122                                                 IGD_DISPLAY_DISABLE);
123                                 break;
124
125                         default:
126                         EMGD_ERROR_EXIT("Unsupported DPMS mode");
127                         return;
128                 }
129         }else {
130                 EMGD_DEBUG("Owner is null for this pipe");
131         }
132
133         EMGD_TRACE_EXIT;
134 }
135
136
137
138 /**
139  * emgd_encoder_mode_fixup
140  *
141  * Called before a mode set, takes the input "mode", matches it to the closest
142  * supported mode, then put the supported mode into "adjusted_mode" to let the
143  * caller know.
144  *
145  * Note: We cannot handle centered and scaled mode with this.  To handle this
146  *       we need to program the pipe and the port to different sets of timings.
147  *       The CRTC Helper does not allow this.  It wants to send adjusted_mode
148  *       to both the CRTC and the Encoder.  We can maybe get around this by
149  *       modifying the "mode" parameter, but that is not the right approach.
150  *
151  * @param encoder (IN) Encoder being prepared
152  * @param mode    (IN) Requested mode
153  * @param adjusted_mode (IN) Encoder supported mode
154  *
155  * @return true, false (details TBD)
156  */
157 static bool emgd_encoder_mode_fixup(struct drm_encoder *encoder,
158                 struct drm_display_mode *mode,
159                 struct drm_display_mode *adjusted_mode)
160 {
161         struct drm_device      *dev          = NULL;
162         igd_context_t          *context      = NULL;
163         igd_display_port_t     *port         = NULL;
164         igd_framebuffer_info_t *fb_info      = NULL;
165         emgd_encoder_t         *emgd_encoder = NULL;
166         igd_timing_info_t      *timing       = NULL;
167         igd_display_info_t     *pt_info      = NULL;
168         emgd_crtc_t            *emgd_crtc    = NULL;
169         igd_display_pipe_t     *pipe         = NULL;
170         unsigned long           existing_height  = 0;
171         unsigned long           existing_width   = 0;
172         unsigned long           existing_refresh = 0;
173         int                     ret;
174
175         EMGD_TRACE_ENTER;
176
177         /* Check ajusted mode to see if it's valid.  If not, populate it */
178         if (adjusted_mode->crtc_htotal == 0) {
179                 EMGD_DEBUG("No valid mode in adjusted mode, setting valid mode");
180                 drm_mode_set_crtcinfo(adjusted_mode, 0);
181         }
182
183         dev = encoder->dev;
184         context = ((drm_emgd_priv_t *)dev->dev_private)->context;
185         emgd_encoder = container_of(encoder, emgd_encoder_t, base);
186         port = emgd_encoder->igd_port;
187         if (!port->pt_info) {
188                 port->pt_info = kzalloc(sizeof(igd_display_info_t), GFP_KERNEL);
189                 if (!port->pt_info) {
190                         EMGD_DEBUG("Cannot allocate igd_display_into_t");
191                         return false;
192                 }
193         }
194         existing_height  = port->pt_info->height;
195         existing_width   = port->pt_info->width;
196         existing_refresh = port->pt_info->refresh;
197         pt_info          = port->pt_info;
198
199         fb_info = kzalloc(sizeof(igd_framebuffer_info_t), GFP_KERNEL);
200         if (!fb_info) {
201                 EMGD_DEBUG("Cannot allocate framebuffer info");
202                 return false;
203         }
204
205         /* Get the dimension of the framebuffer linked to the CRTC.  If it is
206          * smaller than the resolution, kms_match_mode will either center it
207          * or let the encoder hardware scale it */
208         fb_info->width  = encoder->crtc->fb->width;
209         fb_info->height = encoder->crtc->fb->height;
210         EMGD_DEBUG("Setting fb_info to: %dx%d", fb_info->width, fb_info->height);
211
212         pt_info->width        = mode->crtc_hdisplay;
213         pt_info->height       = mode->crtc_vdisplay;
214         pt_info->refresh      = mode->vrefresh;
215         pt_info->dclk         = mode->synth_clock;
216         pt_info->htotal       = mode->crtc_htotal;
217         pt_info->hblank_start = mode->crtc_hblank_start;
218         pt_info->hblank_end   = mode->crtc_hblank_end;
219         pt_info->hsync_start  = mode->crtc_hsync_start;
220         pt_info->hsync_end    = mode->crtc_hsync_end;
221         pt_info->vtotal       = mode->crtc_vtotal;
222         pt_info->vblank_start = mode->crtc_vblank_start;
223         pt_info->vblank_end   = mode->crtc_vblank_end;
224         pt_info->vsync_start  = mode->crtc_vsync_start;
225         pt_info->vsync_end    = mode->crtc_vsync_end;
226         pt_info->mode_number  = mode->clock_index;
227         pt_info->flags        = 0;
228         EMGD_DEBUG("Setting pt_info to: %dx%d", pt_info->width, pt_info->height);
229
230         ret = mode_context->kms_dispatch->kms_match_mode((void *)emgd_encoder,
231                 (void *)fb_info, &timing);
232
233         if (!ret) {
234                 adjusted_mode->crtc_hdisplay     = timing->width;
235                 adjusted_mode->crtc_vdisplay     = timing->height;
236                 adjusted_mode->vrefresh          = timing->refresh;
237                 adjusted_mode->synth_clock       = timing->dclk;
238                 adjusted_mode->crtc_htotal       = timing->htotal;
239                 adjusted_mode->crtc_hblank_start = timing->hblank_start;
240                 adjusted_mode->crtc_hblank_end   = timing->hblank_end;
241                 adjusted_mode->crtc_hsync_start  = timing->hsync_start;
242                 adjusted_mode->crtc_hsync_end    = timing->hsync_end;
243                 adjusted_mode->crtc_vtotal       = timing->vtotal;
244                 adjusted_mode->crtc_vblank_start = timing->vblank_start;
245                 adjusted_mode->crtc_vblank_end   = timing->vblank_end;
246                 adjusted_mode->crtc_vsync_start  = timing->vsync_start;
247                 adjusted_mode->crtc_vsync_end    = timing->vsync_end;
248                 adjusted_mode->clock_index       = timing->mode_number;
249                 adjusted_mode->private_flags     = timing->mode_info_flags;
250
251                 EMGD_DEBUG("(%dx%d@%d)->(%dx%d@%d)",
252                         mode->crtc_hdisplay, mode->crtc_vdisplay, mode->vrefresh,
253                         adjusted_mode->crtc_hdisplay, adjusted_mode->crtc_vdisplay,
254                         adjusted_mode->vrefresh);
255
256                 /* Check our new mode against what currently there
257                  * to see if we can do a seamless mode-set
258                  */
259                 if (emgd_encoder->flags & ENCODER_FLAG_FIRST_ALTER) {
260                         if (mode_context->fw_info) {
261
262                                 emgd_crtc = container_of(encoder->crtc, emgd_crtc_t, base);
263                                 pipe = emgd_crtc->igd_pipe;
264
265                                 existing_width = mode_context->fw_info->
266                                         timing_arr[pipe->pipe_num].width;
267                                 existing_height = mode_context->fw_info->
268                                         timing_arr[pipe->pipe_num].height;
269                                 existing_refresh = mode_context->fw_info->
270                                         timing_arr[pipe->pipe_num].refresh;
271
272                         }
273                 }
274
275                 if (adjusted_mode->crtc_hdisplay == existing_width &&
276                         adjusted_mode->crtc_vdisplay == existing_height) {
277
278                         if (abs(adjusted_mode->vrefresh - existing_refresh) <= 1) {
279                                 emgd_encoder->flags |= ENCODER_FLAG_SEAMLESS;
280                         }
281                 }
282         }
283
284         kfree(fb_info);
285
286         EMGD_TRACE_EXIT;
287         return (!ret);
288 }
289
290
291
292 /**
293  * emgd_encoder_prepare
294  *
295  * Based on the available documentation at the moment, this function gets
296  * called right before a mode change.  Its job is to turn off the display.
297  *
298  * @param encoder (IN) Encoder being prepared
299  *
300  * @return None
301  */
302 static void emgd_encoder_prepare(struct drm_encoder *encoder)
303 {
304         struct drm_encoder_helper_funcs *encoder_funcs;
305         emgd_encoder_t *emgd_encoder;
306
307         EMGD_TRACE_ENTER;
308
309         emgd_encoder = container_of(encoder, emgd_encoder_t, base);
310
311         if (!(emgd_encoder->flags & ENCODER_FLAG_SEAMLESS)) {
312                 encoder_funcs = encoder->helper_private;
313                 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
314         }
315
316         emgd_encoder->flags &= ~ENCODER_FLAG_FIRST_ALTER;
317
318         EMGD_TRACE_EXIT;
319 }
320
321
322
323 /**
324  * emgd_encoder_commit
325  *
326  * This function commits the mode change sequence by actually programming
327  * the registers.
328  *
329  * @param encoder (IN) Encoder being prepared
330  *
331  * @return None
332  */
333 static void emgd_encoder_commit(struct drm_encoder *encoder)
334 {
335         struct drm_encoder_helper_funcs *encoder_funcs;
336         emgd_encoder_t     *emgd_encoder = NULL;
337         emgd_crtc_t        *emgd_crtc;
338         igd_display_port_t *port;
339         igd_display_pipe_t *pipe;
340
341         EMGD_TRACE_ENTER;
342
343         emgd_encoder = container_of(encoder, emgd_encoder_t, base);
344
345
346         if (!(emgd_encoder->flags & ENCODER_FLAG_SEAMLESS)) {
347
348                 port      = emgd_encoder->igd_port;
349                 emgd_crtc = container_of(encoder->crtc, emgd_crtc_t, base);
350                 pipe      = emgd_crtc->igd_pipe;
351
352 /*              mode_context->kms_dispatch->kms_program_port(emgd_encoder,
353                         IGD_DISPLAY_ENABLE);*/
354 /*
355                 port->pd_driver->set_mode(port->pd_context, pipe->timing,
356                                                                 1<<pipe->pipe_num);*/
357
358
359                 encoder_funcs = encoder->helper_private;
360                 encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
361
362         }
363         /* Reset our seamless variable */
364         emgd_encoder->flags &= ~ENCODER_FLAG_SEAMLESS;
365
366         /* TODO: Add call to check_display */
367
368         EMGD_TRACE_EXIT;
369 }
370
371
372
373 /**
374  * emgd_encoder_mode_set
375  *
376  * This function saves the requested timings into the Port Timing Info
377  * structure.  At emgd_encoder_commit() time we should be using these
378  * timings to program the port, but currently we are using timings from
379  * the pipe.  This is fine for now, but at one point we should investigate
380  * the centering case in which the port timings may not match the pipe timings.
381  *
382  * @param encoder (IN) Encoder being prepared
383  * @param mode    (IN)
384  * @param adjusted_mode (IN)
385  *
386  * @return None
387  */
388 static void emgd_encoder_mode_set(struct drm_encoder *encoder,
389                 struct drm_display_mode *mode,
390                 struct drm_display_mode *adjusted_mode)
391 {
392         emgd_crtc_t        *emgd_crtc    = NULL;
393         emgd_encoder_t     *emgd_encoder = NULL;
394         igd_display_pipe_t *pipe         = NULL;
395         igd_display_port_t *port         = NULL;
396         pd_timing_t        *timing       = NULL;
397
398         EMGD_TRACE_ENTER;
399
400
401         emgd_encoder = container_of(encoder, emgd_encoder_t, base);
402
403         if (!(emgd_encoder->flags & ENCODER_FLAG_SEAMLESS)) {
404                 port = emgd_encoder->igd_port;
405                 emgd_crtc = container_of(encoder->crtc, emgd_crtc_t, base);
406                 pipe = emgd_crtc->igd_pipe;
407
408                 if (pipe) {
409                         timing = (pd_timing_t *)pipe->timing;
410
411                         if (NULL == port->pt_info) {
412                                 port->pt_info = kzalloc(sizeof(igd_display_info_t), GFP_KERNEL);
413
414                                 if (!port->pt_info) {
415                                         EMGD_ERROR_EXIT("Unable to allocate pt_info.");
416                                         return;
417                                 }
418                         }
419
420                         port->pt_info->width        = adjusted_mode->crtc_hdisplay;
421                         port->pt_info->height       = adjusted_mode->crtc_vdisplay;
422                         port->pt_info->refresh      = adjusted_mode->vrefresh;
423                         port->pt_info->dclk         = adjusted_mode->synth_clock;
424                         port->pt_info->htotal       = adjusted_mode->crtc_htotal;
425                         port->pt_info->hblank_start = adjusted_mode->crtc_hblank_start;
426                         port->pt_info->hblank_end   = adjusted_mode->crtc_hblank_end;
427                         port->pt_info->hsync_start  = adjusted_mode->crtc_hsync_start;
428                         port->pt_info->hsync_end    = adjusted_mode->crtc_hsync_end;
429                         port->pt_info->vtotal       = adjusted_mode->crtc_vtotal;
430                         port->pt_info->vblank_start = adjusted_mode->crtc_vblank_start;
431                         port->pt_info->vblank_end   = adjusted_mode->crtc_vblank_end;
432                         port->pt_info->vsync_start  = adjusted_mode->crtc_vsync_start;
433                         port->pt_info->vsync_end    = adjusted_mode->crtc_vsync_end;
434                         port->pt_info->mode_number  = adjusted_mode->clock_index;
435                         port->pt_info->flags        = adjusted_mode->private_flags;
436
437                         port->pt_info->x_offset     = timing->x_offset;
438                         port->pt_info->y_offset     = timing->y_offset;
439                         port->pt_info->flags       |= IGD_DISPLAY_ENABLE;
440
441
442                         EMGD_DEBUG("Calculate ELD");
443                         if (calculate_eld(port, timing)) {
444                                 EMGD_DEBUG("Fail to calculate ELD");
445                         }
446
447                 } else {
448                         EMGD_ERROR("Trying to set the mode without a pipe attached.");
449                 }
450         }
451
452         EMGD_TRACE_EXIT;
453 }
454
455
456
457 /**
458  * emgd_encoder_destroy
459  *
460  * Frees the resources allocated for this encoder during "create_encoder()"
461  *
462  * @param encoder (IN) Encoder to be freed
463  *
464  * @return None
465  */
466 static void emgd_encoder_destroy(struct drm_encoder *encoder)
467 {
468         emgd_encoder_t *emgd_encoder;
469
470         EMGD_TRACE_ENTER;
471         emgd_encoder = container_of(encoder, emgd_encoder_t, base);
472
473         drm_encoder_cleanup(encoder);
474
475         kfree(emgd_encoder);
476
477         EMGD_TRACE_EXIT;
478 }