diff options
Diffstat (limited to 'drivers/video/samsung_duallcd/s3cfb_ops.c')
-rw-r--r-- | drivers/video/samsung_duallcd/s3cfb_ops.c | 1551 |
1 files changed, 1551 insertions, 0 deletions
diff --git a/drivers/video/samsung_duallcd/s3cfb_ops.c b/drivers/video/samsung_duallcd/s3cfb_ops.c new file mode 100644 index 0000000..3ff2f05 --- /dev/null +++ b/drivers/video/samsung_duallcd/s3cfb_ops.c @@ -0,0 +1,1551 @@ +/* linux/drivers/video/samsung/s3cfb-ops.c + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * Middle layer 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/poll.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +#if defined(CONFIG_CMA) +#include <linux/cma.h> +#elif defined(CONFIG_S5P_MEM_BOOTMEM) +#include <plat/media.h> +#include <mach/media.h> +#endif + +#ifdef CONFIG_HAS_WAKELOCK +#include <linux/wakelock.h> +#include <linux/earlysuspend.h> +#include <linux/suspend.h> +#endif + +#include <mach/sec_debug.h> +#include <linux/bootmem.h> +#include "s3cfb.h" +#define NOT_DEFAULT_WINDOW 99 +#define CMA_REGION_FIMD "fimd" +#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION +#define CMA_REGION_VIDEO "video" +#else +#define CMA_REGION_VIDEO "fimd" +#endif + +#ifdef CONFIG_BUSFREQ_OPP +#include <mach/dev.h> +#endif + +struct s3c_platform_fb *to_fb_plat(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + + return (struct s3c_platform_fb *)pdev->dev.platform_data; +} + +#ifndef CONFIG_FRAMEBUFFER_CONSOLE +#define LPDDR1_BASE_ADDR 0x50000000 +#define BOOT_FB_BASE_ADDR (LPDDR1_BASE_ADDR + 0x0EC00000) /* 0x5EC00000 from Bootloader */ + +static unsigned int bootloaderfb; +module_param_named(bootloaderfb, bootloaderfb, uint, 0444); +MODULE_PARM_DESC(bootloaderfb, "Address of booting logo image in Bootloader"); + +int s3cfb_draw_logo(struct fb_info *fb) +{ +#ifdef CONFIG_FB_S5P_SPLASH_SCREEN +#ifdef RGB_BOOTSCREEN + struct fb_fix_screeninfo *fix = &fb->fix; + struct fb_var_screeninfo *var = &fb->var; +#endif +#if 0 + struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev); + memcpy(fbdev->fb[pdata->default_win]->screen_base, + LOGO_RGB24, fix->line_length * var->yres); +#else +#ifdef RGB_BOOTSCREEN + u32 height = var->yres / 3; + u32 line = fix->line_length; + u32 i, j; + + for (i = 0; i < height; i++) { + for (j = 0; j < var->xres; j++) { + memset(fb->screen_base + i * line + j * 4 + 0, 0x00, 1); + memset(fb->screen_base + i * line + j * 4 + 1, 0x00, 1); + memset(fb->screen_base + i * line + j * 4 + 2, 0xff, 1); + memset(fb->screen_base + i * line + j * 4 + 3, 0x00, 1); + } + } + + for (i = height; i < height * 2; i++) { + for (j = 0; j < var->xres; j++) { + memset(fb->screen_base + i * line + j * 4 + 0, 0x00, 1); + memset(fb->screen_base + i * line + j * 4 + 1, 0xff, 1); + memset(fb->screen_base + i * line + j * 4 + 2, 0x00, 1); + memset(fb->screen_base + i * line + j * 4 + 3, 0x00, 1); + } + } + + for (i = height * 2; i < height * 3; i++) { + for (j = 0; j < var->xres; j++) { + memset(fb->screen_base + i * line + j * 4 + 0, 0xff, 1); + memset(fb->screen_base + i * line + j * 4 + 1, 0x00, 1); + memset(fb->screen_base + i * line + j * 4 + 2, 0x00, 1); + memset(fb->screen_base + i * line + j * 4 + 3, 0x00, 1); + } + } +#else /* #ifdef RGB_BOOTSCREEN */ + u8 *logo_virt_buf; + + if (bootloaderfb) { + logo_virt_buf = phys_to_virt(bootloaderfb); + memcpy(fb->screen_base, logo_virt_buf, fb->var.yres * fb->fix.line_length); + printk(KERN_INFO "Bootloader sent 'bootloaderfb' : %08X\n", bootloaderfb); + } + +#endif /* #ifdef RGB_BOOTSCREEN */ +#endif +#endif + + return 0; +} +#else +int fb_is_primary_device(struct fb_info *fb) +{ + struct s3cfb_window *win = fb->par; + struct s3cfb_global *fbdev = get_fimd_global(win->id); + struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev); + + dev_dbg(fbdev->dev, "[fb%d] checking for primary device\n", win->id); + + if (win->id == pdata->default_win) + return 1; + else + return 0; +} +#endif + +#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412)\ + || defined(CONFIG_CPU_EXYNOS4210) +int window_on_off_status(struct s3cfb_global *fbdev) +{ + struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev); + struct s3cfb_window *win; + int i; + int ret = 0; + + for (i = 0; i < pdata->nr_wins; i++) { + win = fbdev->fb[i]->par; + if (win->enabled) + ret |= (1 << i); + } + + return ret; +} +#endif + +#ifdef FEATURE_BUSFREQ_LOCK +void s3cfb_busfreq_lock(struct s3cfb_global *fbdev, unsigned int lock) +{ + if (lock) { + if (atomic_read(&fbdev->busfreq_lock_cnt) == 0) { + exynos4_busfreq_lock(DVFS_LOCK_ID_LCD, BUS_L1); + dev_info(fbdev->dev, "[%s] Bus Freq Locked L1\n", __func__); + } + atomic_inc(&fbdev->busfreq_lock_cnt); + fbdev->busfreq_flag = true; + } else { + if (fbdev->busfreq_flag == true) { + atomic_dec(&fbdev->busfreq_lock_cnt); + fbdev->busfreq_flag = false; + if (atomic_read(&fbdev->busfreq_lock_cnt) == 0) { + /* release Freq lock back to normal */ + exynos4_busfreq_lock_free(DVFS_LOCK_ID_LCD); + dev_info(fbdev->dev, "[%s] Bus Freq lock Released Normal !!\n", __func__); + } + } + } +} +#endif + +int s3cfb_enable_window(struct s3cfb_global *fbdev, int id) +{ + struct s3cfb_window *win = fbdev->fb[id]->par; +#ifdef FEATURE_BUSFREQ_LOCK + int enabled_win = 0; +#endif +#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412) +#ifdef CONFIG_BUSFREQ_OPP + if (id != CONFIG_FB_S5P_DEFAULT_WINDOW) + dev_lock(fbdev->bus_dev, fbdev->dev, 133133); +#endif +#endif + + if (!win->enabled) + atomic_inc(&fbdev->enabled_win); + +#ifdef FEATURE_BUSFREQ_LOCK + enabled_win = atomic_read(&fbdev->enabled_win); + if (enabled_win >= 2) + s3cfb_busfreq_lock(fbdev, 1); +#endif + + if (s3cfb_window_on(fbdev, id)) { + win->enabled = 0; + return -EFAULT; + } else { + win->enabled = 1; + return 0; + } +} + +int s3cfb_disable_window(struct s3cfb_global *fbdev, int id) +{ + struct s3cfb_window *win = fbdev->fb[id]->par; +#ifdef FEATURE_BUSFREQ_LOCK + int enabled_win = 0; +#endif +#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412) +#ifdef CONFIG_BUSFREQ_OPP + int win_status; +#endif +#endif + + if (win->enabled) + atomic_dec(&fbdev->enabled_win); + + if (fbdev->regs != 0 && s3cfb_window_off(fbdev, id)) { + win->enabled = 1; + return -EFAULT; + } else { +#ifdef FEATURE_BUSFREQ_LOCK + enabled_win = atomic_read(&fbdev->enabled_win); + if (enabled_win < 2) + s3cfb_busfreq_lock(fbdev, 0); +#endif + win->enabled = 0; +#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412) +#ifdef CONFIG_BUSFREQ_OPP + win_status = window_on_off_status(fbdev); + if ((win_status & ~(1 << CONFIG_FB_S5P_DEFAULT_WINDOW)) == 0) + dev_unlock(fbdev->bus_dev, fbdev->dev); +#endif +#endif + return 0; + } +} + +int s3cfb_update_power_state(struct s3cfb_global *fbdev, int id, int state) +{ + struct s3cfb_window *win = fbdev->fb[id]->par; + win->power_state = state; + + return 0; +} + +int s3cfb_init_global(struct s3cfb_global *fbdev) +{ + fbdev->output = OUTPUT_RGB; + fbdev->rgb_mode = MODE_RGB_P; + + fbdev->wq_count = 0; + init_waitqueue_head(&fbdev->wq); + mutex_init(&fbdev->lock); + + s3cfb_set_output(fbdev); + s3cfb_set_display_mode(fbdev); + s3cfb_set_polarity(fbdev); + s3cfb_set_timing(fbdev); + s3cfb_set_lcd_size(fbdev); + + return 0; +} + +int s3cfb_unmap_video_memory(struct s3cfb_global *fbdev, struct fb_info *fb) +{ + struct fb_fix_screeninfo *fix = &fb->fix; + struct s3cfb_window *win = fb->par; + +#ifdef CONFIG_CMA + struct cma_info mem_info; + int err; +#endif + +#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412) + return 0; +#endif + + if (fix->smem_start) { +#ifdef CONFIG_CMA + err = cma_info(&mem_info, fbdev->dev, 0); + if (ERR_PTR(err)) + return -ENOMEM; + + if (fix->smem_start >= mem_info.lower_bound && + fix->smem_start <= mem_info.upper_bound) + cma_free(fix->smem_start); +#else + dma_free_coherent(fbdev->dev, fix->smem_len, fb->screen_base, fix->smem_start); +#endif + fix->smem_start = 0; + fix->smem_len = 0; + dev_info(fbdev->dev, "[fb%d] video memory released\n", win->id); + } + return 0; +} + +int s3cfb_map_video_memory(struct s3cfb_global *fbdev, struct fb_info *fb) +{ + struct fb_fix_screeninfo *fix = &fb->fix; + struct s3cfb_window *win = fb->par; +#ifdef CONFIG_CMA + struct cma_info mem_info; + int err; +#endif + +#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412) + return 0; +#endif + + if (win->owner == DMA_MEM_OTHER) + return 0; + +#ifdef CONFIG_CMA + err = cma_info(&mem_info, fbdev->dev, CMA_REGION_VIDEO); + if (err) + return err; + fix->smem_start = (dma_addr_t)cma_alloc +#ifdef CONFIG_EXYNOS_CONTENT_PATH_PROTECTION + (fbdev->dev, "fimd_video", (size_t)PAGE_ALIGN(fix->smem_len), 0); +#else + (fbdev->dev, "fimd", (size_t)PAGE_ALIGN(fix->smem_len), 0); +#endif + if (IS_ERR_OR_NULL((char *)fix->smem_start)) { + printk(KERN_ERR "fix->smem_start allocation fail (%x)\n", + (int)fix->smem_start); + return -1; + } + + fb->screen_base = NULL; +#else + fb->screen_base = dma_alloc_writecombine(fbdev->dev, + PAGE_ALIGN(fix->smem_len), + (unsigned int *) + &fix->smem_start, GFP_KERNEL); +#endif + + dev_info(fbdev->dev, "[fb%d] Alloc dma: 0x%08x, " + "size: 0x%08x\n", win->id, + (unsigned int)fix->smem_start, + fix->smem_len); + + win->owner = DMA_MEM_FIMD; + + return 0; +} + +int s3cfb_map_default_video_memory(struct s3cfb_global *fbdev, + struct fb_info *fb, int fimd_id) +{ + struct fb_fix_screeninfo *fix = &fb->fix; + struct s3cfb_window *win = fb->par; +#ifdef CONFIG_CMA + struct cma_info mem_info; + int err; +#endif + + if (win->owner == DMA_MEM_OTHER) + return 0; + +#ifdef CONFIG_CMA + err = cma_info(&mem_info, fbdev->dev, CMA_REGION_FIMD); + if (err) + return err; + fix->smem_start = (dma_addr_t)cma_alloc + (fbdev->dev, "fimd", (size_t)PAGE_ALIGN(fix->smem_len), 0); + fb->screen_base = cma_get_virt(fix->smem_start, PAGE_ALIGN(fix->smem_len), 1); +#elif defined(CONFIG_S5P_MEM_BOOTMEM) + fix->smem_start = s5p_get_media_memory_bank(S5P_MDEV_FIMD, 1); + fix->smem_len = s5p_get_media_memsize_bank(S5P_MDEV_FIMD, 1); + fb->screen_base = ioremap_wc(fix->smem_start, fix->smem_len); +#else + fb->screen_base = dma_alloc_writecombine(fbdev->dev, + PAGE_ALIGN(fix->smem_len), + (unsigned int *) + &fix->smem_start, GFP_KERNEL); +#endif + + if (!fb->screen_base) + return -ENOMEM; + else + dev_info(fbdev->dev, "[fb%d] dma: 0x%08x, cpu: 0x%08x, " + "size: 0x%08x\n", win->id, + (unsigned int)fix->smem_start, + (unsigned int)fb->screen_base, fix->smem_len); + + if (bootloaderfb) + memset(fb->screen_base, 0, fix->smem_len); + win->owner = DMA_MEM_FIMD; + + return 0; +} + +int s3cfb_unmap_default_video_memory(struct s3cfb_global *fbdev, + struct fb_info *fb) +{ + struct fb_fix_screeninfo *fix = &fb->fix; + struct s3cfb_window *win = fb->par; + + if (fix->smem_start) { + iounmap(fb->screen_base); + + fix->smem_start = 0; + fix->smem_len = 0; + dev_info(fbdev->dev, "[fb%d] video memory released\n", win->id); + } + + return 0; +} + +int s3cfb_set_bitfield(struct fb_var_screeninfo *var) +{ + switch (var->bits_per_pixel) { + case 16: + if (var->transp.length == 1) { + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 15; + } else if (var->transp.length == 4) { + var->red.offset = 8; + var->red.length = 4; + var->green.offset = 4; + var->green.length = 4; + var->blue.offset = 0; + var->blue.length = 4; + var->transp.offset = 12; + } else { + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 5; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + var->transp.offset = 0; + } + break; + + case 24: + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 0; + var->transp.length = 0; + break; + + case 32: + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + var->transp.offset = 24; + var->transp.length = 8; /* added for LCD RGB32 */ + break; + } + + return 0; +} + +int s3cfb_set_alpha_info(struct fb_var_screeninfo *var, + struct s3cfb_window *win) +{ + if (var->transp.length > 0) + win->alpha.mode = PIXEL_BLENDING; + else { + win->alpha.mode = PLANE_BLENDING; + win->alpha.channel = 0; + win->alpha.value = S3CFB_AVALUE(0xf, 0xf, 0xf); + } + + return 0; +} + +int s3cfb_check_var_window(struct s3cfb_global *fbdev, + struct fb_var_screeninfo *var, struct fb_info *fb) +{ + struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev); + struct s3cfb_window *win = fb->par; + struct s3cfb_lcd *lcd = fbdev->lcd; + + dev_dbg(fbdev->dev, "[fb%d] check_var\n", win->id); + + if (var->bits_per_pixel != 16 && var->bits_per_pixel != 24 && + var->bits_per_pixel != 32) { + dev_err(fbdev->dev, "invalid bits per pixel\n"); + return -EINVAL; + } + + if (var->xres > lcd->width) + var->xres = lcd->width; + + if (var->yres > lcd->height) + var->yres = lcd->height; + + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + +#if defined(CONFIG_FB_S5P_VIRTUAL) + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres * CONFIG_FB_S5P_NR_BUFFERS; +#endif + + if (var->xoffset > (var->xres_virtual - var->xres)) + var->xoffset = var->xres_virtual - var->xres; + + if (var->yoffset + var->yres > var->yres_virtual) + var->yoffset = var->yres_virtual - var->yres; + + if (win->x + var->xres > lcd->width) + win->x = lcd->width - var->xres; + + if (win->y + var->yres > lcd->height) + win->y = lcd->height - var->yres; + + if (var->pixclock != fbdev->fb[pdata->default_win]->var.pixclock) { + dev_info(fbdev->dev, "pixclk is changed from %d Hz to %d Hz\n", + fbdev->fb[pdata->default_win]->var.pixclock, var->pixclock); + } + + s3cfb_set_bitfield(var); + s3cfb_set_alpha_info(var, win); + + return 0; +} + +int s3cfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb) +{ + struct s3cfb_window *win = fb->par; + struct s3cfb_global *fbdev = get_fimd_global(win->id); + + s3cfb_check_var_window(fbdev, var, fb); + + return 0; +} + +void s3cfb_set_win_params(struct s3cfb_global *fbdev, int id) +{ + s3cfb_set_window_control(fbdev, id); +#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412) + s3cfb_set_oneshot(fbdev, id); +#else + s3cfb_set_window_position(fbdev, id); + s3cfb_set_window_size(fbdev, id); + s3cfb_set_buffer_address(fbdev, id); + s3cfb_set_buffer_size(fbdev, id); +#endif + + if (id > 0) { + s3cfb_set_alpha_blending(fbdev, id); + s3cfb_set_chroma_key(fbdev, id); + s3cfb_set_alpha_value_width(fbdev, id); + /* Set to premultiplied mode as default */ + s3cfb_set_alpha_mode(fbdev, id, BLENDING_PREMULT); + } +} + +int s3cfb_set_par_window(struct s3cfb_global *fbdev, struct fb_info *fb) +{ + struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev); + struct s3cfb_window *win = fb->par; + int ret; + + dev_dbg(fbdev->dev, "[fb%d] set_par\n", win->id); + +#if (!defined(CONFIG_CPU_EXYNOS4210)) + if ((win->id != pdata->default_win) && fb->fix.smem_start) + s3cfb_unmap_video_memory(fbdev, fb); +#endif + + /* modify the fix info */ + if (win->id != pdata->default_win) { + fb->fix.line_length = fb->var.xres_virtual * + fb->var.bits_per_pixel / 8; + fb->fix.smem_len = fb->fix.line_length * fb->var.yres_virtual; + } + + if (win->id != pdata->default_win && !fb->fix.smem_start) { + ret = s3cfb_map_video_memory(fbdev, fb); + if (ret != 0) + return ret; + } + + s3cfb_set_win_params(fbdev, win->id); + + return 0; +} + +int s3cfb_set_par(struct fb_info *fb) +{ + struct s3cfb_window *win = fb->par; + struct s3cfb_global *fbdev = get_fimd_global(win->id); + int ret = 0; + +#ifdef CONFIG_EXYNOS_DEV_PD + if (unlikely(fbdev->system_state == POWER_OFF)) { + dev_err(fbdev->dev, "%s::system_state is POWER_OFF, fb%d\n", __func__, win->id); + return 0; + } +#endif + + ret = s3cfb_set_par_window(fbdev, fb); + + return ret; +} + +int s3cfb_init_fbinfo(struct s3cfb_global *fbdev, int id) +{ + struct fb_info *fb = fbdev->fb[id]; + struct fb_fix_screeninfo *fix = &fb->fix; + struct fb_var_screeninfo *var = &fb->var; + struct s3cfb_window *win = fb->par; + struct s3cfb_alpha *alpha = &win->alpha; + struct s3cfb_lcd *lcd = fbdev->lcd; + struct s3cfb_lcd_timing *timing = &lcd->timing; + + memset(win, 0, sizeof(struct s3cfb_window)); + platform_set_drvdata(to_platform_device(fbdev->dev), fb); + strcpy(fix->id, S3CFB_NAME); + + /* fimd specific */ + win->id = id; + win->path = DATA_PATH_DMA; + win->dma_burst = 16; + s3cfb_update_power_state(fbdev, win->id, FB_BLANK_POWERDOWN); + alpha->mode = PLANE_BLENDING; + + /* fbinfo */ + fb->fbops = &s3cfb_ops; + fb->flags = FBINFO_FLAG_DEFAULT; + fb->pseudo_palette = &win->pseudo_pal; +#if (CONFIG_FB_S5P_NR_BUFFERS != 1) +#if defined(CONFIG_CPU_EXYNOS4210) + fix->xpanstep = 1; /* xpanstep can be 1 if bits_per_pixel is 32 */ +#else + fix->xpanstep = 2; +#endif + fix->ypanstep = 1; +#else + fix->xpanstep = 0; + fix->ypanstep = 0; +#endif + fix->type = FB_TYPE_PACKED_PIXELS; + fix->accel = FB_ACCEL_NONE; + fix->visual = FB_VISUAL_TRUECOLOR; + var->xres = lcd->width; + var->yres = lcd->height; + +#if defined(CONFIG_FB_S5P_VIRTUAL) + var->xres_virtual = CONFIG_FB_S5P_X_VRES; + var->yres_virtual = CONFIG_FB_S5P_Y_VRES * CONFIG_FB_S5P_NR_BUFFERS; +#else + var->xres_virtual = var->xres; + var->yres_virtual = var->yres * CONFIG_FB_S5P_NR_BUFFERS; +#endif + + var->bits_per_pixel = 32; + var->xoffset = 0; + var->yoffset = 0; + var->width = lcd->p_width; + var->height = lcd->p_height; + var->transp.length = 8; + + fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; + fix->smem_len = fix->line_length * var->yres_virtual; + + var->nonstd = 0; + var->activate = FB_ACTIVATE_NOW; + var->vmode = FB_VMODE_NONINTERLACED; + var->hsync_len = timing->h_sw; + var->vsync_len = timing->v_sw; + var->left_margin = timing->h_bp; + var->right_margin = timing->h_fp; + var->upper_margin = timing->v_bp; + var->lower_margin = timing->v_fp; + var->pixclock = (lcd->freq * + (var->left_margin + var->right_margin + + var->hsync_len + var->xres) * + (var->upper_margin + var->lower_margin + + var->vsync_len + var->yres)); + var->pixclock = KHZ2PICOS(var->pixclock/1000); + + s3cfb_set_bitfield(var); + s3cfb_set_alpha_info(var, win); + + return 0; +} + +int s3cfb_alloc_framebuffer(struct s3cfb_global *fbdev, int fimd_id) +{ + struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev); + int ret = 0; + int i; + + fbdev->fb = kmalloc(pdata->nr_wins * + sizeof(struct fb_info *), GFP_KERNEL); + if (!fbdev->fb) { + dev_err(fbdev->dev, "not enough memory\n"); + ret = -ENOMEM; + goto err_alloc; + } + + for (i = 0; i < pdata->nr_wins; i++) { + fbdev->fb[i] = framebuffer_alloc(sizeof(struct s3cfb_window), + fbdev->dev); + if (!fbdev->fb[i]) { + dev_err(fbdev->dev, "not enough memory\n"); + ret = -ENOMEM; + goto err_alloc_fb; + } + + ret = s3cfb_init_fbinfo(fbdev, i); + if (ret) { + dev_err(fbdev->dev, + "failed to allocate memory for fb%d\n", i); + ret = -ENOMEM; + goto err_alloc_fb; + } + + if (i == pdata->default_win) + if (s3cfb_map_default_video_memory(fbdev, + fbdev->fb[i], fimd_id)) { + dev_err(fbdev->dev, + "failed to map video memory " + "for default window (%d)\n", i); + ret = -ENOMEM; + goto err_alloc_fb; + } + sec_getlog_supply_fbinfo((void *)fbdev->fb[i]->fix.smem_start, + fbdev->fb[i]->var.xres, + fbdev->fb[i]->var.yres, + fbdev->fb[i]->var.bits_per_pixel, 2); + } + + return 0; + +err_alloc_fb: + for (i = 0; i < pdata->nr_wins; i++) { + if (fbdev->fb[i]) + framebuffer_release(fbdev->fb[i]); + } + kfree(fbdev->fb); + +err_alloc: + return ret; +} + +int s3cfb_open(struct fb_info *fb, int user) +{ + struct s3cfb_window *win = fb->par; + struct s3cfb_global *fbdev = get_fimd_global(win->id); + struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev); + int ret = 0; + + mutex_lock(&fbdev->lock); + + if (atomic_read(&win->in_use)) { + if (win->id == pdata->default_win) { + dev_dbg(fbdev->dev, + "mutiple open fir default window\n"); + ret = 0; + } else { + dev_dbg(fbdev->dev, + "do not allow multiple open \ + for non-default window\n"); + ret = -EBUSY; + } + } else { + atomic_inc(&win->in_use); + } + + mutex_unlock(&fbdev->lock); + + return ret; +} + +int s3cfb_release_window(struct fb_info *fb) +{ + struct s3cfb_window *win = fb->par; + struct s3cfb_global *fbdev = get_fimd_global(win->id); + struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev); + +#ifdef CONFIG_EXYNOS_DEV_PD + if (unlikely(fbdev->system_state == POWER_OFF)) { + dev_err(fbdev->dev, "%s::system_state is POWER_OFF, fb%d\n", __func__, win->id); + return 0; + } +#endif + if (win->id != pdata->default_win) { + s3cfb_disable_window(fbdev, win->id); + s3cfb_unmap_video_memory(fbdev, fb); +#if !defined(CONFIG_CPU_EXYNOS4212) && !defined(CONFIG_CPU_EXYNOS4412) + s3cfb_set_buffer_address(fbdev, win->id); +#endif + } + + win->x = 0; + win->y = 0; + + return 0; +} + +int s3cfb_release(struct fb_info *fb, int user) +{ + struct s3cfb_window *win = fb->par; + struct s3cfb_global *fbdev = get_fimd_global(win->id); + + s3cfb_release_window(fb); + + mutex_lock(&fbdev->lock); + atomic_dec(&win->in_use); + mutex_unlock(&fbdev->lock); + + return 0; +} + +inline unsigned int __chan_to_field(unsigned int chan, struct fb_bitfield bf) +{ + chan &= 0xffff; + chan >>= 16 - bf.length; + + return chan << bf.offset; +} + +int s3cfb_setcolreg(unsigned int regno, unsigned int red, + unsigned int green, unsigned int blue, + unsigned int transp, struct fb_info *fb) +{ + unsigned int *pal = (unsigned int *)fb->pseudo_palette; + unsigned int val = 0; + + if (regno < 16) { + /* fake palette of 16 colors */ + val |= __chan_to_field(red, fb->var.red); + val |= __chan_to_field(green, fb->var.green); + val |= __chan_to_field(blue, fb->var.blue); + val |= __chan_to_field(transp, fb->var.transp); + pal[regno] = val; + } + + return 0; +} + +int s3cfb_blank(int blank_mode, struct fb_info *fb) +{ + struct s3cfb_window *win = fb->par; + struct s3cfb_window *tmp_win; + struct s3cfb_global *fbdev = get_fimd_global(win->id); + struct platform_device *pdev = to_platform_device(fbdev->dev); + struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev); + int enabled_win = 0; + int i; +#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412) || defined(CONFIG_CPU_EXYNOS4210) + int win_status; +#endif + + dev_info(fbdev->dev, "change blank mode=%d, fb%d\n", blank_mode, win->id); + +#ifdef CONFIG_EXYNOS_DEV_PD + if (unlikely(fbdev->system_state == POWER_OFF)) { + dev_err(fbdev->dev, "%s::system_state is POWER_OFF, fb%d\n", __func__, win->id); + win->power_state = blank_mode; + if (win->id != pdata->default_win) + return NOT_DEFAULT_WINDOW; + else + return 0; + } +#endif + + switch (blank_mode) { + case FB_BLANK_UNBLANK: + if (!fb->fix.smem_start) { + dev_info(fbdev->dev, "[fb%d] no allocated memory \ + for unblank\n", win->id); + break; + } + + if (win->power_state == FB_BLANK_UNBLANK) { + dev_info(fbdev->dev, "[fb%d] already in \ + FB_BLANK_UNBLANK\n", win->id); + break; + } else { + s3cfb_update_power_state(fbdev, win->id, + FB_BLANK_UNBLANK); + } + + enabled_win = atomic_read(&fbdev->enabled_win); + if (enabled_win == 0) { + /* temporarily nonuse for recovery, will modify code */ + /* pdata->clk_on(pdev, &fbdev->clock); */ + s3cfb_init_global(fbdev); + s3cfb_set_clock(fbdev); + for (i = 0; i < pdata->nr_wins; i++) { + tmp_win = fbdev->fb[i]->par; + if (tmp_win->owner == DMA_MEM_FIMD) + s3cfb_set_win_params(fbdev, + tmp_win->id); + } + } + + if (win->enabled) /* from FB_BLANK_NORMAL */ + s3cfb_win_map_off(fbdev, win->id); + else /* from FB_BLANK_POWERDOWN */ + s3cfb_enable_window(fbdev, win->id); + + if (enabled_win == 0) { + s3cfb_display_on(fbdev); + + if (pdata->backlight_on) + pdata->backlight_on(pdev); + if (pdata->lcd_on) + pdata->lcd_on(pdev); + if (fbdev->lcd->init_ldi) + fbdev->lcd->init_ldi(); + } +#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412)\ + || defined(CONFIG_CPU_EXYNOS4210) + win_status = window_on_off_status(fbdev); + if (win_status == 0) +#else + if (win->id != pdata->default_win) +#endif + return NOT_DEFAULT_WINDOW; + + break; + + case FB_BLANK_NORMAL: + if (win->power_state == FB_BLANK_NORMAL) { + dev_info(fbdev->dev, "[fb%d] already in FB_BLANK_NORMAL\n", win->id); + break; + } else { + s3cfb_update_power_state(fbdev, win->id, + FB_BLANK_NORMAL); + } + + enabled_win = atomic_read(&fbdev->enabled_win); + if (enabled_win == 0) { + pdata->clk_on(pdev, &fbdev->clock); + s3cfb_init_global(fbdev); + s3cfb_set_clock(fbdev); + + for (i = 0; i < pdata->nr_wins; i++) { + tmp_win = fbdev->fb[i]->par; + if (tmp_win->owner == DMA_MEM_FIMD) + s3cfb_set_win_params(fbdev, + tmp_win->id); + } + } + + s3cfb_win_map_on(fbdev, win->id, 0x0); + + if (!win->enabled) /* from FB_BLANK_POWERDOWN */ + s3cfb_enable_window(fbdev, win->id); + + if (enabled_win == 0) { + s3cfb_display_on(fbdev); + + if (pdata->backlight_on) + pdata->backlight_on(pdev); + if (pdata->lcd_on) + pdata->lcd_on(pdev); + if (fbdev->lcd->init_ldi) + fbdev->lcd->init_ldi(); + } +#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412)\ + || defined(CONFIG_CPU_EXYNOS4210) + win_status = window_on_off_status(fbdev); + if (win_status == 0) +#else + if (win->id != pdata->default_win) +#endif + return NOT_DEFAULT_WINDOW; + + break; + + case FB_BLANK_POWERDOWN: + if (win->power_state == FB_BLANK_POWERDOWN) { + dev_info(fbdev->dev, "[fb%d] already in FB_BLANK_POWERDOWN\n", win->id); + break; + } else { + s3cfb_update_power_state(fbdev, win->id, + FB_BLANK_POWERDOWN); + } + + s3cfb_disable_window(fbdev, win->id); + s3cfb_win_map_off(fbdev, win->id); + + if (atomic_read(&fbdev->enabled_win) == 0) { + if (pdata->backlight_off) + pdata->backlight_off(pdev); + if (fbdev->lcd->deinit_ldi) + fbdev->lcd->deinit_ldi(); + if (pdata->lcd_off) + pdata->lcd_off(pdev); + /* temporaily nonuse for recovery , will modify code */ + /* s3cfb_display_off(fbdev); + pdata->clk_off(pdev, &fbdev->clock); */ + } +#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412)\ + || defined(CONFIG_CPU_EXYNOS4210) + win_status = window_on_off_status(fbdev); + if (win_status != 0) +#else + if (win->id != pdata->default_win) +#endif + return NOT_DEFAULT_WINDOW; + break; + + case FB_BLANK_VSYNC_SUSPEND: /* fall through */ + case FB_BLANK_HSYNC_SUSPEND: /* fall through */ + default: + dev_dbg(fbdev->dev, "unsupported blank mode\n"); + return -EINVAL; + } + + return 0; +} + +int s3cfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *fb) +{ + struct s3cfb_window *win = fb->par; + struct s3cfb_global *fbdev = get_fimd_global(win->id); + struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev); + + if (win->id == pdata->default_win) + spin_lock(&fbdev->slock); + +#ifdef CONFIG_EXYNOS_DEV_PD + if (unlikely(fbdev->system_state == POWER_OFF) || fbdev->regs == 0) { + dev_err(fbdev->dev, "%s::system_state is POWER_OFF, fb%d\n", __func__, win->id); + if (win->id == pdata->default_win) + spin_unlock(&fbdev->slock); + return -EINVAL; + } +#endif + + if (var->yoffset + var->yres > var->yres_virtual) { + dev_err(fbdev->dev, "invalid yoffset value\n"); + if (win->id == pdata->default_win) + spin_unlock(&fbdev->slock); + return -EINVAL; + } + +#if defined(CONFIG_CPU_EXYNOS4210) + if (unlikely(var->xoffset + var->xres > var->xres_virtual)) { + dev_err(fbdev->dev, "invalid xoffset value\n"); + if (win->id == pdata->default_win) + spin_unlock(&fbdev->slock); + return -EINVAL; + } + fb->var.xoffset = var->xoffset; +#endif + + fb->var.yoffset = var->yoffset; + + dev_dbg(fbdev->dev, "[fb%d] yoffset for pan display: %d\n", win->id, + var->yoffset); + + s3cfb_set_buffer_address(fbdev, win->id); + + if (win->id == pdata->default_win) + spin_unlock(&fbdev->slock); + return 0; +} + +int s3cfb_cursor(struct fb_info *fb, struct fb_cursor *cursor) +{ + /* nothing to do for removing cursor */ + return 0; +} + +int s3cfb_wait_for_vsync(struct s3cfb_global *fbdev) +{ + 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; +} + +int s3cfb_ioctl(struct fb_info *fb, unsigned int cmd, unsigned long arg) +{ + struct fb_var_screeninfo *var = &fb->var; + struct s3cfb_window *win = fb->par; + struct s3cfb_global *fbdev = get_fimd_global(win->id); + struct s3cfb_lcd *lcd = fbdev->lcd; + void *argp = (void *)arg; + int ret = 0; +#if defined(CONFIG_CPU_EXYNOS4210) + unsigned int addr = 0; +#endif + dma_addr_t start_addr = 0; + struct fb_fix_screeninfo *fix = &fb->fix; + + union { + struct s3cfb_user_window user_window; + struct s3cfb_user_plane_alpha user_alpha; + struct s3cfb_user_chroma user_chroma; + int vsync; + unsigned int alpha_mode; + } p; + +#ifdef CONFIG_EXYNOS_DEV_PD + if (unlikely(fbdev->system_state == POWER_OFF)) { + dev_err(fbdev->dev, "%s::system_state is POWER_OFF cmd is 0x%08x, fb%d\n", __func__, cmd, win->id); + return -EFAULT; + } +#endif + + switch (cmd) { + case S3CFB_SET_WIN_ADDR: + fix->smem_start = (unsigned long)argp; + s3cfb_set_buffer_address(fbdev, win->id); + break; + + case FBIO_WAITFORVSYNC: + 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 + s3cfb_set_global_interrupt(fbdev, 1); + s3cfb_set_vsync_interrupt(fbdev, 1); +#endif + /* Wait for Vsync */ + s3cfb_wait_for_vsync(fbdev); + if (fbdev->regs == 0) + return 0; +#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412) + /* Disable Vsync */ + s3cfb_set_global_interrupt(fbdev, 0); + s3cfb_set_vsync_interrupt(fbdev, 0); +#endif + break; + + case S3CFB_WIN_POSITION: + if (copy_from_user(&p.user_window, + (struct s3cfb_user_window __user *)arg, + sizeof(p.user_window))) + ret = -EFAULT; + else { +#if defined(CONFIG_CPU_EXYNOS4212) || defined(CONFIG_CPU_EXYNOS4412) + win->x = p.user_window.x; + win->y = p.user_window.y; +#else + if (p.user_window.x < 0) + p.user_window.x = 0; + + if (p.user_window.y < 0) + p.user_window.y = 0; + + if (p.user_window.x + var->xres > lcd->width) + win->x = lcd->width - var->xres; + else + win->x = p.user_window.x; + + if (p.user_window.y + var->yres > lcd->height) + win->y = lcd->height - var->yres; + else + win->y = p.user_window.y; + + s3cfb_set_window_position(fbdev, win->id); +#endif + } + break; + + case S3CFB_WIN_SET_PLANE_ALPHA: + if (copy_from_user(&p.user_alpha, + (struct s3cfb_user_plane_alpha __user *)arg, + sizeof(p.user_alpha))) + ret = -EFAULT; + else { + win->alpha.mode = PLANE_BLENDING; + win->alpha.channel = p.user_alpha.channel; + win->alpha.value = + S3CFB_AVALUE(p.user_alpha.red, + p.user_alpha.green, p.user_alpha.blue); + + s3cfb_set_alpha_blending(fbdev, win->id); + } + break; + + case S3CFB_WIN_SET_CHROMA: + if (copy_from_user(&p.user_chroma, + (struct s3cfb_user_chroma __user *)arg, + sizeof(p.user_chroma))) + ret = -EFAULT; + else { + win->chroma.enabled = p.user_chroma.enabled; + win->chroma.key = S3CFB_CHROMA(p.user_chroma.red, + p.user_chroma.green, + p.user_chroma.blue); + + s3cfb_set_chroma_key(fbdev, win->id); + } + break; + + case S3CFB_SET_VSYNC_INT: + if (get_user(p.vsync, (int __user *)arg)) + ret = -EFAULT; + else { +#ifdef CONFIG_CPU_EXYNOS4412 + if (!fbdev->regs) + return ret; +#endif + s3cfb_set_global_interrupt(fbdev, p.vsync); + s3cfb_set_vsync_interrupt(fbdev, p.vsync); + } + break; + + case S3CFB_GET_FB_PHY_ADDR: + start_addr = fix->smem_start + ((var->xres_virtual * + var->yoffset + var->xoffset) * + (var->bits_per_pixel / 8)); + + dev_dbg(fbdev->dev, "framebuffer_addr: 0x%08x\n", start_addr); + + if (copy_to_user((void *)arg, &start_addr, sizeof(unsigned int))) { + dev_err(fbdev->dev, "copy_to_user error\n"); + return -EFAULT; + } + + break; + + case S3CFB_GET_CUR_WIN_BUF_ADDR: + start_addr = s3cfb_get_win_cur_buf_addr(fbdev, win->id); + dev_dbg(fbdev->dev, "win_cur_buf_addr: 0x%08x\n", start_addr); + if (copy_to_user((void *)arg, &start_addr, sizeof(unsigned int))) { + dev_err(fbdev->dev, "copy_to_user error\n"); + return -EFAULT; + } + break; + +#if defined(CONFIG_CPU_EXYNOS4210) + case S3CFB_SET_WIN_MEM_START: + if (copy_from_user(&addr, (unsigned int *)arg, sizeof(unsigned int))) + ret = -EFAULT; + else { + fix->smem_start = (unsigned long)addr; + printk("\n\n######smem_start %x \n\n", (unsigned int)fix->smem_start); + } + break; +#endif + + case S3CFB_SET_WIN_ON: + s3cfb_enable_window(fbdev, win->id); + break; + + case S3CFB_SET_WIN_OFF: + s3cfb_disable_window(fbdev, win->id); + break; + + case S3CFB_SET_ALPHA_MODE: + if (copy_from_user(&p.alpha_mode, + (struct s3cfb_user_window __user *)arg, + sizeof(p.alpha_mode))) + ret = -EFAULT; + else + s3cfb_set_alpha_mode(fbdev, win->id, p.alpha_mode); + break; + } + return ret; +} + +int s3cfb_enable_localpath(struct s3cfb_global *fbdev, int id) +{ + struct s3cfb_window *win = fbdev->fb[id]->par; + + if (s3cfb_channel_localpath_on(fbdev, id)) { + win->enabled = 0; + return -EFAULT; + } else { + win->enabled = 1; + return 0; + } +} + +int s3cfb_disable_localpath(struct s3cfb_global *fbdev, int id) +{ + struct s3cfb_window *win = fbdev->fb[id]->par; + + if (s3cfb_channel_localpath_off(fbdev, id)) { + win->enabled = 1; + return -EFAULT; + } else { + win->enabled = 0; + return 0; + } +} + +int s3cfb_open_fifo(int id, int ch, int (*do_priv) (void *), void *param) +{ + struct s3cfb_global *fbdev = get_fimd_global(id); + struct s3cfb_window *win = fbdev->fb[id]->par; + + dev_dbg(fbdev->dev, "[fb%d] open fifo\n", win->id); + + if (win->path == DATA_PATH_DMA) { + dev_err(fbdev->dev, "WIN%d is busy.\n", id); + return -EFAULT; + } + + win->local_channel = ch; + + if (do_priv) { + if (do_priv(param)) { + dev_err(fbdev->dev, "failed to run for " + "private fifo open\n"); + s3cfb_enable_window(fbdev, id); + return -EFAULT; + } + } + + s3cfb_set_window_control(fbdev, id); + s3cfb_enable_window(fbdev, id); + s3cfb_enable_localpath(fbdev, id); + + return 0; +} +EXPORT_SYMBOL(s3cfb_open_fifo); + +int s3cfb_close_fifo(int id, int (*do_priv) (void *), void *param) +{ + struct s3cfb_global *fbdev = get_fimd_global(id); + struct s3cfb_window *win = fbdev->fb[id]->par; + win->path = DATA_PATH_DMA; + + dev_dbg(fbdev->dev, "[fb%d] close fifo\n", win->id); + + if (do_priv) { + s3cfb_display_off(fbdev); + + if (do_priv(param)) { + dev_err(fbdev->dev, "failed to run for" + "private fifo close\n"); + s3cfb_enable_window(fbdev, id); + s3cfb_display_on(fbdev); + return -EFAULT; + } + + s3cfb_disable_window(fbdev, id); + s3cfb_disable_localpath(fbdev, id); + s3cfb_display_on(fbdev); + } else { + s3cfb_disable_window(fbdev, id); + s3cfb_disable_localpath(fbdev, id); + } + + return 0; +} +EXPORT_SYMBOL(s3cfb_close_fifo); + +int s3cfb_direct_ioctl(int id, unsigned int cmd, unsigned long arg) +{ + struct s3cfb_global *fbdev = get_fimd_global(id); + struct fb_info *fb = fbdev->fb[id]; + struct fb_var_screeninfo *var = &fb->var; + struct fb_fix_screeninfo *fix = &fb->fix; + struct s3cfb_window *win = fb->par; + struct s3cfb_lcd *lcd = fbdev->lcd; + struct s3cfb_user_window user_win; +#ifdef CONFIG_EXYNOS_DEV_PD + struct platform_device *pdev = to_platform_device(fbdev->dev); +#endif + void *argp = (void *)arg; + int ret = 0; + +#ifdef CONFIG_EXYNOS_DEV_PD + /* enable the power domain */ + if (fbdev->system_state == POWER_OFF) { + /* This IOCTLs are came from fimc irq. + * To avoid scheduling problem in irq mode, + * runtime get/put sync shold be not called. + */ + switch (cmd) { + case S3CFB_SET_WIN_ADDR: + fix->smem_start = (unsigned long)argp; + return ret; + + case S3CFB_SET_WIN_ON: + if (!win->enabled) + atomic_inc(&fbdev->enabled_win); + win->enabled = 1; + + return ret; + } + + pm_runtime_get_sync(&pdev->dev); + } +#endif + + switch (cmd) { + case FBIOGET_FSCREENINFO: + ret = memcpy(argp, &fb->fix, sizeof(fb->fix)) ? 0 : -EFAULT; + break; + + case FBIOGET_VSCREENINFO: + ret = memcpy(argp, &fb->var, sizeof(fb->var)) ? 0 : -EFAULT; + break; + + case FBIOPUT_VSCREENINFO: + ret = s3cfb_check_var((struct fb_var_screeninfo *)argp, fb); + if (ret) { + dev_err(fbdev->dev, "invalid vscreeninfo\n"); + break; + } + + ret = memcpy(&fb->var, (struct fb_var_screeninfo *)argp, + sizeof(fb->var)) ? 0 : -EFAULT; + if (ret) { + dev_err(fbdev->dev, "failed to put new vscreeninfo\n"); + break; + } + + fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; + fix->smem_len = fix->line_length * var->yres_virtual; + + s3cfb_set_win_params(fbdev, id); + break; + + case S3CFB_WIN_POSITION: + ret = memcpy(&user_win, (struct s3cfb_user_window __user *)arg, + sizeof(user_win)) ? 0 : -EFAULT; + if (ret) { + dev_err(fbdev->dev, "failed to S3CFB_WIN_POSITION\n"); + break; + } + + if (user_win.x < 0) + user_win.x = 0; + + if (user_win.y < 0) + user_win.y = 0; + + if (user_win.x + var->xres > lcd->width) + win->x = lcd->width - var->xres; + else + win->x = user_win.x; + + if (user_win.y + var->yres > lcd->height) + win->y = lcd->height - var->yres; + else + win->y = user_win.y; + + s3cfb_set_window_position(fbdev, win->id); + break; + + case S3CFB_GET_LCD_WIDTH: + ret = memcpy(argp, &lcd->width, sizeof(int)) ? 0 : -EFAULT; + if (ret) { + dev_err(fbdev->dev, "failed to S3CFB_GET_LCD_WIDTH\n"); + break; + } + + break; + + case S3CFB_GET_LCD_HEIGHT: + ret = memcpy(argp, &lcd->height, sizeof(int)) ? 0 : -EFAULT; + if (ret) { + dev_err(fbdev->dev, "failed to S3CFB_GET_LCD_HEIGHT\n"); + break; + } + + break; + + case S3CFB_SET_WRITEBACK: + if ((u32)argp == 1) + fbdev->output = OUTPUT_WB_RGB; + else + fbdev->output = OUTPUT_RGB; + + s3cfb_set_output(fbdev); + + break; + + case S3CFB_SET_WIN_ON: + s3cfb_enable_window(fbdev, id); + break; + + case S3CFB_SET_WIN_OFF: + s3cfb_disable_window(fbdev, id); + break; + + case S3CFB_SET_WIN_PATH: + win->path = (enum s3cfb_data_path_t)argp; + break; + + case S3CFB_SET_WIN_ADDR: + fix->smem_start = (unsigned long)argp; + s3cfb_set_buffer_address(fbdev, id); + break; + + case S3CFB_SET_WIN_MEM: + win->owner = (enum s3cfb_mem_owner_t)argp; + break; + + case S3CFB_SET_VSYNC_INT: + if (argp) + s3cfb_set_global_interrupt(fbdev, 1); + + s3cfb_set_vsync_interrupt(fbdev, (int)argp); + break; + + case S3CFB_GET_VSYNC_INT_STATUS: + ret = s3cfb_get_vsync_interrupt(fbdev); + break; + + default: +#ifdef CONFIG_EXYNOS_DEV_PD + fbdev->system_state = POWER_ON; +#endif + ret = s3cfb_ioctl(fb, cmd, arg); +#ifdef CONFIG_EXYNOS_DEV_PD + fbdev->system_state = POWER_OFF; +#endif + break; + } + +#ifdef CONFIG_EXYNOS_DEV_PD + /* disable the power domain */ + if (fbdev->system_state == POWER_OFF) + pm_runtime_put(&pdev->dev); +#endif + + return ret; +} +EXPORT_SYMBOL(s3cfb_direct_ioctl); + +MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>"); +MODULE_AUTHOR("Jonghun, Han <jonghun.han@samsung.com>"); +MODULE_AUTHOR("Jinsung, Yang <jsgood.yang@samsung.com>"); +MODULE_DESCRIPTION("Samsung Display Controller (FIMD) driver"); +MODULE_LICENSE("GPL"); |