1 /* linux/drivers/media/video/samsung/tvout/s5p_vp_ctrl.c
3 * Copyright (c) 2009 Samsung Electronics
4 * http://www.samsung.com/
6 * Control class functions for S5P video processor
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.
13 #include <linux/delay.h>
14 #include <linux/clk.h>
15 #include <linux/err.h>
17 #include <linux/slab.h>
19 #include "hw_if/hw_if.h"
20 #include "s5p_tvout_ctrl.h"
22 #if defined(CONFIG_BUSFREQ)
23 #include <mach/cpufreq.h>
29 struct s5p_vp_ctrl_op_mode {
35 struct s5p_vp_ctrl_bc_line_eq {
36 enum s5p_vp_line_eq eq_num;
41 struct s5p_vp_ctrl_rect {
48 struct s5p_vp_ctrl_plane {
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;
60 struct s5p_vp_ctrl_pp_param {
64 enum s5p_vp_csc_type csc_t;
65 bool csc_default_coef;
66 bool csc_sub_y_offset_en;
72 struct s5p_vp_ctrl_bc_line_eq bc_line_eq[8];
76 enum s5p_vp_sharpness_control sharpness;
79 bool default_poly_filter;
81 enum s5p_vp_chroma_expansion chroma_exp;
84 struct s5p_vp_ctrl_mixer_param {
90 struct s5p_vp_ctrl_private_data {
91 struct s5p_vp_ctrl_plane src_plane;
92 struct s5p_vp_ctrl_rect src_win;
94 struct s5p_vp_ctrl_rect dst_win;
95 struct s5p_vp_ctrl_op_mode op_mode;
97 struct s5p_vp_ctrl_pp_param pp_param;
98 struct s5p_vp_ctrl_mixer_param mixer_param;
102 struct reg_mem_info reg_mem;
104 struct s5p_tvout_clk_info clk;
110 static struct s5p_vp_ctrl_private_data s5p_vp_ctrl_private = {
125 .field_id = VP_TOP_FIELD,
129 .default_poly_filter = true,
134 .bright_offset = 0x00,
138 .sharpness = VP_SHARPNESS_NO,
143 .csc_default_coef = true,
144 .csc_sub_y_offset_en = false,
150 extern int s5p_vp_get_top_field_address(u32* top_y_addr, u32* top_c_addr);
152 static u8 s5p_vp_ctrl_get_src_scan_mode(void)
154 struct s5p_vp_ctrl_plane *src_plane = &s5p_vp_ctrl_private.src_plane;
155 u8 ret = PROGRESSIVE;
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)
166 static u8 s5p_vp_ctrl_get_dest_scan_mode(
167 enum s5p_tvout_disp_mode display, enum s5p_tvout_o_mode out)
169 u8 ret = PROGRESSIVE;
172 case TVOUT_COMPOSITE:
179 if (display == TVOUT_1080I_60 ||
180 display == TVOUT_1080I_59 ||
181 display == TVOUT_1080I_50)
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,
200 struct s5p_vp_ctrl_op_mode *op_mode = &s5p_vp_ctrl_private.op_mode;
202 if (s5p_vp_ctrl_get_dest_scan_mode(disp, out) == INTERLACED) {
203 if (op_mode->line_skip) {
210 } else if (s5p_vp_ctrl_get_src_scan_mode() == INTERLACED) {
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);
221 int s5p_vp_ctrl_get_src_addr(u32* top_y_addr, u32* top_c_addr)
223 if (s5p_vp_ctrl_private.running)
224 s5p_vp_get_top_field_address(top_y_addr, top_c_addr);
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)
237 if (s5p_vp_set_top_field_address(top_y_addr, top_c_addr))
240 if (s5p_vp_ctrl_get_src_scan_mode() == INTERLACED) {
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;
254 if (s5p_vp_set_bottom_field_address(bot_y, bot_c))
261 static void s5p_vp_ctrl_init_private(void)
264 struct s5p_vp_ctrl_pp_param *pp_param = &s5p_vp_ctrl_private.pp_param;
266 for (i = 0; i < 8; i++)
267 pp_param->bc_line_eq[i].eq_num = VP_LINE_EQ_DEFAULT;
270 static int s5p_vp_ctrl_set_reg(void)
275 enum s5p_tvout_disp_mode tv_std;
276 enum s5p_tvout_o_mode tv_if;
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;
282 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
283 if (suspend_status) {
284 tvout_dbg("driver is suspend_status\n");
288 s5p_tvif_ctrl_get_std_if(&tv_std, &tv_if);
292 s5p_vp_set_endian(TVOUT_BIG_ENDIAN);
295 op_mode->line_skip, src_plane->mem_type,
296 src_plane->mem_mode, pp_param->chroma_exp,
297 op_mode->auto_toggling);
299 s5p_vp_set_field_id(src_plane->field_id);
301 s5p_vp_set_img_size(src_plane->w, src_plane->h);
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);
307 s5p_vp_ctrl_set_src_dst_win(
308 s5p_vp_ctrl_private.src_win,
309 s5p_vp_ctrl_private.dst_win,
312 s5p_vp_ctrl_private.src_plane.color_t,
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,
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);
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);
340 s5p_vp_set_brightness_offset(pp_param->bright_offset);
342 s5p_vp_set_csc_control(
343 pp_param->csc_sub_y_offset_en,
346 if (pp_param->csc_en && pp_param->csc_default_coef) {
347 if (s5p_vp_set_csc_coef_default(pp_param->csc_t))
356 s5p_mixer_ctrl_enable_layer(MIXER_VIDEO_LAYER);
363 static void s5p_vp_ctrl_internal_stop(void)
365 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
366 if (suspend_status) {
367 tvout_dbg("driver is suspend_status\n");
372 s5p_mixer_ctrl_disable_layer(MIXER_VIDEO_LAYER);
375 static void s5p_vp_ctrl_clock(bool on)
378 #ifdef CONFIG_ARCH_EXYNOS4
379 s5p_tvout_pm_runtime_get();
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);
386 clk_disable(s5p_vp_ctrl_private.clk.ptr);
387 #ifdef CONFIG_ARCH_EXYNOS4
388 s5p_tvout_pm_runtime_put();
390 // Set vp_base to NULL
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)
401 struct s5p_vp_ctrl_plane *src_plane = &s5p_vp_ctrl_private.src_plane;
403 src_plane->color_t = color;
404 src_plane->field_id = field;
406 src_plane->top_y_addr = base_y;
407 src_plane->top_c_addr = base_c;
409 src_plane->w = width;
410 src_plane->h = height;
412 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
413 if (suspend_status) {
414 tvout_dbg("driver is suspend_status\n");
418 if (s5p_vp_ctrl_private.running) {
419 s5p_vp_set_img_size(width, height);
421 s5p_vp_set_field_id(field);
422 s5p_vp_ctrl_set_src_addr(base_y, base_c, width, color);
428 void s5p_vp_ctrl_set_src_win(u32 left, u32 top, u32 width, u32 height)
430 struct s5p_vp_ctrl_rect *src_win = &s5p_vp_ctrl_private.src_win;
437 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
438 if (suspend_status) {
439 tvout_dbg("driver is suspend_status\n");
443 if (s5p_vp_ctrl_private.running) {
444 enum s5p_tvout_disp_mode tv_std;
445 enum s5p_tvout_o_mode tv_if;
447 s5p_tvif_ctrl_get_std_if(&tv_std, &tv_if);
449 s5p_vp_ctrl_set_src_dst_win(
451 s5p_vp_ctrl_private.dst_win,
454 s5p_vp_ctrl_private.src_plane.color_t,
455 s5p_vp_ctrl_private.op_mode.ipc);
461 void s5p_vp_ctrl_set_dest_win(u32 left, u32 top, u32 width, u32 height)
463 struct s5p_vp_ctrl_rect *dst_win = &s5p_vp_ctrl_private.dst_win;
469 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
470 if (suspend_status) {
471 tvout_dbg("driver is suspend_status\n");
475 if (s5p_vp_ctrl_private.running) {
476 enum s5p_tvout_disp_mode tv_std;
477 enum s5p_tvout_o_mode tv_if;
479 s5p_tvif_ctrl_get_std_if(&tv_std, &tv_if);
481 s5p_vp_ctrl_set_src_dst_win(
482 s5p_vp_ctrl_private.src_win,
486 s5p_vp_ctrl_private.src_plane.color_t,
487 s5p_vp_ctrl_private.op_mode.ipc);
493 void s5p_vp_ctrl_set_dest_win_alpha_val(u32 alpha)
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");
502 s5p_mixer_ctrl_set_alpha(MIXER_VIDEO_LAYER, alpha);
505 void s5p_vp_ctrl_set_dest_win_blend(bool enable)
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");
514 s5p_mixer_ctrl_set_blend_mode(MIXER_VIDEO_LAYER,
518 void s5p_vp_ctrl_set_dest_win_priority(u32 prio)
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");
527 s5p_mixer_ctrl_set_priority(MIXER_VIDEO_LAYER, prio);
530 void s5p_vp_ctrl_stop(void)
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");
540 s5p_vp_ctrl_clock(0);
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);
550 int s5p_vp_ctrl_start(void)
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;
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;
559 bool i_mode, o_mode; /* 0 for interlaced, 1 for progressive */
561 s5p_tvif_ctrl_get_std_if(&disp, &out);
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:
569 s5p_vp_ctrl_private.pp_param.csc_t = VP_CSC_SD_HD;
582 s5p_vp_ctrl_private.pp_param.csc_t = VP_CSC_HD_SD;
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;
599 i_mode = s5p_vp_ctrl_get_src_scan_mode();
600 o_mode = s5p_vp_ctrl_get_dest_scan_mode(disp, out);
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;
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;
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 */
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;
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;
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;
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;
647 if (s5p_vp_ctrl_private.running)
648 s5p_vp_ctrl_internal_stop();
650 #ifdef CLOCK_GATING_ON_EARLY_SUSPEND
651 if (suspend_status) {
652 tvout_dbg("driver is suspend_status\n");
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__);
663 s5p_vp_ctrl_clock(1);
665 s5p_vp_ctrl_private.running = true;
667 s5p_vp_ctrl_set_reg();
672 int s5p_vp_ctrl_constructor(struct platform_device *pdev)
676 ret = s5p_tvout_map_resource_mem(
678 s5p_vp_ctrl_private.reg_mem.name,
679 &(s5p_vp_ctrl_private.reg_mem.base),
680 &(s5p_vp_ctrl_private.reg_mem.res));
685 s5p_vp_ctrl_private.clk.ptr =
686 clk_get(&pdev->dev, s5p_vp_ctrl_private.clk.name);
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);
695 s5p_vp_init(s5p_vp_ctrl_private.reg_mem.base);
696 s5p_vp_ctrl_init_private();
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);
709 void s5p_vp_ctrl_destructor(void)
711 if (s5p_vp_ctrl_private.reg_mem.base)
712 iounmap(s5p_vp_ctrl_private.reg_mem.base);
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);
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);
726 void s5p_vp_ctrl_suspend(void)
728 tvout_dbg("running(%d)\n", s5p_vp_ctrl_private.running);
729 if (s5p_vp_ctrl_private.running) {
731 s5p_vp_ctrl_clock(0);
735 void s5p_vp_ctrl_resume(void)
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();