[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CACVXFVOh=_wAmQB1fm88RTbwoJxCo5w57-TpiH8zx5V-CC_TqA@mail.gmail.com>
Date: Sun, 27 Nov 2011 11:40:24 +0800
From: Ming Lei <tom.leiming@...il.com>
To: Sylwester Nawrocki <snjw23@...il.com>, Greg KH <greg@...ah.com>,
Alan Cox <alan@...rguk.ukuu.org.uk>,
linux-media <linux-media@...r.kernel.org>
Cc: tony@...mide.com, arnd@...db.de, linux-omap@...r.kernel.org,
linux-arm-kernel@...ts.infradead.org, linux-kernel@...r.kernel.org
Subject: Re: [PATCH 3/3] drivers/misc: introduce face detection module driver(fdif)
Hi guys,
Thanks for your comment.
On Sun, Nov 27, 2011 at 6:16 AM, Sylwester Nawrocki <snjw23@...il.com> wrote:
> Cc: LMML
>
> On 11/26/2011 05:31 AM, tom.leiming@...il.com wrote:
>> From: Ming Lei <ming.lei@...onical.com>
>>
>> One face detection IP[1] is integared inside OMAP4 SoC, so
>> introduce this driver to make face detection function work
>> on OMAP4 SoC.
>
> Face detection IP is of course not specific to OMAP, I've seen it in other SoCs
> already and integrated with the video capture pipeline.
Yes, the driver is platform independent, so at least it can support
the same IP on different platforms.
>
> And it clearly belongs to the media subsystem, there is already an infrastructure
> there that don't need to be re-invented, like buffer management and various IO
> method support.
>
> I think there is not much needed on top of that to support FD. We have already
> various mem-to-mem devices in V4L2, like video or image encoders or video
> post-processors.
I have thought about the FD implementation on v4l2 core, but still not
very clear
how to do it. I will study v4l2 further to figure out how to do it.
Now below are the basic requirements from FD:
- FD on video stream or pictures from external files
- FD on video stream or pictures from video device
(such as camera)
- the input video format may be different for different FD IP
- one method is required to start or stop FD
- one method is required to report the detection results to user space
Any suggestions on how to implement FD on v4l2?
thanks,
--
Ming Lei
>
>>
>> This driver is platform independent, so in theory can
>> be used to drive same IP module on other platforms.
>>
>> [1], ch9 of OMAP4 TRM
>>
>> Signed-off-by: Ming Lei <ming.lei@...onical.com>
>> ---
>> drivers/misc/Kconfig | 7 +
>> drivers/misc/Makefile | 1 +
>> drivers/misc/fdif.c | 874 +++++++++++++++++++++++++++++++++++++++++++++++++
>> include/linux/fdif.h | 67 ++++
>> include/linux/major.h | 1 +
>> 5 files changed, 950 insertions(+), 0 deletions(-)
>> create mode 100644 drivers/misc/fdif.c
>> create mode 100644 include/linux/fdif.h
>>
>> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
>> index 5664696..884d8c7 100644
>> --- a/drivers/misc/Kconfig
>> +++ b/drivers/misc/Kconfig
>> @@ -500,6 +500,13 @@ config USB_SWITCH_FSA9480
>> stereo and mono audio, video, microphone and UART data to use
>> a common connector port.
>>
>> +config FDIF
>> + tristate "Face Detection module"
>> + help
>> + The FDIF is a face detection module, which can be integrated into
>> + SoCs to detect the location of human beings' face in one image. At
>> + least now, TI OMAP4 has the module inside.
>> +
>> source "drivers/misc/c2port/Kconfig"
>> source "drivers/misc/eeprom/Kconfig"
>> source "drivers/misc/cb710/Kconfig"
>> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
>> index b26495a..0ed85ef 100644
>> --- a/drivers/misc/Makefile
>> +++ b/drivers/misc/Makefile
>> @@ -47,4 +47,5 @@ obj-$(CONFIG_AB8500_PWM) += ab8500-pwm.o
>> obj-y += lis3lv02d/
>> obj-y += carma/
>> obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
>> +obj-$(CONFIG_FDIF) += fdif.o
>> obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
>> diff --git a/drivers/misc/fdif.c b/drivers/misc/fdif.c
>> new file mode 100644
>> index 0000000..84a7049
>> --- /dev/null
>> +++ b/drivers/misc/fdif.c
>> @@ -0,0 +1,874 @@
>> +/*
>> + * fdif.c -- face detection module driver
>> + *
>> + * Copyright (C) 2011 Ming Lei (ming.lei@...onical.com)
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * 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, write to the Free Software
>> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
>> + *
>> + */
>> +
>> +/*****************************************************************************/
>> +
>> +#include <linux/init.h>
>> +#include <linux/fs.h>
>> +#include <linux/mm.h>
>> +#include <linux/slab.h>
>> +#include <linux/signal.h>
>> +#include <linux/wait.h>
>> +#include <linux/poll.h>
>> +#include <linux/module.h>
>> +#include <linux/major.h>
>> +#include <linux/cdev.h>
>> +#include <linux/mman.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/delay.h>
>> +#include <linux/user_namespace.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/fdif.h>
>> +#include <asm/uaccess.h>
>> +#include <asm/byteorder.h>
>> +#include <asm/io.h>
>> +
>> +#undef DEBUG
>> +
>> +#define FDIF_DEV MKDEV(FDIF_MAJOR, 0)
>> +#define FDIF_MAX_MINORS 8
>> +
>> +#define WORK_MEM_SIZE (52*1024)
>> +
>> +#define FACE_SIZE_20_PIXELS 0
>> +#define FACE_SIZE_25_PIXELS 1
>> +#define FACE_SIZE_32_PIXELS 2
>> +#define FACE_SIZE_40_PIXELS 3
>> +
>> +#define FACE_DIR_UP 0
>> +#define FACE_DIR_RIGHT 1
>> +#define FACE_DIR_LIFT 2
>> +
>> +/* 9.5 FDIF Register Manua of TI OMAP4 TRM */
>> +#define FDIF_REVISION 0x0
>> +#define FDIF_HWINFO 0x4
>> +#define FDIF_SYSCONFIG 0x10
>> +#define SOFTRESET (1 << 0)
>> +
>> +#define FDIF_IRQSTATUS_RAW_j (0x24 + 2*0x10)
>> +#define FDIF_IRQSTATUS_j (0x28 + 2*0x10)
>> +#define FDIF_IRQENABLE_SET_j (0x2c + 2*0x10)
>> +#define FDIF_IRQENABLE_CLR_j (0x30 + 2*0x10)
>> +#define FINISH_IRQ (1 << 8)
>> +#define ERR_IRQ (1 << 0)
>> +
>> +#define FDIF_PICADDR 0x60
>> +#define FDIF_CTRL 0x64
>> +#define CTRL_MAX_TAGS 0x0A
>> +
>> +#define FDIF_WKADDR 0x68
>> +#define FD_CTRL 0x80
>> +#define CTRL_FINISH (1 << 2)
>> +#define CTRL_RUN (1 << 1)
>> +#define CTRL_SRST (1 << 0)
>> +
>> +
>> +#define FD_DNUM 0x84
>> +#define FD_DCOND 0x88
>> +#define FD_STARTX 0x8c
>> +#define FD_STARTY 0x90
>> +#define FD_SIZEX 0x94
>> +#define FD_SIZEY 0x98
>> +#define FD_LHIT 0x9c
>> +#define FD_CENTERX_i 0x160
>> +#define FD_CENTERY_i 0x164
>> +#define FD_CONFSIZE_i 0x168
>> +#define FD_ANGLE_i 0x16c
>> +
>> +static irqreturn_t handle_detection(int irq, void *__fdif);
>> +
>> +struct fdif {
>> + struct platform_device *pdev;
>> + void __iomem *base;
>> + struct mutex mutex;
>> + int open_count;
>> + int irq;
>> + struct device *dev;
>> + dma_addr_t pict_dma;
>> + dma_addr_t work_dma;
>> +
>> + /* wake up if a face detection completed */
>> + wait_queue_head_t wait;
>> +
>> + int read_idx;
>> +
>> + /*setting*/
>> + struct fdif_setting s;
>> +
>> + /*face detection result*/
>> + int face_cnt;
>> + struct fdif_result faces[MAX_FACE_COUNT];
>> +};
>> +
>> +static struct class *fdif_class;
>> +
>> +/*only support one fdif device now*/
>> +static struct fdif *g_fdif;
>> +
>> +static inline void fdif_writel(void __iomem *base, u32 reg, u32 val)
>> +{
>> + __raw_writel(val, base + reg);
>> +}
>> +
>> +static inline u32 fdif_readl(void __iomem *base, u32 reg)
>> +{
>> + return __raw_readl(base + reg);
>> +}
>> +
>> +#ifdef DEBUG
>> +static void dump_fdif_setting(struct fdif *fdif, const char *func)
>> +{
>> + printk("%s: %s\n", func, __func__);
>> + printk("picture addr:%8p, work mem addr:%8p\n",
>> + fdif->s.pict_addr, fdif->s.work_mem_addr);
>> + printk("face size=%2d, face dir=%2d, lhit=%d\n",
>> + fdif->s.min_face_size, fdif->s.face_dir,
>> + fdif->s.lhit);
>> + printk("startx =%4d starty=%4d sizex=%4d sizey=%4d\n",
>> + fdif->s.startx, fdif->s.starty,
>> + fdif->s.sizex, fdif->s.sizey);
>> +}
>> +
>> +static void dump_fdif_results(struct fdif *fdif, const char *func)
>> +{
>> + int idx;
>> +
>> + printk("%s: %s\n", func, __func__);
>> +
>> + printk("found %d faces\n", fdif->face_cnt);
>> + for(idx=0; idx < fdif->face_cnt; idx++) {
>> + struct fdif_result *fr = &fdif->faces[idx];
>> + printk(" No.%d x=%3d y=%2d sz=%2d ang=%3d conf=%2d\n",
>> + idx, fr->centerx, fr->centery,
>> + fr->size, fr->angle, fr->confidence);
>> + }
>> +}
>> +
>> +static void dump_fdif_regs(struct fdif *fdif, const char *func)
>> +{
>> + printk("%s:%s\n", __func__, func);
>> + printk("FDIF_CTRL=%08x FDIF_SYSCONFIG=%08x\n",
>> + fdif_readl(fdif->base, FDIF_CTRL),
>> + fdif_readl(fdif->base, FDIF_SYSCONFIG));
>> + printk("FDIF_IRQSTATUS_RAW_j=%08x FDIF_IRQSTATUS_j=%08x\n",
>> + fdif_readl(fdif->base, FDIF_IRQSTATUS_RAW_j),
>> + fdif_readl(fdif->base, FDIF_IRQSTATUS_j));
>> + printk("FDIF_PICADDR=%08x FDIF_WKADDR=%08x\n",
>> + fdif_readl(fdif->base, FDIF_PICADDR),
>> + fdif_readl(fdif->base, FDIF_WKADDR));
>> + printk("FD_CTRL=%04x\n", fdif_readl(fdif->base, FD_CTRL));
>> +}
>> +
>> +#else
>> +static inline void dump_fdif_setting(struct fdif *fdif, const char *func)
>> +{
>> +}
>> +static inline void dump_fdif_results(struct fdif *fdif, const char *func)
>> +{
>> +}
>> +static inline void dump_fdif_regs(struct fdif *fdif, const char *func)
>> +{
>> +}
>> +#endif
>> +
>> +static void free_buffer(struct fdif *fdif)
>> +{
>> + int order;
>> +
>> + order = get_order(PICT_SIZE_X * PICT_SIZE_Y);
>> + free_pages((unsigned long)fdif->s.pict_addr, order);
>> +
>> + order = get_order(WORK_MEM_SIZE);
>> + free_pages((unsigned long)fdif->s.work_mem_addr, order);
>> +}
>> +
>> +static int allocate_buffer(struct fdif *fdif)
>> +{
>> + struct device *dev = &fdif->pdev->dev;
>> + int order, ret;
>> +
>> + order = get_order(PICT_SIZE_X * PICT_SIZE_Y);
>> + fdif->s.pict_addr = (void *)__get_free_pages(GFP_KERNEL, order);
>> + if (!fdif->s.pict_addr) {
>> + dev_err(dev, "fdif pict buffer allocation(%d) failed\n",
>> + order);
>> + ret = -ENOMEM;
>> + goto err_out;
>> + }
>> +
>> + order = get_order(WORK_MEM_SIZE);
>> + fdif->s.work_mem_addr = (void *)__get_free_pages(GFP_KERNEL, order);
>> + if (!fdif->s.work_mem_addr) {
>> + dev_err(dev, "fdif buffer allocation(%d) failed\n",
>> + order);
>> + ret = -ENOMEM;
>> + goto err_mem;
>> + }
>> + return 0;
>> +
>> +err_mem:
>> + free_buffer(fdif);
>> +err_out:
>> + return ret;
>> +}
>> +
>> +static int fdif_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> + struct fdif *fdif;
>> + struct resource *res;
>> + int ret;
>> +
>> + fdif = kzalloc(sizeof(*fdif), GFP_KERNEL);
>> + if (!fdif) {
>> + dev_err(dev, "Memory allocation failed\n");
>> + ret = -ENOMEM;
>> + goto end_probe;
>> + }
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + if (!res) {
>> + dev_err(dev, "fdif get resource failed\n");
>> + ret = -ENODEV;
>> + goto err_iomap;
>> + }
>> +
>> + fdif->base = ioremap(res->start, resource_size(res));
>> + if (!fdif->base) {
>> + dev_err(dev, "fdif ioremap failed\n");
>> + ret = -ENOMEM;
>> + goto err_iomap;
>> + }
>> +
>> + fdif->irq = platform_get_irq(pdev, 0);
>> + if (fdif->irq < 0) {
>> + dev_err(dev, "fdif get irq failed\n");
>> + ret = -ENODEV;
>> + goto err_get_irq;
>> + }
>> +
>> + ret = request_irq(fdif->irq, handle_detection, 0, "fdif", fdif);
>> + if (ret)
>> + goto err_get_irq;
>> +
>> + init_waitqueue_head(&fdif->wait);
>> + mutex_init(&fdif->mutex);
>> +
>> + pm_suspend_ignore_children(dev, true);
>> + fdif->dev = device_create(fdif_class, dev,
>> + MKDEV(FDIF_MAJOR, 0),
>> + NULL, "fdif");
>> + if (!fdif->dev) {
>> + ret = -ENOMEM;
>> + goto err_device_create;
>> + }
>> +
>> + fdif->pdev = pdev;
>> + platform_set_drvdata(pdev, fdif);
>> +
>> + pm_runtime_get_sync(dev);
>> + dev_info(dev, "fdif version=%8x hwinfo=%08x\n",
>> + fdif_readl(fdif->base, FDIF_REVISION),
>> + fdif_readl(fdif->base, FDIF_HWINFO));
>> + pm_runtime_put(dev);
>> +
>> + g_fdif = fdif;
>> + return 0;
>> +
>> +err_device_create:
>> + free_irq(fdif->irq, fdif);
>> +err_get_irq:
>> + iounmap(fdif->base);
>> +err_iomap:
>> + kfree(fdif);
>> +end_probe:
>> + return ret;
>> +}
>> +
>> +static int fdif_remove(struct platform_device *pdev)
>> +{
>> + struct fdif *fdif = platform_get_drvdata(pdev);
>> +
>> + device_destroy(fdif_class, MKDEV(FDIF_MAJOR, 0));
>> + free_irq(fdif->irq, fdif);
>> + iounmap(fdif->base);
>> + kfree(fdif);
>> + return 0;
>> +}
>> +
>> +static int fdif_suspend(struct platform_device *pdev, pm_message_t msg)
>> +{
>> + return 0;
>> +}
>> +
>> +static int fdif_resume(struct platform_device *pdev)
>> +{
>> + return 0;
>> +}
>> +
>> +static struct platform_device_id fdif_device_ids[] = {
>> + {.name = "fdif"},
>> + {},
>> +};
>> +
>> +struct platform_driver fdif_driver = {
>> + .probe = fdif_probe,
>> + .remove = fdif_remove,
>> + .suspend = fdif_suspend,
>> + .resume = fdif_resume,
>> + .driver = {
>> + .name = "fdif",
>> + .owner = THIS_MODULE,
>> + },
>> + .id_table = fdif_device_ids,
>> +};
>> +
>> +static void install_default_setting(struct fdif *fdif)
>> +{
>> + fdif->s.min_face_size = FACE_SIZE_25_PIXELS;
>> + fdif->s.face_dir = FACE_DIR_UP;
>> + fdif->s.startx = 0;
>> + fdif->s.starty = 0;
>> + fdif->s.sizex = 0x140;
>> + fdif->s.sizey = 0xf0;
>> + fdif->s.lhit = 0x5;
>> +}
>> +
>> +static void commit_image_setting(struct fdif *fdif)
>> +{
>> + unsigned long conf;
>> + struct device *dev = &fdif->pdev->dev;
>> +
>> + fdif->pict_dma = dma_map_single(dev, fdif->s.pict_addr,
>> + PICT_SIZE_X*PICT_SIZE_Y,
>> + DMA_TO_DEVICE);
>> + fdif_writel(fdif->base, FDIF_PICADDR, fdif->pict_dma);
>> +
>> + conf = (fdif->s.min_face_size & 0x3) ||
>> + ((fdif->s.face_dir & 0x3) << 2);
>> + fdif_writel(fdif->base, FD_DCOND, conf);
>> +
>> + fdif_writel(fdif->base, FD_STARTX, fdif->s.startx);
>> + fdif_writel(fdif->base, FD_STARTY, fdif->s.starty);
>> + fdif_writel(fdif->base, FD_SIZEX, fdif->s.sizex);
>> + fdif_writel(fdif->base, FD_SIZEY, fdif->s.sizey);
>> + fdif_writel(fdif->base, FD_LHIT, fdif->s.lhit);
>> +}
>> +
>> +static void start_detect(struct fdif *fdif)
>> +{
>> + unsigned long conf;
>> +
>> + dump_fdif_setting(fdif, __func__);
>> + dump_fdif_regs(fdif, __func__);
>> +
>> + fdif->face_cnt = -1;
>> + commit_image_setting(fdif);
>> +
>> + /*enable finish irq*/
>> + conf = FINISH_IRQ;
>> + fdif_writel(fdif->base, FDIF_IRQENABLE_SET_j, conf);
>> +
>> + /*set RUN flag*/
>> + conf = CTRL_RUN;
>> + fdif_writel(fdif->base, FD_CTRL, conf);
>> +}
>> +
>> +static void stop_detect(struct fdif *fdif)
>> +{
>> + unsigned long conf;
>> + struct device *dev = &fdif->pdev->dev;
>> +
>> + dma_unmap_single(dev, fdif->pict_dma,
>> + PICT_SIZE_X*PICT_SIZE_Y,
>> + DMA_TO_DEVICE);
>> + /*disable finish irq*/
>> + conf = FINISH_IRQ;
>> + fdif_writel(fdif->base, FDIF_IRQENABLE_CLR_j, conf);
>> +
>> + /*mark FINISH flag*/
>> + conf = CTRL_FINISH;
>> + fdif_writel(fdif->base, FD_CTRL, conf);
>> +}
>> +
>> +/*softreset fdif*/
>> +static int softreset_fdif(struct fdif *fdif)
>> +{
>> + unsigned long conf;
>> + int to = 0;
>> +
>> + conf = fdif_readl(fdif->base, FDIF_SYSCONFIG);
>> + conf |= SOFTRESET;
>> + fdif_writel(fdif->base, FDIF_SYSCONFIG, conf);
>> +
>> + while ((conf & SOFTRESET) && to++ < 2000) {
>> + conf = fdif_readl(fdif->base, FDIF_SYSCONFIG);
>> + udelay(2);
>> + }
>> +
>> + if (to == 2000)
>> + printk(KERN_ERR "%s: reset failed\n", __func__);
>> +
>> + return to == 2000;
>> +}
>> +
>> +static int read_faces(struct fdif *fdif)
>> +{
>> + int cnt;
>> + int idx = 0;
>> +
>> + cnt = fdif_readl(fdif->base, FD_DNUM) & 0x3f;
>> +
>> + fdif->face_cnt = cnt;
>> + fdif->read_idx = 0;
>> +
>> + while(idx < cnt) {
>> + struct fdif_result *fr = &fdif->faces[idx];
>> +
>> + fr->centerx = fdif_readl(fdif->base,
>> + FD_CENTERX_i + idx * 0x10) & 0x1ff;
>> + fr->centery = fdif_readl(fdif->base,
>> + FD_CENTERY_i + idx * 0x10) & 0xff;
>> + fr->angle = fdif_readl(fdif->base,
>> + FD_ANGLE_i + idx * 0x10) & 0x1ff;
>> + fr->size = fdif_readl(fdif->base,
>> + FD_CONFSIZE_i + idx * 0x10);
>> + fr->confidence = (fr->size >> 8) & 0xf;
>> + fr->size = fr->size & 0xff;
>> +
>> + idx++;
>> + }
>> +
>> + stop_detect(fdif);
>> + dump_fdif_results(fdif, __func__);
>> + wake_up(&fdif->wait);
>> +
>> + return fdif->face_cnt;
>> +}
>> +
>> +static irqreturn_t handle_detection(int irq, void *__fdif)
>> +{
>> + unsigned long irqsts;
>> + struct fdif *fdif = __fdif;
>> +
>> + dump_fdif_regs(fdif, __func__);
>> +
>> + /*clear irq status*/
>> + irqsts = fdif_readl(fdif->base, FDIF_IRQSTATUS_j);
>> + fdif_writel(fdif->base, FDIF_IRQSTATUS_j, irqsts);
>> +
>> + if (irqsts & FINISH_IRQ) {
>> + read_faces(fdif);
>> + } else if (irqsts & ERR_IRQ) {
>> + dev_err(&fdif->pdev->dev, "err irq!");
>> + softreset_fdif(fdif);
>> + } else {
>> + dev_err(&fdif->pdev->dev, "spurious irq!");
>> + }
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static void fdif_global_init(struct fdif *fdif)
>> +{
>> + unsigned long conf;
>> + struct device *dev = &fdif->pdev->dev;
>> +
>> + /*softreset fdif*/
>> + softreset_fdif(fdif);
>> +
>> + /*set max tags*/
>> + conf = fdif_readl(fdif->base, FDIF_CTRL);
>> + conf &= ~0x1e;
>> + conf |= (CTRL_MAX_TAGS << 1);
>> + fdif_writel(fdif->base, FDIF_CTRL, conf);
>> +
>> + /*enable error irq*/
>> + conf = ERR_IRQ;
>> + fdif_writel(fdif->base, FDIF_IRQENABLE_SET_j, conf);
>> +
>> + fdif->work_dma = dma_map_single(dev,
>> + fdif->s.work_mem_addr,
>> + WORK_MEM_SIZE,
>> + DMA_TO_DEVICE);
>> + fdif_writel(fdif->base, FDIF_WKADDR, fdif->work_dma);
>> +}
>> +
>> +static void fdif_global_deinit(struct fdif *fdif)
>> +{
>> + unsigned long conf;
>> + struct device *dev = &fdif->pdev->dev;
>> +
>> + /*enable error irq*/
>> + conf = ERR_IRQ;
>> + fdif_writel(fdif->base, FDIF_IRQENABLE_CLR_j, conf);
>> +
>> + dma_unmap_single(dev, fdif->work_dma,
>> + WORK_MEM_SIZE, DMA_TO_DEVICE);
>> +}
>> +
>> +/*
>> + * file operations
>> + */
>> +static int fdif_open(struct inode *inode, struct file *file)
>> +{
>> + struct fdif *fdif = g_fdif;
>> + int ret = 0;
>> +
>> + if (iminor(inode) || !fdif) {
>> + printk("fdif: device is not correct!\n");
>> + return -ENODEV;
>> + }
>> +
>> + mutex_lock(&fdif->mutex);
>> +
>> + if (!fdif->open_count++) {
>> +
>> + ret = allocate_buffer(fdif);
>> + if (ret)
>> + goto err_mem;
>> +
>> + install_default_setting(fdif);
>> +
>> + pm_runtime_get_sync(&fdif->pdev->dev);
>> + fdif_global_init(fdif);
>> + }
>> +
>> + file->private_data = fdif;
>> + goto err_out;
>> +
>> +err_mem:
>> + free_buffer(fdif);
>> +err_out:
>> + mutex_unlock(&fdif->mutex);
>> +
>> + return ret;
>> +}
>> +
>> +static int fdif_release(struct inode *inode, struct file *file)
>> +{
>> + struct fdif *fdif = file->private_data;
>> +
>> + if (!fdif)
>> + return -ENODEV;
>> +
>> + mutex_lock(&fdif->mutex);
>> +
>> + if (!--fdif->open_count) {
>> + stop_detect(fdif);
>> + fdif_global_deinit(fdif);
>> + pm_runtime_put(&fdif->pdev->dev);
>> + free_buffer(fdif);
>> + }
>> +
>> + mutex_unlock(&fdif->mutex);
>> + return 0;
>> +}
>> +
>> +static ssize_t fdif_read(struct file *file, char __user *buf, size_t nbytes,
>> + loff_t *ppos)
>> +{
>> + struct fdif *fdif = file->private_data;
>> + ssize_t ret = 0;
>> + unsigned len;
>> + loff_t pos = *ppos;
>> + int size;
>> +
>> + /*not support unaligned read*/
>> + if ((long)pos % sizeof(struct fdif_result))
>> + return -EFAULT;
>> +
>> + /*wait for completation of current detection*/
>> + wait_event_interruptible(fdif->wait, fdif->face_cnt != -1);
>> +
>> + mutex_lock(&fdif->mutex);
>> + if (fdif->face_cnt == -1) {
>> + ret = -ERESTARTSYS;
>> + goto err;
>> + }
>> +
>> + if (fdif->read_idx < 0) {
>> + ret = -EFAULT;
>> + goto err;
>> + }
>> +
>> + if (fdif->face_cnt <= fdif->read_idx)
>> + goto err;
>> +
>> + size = (fdif->face_cnt - fdif->read_idx) *
>> + sizeof(struct fdif_result);
>> + if (pos < size) {
>> + len = size - pos;
>> + if (len > nbytes)
>> + len = nbytes;
>> +
>> + len -= len % sizeof(struct fdif_result);
>> +
>> + if (copy_to_user(buf, &fdif->faces[fdif->read_idx], len)) {
>> + ret = -EFAULT;
>> + goto err;
>> + }
>> +
>> + fdif->read_idx += len / sizeof(struct fdif_result);
>> + *ppos += len;
>> + ret += len;
>> + }
>> +err:
>> + mutex_unlock(&fdif->mutex);
>> + return ret;
>> +}
>> +
>> +#ifdef DEBUG
>> +static void print_cmd(int cmd, void __user *p)
>> +{
>> + char buf[64];
>> + int arg, l = 63;
>> +
>> + switch (cmd) {
>> + case FDIF_START:
>> + strcpy(buf, "START");
>> + break;
>> + case FDIF_GET_FACE_CNT:
>> + strcpy(buf, "GET_CNT");
>> + break;
>> + case FDIF_STOP:
>> + strcpy(buf, "STOP");
>> + break;
>> + case FDIF_RESET:
>> + strcpy(buf, "RESET");
>> + break;
>> + case FDIF_GET_SETTING:
>> + strcpy(buf, "GET_SETTING");
>> + break;
>> + case FDIF_SET_MIN_FACE_SIZE:
>> + l = sprintf(buf, "SET_MIN_FACE_SIZE: %d",
>> + (get_user(arg,(unsigned int __user *)p) ? : arg));
>> + break;
>> + case FDIF_SET_STARTXY:
>> + l = sprintf(buf, "SET_STARTXY: %d",
>> + (get_user(arg,(unsigned int __user *)p) ? : arg));
>> + break;
>> + case FDIF_SET_SIZEXY:
>> + l = sprintf(buf, "SET_SIZEXY: %d",
>> + (get_user(arg,(unsigned int __user *)p) ? : arg));
>> + break;
>> + case FDIF_SET_FACE_DIR:
>> + l = sprintf(buf, "SET_FACE_DIR: %d",
>> + (get_user(arg,(unsigned int __user *)p) ? : arg));
>> + break;
>> + case FDIF_SET_LHIT:
>> + l = sprintf(buf, "SET_LHIT: %d",
>> + (get_user(arg,(unsigned int __user *)p) ? : arg));
>> + break;
>> + default:
>> + strcpy(buf, "????");
>> + }
>> +
>> + buf[l] = '\0';
>> +
>> + printk("%s: %s\n", __func__, buf);
>> +}
>> +#else
>> +static inline void print_cmd(int cmd, void __user *p)
>> +{
>> +}
>> +#endif
>> +
>> +static long fdif_ioctl(struct file *file, unsigned int cmd,
>> + unsigned long arg)
>> +{
>> + struct fdif *fdif = file->private_data;
>> + int ret = 0;
>> + int val;
>> +
>> + if (!(file->f_mode & FMODE_WRITE))
>> + return -EPERM;
>> +
>> + print_cmd(cmd, (void __user *)arg);
>> +
>> + mutex_lock(&fdif->mutex);
>> + switch (cmd) {
>> + case FDIF_START:
>> + start_detect(fdif);
>> + break;
>> + case FDIF_GET_FACE_CNT:
>> + if (put_user(fdif->face_cnt, (u32 __user *)arg))
>> + ret = -EFAULT;
>> + break;
>> + case FDIF_STOP:
>> + stop_detect(fdif);
>> + break;
>> + case FDIF_RESET:
>> + softreset_fdif(fdif);
>> + break;
>> + case FDIF_GET_SETTING:
>> + ret = (copy_to_user((void __user *)arg, &fdif->s,
>> + sizeof(struct fdif_setting)) ?
>> + -EFAULT : 0);
>> + break;
>> + case FDIF_SET_MIN_FACE_SIZE:
>> + ret = get_user(val, (int __user *)arg) ? -EFAULT : 0;
>> + if (!ret)
>> + fdif->s.min_face_size = val;
>> + break;
>> + case FDIF_SET_STARTXY:
>> + ret = get_user(val, (int __user *)arg) ? -EFAULT : 0;
>> + if (!ret) {
>> + fdif->s.startx = val & 0xffff;
>> + fdif->s.starty = (val >> 16) & 0xffff;
>> + }
>> + break;
>> + case FDIF_SET_SIZEXY:
>> + ret = get_user(val, (int __user *)arg) ? -EFAULT : 0;
>> + if (!ret) {
>> + fdif->s.sizex = val & 0xffff;
>> + fdif->s.sizey = (val >> 16) & 0xffff;
>> + }
>> + break;
>> + case FDIF_SET_FACE_DIR:
>> + ret = get_user(val, (int __user *)arg) ? -EFAULT : 0;
>> + if (!ret)
>> + fdif->s.face_dir = val;
>> + break;
>> + case FDIF_SET_LHIT:
>> + ret = get_user(val, (int __user *)arg) ? -EFAULT : 0;
>> + if (!ret)
>> + fdif->s.lhit = val;
>> + break;
>> + default:
>> + ret = -1;
>> + }
>> + mutex_unlock(&fdif->mutex);
>> +
>> + return ret;
>> +}
>> +
>> +static int fdif_mmap(struct file *file, struct vm_area_struct *vma)
>> +{
>> + struct fdif *fdif = file->private_data;
>> + unsigned long off;
>> + unsigned long start;
>> + u32 len;
>> +
>> + if (!fdif)
>> + return -ENODEV;
>> +
>> + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
>> + return -EINVAL;
>> +
>> + off = vma->vm_pgoff << PAGE_SHIFT;
>> +
>> + mutex_lock(&fdif->mutex);
>> + start = virt_to_phys(fdif->s.pict_addr);
>> + len = PAGE_ALIGN((start & ~PAGE_MASK) +
>> + PICT_SIZE_X * PICT_SIZE_Y);
>> + if (off >= len) {
>> + mutex_unlock(&fdif->mutex);
>> + return -EINVAL;
>> + }
>> + mutex_unlock(&fdif->mutex);
>> +
>> + start &= PAGE_MASK;
>> + if ((vma->vm_end - vma->vm_start + off) > len)
>> + return -EINVAL;
>> + off += start;
>> +
>> + vma->vm_pgoff = off >> PAGE_SHIFT;
>> + vma->vm_flags |= VM_IO | VM_RESERVED;
>> + vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
>> +
>> + if (remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
>> + vma->vm_end - vma->vm_start, vma->vm_page_prot))
>> + return -EAGAIN;
>> +
>> + return 0;
>> +}
>> +
>> +static unsigned int fdif_poll(struct file *file,
>> + struct poll_table_struct *wait)
>> +{
>> + struct fdif *fdif = file->private_data;
>> + unsigned int mask = 0;
>> +
>> + poll_wait(file, &fdif->wait, wait);
>> + if (file->f_mode & FMODE_WRITE && fdif->face_cnt != -1)
>> + mask |= POLLOUT | POLLWRNORM;
>> + return mask;
>> +}
>> +
>> +const struct file_operations fdif_file_operations = {
>> + .owner = THIS_MODULE,
>> + .read = fdif_read,
>> + .poll = fdif_poll,
>> + .unlocked_ioctl = fdif_ioctl,
>> + .mmap = fdif_mmap,
>> + .open = fdif_open,
>> + .release = fdif_release,
>> +};
>> +
>> +static int __init fdif_init(void)
>> +{
>> + int retval;
>> +
>> + fdif_class = class_create(THIS_MODULE, "fdif");
>> + if (IS_ERR(fdif_class)) {
>> + printk(KERN_ERR "Unable to creat fdif class\n");
>> + retval = -ENOMEM;
>> + goto out;
>> + }
>> +
>> + retval = platform_driver_register(&fdif_driver);
>> + if (retval) {
>> + printk(KERN_ERR "Unable to register fdif driver\n");
>> + goto err_driver_register;
>> + }
>> +
>> + if ((retval = register_chrdev(FDIF_MAJOR, "fdif",
>> + &fdif_file_operations))) {
>> + printk(KERN_ERR "Unable to get fdif device major %d\n",
>> + FDIF_MAJOR);
>> + goto err_chr;
>> + }
>> + return 0;
>> +
>> +err_chr:
>> + platform_driver_unregister(&fdif_driver);
>> +err_driver_register:
>> + class_destroy(fdif_class);
>> +out:
>> + return retval;
>> +}
>> +
>> +static void fdif_cleanup(void)
>> +{
>> + unregister_chrdev(FDIF_MAJOR, "fdif");
>> + platform_driver_unregister(&fdif_driver);
>> + class_destroy(fdif_class);
>> +}
>> +
>> +module_init(fdif_init);
>> +module_exit(fdif_cleanup);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_ALIAS("platform:fdif");
>> +MODULE_AUTHOR("Ming Lei <ming.lei@...onical.com>");
>> diff --git a/include/linux/fdif.h b/include/linux/fdif.h
>> new file mode 100644
>> index 0000000..c0494c2
>> --- /dev/null
>> +++ b/include/linux/fdif.h
>> @@ -0,0 +1,67 @@
>> +/*****************************************************************************/
>> +
>> +/*
>> + * fdif.h --- face detection header file
>> + *
>> + * Copyright (C) 2011
>> + * Ming Lei (ming.lei@...onical.com)
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * 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, write to the Free Software
>> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
>> + *
>> + */
>> +
>> +/*****************************************************************************/
>> +
>> +#ifndef _LINUX_FDIF_H
>> +#define _LINUX_FDIF_H
>> +
>> +#include <linux/types.h>
>> +#include <linux/magic.h>
>> +
>> +#define MAX_FACE_COUNT 35
>> +#define PICT_SIZE_X 320
>> +#define PICT_SIZE_Y 240
>> +
>> +struct fdif_setting {
>> + void *pict_addr;
>> + void *work_mem_addr;
>> + int min_face_size;
>> + int face_dir;
>> + int startx, starty;
>> + int sizex, sizey;
>> + int lhit;
>> +};
>> +
>> +struct fdif_result {
>> + unsigned short centerx;
>> + unsigned short centery;
>> + unsigned short angle;
>> + unsigned short size;
>> + unsigned short confidence;
>> +};
>> +
>> +#define FDIF_START _IOR('F', 0x24, unsigned int)
>> +#define FDIF_GET_FACE_CNT _IOR('F', 0x25, unsigned int)
>> +#define FDIF_STOP _IOR('F', 0x26, unsigned int)
>> +#define FDIF_RESET _IOR('F', 0x27, unsigned int)
>> +
>> +#define FDIF_GET_SETTING _IOR('F', 0x28, struct fdif_setting)
>> +#define FDIF_SET_MIN_FACE_SIZE _IOR('F', 0x2a, unsigned int)
>> +#define FDIF_SET_STARTXY _IOR('F', 0x2b, unsigned int)
>> +#define FDIF_SET_SIZEXY _IOR('F', 0x2c, unsigned int)
>> +#define FDIF_SET_FACE_DIR _IOR('F', 0x2d, unsigned int)
>> +#define FDIF_SET_LHIT _IOR('F', 0x2e, unsigned int)
>> +
>> +#endif /* _LINUX_FDIF_H */
>> diff --git a/include/linux/major.h b/include/linux/major.h
>> index 6a8ca98..57d1c98 100644
>> --- a/include/linux/major.h
>> +++ b/include/linux/major.h
>> @@ -174,4 +174,5 @@
>> #define BLOCK_EXT_MAJOR 259
>> #define SCSI_OSD_MAJOR 260 /* open-osd's OSD scsi device */
>>
>> +#define FDIF_MAJOR 261
>> #endif
>
--
Ming Lei
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists