From 6d4339f622ef378d903a0c35484581533a242d2b Mon Sep 17 00:00:00 2001 From: Donghwa Lee Date: Thu, 5 Apr 2012 19:36:17 +0000 Subject: EXYNOS: support EXYNOS framebuffer and FIMD display drivers. This patch support EXYNOS FB and FIMD display drivers. Signed-off-by: Donghwa Lee Signed-off-by: Kyungmin Park Signed-off-by: Inki Dae Acked-by: Anatolij Gustschin --- drivers/video/Makefile | 1 + drivers/video/exynos_fb.c | 128 ++++++++++++++++ drivers/video/exynos_fb.h | 61 ++++++++ drivers/video/exynos_fimd.c | 354 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 544 insertions(+) create mode 100644 drivers/video/exynos_fb.c create mode 100644 drivers/video/exynos_fb.h create mode 100644 drivers/video/exynos_fimd.c (limited to 'drivers') diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 6252f6a..8896abe 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -28,6 +28,7 @@ LIB := $(obj)libvideo.o COBJS-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o COBJS-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o COBJS-$(CONFIG_CFB_CONSOLE) += cfb_console.o +COBJS-$(CONFIG_EXYNOS_FB) += exynos_fb.o exynos_fimd.o COBJS-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o COBJS-$(CONFIG_S6E63D6) += s6e63d6.o COBJS-$(CONFIG_SED156X) += sed156x.o diff --git a/drivers/video/exynos_fb.c b/drivers/video/exynos_fb.c new file mode 100644 index 0000000..0eb7f58 --- /dev/null +++ b/drivers/video/exynos_fb.c @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae + * Author: Donghwa Lee + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exynos_fb.h" + +int lcd_line_length; +int lcd_color_fg; +int lcd_color_bg; + +void *lcd_base; +void *lcd_console_address; + +short console_col; +short console_row; + +static unsigned int panel_width, panel_height; + +/* LCD Panel data */ +vidinfo_t panel_info; + +static void exynos_lcd_init_mem(void *lcdbase, vidinfo_t *vid) +{ + unsigned long palette_size; + unsigned int fb_size; + + fb_size = vid->vl_row * vid->vl_col * (vid->vl_bpix >> 3); + + lcd_base = lcdbase; + + palette_size = NBITS(vid->vl_bpix) == 8 ? 256 : 16; + + exynos_fimd_lcd_init_mem((unsigned long)lcd_base, + (unsigned long)fb_size, palette_size); +} + +static void exynos_lcd_init(vidinfo_t *vid) +{ + exynos_fimd_lcd_init(vid); +} + +static void lcd_panel_on(vidinfo_t *vid) +{ + udelay(vid->init_delay); + + if (vid->backlight_reset) + vid->backlight_reset(); + + if (vid->cfg_gpio) + vid->cfg_gpio(); + + if (vid->lcd_power_on) + vid->lcd_power_on(); + + udelay(vid->power_on_delay); + + if (vid->reset_lcd) { + vid->reset_lcd(); + udelay(vid->reset_delay); + } + + if (vid->backlight_on) + vid->backlight_on(1); + + if (vid->cfg_ldo) + vid->cfg_ldo(); + + if (vid->enable_ldo) + vid->enable_ldo(1); + + if (vid->mipi_enabled) + exynos_mipi_dsi_init(); +} + +void lcd_ctrl_init(void *lcdbase) +{ + set_system_display_ctrl(); + set_lcd_clk(); + + /* initialize parameters which is specific to panel. */ + init_panel_info(&panel_info); + + panel_width = panel_info.vl_width; + panel_height = panel_info.vl_height; + + exynos_lcd_init_mem(lcdbase, &panel_info); + + exynos_lcd_init(&panel_info); +} + +void lcd_enable(void) +{ + lcd_panel_on(&panel_info); +} + +/* dummy function */ +void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) +{ + return; +} diff --git a/drivers/video/exynos_fb.h b/drivers/video/exynos_fb.h new file mode 100644 index 0000000..66f5da6 --- /dev/null +++ b/drivers/video/exynos_fb.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae + * Author: Donghwa Lee + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _EXYNOS_FB_H_ +#define _EXYNOS_FB_H_ + +#include + +#define MAX_CLOCK (86 * 1000000) + +enum exynos_fb_rgb_mode_t { + MODE_RGB_P = 0, + MODE_BGR_P = 1, + MODE_RGB_S = 2, + MODE_BGR_S = 3, +}; + +enum exynos_cpu_auto_cmd_rate { + DISABLE_AUTO_FRM, + PER_TWO_FRM, + PER_FOUR_FRM, + PER_SIX_FRM, + PER_EIGHT_FRM, + PER_TEN_FRM, + PER_TWELVE_FRM, + PER_FOURTEEN_FRM, + PER_SIXTEEN_FRM, + PER_EIGHTEEN_FRM, + PER_TWENTY_FRM, + PER_TWENTY_TWO_FRM, + PER_TWENTY_FOUR_FRM, + PER_TWENTY_SIX_FRM, + PER_TWENTY_EIGHT_FRM, + PER_THIRTY_FRM, +}; + +void exynos_fimd_lcd_init_mem(unsigned long screen_base, unsigned long fb_size, + unsigned long palette_size); +void exynos_fimd_lcd_init(vidinfo_t *vid); +unsigned long exynos_fimd_calc_fbsize(void); + +#endif diff --git a/drivers/video/exynos_fimd.c b/drivers/video/exynos_fimd.c new file mode 100644 index 0000000..2a60fcf --- /dev/null +++ b/drivers/video/exynos_fimd.c @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2012 Samsung Electronics + * + * Author: InKi Dae + * Author: Donghwa Lee + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "exynos_fb.h" + +static unsigned long *lcd_base_addr; +static vidinfo_t *pvid; + +void exynos_fimd_lcd_init_mem(u_long screen_base, u_long fb_size, + u_long palette_size) +{ + lcd_base_addr = (unsigned long *)screen_base; +} + +static void exynos_fimd_set_dualrgb(unsigned int enabled) +{ + struct exynos4_fb *fimd_ctrl = + (struct exynos4_fb *)samsung_get_base_fimd(); + unsigned int cfg = 0; + + 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(pvid->vl_col / 2) | + EXYNOS_DUALRGB_MAIN_CNT(0); + } + + writel(cfg, &fimd_ctrl->dualrgb); +} + +static void exynos_fimd_set_par(unsigned int win_id) +{ + unsigned int cfg = 0; + struct exynos4_fb *fimd_ctrl = + (struct exynos4_fb *)samsung_get_base_fimd(); + + /* set window control */ + cfg = readl((unsigned int)&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; + + /* bpp is 32 */ + cfg |= EXYNOS_WINCON_WSWP_ENABLE; + + /* dma burst is 16 */ + cfg |= EXYNOS_WINCON_BURSTLEN_16WORD; + + /* pixel format is unpacked RGB888 */ + cfg |= EXYNOS_WINCON_BPPMODE_24BPP_888; + + writel(cfg, (unsigned int)&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); + writel(cfg, (unsigned int)&fimd_ctrl->vidosd0a + + EXYNOS_VIDOSD(win_id)); + + cfg = EXYNOS_VIDOSD_RIGHT_X(pvid->vl_col - 1) | + EXYNOS_VIDOSD_BOTTOM_Y(pvid->vl_row - 1); + writel(cfg, (unsigned int)&fimd_ctrl->vidosd0b + + EXYNOS_VIDOSD(win_id)); + + /* set window size for window0*/ + cfg = EXYNOS_VIDOSD_SIZE(pvid->vl_col * pvid->vl_row); + writel(cfg, (unsigned int)&fimd_ctrl->vidosd0c + + EXYNOS_VIDOSD(win_id)); +} + +static void exynos_fimd_set_buffer_address(unsigned int win_id) +{ + unsigned long start_addr, end_addr; + struct exynos4_fb *fimd_ctrl = + (struct exynos4_fb *)samsung_get_base_fimd(); + + start_addr = (unsigned long)lcd_base_addr; + end_addr = start_addr + ((pvid->vl_col * (pvid->vl_bpix / 8)) * + pvid->vl_row); + + writel(start_addr, (unsigned int)&fimd_ctrl->vidw00add0b0 + + EXYNOS_BUFFER_OFFSET(win_id)); + writel(end_addr, (unsigned int)&fimd_ctrl->vidw00add1b0 + + EXYNOS_BUFFER_OFFSET(win_id)); +} + +static void exynos_fimd_set_clock(vidinfo_t *pvid) +{ + unsigned int cfg = 0, div = 0, remainder, remainder_div; + unsigned long pixel_clock; + unsigned long long src_clock; + struct exynos4_fb *fimd_ctrl = + (struct exynos4_fb *)samsung_get_base_fimd(); + + 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 if (pvid->interface_mode == FIMD_CPU_INTERFACE) { + pixel_clock = pvid->vl_freq * + pvid->vl_width * pvid->vl_height * + (pvid->cs_setup + pvid->wr_setup + + pvid->wr_act + pvid->wr_hold + 1); + } 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); + } + + cfg = readl(&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); + + if (pixel_clock > MAX_CLOCK) + pixel_clock = MAX_CLOCK; + + src_clock = (unsigned long long) get_lcd_clk(); + + /* get quotient and remainder. */ + remainder = do_div(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 (pvid->dual_lcd_enabled) + div--; + + cfg |= EXYNOS_VIDCON0_CLKVAL_F(div - 1); + writel(cfg, &fimd_ctrl->vidcon0); +} + +void exynos_set_trigger(void) +{ + unsigned int cfg = 0; + struct exynos4_fb *fimd_ctrl = + (struct exynos4_fb *)samsung_get_base_fimd(); + + cfg = readl(&fimd_ctrl->trigcon); + + cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG); + + writel(cfg, &fimd_ctrl->trigcon); +} + +int exynos_is_i80_frame_done(void) +{ + unsigned int cfg = 0; + int status; + struct exynos4_fb *fimd_ctrl = + (struct exynos4_fb *)samsung_get_base_fimd(); + + cfg = readl(&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; + struct exynos4_fb *fimd_ctrl = + (struct exynos4_fb *)samsung_get_base_fimd(); + + /* display on */ + cfg = readl(&fimd_ctrl->vidcon0); + cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE); + writel(cfg, &fimd_ctrl->vidcon0); +} + +static void exynos_fimd_window_on(unsigned int win_id) +{ + unsigned int cfg = 0; + struct exynos4_fb *fimd_ctrl = + (struct exynos4_fb *)samsung_get_base_fimd(); + + /* enable window */ + cfg = readl((unsigned int)&fimd_ctrl->wincon0 + + EXYNOS_WINCON(win_id)); + cfg |= EXYNOS_WINCON_ENWIN_ENABLE; + writel(cfg, (unsigned int)&fimd_ctrl->wincon0 + + EXYNOS_WINCON(win_id)); + + cfg = readl(&fimd_ctrl->winshmap); + cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id); + writel(cfg, &fimd_ctrl->winshmap); +} + +void exynos_fimd_lcd_off(void) +{ + unsigned int cfg = 0; + struct exynos4_fb *fimd_ctrl = + (struct exynos4_fb *)samsung_get_base_fimd(); + + cfg = readl(&fimd_ctrl->vidcon0); + cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE); + writel(cfg, &fimd_ctrl->vidcon0); +} + +void exynos_fimd_window_off(unsigned int win_id) +{ + unsigned int cfg = 0; + struct exynos4_fb *fimd_ctrl = + (struct exynos4_fb *)samsung_get_base_fimd(); + + cfg = readl((unsigned int)&fimd_ctrl->wincon0 + + EXYNOS_WINCON(win_id)); + cfg &= EXYNOS_WINCON_ENWIN_DISABLE; + writel(cfg, (unsigned int)&fimd_ctrl->wincon0 + + EXYNOS_WINCON(win_id)); + + cfg = readl(&fimd_ctrl->winshmap); + cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id); + writel(cfg, &fimd_ctrl->winshmap); +} + +void exynos_fimd_lcd_init(vidinfo_t *vid) +{ + unsigned int cfg = 0, rgb_mode; + struct exynos4_fb *fimd_ctrl = + (struct exynos4_fb *)samsung_get_base_fimd(); + + /* store panel info to global variable */ + pvid = vid; + + rgb_mode = MODE_RGB_P; + + if (vid->interface_mode == FIMD_RGB_INTERFACE) { + cfg |= EXYNOS_VIDCON0_VIDOUT_RGB; + writel(cfg, &fimd_ctrl->vidcon0); + + cfg = readl(&fimd_ctrl->vidcon2); + cfg &= ~(EXYNOS_VIDCON2_WB_MASK | + EXYNOS_VIDCON2_TVFORMATSEL_MASK | + EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK); + cfg |= EXYNOS_VIDCON2_WB_DISABLE; + writel(cfg, &fimd_ctrl->vidcon2); + + /* set polarity */ + cfg = 0; + if (!pvid->vl_clkp) + cfg |= EXYNOS_VIDCON1_IVCLK_RISING_EDGE; + if (!pvid->vl_hsp) + cfg |= EXYNOS_VIDCON1_IHSYNC_INVERT; + if (!pvid->vl_vsp) + cfg |= EXYNOS_VIDCON1_IVSYNC_INVERT; + if (!pvid->vl_dp) + cfg |= EXYNOS_VIDCON1_IVDEN_INVERT; + + writel(cfg, &fimd_ctrl->vidcon1); + + /* set timing */ + cfg = EXYNOS_VIDTCON0_VFPD(pvid->vl_vfpd - 1); + cfg |= EXYNOS_VIDTCON0_VBPD(pvid->vl_vbpd - 1); + cfg |= EXYNOS_VIDTCON0_VSPW(pvid->vl_vspw - 1); + writel(cfg, &fimd_ctrl->vidtcon0); + + cfg = EXYNOS_VIDTCON1_HFPD(pvid->vl_hfpd - 1); + cfg |= EXYNOS_VIDTCON1_HBPD(pvid->vl_hbpd - 1); + cfg |= EXYNOS_VIDTCON1_HSPW(pvid->vl_hspw - 1); + + writel(cfg, &fimd_ctrl->vidtcon1); + + /* set lcd size */ + cfg = EXYNOS_VIDTCON2_HOZVAL(pvid->vl_col - 1); + cfg |= EXYNOS_VIDTCON2_LINEVAL(pvid->vl_row - 1); + + writel(cfg, &fimd_ctrl->vidtcon2); + } + + /* set display mode */ + cfg = readl(&fimd_ctrl->vidcon0); + cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK; + cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT); + writel(cfg, &fimd_ctrl->vidcon0); + + /* set par */ + exynos_fimd_set_par(pvid->win_id); + + /* set memory address */ + exynos_fimd_set_buffer_address(pvid->win_id); + + /* set buffer size */ + cfg = EXYNOS_VIDADDR_PAGEWIDTH(pvid->vl_col * pvid->vl_bpix / 8); + writel(cfg, (unsigned int)&fimd_ctrl->vidw00add2 + + EXYNOS_BUFFER_SIZE(pvid->win_id)); + + /* set clock */ + exynos_fimd_set_clock(pvid); + + /* set rgb mode to dual lcd. */ + exynos_fimd_set_dualrgb(pvid->dual_lcd_enabled); + + /* display on */ + exynos_fimd_lcd_on(); + + /* window on */ + exynos_fimd_window_on(pvid->win_id); +} + +unsigned long exynos_fimd_calc_fbsize(void) +{ + return pvid->vl_col * pvid->vl_row * (pvid->vl_bpix / 8); +} -- cgit v1.1