diff options
-rw-r--r-- | drivers/gpu/pvr/display/omap_display.c | 352 | ||||
-rw-r--r-- | drivers/gpu/pvr/display/omap_display.h | 3 | ||||
-rw-r--r-- | drivers/gpu/pvr/display/omap_sgx_displayclass.c | 75 |
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; |