aboutsummaryrefslogtreecommitdiff
path: root/src/soc/samsung/exynos5420/fimd.c
diff options
context:
space:
mode:
authorHung-Te Lin <hungte@chromium.org>2013-09-27 12:45:45 +0800
committerIsaac Christensen <isaac.christensen@se-eng.com>2014-08-26 17:55:18 +0200
commit22d0ca0ceb802675cdcab1472b8477066f729373 (patch)
tree79e2e38a2c6b34125f48b05cfd7f9ef3c88c833d /src/soc/samsung/exynos5420/fimd.c
parentb123e0d3345554d7e93361bb4511a53bc95d41a1 (diff)
armv7: Move Exynos from 'cpu' to 'soc'.
The Exynos family and most ARM products are SoC, not just CPU. We used to put ARM code in src/cpu to avoid polluting the code base for what was essentially an experiment at the time. Now that it's past the experimental phase and we're going to see more SoCs (including intel/baytrail) in coreboot. Change-Id: I5ea1f822664244edf5f77087bc8018d7c535f81c Reviewed-on: https://chromium-review.googlesource.com/170891 Tested-by: Hung-Te Lin <hungte@chromium.org> Reviewed-by: Aaron Durbin <adurbin@chromium.org> Reviewed-by: Ronald Minnich <rminnich@chromium.org> Commit-Queue: Hung-Te Lin <hungte@chromium.org> (cherry picked from commit c8bb8fe0b20be37465f93c738d80e7e43033670a) Signed-off-by: Isaac Christensen <isaac.christensen@se-eng.com> Reviewed-on: http://review.coreboot.org/6739 Tested-by: build bot (Jenkins) Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
Diffstat (limited to 'src/soc/samsung/exynos5420/fimd.c')
-rw-r--r--src/soc/samsung/exynos5420/fimd.c432
1 files changed, 432 insertions, 0 deletions
diff --git a/src/soc/samsung/exynos5420/fimd.c b/src/soc/samsung/exynos5420/fimd.c
new file mode 100644
index 0000000000..1e51712e53
--- /dev/null
+++ b/src/soc/samsung/exynos5420/fimd.c
@@ -0,0 +1,432 @@
+/*
+ * Copyright 2013 Google Inc.
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ * Author: InKi Dae <inki.dae@samsung.com>
+ * Author: Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <delay.h>
+#include <arch/io.h>
+#include <console/console.h>
+#include "clk.h"
+#include "dp.h"
+#include "fimd.h"
+#include "periph.h"
+#include "sysreg.h"
+#include "timer.h"
+
+/* fairly useful debugging stuff. */
+#if 0
+static inline void fwadl(unsigned long l,void *v) {
+ writel(l, v);
+ printk(BIOS_SPEW, "W %p %p\n", v, (void *)l);
+}
+#define lwritel(a,b) fwadl((unsigned long)(a), (void *)(b))
+
+static inline unsigned long fradl(void *v) {
+ unsigned long l = readl(v);
+ printk(BIOS_SPEW, "R %p %p\n", v, (void *)l);
+ return l;
+}
+
+#define lreadl(a) fradl((void *)(a))
+
+#else
+#define lwritel(a,b) writel((unsigned long)(a), (void *)(b))
+#define lreadl(a) readl((void *)(a))
+#endif
+
+/* not sure where we want this so ... */
+static unsigned long get_lcd_clk(void)
+{
+ u32 pclk, sclk;
+ unsigned int sel;
+ unsigned int ratio;
+
+ /*
+ * CLK_SRC_DISP10
+ * CLKMUX_FIMD1 [4]
+ * 0: SCLK_RPLL
+ * 1: SCLK_SPLL
+ */
+ sel = lreadl(&exynos_clock->clk_src_disp10);
+ sel &= (1 << 4);
+
+ if (sel){
+ sclk = get_pll_clk(SPLL);
+ } else {
+ sclk = get_pll_clk(RPLL);
+ }
+
+ /*
+ * CLK_DIV_DISP10
+ * FIMD1_RATIO [3:0]
+ */
+ ratio = lreadl(&exynos_clock->clk_div_disp10);
+ ratio = ratio & 0xf;
+
+ pclk = sclk / (ratio + 1);
+
+ return pclk;
+}
+
+static void exynos_fimd_set_dualrgb(vidinfo_t *vid, unsigned int enabled)
+{
+ unsigned int cfg = 0;
+ printk(BIOS_SPEW, "%s %s\n", __func__, enabled ? "enabled" : "not enabled");
+ if (enabled) {
+ cfg = EXYNOS_DUALRGB_BYPASS_DUAL | EXYNOS_DUALRGB_LINESPLIT |
+ EXYNOS_DUALRGB_VDEN_EN_ENABLE;
+
+ /* in case of Line Split mode, MAIN_CNT doesn't neet to set. */
+ cfg |= EXYNOS_DUALRGB_SUB_CNT(vid->vl_col / 2) |
+ EXYNOS_DUALRGB_MAIN_CNT(0);
+ }
+
+ lwritel(cfg, &FIMD_CTRL->dualrgb);
+}
+
+static void exynos_fimd_set_dp_clkcon(unsigned int enabled)
+{
+ unsigned int cfg = 0;
+
+ if (enabled){
+ cfg = EXYNOS_DP_CLK_ENABLE;
+ }
+
+ lwritel(cfg, &FIMD_CTRL->dp_mie_clkcon);
+}
+
+static void exynos_fimd_set_par(vidinfo_t *vid, unsigned int win_id)
+{
+ unsigned int cfg = 0;
+ printk(BIOS_SPEW, "%s %d\n", __func__, win_id);
+ /* set window control */
+ cfg = lreadl(&FIMD_CTRL->wincon0 +
+ EXYNOS_WINCON(win_id));
+
+ cfg &= ~(EXYNOS_WINCON_BITSWP_ENABLE | EXYNOS_WINCON_BYTESWP_ENABLE |
+ EXYNOS_WINCON_HAWSWP_ENABLE | EXYNOS_WINCON_WSWP_ENABLE |
+ EXYNOS_WINCON_BURSTLEN_MASK | EXYNOS_WINCON_BPPMODE_MASK |
+ EXYNOS_WINCON_INRGB_MASK | EXYNOS_WINCON_DATAPATH_MASK);
+
+ /* DATAPATH is DMA */
+ cfg |= EXYNOS_WINCON_DATAPATH_DMA;
+
+ cfg |= EXYNOS_WINCON_HAWSWP_ENABLE;
+
+ /* dma burst is 16 */
+ cfg |= EXYNOS_WINCON_BURSTLEN_16WORD;
+
+ cfg |= EXYNOS_WINCON_BPPMODE_16BPP_565;
+
+ lwritel(cfg, &FIMD_CTRL->wincon0 +
+ EXYNOS_WINCON(win_id));
+
+ /* set window position to x=0, y=0*/
+ cfg = EXYNOS_VIDOSD_LEFT_X(0) | EXYNOS_VIDOSD_TOP_Y(0);
+ lwritel(cfg, &FIMD_CTRL->vidosd0a +
+ EXYNOS_VIDOSD(win_id));
+
+ cfg = EXYNOS_VIDOSD_RIGHT_X(vid->vl_col - 1) |
+ EXYNOS_VIDOSD_BOTTOM_Y(vid->vl_row - 1) |
+ EXYNOS_VIDOSD_RIGHT_X_E(1) |
+ EXYNOS_VIDOSD_BOTTOM_Y_E(0);
+
+ lwritel(cfg, &FIMD_CTRL->vidosd0b +
+ EXYNOS_VIDOSD(win_id));
+ /* set window size for window0*/
+ cfg = EXYNOS_VIDOSD_SIZE(vid->vl_col * vid->vl_row);
+ lwritel(cfg, &FIMD_CTRL->vidosd0c +
+ EXYNOS_VIDOSD(win_id));
+}
+
+static void exynos_fimd_set_buffer_address(vidinfo_t *vid,
+ void *screen_base, int win_id)
+{
+ u32 start_addr, end_addr;
+ printk(BIOS_SPEW, "%s %d\n", __func__, win_id);
+ start_addr = (u32)screen_base;
+ end_addr = start_addr + ((vid->vl_col * ((1<<vid->vl_bpix) / 8)) *
+ vid->vl_row);
+
+ lwritel(start_addr, &FIMD_CTRL->vidw00add0b0 +
+ EXYNOS_BUFFER_OFFSET(win_id));
+ lwritel(end_addr, &FIMD_CTRL->vidw00add1b0 +
+ EXYNOS_BUFFER_OFFSET(win_id));
+}
+
+static void exynos_fimd_set_clock(vidinfo_t *vid)
+{
+ unsigned int cfg = 0, div = 0, remainder = 0, remainder_div;
+ unsigned long pixel_clock;
+ unsigned long long src_clock;
+ printk(BIOS_SPEW, "%s\n", __func__);
+ if (vid->dual_lcd_enabled) {
+ pixel_clock = vid->vl_freq *
+ (vid->vl_hspw + vid->vl_hfpd +
+ vid->vl_hbpd + vid->vl_col / 2) *
+ (vid->vl_vspw + vid->vl_vfpd +
+ vid->vl_vbpd + vid->vl_row);
+ } else if (vid->interface_mode == FIMD_CPU_INTERFACE) {
+ pixel_clock = vid->vl_freq *
+ vid->vl_width * vid->vl_height *
+ (vid->cs_setup + vid->wr_setup +
+ vid->wr_act + vid->wr_hold + 1);
+ } else {
+ pixel_clock = vid->vl_freq *
+ (vid->vl_hspw + vid->vl_hfpd +
+ vid->vl_hbpd + vid->vl_col) *
+ (vid->vl_vspw + vid->vl_vfpd +
+ vid->vl_vbpd + vid->vl_row);
+ }
+ printk(BIOS_SPEW, "Pixel clock is %lx\n", pixel_clock);
+
+ cfg = lreadl(&FIMD_CTRL->vidcon0);
+ cfg &= ~(EXYNOS_VIDCON0_CLKSEL_MASK | EXYNOS_VIDCON0_CLKVALUP_MASK |
+ EXYNOS_VIDCON0_CLKVAL_F(0xFF) | EXYNOS_VIDCON0_VCLKEN_MASK |
+ EXYNOS_VIDCON0_CLKDIR_MASK);
+ cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS |
+ EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED);
+
+ src_clock = (unsigned long long) get_lcd_clk();
+
+ /* get quotient and remainder. */
+ remainder = src_clock % pixel_clock;
+ src_clock /= pixel_clock;
+
+ div = src_clock;
+
+ remainder *= 10;
+ remainder_div = remainder / pixel_clock;
+
+ /* round about one places of decimals. */
+ if (remainder_div >= 5)
+ div++;
+
+ /* in case of dual lcd mode. */
+ if (vid->dual_lcd_enabled)
+ div--;
+
+ cfg |= EXYNOS_VIDCON0_CLKVAL_F(div - 1);
+ lwritel(cfg, &FIMD_CTRL->vidcon0);
+}
+
+void exynos_set_trigger(void)
+{
+ unsigned int cfg = 0;
+ printk(BIOS_SPEW, "%s\n", __func__);
+ cfg = lreadl(&FIMD_CTRL->trigcon);
+
+ cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG);
+
+ lwritel(cfg, &FIMD_CTRL->trigcon);
+}
+
+int exynos_is_i80_frame_done(void)
+{
+ unsigned int cfg = 0;
+ int status;
+ printk(BIOS_SPEW, "%s\n", __func__);
+ cfg = lreadl(&FIMD_CTRL->trigcon);
+
+ /* frame done func is valid only when TRIMODE[0] is set to 1. */
+ status = (cfg & EXYNOS_I80STATUS_TRIG_DONE) ==
+ EXYNOS_I80STATUS_TRIG_DONE;
+
+ return status;
+}
+
+static void exynos_fimd_lcd_on(void)
+{
+ unsigned int cfg = 0;
+
+ printk(BIOS_SPEW, "%s\n", __func__);
+ /* display on */
+ cfg = lreadl(&FIMD_CTRL->vidcon0);
+ cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE);
+ lwritel(cfg, &FIMD_CTRL->vidcon0);
+}
+
+static void exynos_fimd_window_on(unsigned int win_id)
+{
+ unsigned int cfg = 0;
+ printk(BIOS_SPEW, "%s %d\n", __func__, win_id);
+ /* enable window */
+ cfg = lreadl(&FIMD_CTRL->wincon0 +
+ EXYNOS_WINCON(win_id));
+ cfg |= EXYNOS_WINCON_ENWIN_ENABLE;
+ lwritel(cfg, &FIMD_CTRL->wincon0 +
+ EXYNOS_WINCON(win_id));
+
+ cfg = lreadl(&FIMD_CTRL->winshmap);
+ cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id);
+ lwritel(cfg, &FIMD_CTRL->winshmap);
+ cfg = lreadl(&FIMD_CTRL->winshmap);
+}
+
+void exynos_fimd_lcd_off(void)
+{
+ unsigned int cfg = 0;
+ printk(BIOS_SPEW, "%s\n", __func__);
+
+ cfg = lreadl(&FIMD_CTRL->vidcon0);
+ cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE);
+ lwritel(cfg, &FIMD_CTRL->vidcon0);
+}
+
+void exynos_fimd_window_off(unsigned int win_id)
+{
+ unsigned int cfg = 0;
+ printk(BIOS_SPEW, "%s %d\n", __func__, win_id);
+
+ cfg = lreadl(&FIMD_CTRL->wincon0 +
+ EXYNOS_WINCON(win_id));
+ cfg &= EXYNOS_WINCON_ENWIN_DISABLE;
+ lwritel(cfg, &FIMD_CTRL->wincon0 +
+ EXYNOS_WINCON(win_id));
+
+ cfg = lreadl(&FIMD_CTRL->winshmap);
+ cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id);
+ lwritel(cfg, &FIMD_CTRL->winshmap);
+}
+
+static void exynos5_set_system_display(void)
+{
+ unsigned int cfg = 0;
+
+ /*
+ * system register path set
+ * 0: MIE/MDNIE
+ * 1: FIMD Bypass
+ */
+ cfg = lreadl(&exynos_sysreg->disp1blk_cfg);
+ cfg |= (1 << 15);
+ lwritel(cfg, &exynos_sysreg->disp1blk_cfg);
+}
+
+void exynos_fimd_lcd_init(vidinfo_t *vid)
+{
+ unsigned int cfg = 0, rgb_mode;
+ unsigned int offset;
+
+ offset = exynos_fimd_get_base_offset();
+ printk(BIOS_SPEW, "%s\n", __func__);
+ exynos5_set_system_display();
+
+ rgb_mode = vid->rgb_mode;
+
+ if (vid->interface_mode == FIMD_RGB_INTERFACE) {
+ printk(BIOS_SPEW, "%s FIMD_RGB_INTERFACE\n", __func__);
+
+ cfg |= EXYNOS_VIDCON0_VIDOUT_RGB;
+ lwritel(cfg, &FIMD_CTRL->vidcon0);
+
+ cfg = lreadl(&FIMD_CTRL->vidcon2);
+ cfg &= ~(EXYNOS_VIDCON2_WB_MASK |
+ EXYNOS_VIDCON2_TVFORMATSEL_MASK |
+ EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK);
+ cfg |= EXYNOS_VIDCON2_WB_DISABLE;
+ lwritel(cfg, &FIMD_CTRL->vidcon2);
+
+ /* set polarity */
+ cfg = 0;
+ if (!vid->vl_clkp)
+ cfg |= EXYNOS_VIDCON1_IVCLK_RISING_EDGE;
+ if (!vid->vl_hsp)
+ cfg |= EXYNOS_VIDCON1_IHSYNC_INVERT;
+ if (!vid->vl_vsp)
+ cfg |= EXYNOS_VIDCON1_IVSYNC_INVERT;
+ if (!vid->vl_dp)
+ cfg |= EXYNOS_VIDCON1_IVDEN_INVERT;
+
+ lwritel(cfg, &FIMD_CTRL->vidcon1 + offset);
+
+ /* set timing */
+ cfg = EXYNOS_VIDTCON0_VFPD(vid->vl_vfpd - 1);
+ cfg |= EXYNOS_VIDTCON0_VBPD(vid->vl_vbpd - 1);
+ cfg |= EXYNOS_VIDTCON0_VSPW(vid->vl_vspw - 1);
+ lwritel(cfg, &FIMD_CTRL->vidtcon0 + offset);
+
+ cfg = EXYNOS_VIDTCON1_HFPD(vid->vl_hfpd - 1);
+ cfg |= EXYNOS_VIDTCON1_HBPD(vid->vl_hbpd - 1);
+ cfg |= EXYNOS_VIDTCON1_HSPW(vid->vl_hspw - 1);
+
+ lwritel(cfg, &FIMD_CTRL->vidtcon1 + offset);
+
+ /* set lcd size */
+ cfg = EXYNOS_VIDTCON2_HOZVAL(vid->vl_col - 1) |
+ EXYNOS_VIDTCON2_LINEVAL(vid->vl_row - 1) |
+ EXYNOS_VIDTCON2_HOZVAL_E(vid->vl_col - 1) |
+ EXYNOS_VIDTCON2_LINEVAL_E(vid->vl_row - 1);
+
+ lwritel(cfg, &FIMD_CTRL->vidtcon2 + offset);
+ }
+
+ /* set display mode */
+ cfg = lreadl(&FIMD_CTRL->vidcon0);
+ cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK;
+ cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT);
+ lwritel(cfg, &FIMD_CTRL->vidcon0);
+
+ /* set par */
+ exynos_fimd_set_par(vid, vid->win_id);
+
+ /* set memory address */
+ exynos_fimd_set_buffer_address(vid, vid->screen_base, vid->win_id);
+
+ /* set buffer size */
+ cfg = EXYNOS_VIDADDR_PAGEWIDTH(vid->vl_col * (1<<vid->vl_bpix) / 8) |
+ EXYNOS_VIDADDR_PAGEWIDTH_E(vid->vl_col * (1<<vid->vl_bpix) / 8) |
+ EXYNOS_VIDADDR_OFFSIZE(0) |
+ EXYNOS_VIDADDR_OFFSIZE_E(0);
+
+ lwritel(cfg, &FIMD_CTRL->vidw00add2 +
+ EXYNOS_BUFFER_SIZE(vid->win_id));
+
+ /* set clock */
+ exynos_fimd_set_clock(vid);
+
+ /* set rgb mode to dual lcd. */
+ exynos_fimd_set_dualrgb(vid, vid->dual_lcd_enabled);
+
+ /* display on */
+ exynos_fimd_lcd_on();
+
+ /* window on */
+ exynos_fimd_window_on(vid->win_id);
+
+ exynos_fimd_set_dp_clkcon(vid->dp_enabled);
+ exynos5_set_system_display();
+ printk(BIOS_SPEW, "%s: done\n", __func__);
+}
+
+unsigned long exynos_fimd_calc_fbsize(vidinfo_t *vid)
+{
+ printk(BIOS_SPEW, "%s\n", __func__);
+ return vid->vl_col * vid->vl_row * ((1<<vid->vl_bpix) / 8);
+}
+
+void exynos_fimd_lcd_disable(void)
+{
+ int i;
+ printk(BIOS_SPEW, "%s\n", __func__);
+
+ for (i = 0; i < 4; i++)
+ exynos_fimd_window_off(i);
+}