diff options
Diffstat (limited to 'recipes/linux/linux-omap-2.6.31/dss2/0008-OMAP-DSS2-Add-more-core-files.patch')
-rw-r--r-- | recipes/linux/linux-omap-2.6.31/dss2/0008-OMAP-DSS2-Add-more-core-files.patch | 2856 |
1 files changed, 2856 insertions, 0 deletions
diff --git a/recipes/linux/linux-omap-2.6.31/dss2/0008-OMAP-DSS2-Add-more-core-files.patch b/recipes/linux/linux-omap-2.6.31/dss2/0008-OMAP-DSS2-Add-more-core-files.patch new file mode 100644 index 0000000000..3237d49eae --- /dev/null +++ b/recipes/linux/linux-omap-2.6.31/dss2/0008-OMAP-DSS2-Add-more-core-files.patch @@ -0,0 +1,2856 @@ +From 9e37324c13a58b705b85e63b0779f7efbd94342a Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen <tomi.valkeinen@nokia.com> +Date: Fri, 7 Aug 2009 13:43:20 +0300 +Subject: [PATCH 08/18] OMAP: DSS2: Add more core files + +Add more core files to DSS2. + +Signed-off-by: Tomi Valkeinen <tomi.valkeinen@nokia.com> +--- + drivers/video/omap2/dss/display.c | 658 ++++++++++++++++ + drivers/video/omap2/dss/manager.c | 1487 +++++++++++++++++++++++++++++++++++++ + drivers/video/omap2/dss/overlay.c | 673 +++++++++++++++++ + 3 files changed, 2818 insertions(+), 0 deletions(-) + create mode 100644 drivers/video/omap2/dss/display.c + create mode 100644 drivers/video/omap2/dss/manager.c + create mode 100644 drivers/video/omap2/dss/overlay.c + +diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c +new file mode 100644 +index 0000000..6b5d0cf +--- /dev/null ++++ b/drivers/video/omap2/dss/display.c +@@ -0,0 +1,658 @@ ++/* ++ * linux/drivers/video/omap2/dss/display.c ++ * ++ * Copyright (C) 2009 Nokia Corporation ++ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> ++ * ++ * Some code and ideas taken from drivers/video/omap/ driver ++ * by Imre Deak. ++ * ++ * 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/>. ++ */ ++ ++#define DSS_SUBSYS_NAME "DISPLAY" ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/jiffies.h> ++#include <linux/list.h> ++#include <linux/platform_device.h> ++ ++#include <mach/display.h> ++#include "dss.h" ++ ++static LIST_HEAD(display_list); ++ ++static ssize_t display_enabled_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ bool enabled = dssdev->state != OMAP_DSS_DISPLAY_DISABLED; ++ ++ return snprintf(buf, PAGE_SIZE, "%d\n", enabled); ++} ++ ++static ssize_t display_enabled_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ bool enabled, r; ++ ++ enabled = simple_strtoul(buf, NULL, 10); ++ ++ if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) { ++ if (enabled) { ++ r = dssdev->enable(dssdev); ++ if (r) ++ return r; ++ } else { ++ dssdev->disable(dssdev); ++ } ++ } ++ ++ return size; ++} ++ ++static ssize_t display_upd_mode_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ enum omap_dss_update_mode mode = OMAP_DSS_UPDATE_AUTO; ++ if (dssdev->get_update_mode) ++ mode = dssdev->get_update_mode(dssdev); ++ return snprintf(buf, PAGE_SIZE, "%d\n", mode); ++} ++ ++static ssize_t display_upd_mode_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ int val, r; ++ enum omap_dss_update_mode mode; ++ ++ val = simple_strtoul(buf, NULL, 10); ++ ++ switch (val) { ++ case OMAP_DSS_UPDATE_DISABLED: ++ case OMAP_DSS_UPDATE_AUTO: ++ case OMAP_DSS_UPDATE_MANUAL: ++ mode = (enum omap_dss_update_mode)val; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ r = dssdev->set_update_mode(dssdev, mode); ++ if (r) ++ return r; ++ ++ return size; ++} ++ ++static ssize_t display_tear_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ return snprintf(buf, PAGE_SIZE, "%d\n", ++ dssdev->get_te ? dssdev->get_te(dssdev) : 0); ++} ++ ++static ssize_t display_tear_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ unsigned long te; ++ int r; ++ ++ if (!dssdev->enable_te || !dssdev->get_te) ++ return -ENOENT; ++ ++ te = simple_strtoul(buf, NULL, 0); ++ ++ r = dssdev->enable_te(dssdev, te); ++ if (r) ++ return r; ++ ++ return size; ++} ++ ++static ssize_t display_timings_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ struct omap_video_timings t; ++ ++ if (!dssdev->get_timings) ++ return -ENOENT; ++ ++ dssdev->get_timings(dssdev, &t); ++ ++ return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n", ++ t.pixel_clock, ++ t.x_res, t.hfp, t.hbp, t.hsw, ++ t.y_res, t.vfp, t.vbp, t.vsw); ++} ++ ++static ssize_t display_timings_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ struct omap_video_timings t; ++ int r, found; ++ ++ if (!dssdev->set_timings || !dssdev->check_timings) ++ return -ENOENT; ++ ++ found = 0; ++#ifdef CONFIG_OMAP2_DSS_VENC ++ if (strncmp("pal", buf, 3) == 0) { ++ t = omap_dss_pal_timings; ++ found = 1; ++ } else if (strncmp("ntsc", buf, 4) == 0) { ++ t = omap_dss_ntsc_timings; ++ found = 1; ++ } ++#endif ++ if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu", ++ &t.pixel_clock, ++ &t.x_res, &t.hfp, &t.hbp, &t.hsw, ++ &t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9) ++ return -EINVAL; ++ ++ r = dssdev->check_timings(dssdev, &t); ++ if (r) ++ return r; ++ ++ dssdev->set_timings(dssdev, &t); ++ ++ return size; ++} ++ ++static ssize_t display_rotate_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ int rotate; ++ if (!dssdev->get_rotate) ++ return -ENOENT; ++ rotate = dssdev->get_rotate(dssdev); ++ return snprintf(buf, PAGE_SIZE, "%u\n", rotate); ++} ++ ++static ssize_t display_rotate_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ unsigned long rot; ++ int r; ++ ++ if (!dssdev->set_rotate || !dssdev->get_rotate) ++ return -ENOENT; ++ ++ rot = simple_strtoul(buf, NULL, 0); ++ ++ r = dssdev->set_rotate(dssdev, rot); ++ if (r) ++ return r; ++ ++ return size; ++} ++ ++static ssize_t display_mirror_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ int mirror; ++ if (!dssdev->get_mirror) ++ return -ENOENT; ++ mirror = dssdev->get_mirror(dssdev); ++ return snprintf(buf, PAGE_SIZE, "%u\n", mirror); ++} ++ ++static ssize_t display_mirror_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ unsigned long mirror; ++ int r; ++ ++ if (!dssdev->set_mirror || !dssdev->get_mirror) ++ return -ENOENT; ++ ++ mirror = simple_strtoul(buf, NULL, 0); ++ ++ r = dssdev->set_mirror(dssdev, mirror); ++ if (r) ++ return r; ++ ++ return size; ++} ++ ++static ssize_t display_wss_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ unsigned int wss; ++ ++ if (!dssdev->get_wss) ++ return -ENOENT; ++ ++ wss = dssdev->get_wss(dssdev); ++ ++ return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss); ++} ++ ++static ssize_t display_wss_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t size) ++{ ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ unsigned long wss; ++ int r; ++ ++ if (!dssdev->get_wss || !dssdev->set_wss) ++ return -ENOENT; ++ ++ if (strict_strtoul(buf, 0, &wss)) ++ return -EINVAL; ++ ++ if (wss > 0xfffff) ++ return -EINVAL; ++ ++ r = dssdev->set_wss(dssdev, wss); ++ if (r) ++ return r; ++ ++ return size; ++} ++ ++static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR, ++ display_enabled_show, display_enabled_store); ++static DEVICE_ATTR(update_mode, S_IRUGO|S_IWUSR, ++ display_upd_mode_show, display_upd_mode_store); ++static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR, ++ display_tear_show, display_tear_store); ++static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR, ++ display_timings_show, display_timings_store); ++static DEVICE_ATTR(rotate, S_IRUGO|S_IWUSR, ++ display_rotate_show, display_rotate_store); ++static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR, ++ display_mirror_show, display_mirror_store); ++static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR, ++ display_wss_show, display_wss_store); ++ ++static struct device_attribute *display_sysfs_attrs[] = { ++ &dev_attr_enabled, ++ &dev_attr_update_mode, ++ &dev_attr_tear_elim, ++ &dev_attr_timings, ++ &dev_attr_rotate, ++ &dev_attr_mirror, ++ &dev_attr_wss, ++ NULL ++}; ++ ++static void default_get_resolution(struct omap_dss_device *dssdev, ++ u16 *xres, u16 *yres) ++{ ++ *xres = dssdev->panel.timings.x_res; ++ *yres = dssdev->panel.timings.y_res; ++} ++ ++void default_get_overlay_fifo_thresholds(enum omap_plane plane, ++ u32 fifo_size, enum omap_burst_size *burst_size, ++ u32 *fifo_low, u32 *fifo_high) ++{ ++ unsigned burst_size_bytes; ++ ++ *burst_size = OMAP_DSS_BURST_16x32; ++ burst_size_bytes = 16 * 32 / 8; ++ ++ *fifo_high = fifo_size - 1; ++ *fifo_low = fifo_size - burst_size_bytes; ++} ++ ++static int default_wait_vsync(struct omap_dss_device *dssdev) ++{ ++ unsigned long timeout = msecs_to_jiffies(500); ++ u32 irq; ++ ++ if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) ++ irq = DISPC_IRQ_EVSYNC_ODD; ++ else ++ irq = DISPC_IRQ_VSYNC; ++ ++ return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); ++} ++ ++static int default_get_recommended_bpp(struct omap_dss_device *dssdev) ++{ ++ if (dssdev->panel.recommended_bpp) ++ return dssdev->panel.recommended_bpp; ++ ++ switch (dssdev->type) { ++ case OMAP_DISPLAY_TYPE_DPI: ++ if (dssdev->phy.dpi.data_lines == 24) ++ return 24; ++ else ++ return 16; ++ ++ case OMAP_DISPLAY_TYPE_DBI: ++ case OMAP_DISPLAY_TYPE_DSI: ++ if (dssdev->ctrl.pixel_size == 24) ++ return 24; ++ else ++ return 16; ++ case OMAP_DISPLAY_TYPE_VENC: ++ case OMAP_DISPLAY_TYPE_SDI: ++ return 24; ++ return 24; ++ default: ++ BUG(); ++ } ++} ++ ++/* Checks if replication logic should be used. Only use for active matrix, ++ * when overlay is in RGB12U or RGB16 mode, and LCD interface is ++ * 18bpp or 24bpp */ ++bool dss_use_replication(struct omap_dss_device *dssdev, ++ enum omap_color_mode mode) ++{ ++ int bpp; ++ ++ if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16) ++ return false; ++ ++ if (dssdev->type == OMAP_DISPLAY_TYPE_DPI && ++ (dssdev->panel.config & OMAP_DSS_LCD_TFT) == 0) ++ return false; ++ ++ switch (dssdev->type) { ++ case OMAP_DISPLAY_TYPE_DPI: ++ bpp = dssdev->phy.dpi.data_lines; ++ break; ++ case OMAP_DISPLAY_TYPE_VENC: ++ case OMAP_DISPLAY_TYPE_SDI: ++ bpp = 24; ++ break; ++ case OMAP_DISPLAY_TYPE_DBI: ++ case OMAP_DISPLAY_TYPE_DSI: ++ bpp = dssdev->ctrl.pixel_size; ++ break; ++ default: ++ BUG(); ++ } ++ ++ return bpp > 16; ++} ++ ++void dss_init_device(struct platform_device *pdev, ++ struct omap_dss_device *dssdev) ++{ ++ struct device_attribute *attr; ++ int i; ++ int r; ++ ++ switch (dssdev->type) { ++ case OMAP_DISPLAY_TYPE_DPI: ++#ifdef CONFIG_OMAP2_DSS_RFBI ++ case OMAP_DISPLAY_TYPE_DBI: ++#endif ++#ifdef CONFIG_OMAP2_DSS_SDI ++ case OMAP_DISPLAY_TYPE_SDI: ++#endif ++#ifdef CONFIG_OMAP2_DSS_DSI ++ case OMAP_DISPLAY_TYPE_DSI: ++#endif ++#ifdef CONFIG_OMAP2_DSS_VENC ++ case OMAP_DISPLAY_TYPE_VENC: ++#endif ++ break; ++ default: ++ DSSERR("Support for display '%s' not compiled in.\n", ++ dssdev->name); ++ return; ++ } ++ ++ dssdev->get_resolution = default_get_resolution; ++ dssdev->get_recommended_bpp = default_get_recommended_bpp; ++ dssdev->wait_vsync = default_wait_vsync; ++ ++ switch (dssdev->type) { ++ case OMAP_DISPLAY_TYPE_DPI: ++ r = dpi_init_display(dssdev); ++ break; ++#ifdef CONFIG_OMAP2_DSS_RFBI ++ case OMAP_DISPLAY_TYPE_DBI: ++ r = rfbi_init_display(dssdev); ++ break; ++#endif ++#ifdef CONFIG_OMAP2_DSS_VENC ++ case OMAP_DISPLAY_TYPE_VENC: ++ r = venc_init_display(dssdev); ++ break; ++#endif ++#ifdef CONFIG_OMAP2_DSS_SDI ++ case OMAP_DISPLAY_TYPE_SDI: ++ r = sdi_init_display(dssdev); ++ break; ++#endif ++#ifdef CONFIG_OMAP2_DSS_DSI ++ case OMAP_DISPLAY_TYPE_DSI: ++ r = dsi_init_display(dssdev); ++ break; ++#endif ++ default: ++ BUG(); ++ } ++ ++ if (r) { ++ DSSERR("failed to init display %s\n", dssdev->name); ++ return; ++ } ++ ++ /* create device sysfs files */ ++ i = 0; ++ while ((attr = display_sysfs_attrs[i++]) != NULL) { ++ r = device_create_file(&dssdev->dev, attr); ++ if (r) ++ DSSERR("failed to create sysfs file\n"); ++ } ++ ++ /* create display? sysfs links */ ++ r = sysfs_create_link(&pdev->dev.kobj, &dssdev->dev.kobj, ++ dev_name(&dssdev->dev)); ++ if (r) ++ DSSERR("failed to create sysfs display link\n"); ++} ++ ++void dss_uninit_device(struct platform_device *pdev, ++ struct omap_dss_device *dssdev) ++{ ++ struct device_attribute *attr; ++ int i = 0; ++ ++ sysfs_remove_link(&pdev->dev.kobj, dev_name(&dssdev->dev)); ++ ++ while ((attr = display_sysfs_attrs[i++]) != NULL) ++ device_remove_file(&dssdev->dev, attr); ++ ++ if (dssdev->manager) ++ dssdev->manager->unset_device(dssdev->manager); ++} ++ ++static int dss_suspend_device(struct device *dev, void *data) ++{ ++ int r; ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ ++ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { ++ dssdev->activate_after_resume = false; ++ return 0; ++ } ++ ++ if (!dssdev->suspend) { ++ DSSERR("display '%s' doesn't implement suspend\n", ++ dssdev->name); ++ return -ENOSYS; ++ } ++ ++ r = dssdev->suspend(dssdev); ++ if (r) ++ return r; ++ ++ dssdev->activate_after_resume = true; ++ ++ return 0; ++} ++ ++int dss_suspend_all_devices(void) ++{ ++ int r; ++ struct bus_type *bus = dss_get_bus(); ++ ++ r = bus_for_each_dev(bus, NULL, NULL, dss_suspend_device); ++ if (r) { ++ /* resume all displays that were suspended */ ++ dss_resume_all_devices(); ++ return r; ++ } ++ ++ return 0; ++} ++ ++static int dss_resume_device(struct device *dev, void *data) ++{ ++ int r; ++ struct omap_dss_device *dssdev = to_dss_device(dev); ++ ++ if (dssdev->activate_after_resume && dssdev->resume) { ++ r = dssdev->resume(dssdev); ++ if (r) ++ return r; ++ } ++ ++ dssdev->activate_after_resume = false; ++ ++ return 0; ++} ++ ++int dss_resume_all_devices(void) ++{ ++ struct bus_type *bus = dss_get_bus(); ++ ++ return bus_for_each_dev(bus, NULL, NULL, dss_resume_device); ++} ++ ++ ++void omap_dss_get_device(struct omap_dss_device *dssdev) ++{ ++ get_device(&dssdev->dev); ++} ++EXPORT_SYMBOL(omap_dss_get_device); ++ ++void omap_dss_put_device(struct omap_dss_device *dssdev) ++{ ++ put_device(&dssdev->dev); ++} ++EXPORT_SYMBOL(omap_dss_put_device); ++ ++/* ref count of the found device is incremented. ref count ++ * of from-device is decremented. */ ++struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from) ++{ ++ struct device *dev; ++ struct device *dev_start = NULL; ++ struct omap_dss_device *dssdev = NULL; ++ ++ int match(struct device *dev, void *data) ++ { ++ /* skip panels connected to controllers */ ++ if (to_dss_device(dev)->panel.ctrl) ++ return 0; ++ ++ return 1; ++ } ++ ++ if (from) ++ dev_start = &from->dev; ++ dev = bus_find_device(dss_get_bus(), dev_start, NULL, match); ++ if (dev) ++ dssdev = to_dss_device(dev); ++ if (from) ++ put_device(&from->dev); ++ ++ return dssdev; ++} ++EXPORT_SYMBOL(omap_dss_get_next_device); ++ ++struct omap_dss_device *omap_dss_find_device(void *data, ++ int (*match)(struct omap_dss_device *dssdev, void *data)) ++{ ++ struct omap_dss_device *dssdev = NULL; ++ ++ while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) { ++ if (match(dssdev, data)) ++ return dssdev; ++ } ++ ++ return NULL; ++} ++EXPORT_SYMBOL(omap_dss_find_device); ++ ++int omap_dss_start_device(struct omap_dss_device *dssdev) ++{ ++ int r; ++ ++ if (!dssdev->driver) { ++ DSSDBG("no driver\n"); ++ r = -ENODEV; ++ goto err0; ++ } ++ ++ if (dssdev->ctrl.panel && !dssdev->ctrl.panel->driver) { ++ DSSDBG("no panel driver\n"); ++ r = -ENODEV; ++ goto err0; ++ } ++ ++ if (!try_module_get(dssdev->dev.driver->owner)) { ++ r = -ENODEV; ++ goto err0; ++ } ++ ++ if (dssdev->ctrl.panel) { ++ if (!try_module_get(dssdev->ctrl.panel->dev.driver->owner)) { ++ r = -ENODEV; ++ goto err1; ++ } ++ } ++ ++ return 0; ++err1: ++ module_put(dssdev->dev.driver->owner); ++err0: ++ return r; ++} ++EXPORT_SYMBOL(omap_dss_start_device); ++ ++void omap_dss_stop_device(struct omap_dss_device *dssdev) ++{ ++ if (dssdev->ctrl.panel) ++ module_put(dssdev->ctrl.panel->dev.driver->owner); ++ ++ module_put(dssdev->dev.driver->owner); ++} ++EXPORT_SYMBOL(omap_dss_stop_device); ++ +diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c +new file mode 100644 +index 0000000..7ee0b2b +--- /dev/null ++++ b/drivers/video/omap2/dss/manager.c +@@ -0,0 +1,1487 @@ ++/* ++ * linux/drivers/video/omap2/dss/manager.c ++ * ++ * Copyright (C) 2009 Nokia Corporation ++ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> ++ * ++ * Some code and ideas taken from drivers/video/omap/ driver ++ * by Imre Deak. ++ * ++ * 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/>. ++ */ ++ ++#define DSS_SUBSYS_NAME "MANAGER" ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/spinlock.h> ++#include <linux/jiffies.h> ++ ++#include <mach/display.h> ++#include <mach/cpu.h> ++ ++#include "dss.h" ++ ++static int num_managers; ++static struct list_head manager_list; ++ ++static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name); ++} ++ ++static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%s\n", ++ mgr->device ? mgr->device->name : "<none>"); ++} ++ ++static ssize_t manager_display_store(struct omap_overlay_manager *mgr, ++ const char *buf, size_t size) ++{ ++ int r = 0; ++ size_t len = size; ++ struct omap_dss_device *dssdev = NULL; ++ ++ int match(struct omap_dss_device *dssdev, void *data) ++ { ++ const char *str = data; ++ return sysfs_streq(dssdev->name, str); ++ } ++ ++ if (buf[size-1] == '\n') ++ --len; ++ ++ if (len > 0) ++ dssdev = omap_dss_find_device((void *)buf, match); ++ ++ if (len > 0 && dssdev == NULL) ++ return -EINVAL; ++ ++ if (dssdev) ++ DSSDBG("display %s found\n", dssdev->name); ++ ++ if (mgr->device) { ++ r = mgr->unset_device(mgr); ++ if (r) { ++ DSSERR("failed to unset display\n"); ++ goto put_device; ++ } ++ } ++ ++ if (dssdev) { ++ r = mgr->set_device(mgr, dssdev); ++ if (r) { ++ DSSERR("failed to set manager\n"); ++ goto put_device; ++ } ++ ++ r = mgr->apply(mgr); ++ if (r) { ++ DSSERR("failed to apply dispc config\n"); ++ goto put_device; ++ } ++ } ++ ++put_device: ++ if (dssdev) ++ omap_dss_put_device(dssdev); ++ ++ return r ? r : size; ++} ++ ++static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr, ++ char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.default_color); ++} ++ ++static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr, ++ const char *buf, size_t size) ++{ ++ struct omap_overlay_manager_info info; ++ u32 color; ++ int r; ++ ++ if (sscanf(buf, "%d", &color) != 1) ++ return -EINVAL; ++ ++ mgr->get_manager_info(mgr, &info); ++ ++ info.default_color = color; ++ ++ r = mgr->set_manager_info(mgr, &info); ++ if (r) ++ return r; ++ ++ r = mgr->apply(mgr); ++ if (r) ++ return r; ++ ++ return size; ++} ++ ++static const char *trans_key_type_str[] = { ++ "gfx-destination", ++ "video-source", ++}; ++ ++static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr, ++ char *buf) ++{ ++ enum omap_dss_trans_key_type key_type; ++ ++ key_type = mgr->info.trans_key_type; ++ BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str)); ++ ++ return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]); ++} ++ ++static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr, ++ const char *buf, size_t size) ++{ ++ enum omap_dss_trans_key_type key_type; ++ struct omap_overlay_manager_info info; ++ int r; ++ ++ for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST; ++ key_type < ARRAY_SIZE(trans_key_type_str); key_type++) { ++ if (sysfs_streq(buf, trans_key_type_str[key_type])) ++ break; ++ } ++ ++ if (key_type == ARRAY_SIZE(trans_key_type_str)) ++ return -EINVAL; ++ ++ mgr->get_manager_info(mgr, &info); ++ ++ info.trans_key_type = key_type; ++ ++ r = mgr->set_manager_info(mgr, &info); ++ if (r) ++ return r; ++ ++ r = mgr->apply(mgr); ++ if (r) ++ return r; ++ ++ return size; ++} ++ ++static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr, ++ char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_key); ++} ++ ++static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr, ++ const char *buf, size_t size) ++{ ++ struct omap_overlay_manager_info info; ++ u32 key_value; ++ int r; ++ ++ if (sscanf(buf, "%d", &key_value) != 1) ++ return -EINVAL; ++ ++ mgr->get_manager_info(mgr, &info); ++ ++ info.trans_key = key_value; ++ ++ r = mgr->set_manager_info(mgr, &info); ++ if (r) ++ return r; ++ ++ r = mgr->apply(mgr); ++ if (r) ++ return r; ++ ++ return size; ++} ++ ++static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr, ++ char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_enabled); ++} ++ ++static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr, ++ const char *buf, size_t size) ++{ ++ struct omap_overlay_manager_info info; ++ int enable; ++ int r; ++ ++ if (sscanf(buf, "%d", &enable) != 1) ++ return -EINVAL; ++ ++ mgr->get_manager_info(mgr, &info); ++ ++ info.trans_enabled = enable ? true : false; ++ ++ r = mgr->set_manager_info(mgr, &info); ++ if (r) ++ return r; ++ ++ r = mgr->apply(mgr); ++ if (r) ++ return r; ++ ++ return size; ++} ++ ++static ssize_t manager_alpha_blending_enabled_show( ++ struct omap_overlay_manager *mgr, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.alpha_enabled); ++} ++ ++static ssize_t manager_alpha_blending_enabled_store( ++ struct omap_overlay_manager *mgr, ++ const char *buf, size_t size) ++{ ++ struct omap_overlay_manager_info info; ++ int enable; ++ int r; ++ ++ if (sscanf(buf, "%d", &enable) != 1) ++ return -EINVAL; ++ ++ mgr->get_manager_info(mgr, &info); ++ ++ info.alpha_enabled = enable ? true : false; ++ ++ r = mgr->set_manager_info(mgr, &info); ++ if (r) ++ return r; ++ ++ r = mgr->apply(mgr); ++ if (r) ++ return r; ++ ++ return size; ++} ++ ++struct manager_attribute { ++ struct attribute attr; ++ ssize_t (*show)(struct omap_overlay_manager *, char *); ++ ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t); ++}; ++ ++#define MANAGER_ATTR(_name, _mode, _show, _store) \ ++ struct manager_attribute manager_attr_##_name = \ ++ __ATTR(_name, _mode, _show, _store) ++ ++static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL); ++static MANAGER_ATTR(display, S_IRUGO|S_IWUSR, ++ manager_display_show, manager_display_store); ++static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR, ++ manager_default_color_show, manager_default_color_store); ++static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR, ++ manager_trans_key_type_show, manager_trans_key_type_store); ++static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR, ++ manager_trans_key_value_show, manager_trans_key_value_store); ++static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR, ++ manager_trans_key_enabled_show, ++ manager_trans_key_enabled_store); ++static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR, ++ manager_alpha_blending_enabled_show, ++ manager_alpha_blending_enabled_store); ++ ++ ++static struct attribute *manager_sysfs_attrs[] = { ++ &manager_attr_name.attr, ++ &manager_attr_display.attr, ++ &manager_attr_default_color.attr, ++ &manager_attr_trans_key_type.attr, ++ &manager_attr_trans_key_value.attr, ++ &manager_attr_trans_key_enabled.attr, ++ &manager_attr_alpha_blending_enabled.attr, ++ NULL ++}; ++ ++static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr, ++ char *buf) ++{ ++ struct omap_overlay_manager *manager; ++ struct manager_attribute *manager_attr; ++ ++ manager = container_of(kobj, struct omap_overlay_manager, kobj); ++ manager_attr = container_of(attr, struct manager_attribute, attr); ++ ++ if (!manager_attr->show) ++ return -ENOENT; ++ ++ return manager_attr->show(manager, buf); ++} ++ ++static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct omap_overlay_manager *manager; ++ struct manager_attribute *manager_attr; ++ ++ manager = container_of(kobj, struct omap_overlay_manager, kobj); ++ manager_attr = container_of(attr, struct manager_attribute, attr); ++ ++ if (!manager_attr->store) ++ return -ENOENT; ++ ++ return manager_attr->store(manager, buf, size); ++} ++ ++static struct sysfs_ops manager_sysfs_ops = { ++ .show = manager_attr_show, ++ .store = manager_attr_store, ++}; ++ ++static struct kobj_type manager_ktype = { ++ .sysfs_ops = &manager_sysfs_ops, ++ .default_attrs = manager_sysfs_attrs, ++}; ++ ++/* ++ * We have 4 levels of cache for the dispc settings. First two are in SW and ++ * the latter two in HW. ++ * ++ * +--------------------+ ++ * |overlay/manager_info| ++ * +--------------------+ ++ * v ++ * apply() ++ * v ++ * +--------------------+ ++ * | dss_cache | ++ * +--------------------+ ++ * v ++ * configure() ++ * v ++ * +--------------------+ ++ * | shadow registers | ++ * +--------------------+ ++ * v ++ * VFP or lcd/digit_enable ++ * v ++ * +--------------------+ ++ * | registers | ++ * +--------------------+ ++ */ ++ ++struct overlay_cache_data { ++ /* If true, cache changed, but not written to shadow registers. Set ++ * in apply(), cleared when registers written. */ ++ bool dirty; ++ /* If true, shadow registers contain changed values not yet in real ++ * registers. Set when writing to shadow registers, cleared at ++ * VSYNC/EVSYNC */ ++ bool shadow_dirty; ++ ++ bool enabled; ++ ++ u32 paddr; ++ void __iomem *vaddr; ++ u16 screen_width; ++ u16 width; ++ u16 height; ++ enum omap_color_mode color_mode; ++ u8 rotation; ++ enum omap_dss_rotation_type rotation_type; ++ bool mirror; ++ ++ u16 pos_x; ++ u16 pos_y; ++ u16 out_width; /* if 0, out_width == width */ ++ u16 out_height; /* if 0, out_height == height */ ++ u8 global_alpha; ++ ++ enum omap_channel channel; ++ bool replication; ++ bool ilace; ++ ++ enum omap_burst_size burst_size; ++ u32 fifo_low; ++ u32 fifo_high; ++ ++ bool manual_update; ++}; ++ ++struct manager_cache_data { ++ /* If true, cache changed, but not written to shadow registers. Set ++ * in apply(), cleared when registers written. */ ++ bool dirty; ++ /* If true, shadow registers contain changed values not yet in real ++ * registers. Set when writing to shadow registers, cleared at ++ * VSYNC/EVSYNC */ ++ bool shadow_dirty; ++ ++ u32 default_color; ++ ++ enum omap_dss_trans_key_type trans_key_type; ++ u32 trans_key; ++ bool trans_enabled; ++ ++ bool alpha_enabled; ++ ++ bool manual_upd_display; ++ bool manual_update; ++ bool do_manual_update; ++ ++ /* manual update region */ ++ u16 x, y, w, h; ++}; ++ ++static struct { ++ spinlock_t lock; ++ struct overlay_cache_data overlay_cache[3]; ++ struct manager_cache_data manager_cache[2]; ++ ++ bool irq_enabled; ++} dss_cache; ++ ++ ++ ++static int omap_dss_set_device(struct omap_overlay_manager *mgr, ++ struct omap_dss_device *dssdev) ++{ ++ int i; ++ int r; ++ ++ if (dssdev->manager) { ++ DSSERR("display '%s' already has a manager '%s'\n", ++ dssdev->name, dssdev->manager->name); ++ return -EINVAL; ++ } ++ ++ if ((mgr->supported_displays & dssdev->type) == 0) { ++ DSSERR("display '%s' does not support manager '%s'\n", ++ dssdev->name, mgr->name); ++ return -EINVAL; ++ } ++ ++ for (i = 0; i < mgr->num_overlays; i++) { ++ struct omap_overlay *ovl = mgr->overlays[i]; ++ ++ if (ovl->manager != mgr || !ovl->info.enabled) ++ continue; ++ ++ r = dss_check_overlay(ovl, dssdev); ++ if (r) ++ return r; ++ } ++ ++ dssdev->manager = mgr; ++ mgr->device = dssdev; ++ mgr->device_changed = true; ++ ++ return 0; ++} ++ ++static int omap_dss_unset_device(struct omap_overlay_manager *mgr) ++{ ++ if (!mgr->device) { ++ DSSERR("failed to unset display, display not set.\n"); ++ return -EINVAL; ++ } ++ ++ mgr->device->manager = NULL; ++ mgr->device = NULL; ++ mgr->device_changed = true; ++ ++ return 0; ++} ++ ++static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) ++{ ++ unsigned long timeout = msecs_to_jiffies(500); ++ struct manager_cache_data *mc; ++ enum omap_channel channel; ++ u32 irq; ++ int r; ++ int i; ++ ++ if (!mgr->device) ++ return 0; ++ ++ if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) { ++ irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; ++ channel = OMAP_DSS_CHANNEL_DIGIT; ++ } else { ++ if (mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { ++ enum omap_dss_update_mode mode; ++ mode = mgr->device->get_update_mode(mgr->device); ++ if (mode != OMAP_DSS_UPDATE_AUTO) ++ return 0; ++ ++ irq = DISPC_IRQ_FRAMEDONE; ++ } else { ++ irq = DISPC_IRQ_VSYNC; ++ } ++ channel = OMAP_DSS_CHANNEL_LCD; ++ } ++ ++ mc = &dss_cache.manager_cache[mgr->id]; ++ i = 0; ++ while (1) { ++ unsigned long flags; ++ bool shadow_dirty, dirty; ++ ++ spin_lock_irqsave(&dss_cache.lock, flags); ++ dirty = mc->dirty; ++ shadow_dirty = mc->shadow_dirty; ++ spin_unlock_irqrestore(&dss_cache.lock, flags); ++ ++ if (!dirty && !shadow_dirty) { ++ r = 0; ++ break; ++ } ++ ++ /* 4 iterations is the worst case: ++ * 1 - initial iteration, dirty = true (between VFP and VSYNC) ++ * 2 - first VSYNC, dirty = true ++ * 3 - dirty = false, shadow_dirty = true ++ * 4 - shadow_dirty = false */ ++ if (i++ == 3) { ++ DSSERR("mgr(%d)->wait_for_go() not finishing\n", ++ mgr->id); ++ r = 0; ++ break; ++ } ++ ++ r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); ++ if (r == -ERESTARTSYS) ++ break; ++ ++ if (r) { ++ DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id); ++ break; ++ } ++ } ++ ++ return r; ++} ++ ++int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) ++{ ++ unsigned long timeout = msecs_to_jiffies(500); ++ enum omap_channel channel; ++ struct overlay_cache_data *oc; ++ struct omap_dss_device *dssdev; ++ u32 irq; ++ int r; ++ int i; ++ ++ if (!ovl->manager || !ovl->manager->device) ++ return 0; ++ ++ dssdev = ovl->manager->device; ++ ++ if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { ++ irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; ++ channel = OMAP_DSS_CHANNEL_DIGIT; ++ } else { ++ if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { ++ enum omap_dss_update_mode mode; ++ mode = dssdev->get_update_mode(dssdev); ++ if (mode != OMAP_DSS_UPDATE_AUTO) ++ return 0; ++ ++ irq = DISPC_IRQ_FRAMEDONE; ++ } else { ++ irq = DISPC_IRQ_VSYNC; ++ } ++ channel = OMAP_DSS_CHANNEL_LCD; ++ } ++ ++ oc = &dss_cache.overlay_cache[ovl->id]; ++ i = 0; ++ while (1) { ++ unsigned long flags; ++ bool shadow_dirty, dirty; ++ ++ spin_lock_irqsave(&dss_cache.lock, flags); ++ dirty = oc->dirty; ++ shadow_dirty = oc->shadow_dirty; ++ spin_unlock_irqrestore(&dss_cache.lock, flags); ++ ++ if (!dirty && !shadow_dirty) { ++ r = 0; ++ break; ++ } ++ ++ /* 4 iterations is the worst case: ++ * 1 - initial iteration, dirty = true (between VFP and VSYNC) ++ * 2 - first VSYNC, dirty = true ++ * 3 - dirty = false, shadow_dirty = true ++ * 4 - shadow_dirty = false */ ++ if (i++ == 3) { ++ DSSERR("ovl(%d)->wait_for_go() not finishing\n", ++ ovl->id); ++ r = 0; ++ break; ++ } ++ ++ r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); ++ if (r == -ERESTARTSYS) ++ break; ++ ++ if (r) { ++ DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id); ++ break; ++ } ++ } ++ ++ return r; ++} ++ ++static int overlay_enabled(struct omap_overlay *ovl) ++{ ++ return ovl->info.enabled && ovl->manager && ovl->manager->device; ++} ++ ++/* Is rect1 a subset of rect2? */ ++static bool rectangle_subset(int x1, int y1, int w1, int h1, ++ int x2, int y2, int w2, int h2) ++{ ++ if (x1 < x2 || y1 < y2) ++ return false; ++ ++ if (x1 + w1 > x2 + w2) ++ return false; ++ ++ if (y1 + h1 > y2 + h2) ++ return false; ++ ++ return true; ++} ++ ++/* Do rect1 and rect2 overlap? */ ++static bool rectangle_intersects(int x1, int y1, int w1, int h1, ++ int x2, int y2, int w2, int h2) ++{ ++ if (x1 >= x2 + w2) ++ return false; ++ ++ if (x2 >= x1 + w1) ++ return false; ++ ++ if (y1 >= y2 + h2) ++ return false; ++ ++ if (y2 >= y1 + h1) ++ return false; ++ ++ return true; ++} ++ ++static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc) ++{ ++ if (oc->out_width != 0 && oc->width != oc->out_width) ++ return true; ++ ++ if (oc->out_height != 0 && oc->height != oc->out_height) ++ return true; ++ ++ return false; ++} ++ ++static int configure_overlay(enum omap_plane plane) ++{ ++ struct overlay_cache_data *c; ++ struct manager_cache_data *mc; ++ u16 outw, outh; ++ u16 x, y, w, h; ++ u32 paddr; ++ int r; ++ ++ DSSDBGF("%d", plane); ++ ++ c = &dss_cache.overlay_cache[plane]; ++ ++ if (!c->enabled) { ++ dispc_enable_plane(plane, 0); ++ return 0; ++ } ++ ++ mc = &dss_cache.manager_cache[c->channel]; ++ ++ x = c->pos_x; ++ y = c->pos_y; ++ w = c->width; ++ h = c->height; ++ outw = c->out_width == 0 ? c->width : c->out_width; ++ outh = c->out_height == 0 ? c->height : c->out_height; ++ paddr = c->paddr; ++ ++ if (c->manual_update && mc->do_manual_update) { ++ unsigned bpp; ++ /* If the overlay is outside the update region, disable it */ ++ if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h, ++ x, y, outw, outh)) { ++ dispc_enable_plane(plane, 0); ++ return 0; ++ } ++ ++ switch (c->color_mode) { ++ case OMAP_DSS_COLOR_RGB16: ++ case OMAP_DSS_COLOR_ARGB16: ++ case OMAP_DSS_COLOR_YUV2: ++ case OMAP_DSS_COLOR_UYVY: ++ bpp = 16; ++ break; ++ ++ case OMAP_DSS_COLOR_RGB24P: ++ bpp = 24; ++ break; ++ ++ case OMAP_DSS_COLOR_RGB24U: ++ case OMAP_DSS_COLOR_ARGB32: ++ case OMAP_DSS_COLOR_RGBA32: ++ case OMAP_DSS_COLOR_RGBX32: ++ bpp = 32; ++ break; ++ ++ default: ++ BUG(); ++ } ++ ++ if (dispc_is_overlay_scaled(c)) { ++ /* If the overlay is scaled, the update area has ++ * already been enlarged to cover the whole overlay. We ++ * only need to adjust x/y here */ ++ x = c->pos_x - mc->x; ++ y = c->pos_y - mc->y; ++ } else { ++ if (mc->x > c->pos_x) { ++ x = 0; ++ w -= (mc->x - c->pos_x); ++ paddr += (mc->x - c->pos_x) * bpp / 8; ++ } else { ++ x = c->pos_x - mc->x; ++ } ++ ++ if (mc->y > c->pos_y) { ++ y = 0; ++ h -= (mc->y - c->pos_y); ++ paddr += (mc->y - c->pos_y) * c->screen_width * ++ bpp / 8; ++ } else { ++ y = c->pos_y - mc->y; ++ } ++ ++ if (mc->w < (x+w)) ++ w -= (x+w) - (mc->w); ++ ++ if (mc->h < (y+h)) ++ h -= (y+h) - (mc->h); ++ ++ outw = w; ++ outh = h; ++ } ++ } ++ ++ r = dispc_setup_plane(plane, ++ paddr, ++ c->screen_width, ++ x, y, ++ w, h, ++ outw, outh, ++ c->color_mode, ++ c->ilace, ++ c->rotation_type, ++ c->rotation, ++ c->mirror, ++ c->global_alpha); ++ ++ if (r) { ++ /* this shouldn't happen */ ++ DSSERR("dispc_setup_plane failed for ovl %d\n", plane); ++ dispc_enable_plane(plane, 0); ++ return r; ++ } ++ ++ dispc_enable_replication(plane, c->replication); ++ ++ dispc_set_burst_size(plane, c->burst_size); ++ dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high); ++ ++ dispc_enable_plane(plane, 1); ++ ++ return 0; ++} ++ ++static void configure_manager(enum omap_channel channel) ++{ ++ struct manager_cache_data *c; ++ ++ DSSDBGF("%d", channel); ++ ++ c = &dss_cache.manager_cache[channel]; ++ ++ dispc_set_trans_key(channel, c->trans_key_type, c->trans_key); ++ dispc_enable_trans_key(channel, c->trans_enabled); ++ dispc_enable_alpha_blending(channel, c->alpha_enabled); ++} ++ ++/* configure_dispc() tries to write values from cache to shadow registers. ++ * It writes only to those managers/overlays that are not busy. ++ * returns 0 if everything could be written to shadow registers. ++ * returns 1 if not everything could be written to shadow registers. */ ++static int configure_dispc(void) ++{ ++ struct overlay_cache_data *oc; ++ struct manager_cache_data *mc; ++ const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); ++ const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); ++ int i; ++ int r; ++ bool mgr_busy[2]; ++ bool mgr_go[2]; ++ bool busy; ++ ++ r = 0; ++ busy = false; ++ ++ mgr_busy[0] = dispc_go_busy(0); ++ mgr_busy[1] = dispc_go_busy(1); ++ mgr_go[0] = false; ++ mgr_go[1] = false; ++ ++ /* Commit overlay settings */ ++ for (i = 0; i < num_ovls; ++i) { ++ oc = &dss_cache.overlay_cache[i]; ++ mc = &dss_cache.manager_cache[oc->channel]; ++ ++ if (!oc->dirty) ++ continue; ++ ++ if (oc->manual_update && !mc->do_manual_update) ++ continue; ++ ++ if (mgr_busy[oc->channel]) { ++ busy = true; ++ continue; ++ } ++ ++ r = configure_overlay(i); ++ if (r) ++ DSSERR("configure_overlay %d failed\n", i); ++ ++ oc->dirty = false; ++ oc->shadow_dirty = true; ++ mgr_go[oc->channel] = true; ++ } ++ ++ /* Commit manager settings */ ++ for (i = 0; i < num_mgrs; ++i) { ++ mc = &dss_cache.manager_cache[i]; ++ ++ if (!mc->dirty) ++ continue; ++ ++ if (mc->manual_update && !mc->do_manual_update) ++ continue; ++ ++ if (mgr_busy[i]) { ++ busy = true; ++ continue; ++ } ++ ++ configure_manager(i); ++ mc->dirty = false; ++ mc->shadow_dirty = true; ++ mgr_go[i] = true; ++ } ++ ++ /* set GO */ ++ for (i = 0; i < num_mgrs; ++i) { ++ mc = &dss_cache.manager_cache[i]; ++ ++ if (!mgr_go[i]) ++ continue; ++ ++ /* We don't need GO with manual update display. LCD iface will ++ * always be turned off after frame, and new settings will be ++ * taken in to use at next update */ ++ if (!mc->manual_upd_display) ++ dispc_go(i); ++ } ++ ++ if (busy) ++ r = 1; ++ else ++ r = 0; ++ ++ return r; ++} ++ ++/* Configure dispc for partial update. Return possibly modified update ++ * area */ ++void dss_setup_partial_planes(struct omap_dss_device *dssdev, ++ u16 *xi, u16 *yi, u16 *wi, u16 *hi) ++{ ++ struct overlay_cache_data *oc; ++ struct manager_cache_data *mc; ++ const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); ++ struct omap_overlay_manager *mgr; ++ int i; ++ u16 x, y, w, h; ++ unsigned long flags; ++ ++ x = *xi; ++ y = *yi; ++ w = *wi; ++ h = *hi; ++ ++ DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n", ++ *xi, *yi, *wi, *hi); ++ ++ mgr = dssdev->manager; ++ ++ if (!mgr) { ++ DSSDBG("no manager\n"); ++ return; ++ } ++ ++ spin_lock_irqsave(&dss_cache.lock, flags); ++ ++ /* We need to show the whole overlay if it is scaled. So look for ++ * those, and make the update area larger if found. ++ * Also mark the overlay cache dirty */ ++ for (i = 0; i < num_ovls; ++i) { ++ unsigned x1, y1, x2, y2; ++ unsigned outw, outh; ++ ++ oc = &dss_cache.overlay_cache[i]; ++ ++ if (oc->channel != mgr->id) ++ continue; ++ ++ oc->dirty = true; ++ ++ if (!oc->enabled) ++ continue; ++ ++ if (!dispc_is_overlay_scaled(oc)) ++ continue; ++ ++ outw = oc->out_width == 0 ? oc->width : oc->out_width; ++ outh = oc->out_height == 0 ? oc->height : oc->out_height; ++ ++ /* is the overlay outside the update region? */ ++ if (!rectangle_intersects(x, y, w, h, ++ oc->pos_x, oc->pos_y, ++ outw, outh)) ++ continue; ++ ++ /* if the overlay totally inside the update region? */ ++ if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh, ++ x, y, w, h)) ++ continue; ++ ++ if (x > oc->pos_x) ++ x1 = oc->pos_x; ++ else ++ x1 = x; ++ ++ if (y > oc->pos_y) ++ y1 = oc->pos_y; ++ else ++ y1 = y; ++ ++ if ((x + w) < (oc->pos_x + outw)) ++ x2 = oc->pos_x + outw; ++ else ++ x2 = x + w; ++ ++ if ((y + h) < (oc->pos_y + outh)) ++ y2 = oc->pos_y + outh; ++ else ++ y2 = y + h; ++ ++ x = x1; ++ y = y1; ++ w = x2 - x1; ++ h = y2 - y1; ++ ++ DSSDBG("changing upd area due to ovl(%d) scaling %d,%d %dx%d\n", ++ i, x, y, w, h); ++ } ++ ++ mc = &dss_cache.manager_cache[mgr->id]; ++ mc->do_manual_update = true; ++ mc->x = x; ++ mc->y = y; ++ mc->w = w; ++ mc->h = h; ++ ++ configure_dispc(); ++ ++ mc->do_manual_update = false; ++ ++ spin_unlock_irqrestore(&dss_cache.lock, flags); ++ ++ *xi = x; ++ *yi = y; ++ *wi = w; ++ *hi = h; ++} ++ ++void dss_start_update(struct omap_dss_device *dssdev) ++{ ++ struct manager_cache_data *mc; ++ struct overlay_cache_data *oc; ++ const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); ++ const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); ++ struct omap_overlay_manager *mgr; ++ int i; ++ ++ mgr = dssdev->manager; ++ ++ for (i = 0; i < num_ovls; ++i) { ++ oc = &dss_cache.overlay_cache[i]; ++ if (oc->channel != mgr->id) ++ continue; ++ ++ oc->shadow_dirty = false; ++ } ++ ++ for (i = 0; i < num_mgrs; ++i) { ++ mc = &dss_cache.manager_cache[i]; ++ if (mgr->id != i) ++ continue; ++ ++ mc->shadow_dirty = false; ++ } ++ ++ dispc_enable_lcd_out(1); ++} ++ ++static void dss_apply_irq_handler(void *data, u32 mask) ++{ ++ struct manager_cache_data *mc; ++ struct overlay_cache_data *oc; ++ const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); ++ const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); ++ int i, r; ++ bool mgr_busy[2]; ++ ++ mgr_busy[0] = dispc_go_busy(0); ++ mgr_busy[1] = dispc_go_busy(1); ++ ++ spin_lock(&dss_cache.lock); ++ ++ for (i = 0; i < num_ovls; ++i) { ++ oc = &dss_cache.overlay_cache[i]; ++ if (!mgr_busy[oc->channel]) ++ oc->shadow_dirty = false; ++ } ++ ++ for (i = 0; i < num_mgrs; ++i) { ++ mc = &dss_cache.manager_cache[i]; ++ if (!mgr_busy[i]) ++ mc->shadow_dirty = false; ++ } ++ ++ r = configure_dispc(); ++ if (r == 1) ++ goto end; ++ ++ /* re-read busy flags */ ++ mgr_busy[0] = dispc_go_busy(0); ++ mgr_busy[1] = dispc_go_busy(1); ++ ++ /* keep running as long as there are busy managers, so that ++ * we can collect overlay-applied information */ ++ for (i = 0; i < num_mgrs; ++i) { ++ if (mgr_busy[i]) ++ goto end; ++ } ++ ++ omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, ++ DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | ++ DISPC_IRQ_EVSYNC_EVEN); ++ dss_cache.irq_enabled = false; ++ ++end: ++ spin_unlock(&dss_cache.lock); ++} ++ ++static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) ++{ ++ struct overlay_cache_data *oc; ++ struct manager_cache_data *mc; ++ int i; ++ struct omap_overlay *ovl; ++ int num_planes_enabled = 0; ++ bool use_fifomerge; ++ unsigned long flags; ++ int r; ++ ++ DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name); ++ ++ spin_lock_irqsave(&dss_cache.lock, flags); ++ ++ /* Configure overlays */ ++ for (i = 0; i < omap_dss_get_num_overlays(); ++i) { ++ struct omap_dss_device *dssdev; ++ ++ ovl = omap_dss_get_overlay(i); ++ ++ if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) ++ continue; ++ ++ oc = &dss_cache.overlay_cache[ovl->id]; ++ ++ if (!overlay_enabled(ovl)) { ++ if (oc->enabled) { ++ oc->enabled = false; ++ oc->dirty = true; ++ } ++ continue; ++ } ++ ++ if (!ovl->info_dirty) { ++ if (oc->enabled) ++ ++num_planes_enabled; ++ continue; ++ } ++ ++ dssdev = ovl->manager->device; ++ ++ if (dss_check_overlay(ovl, dssdev)) { ++ if (oc->enabled) { ++ oc->enabled = false; ++ oc->dirty = true; ++ } ++ continue; ++ } ++ ++ ovl->info_dirty = false; ++ oc->dirty = true; ++ ++ oc->paddr = ovl->info.paddr; ++ oc->vaddr = ovl->info.vaddr; ++ oc->screen_width = ovl->info.screen_width; ++ oc->width = ovl->info.width; ++ oc->height = ovl->info.height; ++ oc->color_mode = ovl->info.color_mode; ++ oc->rotation = ovl->info.rotation; ++ oc->rotation_type = ovl->info.rotation_type; ++ oc->mirror = ovl->info.mirror; ++ oc->pos_x = ovl->info.pos_x; ++ oc->pos_y = ovl->info.pos_y; ++ oc->out_width = ovl->info.out_width; ++ oc->out_height = ovl->info.out_height; ++ oc->global_alpha = ovl->info.global_alpha; ++ ++ oc->replication = ++ dss_use_replication(dssdev, ovl->info.color_mode); ++ ++ oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC; ++ ++ oc->channel = ovl->manager->id; ++ ++ oc->enabled = true; ++ ++ oc->manual_update = ++ dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && ++ dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO; ++ ++ ++num_planes_enabled; ++ } ++ ++ /* Configure managers */ ++ list_for_each_entry(mgr, &manager_list, list) { ++ struct omap_dss_device *dssdev; ++ ++ if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC)) ++ continue; ++ ++ mc = &dss_cache.manager_cache[mgr->id]; ++ ++ if (mgr->device_changed) { ++ mgr->device_changed = false; ++ mgr->info_dirty = true; ++ } ++ ++ if (!mgr->info_dirty) ++ continue; ++ ++ if (!mgr->device) ++ continue; ++ ++ dssdev = mgr->device; ++ ++ mgr->info_dirty = false; ++ mc->dirty = true; ++ ++ mc->default_color = mgr->info.default_color; ++ mc->trans_key_type = mgr->info.trans_key_type; ++ mc->trans_key = mgr->info.trans_key; ++ mc->trans_enabled = mgr->info.trans_enabled; ++ mc->alpha_enabled = mgr->info.alpha_enabled; ++ ++ mc->manual_upd_display = ++ dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; ++ ++ mc->manual_update = ++ dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && ++ dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO; ++ } ++ ++ /* XXX TODO: Try to get fifomerge working. The problem is that it ++ * affects both managers, not individually but at the same time. This ++ * means the change has to be well synchronized. I guess the proper way ++ * is to have a two step process for fifo merge: ++ * fifomerge enable: ++ * 1. disable other planes, leaving one plane enabled ++ * 2. wait until the planes are disabled on HW ++ * 3. config merged fifo thresholds, enable fifomerge ++ * fifomerge disable: ++ * 1. config unmerged fifo thresholds, disable fifomerge ++ * 2. wait until fifo changes are in HW ++ * 3. enable planes ++ */ ++ use_fifomerge = false; ++ ++ /* Configure overlay fifos */ ++ for (i = 0; i < omap_dss_get_num_overlays(); ++i) { ++ struct omap_dss_device *dssdev; ++ u32 size; ++ ++ ovl = omap_dss_get_overlay(i); ++ ++ if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) ++ continue; ++ ++ oc = &dss_cache.overlay_cache[ovl->id]; ++ ++ if (!oc->enabled) ++ continue; ++ ++ dssdev = ovl->manager->device; ++ ++ size = dispc_get_plane_fifo_size(ovl->id); ++ if (use_fifomerge) ++ size *= 3; ++ ++ switch (dssdev->type) { ++ case OMAP_DISPLAY_TYPE_DPI: ++ case OMAP_DISPLAY_TYPE_DBI: ++ case OMAP_DISPLAY_TYPE_SDI: ++ case OMAP_DISPLAY_TYPE_VENC: ++ default_get_overlay_fifo_thresholds(ovl->id, size, ++ &oc->burst_size, &oc->fifo_low, ++ &oc->fifo_high); ++ break; ++#ifdef CONFIG_OMAP2_DSS_DSI ++ case OMAP_DISPLAY_TYPE_DSI: ++ dsi_get_overlay_fifo_thresholds(ovl->id, size, ++ &oc->burst_size, &oc->fifo_low, ++ &oc->fifo_high); ++ break; ++#endif ++ default: ++ BUG(); ++ } ++ } ++ ++ r = 0; ++ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); ++ if (!dss_cache.irq_enabled) { ++ r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, ++ DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | ++ DISPC_IRQ_EVSYNC_EVEN); ++ dss_cache.irq_enabled = true; ++ } ++ configure_dispc(); ++ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); ++ ++ spin_unlock_irqrestore(&dss_cache.lock, flags); ++ ++ return r; ++} ++ ++static int dss_check_manager(struct omap_overlay_manager *mgr) ++{ ++ /* OMAP supports only graphics source transparency color key and alpha ++ * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */ ++ ++ if (mgr->info.alpha_enabled && mgr->info.trans_enabled && ++ mgr->info.trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr, ++ struct omap_overlay_manager_info *info) ++{ ++ int r; ++ struct omap_overlay_manager_info old_info; ++ ++ old_info = mgr->info; ++ mgr->info = *info; ++ ++ r = dss_check_manager(mgr); ++ if (r) { ++ mgr->info = old_info; ++ return r; ++ } ++ ++ mgr->info_dirty = true; ++ ++ return 0; ++} ++ ++static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr, ++ struct omap_overlay_manager_info *info) ++{ ++ *info = mgr->info; ++} ++ ++static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager) ++{ ++ ++num_managers; ++ list_add_tail(&manager->list, &manager_list); ++} ++ ++int dss_init_overlay_managers(struct platform_device *pdev) ++{ ++ int i, r; ++ ++ spin_lock_init(&dss_cache.lock); ++ ++ INIT_LIST_HEAD(&manager_list); ++ ++ num_managers = 0; ++ ++ for (i = 0; i < 2; ++i) { ++ struct omap_overlay_manager *mgr; ++ mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); ++ ++ BUG_ON(mgr == NULL); ++ ++ switch (i) { ++ case 0: ++ mgr->name = "lcd"; ++ mgr->id = OMAP_DSS_CHANNEL_LCD; ++ mgr->supported_displays = ++ OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | ++ OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI; ++ break; ++ case 1: ++ mgr->name = "tv"; ++ mgr->id = OMAP_DSS_CHANNEL_DIGIT; ++ mgr->supported_displays = OMAP_DISPLAY_TYPE_VENC; ++ break; ++ } ++ ++ mgr->set_device = &omap_dss_set_device; ++ mgr->unset_device = &omap_dss_unset_device; ++ mgr->apply = &omap_dss_mgr_apply; ++ mgr->set_manager_info = &omap_dss_mgr_set_info; ++ mgr->get_manager_info = &omap_dss_mgr_get_info; ++ mgr->wait_for_go = &dss_mgr_wait_for_go; ++ ++ mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC; ++ ++ dss_overlay_setup_dispc_manager(mgr); ++ ++ omap_dss_add_overlay_manager(mgr); ++ ++ r = kobject_init_and_add(&mgr->kobj, &manager_ktype, ++ &pdev->dev.kobj, "manager%d", i); ++ ++ if (r) { ++ DSSERR("failed to create sysfs file\n"); ++ continue; ++ } ++ } ++ ++#ifdef L4_EXAMPLE ++ { ++ int omap_dss_mgr_apply_l4(struct omap_overlay_manager *mgr) ++ { ++ DSSDBG("omap_dss_mgr_apply_l4(%s)\n", mgr->name); ++ ++ return 0; ++ } ++ ++ struct omap_overlay_manager *mgr; ++ mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); ++ ++ BUG_ON(mgr == NULL); ++ ++ mgr->name = "l4"; ++ mgr->supported_displays = ++ OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI; ++ ++ mgr->set_device = &omap_dss_set_device; ++ mgr->unset_device = &omap_dss_unset_device; ++ mgr->apply = &omap_dss_mgr_apply_l4; ++ mgr->set_manager_info = &omap_dss_mgr_set_info; ++ mgr->get_manager_info = &omap_dss_mgr_get_info; ++ ++ dss_overlay_setup_l4_manager(mgr); ++ ++ omap_dss_add_overlay_manager(mgr); ++ ++ r = kobject_init_and_add(&mgr->kobj, &manager_ktype, ++ &pdev->dev.kobj, "managerl4"); ++ ++ if (r) ++ DSSERR("failed to create sysfs file\n"); ++ } ++#endif ++ ++ return 0; ++} ++ ++void dss_uninit_overlay_managers(struct platform_device *pdev) ++{ ++ struct omap_overlay_manager *mgr; ++ ++ while (!list_empty(&manager_list)) { ++ mgr = list_first_entry(&manager_list, ++ struct omap_overlay_manager, list); ++ list_del(&mgr->list); ++ kobject_del(&mgr->kobj); ++ kobject_put(&mgr->kobj); ++ kfree(mgr); ++ } ++ ++ num_managers = 0; ++} ++ ++int omap_dss_get_num_overlay_managers(void) ++{ ++ return num_managers; ++} ++EXPORT_SYMBOL(omap_dss_get_num_overlay_managers); ++ ++struct omap_overlay_manager *omap_dss_get_overlay_manager(int num) ++{ ++ int i = 0; ++ struct omap_overlay_manager *mgr; ++ ++ list_for_each_entry(mgr, &manager_list, list) { ++ if (i++ == num) ++ return mgr; ++ } ++ ++ return NULL; ++} ++EXPORT_SYMBOL(omap_dss_get_overlay_manager); ++ +diff --git a/drivers/video/omap2/dss/overlay.c b/drivers/video/omap2/dss/overlay.c +new file mode 100644 +index 0000000..c7cb81b +--- /dev/null ++++ b/drivers/video/omap2/dss/overlay.c +@@ -0,0 +1,673 @@ ++/* ++ * linux/drivers/video/omap2/dss/overlay.c ++ * ++ * Copyright (C) 2009 Nokia Corporation ++ * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> ++ * ++ * Some code and ideas taken from drivers/video/omap/ driver ++ * by Imre Deak. ++ * ++ * 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/>. ++ */ ++ ++#define DSS_SUBSYS_NAME "OVERLAY" ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/err.h> ++#include <linux/sysfs.h> ++#include <linux/kobject.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++ ++#include <mach/display.h> ++ ++#include "dss.h" ++ ++static int num_overlays; ++static struct list_head overlay_list; ++ ++static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name); ++} ++ ++static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%s\n", ++ ovl->manager ? ovl->manager->name : "<none>"); ++} ++ ++static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf, ++ size_t size) ++{ ++ int i, r; ++ struct omap_overlay_manager *mgr = NULL; ++ struct omap_overlay_manager *old_mgr; ++ int len = size; ++ ++ if (buf[size-1] == '\n') ++ --len; ++ ++ if (len > 0) { ++ for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { ++ mgr = omap_dss_get_overlay_manager(i); ++ ++ if (strncmp(buf, mgr->name, len) == 0) ++ break; ++ ++ mgr = NULL; ++ } ++ } ++ ++ if (len > 0 && mgr == NULL) ++ return -EINVAL; ++ ++ if (mgr) ++ DSSDBG("manager %s found\n", mgr->name); ++ ++ if (mgr == ovl->manager) ++ return size; ++ ++ old_mgr = ovl->manager; ++ ++ /* detach old manager */ ++ if (old_mgr) { ++ r = ovl->unset_manager(ovl); ++ if (r) { ++ DSSERR("detach failed\n"); ++ return r; ++ } ++ ++ r = old_mgr->apply(old_mgr); ++ if (r) ++ return r; ++ } ++ ++ if (mgr) { ++ r = ovl->set_manager(ovl, mgr); ++ if (r) { ++ DSSERR("Failed to attach overlay\n"); ++ return r; ++ } ++ ++ r = mgr->apply(mgr); ++ if (r) ++ return r; ++ } ++ ++ return size; ++} ++ ++static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%d,%d\n", ++ ovl->info.width, ovl->info.height); ++} ++ ++static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.screen_width); ++} ++ ++static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%d,%d\n", ++ ovl->info.pos_x, ovl->info.pos_y); ++} ++ ++static ssize_t overlay_position_store(struct omap_overlay *ovl, ++ const char *buf, size_t size) ++{ ++ int r; ++ char *last; ++ struct omap_overlay_info info; ++ ++ ovl->get_overlay_info(ovl, &info); ++ ++ info.pos_x = simple_strtoul(buf, &last, 10); ++ ++last; ++ if (last - buf >= size) ++ return -EINVAL; ++ ++ info.pos_y = simple_strtoul(last, &last, 10); ++ ++ r = ovl->set_overlay_info(ovl, &info); ++ if (r) ++ return r; ++ ++ if (ovl->manager) { ++ r = ovl->manager->apply(ovl->manager); ++ if (r) ++ return r; ++ } ++ ++ return size; ++} ++ ++static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%d,%d\n", ++ ovl->info.out_width, ovl->info.out_height); ++} ++ ++static ssize_t overlay_output_size_store(struct omap_overlay *ovl, ++ const char *buf, size_t size) ++{ ++ int r; ++ char *last; ++ struct omap_overlay_info info; ++ ++ ovl->get_overlay_info(ovl, &info); ++ ++ info.out_width = simple_strtoul(buf, &last, 10); ++ ++last; ++ if (last - buf >= size) ++ return -EINVAL; ++ ++ info.out_height = simple_strtoul(last, &last, 10); ++ ++ r = ovl->set_overlay_info(ovl, &info); ++ if (r) ++ return r; ++ ++ if (ovl->manager) { ++ r = ovl->manager->apply(ovl->manager); ++ if (r) ++ return r; ++ } ++ ++ return size; ++} ++ ++static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.enabled); ++} ++ ++static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf, ++ size_t size) ++{ ++ int r; ++ struct omap_overlay_info info; ++ ++ ovl->get_overlay_info(ovl, &info); ++ ++ info.enabled = simple_strtoul(buf, NULL, 10); ++ ++ r = ovl->set_overlay_info(ovl, &info); ++ if (r) ++ return r; ++ ++ if (ovl->manager) { ++ r = ovl->manager->apply(ovl->manager); ++ if (r) ++ return r; ++ } ++ ++ return size; ++} ++ ++static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf) ++{ ++ return snprintf(buf, PAGE_SIZE, "%d\n", ++ ovl->info.global_alpha); ++} ++ ++static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, ++ const char *buf, size_t size) ++{ ++ int r; ++ struct omap_overlay_info info; ++ ++ ovl->get_overlay_info(ovl, &info); ++ ++ /* Video1 plane does not support global alpha ++ * to always make it 255 completely opaque ++ */ ++ if (ovl->id == OMAP_DSS_VIDEO1) ++ info.global_alpha = 255; ++ else ++ info.global_alpha = simple_strtoul(buf, NULL, 10); ++ ++ r = ovl->set_overlay_info(ovl, &info); ++ if (r) ++ return r; ++ ++ if (ovl->manager) { ++ r = ovl->manager->apply(ovl->manager); ++ if (r) ++ return r; ++ } ++ ++ return size; ++} ++ ++struct overlay_attribute { ++ struct attribute attr; ++ ssize_t (*show)(struct omap_overlay *, char *); ++ ssize_t (*store)(struct omap_overlay *, const char *, size_t); ++}; ++ ++#define OVERLAY_ATTR(_name, _mode, _show, _store) \ ++ struct overlay_attribute overlay_attr_##_name = \ ++ __ATTR(_name, _mode, _show, _store) ++ ++static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL); ++static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR, ++ overlay_manager_show, overlay_manager_store); ++static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL); ++static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL); ++static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR, ++ overlay_position_show, overlay_position_store); ++static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR, ++ overlay_output_size_show, overlay_output_size_store); ++static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR, ++ overlay_enabled_show, overlay_enabled_store); ++static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR, ++ overlay_global_alpha_show, overlay_global_alpha_store); ++ ++static struct attribute *overlay_sysfs_attrs[] = { ++ &overlay_attr_name.attr, ++ &overlay_attr_manager.attr, ++ &overlay_attr_input_size.attr, ++ &overlay_attr_screen_width.attr, ++ &overlay_attr_position.attr, ++ &overlay_attr_output_size.attr, ++ &overlay_attr_enabled.attr, ++ &overlay_attr_global_alpha.attr, ++ NULL ++}; ++ ++static ssize_t overlay_attr_show(struct kobject *kobj, struct attribute *attr, ++ char *buf) ++{ ++ struct omap_overlay *overlay; ++ struct overlay_attribute *overlay_attr; ++ ++ overlay = container_of(kobj, struct omap_overlay, kobj); ++ overlay_attr = container_of(attr, struct overlay_attribute, attr); ++ ++ if (!overlay_attr->show) ++ return -ENOENT; ++ ++ return overlay_attr->show(overlay, buf); ++} ++ ++static ssize_t overlay_attr_store(struct kobject *kobj, struct attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct omap_overlay *overlay; ++ struct overlay_attribute *overlay_attr; ++ ++ overlay = container_of(kobj, struct omap_overlay, kobj); ++ overlay_attr = container_of(attr, struct overlay_attribute, attr); ++ ++ if (!overlay_attr->store) ++ return -ENOENT; ++ ++ return overlay_attr->store(overlay, buf, size); ++} ++ ++static struct sysfs_ops overlay_sysfs_ops = { ++ .show = overlay_attr_show, ++ .store = overlay_attr_store, ++}; ++ ++static struct kobj_type overlay_ktype = { ++ .sysfs_ops = &overlay_sysfs_ops, ++ .default_attrs = overlay_sysfs_attrs, ++}; ++ ++/* Check if overlay parameters are compatible with display */ ++int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev) ++{ ++ struct omap_overlay_info *info; ++ u16 outw, outh; ++ u16 dw, dh; ++ ++ if (!dssdev) ++ return 0; ++ ++ if (!ovl->info.enabled) ++ return 0; ++ ++ info = &ovl->info; ++ ++ if (info->paddr == 0) { ++ DSSDBG("check_overlay failed: paddr 0\n"); ++ return -EINVAL; ++ } ++ ++ dssdev->get_resolution(dssdev, &dw, &dh); ++ ++ DSSDBG("check_overlay %d: (%d,%d %dx%d -> %dx%d) disp (%dx%d)\n", ++ ovl->id, ++ info->pos_x, info->pos_y, ++ info->width, info->height, ++ info->out_width, info->out_height, ++ dw, dh); ++ ++ if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { ++ outw = info->width; ++ outh = info->height; ++ } else { ++ if (info->out_width == 0) ++ outw = info->width; ++ else ++ outw = info->out_width; ++ ++ if (info->out_height == 0) ++ outh = info->height; ++ else ++ outh = info->out_height; ++ } ++ ++ if (dw < info->pos_x + outw) { ++ DSSDBG("check_overlay failed 1: %d < %d + %d\n", ++ dw, info->pos_x, outw); ++ return -EINVAL; ++ } ++ ++ if (dh < info->pos_y + outh) { ++ DSSDBG("check_overlay failed 2: %d < %d + %d\n", ++ dh, info->pos_y, outh); ++ return -EINVAL; ++ } ++ ++ if ((ovl->supported_modes & info->color_mode) == 0) { ++ DSSERR("overlay doesn't support mode %d\n", info->color_mode); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int dss_ovl_set_overlay_info(struct omap_overlay *ovl, ++ struct omap_overlay_info *info) ++{ ++ int r; ++ struct omap_overlay_info old_info; ++ ++ old_info = ovl->info; ++ ovl->info = *info; ++ ++ if (ovl->manager) { ++ r = dss_check_overlay(ovl, ovl->manager->device); ++ if (r) { ++ ovl->info = old_info; ++ return r; ++ } ++ } ++ ++ ovl->info_dirty = true; ++ ++ return 0; ++} ++ ++static void dss_ovl_get_overlay_info(struct omap_overlay *ovl, ++ struct omap_overlay_info *info) ++{ ++ *info = ovl->info; ++} ++ ++static int dss_ovl_wait_for_go(struct omap_overlay *ovl) ++{ ++ return dss_mgr_wait_for_go_ovl(ovl); ++} ++ ++static int omap_dss_set_manager(struct omap_overlay *ovl, ++ struct omap_overlay_manager *mgr) ++{ ++ if (!mgr) ++ return -EINVAL; ++ ++ if (ovl->manager) { ++ DSSERR("overlay '%s' already has a manager '%s'\n", ++ ovl->name, ovl->manager->name); ++ return -EINVAL; ++ } ++ ++ if (ovl->info.enabled) { ++ DSSERR("overlay has to be disabled to change the manager\n"); ++ return -EINVAL; ++ } ++ ++ ovl->manager = mgr; ++ ++ dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); ++ /* XXX: on manual update display, in auto update mode, a bug happens ++ * here. When an overlay is first enabled on LCD, then it's disabled, ++ * and the manager is changed to TV, we sometimes get SYNC_LOST_DIGIT ++ * errors. Waiting before changing the channel_out fixes it. I'm ++ * guessing that the overlay is still somehow being used for the LCD, ++ * but I don't understand how or why. */ ++ msleep(40); ++ dispc_set_channel_out(ovl->id, mgr->id); ++ dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); ++ ++ return 0; ++} ++ ++static int omap_dss_unset_manager(struct omap_overlay *ovl) ++{ ++ int r; ++ ++ if (!ovl->manager) { ++ DSSERR("failed to detach overlay: manager not set\n"); ++ return -EINVAL; ++ } ++ ++ if (ovl->info.enabled) { ++ DSSERR("overlay has to be disabled to unset the manager\n"); ++ return -EINVAL; ++ } ++ ++ r = ovl->wait_for_go(ovl); ++ if (r) ++ return r; ++ ++ ovl->manager = NULL; ++ ++ return 0; ++} ++ ++int omap_dss_get_num_overlays(void) ++{ ++ return num_overlays; ++} ++EXPORT_SYMBOL(omap_dss_get_num_overlays); ++ ++struct omap_overlay *omap_dss_get_overlay(int num) ++{ ++ int i = 0; ++ struct omap_overlay *ovl; ++ ++ list_for_each_entry(ovl, &overlay_list, list) { ++ if (i++ == num) ++ return ovl; ++ } ++ ++ return NULL; ++} ++EXPORT_SYMBOL(omap_dss_get_overlay); ++ ++static void omap_dss_add_overlay(struct omap_overlay *overlay) ++{ ++ ++num_overlays; ++ list_add_tail(&overlay->list, &overlay_list); ++} ++ ++static struct omap_overlay *dispc_overlays[3]; ++ ++void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr) ++{ ++ mgr->num_overlays = 3; ++ mgr->overlays = dispc_overlays; ++} ++ ++#ifdef L4_EXAMPLE ++static struct omap_overlay *l4_overlays[1]; ++void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr) ++{ ++ mgr->num_overlays = 1; ++ mgr->overlays = l4_overlays; ++} ++#endif ++ ++void dss_init_overlays(struct platform_device *pdev) ++{ ++ int i, r; ++ ++ INIT_LIST_HEAD(&overlay_list); ++ ++ num_overlays = 0; ++ ++ for (i = 0; i < 3; ++i) { ++ struct omap_overlay *ovl; ++ ovl = kzalloc(sizeof(*ovl), GFP_KERNEL); ++ ++ BUG_ON(ovl == NULL); ++ ++ switch (i) { ++ case 0: ++ ovl->name = "gfx"; ++ ovl->id = OMAP_DSS_GFX; ++ ovl->supported_modes = OMAP_DSS_COLOR_GFX_OMAP3; ++ ovl->caps = OMAP_DSS_OVL_CAP_DISPC; ++ ovl->info.global_alpha = 255; ++ break; ++ case 1: ++ ovl->name = "vid1"; ++ ovl->id = OMAP_DSS_VIDEO1; ++ ovl->supported_modes = OMAP_DSS_COLOR_VID_OMAP3; ++ ovl->caps = OMAP_DSS_OVL_CAP_SCALE | ++ OMAP_DSS_OVL_CAP_DISPC; ++ ovl->info.global_alpha = 255; ++ break; ++ case 2: ++ ovl->name = "vid2"; ++ ovl->id = OMAP_DSS_VIDEO2; ++ ovl->supported_modes = OMAP_DSS_COLOR_VID_OMAP3; ++ ovl->caps = OMAP_DSS_OVL_CAP_SCALE | ++ OMAP_DSS_OVL_CAP_DISPC; ++ ovl->info.global_alpha = 255; ++ break; ++ } ++ ++ ovl->set_manager = &omap_dss_set_manager; ++ ovl->unset_manager = &omap_dss_unset_manager; ++ ovl->set_overlay_info = &dss_ovl_set_overlay_info; ++ ovl->get_overlay_info = &dss_ovl_get_overlay_info; ++ ovl->wait_for_go = &dss_ovl_wait_for_go; ++ ++ omap_dss_add_overlay(ovl); ++ ++ r = kobject_init_and_add(&ovl->kobj, &overlay_ktype, ++ &pdev->dev.kobj, "overlay%d", i); ++ ++ if (r) { ++ DSSERR("failed to create sysfs file\n"); ++ continue; ++ } ++ ++ dispc_overlays[i] = ovl; ++ } ++ ++#ifdef L4_EXAMPLE ++ { ++ struct omap_overlay *ovl; ++ ovl = kzalloc(sizeof(*ovl), GFP_KERNEL); ++ ++ BUG_ON(ovl == NULL); ++ ++ ovl->name = "l4"; ++ ovl->supported_modes = OMAP_DSS_COLOR_RGB24U; ++ ++ ovl->set_manager = &omap_dss_set_manager; ++ ovl->unset_manager = &omap_dss_unset_manager; ++ ovl->set_overlay_info = &dss_ovl_set_overlay_info; ++ ovl->get_overlay_info = &dss_ovl_get_overlay_info; ++ ++ omap_dss_add_overlay(ovl); ++ ++ r = kobject_init_and_add(&ovl->kobj, &overlay_ktype, ++ &pdev->dev.kobj, "overlayl4"); ++ ++ if (r) ++ DSSERR("failed to create sysfs file\n"); ++ ++ l4_overlays[0] = ovl; ++ } ++#endif ++} ++ ++/* connect overlays to the new device, if not already connected. if force ++ * selected, connect always. */ ++void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) ++{ ++ int i; ++ struct omap_overlay_manager *lcd_mgr; ++ struct omap_overlay_manager *tv_mgr; ++ struct omap_overlay_manager *mgr = NULL; ++ ++ lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD); ++ tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV); ++ ++ if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) { ++ if (!lcd_mgr->device || force) { ++ if (lcd_mgr->device) ++ lcd_mgr->unset_device(lcd_mgr); ++ lcd_mgr->set_device(lcd_mgr, dssdev); ++ mgr = lcd_mgr; ++ } ++ } ++ ++ if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { ++ if (!tv_mgr->device || force) { ++ if (tv_mgr->device) ++ tv_mgr->unset_device(tv_mgr); ++ tv_mgr->set_device(tv_mgr, dssdev); ++ mgr = tv_mgr; ++ } ++ } ++ ++ if (mgr) { ++ for (i = 0; i < 3; i++) { ++ struct omap_overlay *ovl; ++ ovl = omap_dss_get_overlay(i); ++ if (!ovl->manager || force) { ++ if (ovl->manager) ++ omap_dss_unset_manager(ovl); ++ omap_dss_set_manager(ovl, mgr); ++ } ++ } ++ } ++} ++ ++void dss_uninit_overlays(struct platform_device *pdev) ++{ ++ struct omap_overlay *ovl; ++ ++ while (!list_empty(&overlay_list)) { ++ ovl = list_first_entry(&overlay_list, ++ struct omap_overlay, list); ++ list_del(&ovl->list); ++ kobject_del(&ovl->kobj); ++ kobject_put(&ovl->kobj); ++ kfree(ovl); ++ } ++ ++ num_overlays = 0; ++} ++ +-- +1.6.2.4 + |