/* * 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 /* 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_DEBUG 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; /* 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 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; 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) { 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; int i; omapfb_lock(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); /* If the overlay is not enabled don't update it */ if (!overlay_info.enabled) continue; overlay_info.paddr = buffer->physical_addr; overlay_info.vaddr = (void *) buffer->virtual_addr; overlay->set_overlay_info(overlay, &overlay_info); if (overlay->manager) { overlay->manager->apply(overlay->manager); if (overlay->manager->device && overlay->manager->device->update) { overlay->manager->device->update( overlay->manager->device, 0, 0, overlay_info.width, overlay_info.height); } } } omapfb_unlock(fbdev); } else { ERR_PRINT("Framebuffer %i doesn't exist for display '%s'", fb_idx, display->name); return 1; } return 0; } 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; } 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; err = display->sync(display); err |= display->present_buffer(buffer); return err; } 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); } void display_irq_wait_2(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); } /* * 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; 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) { 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; sync_item_primary.buffer = &temp_buffer_bottom; sync_item_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; } 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)); /* 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)); return 0; } static int display_sync(struct omap_display_device *display) { /* TODO: Synchronize properly with multiple managers */ struct omap_dss_device *dss_device = display->overlay_managers[0]->device; if (!dss_device || !dss_device->wait_vsync) { ERR_PRINT("Unable to synchronize with '%s'", display->name); return 1; } dss_device->wait_vsync(dss_device); return 0; } 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 */ 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"); BUG(); } 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; } 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, "2lcd")) { 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->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->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; unsigned int bytes_to_alloc; 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_init(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; } } return 0; } EXPORT_SYMBOL(omap_display_init); int omap_display_deinit(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; return err; } EXPORT_SYMBOL(omap_display_deinit);