diff options
Diffstat (limited to 'drivers/video/samsung_duallcd/s3cfb_fimd6x.c')
-rw-r--r-- | drivers/video/samsung_duallcd/s3cfb_fimd6x.c | 998 |
1 files changed, 998 insertions, 0 deletions
diff --git a/drivers/video/samsung_duallcd/s3cfb_fimd6x.c b/drivers/video/samsung_duallcd/s3cfb_fimd6x.c new file mode 100644 index 0000000..71a24ae --- /dev/null +++ b/drivers/video/samsung_duallcd/s3cfb_fimd6x.c @@ -0,0 +1,998 @@ +/* linux/drivers/video/samsung/s3cfb_fimd6x.c + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * Register interface file for Samsung Display Controller (FIMD) driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/fb.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <mach/map.h> +#include <plat/clock.h> +#include <plat/fb-s5p.h> +#include <plat/regs-fb-s5p.h> + +#include "s3cfb.h" + +void s3cfb_check_line_count(struct s3cfb_global *ctrl) +{ + int timeout = 30 * 5300; + int i = 0; + + do { + if (!(readl(ctrl->regs + S3C_VIDCON1) & 0x7ff0000)) + break; + i++; + } while (i < timeout); + + if (i == timeout) { + dev_err(ctrl->dev, "line count mismatch\n"); + s3cfb_display_on(ctrl); + } +} + +int s3cfb_check_vsync_status(struct s3cfb_global *ctrl) +{ + u32 cfg; + + if (unlikely(!ctrl->regs)) { + dev_err(ctrl->dev, "reg is zero\n"); + return 0; + } + + cfg = (readl(ctrl->regs + S3C_VIDCON1) & S3C_VIDCON1_VSTATUS_MASK); + + if (cfg != S3C_VIDCON1_VSTATUS_ACTIVE && cfg != S3C_VIDCON1_VSTATUS_BACK) + return 1; + else + return 0; +} + +int s3cfb_set_output(struct s3cfb_global *ctrl) +{ + u32 cfg; + + cfg = readl(ctrl->regs + S3C_VIDCON0); + cfg &= ~S3C_VIDCON0_VIDOUT_MASK; + + if (ctrl->output == OUTPUT_RGB) + cfg |= S3C_VIDCON0_VIDOUT_RGB; + else if (ctrl->output == OUTPUT_ITU) + cfg |= S3C_VIDCON0_VIDOUT_ITU; + else if (ctrl->output == OUTPUT_I80LDI0) { + cfg |= S3C_VIDCON0_VIDOUT_I80LDI0; + cfg |= S3C_VIDCON0_DSI_ENABLE; + } else if (ctrl->output == OUTPUT_I80LDI1) + cfg |= S3C_VIDCON0_VIDOUT_I80LDI1; + else if (ctrl->output == OUTPUT_WB_RGB) + cfg |= S3C_VIDCON0_VIDOUT_WB_RGB; + else if (ctrl->output == OUTPUT_WB_I80LDI0) + cfg |= S3C_VIDCON0_VIDOUT_WB_I80LDI0; + else if (ctrl->output == OUTPUT_WB_I80LDI1) + cfg |= S3C_VIDCON0_VIDOUT_WB_I80LDI1; + else { + dev_err(ctrl->dev, "invalid output type: %d\n", ctrl->output); + return -EINVAL; + } + + writel(cfg, ctrl->regs + S3C_VIDCON0); + + cfg = readl(ctrl->regs + S3C_VIDCON2); + cfg &= ~(S3C_VIDCON2_WB_MASK | S3C_VIDCON2_TVFORMATSEL_MASK | \ + S3C_VIDCON2_TVFORMATSEL_YUV_MASK); + + if (ctrl->output == OUTPUT_RGB) + cfg |= S3C_VIDCON2_WB_DISABLE; + else if (ctrl->output == OUTPUT_ITU) + cfg |= S3C_VIDCON2_WB_DISABLE; + else if (ctrl->output == OUTPUT_I80LDI0) + cfg |= S3C_VIDCON2_WB_DISABLE; + else if (ctrl->output == OUTPUT_I80LDI1) + cfg |= S3C_VIDCON2_WB_DISABLE; + else if (ctrl->output == OUTPUT_WB_RGB) + cfg |= (S3C_VIDCON2_WB_ENABLE | S3C_VIDCON2_TVFORMATSEL_SW | \ + S3C_VIDCON2_TVFORMATSEL_YUV444); + else if (ctrl->output == OUTPUT_WB_I80LDI0) + cfg |= (S3C_VIDCON2_WB_ENABLE | S3C_VIDCON2_TVFORMATSEL_SW | \ + S3C_VIDCON2_TVFORMATSEL_YUV444); + else if (ctrl->output == OUTPUT_WB_I80LDI1) + cfg |= (S3C_VIDCON2_WB_ENABLE | S3C_VIDCON2_TVFORMATSEL_SW | \ + S3C_VIDCON2_TVFORMATSEL_YUV444); + else { + dev_err(ctrl->dev, "invalid output type: %d\n", ctrl->output); + return -EINVAL; + } + + writel(cfg, ctrl->regs + S3C_VIDCON2); + + if (ctrl->output == OUTPUT_I80LDI0) { + cfg = readl(ctrl->regs + S3C_I80IFCONA0); + cfg |= ((1<<0)|(1<<8)); + writel(cfg, ctrl->regs + S3C_I80IFCONA0); + } + + return 0; +} + +#ifdef CONFIG_FB_S5P_MIPI_DSIM +void s3cfb_set_trigger(struct s3cfb_global *ctrl) +{ + u32 reg = 0; + + reg = readl(ctrl->regs + S3C_TRIGCON); + + reg |= 1 << 0 | 1 << 1; + + writel(reg, ctrl->regs + S3C_TRIGCON); +} +#endif + +int s3cfb_set_display_mode(struct s3cfb_global *ctrl) +{ + u32 cfg; + + cfg = readl(ctrl->regs + S3C_VIDCON0); + cfg &= ~S3C_VIDCON0_PNRMODE_MASK; + cfg |= (ctrl->rgb_mode << S3C_VIDCON0_PNRMODE_SHIFT); + writel(cfg, ctrl->regs + S3C_VIDCON0); + + return 0; +} + +int s3cfb_display_on(struct s3cfb_global *ctrl) +{ + u32 cfg; + + cfg = readl(ctrl->regs + S3C_VIDCON0); + cfg |= (S3C_VIDCON0_ENVID_ENABLE | S3C_VIDCON0_ENVID_F_ENABLE); + writel(cfg, ctrl->regs + S3C_VIDCON0); + + dev_dbg(ctrl->dev, "global display is on\n"); + + return 0; +} + +int s3cfb_display_off(struct s3cfb_global *ctrl) +{ + u32 cfg, fimd_count = 0; + + cfg = readl(ctrl->regs + S3C_VIDCON0); + cfg |= S3C_VIDCON0_ENVID_ENABLE; + cfg &= ~S3C_VIDCON0_ENVID_F_ENABLE; + + writel(cfg, ctrl->regs + S3C_VIDCON0); + + do { + if (++fimd_count > 2000000) { + dev_err(ctrl->dev, "FIMD off fail\n"); + return 1; + } + if (!(readl(ctrl->regs + S3C_VIDCON0) & 0x1)) + return 0; + } while (1); +} + +int s3cfb_frame_off(struct s3cfb_global *ctrl) +{ + u32 cfg; + + cfg = readl(ctrl->regs + S3C_VIDCON0); + cfg &= ~S3C_VIDCON0_ENVID_F_ENABLE; + writel(cfg, ctrl->regs + S3C_VIDCON0); + + dev_dbg(ctrl->dev, "current frame display is off\n"); + + return 0; +} + +int s3cfb_set_clock(struct s3cfb_global *ctrl) +{ + struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev); + u32 cfg, maxclk, src_clk, vclk, div; + + /* spec is under 100MHz */ + maxclk = 100 * 1000000; + + cfg = readl(ctrl->regs + S3C_VIDCON0); + + if (pdata->hw_ver == 0x70) { + cfg &= ~(S3C_VIDCON0_CLKVALUP_MASK | + S3C_VIDCON0_VCLKEN_MASK); + cfg |= (S3C_VIDCON0_CLKVALUP_ALWAYS | + S3C_VIDCON0_VCLKEN_FREERUN); + + src_clk = clk_get_rate(ctrl->clock); + printk(KERN_DEBUG "FIMD src sclk = %d\n", src_clk); + } else { + cfg &= ~(S3C_VIDCON0_CLKSEL_MASK | + S3C_VIDCON0_CLKVALUP_MASK | + S3C_VIDCON0_VCLKEN_MASK | + S3C_VIDCON0_CLKDIR_MASK); + cfg |= (S3C_VIDCON0_CLKVALUP_ALWAYS | + S3C_VIDCON0_VCLKEN_NORMAL | + S3C_VIDCON0_CLKDIR_DIVIDED); + + if (strcmp(pdata->clk_name, "sclk_fimd") == 0) { + cfg |= S3C_VIDCON0_CLKSEL_SCLK; + src_clk = clk_get_rate(ctrl->clock); + printk(KERN_DEBUG "FIMD src sclk = %d\n", src_clk); + + } else { + cfg |= S3C_VIDCON0_CLKSEL_HCLK; + src_clk = ctrl->clock->parent->rate; + printk(KERN_DEBUG "FIMD src hclk = %d\n", src_clk); + } + } + + vclk = PICOS2KHZ(ctrl->fb[pdata->default_win]->var.pixclock) * 1000; + + if (vclk > maxclk) { + dev_info(ctrl->dev, "vclk(%d) should be smaller than %d\n", + vclk, maxclk); + /* vclk = maxclk; */ + } + + div = DIV_ROUND_CLOSEST(src_clk, vclk); + + if (div == 0) { + dev_err(ctrl->dev, "div(%d) should be non-zero\n", div); + div = 1; + } + + if ((src_clk/div) > maxclk) + dev_info(ctrl->dev, "vclk(%d) should be smaller than %d Hz\n", + src_clk/div, maxclk); + + cfg &= ~S3C_VIDCON0_CLKVAL_F(0xff); + cfg |= S3C_VIDCON0_CLKVAL_F(div - 1); + writel(cfg, ctrl->regs + S3C_VIDCON0); + + dev_info(ctrl->dev, "parent clock: %d, vclk: %d, vclk div: %d\n", + src_clk, vclk, div); + + return 0; +} + +int s3cfb_set_polarity(struct s3cfb_global *ctrl) +{ + struct s3cfb_lcd_polarity *pol; + u32 cfg; + + pol = &ctrl->lcd->polarity; + cfg = 0; + + /* Set VCLK hold scheme */ + cfg &= S3C_VIDCON1_FIXVCLK_MASK; + cfg |= S3C_VIDCON1_FIXVCLK_VCLK_RUN_VDEN_DIS; + + if (pol->rise_vclk) + cfg |= S3C_VIDCON1_IVCLK_RISING_EDGE; + + if (pol->inv_hsync) + cfg |= S3C_VIDCON1_IHSYNC_INVERT; + + if (pol->inv_vsync) + cfg |= S3C_VIDCON1_IVSYNC_INVERT; + + if (pol->inv_vden) + cfg |= S3C_VIDCON1_IVDEN_INVERT; + + writel(cfg, ctrl->regs + S3C_VIDCON1); + + return 0; +} + +int s3cfb_set_polarity_only(struct s3cfb_global *ctrl) +{ + struct s3cfb_lcd_polarity *pol; + u32 cfg = 0; + + pol = &ctrl->lcd->polarity; + + if (pol->rise_vclk) + cfg |= S3C_VIDCON1_IVCLK_RISING_EDGE; + + if (pol->inv_hsync) + cfg |= S3C_VIDCON1_IHSYNC_INVERT; + + if (pol->inv_vsync) + cfg |= S3C_VIDCON1_IVSYNC_INVERT; + + if (pol->inv_vden) + cfg |= S3C_VIDCON1_IVDEN_INVERT; + + writel(cfg, ctrl->regs + S3C_VIDCON1); + + return 0; +} + +int s3cfb_set_timing(struct s3cfb_global *ctrl) +{ + struct s3cfb_lcd_timing *time; + u32 cfg; + + time = &ctrl->lcd->timing; + cfg = 0; + + cfg |= S3C_VIDTCON0_VBPDE(time->v_bpe - 1); + cfg |= S3C_VIDTCON0_VBPD(time->v_bp - 1); + cfg |= S3C_VIDTCON0_VFPD(time->v_fp - 1); + cfg |= S3C_VIDTCON0_VSPW(time->v_sw - 1); + + writel(cfg, ctrl->regs + S3C_VIDTCON0); + + cfg = 0; + + cfg |= S3C_VIDTCON1_VFPDE(time->v_fpe - 1); + cfg |= S3C_VIDTCON1_HBPD(time->h_bp - 1); + cfg |= S3C_VIDTCON1_HFPD(time->h_fp - 1); + cfg |= S3C_VIDTCON1_HSPW(time->h_sw - 1); + + writel(cfg, ctrl->regs + S3C_VIDTCON1); + + return 0; +} + +int s3cfb_set_lcd_size(struct s3cfb_global *ctrl) +{ + u32 cfg = 0; + +#ifdef CONFIG_FB_S5P_WA101S + cfg |= S3C_VIDTCON2_HOZVAL(ctrl->lcd->width - 1 + 6); +#else + cfg |= S3C_VIDTCON2_HOZVAL(ctrl->lcd->width - 1); +#endif + + cfg |= S3C_VIDTCON2_LINEVAL(ctrl->lcd->height - 1); + + writel(cfg, ctrl->regs + S3C_VIDTCON2); + + return 0; +} + +int s3cfb_set_global_interrupt(struct s3cfb_global *ctrl, int enable) +{ + u32 cfg = 0; + + cfg = readl(ctrl->regs + S3C_VIDINTCON0); + cfg &= ~(S3C_VIDINTCON0_INTFRMEN_ENABLE | S3C_VIDINTCON0_INT_ENABLE); + + if (enable) { + dev_dbg(ctrl->dev, "video interrupt is on\n"); + cfg |= (S3C_VIDINTCON0_INTFRMEN_ENABLE | + S3C_VIDINTCON0_INT_ENABLE); + } else { + dev_dbg(ctrl->dev, "video interrupt is off\n"); + cfg |= (S3C_VIDINTCON0_INTFRMEN_DISABLE | + S3C_VIDINTCON0_INT_DISABLE); + } + + writel(cfg, ctrl->regs + S3C_VIDINTCON0); + + return 0; +} + +int s3cfb_set_vsync_interrupt(struct s3cfb_global *ctrl, int enable) +{ + u32 cfg = 0; + + cfg = readl(ctrl->regs + S3C_VIDINTCON0); + cfg &= ~S3C_VIDINTCON0_FRAMESEL0_MASK; + + if (enable) { + dev_dbg(ctrl->dev, "vsync interrupt is on\n"); + cfg |= S3C_VIDINTCON0_FRAMESEL0_VSYNC; + } else { + dev_dbg(ctrl->dev, "vsync interrupt is off\n"); + cfg &= ~S3C_VIDINTCON0_FRAMESEL0_VSYNC; + } + + writel(cfg, ctrl->regs + S3C_VIDINTCON0); + + return 0; +} + +int s3cfb_get_vsync_interrupt(struct s3cfb_global *ctrl) +{ + u32 cfg = 0; + + cfg = readl(ctrl->regs + S3C_VIDINTCON0); + cfg &= S3C_VIDINTCON0_FRAMESEL0_VSYNC; + + if (cfg & S3C_VIDINTCON0_FRAMESEL0_VSYNC) { + dev_dbg(ctrl->dev, "vsync interrupt is on\n"); + return 1; + } else { + dev_dbg(ctrl->dev, "vsync interrupt is off\n"); + return 0; + } +} + + +#ifdef CONFIG_FB_S5P_TRACE_UNDERRUN +int s3cfb_set_fifo_interrupt(struct s3cfb_global *ctrl, int enable) +{ + u32 cfg = 0; + + cfg = readl(ctrl->regs + S3C_VIDINTCON0); + + cfg &= ~(S3C_VIDINTCON0_FIFOSEL_MASK | S3C_VIDINTCON0_FIFOLEVEL_MASK); + cfg |= (S3C_VIDINTCON0_FIFOSEL_ALL | S3C_VIDINTCON0_FIFOLEVEL_EMPTY); + + if (enable) { + dev_dbg(ctrl->dev, "fifo interrupt is on\n"); + cfg |= (S3C_VIDINTCON0_INTFIFO_ENABLE | + S3C_VIDINTCON0_INT_ENABLE); + } else { + dev_dbg(ctrl->dev, "fifo interrupt is off\n"); + cfg &= ~(S3C_VIDINTCON0_INTFIFO_ENABLE | + S3C_VIDINTCON0_INT_ENABLE); + } + + writel(cfg, ctrl->regs + S3C_VIDINTCON0); + + return 0; +} +#endif + +int s3cfb_clear_interrupt(struct s3cfb_global *ctrl) +{ + u32 cfg = 0; + + cfg = readl(ctrl->regs + S3C_VIDINTCON1); + + if (cfg & S3C_VIDINTCON1_INTFIFOPEND) + dev_info(ctrl->dev, "fifo underrun occur\n"); + + cfg |= (S3C_VIDINTCON1_INTVPPEND | S3C_VIDINTCON1_INTI80PEND | + S3C_VIDINTCON1_INTFRMPEND | S3C_VIDINTCON1_INTFIFOPEND); + + writel(cfg, ctrl->regs + S3C_VIDINTCON1); + + return 0; +} + +int s3cfb_channel_localpath_on(struct s3cfb_global *ctrl, int id) +{ + struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev); + u32 cfg; + + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + cfg = readl(ctrl->regs + S3C_WINSHMAP); + cfg |= S3C_WINSHMAP_LOCAL_ENABLE(id); + writel(cfg, ctrl->regs + S3C_WINSHMAP); + } + + dev_dbg(ctrl->dev, "[fb%d] local path enabled\n", id); + + return 0; +} + +int s3cfb_channel_localpath_off(struct s3cfb_global *ctrl, int id) +{ + struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev); + u32 cfg; + + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + cfg = readl(ctrl->regs + S3C_WINSHMAP); + cfg &= ~S3C_WINSHMAP_LOCAL_DISABLE(id); + writel(cfg, ctrl->regs + S3C_WINSHMAP); + } + + dev_dbg(ctrl->dev, "[fb%d] local path disabled\n", id); + + return 0; +} + +int s3cfb_window_on(struct s3cfb_global *ctrl, int id) +{ + struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev); + u32 cfg; + + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + cfg = readl(ctrl->regs + S3C_WINSHMAP); + cfg |= S3C_WINSHMAP_CH_ENABLE(id); + writel(cfg, ctrl->regs + S3C_WINSHMAP); + } + + cfg = readl(ctrl->regs + S3C_WINCON(id)); + cfg |= S3C_WINCON_ENWIN_ENABLE; + writel(cfg, ctrl->regs + S3C_WINCON(id)); + + dev_dbg(ctrl->dev, "[fb%d] turn on\n", id); + + return 0; +} + +int s3cfb_window_off(struct s3cfb_global *ctrl, int id) +{ + struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev); + u32 cfg; + + cfg = readl(ctrl->regs + S3C_WINCON(id)); + cfg &= ~(S3C_WINCON_ENWIN_ENABLE | S3C_WINCON_DATAPATH_MASK); + cfg |= S3C_WINCON_DATAPATH_DMA; + writel(cfg, ctrl->regs + S3C_WINCON(id)); + + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + cfg = readl(ctrl->regs + S3C_WINSHMAP); + cfg &= ~S3C_WINSHMAP_CH_DISABLE(id); + writel(cfg, ctrl->regs + S3C_WINSHMAP); + } + + dev_dbg(ctrl->dev, "[fb%d] turn off\n", id); + + return 0; +} + +int s3cfb_win_map_on(struct s3cfb_global *ctrl, int id, int color) +{ + u32 cfg = 0; + + cfg |= S3C_WINMAP_ENABLE; + cfg |= S3C_WINMAP_COLOR(color); + writel(cfg, ctrl->regs + S3C_WINMAP(id)); + + dev_dbg(ctrl->dev, "[fb%d] win map on : 0x%08x\n", id, color); + + return 0; +} + +int s3cfb_win_map_off(struct s3cfb_global *ctrl, int id) +{ + writel(0, ctrl->regs + S3C_WINMAP(id)); + + dev_dbg(ctrl->dev, "[fb%d] win map off\n", id); + + return 0; +} + +int s3cfb_set_window_control(struct s3cfb_global *ctrl, int id) +{ + struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev); + struct fb_info *fb = ctrl->fb[id]; + struct fb_var_screeninfo *var = &fb->var; + struct s3cfb_window *win = fb->par; + u32 cfg; + u32 shw; + + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + shw = readl(ctrl->regs + S3C_WINSHMAP); + shw |= S3C_WINSHMAP_PROTECT(id); + writel(shw, ctrl->regs + S3C_WINSHMAP); + } + + cfg = readl(ctrl->regs + S3C_WINCON(id)); + + cfg &= ~(S3C_WINCON_BITSWP_ENABLE | S3C_WINCON_BYTESWP_ENABLE | + S3C_WINCON_HAWSWP_ENABLE | S3C_WINCON_WSWP_ENABLE | + S3C_WINCON_BURSTLEN_MASK | S3C_WINCON_BPPMODE_MASK | + S3C_WINCON_INRGB_MASK | S3C_WINCON_DATAPATH_MASK); + + if (win->path != DATA_PATH_DMA) { + dev_dbg(ctrl->dev, "[fb%d] data path: fifo\n", id); + + cfg |= S3C_WINCON_DATAPATH_LOCAL; + + if (win->path == DATA_PATH_FIFO) { + cfg |= S3C_WINCON_INRGB_RGB; + cfg |= S3C_WINCON_BPPMODE_24BPP_888; + } else if (win->path == DATA_PATH_IPC) { + cfg |= S3C_WINCON_INRGB_YUV; + cfg |= S3C_WINCON_BPPMODE_24BPP_888; + } + + if (id == 1) { + cfg &= ~(S3C_WINCON1_LOCALSEL_MASK | + S3C_WINCON1_VP_ENABLE); + + if (win->local_channel == 0) { + cfg |= S3C_WINCON1_LOCALSEL_FIMC1; + } else { + cfg |= (S3C_WINCON1_LOCALSEL_VP | + S3C_WINCON1_VP_ENABLE); + } + } + } else { + dev_dbg(ctrl->dev, "[fb%d] data path: dma\n", id); + + cfg |= S3C_WINCON_DATAPATH_DMA; + + if (fb->var.bits_per_pixel == 16 && pdata->swap & FB_SWAP_HWORD) + cfg |= S3C_WINCON_HAWSWP_ENABLE; + + if (fb->var.bits_per_pixel == 32 && pdata->swap & FB_SWAP_WORD) + cfg |= S3C_WINCON_WSWP_ENABLE; + + /* dma burst */ + if (win->dma_burst == 4) + cfg |= S3C_WINCON_BURSTLEN_4WORD; + else if (win->dma_burst == 8) + cfg |= S3C_WINCON_BURSTLEN_8WORD; + else + cfg |= S3C_WINCON_BURSTLEN_16WORD; + + /* bpp mode set */ + switch (fb->var.bits_per_pixel) { + case 16: + if (var->transp.length == 1) { + dev_dbg(ctrl->dev, + "[fb%d] bpp mode: A1-R5-G5-B5\n", id); + cfg |= S3C_WINCON_BPPMODE_16BPP_A555; + } else if (var->transp.length == 4) { + dev_dbg(ctrl->dev, + "[fb%d] bpp mode: A4-R4-G4-B4\n", id); + cfg |= S3C_WINCON_BPPMODE_16BPP_A444; + } else { + dev_dbg(ctrl->dev, + "[fb%d] bpp mode: R5-G6-B5\n", id); + cfg |= S3C_WINCON_BPPMODE_16BPP_565; + } + break; + + case 24: /* packed 24 bpp: nothing to do for 6.x fimd */ + break; + + case 32: + if (var->transp.length == 0) { + dev_dbg(ctrl->dev, + "[fb%d] bpp mode: R8-G8-B8\n", id); + cfg |= S3C_WINCON_BPPMODE_24BPP_888; + } else { + dev_dbg(ctrl->dev, + "[fb%d] bpp mode: A8-R8-G8-B8\n", id); + cfg |= S3C_WINCON_BPPMODE_32BPP; + } + break; + } + } + + writel(cfg, ctrl->regs + S3C_WINCON(id)); + + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + shw = readl(ctrl->regs + S3C_WINSHMAP); + shw &= ~(S3C_WINSHMAP_PROTECT(id)); + writel(shw, ctrl->regs + S3C_WINSHMAP); + } + + return 0; +} + +int s3cfb_get_win_cur_buf_addr(struct s3cfb_global *ctrl, int id) +{ + dma_addr_t start_addr = 0; + + start_addr = readl(ctrl->regs + S3C_VIDADDR_START0(id) + S3C_SHD_WIN_BASE); + + dev_dbg(ctrl->dev, "[fb%d] start_addr: 0x%08x\n", id, start_addr); + + return start_addr; +} + +int s3cfb_set_buffer_address(struct s3cfb_global *ctrl, int id) +{ + struct fb_fix_screeninfo *fix = &ctrl->fb[id]->fix; + struct fb_var_screeninfo *var = &ctrl->fb[id]->var; + struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev); + dma_addr_t start_addr = 0, end_addr = 0; + u32 shw; + + if (fix->smem_start) { + start_addr = fix->smem_start + ((var->xres_virtual * + var->yoffset + var->xoffset) * + (var->bits_per_pixel / 8)); + + end_addr = start_addr + fix->line_length * var->yres; + } + + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + shw = readl(ctrl->regs + S3C_WINSHMAP); + shw |= S3C_WINSHMAP_PROTECT(id); + writel(shw, ctrl->regs + S3C_WINSHMAP); + } + + writel(start_addr, ctrl->regs + S3C_VIDADDR_START0(id)); + writel(end_addr, ctrl->regs + S3C_VIDADDR_END0(id)); + + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + shw = readl(ctrl->regs + S3C_WINSHMAP); + shw &= ~(S3C_WINSHMAP_PROTECT(id)); + writel(shw, ctrl->regs + S3C_WINSHMAP); + } + + dev_dbg(ctrl->dev, "[fb%d] start_addr: 0x%08x, end_addr: 0x%08x\n", + id, start_addr, end_addr); + + return 0; +} + +int s3cfb_set_alpha_value(struct s3cfb_global *ctrl, int value) +{ + writel(value, ctrl->regs + S3C_BLENDCON); + + return 0; +} + +int s3cfb_set_alpha_mode(struct s3cfb_global *ctrl, int id, unsigned int mode) +{ + if (id <= 0 || id > 5) + return 0; + + if (mode == BLENDING_PREMULT) + writel(0xc1, ctrl->regs + S3C_BLENDEQ1 + 4 * (id - 1)); + else + writel(0xc2, ctrl->regs + S3C_BLENDEQ1 + 4 * (id - 1)); + + return 0; +} + +int s3cfb_set_alpha_value_width(struct s3cfb_global *ctrl, int id) +{ + struct fb_var_screeninfo *var = &ctrl->fb[id]->var; + + if (var->bits_per_pixel == 32 && var->transp.length > 4) + writel(1, ctrl->regs + S3C_BLENDCON); + else + writel(0, ctrl->regs + S3C_BLENDCON); + + return 0; +} + +int s3cfb_set_alpha_blending(struct s3cfb_global *ctrl, int id) +{ + struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev); + struct s3cfb_window *win = ctrl->fb[id]->par; + struct s3cfb_alpha *alpha = &win->alpha; + u32 avalue = 0, cfg; + u32 shw; + + if (id == 0) { + dev_err(ctrl->dev, "[fb%d] does not support alpha blending\n", + id); + return -EINVAL; + } + + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + shw = readl(ctrl->regs + S3C_WINSHMAP); + shw |= S3C_WINSHMAP_PROTECT(id); + writel(shw, ctrl->regs + S3C_WINSHMAP); + } + + cfg = readl(ctrl->regs + S3C_WINCON(id)); + cfg &= ~(S3C_WINCON_BLD_MASK | S3C_WINCON_ALPHA_SEL_MASK); + + if (alpha->mode == PIXEL_BLENDING) { + dev_dbg(ctrl->dev, "[fb%d] alpha mode: pixel blending\n", id); + + /* fixing to DATA[31:24] for alpha value */ + cfg |= (S3C_WINCON_BLD_PIXEL | S3C_WINCON_ALPHA1_SEL); + } else { + dev_dbg(ctrl->dev, "[fb%d] alpha mode: plane %d blending\n", + id, alpha->channel); + + cfg |= S3C_WINCON_BLD_PLANE; + + if (alpha->channel == 0) { + cfg |= S3C_WINCON_ALPHA0_SEL; + avalue = (alpha->value << S3C_VIDOSD_ALPHA0_SHIFT); + } else { + cfg |= S3C_WINCON_ALPHA1_SEL; + avalue = (alpha->value << S3C_VIDOSD_ALPHA1_SHIFT); + } + } + + writel(cfg, ctrl->regs + S3C_WINCON(id)); + writel(avalue, ctrl->regs + S3C_VIDOSD_C(id)); + + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + shw = readl(ctrl->regs + S3C_WINSHMAP); + shw &= ~(S3C_WINSHMAP_PROTECT(id)); + writel(shw, ctrl->regs + S3C_WINSHMAP); + } + + return 0; +} + +int s3cfb_set_oneshot(struct s3cfb_global *ctrl, int id) +{ + struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev); + struct fb_var_screeninfo *var = &ctrl->fb[id]->var; + struct fb_fix_screeninfo *fix = &ctrl->fb[id]->fix; + struct s3cfb_window *win = ctrl->fb[id]->par; + u32 cfg, shw; + u32 offset = (var->xres_virtual - var->xres) * var->bits_per_pixel / 8; + dma_addr_t start_addr = 0, end_addr = 0; + + /* Shadow Register Protection */ + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + shw = readl(ctrl->regs + S3C_WINSHMAP); + shw |= S3C_WINSHMAP_PROTECT(id); + writel(shw, ctrl->regs + S3C_WINSHMAP); + } + + /* s3cfb_set_window_position */ + cfg = S3C_VIDOSD_LEFT_X(win->x) | S3C_VIDOSD_TOP_Y(win->y); + writel(cfg, ctrl->regs + S3C_VIDOSD_A(id)); + + cfg = S3C_VIDOSD_RIGHT_X(win->x + var->xres - 1) | + S3C_VIDOSD_BOTTOM_Y(win->y + var->yres - 1); + writel(cfg, ctrl->regs + S3C_VIDOSD_B(id)); + + dev_dbg(ctrl->dev, "[fb%d] offset: (%d, %d, %d, %d)\n", id, + win->x, win->y, win->x + var->xres - 1, win->y + var->yres - 1); + + /* s3cfb_set_buffer_address */ + if (fix->smem_start) { + start_addr = fix->smem_start + ((var->xres_virtual * + var->yoffset + var->xoffset) * + (var->bits_per_pixel / 8)); + + end_addr = start_addr + fix->line_length * var->yres; + } + + writel(start_addr, ctrl->regs + S3C_VIDADDR_START0(id)); + writel(end_addr, ctrl->regs + S3C_VIDADDR_END0(id)); + + dev_dbg(ctrl->dev, "[fb%d] start_addr: 0x%08x, end_addr: 0x%08x\n", + id, start_addr, end_addr); + + /* s3cfb_set_window_size */ + if (id <= 2) { + cfg = S3C_VIDOSD_SIZE(var->xres * var->yres); + if (id == 0) + writel(cfg, ctrl->regs + S3C_VIDOSD_C(id)); + else + writel(cfg, ctrl->regs + S3C_VIDOSD_D(id)); + + dev_dbg(ctrl->dev, "[fb%d] resolution: %d x %d\n", id, + var->xres, var->yres); + } + + /* s3cfb_set_buffer_size */ + cfg = S3C_VIDADDR_PAGEWIDTH(var->xres * var->bits_per_pixel / 8); + cfg |= S3C_VIDADDR_OFFSIZE(offset); + writel(cfg, ctrl->regs + S3C_VIDADDR_SIZE(id)); + + /* Shadow Register Un-Protection */ + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + shw = readl(ctrl->regs + S3C_WINSHMAP); + shw &= ~(S3C_WINSHMAP_PROTECT(id)); + writel(shw, ctrl->regs + S3C_WINSHMAP); + } + + return 0; +} + +int s3cfb_set_window_position(struct s3cfb_global *ctrl, int id) +{ + struct fb_var_screeninfo *var = &ctrl->fb[id]->var; + struct s3cfb_window *win = ctrl->fb[id]->par; + u32 cfg, shw; + + shw = readl(ctrl->regs + S3C_WINSHMAP); + shw |= S3C_WINSHMAP_PROTECT(id); + writel(shw, ctrl->regs + S3C_WINSHMAP); + + cfg = S3C_VIDOSD_LEFT_X(win->x) | S3C_VIDOSD_TOP_Y(win->y); + writel(cfg, ctrl->regs + S3C_VIDOSD_A(id)); + + cfg = S3C_VIDOSD_RIGHT_X(win->x + var->xres - 1) | + S3C_VIDOSD_BOTTOM_Y(win->y + var->yres - 1); + + writel(cfg, ctrl->regs + S3C_VIDOSD_B(id)); + + shw = readl(ctrl->regs + S3C_WINSHMAP); + shw &= ~(S3C_WINSHMAP_PROTECT(id)); + writel(shw, ctrl->regs + S3C_WINSHMAP); + + dev_dbg(ctrl->dev, "[fb%d] offset: (%d, %d, %d, %d)\n", id, + win->x, win->y, win->x + var->xres - 1, win->y + var->yres - 1); + + return 0; +} + +int s3cfb_set_window_size(struct s3cfb_global *ctrl, int id) +{ + struct fb_var_screeninfo *var = &ctrl->fb[id]->var; + u32 cfg; + + if (id > 2) + return 0; + + cfg = S3C_VIDOSD_SIZE(var->xres * var->yres); + + if (id == 0) + writel(cfg, ctrl->regs + S3C_VIDOSD_C(id)); + else + writel(cfg, ctrl->regs + S3C_VIDOSD_D(id)); + + dev_dbg(ctrl->dev, "[fb%d] resolution: %d x %d\n", id, + var->xres, var->yres); + + return 0; +} + +int s3cfb_set_buffer_size(struct s3cfb_global *ctrl, int id) +{ + struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev); + struct fb_var_screeninfo *var = &ctrl->fb[id]->var; + u32 offset = (var->xres_virtual - var->xres) * var->bits_per_pixel / 8; + u32 cfg = 0; + u32 shw; + + cfg = S3C_VIDADDR_PAGEWIDTH(var->xres * var->bits_per_pixel / 8); + cfg |= S3C_VIDADDR_OFFSIZE(offset); + + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + shw = readl(ctrl->regs + S3C_WINSHMAP); + shw |= S3C_WINSHMAP_PROTECT(id); + writel(shw, ctrl->regs + S3C_WINSHMAP); + } + + writel(cfg, ctrl->regs + S3C_VIDADDR_SIZE(id)); + + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + shw = readl(ctrl->regs + S3C_WINSHMAP); + shw &= ~(S3C_WINSHMAP_PROTECT(id)); + writel(shw, ctrl->regs + S3C_WINSHMAP); + } + + return 0; +} + +int s3cfb_set_chroma_key(struct s3cfb_global *ctrl, int id) +{ + struct s3c_platform_fb *pdata = to_fb_plat(ctrl->dev); + struct s3cfb_window *win = ctrl->fb[id]->par; + struct s3cfb_chroma *chroma = &win->chroma; + u32 cfg = 0; + u32 shw; + + if (id == 0) { + dev_err(ctrl->dev, "[fb%d] does not support chroma key\n", id); + return -EINVAL; + } + + cfg = (S3C_KEYCON0_KEYBLEN_DISABLE | S3C_KEYCON0_DIRCON_MATCH_FG); + + if (chroma->enabled) + cfg |= S3C_KEYCON0_KEY_ENABLE; + + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + shw = readl(ctrl->regs + S3C_WINSHMAP); + shw |= S3C_WINSHMAP_PROTECT(id); + writel(shw, ctrl->regs + S3C_WINSHMAP); + } + + writel(cfg, ctrl->regs + S3C_KEYCON(id)); + + cfg = S3C_KEYCON1_COLVAL(chroma->key); + writel(cfg, ctrl->regs + S3C_KEYVAL(id)); + + if ((pdata->hw_ver == 0x62) || (pdata->hw_ver == 0x70)) { + shw = readl(ctrl->regs + S3C_WINSHMAP); + shw &= ~(S3C_WINSHMAP_PROTECT(id)); + writel(shw, ctrl->regs + S3C_WINSHMAP); + } + + dev_dbg(ctrl->dev, "[fb%d] chroma key: 0x%08x, %s\n", id, cfg, + chroma->enabled ? "enabled" : "disabled"); + + return 0; +} + +int s3cfb_set_dualrgb(struct s3cfb_global *ctrl, int mode) +{ + writel(mode, ctrl->regs + S3C_DUALRGB); + + return 0; +} + |