Initial commit
[kernel/linux-3.0.git] / drivers / media / video / samsung / tvout / s5p_vp_ctrl.c
1 /* linux/drivers/media/video/samsung/tvout/s5p_vp_ctrl.c
2  *
3  * Copyright (c) 2009 Samsung Electronics
4  *              http://www.samsung.com/
5  *
6  * Control class functions for S5P video processor
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
12
13 #include <linux/delay.h>
14 #include <linux/clk.h>
15 #include <linux/err.h>
16 #include <linux/io.h>
17 #include <linux/slab.h>
18
19 #include "hw_if/hw_if.h"
20 #include "s5p_tvout_ctrl.h"
21
22 #if defined(CONFIG_BUSFREQ)
23 #include <mach/cpufreq.h>
24 #endif
25
26 #define INTERLACED      0
27 #define PROGRESSIVE     1
28
29 struct s5p_vp_ctrl_op_mode {
30         bool    ipc;
31         bool    line_skip;
32         bool    auto_toggling;
33 };
34
35 struct s5p_vp_ctrl_bc_line_eq {
36         enum s5p_vp_line_eq     eq_num;
37         u32                     intc;
38         u32                     slope;
39 };
40
41 struct s5p_vp_ctrl_rect {
42         u32 x;
43         u32 y;
44         u32 w;
45         u32 h;
46 };
47
48 struct s5p_vp_ctrl_plane {
49         u32                     top_y_addr;
50         u32                     top_c_addr;
51         u32                     w;
52         u32                     h;
53
54         enum s5p_vp_src_color   color_t;
55         enum s5p_vp_field       field_id;
56         enum s5p_vp_mem_type    mem_type;
57         enum s5p_vp_mem_mode    mem_mode;
58 };
59
60 struct s5p_vp_ctrl_pp_param {
61         bool                            bypass;
62
63         bool csc_en;
64         enum s5p_vp_csc_type            csc_t;
65         bool                            csc_default_coef;
66         bool                            csc_sub_y_offset_en;
67
68         u32                             saturation;
69         u8                              contrast;
70         bool                            brightness;
71         u32                             bright_offset;
72         struct s5p_vp_ctrl_bc_line_eq   bc_line_eq[8];
73
74         /* sharpness */
75         u32                             th_hnoise;
76         enum s5p_vp_sharpness_control   sharpness;
77
78
79         bool                            default_poly_filter;
80
81         enum s5p_vp_chroma_expansion    chroma_exp;
82 };
83
84 struct s5p_vp_ctrl_mixer_param {
85         bool    blend;
86         u32     alpha;
87         u32     prio;
88 };
89
90 struct s5p_vp_ctrl_private_data {
91         struct s5p_vp_ctrl_plane        src_plane;
92         struct s5p_vp_ctrl_rect         src_win;
93
94         struct s5p_vp_ctrl_rect         dst_win;
95         struct s5p_vp_ctrl_op_mode      op_mode;
96
97         struct s5p_vp_ctrl_pp_param     pp_param;
98         struct s5p_vp_ctrl_mixer_param  mixer_param;
99
100         bool                            running;
101
102         struct reg_mem_info             reg_mem;
103
104         struct s5p_tvout_clk_info       clk;
105         char                            *pow_name;
106
107         struct device *dev;
108 };
109
110 static struct s5p_vp_ctrl_private_data s5p_vp_ctrl_private = {
111         .reg_mem = {
112                 .name                   = "s5p-vp",
113                 .res                    = NULL,
114                 .base                   = NULL
115         },
116
117         .clk = {
118                 .name                   = "vp",
119                 .ptr                    = NULL
120         },
121
122         .pow_name                       = "vp_pd",
123
124         .src_plane = {
125                 .field_id               = VP_TOP_FIELD,
126         },
127
128         .pp_param = {
129                 .default_poly_filter    = true,
130                 .bypass                 = false,
131
132                 .saturation             = 0x80,
133                 .brightness             = 0x00,
134                 .bright_offset          = 0x00,
135                 .contrast               = 0x80,
136
137                 .th_hnoise              = 0,
138                 .sharpness              = VP_SHARPNESS_NO,
139
140                 .chroma_exp             = 0,
141
142                 .csc_en                 = false,
143                 .csc_default_coef       = true,
144                 .csc_sub_y_offset_en    = false,
145         },
146
147         .running                        = false
148 };
149
150 extern int s5p_vp_get_top_field_address(u32* top_y_addr, u32* top_c_addr);
151
152 static u8 s5p_vp_ctrl_get_src_scan_mode(void)
153 {
154         struct s5p_vp_ctrl_plane *src_plane = &s5p_vp_ctrl_private.src_plane;
155         u8 ret = PROGRESSIVE;
156
157         if (src_plane->color_t == VP_SRC_COLOR_NV12IW ||
158                 src_plane->color_t == VP_SRC_COLOR_TILE_NV12IW ||
159                 src_plane->color_t == VP_SRC_COLOR_NV21IW ||
160                 src_plane->color_t == VP_SRC_COLOR_TILE_NV21IW)
161                 ret = INTERLACED;
162
163         return ret;
164 }
165
166 static u8 s5p_vp_ctrl_get_dest_scan_mode(
167                 enum s5p_tvout_disp_mode display, enum s5p_tvout_o_mode out)
168 {
169         u8 ret = PROGRESSIVE;
170
171         switch (out) {
172         case TVOUT_COMPOSITE:
173                 ret = INTERLACED;
174                 break;
175
176         case TVOUT_HDMI_RGB:
177         case TVOUT_HDMI:
178         case TVOUT_DVI:
179                 if (display == TVOUT_1080I_60 ||
180                    display == TVOUT_1080I_59 ||
181                    display == TVOUT_1080I_50)
182                         ret = INTERLACED;
183                 break;
184
185         default:
186                 break;
187         }
188
189         return ret;
190 }
191
192 static void s5p_vp_ctrl_set_src_dst_win(
193                 struct s5p_vp_ctrl_rect src_win,
194                 struct s5p_vp_ctrl_rect dst_win,
195                 enum s5p_tvout_disp_mode        disp,
196                 enum s5p_tvout_o_mode   out,
197                 enum s5p_vp_src_color   color_t,
198                 bool ipc)
199 {
200         struct s5p_vp_ctrl_op_mode *op_mode = &s5p_vp_ctrl_private.op_mode;
201
202         if (s5p_vp_ctrl_get_dest_scan_mode(disp, out) == INTERLACED) {
203                 if (op_mode->line_skip) {
204                         src_win.y /= 2;
205                         src_win.h /= 2;
206                 }
207
208                 dst_win.y /= 2;
209                 dst_win.h /= 2;
210         } else if (s5p_vp_ctrl_get_src_scan_mode() == INTERLACED) {
211                 src_win.y /= 2;
212                 src_win.h /= 2;
213         }
214
215         s5p_vp_set_src_position(src_win.x, 0, src_win.y);
216         s5p_vp_set_dest_position(dst_win.x, dst_win.y);
217         s5p_vp_set_src_dest_size(
218                 src_win.w, src_win.h, dst_win.w, dst_win.h, ipc);
219 }
220
221 int s5p_vp_ctrl_get_src_addr(u32* top_y_addr, u32* top_c_addr)
222 {
223         if (s5p_vp_ctrl_private.running)
224                 s5p_vp_get_top_field_address(top_y_addr, top_c_addr);
225         else {
226                 *top_y_addr = 0;
227                 *top_c_addr = 0;
228         }
229
230         return 0;
231 }
232
233 static int s5p_vp_ctrl_set_src_addr(
234                 u32 top_y_addr, u32 top_c_addr,
235                 u32 img_w, enum s5p_vp_src_color color_t)
236 {
237         if (s5p_vp_set_top_field_address(top_y_addr, top_c_addr))
238                 return -1;
239
240         if (s5p_vp_ctrl_get_src_scan_mode() == INTERLACED) {
241                 u32     bot_y = 0;
242                 u32     bot_c = 0;
243
244                 if (color_t == VP_SRC_COLOR_NV12IW ||
245                                 color_t == VP_SRC_COLOR_NV21IW) {
246                         bot_y = top_y_addr + img_w;
247                         bot_c = top_c_addr + img_w;
248                 } else if (color_t == VP_SRC_COLOR_TILE_NV12IW ||
249                                 color_t == VP_SRC_COLOR_TILE_NV21IW) {
250                         bot_y = top_y_addr + 0x40;
251                         bot_c = top_c_addr + 0x40;
252                 }
253
254                 if (s5p_vp_set_bottom_field_address(bot_y, bot_c))
255                         return -1;
256         }
257
258         return 0;
259 }
260
261 static void s5p_vp_ctrl_init_private(void)
262 {
263         int i;
264         struct s5p_vp_ctrl_pp_param *pp_param = &s5p_vp_ctrl_private.pp_param;
265
266         for (i = 0; i < 8; i++)
267                 pp_param->bc_line_eq[i].eq_num = VP_LINE_EQ_DEFAULT;
268 }
269
270 static int s5p_vp_ctrl_set_reg(void)
271 {
272         int i;
273         int ret = 0;
274
275         enum s5p_tvout_disp_mode        tv_std;
276         enum s5p_tvout_o_mode           tv_if;
277
278         struct s5p_vp_ctrl_plane *src_plane = &s5p_vp_ctrl_private.src_plane;
279         struct s5p_vp_ctrl_pp_param *pp_param = &s5p_vp_ctrl_private.pp_param;
280         struct s5p_vp_ctrl_op_mode *op_mode = &s5p_vp_ctrl_private.op_mode;
281
282 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
283         if (suspend_status) {
284                 tvout_dbg("driver is suspend_status\n");
285         } else
286 #endif
287         {
288                 s5p_tvif_ctrl_get_std_if(&tv_std, &tv_if);
289
290                 s5p_vp_sw_reset();
291
292                 s5p_vp_set_endian(TVOUT_BIG_ENDIAN);
293
294                 s5p_vp_set_op_mode(
295                         op_mode->line_skip, src_plane->mem_type,
296                         src_plane->mem_mode, pp_param->chroma_exp,
297                         op_mode->auto_toggling);
298
299                 s5p_vp_set_field_id(src_plane->field_id);
300
301                 s5p_vp_set_img_size(src_plane->w, src_plane->h);
302
303                 s5p_vp_ctrl_set_src_addr(
304                         src_plane->top_y_addr, src_plane->top_c_addr,
305                         src_plane->w, src_plane->color_t);
306
307                 s5p_vp_ctrl_set_src_dst_win(
308                         s5p_vp_ctrl_private.src_win,
309                         s5p_vp_ctrl_private.dst_win,
310                         tv_std,
311                         tv_if,
312                         s5p_vp_ctrl_private.src_plane.color_t,
313                         op_mode->ipc);
314
315                 if (pp_param->default_poly_filter)
316                         s5p_vp_set_poly_filter_coef_default(
317                                 s5p_vp_ctrl_private.src_win.w,
318                                 s5p_vp_ctrl_private.src_win.h,
319                                 s5p_vp_ctrl_private.dst_win.w,
320                                 s5p_vp_ctrl_private.dst_win.h,
321                                 op_mode->ipc);
322
323                 s5p_vp_set_bypass_post_process(pp_param->bypass);
324                 s5p_vp_set_sharpness(pp_param->th_hnoise, pp_param->sharpness);
325                 s5p_vp_set_saturation(pp_param->saturation);
326                 s5p_vp_set_brightness_contrast(
327                                 pp_param->brightness, pp_param->contrast);
328
329                 for (i = VP_LINE_EQ_0; i <= VP_LINE_EQ_7; i++) {
330                         if (pp_param->bc_line_eq[i].eq_num == i)
331                                 ret = s5p_vp_set_brightness_contrast_control(
332                                         pp_param->bc_line_eq[i].eq_num,
333                                         pp_param->bc_line_eq[i].intc,
334                                         pp_param->bc_line_eq[i].slope);
335
336                         if (ret != 0)
337                                 return -1;
338                 }
339
340                 s5p_vp_set_brightness_offset(pp_param->bright_offset);
341
342                 s5p_vp_set_csc_control(
343                                 pp_param->csc_sub_y_offset_en,
344                                 pp_param->csc_en);
345
346                 if (pp_param->csc_en && pp_param->csc_default_coef) {
347                         if (s5p_vp_set_csc_coef_default(pp_param->csc_t))
348                                 return -1;
349                 }
350
351                 if (s5p_vp_start())
352                         return -1;
353
354         }
355
356         s5p_mixer_ctrl_enable_layer(MIXER_VIDEO_LAYER);
357
358         mdelay(50);
359
360         return 0;
361 }
362
363 static void s5p_vp_ctrl_internal_stop(void)
364 {
365 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
366         if (suspend_status) {
367                 tvout_dbg("driver is suspend_status\n");
368         } else
369 #endif
370                 s5p_vp_stop();
371
372         s5p_mixer_ctrl_disable_layer(MIXER_VIDEO_LAYER);
373 }
374
375 static void s5p_vp_ctrl_clock(bool on)
376 {
377         if (on) {
378 #ifdef CONFIG_ARCH_EXYNOS4
379                 s5p_tvout_pm_runtime_get();
380 #endif
381                 clk_enable(s5p_vp_ctrl_private.clk.ptr);
382                 // Restore vp_base address
383                 s5p_vp_init(s5p_vp_ctrl_private.reg_mem.base);
384
385         } else {
386                 clk_disable(s5p_vp_ctrl_private.clk.ptr);
387 #ifdef CONFIG_ARCH_EXYNOS4
388                 s5p_tvout_pm_runtime_put();
389 #endif
390                 // Set vp_base to NULL
391                 s5p_vp_init(NULL);
392         }
393 }
394
395
396
397 void s5p_vp_ctrl_set_src_plane(
398                 u32 base_y, u32 base_c, u32 width, u32 height,
399                 enum s5p_vp_src_color color, enum s5p_vp_field field)
400 {
401         struct s5p_vp_ctrl_plane *src_plane = &s5p_vp_ctrl_private.src_plane;
402
403         src_plane->color_t      = color;
404         src_plane->field_id     = field;
405
406         src_plane->top_y_addr   = base_y;
407         src_plane->top_c_addr   = base_c;
408
409         src_plane->w            = width;
410         src_plane->h            = height;
411
412 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
413         if (suspend_status) {
414                 tvout_dbg("driver is suspend_status\n");
415                 return;
416         }
417 #endif
418         if (s5p_vp_ctrl_private.running) {
419                 s5p_vp_set_img_size(width, height);
420
421                 s5p_vp_set_field_id(field);
422                 s5p_vp_ctrl_set_src_addr(base_y, base_c, width, color);
423
424                 s5p_vp_update();
425         }
426 }
427
428 void s5p_vp_ctrl_set_src_win(u32 left, u32 top, u32 width, u32 height)
429 {
430         struct s5p_vp_ctrl_rect *src_win = &s5p_vp_ctrl_private.src_win;
431
432         src_win->x = left;
433         src_win->y = top;
434         src_win->w = width;
435         src_win->h = height;
436
437 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
438         if (suspend_status) {
439                 tvout_dbg("driver is suspend_status\n");
440                 return;
441         }
442 #endif
443         if (s5p_vp_ctrl_private.running) {
444                 enum s5p_tvout_disp_mode        tv_std;
445                 enum s5p_tvout_o_mode           tv_if;
446
447                 s5p_tvif_ctrl_get_std_if(&tv_std, &tv_if);
448
449                 s5p_vp_ctrl_set_src_dst_win(
450                         *src_win,
451                         s5p_vp_ctrl_private.dst_win,
452                         tv_std,
453                         tv_if,
454                         s5p_vp_ctrl_private.src_plane.color_t,
455                         s5p_vp_ctrl_private.op_mode.ipc);
456
457                 s5p_vp_update();
458         }
459 }
460
461 void s5p_vp_ctrl_set_dest_win(u32 left, u32 top, u32 width, u32 height)
462 {
463         struct s5p_vp_ctrl_rect *dst_win = &s5p_vp_ctrl_private.dst_win;
464
465         dst_win->x = left;
466         dst_win->y = top;
467         dst_win->w = width;
468         dst_win->h = height;
469 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
470         if (suspend_status) {
471                 tvout_dbg("driver is suspend_status\n");
472                 return;
473         }
474 #endif
475         if (s5p_vp_ctrl_private.running) {
476                 enum s5p_tvout_disp_mode        tv_std;
477                 enum s5p_tvout_o_mode           tv_if;
478
479                 s5p_tvif_ctrl_get_std_if(&tv_std, &tv_if);
480
481                 s5p_vp_ctrl_set_src_dst_win(
482                         s5p_vp_ctrl_private.src_win,
483                         *dst_win,
484                         tv_std,
485                         tv_if,
486                         s5p_vp_ctrl_private.src_plane.color_t,
487                         s5p_vp_ctrl_private.op_mode.ipc);
488
489                 s5p_vp_update();
490         }
491 }
492
493 void s5p_vp_ctrl_set_dest_win_alpha_val(u32 alpha)
494 {
495         s5p_vp_ctrl_private.mixer_param.alpha = alpha;
496 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
497         if (suspend_status) {
498                 tvout_dbg("driver is suspend_status\n");
499                 return;
500         }
501 #endif
502         s5p_mixer_ctrl_set_alpha(MIXER_VIDEO_LAYER, alpha);
503 }
504
505 void s5p_vp_ctrl_set_dest_win_blend(bool enable)
506 {
507         s5p_vp_ctrl_private.mixer_param.blend = enable;
508 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
509         if (suspend_status) {
510                 tvout_dbg("driver is suspend_status\n");
511                 return;
512         }
513 #endif
514         s5p_mixer_ctrl_set_blend_mode(MIXER_VIDEO_LAYER,
515                         LAYER_BLENDING);
516 }
517
518 void s5p_vp_ctrl_set_dest_win_priority(u32 prio)
519 {
520         s5p_vp_ctrl_private.mixer_param.prio = prio;
521 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
522         if (suspend_status) {
523                 tvout_dbg("driver is suspend_status\n");
524                 return;
525         }
526 #endif
527         s5p_mixer_ctrl_set_priority(MIXER_VIDEO_LAYER, prio);
528 }
529
530 void s5p_vp_ctrl_stop(void)
531 {
532         if (s5p_vp_ctrl_private.running) {
533                 s5p_vp_ctrl_internal_stop();
534 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
535         if (suspend_status) {
536                 tvout_dbg("driver is suspend_status\n");
537         } else
538 #endif
539         {
540                 s5p_vp_ctrl_clock(0);
541         }
542
543                 s5p_vp_ctrl_private.running = false;
544 #if defined(CONFIG_BUSFREQ) || defined(CONFIG_BUSFREQ_LOCK_WRAPPER)
545         exynos4_busfreq_lock_free(DVFS_LOCK_ID_TV);
546 #endif
547         }
548 }
549
550 int s5p_vp_ctrl_start(void)
551 {
552         struct s5p_vp_ctrl_plane *src_plane = &s5p_vp_ctrl_private.src_plane;
553         enum s5p_tvout_disp_mode disp;
554         enum s5p_tvout_o_mode out;
555
556         struct s5p_vp_ctrl_rect *src_win = &s5p_vp_ctrl_private.src_win;
557         struct s5p_vp_ctrl_rect *dst_win = &s5p_vp_ctrl_private.dst_win;
558
559         bool i_mode, o_mode; /* 0 for interlaced, 1 for progressive */
560
561         s5p_tvif_ctrl_get_std_if(&disp, &out);
562
563         switch (disp) {
564         case TVOUT_480P_60_16_9:
565         case TVOUT_480P_60_4_3:
566         case TVOUT_576P_50_16_9:
567         case TVOUT_576P_50_4_3:
568         case TVOUT_480P_59:
569                 s5p_vp_ctrl_private.pp_param.csc_t = VP_CSC_SD_HD;
570                 break;
571
572         case TVOUT_1080I_50:
573         case TVOUT_1080I_60:
574         case TVOUT_1080P_50:
575         case TVOUT_1080P_30:
576         case TVOUT_1080P_60:
577         case TVOUT_720P_59:
578         case TVOUT_1080I_59:
579         case TVOUT_1080P_59:
580         case TVOUT_720P_50:
581         case TVOUT_720P_60:
582                 s5p_vp_ctrl_private.pp_param.csc_t = VP_CSC_HD_SD;
583                 break;
584 #ifdef CONFIG_HDMI_14A_3D
585         case TVOUT_720P_60_SBS_HALF:
586         case TVOUT_720P_59_SBS_HALF:
587         case TVOUT_720P_50_TB:
588         case TVOUT_1080P_24_TB:
589         case TVOUT_1080P_23_TB:
590                 s5p_vp_ctrl_private.pp_param.csc_t = VP_CSC_HD_SD;
591                 break;
592
593 #endif
594
595         default:
596                 break;
597         }
598
599         i_mode = s5p_vp_ctrl_get_src_scan_mode();
600         o_mode = s5p_vp_ctrl_get_dest_scan_mode(disp, out);
601
602         /* check o_mode */
603         if (i_mode == INTERLACED) {
604                 if (o_mode == INTERLACED) {
605                         /* i to i : line skip 1, ipc 0, auto toggle 0 */
606                         s5p_vp_ctrl_private.op_mode.line_skip           = true;
607                         s5p_vp_ctrl_private.op_mode.ipc                 = false;
608                         s5p_vp_ctrl_private.op_mode.auto_toggling       = false;
609                 } else {
610                         /* i to p : line skip 1, ipc 1, auto toggle 0 */
611                         s5p_vp_ctrl_private.op_mode.line_skip           = true;
612                         s5p_vp_ctrl_private.op_mode.ipc                 = true;
613                         s5p_vp_ctrl_private.op_mode.auto_toggling       = false;
614                 }
615         } else {
616                 if (o_mode == INTERLACED) {
617                         /* p to i : line skip 0, ipc 0, auto toggle 0 */
618                         if (dst_win->h > src_win->h &&
619                            ((dst_win->h << 16)/src_win->h < 0x100000))
620                                 s5p_vp_ctrl_private.op_mode.line_skip   = false;
621                         /* p to i : line skip 1, ipc 0, auto toggle 0 */
622                         else
623                                 s5p_vp_ctrl_private.op_mode.line_skip   = true;
624                         s5p_vp_ctrl_private.op_mode.ipc                 = false;
625                         s5p_vp_ctrl_private.op_mode.auto_toggling       = false;
626                 } else {
627                         /* p to p : line skip 0, ipc 0, auto toggle 0 */
628                         s5p_vp_ctrl_private.op_mode.line_skip           = false;
629                         s5p_vp_ctrl_private.op_mode.ipc                 = false;
630                         s5p_vp_ctrl_private.op_mode.auto_toggling       = false;
631                 }
632         }
633         src_plane->mem_type
634                 = ((src_plane->color_t == VP_SRC_COLOR_NV12) ||
635                         (src_plane->color_t == VP_SRC_COLOR_NV12IW) ||
636                         (src_plane->color_t == VP_SRC_COLOR_TILE_NV12) ||
637                         (src_plane->color_t == VP_SRC_COLOR_TILE_NV12IW)) ?
638                         VP_YUV420_NV12 : VP_YUV420_NV21;
639
640         src_plane->mem_mode
641                 = ((src_plane->color_t == VP_SRC_COLOR_NV12) ||
642                         (src_plane->color_t == VP_SRC_COLOR_NV12IW) ||
643                         (src_plane->color_t == VP_SRC_COLOR_NV21) ||
644                         (src_plane->color_t == VP_SRC_COLOR_NV21IW)) ?
645                         VP_LINEAR_MODE : VP_2D_TILE_MODE;
646
647         if (s5p_vp_ctrl_private.running)
648                 s5p_vp_ctrl_internal_stop();
649         else {
650 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
651                 if (suspend_status) {
652                         tvout_dbg("driver is suspend_status\n");
653                 } else
654 #endif
655                 {
656 #if defined(CONFIG_BUSFREQ) || defined(CONFIG_BUSFREQ_LOCK_WRAPPER)
657                         if ((disp == TVOUT_1080P_60) || (disp == TVOUT_1080P_59)
658                                         || (disp == TVOUT_1080P_50)) {
659                                 if (exynos4_busfreq_lock(DVFS_LOCK_ID_TV, BUS_L0))
660                                         printk(KERN_ERR "%s: failed lock DVFS\n", __func__);
661                         }
662 #endif
663                         s5p_vp_ctrl_clock(1);
664                 }
665                 s5p_vp_ctrl_private.running = true;
666         }
667         s5p_vp_ctrl_set_reg();
668
669         return 0;
670 }
671
672 int s5p_vp_ctrl_constructor(struct platform_device *pdev)
673 {
674         int ret = 0;
675
676         ret = s5p_tvout_map_resource_mem(
677                 pdev,
678                 s5p_vp_ctrl_private.reg_mem.name,
679                 &(s5p_vp_ctrl_private.reg_mem.base),
680                 &(s5p_vp_ctrl_private.reg_mem.res));
681
682         if (ret)
683                 goto err_on_res;
684
685         s5p_vp_ctrl_private.clk.ptr =
686                 clk_get(&pdev->dev, s5p_vp_ctrl_private.clk.name);
687
688         if (IS_ERR(s5p_vp_ctrl_private.clk.ptr)) {
689                 tvout_err("Failed to find clock %s\n",
690                         s5p_vp_ctrl_private.clk.name);
691                 ret = -ENOENT;
692                 goto err_on_clk;
693         }
694
695         s5p_vp_init(s5p_vp_ctrl_private.reg_mem.base);
696         s5p_vp_ctrl_init_private();
697
698         return 0;
699
700 err_on_clk:
701         iounmap(s5p_vp_ctrl_private.reg_mem.base);
702         release_resource(s5p_vp_ctrl_private.reg_mem.res);
703         kfree(s5p_vp_ctrl_private.reg_mem.res);
704
705 err_on_res:
706         return ret;
707 }
708
709 void s5p_vp_ctrl_destructor(void)
710 {
711         if (s5p_vp_ctrl_private.reg_mem.base)
712                 iounmap(s5p_vp_ctrl_private.reg_mem.base);
713
714         if (s5p_vp_ctrl_private.reg_mem.res) {
715                 release_resource(s5p_vp_ctrl_private.reg_mem.res);
716                 kfree(s5p_vp_ctrl_private.reg_mem.res);
717         }
718
719         if (s5p_vp_ctrl_private.clk.ptr) {
720                 if (s5p_vp_ctrl_private.running)
721                         clk_disable(s5p_vp_ctrl_private.clk.ptr);
722                 clk_put(s5p_vp_ctrl_private.clk.ptr);
723         }
724 }
725
726 void s5p_vp_ctrl_suspend(void)
727 {
728         tvout_dbg("running(%d)\n", s5p_vp_ctrl_private.running);
729         if (s5p_vp_ctrl_private.running) {
730                 s5p_vp_stop();
731                 s5p_vp_ctrl_clock(0);
732         }
733 }
734
735 void s5p_vp_ctrl_resume(void)
736 {
737         tvout_dbg("running(%d)\n", s5p_vp_ctrl_private.running);
738         if (s5p_vp_ctrl_private.running) {
739                 s5p_vp_ctrl_clock(1);
740                 s5p_vp_ctrl_set_reg();
741         }
742 }