diff options
author | Per-Daniel Olsson <per-daniel.olsson@stericsson.com> | 2012-02-10 20:10:44 +0100 |
---|---|---|
committer | Philippe Langlais <philippe.langlais@stericsson.com> | 2012-05-22 11:04:28 +0200 |
commit | ec3fd76a1edd94995318f1c3118e4abe8e47aced (patch) | |
tree | 86bb7fd5a49465bf45cb562e993ff4b74c175567 | |
parent | fdd0c051aff401925d7e6a7118931245052d434a (diff) |
misc: clonedev: New device for cloning
A new device for content cloning between drifferent instances of comdev has
been added.
ST-Ericsson Linux next: NA
ST-Ericsson ID: 404691
ST-Ericsson FOSS-OUT ID: Trivial
Change-Id: I8c96bede685fad46ebb51b0217c48164dcad6659
Signed-off-by: Per-Daniel Olsson <per-daniel.olsson@stericsson.com>
Conflicts:
drivers/video/mcde/display-av8100.c
Signed-off-by: Per-Daniel Olsson <per-daniel.olsson@stericsson.com>
Change-Id: I8c96bede685fad46ebb51b0217c48164dcad6659
Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/49977
Reviewed-by: Robert FEKETE <robert.fekete@stericsson.com>
-rw-r--r-- | drivers/misc/Kconfig | 16 | ||||
-rw-r--r-- | drivers/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/clonedev/Makefile | 5 | ||||
-rw-r--r-- | drivers/misc/clonedev/clonedev.c | 312 | ||||
-rw-r--r-- | drivers/video/mcde/display-av8100.c | 42 | ||||
-rw-r--r-- | include/linux/clonedev.h | 50 |
6 files changed, 424 insertions, 2 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index c7c7114859f..0e2c6a34f19 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -479,6 +479,22 @@ config COMPDEV This driver replaces the use of the framebuffer The device allows for posting userspace buffers to be used with the overlays. +config CLONEDEV + bool "Display cloning device" + depends on FB_MCDE && HWMEM && COMPDEV + default n + help + This driver provides a way to clone content between two compdev + devices. + +config CLONEDEV_DEBUG + bool "Display cloning device debug" + depends on CLONEDEV + default n + help + This driver provides a way to clone content between two compdev + devices. + config PCH_PHUB tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) PHUB" depends on PCI diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 6f925aafbd5..570f57e2717 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -48,6 +48,7 @@ obj-y += lis3lv02d/ obj-y += carma/ obj-$(CONFIG_DISPDEV) += dispdev/ obj-$(CONFIG_COMPDEV) += compdev/ +obj-$(CONFIG_CLONEDEV) += clonedev/ obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ obj-$(CONFIG_MAX8997_MUIC) += max8997-muic.o diff --git a/drivers/misc/clonedev/Makefile b/drivers/misc/clonedev/Makefile new file mode 100644 index 00000000000..f84859dd3ee --- /dev/null +++ b/drivers/misc/clonedev/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_CLONEDEV) += clonedev.o + +ifdef CONFIG_CLONEDEV_DEBUG +EXTRA_CFLAGS += -DDEBUG +endif diff --git a/drivers/misc/clonedev/clonedev.c b/drivers/misc/clonedev/clonedev.c new file mode 100644 index 00000000000..d3b770fd324 --- /dev/null +++ b/drivers/misc/clonedev/clonedev.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * Device for display cloning on external output. + * + * Author: Per-Daniel Olsson <per-daniel.olsson@stericsson.com> + * for ST-Ericsson. + * + * License terms: GNU General Public License (GPL), version 2. + */ + +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/list.h> +#include <linux/mutex.h> +#include <linux/ioctl.h> + +#include <linux/clonedev.h> + +#include <linux/compdev.h> +#include <linux/mm.h> +#include <video/mcde.h> + +static LIST_HEAD(dev_list); +static DEFINE_MUTEX(dev_list_lock); + +struct clonedev { + struct mutex lock; + struct miscdevice mdev; + struct list_head list; + bool open; + struct compdev *src_compdev; + struct compdev *dst_compdev; + bool overlay_case; + struct compdev_size dst_size; + struct compdev_scene_info s_info; +}; + +static void best_fit(struct compdev_rect *src_rect, + struct compdev_size *dst_size, + struct compdev_img *img) +{ + /* aspect ratio in 26.6 fixed point */ + int aspect = 1; + int dst_w; + int dst_h; + + if (img->rotation == COMPDEV_ROT_90_CCW || + img->rotation == COMPDEV_ROT_270_CCW) + aspect = (src_rect->height << 6) / src_rect->width; + else + aspect = (src_rect->width << 6) / src_rect->height; + + dst_w = aspect * dst_size->height >> 6; + dst_h = dst_size->height; + img->dst_rect.y = 0; + + if (dst_w > dst_size->width) { + /* + * Destination rectangle too wide. + * Clamp to image width. Keep aspect ratio. + */ + dst_h = (dst_size->width << 6) / aspect; + dst_w = dst_size->width; + } + + /* center the image */ + if (dst_w < dst_size->width) { + int offset = (dst_size->width - dst_w) / 2; + img->dst_rect.x = offset; + } + + if (dst_h < dst_size->height) { + int offset = (dst_size->height - dst_h) / 2; + img->dst_rect.y = offset; + } + + img->dst_rect.width = dst_w; + img->dst_rect.height = dst_h; +} + +static int clonedev_open(struct inode *inode, struct file *file) +{ + struct clonedev *cd = NULL; + + mutex_lock(&dev_list_lock); + list_for_each_entry(cd, &dev_list, list) + if (cd->mdev.minor == iminor(inode)) + break; + + if (&cd->list == &dev_list) { + mutex_unlock(&dev_list_lock); + return -ENODEV; + } + + if (cd->open) { + mutex_unlock(&dev_list_lock); + return -EBUSY; + } + + cd->open = true; + + mutex_unlock(&dev_list_lock); + + file->private_data = cd; + + return 0; +} + +static int clonedev_release(struct inode *inode, struct file *file) +{ + struct clonedev *cd = NULL; + + mutex_lock(&dev_list_lock); + list_for_each_entry(cd, &dev_list, list) + if (cd->mdev.minor == iminor(inode)) + break; + mutex_unlock(&dev_list_lock); + + if (&cd->list == &dev_list) + return -ENODEV; + + cd->open = false; + return 0; +} + +static long clonedev_ioctl(struct file *file, + unsigned int cmd, + unsigned long arg) +{ + int ret; + struct clonedev *cd = (struct clonedev *)file->private_data; + + mutex_lock(&cd->lock); + + switch (cmd) { + case CLONEDEV_SET_MODE_IOC: + /* TODO: Get the user data */ + + break; + + default: + ret = -ENOSYS; + } + + mutex_unlock(&cd->lock); + + return ret; +} + +static const struct file_operations clonedev_fops = { + .open = clonedev_open, + .release = clonedev_release, + .unlocked_ioctl = clonedev_ioctl, +}; + +static void init_clonedev(struct clonedev *cd, const char *name) +{ + mutex_init(&cd->lock); + INIT_LIST_HEAD(&cd->list); + + cd->mdev.minor = MISC_DYNAMIC_MINOR; + cd->mdev.name = name; + cd->mdev.fops = &clonedev_fops; +} + +static void clonedev_post_buffer_callback(void *data, + struct compdev_img *cb_img) +{ + struct clonedev *cd = (struct clonedev *)data; + + mutex_lock(&cd->lock); + + if (!cd->overlay_case || (cd->overlay_case && + (cb_img->flags & COMPDEV_OVERLAY_FLAG))) { + struct compdev_img img; + + img = *cb_img; + + if (img.flags & COMPDEV_BYPASS_FLAG) + img.flags &= ~COMPDEV_BYPASS_FLAG; + + if (cd->overlay_case) + img.rotation = cd->s_info.ovly_rotation; + else + img.rotation = cd->s_info.fb_rotation; + + best_fit(&img.src_rect, &cd->dst_size, &img); + + compdev_post_buffer(cd->dst_compdev, &img); + } + mutex_unlock(&cd->lock); +} + +static void clonedev_post_scene_info_callback(void *data, + struct compdev_scene_info *s_info) +{ + struct clonedev *cd = (struct clonedev *)data; + + mutex_lock(&cd->lock); + if (s_info->img_count > 1) + cd->overlay_case = true; + else + cd->overlay_case = false; + + cd->s_info = *s_info; + cd->s_info.img_count = 1; + compdev_post_scene_info(cd->dst_compdev, &cd->s_info); + mutex_unlock(&cd->lock); +} + +int clonedev_create(void) +{ + int ret; + struct clonedev *cd; + + static int counter; + char name[10]; + + cd = kzalloc(sizeof(struct clonedev), GFP_KERNEL); + if (!cd) + return -ENOMEM; + + snprintf(name, sizeof(name), "%s%d", CLONEDEV_DEFAULT_DEVICE_PREFIX, + counter++); + init_clonedev(cd, name); + + ret = misc_register(&cd->mdev); + if (ret) + goto fail_register_misc; + mutex_lock(&dev_list_lock); + list_add_tail(&cd->list, &dev_list); + mutex_unlock(&dev_list_lock); + + mutex_lock(&cd->lock); + + compdev_get(0, &cd->src_compdev); + compdev_get(1, &cd->dst_compdev); + compdev_get_size(cd->dst_compdev, &cd->dst_size); + + compdev_register_listener_callbacks(cd->src_compdev, (void *)cd, + &clonedev_post_buffer_callback, + &clonedev_post_scene_info_callback); + + mutex_unlock(&cd->lock); + goto out; + +fail_register_misc: + kfree(cd); +out: + return ret; +} + +void clonedev_destroy(void) +{ + struct clonedev *cd; + struct clonedev *tmp; + + mutex_lock(&dev_list_lock); + list_for_each_entry_safe(cd, tmp, &dev_list, list) { + compdev_put(cd->src_compdev); + compdev_put(cd->dst_compdev); + compdev_deregister_callbacks(cd->src_compdev); + list_del(&cd->list); + misc_deregister(&cd->mdev); + kfree(cd); + break; + } + mutex_unlock(&dev_list_lock); +} + +static void clonedev_destroy_all(void) +{ + struct clonedev *cd; + struct clonedev *tmp; + + mutex_lock(&dev_list_lock); + list_for_each_entry_safe(cd, tmp, &dev_list, list) { + list_del(&cd->list); + misc_deregister(&cd->mdev); + kfree(cd); + } + mutex_unlock(&dev_list_lock); + + mutex_destroy(&dev_list_lock); +} + +static int __init clonedev_init(void) +{ + pr_info("%s\n", __func__); + + mutex_init(&dev_list_lock); + + return 0; +} +module_init(clonedev_init); + +static void __exit clonedev_exit(void) +{ + clonedev_destroy_all(); + pr_info("%s\n", __func__); +} +module_exit(clonedev_exit); + +MODULE_AUTHOR("Per-Daniel Olsson <per-daniel.olsson@stericsson.com>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Device for display cloning on external output"); + diff --git a/drivers/video/mcde/display-av8100.c b/drivers/video/mcde/display-av8100.c index b443f7f1760..70750998824 100644 --- a/drivers/video/mcde/display-av8100.c +++ b/drivers/video/mcde/display-av8100.c @@ -17,6 +17,8 @@ #include <linux/gpio.h> #include <linux/io.h> #include <linux/slab.h> +#include <linux/compdev.h> +#include <linux/clonedev.h> #include <video/mcde_fb.h> #include <video/mcde_display.h> @@ -199,6 +201,9 @@ static ssize_t store_disponoff(struct device *dev, bool enable = false; u8 cea = 0; u8 vesa_cea_nr = 0; +#ifdef CONFIG_COMPDEV + struct mcde_fb *mfb; +#endif dev_dbg(dev, "%s\n", __func__); @@ -211,7 +216,7 @@ static ssize_t store_disponoff(struct device *dev, vesa_cea_nr = (hex_to_bin(buf[4]) << 4) + hex_to_bin(buf[5]); dev_dbg(dev, "enable:%d cea:%d nr:%d\n", enable, cea, vesa_cea_nr); - if (enable && !mdev->enabled && mdev->fbi == NULL) { + if (enable && !mdev->fbi) { struct display_driver_data *driver_data = dev_get_drvdata(dev); u16 w = mdev->native_x_res; u16 h = mdev->native_y_res, vh; @@ -226,7 +231,40 @@ static ssize_t store_disponoff(struct device *dev, dev_warn(dev, "fb create failed\n"); else driver_data->fbdevname = dev_name(fbi->dev); - } else if (!enable && mdev->enabled) { + +#ifdef CONFIG_COMPDEV + /* TODO need another way for compdev to get actual size */ + mdev->native_x_res = w; + mdev->native_y_res = h; + + mfb = to_mcde_fb(fbi); + /* Create a compdev overlay for this display */ + if (compdev_create(mdev, mfb->ovlys[0], false) < 0) { + dev_warn(&mdev->dev, + "Failed to create compdev for display %s\n", + mdev->name); + } else { + dev_dbg(&mdev->dev, "compdev created for (%s)\n", + mdev->name); + } +#ifdef CONFIG_CLONEDEV + if (clonedev_create()) { + dev_warn(&mdev->dev, + "Failed to create clonedev for display %s\n", + mdev->name); + } else { + dev_dbg(&mdev->dev, "clonedev created for (%s)\n", + mdev->name); + } +#endif +#endif + } else if (!enable && mdev->fbi) { +#ifdef CONFIG_CLONEDEV + clonedev_destroy(); +#endif +#ifdef CONFIG_COMPDEV + compdev_destroy(mdev); +#endif mcde_fb_destroy(mdev); } diff --git a/include/linux/clonedev.h b/include/linux/clonedev.h new file mode 100644 index 00000000000..575233f07e9 --- /dev/null +++ b/include/linux/clonedev.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) ST-Ericsson SA 2011 + * + * ST-Ericsson Display overlay compositer device driver + * + * Author: Per-Daniel Olsson <per-daniel.olsson@stericsson.com> + * for ST-Ericsson. + * + * License terms: GNU General Public License (GPL), version 2. + */ + +#ifndef _CLONEDEV_H_ +#define _CLONEDEV_H_ + +#if !defined(__KERNEL__) && !defined(_KERNEL) +#include <stdint.h> +#else +#include <linux/types.h> +#include <video/mcde.h> +#endif + +#if defined(__KERNEL__) || defined(_KERNEL) +#include <linux/mm_types.h> +#include <linux/bitops.h> +#else +#define BIT(nr) (1UL << (nr)) +#endif + +#define CLONEDEV_DEFAULT_DEVICE_PREFIX "clone" + +/* Cloning mode */ +enum clonedev_mode { + CLONEDEV_CLONE_NONE, + CLONEDEV_CLONE_VIDEO_OR_UI, + CLONEDEV_CLONE_VIDEO_AND_UI, + CLONEDEV_CLONE_VIDEO, + CLONEDEV_CLONE_UI, +}; + +#define CLONEDEV_SET_MODE_IOC _IOW('D', 1, __u32*) + +#ifdef __KERNEL__ + +int clonedev_create(void); +void clonedev_destroy(void); + +#endif /* __KERNEL__ */ + +#endif /* _CLONEDEV_H_ */ + |