blob: d3859084b83f4f126d3f684115025f3def871f81 [file] [log] [blame]
/*
* 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);