s5pc110: fb: changed source clock frequency for fimd to 222MHz.
[kernel/u-boot.git] / drivers / video / s5p-fimd.c
index 05bd160..cb52faf 100644 (file)
 #include <linux/types.h>
 #include <asm/io.h>
 #include <lcd.h>
+#include <div64.h>
 
+#include <asm/arch/clk.h>
+#include <asm/arch/clock.h>
 #include <asm/arch/cpu.h>
 #include <asm/arch/regs-fb.h>
-#include <asm/arch/hardware.h>
+#include <asm/arch/gpio.h>
 #include "s5p-fb.h"
 
-#define MPLL 1
-
-/* LCD Panel definitions */
-#define PANEL_WIDTH            480
-#define PANEL_HEIGHT           800
-#define S5P_LCD_BPP            32      
-
-#define S5PCFB_VBPE            1
-
-#define S5PCFB_VFPE            1
-
-#define S5PCFB_HRES            480
-#define S5PCFB_VRES            800
-
-#define S5PCFB_HRES_VIRTUAL    480
-#define S5PCFB_VRES_VIRTUAL    800
+/* LCD CONTROLLER REGISTER BASE */
+#define S5PC100_LCRB           0xEE000000
+#define S5PC110_LCRB           0xF8000000
 
-#define S5PCFB_HRES_OSD                480
-#define S5PCFB_VRES_OSD                800
-
-#define S5P_VFRAME_FREQ                60
+#define MPLL 1
 
 static unsigned int ctrl_base;
 static unsigned long *lcd_base_addr;
