summaryrefslogtreecommitdiff
path: root/drivers/gpu/pvr
diff options
context:
space:
mode:
authorGustavo Diaz Prado <x0083741@ti.com>2011-05-31 09:25:10 +0100
committerAndy Green <andy.green@linaro.org>2011-05-31 09:25:10 +0100
commit41f125159ae0293151376d563cda5b2b81e4bb36 (patch)
tree02302df36974a7269672a1ab87310032212e4c0f /drivers/gpu/pvr
parent757b28a2b37b55e882af915567c36ea1ca27d74d (diff)
OMAP4: Virtual display: Add manual update support for DSI panels
This patch adds manual update support for the virtual display. Synchronization code is now executed in process context rather than interrupt context which could cause schedule while atomic issues. In order to achieve the maximum refresh rate without display synchronization (to prevent tearing), updates are made with driver->sched_update rather than driver->update when available on manual update panels. The SGX display driver used when Virtual display is enabled has also been updated to allow the applications to behave normally when the SwapInterval is 2 or more on manual update panels, this change is already present on the UDD without Virtual display enabled so they are aligned. Signed-off-by: Gustavo Diaz Prado <x0083741@ti.com> Change-Id: I53b9207fd839374c135c66a8bf8b18f0cf722642
Diffstat (limited to 'drivers/gpu/pvr')
-rw-r--r--drivers/gpu/pvr/display/omap_display.c352
-rw-r--r--drivers/gpu/pvr/display/omap_display.h3
-rw-r--r--drivers/gpu/pvr/display/omap_sgx_displayclass.c75
3 files changed, 218 insertions, 212 deletions
diff --git a/drivers/gpu/pvr/display/omap_display.c b/drivers/gpu/pvr/display/omap_display.c
index 1f130753c46..d3859084b83 100644
--- a/drivers/gpu/pvr/display/omap_display.c
+++ b/drivers/gpu/pvr/display/omap_display.c
@@ -44,7 +44,7 @@
#define OMAP_DISP_IRQ_TIMEOUT 500
#ifdef DEBUG
-#define DBG_PRINT(format, ...) printk(KERN_DEBUG OMAP_DISP_DRV_NAME \
+#define DBG_PRINT(format, ...) printk(KERN_INFO OMAP_DISP_DRV_NAME \
" (%s %i): " format "\n", __func__, __LINE__, ## __VA_ARGS__)
#define WRN_PRINT(format, ...) printk(KERN_WARNING OMAP_DISP_DRV_NAME \
" (%s %i): " format "\n", __func__, __LINE__, ## __VA_ARGS__)
@@ -62,10 +62,17 @@
static struct omap_display_device *omap_display_list;
static unsigned int omap_display_number;
+/* Workqueues for virtual display (primary, seconday)*/
+static struct workqueue_struct *vdisp_wq_primary;
+static struct workqueue_struct *vdisp_wq_secondary;
+static struct omap_display_sync_item vdisp_sync_primary;
+static struct omap_display_sync_item vdisp_sync_secondary;
+
/* Forward declarations */
static struct omap_display_buffer *create_main_buffer(
struct omap_display_device *display);
static int display_destroy_buffer(struct omap_display_buffer *buffer);
+static void vdisp_sync_handler(struct work_struct *work);
static int open_display(struct omap_display_device *display,
enum omap_display_feature features)
@@ -400,6 +407,10 @@ static int present_buffer_virtual(struct omap_display_buffer *buffer)
static int present_buffer(struct omap_display_buffer *buffer)
{
struct omap_display_device *display = buffer->display;
+ struct fb_info *framebuffer;
+ struct omapfb_info *ofbi;
+ struct omapfb2_device *fbdev;
+ int i;
int fb_idx;
switch (display->id) {
@@ -419,124 +430,130 @@ static int present_buffer(struct omap_display_buffer *buffer)
BUG();
}
- if (fb_idx >= 0 && fb_idx < num_registered_fb) {
- struct fb_info *framebuffer = registered_fb[fb_idx];
- struct omapfb_info *ofbi = FB2OFB(framebuffer);
- struct omapfb2_device *fbdev = ofbi->fbdev;
- struct omap_overlay *overlay;
- struct omap_overlay_info overlay_info;
- struct omap_dss_device *dss_device;
- int i;
+ if (fb_idx < 0 || fb_idx >= num_registered_fb) {
+ ERR_PRINT("Framebuffer %i doesn't exist for display '%s'",
+ fb_idx, display->name);
+ return 1;
+ }
- omapfb_lock(fbdev);
+ framebuffer = registered_fb[fb_idx];
+ ofbi = FB2OFB(framebuffer);
+ fbdev = ofbi->fbdev;
- /* Get the overlays attached to the framebuffer */
- for (i = 0; i < ofbi->num_overlays ; i++) {
- overlay = ofbi->overlays[i];
- overlay->get_overlay_info(overlay, &overlay_info);
+ omapfb_lock(fbdev);
- /* If the overlay is not enabled don't update it */
- if (!overlay_info.enabled)
- continue;
+ /* Get the overlays attached to the framebuffer */
+ for (i = 0; i < ofbi->num_overlays ; i++) {
+ struct omap_dss_device *display = NULL;
+ struct omap_dss_driver *driver = NULL;
+ struct omap_overlay_manager *manager;
+ struct omap_overlay *overlay;
+ struct omap_overlay_info overlay_info;
- overlay_info.paddr = buffer->physical_addr;
- overlay_info.vaddr = (void *) buffer->virtual_addr;
- overlay->set_overlay_info(overlay, &overlay_info);
+ overlay = ofbi->overlays[i];
+ manager = overlay->manager;
+ overlay->get_overlay_info(overlay, &overlay_info);
- if (overlay->manager) {
- overlay->manager->apply(overlay->manager);
- dss_device = overlay->manager->device;
- /*
- * FIXME: Is the update really needed?
- * On 2.6.35 calling this code triggers a bug
- */
-#if 0
- if (dss_device &&
- dss_device->driver->update) {
- dss_device->driver->update(
- dss_device,
- 0, 0,
- overlay_info.width,
- overlay_info.height);
- }
-#endif
- }
+ overlay_info.paddr = buffer->physical_addr;
+ overlay_info.vaddr = (void *) buffer->virtual_addr;
+ overlay->set_overlay_info(overlay, &overlay_info);
+
+ if (manager) {
+ manager->apply(manager);
+ display = manager->device;
+ driver = display ? display->driver : NULL;
}
- omapfb_unlock(fbdev);
- } else {
- ERR_PRINT("Framebuffer %i doesn't exist for display '%s'",
- fb_idx, display->name);
- return 1;
+ if (dss_ovl_manually_updated(overlay)) {
+ if (driver->sched_update)
+ driver->sched_update(display, 0, 0,
+ overlay_info.width,
+ overlay_info.height);
+ else if (driver->update)
+ driver->update(display, 0, 0,
+ overlay_info.width,
+ overlay_info.height);
+ }
}
- return 0;
-}
+ omapfb_unlock(fbdev);
-static u32 get_dss_irq(struct omap_dss_device *dss_device)
-{
- u32 irq;
-
- if (dss_device->type == OMAP_DISPLAY_TYPE_VENC)
- irq = DISPC_IRQ_EVSYNC_ODD;
- else if (dss_device->type == OMAP_DISPLAY_TYPE_HDMI)
- irq = DISPC_IRQ_EVSYNC_EVEN;
- else if (dss_device->type == OMAP_DISPLAY_TYPE_DSI)
- if (!strcmp(dss_device->name, "lcd"))
- irq = DISPC_IRQ_FRAMEDONE;
- else
- irq = DISPC_IRQ_FRAMEDONE2;
- else
- if (!strcmp(dss_device->name, "lcd"))
- irq = DISPC_IRQ_VSYNC;
- else
- irq = DISPC_IRQ_VSYNC2;
-
- return irq;
+
+ return 0;
}
static int present_buffer_sync(struct omap_display_buffer *buffer)
{
/* TODO: Cloning may tear with this implementation */
- int err = 0;
struct omap_display_device *display = buffer->display;
+ struct fb_info *framebuffer;
+ struct omap_dss_device *dss_device;
+ struct omap_dss_driver *driver;
+ struct omap_overlay_manager *manager;
+ int fb_idx;
+ int err = 1;
- err = display->sync(display);
- err |= display->present_buffer(buffer);
-
- return err;
-}
+ switch (display->id) {
+ case OMAP_DISPID_PRIMARY:
+ fb_idx = 0;
+ break;
+ case OMAP_DISPID_SECONDARY:
+ fb_idx = 1;
+ break;
+ case OMAP_DISPID_TERTIARY:
+ fb_idx = 2;
+ break;
+ case OMAP_DISPID_VIRTUAL:
+ case OMAP_DISPID_BADSTATE:
+ default:
+ ERR_PRINT("Unable to handle display %i", display->id);
+ BUG();
+ }
-static int present_buffer_sync_virtual(struct omap_display_buffer *buffer)
-{
- void display_irq_wait_1(void *data, u32 mask)
- {
- struct omap_display_sync_item *sync_item =
- (struct omap_display_sync_item *) data;
-
- if (sync_item->invalidate)
- return;
-
- /* IRQ happened, present immediately */
- sync_item->invalidate = 1;
- sync_item->buffer->display->present_buffer(sync_item->buffer);
- complete(sync_item->task);
+ if (fb_idx < 0 || fb_idx >= num_registered_fb) {
+ ERR_PRINT("Framebuffer %i doesn't exist for display '%s'",
+ fb_idx, display->name);
+ return 1;
}
- void display_irq_wait_2(void *data, u32 mask)
- {
- struct omap_display_sync_item *sync_item =
- (struct omap_display_sync_item *) data;
+ framebuffer = registered_fb[fb_idx];
+ dss_device = fb2display(framebuffer);
- if (sync_item->invalidate)
- return;
+ if (!dss_device) {
+ WRN_PRINT("No DSS device to sync with display '%s'!",
+ display->name);
+ return 1;
+ }
- /* IRQ happened, present immediately */
- sync_item->invalidate = 1;
- sync_item->buffer->display->present_buffer(sync_item->buffer);
- complete(sync_item->task);
+ driver = dss_device->driver;
+ manager = dss_device->manager;
+
+ if (driver && driver->sync &&
+ driver->get_update_mode(dss_device) ==
+ OMAP_DSS_UPDATE_MANUAL) {
+ err = driver->sync(dss_device);
+ err |= display->present_buffer(buffer);
+ } else if (manager && manager->wait_for_vsync) {
+ err = manager->wait_for_vsync(manager);
+ err |= display->present_buffer(buffer);
}
+ if (err)
+ WRN_PRINT("Unable to sync with display '%s'!", display->name);
+
+ return err;
+}
+
+static void vdisp_sync_handler(struct work_struct *work)
+{
+ struct omap_display_sync_item *sync_item =
+ (struct omap_display_sync_item *) work;
+ struct omap_display_device *display = sync_item->buffer->display;
+ display->present_buffer_sync(sync_item->buffer);
+}
+
+static int present_buffer_sync_virtual(struct omap_display_buffer *buffer)
+{
/*
* TODO: Support for ORIENTATION_VERTICAL is in place,
* ORIENTATION_HORIZONTAL is missing. Some code can be reduced here,
@@ -547,10 +564,6 @@ static int present_buffer_sync_virtual(struct omap_display_buffer *buffer)
struct omap_display_device *display_secondary;
struct omap_display_buffer temp_buffer_top;
struct omap_display_buffer temp_buffer_bottom;
- struct omap_display_sync_item sync_item_primary;
- struct omap_display_sync_item sync_item_secondary;
- DECLARE_COMPLETION_ONSTACK(completion_primary);
- DECLARE_COMPLETION_ONSTACK(completion_secondary);
unsigned int buffer_offset;
if (display_virtual->id != OMAP_DISPID_VIRTUAL) {
@@ -580,41 +593,25 @@ static int present_buffer_sync_virtual(struct omap_display_buffer *buffer)
/* Secondary display has the base */
temp_buffer_top.display = display_secondary;
temp_buffer_bottom.display = display_primary;
- sync_item_primary.buffer = &temp_buffer_bottom;
- sync_item_secondary.buffer = &temp_buffer_top;
-
+ vdisp_sync_primary.buffer = &temp_buffer_bottom;
+ vdisp_sync_secondary.buffer = &temp_buffer_top;
} else {
/* Primary display has the base */
temp_buffer_top.display = display_primary;
temp_buffer_bottom.display = display_secondary;
- sync_item_primary.buffer = &temp_buffer_top;
- sync_item_secondary.buffer = &temp_buffer_bottom;
+ vdisp_sync_primary.buffer = &temp_buffer_top;
+ vdisp_sync_secondary.buffer = &temp_buffer_bottom;
}
- sync_item_primary.task = &completion_primary;
- sync_item_secondary.task = &completion_secondary;
- sync_item_primary.invalidate = 0;
- sync_item_secondary.invalidate = 0;
-
- /* Register an ISR per display with its corresponding IRQ */
- omap_dispc_register_isr(display_irq_wait_1, &sync_item_primary,
- get_dss_irq(display_primary->overlay_managers[0]->device));
-
- omap_dispc_register_isr(display_irq_wait_2, &sync_item_secondary,
- get_dss_irq(display_secondary->overlay_managers[0]->device));
+ /* Launch the workqueues for each display to present independently */
+ queue_work(vdisp_wq_primary,
+ (struct work_struct *)&vdisp_sync_primary);
+ queue_work(vdisp_wq_secondary,
+ (struct work_struct *)&vdisp_sync_secondary);
/* Wait until each display sync and present */
- wait_for_completion_interruptible_timeout(&completion_primary,
- OMAP_DISP_IRQ_TIMEOUT);
- wait_for_completion_interruptible_timeout(&completion_secondary,
- OMAP_DISP_IRQ_TIMEOUT);
-
- /* Unregister ISRs */
- omap_dispc_unregister_isr(display_irq_wait_1, &sync_item_primary,
- get_dss_irq(display_primary->overlay_managers[0]->device));
-
- omap_dispc_unregister_isr(display_irq_wait_2, &sync_item_secondary,
- get_dss_irq(display_secondary->overlay_managers[0]->device));
+ flush_workqueue(vdisp_wq_primary);
+ flush_workqueue(vdisp_wq_secondary);
return 0;
}
@@ -622,32 +619,68 @@ static int present_buffer_sync_virtual(struct omap_display_buffer *buffer)
static int display_sync(struct omap_display_device *display)
{
/* TODO: Synchronize properly with multiple managers */
- struct omap_overlay_manager *manager =
- display->overlay_managers[0];
- if (!manager) {
- ERR_PRINT("Unable to synchronize with '%s'", display->name);
+ struct fb_info *framebuffer;
+ struct omap_dss_device *dss_device;
+ struct omap_dss_driver *driver;
+ struct omap_overlay_manager *manager;
+ int fb_idx;
+ int err = 1;
+
+ switch (display->id) {
+ case OMAP_DISPID_PRIMARY:
+ fb_idx = 0;
+ break;
+ case OMAP_DISPID_SECONDARY:
+ fb_idx = 1;
+ break;
+ case OMAP_DISPID_TERTIARY:
+ fb_idx = 2;
+ break;
+ case OMAP_DISPID_VIRTUAL:
+ case OMAP_DISPID_BADSTATE:
+ default:
+ ERR_PRINT("Unable to handle display %i", display->id);
+ BUG();
+ }
+
+ if (fb_idx < 0 || fb_idx >= num_registered_fb) {
+ ERR_PRINT("Framebuffer %i doesn't exist for display '%s'",
+ fb_idx, display->name);
return 1;
}
- manager->wait_for_vsync(manager);
- return 0;
+
+ framebuffer = registered_fb[fb_idx];
+ dss_device = fb2display(framebuffer);
+
+ if (!dss_device) {
+ WRN_PRINT("No DSS device to sync with display '%s'!",
+ display->name);
+ return 1;
+ }
+
+ driver = dss_device->driver;
+ manager = dss_device->manager;
+
+ if (driver && driver->sync &&
+ driver->get_update_mode(dss_device) == OMAP_DSS_UPDATE_MANUAL)
+ err = driver->sync(dss_device);
+ else if (manager && manager->wait_for_vsync)
+ err = manager->wait_for_vsync(manager);
+
+ if (err)
+ WRN_PRINT("Unable to sync with display '%s'!", display->name);
+
+ return err;
}
static int display_sync_virtual(struct omap_display_device *display_virtual)
{
- void display_irq_wait(void *data, u32 mask)
- {
- complete((struct completion *)data);
- }
-
/*
- * Return as soon as one display generates an IRQ
+ * XXX: This function only waits for the primary display it should
+ * be adapted to the customer needs since waiting for the primary
+ * AND the secondary display may take too long for a single sync.
*/
struct omap_display_device *display_primary;
- struct omap_display_device *display_secondary;
- u32 irq_primary;
- u32 irq_secondary;
- u32 irq_mask;
- DECLARE_COMPLETION_ONSTACK(completion);
if (display_virtual->id != OMAP_DISPID_VIRTUAL) {
ERR_PRINT("Not a virtual display");
@@ -655,25 +688,7 @@ static int display_sync_virtual(struct omap_display_device *display_virtual)
}
display_primary = omap_display_get(OMAP_DISPID_PRIMARY);
- display_secondary = omap_display_get(OMAP_DISPID_SECONDARY);
-
- irq_primary = get_dss_irq(
- display_primary->overlay_managers[0]->device);
-
- irq_secondary = get_dss_irq(
- display_secondary->overlay_managers[0]->device);
-
- irq_mask = irq_primary | irq_secondary;
-
- /* Register an ISR with both IRQs and wait, then unregister */
- omap_dispc_register_isr(display_irq_wait, &completion, irq_mask);
-
- wait_for_completion_interruptible_timeout(&completion,
- OMAP_DISP_IRQ_TIMEOUT);
-
- omap_dispc_unregister_isr(display_irq_wait, &completion, irq_mask);
-
- return 0;
+ return display_primary->sync(display_primary);
}
static struct omap_display_buffer *create_main_buffer(
@@ -1047,6 +1062,14 @@ int omap_display_initialize(void)
return 1;
}
}
+
+ vdisp_wq_primary = __create_workqueue("vdisp_wq_primary", 1, 1, 1);
+ vdisp_wq_secondary = __create_workqueue("vdisp_wq_secondary", 1, 1, 1);
+ INIT_WORK((struct work_struct *)&vdisp_sync_primary,
+ vdisp_sync_handler);
+ INIT_WORK((struct work_struct *)&vdisp_sync_secondary,
+ vdisp_sync_handler);
+
return 0;
}
EXPORT_SYMBOL(omap_display_initialize);
@@ -1081,6 +1104,11 @@ int omap_display_deinitialize(void)
kfree(omap_display_list);
omap_display_list = 0;
+ destroy_workqueue(vdisp_wq_primary);
+ destroy_workqueue(vdisp_wq_secondary);
+ vdisp_wq_primary = NULL;
+ vdisp_wq_secondary = NULL;
+
return err;
}
EXPORT_SYMBOL(omap_display_deinitialize);
diff --git a/drivers/gpu/pvr/display/omap_display.h b/drivers/gpu/pvr/display/omap_display.h
index 71b9dac23b1..8076f88a46f 100644
--- a/drivers/gpu/pvr/display/omap_display.h
+++ b/drivers/gpu/pvr/display/omap_display.h
@@ -66,9 +66,8 @@ struct omap_display_flip_chain {
};
struct omap_display_sync_item {
+ struct work_struct work;
struct omap_display_buffer *buffer;
- struct completion *task;
- int invalidate;
};
struct omap_display_device {
diff --git a/drivers/gpu/pvr/display/omap_sgx_displayclass.c b/drivers/gpu/pvr/display/omap_sgx_displayclass.c
index fce8f67ac20..7ae2420d3e3 100644
--- a/drivers/gpu/pvr/display/omap_sgx_displayclass.c
+++ b/drivers/gpu/pvr/display/omap_sgx_displayclass.c
@@ -887,60 +887,38 @@ static void display_sync_handler(struct work_struct *work)
ulMaxIndex = psSwapChain->ulBufferCount - 1;
/* Iterate through the flip items and flip them if necessary */
- while(psFlipItem->bValid)
- {
- if(psFlipItem->bFlipped)
- {
- if(!psFlipItem->bCmdCompleted)
- {
- psSwapChain->psPVRJTable->pfnPVRSRVCmdComplete(
- (IMG_HANDLE)psFlipItem->hCmdComplete,
- IMG_TRUE);
- psFlipItem->bCmdCompleted = OMAP_TRUE;
- }
+ while (psFlipItem->bValid) {
+ /* Update display */
+ display->present_buffer_sync(psFlipItem->display_buffer);
- if(psFlipItem->ulSwapInterval == 0)
- {
- psSwapChain->ulRemoveIndex++;
- if(psSwapChain->ulRemoveIndex > ulMaxIndex)
- psSwapChain->ulRemoveIndex = 0;
- psFlipItem->bCmdCompleted = OMAP_FALSE;
- psFlipItem->bFlipped = OMAP_FALSE;
- psFlipItem->bValid = OMAP_FALSE;
- }
- else
- {
- /*
- * Here the swap interval is not zero yet
- * we need to schedule another work until
- * it reaches zero
- */
- display->sync(display);
- psFlipItem->ulSwapInterval--;
- queue_work(psDevInfo->sync_display_wq,
- &psDevInfo->sync_display_work);
- goto ExitUnlock;
- }
- }
- else
- {
- display->present_buffer_sync(
- psFlipItem->display_buffer);
- /*
- * present_buffer_sync waits and then present, then
- * swap interval decreases here too.
- */
- psFlipItem->ulSwapInterval--;
- psFlipItem->bFlipped = OMAP_TRUE;
+ psFlipItem->ulSwapInterval--;
+ psFlipItem->bFlipped = OMAP_TRUE;
+
+ if (psFlipItem->ulSwapInterval == 0) {
+
+ /* Mark the flip item as completed to reuse it */
+ psSwapChain->ulRemoveIndex++;
+ if (psSwapChain->ulRemoveIndex > ulMaxIndex)
+ psSwapChain->ulRemoveIndex = 0;
+ psFlipItem->bCmdCompleted = OMAP_FALSE;
+ psFlipItem->bFlipped = OMAP_FALSE;
+ psFlipItem->bValid = OMAP_FALSE;
+
+ psSwapChain->psPVRJTable->pfnPVRSRVCmdComplete(
+ (IMG_HANDLE)psFlipItem->hCmdComplete,
+ IMG_TRUE);
+ psFlipItem->bCmdCompleted = OMAP_TRUE;
+ } else {
/*
- * If the flip has been presented here then we need
- * in the next sync execute the command complete,
- * schedule another work
+ * Here the swap interval is not zero yet
+ * we need to schedule another work until
+ * it reaches zero
*/
queue_work(psDevInfo->sync_display_wq,
&psDevInfo->sync_display_work);
- goto ExitUnlock;
+ break;
}
+
psFlipItem =
&psSwapChain->psFlipItems[psSwapChain->ulRemoveIndex];
}
@@ -1291,6 +1269,7 @@ static enum OMAP_ERROR init_display_device(struct OMAP_DISP_DEVINFO *psDevInfo,
psDevInfo->sSystemBuffer.sCPUVAddr =
(IMG_CPU_VIRTADDR) display->main_buffer->virtual_addr;
psDevInfo->sSystemBuffer.ulBufferSize = display->main_buffer->size;
+ psDevInfo->sSystemBuffer.display_buffer = display->main_buffer;
psDevInfo->display = display;
return OMAP_OK;