[PATCH 3/3] drivers/misc: introduce face detection module driver(fdif)

Ming Lei tom.leiming at gmail.com
Sat Nov 26 22:40:24 EST 2011


Hi guys,

Thanks for your comment.

On Sun, Nov 27, 2011 at 6:16 AM, Sylwester Nawrocki <snjw23 at gmail.com> wrote:
> Cc: LMML
>
> On 11/26/2011 05:31 AM, tom.leiming at gmail.com wrote:
>> From: Ming Lei <ming.lei at canonical.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 at canonical.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 at canonical.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 at canonical.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 at canonical.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



More information about the linux-arm-kernel mailing list