Stefan Bosch | e1e96ba | 2020-07-10 19:07:36 +0200 | [diff] [blame^] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Copyright (C) 2016 Nexell Co., Ltd. |
| 4 | * |
| 5 | * Author: junghyun, kim <jhkim@nexell.co.kr> |
| 6 | */ |
| 7 | |
| 8 | #include <config.h> |
| 9 | #include <common.h> |
| 10 | #include <errno.h> |
| 11 | #include <log.h> |
| 12 | #include <asm/arch/reset.h> |
| 13 | #include <asm/arch/nexell.h> |
| 14 | #include <asm/arch/display.h> |
| 15 | |
| 16 | #include "soc/s5pxx18_soc_disptop.h" |
| 17 | #include "soc/s5pxx18_soc_dpc.h" |
| 18 | #include "soc/s5pxx18_soc_mlc.h" |
| 19 | |
| 20 | #define MLC_LAYER_RGB_0 0 /* number of RGB layer 0 */ |
| 21 | #define MLC_LAYER_RGB_1 1 /* number of RGB layer 1 */ |
| 22 | #define MLC_LAYER_VIDEO 3 /* number of Video layer: 3 = VIDEO */ |
| 23 | |
| 24 | #define __io_address(a) (void *)(uintptr_t)(a) |
| 25 | |
| 26 | void dp_control_init(int module) |
| 27 | { |
| 28 | void *base; |
| 29 | |
| 30 | /* top */ |
| 31 | base = __io_address(nx_disp_top_get_physical_address()); |
| 32 | nx_disp_top_set_base_address(base); |
| 33 | |
| 34 | /* control */ |
| 35 | base = __io_address(nx_dpc_get_physical_address(module)); |
| 36 | nx_dpc_set_base_address(module, base); |
| 37 | |
| 38 | /* top controller */ |
| 39 | nx_rstcon_setrst(RESET_ID_DISP_TOP, RSTCON_ASSERT); |
| 40 | nx_rstcon_setrst(RESET_ID_DISP_TOP, RSTCON_NEGATE); |
| 41 | |
| 42 | /* display controller */ |
| 43 | nx_rstcon_setrst(RESET_ID_DISPLAY, RSTCON_ASSERT); |
| 44 | nx_rstcon_setrst(RESET_ID_DISPLAY, RSTCON_NEGATE); |
| 45 | |
| 46 | nx_dpc_set_clock_pclk_mode(module, nx_pclkmode_always); |
| 47 | } |
| 48 | |
| 49 | int dp_control_setup(int module, |
| 50 | struct dp_sync_info *sync, struct dp_ctrl_info *ctrl) |
| 51 | { |
| 52 | unsigned int out_format; |
| 53 | unsigned int delay_mask; |
| 54 | int rgb_pvd = 0, hsync_cp1 = 7, vsync_fram = 7, de_cp2 = 7; |
| 55 | int v_vso = 1, v_veo = 1, e_vso = 1, e_veo = 1; |
| 56 | |
| 57 | int interlace = 0; |
| 58 | int invert_field; |
| 59 | int swap_rb; |
| 60 | unsigned int yc_order; |
| 61 | int vck_select; |
| 62 | int vclk_invert; |
| 63 | int emb_sync; |
| 64 | |
| 65 | enum nx_dpc_dither r_dither, g_dither, b_dither; |
| 66 | int rgb_mode = 0; |
| 67 | |
| 68 | if (NULL == sync || NULL == ctrl) { |
| 69 | debug("error, dp.%d not set sync or pad clock info !!!\n", |
| 70 | module); |
| 71 | return -EINVAL; |
| 72 | } |
| 73 | |
| 74 | out_format = ctrl->out_format; |
| 75 | delay_mask = ctrl->delay_mask; |
| 76 | interlace = sync->interlace; |
| 77 | invert_field = ctrl->invert_field; |
| 78 | swap_rb = ctrl->swap_RB; |
| 79 | yc_order = ctrl->yc_order; |
| 80 | vck_select = ctrl->vck_select; |
| 81 | vclk_invert = ctrl->clk_inv_lv0 | ctrl->clk_inv_lv1; |
| 82 | emb_sync = (out_format == DPC_FORMAT_CCIR656 ? 1 : 0); |
| 83 | |
| 84 | /* set delay mask */ |
| 85 | if (delay_mask & DP_SYNC_DELAY_RGB_PVD) |
| 86 | rgb_pvd = ctrl->d_rgb_pvd; |
| 87 | if (delay_mask & DP_SYNC_DELAY_HSYNC_CP1) |
| 88 | hsync_cp1 = ctrl->d_hsync_cp1; |
| 89 | if (delay_mask & DP_SYNC_DELAY_VSYNC_FRAM) |
| 90 | vsync_fram = ctrl->d_vsync_fram; |
| 91 | if (delay_mask & DP_SYNC_DELAY_DE_CP) |
| 92 | de_cp2 = ctrl->d_de_cp2; |
| 93 | |
| 94 | if (ctrl->vs_start_offset != 0 || |
| 95 | ctrl->vs_end_offset != 0 || |
| 96 | ctrl->ev_start_offset != 0 || ctrl->ev_end_offset != 0) { |
| 97 | v_vso = ctrl->vs_start_offset; |
| 98 | v_veo = ctrl->vs_end_offset; |
| 99 | e_vso = ctrl->ev_start_offset; |
| 100 | e_veo = ctrl->ev_end_offset; |
| 101 | } |
| 102 | |
| 103 | if (nx_dpc_format_rgb555 == out_format || |
| 104 | nx_dpc_format_mrgb555a == out_format || |
| 105 | nx_dpc_format_mrgb555b == out_format) { |
| 106 | r_dither = nx_dpc_dither_5bit; |
| 107 | g_dither = nx_dpc_dither_5bit; |
| 108 | b_dither = nx_dpc_dither_5bit; |
| 109 | rgb_mode = 1; |
| 110 | } else if (nx_dpc_format_rgb565 == out_format || |
| 111 | nx_dpc_format_mrgb565 == out_format) { |
| 112 | r_dither = nx_dpc_dither_5bit; |
| 113 | b_dither = nx_dpc_dither_5bit; |
| 114 | g_dither = nx_dpc_dither_6bit, rgb_mode = 1; |
| 115 | } else if ((nx_dpc_format_rgb666 == out_format) || |
| 116 | (nx_dpc_format_mrgb666 == out_format)) { |
| 117 | r_dither = nx_dpc_dither_6bit; |
| 118 | g_dither = nx_dpc_dither_6bit; |
| 119 | b_dither = nx_dpc_dither_6bit; |
| 120 | rgb_mode = 1; |
| 121 | } else { |
| 122 | r_dither = nx_dpc_dither_bypass; |
| 123 | g_dither = nx_dpc_dither_bypass; |
| 124 | b_dither = nx_dpc_dither_bypass; |
| 125 | rgb_mode = 1; |
| 126 | } |
| 127 | |
| 128 | /* CLKGEN0/1 */ |
| 129 | nx_dpc_set_clock_source(module, 0, ctrl->clk_src_lv0 == 3 ? |
| 130 | 6 : ctrl->clk_src_lv0); |
| 131 | nx_dpc_set_clock_divisor(module, 0, ctrl->clk_div_lv0); |
| 132 | nx_dpc_set_clock_source(module, 1, ctrl->clk_src_lv1); |
| 133 | nx_dpc_set_clock_divisor(module, 1, ctrl->clk_div_lv1); |
| 134 | nx_dpc_set_clock_out_delay(module, 0, ctrl->clk_delay_lv0); |
| 135 | nx_dpc_set_clock_out_delay(module, 1, ctrl->clk_delay_lv1); |
| 136 | |
| 137 | /* LCD out */ |
| 138 | nx_dpc_set_mode(module, out_format, interlace, invert_field, |
| 139 | rgb_mode, swap_rb, yc_order, emb_sync, emb_sync, |
| 140 | vck_select, vclk_invert, 0); |
| 141 | nx_dpc_set_hsync(module, sync->h_active_len, sync->h_sync_width, |
| 142 | sync->h_front_porch, sync->h_back_porch, |
| 143 | sync->h_sync_invert); |
| 144 | nx_dpc_set_vsync(module, sync->v_active_len, sync->v_sync_width, |
| 145 | sync->v_front_porch, sync->v_back_porch, |
| 146 | sync->v_sync_invert, sync->v_active_len, |
| 147 | sync->v_sync_width, sync->v_front_porch, |
| 148 | sync->v_back_porch); |
| 149 | nx_dpc_set_vsync_offset(module, v_vso, v_veo, e_vso, e_veo); |
| 150 | nx_dpc_set_delay(module, rgb_pvd, hsync_cp1, vsync_fram, de_cp2); |
| 151 | nx_dpc_set_dither(module, r_dither, g_dither, b_dither); |
| 152 | |
| 153 | if (IS_ENABLED(CONFIG_MACH_S5P6818)) { |
| 154 | /* Set TFT_CLKCTRL (offset : 1030h) |
| 155 | * Field name : DPC0_CLKCTRL, DPC1_CLKCRL |
| 156 | * Default value : clk_inv_lv0/1 = 0 : PADCLK_InvCLK |
| 157 | * Invert case : clk_inv_lv0/1 = 1 : PADCLK_CLK |
| 158 | */ |
| 159 | if (module == 0 && ctrl->clk_inv_lv0) |
| 160 | nx_disp_top_set_padclock(padmux_primary_mlc, |
| 161 | padclk_clk); |
| 162 | if (module == 1 && ctrl->clk_inv_lv1) |
| 163 | nx_disp_top_set_padclock(padmux_secondary_mlc, |
| 164 | padclk_clk); |
| 165 | } |
| 166 | |
| 167 | debug("%s: dp.%d x:%4d, hf:%3d, hb:%3d, hs:%3d, hi=%d\n", |
| 168 | __func__, module, sync->h_active_len, sync->h_front_porch, |
| 169 | sync->h_back_porch, sync->h_sync_width, sync->h_sync_invert); |
| 170 | debug("%s: dp.%d y:%4d, vf:%3d, vb:%3d, vs:%3d, vi=%d\n", |
| 171 | __func__, module, sync->v_active_len, sync->v_front_porch, |
| 172 | sync->v_back_porch, sync->v_sync_width, sync->h_sync_invert); |
| 173 | debug("%s: dp.%d ck.0:%d:%d:%d, ck.1:%d:%d:%d\n", |
| 174 | __func__, module, |
| 175 | ctrl->clk_src_lv0, ctrl->clk_div_lv0, ctrl->clk_inv_lv0, |
| 176 | ctrl->clk_src_lv1, ctrl->clk_div_lv1, ctrl->clk_inv_lv1); |
| 177 | debug("%s: dp.%d vs:%d, ve:%d, es:%d, ee:%d\n", |
| 178 | __func__, module, v_vso, v_veo, e_vso, e_veo); |
| 179 | debug("%s: dp.%d delay RGB:%d, hs:%d, vs:%d, de:%d, fmt:0x%x\n", |
| 180 | __func__, module, rgb_pvd, hsync_cp1, vsync_fram, de_cp2, |
| 181 | out_format); |
| 182 | |
| 183 | return 0; |
| 184 | } |
| 185 | |
| 186 | void dp_control_enable(int module, int on) |
| 187 | { |
| 188 | debug("%s: dp.%d top %s\n", __func__, module, on ? "ON" : "OFF"); |
| 189 | |
| 190 | nx_dpc_set_dpc_enable(module, on); |
| 191 | nx_dpc_set_clock_divisor_enable(module, on); |
| 192 | } |
| 193 | |
| 194 | void dp_plane_init(int module) |
| 195 | { |
| 196 | void *base = __io_address(nx_mlc_get_physical_address(module)); |
| 197 | |
| 198 | nx_mlc_set_base_address(module, base); |
| 199 | nx_mlc_set_clock_pclk_mode(module, nx_pclkmode_always); |
| 200 | nx_mlc_set_clock_bclk_mode(module, nx_bclkmode_always); |
| 201 | } |
| 202 | |
| 203 | int dp_plane_screen_setup(int module, struct dp_plane_top *top) |
| 204 | { |
| 205 | int width = top->screen_width; |
| 206 | int height = top->screen_height; |
| 207 | int interlace = top->interlace; |
| 208 | int video_prior = top->video_prior; |
| 209 | unsigned int bg_color = top->back_color; |
| 210 | |
| 211 | /* MLC TOP layer */ |
| 212 | nx_mlc_set_screen_size(module, width, height); |
| 213 | nx_mlc_set_layer_priority(module, video_prior); |
| 214 | nx_mlc_set_background(module, bg_color); |
| 215 | nx_mlc_set_field_enable(module, interlace); |
| 216 | nx_mlc_set_rgblayer_gama_table_power_mode(module, 0, 0, 0); |
| 217 | nx_mlc_set_rgblayer_gama_table_sleep_mode(module, 1, 1, 1); |
| 218 | nx_mlc_set_rgblayer_gamma_enable(module, 0); |
| 219 | nx_mlc_set_dither_enable_when_using_gamma(module, 0); |
| 220 | nx_mlc_set_gamma_priority(module, 0); |
| 221 | nx_mlc_set_top_power_mode(module, 1); |
| 222 | nx_mlc_set_top_sleep_mode(module, 0); |
| 223 | |
| 224 | debug("%s: dp.%d screen %dx%d, %s, priority:%d, bg:0x%x\n", |
| 225 | __func__, module, width, height, |
| 226 | interlace ? "Interlace" : "Progressive", |
| 227 | video_prior, bg_color); |
| 228 | |
| 229 | return 0; |
| 230 | } |
| 231 | |
| 232 | void dp_plane_screen_enable(int module, int on) |
| 233 | { |
| 234 | /* enable top screen */ |
| 235 | nx_mlc_set_mlc_enable(module, on); |
| 236 | nx_mlc_set_top_dirty_flag(module); |
| 237 | debug("%s: dp.%d top %s\n", __func__, module, on ? "ON" : "OFF"); |
| 238 | } |
| 239 | |
| 240 | int dp_plane_layer_setup(int module, struct dp_plane_info *plane) |
| 241 | { |
| 242 | int sx = plane->left; |
| 243 | int sy = plane->top; |
| 244 | int ex = sx + plane->width - 1; |
| 245 | int ey = sy + plane->height - 1; |
| 246 | int pixel_byte = plane->pixel_byte; |
| 247 | int mem_lock_size = 16; /* fix mem lock size */ |
| 248 | int layer = plane->layer; |
| 249 | unsigned int format = plane->format; |
| 250 | |
| 251 | if (!plane->enable) |
| 252 | return -EINVAL; |
| 253 | |
| 254 | /* MLC layer */ |
| 255 | nx_mlc_set_lock_size(module, layer, mem_lock_size); |
| 256 | nx_mlc_set_alpha_blending(module, layer, 0, 15); |
| 257 | nx_mlc_set_transparency(module, layer, 0, 0); |
| 258 | nx_mlc_set_color_inversion(module, layer, 0, 0); |
| 259 | nx_mlc_set_rgblayer_invalid_position(module, layer, 0, 0, 0, 0, 0, 0); |
| 260 | nx_mlc_set_rgblayer_invalid_position(module, layer, 1, 0, 0, 0, 0, 0); |
| 261 | nx_mlc_set_format_rgb(module, layer, format); |
| 262 | nx_mlc_set_position(module, layer, sx, sy, ex, ey); |
| 263 | nx_mlc_set_rgblayer_stride(module, layer, pixel_byte, |
| 264 | plane->width * pixel_byte); |
| 265 | nx_mlc_set_rgblayer_address(module, layer, plane->fb_base); |
| 266 | |
| 267 | debug("%s: dp.%d.%d %d * %d, %dbpp, fmt:0x%x\n", |
| 268 | __func__, module, layer, plane->width, plane->height, |
| 269 | pixel_byte * 8, format); |
| 270 | debug("%s: b:0x%x, l:%d, t:%d, r:%d, b:%d, hs:%d, vs:%d\n", |
| 271 | __func__, plane->fb_base, sx, sy, ex, ey, |
| 272 | plane->width * pixel_byte, pixel_byte); |
| 273 | |
| 274 | return 0; |
| 275 | } |
| 276 | |
| 277 | int dp_plane_set_enable(int module, int layer, int on) |
| 278 | { |
| 279 | int hl, hc; |
| 280 | int vl, vc; |
| 281 | |
| 282 | debug("%s: dp.%d.%d %s:%s\n", |
| 283 | __func__, module, layer, |
| 284 | layer == MLC_LAYER_VIDEO ? "Video" : "RGB", |
| 285 | on ? "ON" : "OFF"); |
| 286 | |
| 287 | if (layer != MLC_LAYER_VIDEO) { |
| 288 | nx_mlc_set_layer_enable(module, layer, on); |
| 289 | nx_mlc_set_dirty_flag(module, layer); |
| 290 | return 0; |
| 291 | } |
| 292 | |
| 293 | /* video layer */ |
| 294 | if (on) { |
| 295 | nx_mlc_set_video_layer_line_buffer_power_mode(module, 1); |
| 296 | nx_mlc_set_video_layer_line_buffer_sleep_mode(module, 0); |
| 297 | nx_mlc_set_layer_enable(module, layer, 1); |
| 298 | nx_mlc_set_dirty_flag(module, layer); |
| 299 | } else { |
| 300 | nx_mlc_set_layer_enable(module, layer, 0); |
| 301 | nx_mlc_set_dirty_flag(module, layer); |
| 302 | nx_mlc_get_video_layer_scale_filter(module, |
| 303 | &hl, &hc, &vl, &vc); |
| 304 | if (hl || hc || vl || vc) |
| 305 | nx_mlc_set_video_layer_scale_filter(module, 0, 0, 0, 0); |
| 306 | nx_mlc_set_video_layer_line_buffer_power_mode(module, 0); |
| 307 | nx_mlc_set_video_layer_line_buffer_sleep_mode(module, 1); |
| 308 | nx_mlc_set_dirty_flag(module, layer); |
| 309 | } |
| 310 | |
| 311 | return 0; |
| 312 | } |
| 313 | |
| 314 | void dp_plane_layer_enable(int module, |
| 315 | struct dp_plane_info *plane, int on) |
| 316 | { |
| 317 | dp_plane_set_enable(module, plane->layer, on); |
| 318 | } |
| 319 | |
| 320 | int dp_plane_set_address(int module, int layer, unsigned int address) |
| 321 | { |
| 322 | nx_mlc_set_rgblayer_address(module, layer, address); |
| 323 | nx_mlc_set_dirty_flag(module, layer); |
| 324 | |
| 325 | return 0; |
| 326 | } |
| 327 | |
| 328 | int dp_plane_wait_vsync(int module, int layer, int fps) |
| 329 | { |
| 330 | int cnt = 0; |
| 331 | |
| 332 | if (fps == 0) |
| 333 | return (int)nx_mlc_get_dirty_flag(module, layer); |
| 334 | |
| 335 | while (fps > cnt++) { |
| 336 | while (nx_mlc_get_dirty_flag(module, layer)) |
| 337 | ; |
| 338 | nx_mlc_set_dirty_flag(module, layer); |
| 339 | } |
| 340 | return 0; |
| 341 | } |