diff options
Diffstat (limited to 'drivers/video/samsung_duallcd/extension/s5p_fimd_ext.c')
-rw-r--r-- | drivers/video/samsung_duallcd/extension/s5p_fimd_ext.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/drivers/video/samsung_duallcd/extension/s5p_fimd_ext.c b/drivers/video/samsung_duallcd/extension/s5p_fimd_ext.c new file mode 100644 index 0000000..0c197b6 --- /dev/null +++ b/drivers/video/samsung_duallcd/extension/s5p_fimd_ext.c @@ -0,0 +1,209 @@ +/* linux/drivers/video/samsung/s5p_fimd_ext.c + * + * Samsung SoC FIMD Extension Device Framework. + * + * Inki Dae <inki.dae@samsung.com> + * + * 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. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/types.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/platform_device.h> + +#include <plat/fimd_lite_ext.h> + +#include "s5p_fimd_ext.h" + +struct s5p_fimd_ext { + struct list_head list; + struct device *dev; +}; + +static LIST_HEAD(fimd_ext_list); +static DEFINE_MUTEX(fimd_ext_lock); + +struct s5p_fimd_ext_device *to_fimd_ext_device(struct device *dev) +{ + return dev ? container_of(dev, struct s5p_fimd_ext_device, dev) : NULL; +} +EXPORT_SYMBOL(to_fimd_ext_device); + +struct s5p_fimd_ext_driver *to_fimd_ext_driver(struct device_driver *drv) +{ + return drv ? container_of(drv, struct s5p_fimd_ext_driver, driver) : + NULL; +} +EXPORT_SYMBOL(to_fimd_ext_driver); + +static ssize_t modalias_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct s5p_fimd_ext_device *fx_dev = to_fimd_ext_device(dev); + int len = snprintf(buf, PAGE_SIZE, "platform:%s\n", fx_dev->name); + + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; +} + +static struct device_attribute s5p_fimd_ext_dev_attrs[] = { + __ATTR_RO(modalias), + __ATTR_NULL, +}; + +static int s5p_fimd_ext_match_device(struct device *dev, + struct device_driver *drv) +{ + const struct s5p_fimd_ext_device *fx_dev = to_fimd_ext_device(dev); + const struct s5p_fimd_ext_driver *fx_drv = to_fimd_ext_driver(drv); + + return strcmp(fx_dev->name, fx_drv->driver.name) == 0; +} + +struct bus_type s5p_fimd_ext_bus_type = { + .name = "fimd_ext", + .dev_attrs = s5p_fimd_ext_dev_attrs, + .match = s5p_fimd_ext_match_device, +}; + +static int s5p_fimd_ext_drv_probe(struct device *dev) +{ + const struct s5p_fimd_ext_driver *fx_drv = + to_fimd_ext_driver(dev->driver); + + return fx_drv->probe(to_fimd_ext_device(dev)); +} + +struct resource *s5p_fimd_ext_get_resource(struct s5p_fimd_ext_device *fx_dev, + unsigned int type, unsigned int num) +{ + int i; + + for (i = 0; i < fx_dev->num_resources; i++) { + struct resource *r = &fx_dev->resource[i]; + + if (type == resource_type(r) && num-- == 0) + return r; + } + + return NULL; +} + +int s5p_fimd_ext_get_irq(struct s5p_fimd_ext_device *fx_dev, unsigned int num) +{ + struct resource *r = s5p_fimd_ext_get_resource(fx_dev, + IORESOURCE_IRQ, num); + + return r ? r->start : -ENXIO; +} + +int s5p_fimd_ext_device_register(struct s5p_fimd_ext_device *fx_dev) +{ + struct s5p_fimd_ext *fimd_ext; + int i, ret = 0; + + fimd_ext = kzalloc(sizeof(struct s5p_fimd_ext), GFP_KERNEL); + if (!fimd_ext) { + printk(KERN_ERR "failed to allocate fimd_ext object.\n"); + return -EFAULT; + } + + fimd_ext->dev = &fx_dev->dev; + + device_initialize(&fx_dev->dev); + + if (!fx_dev->dev.parent) + fx_dev->dev.parent = &platform_bus; + + fx_dev->dev.bus = &s5p_fimd_ext_bus_type; + + if (fx_dev->id != -1) + dev_set_name(&fx_dev->dev, "%s.%d", fx_dev->name, fx_dev->id); + else + dev_set_name(&fx_dev->dev, "%s", fx_dev->name); + + for (i = 0; i < fx_dev->num_resources; i++) { + struct resource *r = &fx_dev->resource[i]; + + if (r->name == NULL) + r->name = dev_name(&fx_dev->dev); + } + + ret = device_add(&fx_dev->dev); + if (ret == 0) { + mutex_lock(&fimd_ext_lock); + list_add_tail(&fimd_ext->list, &fimd_ext_list); + mutex_unlock(&fimd_ext_lock); + + return ret; + } + + kfree(fimd_ext); + + return ret; +} +EXPORT_SYMBOL(s5p_fimd_ext_device_register); + +int s5p_fimd_ext_driver_register(struct s5p_fimd_ext_driver *fx_drv) +{ + fx_drv->driver.bus = &s5p_fimd_ext_bus_type; + if (fx_drv->probe) + fx_drv->driver.probe = s5p_fimd_ext_drv_probe; + + /* add some callbacks here. */ + + printk(KERN_DEBUG "registered a driver(%s) to fimd_ext driver.\n", + fx_drv->driver.name); + + return driver_register(&fx_drv->driver); +} +EXPORT_SYMBOL(s5p_fimd_ext_driver_register); + +struct s5p_fimd_ext_device *s5p_fimd_ext_find_device(const char *name) +{ + struct s5p_fimd_ext *fimd_ext; + struct s5p_fimd_ext_device *fx_dev; + struct device *dev; + + mutex_lock(&fimd_ext_lock); + + printk(KERN_DEBUG "find ext driver (%s).\n", name); + + list_for_each_entry(fimd_ext, &fimd_ext_list, list) { + dev = fimd_ext->dev; + fx_dev = to_fimd_ext_device(dev); + + if ((strcmp(fx_dev->name, name)) == 0) { + mutex_unlock(&fimd_ext_lock); + printk(KERN_DEBUG "found!!!(%s).\n", fx_dev->name); + return fx_dev; + } + } + + printk(KERN_WARNING "failed to find ext device(%s).\n", name); + + mutex_unlock(&fimd_ext_lock); + + return NULL; +} + +static int __init s5p_fimd_ext_init(void) +{ + return bus_register(&s5p_fimd_ext_bus_type); +} + +static void __exit s5p_fimd_ext_exit(void) +{ +} + +early_initcall(s5p_fimd_ext_init); +module_exit(s5p_fimd_ext_exit); + +MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>"); +MODULE_DESCRIPTION("Samsung SoC FIMD Extension Framework"); +MODULE_LICENSE("GPL"); |