-
-extern unsigned long get_pll_clk(int pllreg);
+static vidinfo_t *pvid = NULL;
 
 void s5pc_fimd_lcd_init_mem(u_long screen_base, u_long fb_size, u_long palette_size)
 {
@@ -67,66 +53,20 @@ void s5pc_fimd_lcd_init_mem(u_long screen_base, u_long fb_size, u_long palette_s
        return;
 }
 
-void s5pc_c100_gpio_setup(void)
+static void s5pc_fimd_set_dualrgb(unsigned int enabled)
 {
-       /* set GPF0[0:7] for RGB Interface and Data lines */
-       writel(0x22222222, 0xE03000E0);
-
-       /* set Data lines */
-       writel(0x22222222, 0xE0300100);
-       writel(0x22222222, 0xE0300120);
-       writel(0x2222, 0xE0300140);
-
-       /* set gpio configuration pin for MLCD_RST */
-       writel(0x10000000, 0xE0300C20);
+       unsigned int cfg = 0;
 
-       /* set gpio configuration pin for MLCD_ON */
-       writel(0x1000, 0xE0300220);
-       writel(readl(0xE0300224) & 0xf7, 0xE0300224);
+       if (enabled) {
+               cfg = S5P_DUALRGB_BYPASS_DUAL | S5P_DUALRGB_LINESPLIT |
+                       S5P_DUALRGB_VDEN_EN_ENABLE;
 
-       /* set gpio configuration pin for DISPLAY_CS, DISPLAY_CLK and DISPLSY_SI */
-       writel(0x11100000, 0xE0300300);
-}
+               /* in case of Line Split mode, MAIN_CNT doesn't neet to set. */
+               cfg |= S5P_DUALRGB_SUB_CNT(pvid->vl_col/2) | S5P_DUALRGB_MAIN_CNT(0);
+       } else
+               cfg = 0;
 
-void s5pc_c110_gpio_setup(void)
-{
-       /* set GPF0[0:7] for RGB Interface and Data lines */
-       writel(0x22222222, 0xE0200120);
-       /* pull-up/down disable */
-       writel(0x0, 0xE0200128);
-       /* drive strength to max */
-       writel(0xffffffff, 0xE020012C);
-
-       /* set Data lines */
-       writel(0x22222222, 0xE0200140);
-       writel(0x22222222, 0xE0200160);
-       writel(0x2222, 0xE0200180);
-
-       /* drive strength to max */
-       writel(0xffffffff, 0xE020014C);
-       writel(0xffffffff, 0xE020016C);
-       writel(0x000000ff, 0xE020018C);
-
-       /* pull-up/down disable */
-       writel(0x0, 0xE0200148);
-       writel(0x0, 0xE0200168);
-       writel(0x0, 0xE0200188);
-
-       /* display output path selection */
-       writel(0x2, 0xE0107008);
-
-       /* set gpio configuration pin for MLCD_RST */
-       writel(0x10000000, 0xE0200C20);
-
-       /* set gpio configuration pin for MLCD_ON */
-       writel(0x1000, 0xE0200260);
-       writel(readl(0xE0200264) & 0xf7, 0xE0200264);
-
-       /* set gpio configuration pin for DISPLAY_CS, DISPLAY_CLK, DISPLSY_SI and LCD_ID */
-       writel(0x10, 0xE02002E0);
-       writel(0x1110, 0xE0200340);
-
-       return;
+       writel(cfg, ctrl_base + S5P_DUALRGB);
 }
 
 static void s5pc_fimd_set_par(unsigned int win_id)
@@ -161,13 +101,13 @@ static void s5pc_fimd_set_par(unsigned int win_id)
        writel(cfg, ctrl_base + S5P_VIDOSD_A(win_id));
        udebug("window postion left,top = %x\n", cfg);
 
-       cfg = S5P_VIDOSD_RIGHT_X(PANEL_WIDTH - 1) |
-               S5P_VIDOSD_BOTTOM_Y(PANEL_HEIGHT - 1);
+       cfg = S5P_VIDOSD_RIGHT_X(pvid->vl_col - 1) |
+               S5P_VIDOSD_BOTTOM_Y(pvid->vl_row - 1);
        writel(cfg, ctrl_base + S5P_VIDOSD_B(win_id));
        udebug("window postion right,bottom= %x\n", cfg);
 
        /* set window size for window0*/
-       cfg = S5P_VIDOSD_SIZE(PANEL_WIDTH * PANEL_HEIGHT);
+       cfg = S5P_VIDOSD_SIZE(pvid->vl_col * pvid->vl_row);
        writel(cfg, ctrl_base + S5P_VIDOSD_C(win_id));
        udebug("vidosd_c%d= %x\n", win_id, cfg);
 
@@ -179,8 +119,8 @@ static void s5pc_fimd_set_buffer_address(unsigned int win_id)
        unsigned long start_addr, end_addr;
 
        start_addr = (unsigned long)lcd_base_addr;
-       end_addr = start_addr + ((PANEL_WIDTH * (S5P_LCD_BPP / 8))
-               * PANEL_HEIGHT);
+       end_addr = start_addr + ((pvid->vl_col * (pvid->vl_bpix / 8))
+               * pvid->vl_row);
 
        writel(start_addr, ctrl_base + S5P_VIDADDR_START0(win_id));
        writel(end_addr, ctrl_base + S5P_VIDADDR_END0(win_id));
@@ -190,61 +130,150 @@ static void s5pc_fimd_set_buffer_address(unsigned int win_id)
        return;
 }
 
