diff options
Diffstat (limited to 'drivers/gpu/pvr/display/omap_display.c')
-rw-r--r-- | drivers/gpu/pvr/display/omap_display.c | 1115 |
1 files changed, 1115 insertions, 0 deletions
diff --git a/drivers/gpu/pvr/display/omap_display.c b/drivers/gpu/pvr/display/omap_display.c new file mode 100644 index 0000000..d385908 --- /dev/null +++ b/drivers/gpu/pvr/display/omap_display.c @@ -0,0 +1,1115 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/fb.h> + +#include <plat/vrfb.h> +#include <plat/display.h> + +/* 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); + |