aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/samsung/s3cfb_ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/samsung/s3cfb_ops.c')
-rw-r--r--drivers/video/samsung/s3cfb_ops.c694
1 files changed, 678 insertions, 16 deletions
diff --git a/drivers/video/samsung/s3cfb_ops.c b/drivers/video/samsung/s3cfb_ops.c
index 82ece16..759fb63 100644
--- a/drivers/video/samsung/s3cfb_ops.c
+++ b/drivers/video/samsung/s3cfb_ops.c
@@ -15,6 +15,12 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/sw_sync.h>
+#include <plat/regs-fb.h>
+#include <plat/regs-fb-s5p.h>
+
#if defined(CONFIG_CMA)
#include <linux/cma.h>
#elif defined(CONFIG_S5P_MEM_BOOTMEM)
@@ -43,6 +49,10 @@
#include <mach/dev.h>
#endif
+#ifdef CONFIG_FB_S5P_SYSMMU
+#include <plat/s5p-sysmmu.h>
+#endif
+
struct s3c_platform_fb *to_fb_plat(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -183,7 +193,10 @@ int s3cfb_enable_window(struct s3cfb_global *fbdev, int id)
#endif
#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412)
#ifdef CONFIG_BUSFREQ_OPP
- if (id != CONFIG_FB_S5P_DEFAULT_WINDOW)
+ if (CONFIG_FB_S5P_DEFAULT_WINDOW == 3 &&
+ id == CONFIG_FB_S5P_DEFAULT_WINDOW-1)
+ dev_lock(fbdev->bus_dev, fbdev->dev, 267160);
+ else if (id != CONFIG_FB_S5P_DEFAULT_WINDOW)
dev_lock(fbdev->bus_dev, fbdev->dev, 133133);
#endif
#endif
@@ -394,6 +407,10 @@ int s3cfb_map_default_video_memory(struct s3cfb_global *fbdev,
memset(fb->screen_base, 0, fix->smem_len);
win->owner = DMA_MEM_FIMD;
+#ifdef CONFIG_FB_S5P_SYSMMU
+ fbdev->sysmmu.default_fb_addr = fix->smem_start;
+#endif
+
return 0;
}
@@ -457,11 +474,19 @@ int s3cfb_set_bitfield(struct fb_var_screeninfo *var)
break;
case 32:
+#if defined(CONFIG_FB_RGBA_ORDER)
+ var->red.offset = 0;
+#else
var->red.offset = 16;
+#endif
var->red.length = 8;
var->green.offset = 8;
var->green.length = 8;
+#if defined(CONFIG_FB_RGBA_ORDER)
+ var->blue.offset = 16;
+#else
var->blue.offset = 0;
+#endif
var->blue.length = 8;
var->transp.offset = 24;
var->transp.length = 8; /* added for LCD RGB32 */
@@ -1082,30 +1107,631 @@ int s3cfb_cursor(struct fb_info *fb, struct fb_cursor *cursor)
return 0;
}
-int s3cfb_vsync_timestamp_changed(struct s3cfb_global *fbdev, ktime_t prev_timestamp)
+#if !defined(CONFIG_FB_S5P_VSYNC_THREAD)
+int s3cfb_wait_for_vsync(struct s3cfb_global *fbdev)
{
- return !ktime_equal(prev_timestamp, fbdev->vsync_timestamp);
+ dev_dbg(fbdev->dev, "waiting for VSYNC interrupt\n");
+
+ sleep_on_timeout(&fbdev->wq, HZ / 10);
+
+ dev_dbg(fbdev->dev, "got a VSYNC interrupt\n");
+
+ return 0;
}
+#endif
-int s3cfb_wait_for_vsync(struct s3cfb_global *fbdev)
+
+/**
+ * s3c_fb_align_word() - align pixel count to word boundary
+ * @bpp: The number of bits per pixel
+ * @pix: The value to be aligned.
+ *
+ * Align the given pixel count so that it will start on an 32bit word
+ * boundary.
+ */
+static int s3c_fb_align_word(unsigned int bpp, unsigned int pix)
+{
+ int pix_per_word;
+
+ if (bpp > 16)
+ return pix;
+
+ pix_per_word = (8 * 32) / bpp;
+ return ALIGN(pix, pix_per_word);
+}
+
+static u32 s3c_fb_red_length(int format)
+{
+ switch (format) {
+ case S3C_FB_PIXEL_FORMAT_RGBA_8888:
+ case S3C_FB_PIXEL_FORMAT_RGBX_8888:
+ case S3C_FB_PIXEL_FORMAT_RGB_888:
+ case S3C_FB_PIXEL_FORMAT_BGRA_8888:
+ return 8;
+
+ case S3C_FB_PIXEL_FORMAT_RGB_565:
+ case S3C_FB_PIXEL_FORMAT_RGBA_5551:
+ return 5;
+
+ case S3C_FB_PIXEL_FORMAT_RGBA_4444:
+ return 4;
+
+ default:
+ pr_warn("s3c-fb: unrecognized pixel format %u\n", format);
+ return 0;
+ }
+}
+
+static u32 s3c_fb_red_offset(int format)
+{
+ switch (format) {
+ case S3C_FB_PIXEL_FORMAT_RGBA_8888:
+ case S3C_FB_PIXEL_FORMAT_RGBX_8888:
+ case S3C_FB_PIXEL_FORMAT_RGB_888:
+ case S3C_FB_PIXEL_FORMAT_RGB_565:
+ case S3C_FB_PIXEL_FORMAT_RGBA_5551:
+ case S3C_FB_PIXEL_FORMAT_RGBA_4444:
+ return 0;
+
+ case S3C_FB_PIXEL_FORMAT_BGRA_8888:
+ return 16;
+
+ default:
+ pr_warn("s3c-fb: unrecognized pixel format %u\n", format);
+ return 0;
+ }
+}
+
+static u32 s3c_fb_green_length(int format)
+{
+ if (format == S3C_FB_PIXEL_FORMAT_RGB_565)
+ return 6;
+
+ return s3c_fb_red_length(format);
+}
+
+static u32 s3c_fb_green_offset(int format)
+{
+ switch (format) {
+ case S3C_FB_PIXEL_FORMAT_RGBA_8888:
+ case S3C_FB_PIXEL_FORMAT_RGB_888:
+ case S3C_FB_PIXEL_FORMAT_BGRA_8888:
+ case S3C_FB_PIXEL_FORMAT_RGBX_8888:
+ return 8;
+
+ case S3C_FB_PIXEL_FORMAT_RGB_565:
+ case S3C_FB_PIXEL_FORMAT_RGBA_5551:
+ return 5;
+
+ case S3C_FB_PIXEL_FORMAT_RGBA_4444:
+ return 4;
+
+ default:
+ pr_warn("s3c-fb: unrecognized pixel format %u\n", format);
+ return 0;
+ }
+}
+
+static u32 s3c_fb_blue_length(int format)
+{
+ return s3c_fb_red_length(format);
+}
+
+static u32 s3c_fb_blue_offset(int format)
+{
+ switch (format) {
+ case S3C_FB_PIXEL_FORMAT_RGBA_8888:
+ case S3C_FB_PIXEL_FORMAT_RGB_888:
+ case S3C_FB_PIXEL_FORMAT_RGBX_8888:
+ return 16;
+
+ case S3C_FB_PIXEL_FORMAT_RGB_565:
+ return 11;
+
+ case S3C_FB_PIXEL_FORMAT_RGBA_5551:
+ return 10;
+
+ case S3C_FB_PIXEL_FORMAT_RGBA_4444:
+ return 8;
+
+ case S3C_FB_PIXEL_FORMAT_BGRA_8888:
+ return 0;
+
+ default:
+ pr_warn("s3c-fb: unrecognized pixel format %u\n", format);
+ return 0;
+ }
+}
+
+static u32 s3c_fb_transp_length(int format)
+{
+ switch (format) {
+ case S3C_FB_PIXEL_FORMAT_RGBA_8888:
+ case S3C_FB_PIXEL_FORMAT_BGRA_8888:
+ return 8;
+
+ case S3C_FB_PIXEL_FORMAT_RGBA_5551:
+ return 1;
+
+ case S3C_FB_PIXEL_FORMAT_RGBA_4444:
+ return 4;
+
+ case S3C_FB_PIXEL_FORMAT_RGB_888:
+ case S3C_FB_PIXEL_FORMAT_RGB_565:
+ case S3C_FB_PIXEL_FORMAT_RGBX_8888:
+ return 0;
+
+ default:
+ pr_warn("s3c-fb: unrecognized pixel format %u\n", format);
+ return 0;
+ }
+}
+
+static u32 s3c_fb_transp_offset(int format)
+{
+ switch (format) {
+ case S3C_FB_PIXEL_FORMAT_RGBA_8888:
+ case S3C_FB_PIXEL_FORMAT_BGRA_8888:
+ return 24;
+
+ case S3C_FB_PIXEL_FORMAT_RGBA_5551:
+ return 15;
+
+ case S3C_FB_PIXEL_FORMAT_RGBA_4444:
+ return 12;
+
+ case S3C_FB_PIXEL_FORMAT_RGB_888:
+ case S3C_FB_PIXEL_FORMAT_RGB_565:
+ case S3C_FB_PIXEL_FORMAT_RGBX_8888:
+ return s3c_fb_blue_offset(format);
+
+ default:
+ pr_warn("s3c-fb: unrecognized pixel format %u\n", format);
+ return 0;
+ }
+}
+
+static u32 s3c_fb_padding(int format)
+{
+ switch (format) {
+ case S3C_FB_PIXEL_FORMAT_RGBX_8888:
+ return 8;
+
+ case S3C_FB_PIXEL_FORMAT_RGB_565:
+ case S3C_FB_PIXEL_FORMAT_RGBA_8888:
+ case S3C_FB_PIXEL_FORMAT_RGBA_5551:
+ case S3C_FB_PIXEL_FORMAT_RGBA_4444:
+ return 0;
+
+ default:
+ pr_warn("s3c-fb: unrecognized pixel format %u\n", format);
+ return 0;
+ }
+
+}
+
+static inline u32 fb_visual(u32 bits_per_pixel, unsigned short palette_sz)
+{
+ switch (bits_per_pixel) {
+ case 32:
+ case 24:
+ case 16:
+ case 12:
+ return FB_VISUAL_TRUECOLOR;
+ case 8:
+ if (palette_sz >= 256)
+ return FB_VISUAL_PSEUDOCOLOR;
+ else
+ return FB_VISUAL_TRUECOLOR;
+ case 1:
+ return FB_VISUAL_MONO01;
+ default:
+ return FB_VISUAL_PSEUDOCOLOR;
+ }
+}
+
+
+static inline u32 fb_linelength(u32 xres_virtual, u32 bits_per_pixel)
+{
+ return (xres_virtual * bits_per_pixel) / 8;
+}
+
+static inline u16 fb_panstep(u32 res, u32 res_virtual)
+{
+ return res_virtual > res ? 1 : 0;
+}
+
+static inline u32 vidw_buf_size(u32 xres, u32 line_length, u32 bits_per_pixel)
+{
+ u32 pagewidth = (xres * bits_per_pixel) >> 3;
+ return VIDW_BUF_SIZE_OFFSET(line_length - pagewidth) |
+ VIDW_BUF_SIZE_PAGEWIDTH(pagewidth) |
+ VIDW_BUF_SIZE_OFFSET_E(line_length - pagewidth) |
+ VIDW_BUF_SIZE_PAGEWIDTH_E(pagewidth);
+}
+
+inline u32 vidosd_a(int x, int y)
+{
+ return VIDOSDxA_TOPLEFT_X(x) |
+ VIDOSDxA_TOPLEFT_Y(y) |
+ VIDOSDxA_TOPLEFT_X_E(x) |
+ VIDOSDxA_TOPLEFT_Y_E(y);
+}
+
+inline u32 vidosd_b(int x, int y, u32 xres, u32 yres, u32 bits_per_pixel)
+{
+ return VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(bits_per_pixel,
+ x + xres - 1)) |
+ VIDOSDxB_BOTRIGHT_Y(y + yres - 1) |
+ VIDOSDxB_BOTRIGHT_X_E(s3c_fb_align_word(bits_per_pixel,
+ x + xres - 1)) |
+ VIDOSDxB_BOTRIGHT_Y_E(y + yres - 1);
+}
+
+static inline u32 wincon(u32 bits_per_pixel, u32 transp_length, u32 red_length)
+{
+ u32 data = 0;
+
+ switch (bits_per_pixel) {
+ case 1:
+ data |= WINCON0_BPPMODE_1BPP;
+ data |= WINCONx_BITSWP;
+ data |= WINCONx_BURSTLEN_4WORD;
+ break;
+ case 2:
+ data |= WINCON0_BPPMODE_2BPP;
+ data |= WINCONx_BITSWP;
+ data |= WINCONx_BURSTLEN_8WORD;
+ break;
+ case 4:
+ data |= WINCON0_BPPMODE_4BPP;
+ data |= WINCONx_BITSWP;
+ data |= WINCONx_BURSTLEN_8WORD;
+ break;
+ case 8:
+ if (transp_length != 0)
+ data |= WINCON1_BPPMODE_8BPP_1232;
+ else
+ data |= WINCON0_BPPMODE_8BPP_PALETTE;
+ data |= WINCONx_BURSTLEN_8WORD;
+ data |= WINCONx_BYTSWP;
+ break;
+ case 16:
+ if (transp_length != 0)
+ data |= WINCON1_BPPMODE_16BPP_A1555;
+ else
+ data |= WINCON0_BPPMODE_16BPP_565;
+ data |= WINCONx_HAWSWP;
+ data |= WINCONx_BURSTLEN_16WORD;
+ break;
+ case 24:
+ case 32:
+ if (red_length == 6) {
+ if (transp_length != 0)
+ data |= WINCON1_BPPMODE_19BPP_A1666;
+ else
+ data |= WINCON1_BPPMODE_18BPP_666;
+ } else if (transp_length == 1)
+ data |= WINCON1_BPPMODE_25BPP_A1888
+ | WINCON1_BLD_PIX;
+ else if ((transp_length == 4) ||
+ (transp_length == 8))
+ data |= WINCON1_BPPMODE_28BPP_A4888
+ | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
+ else
+ data |= WINCON0_BPPMODE_24BPP_888;
+
+ data |= WINCONx_WSWP;
+ data |= WINCONx_BURSTLEN_16WORD;
+ break;
+ }
+
+ return data;
+}
+
+void s3c_fb_update_regs(struct s3cfb_global *fbdev, struct s3c_reg_data *regs)
{
- ktime_t prev_timestamp;
+ struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev);
+ unsigned short i;
+ bool wait_for_vsync;
+ struct s3cfb_window *win;
+
+#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412)
+#ifdef CONFIG_BUSFREQ_OPP
+ unsigned int new_num_of_win = 0;
+ unsigned int pre_num_of_win = 0;
+ unsigned int shadow_regs = 0;
+ unsigned int clkval = 0;
+
+ for (i = 0; i < pdata->nr_wins; i++)
+ if (regs->shadowcon & SHADOWCON_CHx_ENABLE(i))
+ new_num_of_win++;
+ shadow_regs = readl(fbdev->regs + S3C_WINSHMAP);
+ for (i = 0; i < pdata->nr_wins; i++)
+ if (shadow_regs & SHADOWCON_CHx_ENABLE(i))
+ pre_num_of_win++;
+
+ if (pre_num_of_win < new_num_of_win) {
+ switch(new_num_of_win) {
+ case 0:
+ case 1:
+ clkval = 100100;
+ break;
+ case 2:
+ clkval = 133133;
+ break;
+ case 3:
+ clkval = 133133;
+ break;
+ case 4:
+ clkval = 133133;
+ break;
+ case 5:
+ clkval = 133133;
+ break;
+ }
+ dev_lock(fbdev->bus_dev, fbdev->dev, clkval);
+ }
+#endif
+#endif
+
+ for (i = 0; i < pdata->nr_wins; i++)
+ s3cfb_set_window_protect(fbdev, i, 1);
+
+ for (i = 0; i < pdata->nr_wins; i++) {
+ win = fbdev->fb[i]->par;
+ writel(regs->wincon[i], fbdev->regs + S3C_WINCON(i));
+ writel(regs->vidosd_a[i], fbdev->regs + S3C_VIDOSD_A(i));
+ writel(regs->vidosd_b[i], fbdev->regs + S3C_VIDOSD_B(i));
+ writel(regs->vidosd_c[i], fbdev->regs + S3C_VIDOSD_C(i));
+ if (i == 1 || i == 2)
+ writel(regs->vidosd_d[i], fbdev->regs + S3C_VIDOSD_D(i));
+ writel(regs->vidw_buf_start[i],
+ fbdev->regs + S3C_VIDADDR_START0(i));
+ writel(regs->vidw_buf_end[i],
+ fbdev->regs + S3C_VIDADDR_END0(i));
+ writel(regs->vidw_buf_size[i],
+ fbdev->regs + S3C_VIDADDR_SIZE(i));
+
+ win->enabled = !!(regs->wincon[i] & WINCONx_ENWIN);
+ }
+
+ writel(regs->shadowcon, fbdev->regs + S3C_WINSHMAP);
+
+ for (i = 0; i < pdata->nr_wins; i++)
+ s3cfb_set_window_protect(fbdev, i, 0);
+
+ do {
+#if defined(CONFIG_FB_S5P_VSYNC_THREAD)
+ s3cfb_wait_for_vsync(fbdev, 0);
+#else
+ s3cfb_wait_for_vsync(fbdev);
+#endif
+ wait_for_vsync = false;
+
+ for (i = 0; i < pdata->nr_wins; i++) {
+ u32 new_start = regs->vidw_buf_start[i];
+ u32 shadow_start = s3cfb_get_win_cur_buf_addr(fbdev, i);
+ if (unlikely(new_start != shadow_start)) {
+ wait_for_vsync = true;
+ break;
+ }
+ }
+ } while (wait_for_vsync);
+
+ sw_sync_timeline_inc(fbdev->timeline, 1);
+
+#ifdef CONFIG_FB_S5P_SYSMMU
+ if ((fbdev->sysmmu.enabled == false) &&
+ (fbdev->sysmmu.pgd)) {
+ fbdev->sysmmu.enabled = true;
+ s5p_sysmmu_enable(fbdev->dev,
+ (unsigned long)virt_to_phys((unsigned int*)fbdev->sysmmu.pgd));
+ }
+#endif
+
+#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412)
+#ifdef CONFIG_BUSFREQ_OPP
+ if (pre_num_of_win > new_num_of_win) {
+ switch(new_num_of_win) {
+ case 0:
+ case 1:
+ clkval = 100100;
+ break;
+ case 2:
+ clkval = 133133;
+ break;
+ case 3:
+ clkval = 133133;
+ break;
+ case 4:
+ clkval = 133133;
+ break;
+ case 5:
+ clkval = 133133;
+ break;
+ }
+ dev_lock(fbdev->bus_dev, fbdev->dev, clkval);
+ }
+#endif
+#endif
+}
+
+static int s3c_fb_set_win_buffer(struct s3cfb_global *fbdev,
+ struct fb_info *fb, struct s3c_fb_win_config *win_config,
+ struct s3c_reg_data *regs)
+{
+ struct s3cfb_window *win = fb->par;
+ struct fb_fix_screeninfo prev_fix = fb->fix;
+ struct fb_var_screeninfo prev_var = fb->var;
+ unsigned short win_no = win->id;
int ret;
+ size_t window_size;
+ u32 alpha, size;
- dev_dbg(fbdev->dev, "waiting for VSYNC interrupt\n");
+ if (win_config->format >= S3C_FB_PIXEL_FORMAT_MAX) {
+ dev_err(fbdev->dev, "unknown pixel format %u\n",
+ win_config->format);
+ return -EINVAL;
+ }
- prev_timestamp = fbdev->vsync_timestamp;
+ fb->var.red.length = s3c_fb_red_length(win_config->format);
+ fb->var.red.offset = s3c_fb_red_offset(win_config->format);
+ fb->var.green.length = s3c_fb_green_length(win_config->format);
+ fb->var.green.offset = s3c_fb_green_offset(win_config->format);
+ fb->var.blue.length = s3c_fb_blue_length(win_config->format);
+ fb->var.blue.offset = s3c_fb_blue_offset(win_config->format);
+ fb->var.transp.length = s3c_fb_transp_length(win_config->format);
+ fb->var.transp.offset = s3c_fb_transp_offset(win_config->format);
+ fb->var.bits_per_pixel = fb->var.red.length +
+ fb->var.green.length +
+ fb->var.blue.length +
+ fb->var.transp.length +
+ s3c_fb_padding(win_config->format);
+
+ window_size = win_config->stride * win_config->h;
+
+ if (win_config->phys_addr != 0)
+ fb->fix.smem_start = win_config->phys_addr + win_config->offset;
+ else
+ fb->fix.smem_start = win_config->virt_addr + win_config->offset;
+ fb->fix.smem_len = window_size;
+ fb->var.xres = win_config->w;
+ fb->var.xres_virtual = win_config->stride * 8 /
+ fb->var.bits_per_pixel;
+ fb->var.yres = fb->var.yres_virtual = win_config->h;
+ fb->var.xoffset = win_config->offset % win_config->stride;
+ fb->var.yoffset = win_config->offset / win_config->stride;
+
+ fb->fix.visual = fb_visual(fb->var.bits_per_pixel, 256);
+ fb->fix.line_length = win_config->stride;
+ fb->fix.xpanstep = fb_panstep(win_config->w,
+ fb->var.xres_virtual);
+ fb->fix.ypanstep = fb_panstep(win_config->h, win_config->h);
+
+#ifdef CONFIG_FB_S5P_SYSMMU
+ if ((fbdev->sysmmu.enabled == false) &&
+ (current->mm))
+ fbdev->sysmmu.pgd = (unsigned int)current->mm->pgd;
+#endif
- ret = wait_event_interruptible_timeout(fbdev->wq,
- s3cfb_vsync_timestamp_changed(fbdev, prev_timestamp),
- msecs_to_jiffies(100));
+#ifdef CONFIG_FB_S5P_SYSMMU
+ if (win_config->phys_addr != 0)
+ regs->vidw_buf_start[win_no] = (u32)phys_to_virt(fb->fix.smem_start);
+ else
+#endif
+ regs->vidw_buf_start[win_no] = fb->fix.smem_start;
- if (ret == 0)
- return -ETIMEDOUT;
- if (ret < 0)
- return ret;
+ regs->vidw_buf_end[win_no] = regs->vidw_buf_start[win_no] +
+ window_size;
- dev_dbg(fbdev->dev, "got a VSYNC interrupt\n");
+ regs->vidw_buf_size[win_no] = vidw_buf_size(win_config->w,
+ fb->fix.line_length,
+ fb->var.bits_per_pixel);
+
+#ifdef CONFIG_FB_S5P_SYSMMU
+ if ((fb->fix.smem_start) &&
+ (fb->fix.smem_start != fbdev->sysmmu.default_fb_addr))
+ s3cfb_clean_outer_pagetable(regs->vidw_buf_start[win_no],
+ regs->vidw_buf_end[win_no] - regs->vidw_buf_start[win_no]);
+#endif
+
+
+ regs->vidosd_a[win_no] = vidosd_a(win_config->x, win_config->y);
+ regs->vidosd_b[win_no] = vidosd_b(win_config->x, win_config->y,
+ win_config->w, win_config->h,
+ fb->var.bits_per_pixel);
+
+ alpha = VIDISD14C_ALPHA1_R(0xf) |
+ VIDISD14C_ALPHA1_G(0xf) |
+ VIDISD14C_ALPHA1_B(0xf);
+ regs->vidosd_c[win_no] = alpha;
+
+ if (win_no <= 2) {
+ size = win_config->w * win_config->h;
+ regs->vidosd_d[win_no] = size;
+ }
+
+ regs->shadowcon |= SHADOWCON_CHx_ENABLE(win_no);
+
+ regs->wincon[win_no] = wincon(fb->var.bits_per_pixel,
+ fb->var.transp.length,
+ fb->var.red.length);
+
+ return 0;
+}
+
+static int s3c_fb_set_win_config(struct s3cfb_global *fbdev,
+ struct s3c_fb_win_config_data *win_data)
+{
+ struct fb_info *fb;
+ struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev);
+ struct s3c_fb_win_config *win_config = win_data->config;
+ int ret = 0;
+ unsigned short i;
+ struct s3c_reg_data *regs = kzalloc(sizeof(struct s3c_reg_data),
+ GFP_KERNEL);
+ struct sync_fence *fence;
+ struct sync_pt *pt;
+ int fd;
+
+ if (!regs) {
+ dev_err(fbdev->dev, "could not allocate s3c_reg_data");
+ return -ENOMEM;
+ }
+
+ fd = get_unused_fd();
+
+ for (i = 0; i < pdata->nr_wins && !ret; i++) {
+ struct s3c_fb_win_config *config = &win_config[i];
+ bool enabled = 0;
+ u32 color_map = WINxMAP_MAP | WINxMAP_MAP_COLOUR(0);
+
+ fb = fbdev->fb[i];
+
+ switch (config->state) {
+ case S3C_FB_WIN_STATE_DISABLED:
+ break;
+ case S3C_FB_WIN_STATE_COLOR:
+ enabled = 1;
+ color_map |= WINxMAP_MAP_COLOUR(config->color);
+ break;
+ case S3C_FB_WIN_STATE_BUFFER:
+ ret = s3c_fb_set_win_buffer(fbdev, fb, config, regs);
+ if (!ret) {
+ enabled = 1;
+ color_map = 0;
+ }
+ break;
+ default:
+ dev_warn(fbdev->dev, "unrecognized window state %u",
+ config->state);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (enabled)
+ regs->wincon[i] |= WINCONx_ENWIN;
+ else
+ regs->wincon[i] &= ~WINCONx_ENWIN;
+ regs->winmap[i] = color_map;
+ }
+
+ if (ret) {
+ put_unused_fd(fd);
+ kfree(regs);
+ } else {
+ mutex_lock(&fbdev->update_regs_list_lock);
+ fbdev->timeline_max++;
+ pt = sw_sync_pt_create(fbdev->timeline, fbdev->timeline_max);
+ fence = sync_fence_create("display", pt);
+ sync_fence_install(fence, fd);
+ win_data->fence = fd;
+
+ list_add_tail(&regs->list, &fbdev->update_regs_list);
+ mutex_unlock(&fbdev->update_regs_list_lock);
+ queue_kthread_work(&fbdev->update_regs_worker, &fbdev->update_regs_work);
+ }
return ret;
}
@@ -1128,6 +1754,7 @@ int s3cfb_ioctl(struct fb_info *fb, unsigned int cmd, unsigned long arg)
struct s3cfb_user_window user_window;
struct s3cfb_user_plane_alpha user_alpha;
struct s3cfb_user_chroma user_chroma;
+ struct s3c_fb_win_config_data win_data;
int vsync;
unsigned int alpha_mode;
} p;
@@ -1149,23 +1776,31 @@ int s3cfb_ioctl(struct fb_info *fb, unsigned int cmd, unsigned long arg)
if (fbdev->regs == 0)
return 0;
#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412)
- /* Enable Vsync */
#ifdef CONFIG_CPU_EXYNOS4412
if (!fbdev->regs)
return ret;
#endif
+#if !defined(CONFIG_FB_S5P_VSYNC_THREAD)
+ /* Enable Vsync */
s3cfb_set_global_interrupt(fbdev, 1);
s3cfb_set_vsync_interrupt(fbdev, 1);
#endif
+#endif
/* Wait for Vsync */
+#if defined(CONFIG_FB_S5P_VSYNC_THREAD)
+ s3cfb_wait_for_vsync(fbdev, HZ/10);
+#else
s3cfb_wait_for_vsync(fbdev);
+#endif
if (fbdev->regs == 0)
return 0;
#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412)
+#if !defined(CONFIG_FB_S5P_VSYNC_THREAD)
/* Disable Vsync */
s3cfb_set_global_interrupt(fbdev, 0);
s3cfb_set_vsync_interrupt(fbdev, 0);
#endif
+#endif
break;
case S3CFB_WIN_POSITION:
@@ -1238,8 +1873,12 @@ int s3cfb_ioctl(struct fb_info *fb, unsigned int cmd, unsigned long arg)
if (!fbdev->regs)
return ret;
#endif
+#if defined(CONFIG_FB_S5P_VSYNC_THREAD)
+ ret = s3cfb_set_vsync_int(fb, p.vsync);
+#else
s3cfb_set_global_interrupt(fbdev, p.vsync);
s3cfb_set_vsync_interrupt(fbdev, p.vsync);
+#endif
}
break;
@@ -1293,7 +1932,30 @@ int s3cfb_ioctl(struct fb_info *fb, unsigned int cmd, unsigned long arg)
else
s3cfb_set_alpha_mode(fbdev, win->id, p.alpha_mode);
break;
+
+#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412)
+ case S3CFB_WIN_CONFIG:
+ if (copy_from_user(&p.win_data,
+ (struct s3c_fb_win_config_data __user *)arg,
+ sizeof(p.win_data))) {
+ ret = -EFAULT;
+ break;
+ }
+
+ ret = s3c_fb_set_win_config(fbdev, &p.win_data);
+ if (ret)
+ break;
+
+ if (copy_to_user((struct s3c_fb_win_config_data __user *)arg,
+ &p.win_data,
+ sizeof(p.win_data))) {
+ ret = -EFAULT;
+ break;
+ }
+ break;
+#endif
}
+
return ret;
}