/* * drivers/gpu/pvr/display/omap_display.c * * Copyright (C) 2010 Texas Instruments * * 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. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ #include #include #include #include #include #include /* Workaround for DEBUG macro clash in framebuffer */ #ifdef RELEASE #include <../drivers/video/omap2/omapfb/omapfb.h> #undef DEBUG #else #undef DEBUG #include <../drivers/video/omap2/omapfb/omapfb.h> #endif #define OMAP_DISP_DRV_NAME "omap_display" #define OMAP_DISP_FRAMEBUFFER_COUNT num_registered_fb #define OMAP_DISP_PAGE_MASK (PAGE_SIZE - 1) #define OMAP_DISP_PAGE_TRUNCATE (~OMAP_DISP_PAGE_MASK) #define OMAP_DISP_PAGE_ROUND_UP(x) \ (((x)+OMAP_DISP_PAGE_MASK) & OMAP_DISP_PAGE_TRUNCATE) #define OMAP_DISP_IRQ_TIMEOUT 500 #ifdef DEBUG #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__) #define ERR_PRINT(format, ...) printk(KERN_ERR OMAP_DISP_DRV_NAME \ " (%s %i): " format "\n", __func__, __LINE__, ## __VA_ARGS__) #else #define DBG_PRINT(format, ...) #define WRN_PRINT(format, ...) #define ERR_PRINT(format, ...) #endif #include "omap_display.h" /* List for the available displays */ 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) { int i; DBG_PRINT("Opening display '%s'", display->name); /* TODO: Support horizontal orientation */ if (features & ORIENTATION_HORIZONTAL) { DBG_PRINT("Horizontal orientation is not supported yet , " "falling back to vertical orientation"); features = ORIENTATION_VERTICAL; } display->features = features; display->reference_count++; for (i = 0; i < display->overlay_managers_count; i++) omap_dss_get_device(display->overlay_managers[i]->device); /* If the main buffer doesn't exist create it */ if (!display->main_buffer) { DBG_PRINT("Main buffer doesn't exist for display '%s', create" " one", display->name); display->main_buffer = create_main_buffer(display); if (!display->main_buffer) { ERR_PRINT("Failed to create main buffer for '%s'", display->name); return 1; } } return 0; } static int close_display(struct omap_display_device *display) { int err; int i; /* TODO: Is it the same thing to close a virtual and single display? */ DBG_PRINT("Closing display '%s'", display->name); display->reference_count--; for (i = 0; i < display->overlay_managers_count; i++) omap_dss_put_device(display->overlay_managers[i]->device); if (display->flip_chain) { err = display->destroy_flip_chain(display); display->flip_chain = 0; if (err) WRN_PRINT("An error happened when destroying flip " "chain for '%s'", display->name); } return 0; } static int get_max_buffers(struct omap_display_device *display) { /* TODO: If TILER is wanted to be used how do you calculate this? */ int fb_idx; 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: fb_idx = 0; break; case OMAP_DISPID_BADSTATE: default: ERR_PRINT("Unknown display id %i", display->id); BUG(); } /* Use the framebuffer memory */ if (fb_idx >= 0 && fb_idx < num_registered_fb) { struct fb_info *framebuffer = registered_fb[fb_idx]; unsigned long buffer_size; /* Single buffer size */ buffer_size = display->width * display->height * display->bytes_per_pixel; /* Page align the buffer size, round up to the page size */ buffer_size = OMAP_DISP_PAGE_ROUND_UP(buffer_size); return (int) (framebuffer->fix.smem_len / buffer_size); } else { ERR_PRINT("Framebuffer %i doesn't exist for display '%s'", fb_idx, display->name); return 0; } } static int create_flip_chain(struct omap_display_device *display, unsigned int buffer_count) { int fb_idx; /* TODO: What about TILER buffers */ if (buffer_count <= 1) { ERR_PRINT("Flip chains with %i buffers not supported", buffer_count); return 1; } else if (buffer_count > display->buffers_available) { ERR_PRINT("Requesting %i buffers when there is %i available" " for '%s'", buffer_count, display->buffers_available, display->name); return 1; } else if (display->flip_chain) { ERR_PRINT("Flip chain already exists for '%s'", display->name); return 1; } /* Create the flip chain with the framebuffer memory */ 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: fb_idx = 0; break; case OMAP_DISPID_BADSTATE: default: ERR_PRINT("Unknown display id %i", display->id); BUG(); } /* Use the framebuffer memory */ if (fb_idx >= 0 && fb_idx < num_registered_fb) { struct fb_info *framebuffer = registered_fb[fb_idx]; unsigned long buffer_size; struct omap_display_flip_chain *flip_chain; int i; if (!framebuffer || !framebuffer->fix.smem_start || !framebuffer->screen_base) { ERR_PRINT("Framebuffer %i doesn't seem to be " "initialized", fb_idx); return 1; } /* * Check if there is enough memory in the fb for the requested * buffers */ buffer_size = display->width * display->height * display->bytes_per_pixel; /* Page align the buffer size, round up to the page size */ buffer_size = OMAP_DISP_PAGE_ROUND_UP(buffer_size); if (buffer_size * buffer_count > framebuffer->fix.smem_len) { ERR_PRINT("Not enough memory to allocate %i buffers " "(%lu bytes each), memory available %lu for " "display '%s'", buffer_count, buffer_size, (unsigned long)framebuffer->fix.smem_len, display->name); return 1; } flip_chain = kzalloc(sizeof(*flip_chain), GFP_KERNEL); if (!flip_chain) { ERR_PRINT("Out of memory"); return 1; } for (i = 0; i < buffer_count; i++) { struct omap_display_buffer *buffer; /* * Reuse the main buffer as the first buffer in the * flip chain */ if (i == 0) { buffer = display->main_buffer; flip_chain->buffers[i] = buffer; DBG_PRINT("Flip chain buffer %i has address " "%lx for display '%s'", i, buffer->physical_addr, display->name); continue; } buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); if (!buffer) { /* * FIXME: If one buffer allocation fails, * deallocate flip chain and buffers */ ERR_PRINT("Out of memory"); return 1; } buffer->physical_addr = framebuffer->fix.smem_start + (buffer_size * i); buffer->virtual_addr = (unsigned long) framebuffer->screen_base + (buffer_size * i); buffer->size = buffer_size; buffer->display = display; flip_chain->buffers[i] = buffer; DBG_PRINT("Flip chain buffer %i has address %lx for" " display '%s'", i, buffer->physical_addr, display->name); } display->flip_chain = flip_chain; return 0; } else { ERR_PRINT("Framebuffer %i doesn't exist for display '%s'", fb_idx, display->name); return 1; } return 0; } static int destroy_flip_chain(struct omap_display_device *display) { int i; int err; if (!display->flip_chain) { DBG_PRINT("No flip chain to destroy for '%s'", display->name); return 0; } for (i = 0; i < display->flip_chain->buffer_count; i++) { struct omap_display_buffer *buffer = display->flip_chain->buffers[i]; /* If buffer is main buffer don't touch it */ if (display->main_buffer == buffer) continue; err = display_destroy_buffer(buffer); if (err) { ERR_PRINT("Error destroying buffer in flip chain for" " '%s'", display->name); return 1; } } DBG_PRINT("Destroying flip chain for '%s'", display->name); kfree(display->flip_chain); display->flip_chain = 0; return 0; } static int rotate_display(struct omap_display_device *display, unsigned int rotation) { ERR_PRINT("Not supported yet"); return 1; } static int display_destroy_buffer(struct omap_display_buffer *buffer) { kfree(buffer); return 0; } static int present_buffer_virtual(struct omap_display_buffer *buffer) { /* * TODO: Support for ORIENTATION_VERTICAL is in place, * ORIENTATION_HORIZONTAL is missing */ struct omap_display_device *display_virtual = buffer->display; struct omap_display_device *display_primary; struct omap_display_device *display_secondary; struct omap_display_buffer temp_buffer; unsigned int buffer_offset; if (display_virtual->id != OMAP_DISPID_VIRTUAL) { ERR_PRINT("Not a virtual display"); BUG(); } display_primary = omap_display_get(OMAP_DISPID_PRIMARY); display_secondary = omap_display_get(OMAP_DISPID_SECONDARY); /* * Calculate offset without page alignment round up otherwise second * display may see incorrect data */ buffer_offset = display_primary->height * display_virtual->byte_stride; /* The first buffer will be the base */ temp_buffer.physical_addr = buffer->physical_addr; temp_buffer.virtual_addr = buffer->virtual_addr; temp_buffer.size = buffer->size >> 1; if (display_virtual->features & ORIENTATION_INVERT) { /* Secondary display has the base */ temp_buffer.display = display_secondary; display_secondary->present_buffer(&temp_buffer); } else { /* Primary display has the base */ temp_buffer.display = display_primary; display_primary->present_buffer(&temp_buffer); } /* Remaining display will show the rest */ temp_buffer.physical_addr = buffer->physical_addr + buffer_offset; temp_buffer.virtual_addr = buffer->virtual_addr + buffer_offset; if (display_virtual->features & ORIENTATION_INVERT) { temp_buffer.display = display_primary; display_primary->present_buffer(&temp_buffer); } else { temp_buffer.display = display_secondary; display_secondary->present_buffer(&temp_buffer); } return 0; } 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) { 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; } framebuffer = registered_fb[fb_idx]; ofbi = FB2OFB(framebuffer); fbdev = ofbi->fbdev; omapfb_lock(fbdev); /* 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 = ofbi->overlays[i]; manager = overlay->manager; overlay->get_overlay_info(overlay, &overlay_info); 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; } 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); } } omapfb_unlock(fbdev); return 0; } static int present_buffer_sync(struct omap_display_buffer *buffer) { /* TODO: Cloning may tear with this implementation */ 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; 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; } 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); 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, * it will be simplified in the future. */ struct omap_display_device *display_virtual = buffer->display; struct omap_display_device *display_primary; struct omap_display_device *display_secondary; struct omap_display_buffer temp_buffer_top; struct omap_display_buffer temp_buffer_bottom; unsigned int buffer_offset; if (display_virtual->id != OMAP_DISPID_VIRTUAL) { ERR_PRINT("Not a virtual display"); BUG(); } display_primary = omap_display_get(OMAP_DISPID_PRIMARY); display_secondary = omap_display_get(OMAP_DISPID_SECONDARY); /* * Calculate offset without page alignment round up otherwise second * display may see incorrect data */ buffer_offset = display_primary->height * display_virtual->byte_stride; /* The first buffer will be the top */ temp_buffer_top.physical_addr = buffer->physical_addr; temp_buffer_top.virtual_addr = buffer->virtual_addr; temp_buffer_top.size = buffer->size >> 1; /* Then the bottom */ temp_buffer_bottom.physical_addr = buffer->physical_addr + buffer_offset; temp_buffer_bottom.virtual_addr = buffer->virtual_addr + buffer_offset; temp_buffer_bottom.size = buffer->size >> 1; if (display_virtual->features & ORIENTATION_INVERT) { /* Secondary display has the base */ temp_buffer_top.display = display_secondary; temp_buffer_bottom.display = display_primary; 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; vdisp_sync_primary.buffer = &temp_buffer_top; vdisp_sync_secondary.buffer = &temp_buffer_bottom; } /* 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 */ flush_workqueue(vdisp_wq_primary); flush_workqueue(vdisp_wq_secondary); return 0; } static int display_sync(struct omap_display_device *display) { /* TODO: Synchronize properly with multiple managers */ 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; } 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) { /* * 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; if (display_virtual->id != OMAP_DISPID_VIRTUAL) { ERR_PRINT("Not a virtual display"); BUG(); } display_primary = omap_display_get(OMAP_DISPID_PRIMARY); return display_primary->sync(display_primary); } static struct omap_display_buffer *create_main_buffer( struct omap_display_device *display) { int fb_idx; 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: /* Use fb0 for virtual display */ fb_idx = 0; break; case OMAP_DISPID_BADSTATE: default: ERR_PRINT("Unknown display id %i", display->id); BUG(); } /* Use the framebuffer memory */ if (fb_idx >= 0 && fb_idx < num_registered_fb) { struct fb_info *framebuffer = registered_fb[fb_idx]; unsigned long buffer_size; struct omap_display_buffer *buffer; if (!framebuffer || !framebuffer->fix.smem_start || !framebuffer->screen_base) { ERR_PRINT("Framebuffer %i doesn't seem to be " "initialized", fb_idx); return NULL; } /* * Check if there is enough memory in the fb for the * main buffer */ buffer_size = display->width * display->height * display->bytes_per_pixel; /* Page align the buffer size */ buffer_size = OMAP_DISP_PAGE_ROUND_UP(buffer_size); if (buffer_size > framebuffer->fix.smem_len) { ERR_PRINT("Main buffer needs %lu bytes while the " "framebuffer %i has only %lu bytes for display" " '%s'", buffer_size, fb_idx, (unsigned long)framebuffer->fix.smem_len, display->name); return NULL; } buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); if (!buffer) { ERR_PRINT("Out of memory"); return NULL; } /* Use base addresses reported by the framebuffer */ buffer->physical_addr = framebuffer->fix.smem_start; buffer->virtual_addr = (unsigned long) framebuffer->screen_base; buffer->size = buffer_size; buffer->display = display; DBG_PRINT("Created main buffer %lx for display '%s'", buffer->physical_addr, display->name); return buffer; } else { ERR_PRINT("Framebuffer %i doesn't exist for display '%s'", fb_idx, display->name); return NULL; } } static int populate_display_info(struct omap_display_device *display, struct omap_overlay_manager *overlay_manager) { struct omap_dss_device *dss_device = overlay_manager->device; u16 xres; u16 yres; int i; if (!strcmp(dss_device->name, "lcd")) { display->id = OMAP_DISPID_PRIMARY; display->name = "primary"; } else if (!strcmp(dss_device->name, "lcd2")) { display->id = OMAP_DISPID_SECONDARY; display->name = "secondary"; } else if (!strcmp(dss_device->name, "hdmi")) { display->id = OMAP_DISPID_TERTIARY; display->name = "tertiary"; } else { ERR_PRINT("Display id '%s' not supported", dss_device->name); return 1; } dss_device->driver->get_resolution(dss_device, &xres, &yres); if (xres == 0 || yres == 0) { ERR_PRINT("Unable to handle display '%s' with width %i " "and height %i", dss_device->name, xres, yres); return 1; } display->width = xres; display->height = yres; display->bits_per_pixel = dss_device->driver->get_recommended_bpp(dss_device); switch (display->bits_per_pixel) { case 16: /* * TODO: Asume RGB_565, maybe need to double check in * the DSS if this is true */ display->pixel_format = RGB_565; display->bytes_per_pixel = 2; break; case 24: /* 24 bits are encapsulated with 32 bits */ case 32: /* * TODO: Asume ARGB_8888, maybe need to double check in * the DSS if this is true */ display->pixel_format = ARGB_8888; display->bytes_per_pixel = 4; break; default: ERR_PRINT("Unable to handle %i bpp", display->bits_per_pixel); return 1; } display->byte_stride = display->bytes_per_pixel * display->width; display->rotation = OMAP_DSS_ROT_0; /* Asume rotation 0 degrees */ display->main_buffer = 0; display->flip_chain = 0; /* Add the manager to the list */ for (i = 0; i < OMAP_DISP_MAX_MANAGERS; i++) display->overlay_managers[i] = 0; display->overlay_managers[0] = overlay_manager; display->overlay_managers_count = 1; /* Assign function pointers for display operations */ display->open = open_display; display->close = close_display; display->create_flip_chain = create_flip_chain; display->destroy_flip_chain = destroy_flip_chain; display->rotate = rotate_display; display->present_buffer = present_buffer; display->sync = display_sync; display->present_buffer_sync = present_buffer_sync; display->main_buffer = create_main_buffer(display); if (!display->main_buffer) WRN_PRINT("Failed to create main buffer for '%s'", display->name); display->buffers_available = get_max_buffers(display); /* Just print some display info */ DBG_PRINT("Found display '%s-%s' (%i,%i) %i bpp (%i bytes per pixel)" " rotation %i", display->name, dss_device->name, display->width, display->height, display->bits_per_pixel, display->bytes_per_pixel, display->rotation); return 0; } static int populate_virtual_display_info(struct omap_display_device *display) { struct omap_display_device *display_primary ; struct omap_display_device *display_secondary; int i; display->id = OMAP_DISPID_VIRTUAL; display->name = "virtual"; display_primary = omap_display_get(OMAP_DISPID_PRIMARY); display_secondary = omap_display_get(OMAP_DISPID_SECONDARY); if (!display_primary) { ERR_PRINT("Primary display doesn't exist"); return 1; } else if (!display_secondary) { ERR_PRINT("Secondary display doesn't exist"); return 1; } /* Combine primary and secondary display resolutions */ if (display_primary->width != display_secondary->width || display_primary->height != display_secondary->height) { ERR_PRINT("Primary and seconday displays resolution are not" " the same"); return 1; } /* * TODO: Here it is hardcoded the resolution asumming a vertical * virtual config, what about horizontal? */ display->width = display_primary->width; display->height = display_primary->height * 2; if (display_primary->bits_per_pixel != display_secondary->bits_per_pixel) { ERR_PRINT("Primary and seconday displays format are" " not the same"); return 1; } display->bits_per_pixel = display_primary->bits_per_pixel; switch (display->bits_per_pixel) { case 16: /* * TODO: Asume RGB_565, maybe need to double check in * the DSS if this is true */ display->pixel_format = RGB_565; display->bytes_per_pixel = 2; break; case 24: /* 24 bits are encapsulated with 32 bits */ case 32: /* * TODO: Asume ARGB_8888, maybe need to double check in * the DSS if this is true */ display->pixel_format = ARGB_8888; display->bytes_per_pixel = 4; break; default: ERR_PRINT("Unable to handle %i bpp", display->bits_per_pixel); return 1; } /* TODO: Asumming a vertical virtual config too for stride */ display->byte_stride = display->bytes_per_pixel * display->width; display->rotation = OMAP_DSS_ROT_0; /* Asume rotation 0 degrees */ display->main_buffer = 0; display->flip_chain = 0; /* Add the primary and secondary overlay managers */ for (i = 0; i < OMAP_DISP_MAX_MANAGERS; i++) display->overlay_managers[i] = 0; display->overlay_managers[0] = display_primary->overlay_managers[0]; display->overlay_managers[1] = display_secondary->overlay_managers[0]; display->overlay_managers_count = 2; /* Assign function pointers for display operations */ display->open = open_display; display->close = close_display; display->create_flip_chain = create_flip_chain; display->destroy_flip_chain = destroy_flip_chain; display->rotate = rotate_display; display->present_buffer = present_buffer_virtual; display->sync = display_sync_virtual; display->present_buffer_sync = present_buffer_sync_virtual; display->main_buffer = create_main_buffer(display); if (!display->main_buffer) WRN_PRINT("Failed to create main buffer for '%s'", display->name); display->buffers_available = get_max_buffers(display); /* Just print some display info */ DBG_PRINT("Found display '%s' (%i,%i) %i bpp (%i bytes per pixel)" " rotation %i", display->name, display->width, display->height, display->bits_per_pixel, display->bytes_per_pixel, display->rotation); return 0; } static int create_display_list(void) { int i; struct omap_display_device *display; /* Query number of possible displays available first */ omap_display_number = omap_dss_get_num_overlay_managers(); /* For virtual display */ omap_display_number++; /* Allocate custom display list */ omap_display_list = kzalloc( sizeof(*display) * omap_display_number, GFP_KERNEL); if (!omap_display_list) { ERR_PRINT("Out of memory"); return 1; } /* Populate each display info */ for (i = 0; i < omap_display_number - 1; i++) { struct omap_overlay_manager *overlay_manager = omap_dss_get_overlay_manager(i); display = &omap_display_list[i]; if (!overlay_manager->device) { WRN_PRINT("Display '%s' doesn't have a dss device " "attached to it, ignoring", overlay_manager->name); display->id = OMAP_DISPID_BADSTATE; continue; } if (populate_display_info(display, overlay_manager)) { ERR_PRINT("Error populating display %i info with " "manager '%s'", i, overlay_manager->device->name); display->id = OMAP_DISPID_BADSTATE; continue; } } /* Populate virtual display */ display = &omap_display_list[omap_display_number - 1]; if (populate_virtual_display_info(display)) { ERR_PRINT("Error populating virtual display info"); display->id = OMAP_DISPID_BADSTATE; } return 0; } struct omap_display_device *omap_display_get(enum omap_display_id id) { int i; struct omap_display_device *display; if (id == OMAP_DISPID_BADSTATE) { ERR_PRINT("Oops.. user must never request a bad display"); BUG(); } for (i = 0; i < omap_display_number; i++) { display = &omap_display_list[i]; if (display->id == id) return display; } ERR_PRINT("Unknown display %i requested", id); return 0; } EXPORT_SYMBOL(omap_display_get); int omap_display_count(void) { return omap_display_number; } EXPORT_SYMBOL(omap_display_count); int omap_display_initialize(void) { /* * TODO: Is there a better way to check if list is already created? */ if (!omap_display_list) { DBG_PRINT("Initializing driver"); if (create_display_list()) { ERR_PRINT("Error loading driver"); 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); int omap_display_deinitialize(void) { int i; int err = 0; DBG_PRINT("Driver exiting"); for (i = 0; i < omap_display_number; i++) { struct omap_display_device *display = &omap_display_list[i]; if (!display) continue; if (display->main_buffer) { err = display_destroy_buffer(display->main_buffer); display->main_buffer = 0; if (err) WRN_PRINT("An error happened when destroying " "main buffer for '%s'", display->name); } err = display->close(display); if (err) ERR_PRINT("Unable to close display '%s'", display->name); } 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);