-static void s5pc_fimd_set_clock(vidinfo_t *vid)
+static void s5pc_fimd_set_clock(void)
 {
-       unsigned int cfg = 0, div = 0, mpll_ratio = 0;
+       unsigned int cfg = 0, div = 0, fimd_ratio = 0, temp = 0,
+                    remainder, remainder_div;
        unsigned long pixel_clock, src_clock, max_clock;
+       struct s5pc110_clock *clk = (struct s5pc110_clock *)S5PC1XX_CLOCK_BASE;
+       u64 div64;
 
-       max_clock = 66 * 1000000;
+       s5pc1xx_clock_init();
 
-       pixel_clock = S5P_VFRAME_FREQ * (vid->vl_hpw + vid->vl_blw + vid->vl_elw + vid->vl_width) *
-               (vid->vl_vpw + vid->vl_bfw + vid->vl_efw + vid->vl_height);
+       max_clock = 86 * 1000000;
 
+       if (pvid->dual_lcd_enabled)
+               pixel_clock = pvid->vl_freq * (pvid->vl_hspw + pvid->vl_hfpd +
+                       pvid->vl_hbpd + pvid->vl_col / 2) * (pvid->vl_vspw +
+                           pvid->vl_vfpd + pvid->vl_vbpd + pvid->vl_row);
+       else
+               pixel_clock = pvid->vl_freq * (pvid->vl_hspw + pvid->vl_hfpd +
+                       pvid->vl_hbpd + pvid->vl_col) * (pvid->vl_vspw +
+                           pvid->vl_vfpd + pvid->vl_vbpd + pvid->vl_row);
+
+
+       if (get_pll_clk == NULL) {
+               printf("get_pll_clk is null.\n");
+               return;
+       }
        src_clock = get_pll_clk(MPLL);
 
        cfg = readl(ctrl_base + S5P_VIDCON0);
        cfg &= ~(S5P_VIDCON0_CLKSEL_MASK | S5P_VIDCON0_CLKVALUP_MASK | \
                S5P_VIDCON0_VCLKEN_MASK | S5P_VIDCON0_CLKDIR_MASK);
-       cfg |= (S5P_VIDCON0_CLKSEL_HCLK | S5P_VIDCON0_CLKVALUP_ALWAYS | \
+       cfg |= (S5P_VIDCON0_CLKSEL_SCLK | S5P_VIDCON0_CLKVALUP_ALWAYS | \
                S5P_VIDCON0_VCLKEN_NORMAL | S5P_VIDCON0_CLKDIR_DIVIDED);
 
        if (pixel_clock > max_clock)
                pixel_clock = max_clock;
 
+       /* set source clock to SCLKMPLL. */
+       temp = readl(&clk->src1);
+       writel((temp & ~0xf00000) | 0x600000, &clk->src1);
+       temp = 0;
+
+       /* set fimd ratio to 3. */
+       temp = readl(&clk->div1);
+       writel((temp & ~0xf00000) | 0x200000, &clk->div1);
+       temp = 0;
+
        /* get mpll ratio */
-       if (cpu_is_s5pc110())
-               mpll_ratio = (readl(0xE0100300) & 0xf0000) >> 16;
-       else
-               mpll_ratio = (readl(0xE0100304) & 0xf0) >> 4;
-
-       /* 
-        * It can get sorce clock speed as (mpll / mpll_ratio) 
-        * because lcd controller uses hclk_dsys.
-        * mpll is a parent of hclk_dsys.
-        */
-       div = (unsigned int)((src_clock / (mpll_ratio + 1)) / pixel_clock);
+       temp = readl(&clk->div1);
+       fimd_ratio = (temp & 0xf00000) >> 20;
+       temp = 0;
+
+       div64 = ((u64)src_clock) / (fimd_ratio + 1);
+
+       /* get quotient and remainder. */
+       remainder = do_div(div64, pixel_clock);
+       div = (u32) div64;
+
+       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 (pvid->dual_lcd_enabled)
+               div--;
+
        cfg |= S5P_VIDCON0_CLKVAL_F(div - 1);
        writel(cfg, ctrl_base + S5P_VIDCON0);
 
-       udebug("mpll_ratio = %d, src_clock = %d, pixel_clock = %d, div = %d\n",
-               mpll_ratio, src_clock, pixel_clock, div);
+       udebug("fimd_ratio = %d, src_clock = %d, pixel_clock = %d, div = %d\n",
+               fimd_ratio, src_clock / (fimd_ratio + 1), pixel_clock, div);
 
        return;
 }
 
+static void s5pc_fimd_lcd_on(unsigned int win_id)
+{
+       unsigned int cfg = 0;
+
+       /* display on */
+       cfg = readl(ctrl_base + S5P_VIDCON0);
+       cfg |= (S5P_VIDCON0_ENVID_ENABLE | S5P_VIDCON0_ENVID_F_ENABLE);
+       writel(cfg, ctrl_base + S5P_VIDCON0);
+       udebug("vidcon0 = %x\n", cfg);
+}
+
+static void s5pc_fimd_window_on(unsigned int win_id)
+{
+       unsigned int cfg = 0;
+
+       /* enable window */
+       cfg = readl(ctrl_base + S5P_WINCON(win_id));
+       cfg |= S5P_WINCON_ENWIN_ENABLE;
+       writel(cfg, ctrl_base + S5P_WINCON(win_id));
+       udebug("wincon%d=%x\n", win_id, cfg);
+
+       /* evt1 */
+       cfg = readl(ctrl_base + S5P_WINSHMAP);
+       cfg |= S5P_WINSHMAP_CH_ENABLE(win_id);
+       writel(cfg, ctrl_base + S5P_WINSHMAP);
+}
+
+void s5pc_fimd_lcd_off(unsigned int win_id)
+{
+       unsigned int cfg = 0;
+
+       cfg = readl(ctrl_base + S5P_VIDCON0);
+       cfg &= (S5P_VIDCON0_ENVID_DISABLE | S5P_VIDCON0_ENVID_F_DISABLE);
+       writel(cfg, ctrl_base + S5P_VIDCON0);
+}
+
+void s5pc_fimd_window_off(unsigned int win_id)
+{
+       unsigned int cfg = 0;
+
+       cfg = readl(ctrl_base + S5P_WINCON(win_id));
+       cfg &= S5P_WINCON_ENWIN_DISABLE;
+       writel(cfg, ctrl_base + S5P_WINCON(win_id));
+
+       /* evt1 */
+       cfg = readl(ctrl_base + S5P_WINSHMAP);
+       cfg &= ~S5P_WINSHMAP_CH_DISABLE(win_id);
+       writel(cfg, ctrl_base + S5P_WINSHMAP);
+}
+
 void s5pc_fimd_lcd_init(vidinfo_t *vid)
 {
-       unsigned int cfg = 0, rgb_mode, win_id = 0;
+       unsigned int cfg = 0, rgb_mode, win_id = 3;
+
+       /* store panel info to global variable */
+       pvid = vid;
 
        /* select register base according to cpu type */
        if (cpu_is_s5pc110())
-               ctrl_base = 0xF8000000;
+               ctrl_base = S5PC110_LCRB;
        else
-               ctrl_base = 0xEE000000;
+               ctrl_base = S5PC100_LCRB;
 
        /* set output to RGB */
        rgb_mode = MODE_RGB_P;
-       cfg = readl(ctrl_base + S5P_VIDCON0);   
+       cfg = readl(ctrl_base + S5P_VIDCON0);
        cfg &= ~S5P_VIDCON0_VIDOUT_MASK;
 
        /* clock source is HCLK */
@@ -261,33 +290,38 @@ void s5pc_fimd_lcd_init(vidinfo_t *vid)
 
        /* set polarity */
        cfg = 0;
-       cfg |= S5P_VIDCON1_IVDEN_INVERT | S5P_VIDCON1_IVCLK_RISING_EDGE;
-       writel(cfg, ctrl_base + S5P_VIDCON1);
+       if (!pvid->vl_clkp)
+               cfg |= S5P_VIDCON1_IVCLK_RISING_EDGE;
+       if (!pvid->vl_hsp)
+               cfg |= S5P_VIDCON1_IHSYNC_INVERT;
+       if (!pvid->vl_vsp)
+               cfg |= S5P_VIDCON1_IVSYNC_INVERT;
+       if (!pvid->vl_dp)
+               cfg |= S5P_VIDCON1_IVDEN_INVERT;
 
+       writel(cfg, ctrl_base + S5P_VIDCON1);
 
        /* set timing */
        cfg = 0;
-       //cfg |= S5P_VIDTCON0_VBPDE(S5PCFB_VBPE - 1);
-       cfg |= S5P_VIDTCON0_VBPD(vid->vl_bfw - 1);
-       cfg |= S5P_VIDTCON0_VFPD(vid->vl_efw - 1);
-       cfg |= S5P_VIDTCON0_VSPW(vid->vl_vpw - 1);
+       cfg |= S5P_VIDTCON0_VFPD(pvid->vl_vfpd - 1);
+       cfg |= S5P_VIDTCON0_VBPD(pvid->vl_vbpd - 1);
+       cfg |= S5P_VIDTCON0_VSPW(pvid->vl_vspw - 1);
        writel(cfg, ctrl_base + S5P_VIDTCON0);
        udebug("vidtcon0 = %x\n", cfg);
 
        cfg = 0;
-       //cfg |= S5P_VIDTCON1_VFPDE(S5PCFB_VFPE - 1);
-       cfg |= S5P_VIDTCON1_HBPD(vid->vl_blw - 1);
-       cfg |= S5P_VIDTCON1_HFPD(vid->vl_elw - 1);
-       cfg |= S5P_VIDTCON1_HSPW(vid->vl_hpw - 1);
+       cfg |= S5P_VIDTCON1_HFPD(pvid->vl_hfpd - 1);
+       cfg |= S5P_VIDTCON1_HBPD(pvid->vl_hbpd - 1);
+       cfg |= S5P_VIDTCON1_HSPW(pvid->vl_hspw - 1);
 
        writel(cfg, ctrl_base + S5P_VIDTCON1);
        udebug("vidtcon1 = %x\n", cfg);
 
        /* set lcd size */
        cfg = 0;
-       cfg |= S5P_VIDTCON2_HOZVAL(PANEL_WIDTH - 1);
-       cfg |= S5P_VIDTCON2_LINEVAL(PANEL_HEIGHT - 1);
-       
+       cfg |= S5P_VIDTCON2_HOZVAL(pvid->vl_col - 1);
+       cfg |= S5P_VIDTCON2_LINEVAL(pvid->vl_row - 1);
+
        writel(cfg, ctrl_base + S5P_VIDTCON2);
        udebug("vidtcon2 = %x\n", cfg);
 
@@ -298,24 +332,24 @@ void s5pc_fimd_lcd_init(vidinfo_t *vid)
        s5pc_fimd_set_buffer_address(win_id);
 
        /* set buffer size */
-       cfg = S5P_VIDADDR_PAGEWIDTH(PANEL_WIDTH * S5P_LCD_BPP / 8);
+       cfg = S5P_VIDADDR_PAGEWIDTH(pvid->vl_col * pvid->vl_bpix / 8);
        writel(cfg, ctrl_base + S5P_VIDADDR_SIZE(win_id));
        udebug("vidaddr_pagewidth = %d\n", cfg);
 
        /* set clock */
-       s5pc_fimd_set_clock(vid);
+       s5pc_fimd_set_clock();
+
+       /* set rgb mode to dual lcd. */
+       if (pvid->dual_lcd_enabled)
+               s5pc_fimd_set_dualrgb(1);
+       else
+               s5pc_fimd_set_dualrgb(0);
 
        /* display on */
-       cfg = readl(ctrl_base + S5P_VIDCON0);
-       cfg |= (S5P_VIDCON0_ENVID_ENABLE | S5P_VIDCON0_ENVID_F_ENABLE);
-       writel(cfg, ctrl_base + S5P_VIDCON0);
-       udebug("vidcon0 = %x\n", cfg);
+       s5pc_fimd_lcd_on(win_id);
 
-       /* enable window */
-       cfg = readl(ctrl_base + S5P_WINCON(win_id));
-       cfg |= S5P_WINCON_ENWIN_ENABLE;
-       writel(cfg, ctrl_base + S5P_WINCON(win_id));
-       udebug("wincon%d=%x\n", win_id, cfg);
+       /* window on */
+       s5pc_fimd_window_on(win_id);
 
        udebug("lcd controller init completed.\n");
 
@@ -324,5 +358,5 @@ void s5pc_fimd_lcd_init(vidinfo_t *vid)
 
 ulong s5pc_fimd_calc_fbsize(void)
 {
-       return (PANEL_WIDTH * PANEL_HEIGHT * (S5P_LCD_BPP / 8));
+       return (pvid->vl_col * pvid->vl_row * (pvid->vl_bpix / 8));
 }