diff options
Diffstat (limited to 'drivers/gpu/drm/arm/hdlcd_drv.c')
-rw-r--r-- | drivers/gpu/drm/arm/hdlcd_drv.c | 93 |
1 files changed, 80 insertions, 13 deletions
diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index 0f49c4b12772..ea19a4781ba9 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -23,7 +23,7 @@ #include <drm/drm_crtc.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_fb_helper.h> -#include <drm/drm_fb_cma_helper.h> +#include "hdlcd_fb_helper.h" #include <drm/drm_gem_cma_helper.h> #include <drm/drm_of.h> @@ -67,6 +67,12 @@ static int hdlcd_load(struct drm_device *drm, unsigned long flags) (version & HDLCD_VERSION_MAJOR_MASK) >> 8, version & HDLCD_VERSION_MINOR_MASK); + /* Make sure hardware is in a safe reset state */ + hdlcd_write(hdlcd, HDLCD_REG_COMMAND, 0); + hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, 0); + hdlcd_write(hdlcd, HDLCD_REG_INT_CLEAR,~0); + hdlcd_write(hdlcd, HDLCD_REG_INT_RAWSTAT, 0); + /* Get the optional framebuffer memory resource */ ret = of_reserved_mem_device_init(drm->dev); if (ret && ret != -ENODEV) @@ -88,6 +94,9 @@ static int hdlcd_load(struct drm_device *drm, unsigned long flags) goto irq_fail; } + spin_lock_init(&hdlcd->frame_completion_lock); + init_completion(&hdlcd->frame_completion); + return 0; irq_fail: @@ -102,11 +111,11 @@ static void hdlcd_fb_output_poll_changed(struct drm_device *drm) { struct hdlcd_drm_private *hdlcd = drm->dev_private; - drm_fbdev_cma_hotplug_event(hdlcd->fbdev); + hdlcd_drm_fbdev_hotplug_event(hdlcd->fbdev); } static const struct drm_mode_config_funcs hdlcd_mode_config_funcs = { - .fb_create = drm_fb_cma_create, + .fb_create = hdlcd_fb_create, .output_poll_changed = hdlcd_fb_output_poll_changed, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, @@ -126,7 +135,7 @@ static void hdlcd_lastclose(struct drm_device *drm) { struct hdlcd_drm_private *hdlcd = drm->dev_private; - drm_fbdev_cma_restore_mode(hdlcd->fbdev); + hdlcd_drm_fbdev_restore_mode(hdlcd->fbdev); } static irqreturn_t hdlcd_irq(int irq, void *arg) @@ -134,6 +143,7 @@ static irqreturn_t hdlcd_irq(int irq, void *arg) struct drm_device *drm = arg; struct hdlcd_drm_private *hdlcd = drm->dev_private; unsigned long irq_status; + unsigned long flags; irq_status = hdlcd_read(hdlcd, HDLCD_REG_INT_STATUS); @@ -154,6 +164,16 @@ static irqreturn_t hdlcd_irq(int irq, void *arg) if (irq_status & HDLCD_INTERRUPT_VSYNC) drm_crtc_handle_vblank(&hdlcd->crtc); + spin_lock_irqsave(&hdlcd->frame_completion_lock, flags); + if (hdlcd_read(hdlcd, HDLCD_REG_INT_STATUS) & HDLCD_INTERRUPT_DMA_END) { + /* Clear DMA_END interrupt here, under frame_completion_lock */ + hdlcd_write(hdlcd, HDLCD_REG_INT_CLEAR, HDLCD_INTERRUPT_DMA_END); + irq_status &= ~HDLCD_INTERRUPT_DMA_END; + /* Wake up everyone waiting for frame completion */ + complete_all(&hdlcd->frame_completion); + } + spin_unlock_irqrestore(&hdlcd->frame_completion_lock, flags); + /* acknowledge interrupt(s) */ hdlcd_write(hdlcd, HDLCD_REG_INT_CLEAR, irq_status); @@ -170,15 +190,18 @@ static void hdlcd_irq_preinstall(struct drm_device *drm) static int hdlcd_irq_postinstall(struct drm_device *drm) { -#ifdef CONFIG_DEBUG_FS struct hdlcd_drm_private *hdlcd = drm->dev_private; unsigned long irq_mask = hdlcd_read(hdlcd, HDLCD_REG_INT_MASK); +#ifdef CONFIG_DEBUG_FS /* enable debug interrupts */ irq_mask |= HDLCD_DEBUG_INT_MASK; +#endif + /* enable DMA completion interrupts */ + irq_mask |= HDLCD_INTERRUPT_DMA_END; hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, irq_mask); -#endif + return 0; } @@ -193,12 +216,34 @@ static void hdlcd_irq_uninstall(struct drm_device *drm) irq_mask &= ~HDLCD_DEBUG_INT_MASK; #endif - /* disable vsync interrupts */ - irq_mask &= ~HDLCD_INTERRUPT_VSYNC; + /* disable vsync and dma interrupts */ + irq_mask &= ~(HDLCD_INTERRUPT_VSYNC | HDLCD_INTERRUPT_DMA_END); hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, irq_mask); } +void hdlcd_wait_for_frame_completion(struct drm_device *drm) +{ + struct hdlcd_drm_private *hdlcd = drm->dev_private; + + if (drm_crtc_vblank_get(&hdlcd->crtc)) + return; /* vblank interrupts not available so don't try and wait */ + + /* + * Clear pending interrupts and completions so we won't get signalled + * for any earlier frames, + */ + spin_lock_irq(&hdlcd->frame_completion_lock); + hdlcd_write(hdlcd, HDLCD_REG_INT_CLEAR, HDLCD_INTERRUPT_DMA_END); + reinit_completion(&hdlcd->frame_completion); + spin_unlock_irq(&hdlcd->frame_completion_lock); + + /* Wait for end of current frame */ + wait_for_completion_interruptible_timeout(&hdlcd->frame_completion, HZ / 10); + + drm_crtc_vblank_put(&hdlcd->crtc); +} + #ifdef CONFIG_DEBUG_FS static int hdlcd_show_underrun_count(struct seq_file *m, void *arg) { @@ -229,7 +274,7 @@ static int hdlcd_show_pxlclock(struct seq_file *m, void *arg) static struct drm_info_list hdlcd_debugfs_list[] = { { "interrupt_count", hdlcd_show_underrun_count, 0 }, { "clocks", hdlcd_show_pxlclock, 0 }, - { "fb", drm_fb_cma_debugfs_show, 0 }, + { "fb", hdlcd_fb_debugfs_show, 0 }, }; static int hdlcd_debugfs_init(struct drm_minor *minor) @@ -280,6 +325,8 @@ static int hdlcd_drm_bind(struct device *dev) struct drm_device *drm; struct hdlcd_drm_private *hdlcd; int ret; + struct device_node *node; + int preferred_bpp; hdlcd = devm_kzalloc(dev, sizeof(*hdlcd), GFP_KERNEL); if (!hdlcd) @@ -318,7 +365,15 @@ static int hdlcd_drm_bind(struct device *dev) drm_mode_config_reset(drm); drm_kms_helper_poll_init(drm); - hdlcd->fbdev = drm_fbdev_cma_init(drm, 32, + /* Try to pick the colour depth that Android user-side is hard-coded for */ + preferred_bpp = 16; + node = of_find_compatible_node(NULL,NULL,"arm,mali-midgard"); + if (node) { + of_node_put(node); + preferred_bpp = 32; /* If Mali present, assume 32bpp */ + } + + hdlcd->fbdev = hdlcd_drm_fbdev_init(drm, preferred_bpp, drm->mode_config.num_connector); if (IS_ERR(hdlcd->fbdev)) { @@ -335,7 +390,7 @@ static int hdlcd_drm_bind(struct device *dev) err_register: if (hdlcd->fbdev) { - drm_fbdev_cma_fini(hdlcd->fbdev); + hdlcd_drm_fbdev_fini(hdlcd->fbdev); hdlcd->fbdev = NULL; } err_fbdev: @@ -363,7 +418,7 @@ static void hdlcd_drm_unbind(struct device *dev) drm_dev_unregister(drm); if (hdlcd->fbdev) { - drm_fbdev_cma_fini(hdlcd->fbdev); + hdlcd_drm_fbdev_fini(hdlcd->fbdev); hdlcd->fbdev = NULL; } drm_kms_helper_poll_fini(drm); @@ -465,7 +520,19 @@ static struct platform_driver hdlcd_platform_driver = { }, }; -module_platform_driver(hdlcd_platform_driver); +static int __init hdlcd_init(void) +{ + return platform_driver_register(&hdlcd_platform_driver); +} + +static void __exit hdlcd_exit(void) +{ + platform_driver_unregister(&hdlcd_platform_driver); +} + +/* need late_initcall() so we load after i2c driver */ +late_initcall(hdlcd_init); +module_exit(hdlcd_exit); MODULE_AUTHOR("Liviu Dudau"); MODULE_DESCRIPTION("ARM HDLCD DRM driver"); |