diff options
Diffstat (limited to 'drivers/video/samsung/s3cfb_main.c')
-rw-r--r-- | drivers/video/samsung/s3cfb_main.c | 293 |
1 files changed, 232 insertions, 61 deletions
diff --git a/drivers/video/samsung/s3cfb_main.c b/drivers/video/samsung/s3cfb_main.c index 2cb7c86..a723306 100644 --- a/drivers/video/samsung/s3cfb_main.c +++ b/drivers/video/samsung/s3cfb_main.c @@ -29,7 +29,7 @@ #include <linux/memory.h> #include <linux/pm_runtime.h> #include <linux/delay.h> -#include <linux/kthread.h> +#include <linux/sw_sync.h> #include <plat/clock.h> #include <plat/media.h> #include <mach/media.h> @@ -58,11 +58,13 @@ #include <mach/regs-pmu.h> #include <plat/regs-fb-s5p.h> -extern int s3cfb_vsync_timestamp_changed(struct s3cfb_global *fbdev, ktime_t prev_timestamp); +#ifdef CONFIG_FB_S5P_SYSMMU +#include <plat/s5p-sysmmu.h> +#endif struct s3cfb_fimd_desc *fbfimd; -inline struct s3cfb_global *get_fimd_global(int id) +struct s3cfb_global *get_fimd_global(int id) { struct s3cfb_global *fbdev; @@ -85,17 +87,57 @@ int s3cfb_vsync_status_check(void) return 0; } +#if defined(CONFIG_FB_S5P_VSYNC_THREAD) +static void s3cfb_activate_vsync(struct s3cfb_global *fbdev) +{ + int prev_refcount; + + mutex_lock(&fbdev->vsync_info.irq_lock); + prev_refcount = fbdev->vsync_info.irq_refcount++; + if (!prev_refcount) { + s3cfb_set_global_interrupt(fbdev, 1); + s3cfb_set_vsync_interrupt(fbdev, 1); + } + + mutex_unlock(&fbdev->vsync_info.irq_lock); +} + +static void s3cfb_deactivate_vsync(struct s3cfb_global *fbdev) +{ + int new_refcount; + + mutex_lock(&fbdev->vsync_info.irq_lock); + + new_refcount = --fbdev->vsync_info.irq_refcount; + WARN_ON(new_refcount < 0); + if (!new_refcount) { + s3cfb_set_global_interrupt(fbdev, 0); + s3cfb_set_vsync_interrupt(fbdev, 0); + } + + mutex_unlock(&fbdev->vsync_info.irq_lock); +} +#endif + static irqreturn_t s3cfb_irq_frame(int irq, void *dev_id) { struct s3cfb_global *fbdev[2]; fbdev[0] = fbfimd->fbdev[0]; + spin_lock(&fbdev[0]->vsync_slock); + if (fbdev[0]->regs != 0) s3cfb_clear_interrupt(fbdev[0]); +#if defined(CONFIG_FB_S5P_VSYNC_THREAD) + fbdev[0]->vsync_info.timestamp = ktime_get(); + wake_up_interruptible_all(&fbdev[0]->vsync_info.wait); +#endif + fbdev[0]->wq_count++; - fbdev[0]->vsync_timestamp = ktime_get(); - wake_up_interruptible(&fbdev[0]->wq); + wake_up(&fbdev[0]->wq); + + spin_unlock(&fbdev[0]->vsync_slock); return IRQ_HANDLED; } @@ -113,6 +155,57 @@ static irqreturn_t s3cfb_irq_fifo(int irq, void *dev_id) } #endif +#if defined(CONFIG_FB_S5P_VSYNC_THREAD) +int s3cfb_set_vsync_int(struct fb_info *info, bool active) +{ + struct s3cfb_global *fbdev = fbfimd->fbdev[0]; + bool prev_active = fbdev->vsync_info.active; + + fbdev->vsync_info.active = active; + + if (active && !prev_active) + s3cfb_activate_vsync(fbdev); + else if (!active && prev_active) + s3cfb_deactivate_vsync(fbdev); + + return 0; +} + +/** + * s3cfb_wait_for_vsync() - sleep until next VSYNC interrupt or timeout + * @sfb: main hardware state + * @timeout: timeout in msecs, or 0 to wait indefinitely. + */ +int s3cfb_wait_for_vsync(struct s3cfb_global *fbdev, u32 timeout) +{ + ktime_t timestamp; + int ret; + + pm_runtime_get_sync(fbdev->dev); + + timestamp = fbdev->vsync_info.timestamp; + s3cfb_activate_vsync(fbdev); + if (timeout) { + ret = wait_event_interruptible_timeout(fbdev->vsync_info.wait, + !ktime_equal(timestamp, + fbdev->vsync_info.timestamp), + msecs_to_jiffies(timeout)); + } else { + ret = wait_event_interruptible(fbdev->vsync_info.wait, + !ktime_equal(timestamp, + fbdev->vsync_info.timestamp)); + } + s3cfb_deactivate_vsync(fbdev); + + pm_runtime_put_sync(fbdev->dev); + + if (timeout && ret == 0) + return -ETIMEDOUT; + + return 0; +} +#endif + int s3cfb_register_framebuffer(struct s3cfb_global *fbdev) { struct s3c_platform_fb *pdata = to_fb_plat(fbdev->dev); @@ -322,30 +415,53 @@ void s3cfb_trigger(void) EXPORT_SYMBOL(s3cfb_trigger); #endif +#if defined(CONFIG_FB_S5P_VSYNC_THREAD) static int s3cfb_wait_for_vsync_thread(void *data) { - struct s3cfb_global *fbdev = data; - - while (!kthread_should_stop()) { - ktime_t prev_timestamp = fbdev->vsync_timestamp; - - int ret = wait_event_interruptible_timeout(fbdev->wq, - s3cfb_vsync_timestamp_changed(fbdev, prev_timestamp), - msecs_to_jiffies(100)); - - if (ret > 0) { - char *envp[2]; - char buf[64]; + struct s3cfb_global *fbdev = data; + + while (!kthread_should_stop()) { + ktime_t timestamp = fbdev->vsync_info.timestamp; + int ret = wait_event_interruptible_timeout( + fbdev->vsync_info.wait, + !ktime_equal(timestamp, + fbdev->vsync_info.timestamp) && + fbdev->vsync_info.active, + msecs_to_jiffies(VSYNC_TIMEOUT_MSEC)); + + if (ret > 0) { + char *envp[2]; + char buf[64]; + snprintf(buf, sizeof(buf), "VSYNC=%llu", + ktime_to_ns(fbdev->vsync_info.timestamp)); + envp[0] = buf; + envp[1] = NULL; + kobject_uevent_env(&fbdev->dev->kobj, KOBJ_CHANGE, + envp); + } + } - snprintf(buf, sizeof(buf), "VSYNC=%llu", - ktime_to_ns(fbdev->vsync_timestamp)); - envp[0] = buf; - envp[1] = NULL; - kobject_uevent_env(&fbdev->dev->kobj, KOBJ_CHANGE, envp); - } - } + return 0; +} +#endif - return 0; +static void s3c_fb_update_regs_handler(struct kthread_work *work) +{ + struct s3cfb_global *fbdev = + container_of(work, struct s3cfb_global, update_regs_work); + struct s3c_reg_data *data, *next; + struct list_head saved_list; + + mutex_lock(&fbdev->update_regs_list_lock); + saved_list = fbdev->update_regs_list; + list_replace_init(&fbdev->update_regs_list, &saved_list); + mutex_unlock(&fbdev->update_regs_list_lock); + + list_for_each_entry_safe(data, next, &saved_list, list) { + s3c_fb_update_regs(fbdev, data); + list_del(&data->list); + kfree(data); + } } static int s3cfb_probe(struct platform_device *pdev) @@ -362,7 +478,13 @@ static int s3cfb_probe(struct platform_device *pdev) /* enable the power domain */ pm_runtime_get_sync(&pdev->dev); #endif + fbfimd = kzalloc(sizeof(struct s3cfb_fimd_desc), GFP_KERNEL); + if (!fbfimd) { + printk(KERN_ERR "failed to allocate for fimd fb descriptor\n"); + ret = -ENOMEM; + goto err_fimd_desc; + } if (FIMD_MAX == 2) fbfimd->dual = 1; @@ -418,9 +540,30 @@ static int s3cfb_probe(struct platform_device *pdev) if (!fbdev[i]->regs) { dev_err(fbdev[i]->dev, "failed to remap io region\n"); ret = -EINVAL; - goto err1; + goto err_ioremap; } + spin_lock_init(&fbdev[i]->vsync_slock); + +#if defined(CONFIG_FB_S5P_VSYNC_THREAD) + INIT_LIST_HEAD(&fbdev[i]->update_regs_list); + mutex_init(&fbdev[i]->update_regs_list_lock); + init_kthread_worker(&fbdev[i]->update_regs_worker); + + fbdev[i]->update_regs_thread = kthread_run(kthread_worker_fn, + &fbdev[i]->update_regs_worker, "s3c-fb"); + if (IS_ERR(fbdev[i]->update_regs_thread)) { + int err = PTR_ERR(fbdev[i]->update_regs_thread); + fbdev[i]->update_regs_thread = NULL; + + dev_err(fbdev[i]->dev, "failed to run update_regs thread\n"); + return err; + } + init_kthread_work(&fbdev[i]->update_regs_work, s3c_fb_update_regs_handler); + fbdev[i]->timeline = sw_sync_timeline_create("s3c-fb"); + fbdev[i]->timeline_max = 0; +#endif + /* irq */ fbdev[i]->irq = platform_get_irq(pdev, 0); if (request_irq(fbdev[i]->irq, s3cfb_irq_frame, IRQF_SHARED, @@ -463,7 +606,7 @@ static int s3cfb_probe(struct platform_device *pdev) /* register fb_info */ if (s3cfb_register_framebuffer(fbdev[i])) { dev_err(fbdev[i]->dev, "register error fimd[%d]\n", i); - return -EINVAL; + ret = -EINVAL; goto err3; } @@ -506,6 +649,20 @@ static int s3cfb_probe(struct platform_device *pdev) register_early_suspend(&fbdev[i]->early_suspend); #endif #endif +#if defined(CONFIG_FB_S5P_VSYNC_THREAD) + init_waitqueue_head(&fbdev[i]->vsync_info.wait); + + /* Create vsync thread */ + mutex_init(&fbdev[i]->vsync_info.irq_lock); + + fbdev[i]->vsync_info.thread = kthread_run( + s3cfb_wait_for_vsync_thread, + fbdev[i], "s3c-fb-vsync"); + if (fbdev[i]->vsync_info.thread == ERR_PTR(-ENOMEM)) { + dev_err(fbdev[i]->dev, "failed to run vsync thread\n"); + fbdev[i]->vsync_info.thread = NULL; + } +#endif ret = device_create_file(fbdev[i]->dev, &dev_attr_fimd_dump); if (ret < 0) dev_err(fbdev[0]->dev, "failed to add sysfs entries\n"); @@ -524,12 +681,6 @@ static int s3cfb_probe(struct platform_device *pdev) pdata->lcd_on(pdev); #endif - fbdev[0]->vsync_thread = kthread_run(s3cfb_wait_for_vsync_thread, fbdev[0], "s3cfb-vsync"); - if (fbdev[0]->vsync_thread == ERR_PTR(-ENOMEM)) { - dev_err(fbdev[0]->dev, "failed to run vsync thread\n"); - fbdev[0]->vsync_thread = NULL; - } - ret = device_create_file(&(pdev->dev), &dev_attr_win_power); if (ret < 0) dev_err(fbdev[0]->dev, "failed to add sysfs entries\n"); @@ -557,10 +708,18 @@ err3: err2: for (i = 0; i < FIMD_MAX; i++) iounmap(fbdev[i]->regs); + +err_ioremap: + release_mem_region(res->start, res->end - res->start + 1); + err1: - for (i = 0; i < FIMD_MAX; i++) + for (i = 0; i < FIMD_MAX; i++) { pdata->clk_off(pdev, &fbdev[i]->clock); + kfree(fbfimd->fbdev[i]); + } err0: + kfree(fbfimd); +err_fimd_desc: return ret; } @@ -600,9 +759,10 @@ static int s3cfb_remove(struct platform_device *pdev) framebuffer_release(fb); } } - - if (fbdev[i]->vsync_thread) - kthread_stop(fbdev[i]->vsync_thread); +#if defined(CONFIG_FB_S5P_VSYNC_THREAD) + if (fbdev[i]->vsync_info.thread) + kthread_stop(fbdev[i]->vsync_info.thread); +#endif kfree(fbdev[i]->fb); kfree(fbdev[i]); @@ -694,6 +854,9 @@ void s3cfb_lcd0_pmu_off(void) #ifdef CONFIG_PM #ifdef CONFIG_HAS_EARLYSUSPEND +void (*lcd_early_suspend)(void); +void (*lcd_late_resume)(void); + void s3cfb_early_suspend(struct early_suspend *h) { struct s3cfb_global *info = container_of(h, struct s3cfb_global, early_suspend); @@ -705,11 +868,8 @@ void s3cfb_early_suspend(struct early_suspend *h) printk(KERN_INFO "+%s\n", __func__); #ifdef CONFIG_FB_S5P_MIPI_DSIM -#if defined(CONFIG_FB_S5P_S6E63M0) - s6e63m0_early_suspend(); -#else - s6e8ax0_early_suspend(); -#endif + if (lcd_early_suspend) + lcd_early_suspend(); #endif for (i = 0; i < FIMD_MAX; i++) { @@ -765,8 +925,15 @@ void s3cfb_early_suspend(struct early_suspend *h) pm_runtime_put_sync(&pdev->dev); #endif - printk(KERN_INFO "-%s\n", __func__); +#ifdef CONFIG_FB_S5P_SYSMMU + if (fbdev[0]->sysmmu.enabled == true) { + fbdev[0]->sysmmu.enabled = false; + fbdev[0]->sysmmu.pgd = 0; + s5p_sysmmu_disable(fbdev[0]->dev); + } +#endif + printk(KERN_INFO "-%s\n", __func__); return ; } @@ -780,28 +947,18 @@ void s3cfb_late_resume(struct early_suspend *h) int i, j; struct platform_device *pdev = to_platform_device(info->dev); - printk(KERN_INFO "+%s\n", __func__); + dev_info(info->dev, "+%s\n", __func__); dev_dbg(info->dev, "wake up from suspend\n"); #ifdef CONFIG_EXYNOS_DEV_PD /* enable the power domain */ - printk(KERN_DEBUG "s3cfb - enable power domain\n"); + dev_dbg(info->dev, "s3cfb - enable power domain\n"); pm_runtime_get_sync(&pdev->dev); #endif #ifdef CONFIG_FB_S5P_MIPI_DSIM s5p_dsim_late_resume(); - - if (s5p_dsim_fifo_clear() == 0) { - s5p_dsim_early_suspend(); - usleep_range(10000, 10000); - s5p_dsim_late_resume(); - if (s5p_dsim_fifo_clear() == 0) - pr_info("dsim resume fail!!!\n"); - } - - usleep_range(10000, 10000); #endif #if defined(CONFIG_FB_S5P_DUMMYLCD) @@ -819,6 +976,10 @@ void s3cfb_late_resume(struct early_suspend *h) /* fbdev[i]->regs_org should be non-zero value */ BUG(); +#if defined(CONFIG_FB_MDNIE_PWM) + set_mdnie_pwm_value(g_mdnie, 0); +#endif + if (pdata->set_display_path) pdata->set_display_path(); @@ -848,6 +1009,9 @@ void s3cfb_late_resume(struct early_suspend *h) /* Set alpha value width to 8-bit */ s3cfb_set_alpha_value_width(fbdev[i], i); +#if defined(CONFIG_FB_RGBA_ORDER) + s3cfb_set_output(fbdev[i]); +#else for (j = 0; j < pdata->nr_wins; j++) { fb = fbdev[i]->fb[j]; win = fb->par; @@ -857,6 +1021,7 @@ void s3cfb_late_resume(struct early_suspend *h) s3cfb_enable_window(fbdev[i], win->id); } } +#endif if (pdata->cfg_gpio) pdata->cfg_gpio(pdev); @@ -869,17 +1034,23 @@ void s3cfb_late_resume(struct early_suspend *h) if (pdata->backlight_on) pdata->backlight_on(pdev); + +#if defined(CONFIG_FB_S5P_VSYNC_THREAD) + mutex_lock(&fbdev[i]->vsync_info.irq_lock); + if (fbdev[i]->vsync_info.irq_refcount) { + s3cfb_set_global_interrupt(fbdev[i], 1); + s3cfb_set_vsync_interrupt(fbdev[i], 1); + } + mutex_unlock(&fbdev[i]->vsync_info.irq_lock); +#endif } #ifdef CONFIG_FB_S5P_MIPI_DSIM -#if defined(CONFIG_FB_S5P_S6E63M0) - s6e63m0_late_resume(); -#else - s6e8ax0_late_resume(); -#endif + if (lcd_late_resume) + lcd_late_resume(); #endif - printk(KERN_INFO "-%s\n", __func__); + dev_info(info->dev, "-%s\n", __func__); return; } |