linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] OMAP4&drivers/misc: introduce face detection module driver
@ 2011-11-26  4:31 tom.leiming at gmail.com
  2011-11-26  4:31 ` [PATCH 1/3] omap4: introduce fdif(face detect module) hwmod tom.leiming at gmail.com
                   ` (2 more replies)
  0 siblings, 3 replies; 10+ messages in thread
From: tom.leiming at gmail.com @ 2011-11-26  4:31 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

These patches(against -next tree) introduce a device driver under
drivers/misc for enabling one face detection IP[1] which is
integrated inside OMAP4 SoC currently, and have some OMAP4
platform dependent changes to make the module workable on OMAP4 SoC.

For verification purpose, I write one user space utility[2] to
test the module and driver, follows its basic functions:

	- detect faces in a input grayscal picture(PGM raw, 320 by 240)
	- plot a rectangle to mark the detected faces, and save it as
	another same type grayscal picture
	- set detection parameters

I should have though about implementing a face detection demo on video
playback from uvc camera, but OMAP4 DSS on -next tree can't work,
so have to quit the idea.

Looks the performance of the module is not bad, see some detection
results on the link[3][4]. 

Face detection can be used to implement some interesting applications
(camera, face unlock, baby monitor, ...).

 arch/arm/mach-omap2/devices.c              |   33 +
 arch/arm/mach-omap2/omap_hwmod_44xx_data.c |   81 +++
 drivers/misc/Kconfig                       |    7 +
 drivers/misc/Makefile                      |    1 +
 drivers/misc/fdif.c                        |  874 ++++++++++++++++++++++++++++
 include/linux/fdif.h                       |   67 +++
 include/linux/major.h                      |    1 +
 7 files changed, 1064 insertions(+), 0 deletions(-)


thanks,
--
Ming Lei


[1], Ch9 of OMAP4 Technical Reference Manual
[2], http://kernel.ubuntu.com/git?p=ming/fdif.git;a=summary
[3], http://kernel.ubuntu.com/~ming/dev/fdif/output
[4], All pictures are taken from http://www.google.com/imghp
and converted to pnm from jpeg format, only for test purpose.

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/3] omap4: introduce fdif(face detect module) hwmod
  2011-11-26  4:31 [PATCH 0/3] OMAP4&drivers/misc: introduce face detection module driver tom.leiming at gmail.com
@ 2011-11-26  4:31 ` tom.leiming at gmail.com
  2011-11-26  4:31 ` [PATCH 2/3] omap4: build fdif omap device from hwmod tom.leiming at gmail.com
  2011-11-26  4:31 ` [PATCH 3/3] drivers/misc: introduce face detection module driver(fdif) tom.leiming at gmail.com
  2 siblings, 0 replies; 10+ messages in thread
From: tom.leiming at gmail.com @ 2011-11-26  4:31 UTC (permalink / raw)
  To: linux-arm-kernel

From: Ming Lei <ming.lei@canonical.com>

Signed-off-by: Ming Lei <ming.lei@canonical.com>
---
 arch/arm/mach-omap2/omap_hwmod_44xx_data.c |   81 ++++++++++++++++++++++++++++
 1 files changed, 81 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index 6cf21ee..30db754 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -53,6 +53,7 @@ static struct omap_hwmod omap44xx_dmm_hwmod;
 static struct omap_hwmod omap44xx_dsp_hwmod;
 static struct omap_hwmod omap44xx_dss_hwmod;
 static struct omap_hwmod omap44xx_emif_fw_hwmod;
+static struct omap_hwmod omap44xx_fdif_hwmod;
 static struct omap_hwmod omap44xx_hsi_hwmod;
 static struct omap_hwmod omap44xx_ipu_hwmod;
 static struct omap_hwmod omap44xx_iss_hwmod;
@@ -354,6 +355,14 @@ static struct omap_hwmod_ocp_if omap44xx_dma_system__l3_main_2 = {
 	.user		= OCP_USER_MPU | OCP_USER_SDMA,
 };
 
+/* fdif -> l3_main_2 */
+static struct omap_hwmod_ocp_if omap44xx_fdif__l3_main_2 = {
+	.master		= &omap44xx_fdif_hwmod,
+	.slave		= &omap44xx_l3_main_2_hwmod,
+	.clk		= "l3_div_ck",
+	.user		= OCP_USER_MPU | OCP_USER_SDMA,
+};
+
 /* hsi -> l3_main_2 */
 static struct omap_hwmod_ocp_if omap44xx_hsi__l3_main_2 = {
 	.master		= &omap44xx_hsi_hwmod,
@@ -5444,6 +5453,75 @@ static struct omap_hwmod omap44xx_wd_timer3_hwmod = {
 	.slaves_cnt	= ARRAY_SIZE(omap44xx_wd_timer3_slaves),
 };
 
+/* 'fdif' class */
+static struct omap_hwmod_class_sysconfig omap44xx_fdif_sysc = {
+	.rev_offs	= 0x0000,
+	.sysc_offs	= 0x0010,
+	.sysc_flags	= (SYSC_HAS_MIDLEMODE | SYSC_HAS_RESET_STATUS |
+			   SYSC_HAS_SIDLEMODE | SYSC_HAS_SOFTRESET),
+	.idlemodes	= (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
+			   MSTANDBY_FORCE | MSTANDBY_NO |
+			   MSTANDBY_SMART),
+	.sysc_fields	= &omap_hwmod_sysc_type2,
+};
+
+static struct omap_hwmod_class omap44xx_fdif_hwmod_class = {
+	.name	= "fdif",
+	.sysc	= &omap44xx_fdif_sysc,
+};
+
+/*fdif*/
+static struct omap_hwmod_addr_space omap44xx_fdif_addrs[] = {
+	{
+		.pa_start	= 0x4a10a000,
+		.pa_end		= 0x4a10afff,
+		.flags		= ADDR_TYPE_RT
+	},
+	{ }
+};
+
+/* l4_cfg -> fdif */
+static struct omap_hwmod_ocp_if omap44xx_l4_cfg__fdif = {
+	.master		= &omap44xx_l4_cfg_hwmod,
+	.slave		= &omap44xx_fdif_hwmod,
+	.clk		= "l4_div_ck",
+	.addr		= omap44xx_fdif_addrs,
+	.user		= OCP_USER_MPU | OCP_USER_SDMA,
+};
+
+/* fdif slave ports */
+static struct omap_hwmod_ocp_if *omap44xx_fdif_slaves[] = {
+	&omap44xx_l4_cfg__fdif,
+};
+static struct omap_hwmod_irq_info omap44xx_fdif_irqs[] = {
+	{ .irq = 69 + OMAP44XX_IRQ_GIC_START },
+	{ .irq = -1 }
+};
+
+/* fdif master ports */
+static struct omap_hwmod_ocp_if *omap44xx_fdif_masters[] = {
+	&omap44xx_fdif__l3_main_2,
+};
+
+static struct omap_hwmod omap44xx_fdif_hwmod = {
+	.name		= "fdif",
+	.class		= &omap44xx_fdif_hwmod_class,
+	.clkdm_name	= "iss_clkdm",
+	.mpu_irqs	= omap44xx_fdif_irqs,
+	.main_clk	= "fdif_fck",
+	.prcm = {
+		.omap4 = {
+			.clkctrl_offs = OMAP4_CM_CAM_FDIF_CLKCTRL_OFFSET,
+			.context_offs = OMAP4_RM_CAM_FDIF_CONTEXT_OFFSET,
+			.modulemode   = MODULEMODE_SWCTRL,
+		},
+	},
+	.slaves		= omap44xx_fdif_slaves,
+	.slaves_cnt	= ARRAY_SIZE(omap44xx_fdif_slaves),
+	.masters	= omap44xx_fdif_masters,
+	.masters_cnt	= ARRAY_SIZE(omap44xx_fdif_masters),
+};
+
 static __initdata struct omap_hwmod *omap44xx_hwmods[] = {
 
 	/* dmm class */
@@ -5593,6 +5671,9 @@ static __initdata struct omap_hwmod *omap44xx_hwmods[] = {
 	&omap44xx_wd_timer2_hwmod,
 	&omap44xx_wd_timer3_hwmod,
 
+	/* fdif class */
+	&omap44xx_fdif_hwmod,
+
 	NULL,
 };
 
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/3] omap4: build fdif omap device from hwmod
  2011-11-26  4:31 [PATCH 0/3] OMAP4&drivers/misc: introduce face detection module driver tom.leiming at gmail.com
  2011-11-26  4:31 ` [PATCH 1/3] omap4: introduce fdif(face detect module) hwmod tom.leiming at gmail.com
@ 2011-11-26  4:31 ` tom.leiming at gmail.com
  2011-11-26  4:31 ` [PATCH 3/3] drivers/misc: introduce face detection module driver(fdif) tom.leiming at gmail.com
  2 siblings, 0 replies; 10+ messages in thread
From: tom.leiming at gmail.com @ 2011-11-26  4:31 UTC (permalink / raw)
  To: linux-arm-kernel

From: Ming Lei <ming.lei@canonical.com>

Signed-off-by: Ming Lei <ming.lei@canonical.com>
---
 arch/arm/mach-omap2/devices.c |   33 +++++++++++++++++++++++++++++++++
 1 files changed, 33 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c
index 1166bdc..a392af5 100644
--- a/arch/arm/mach-omap2/devices.c
+++ b/arch/arm/mach-omap2/devices.c
@@ -728,6 +728,38 @@ void __init omap242x_init_mmc(struct omap_mmc_platform_data **mmc_data)
 
 #endif
 
+static struct platform_device* __init omap4_init_fdif(void)
+{
+	int id = -1;
+	struct platform_device *pd;
+	struct omap_hwmod *oh;
+	const char *dev_name = "fdif";
+
+	oh = omap_hwmod_lookup("fdif");
+	if (!oh) {
+		pr_err("Could not look up fdif hwmod\n");
+		return NULL;
+	}
+
+	pd = omap_device_build(dev_name, id, oh, NULL, 0, NULL, 0, 0);
+	WARN(IS_ERR(pd), "Can't build omap_device for %s.\n",
+				dev_name);
+	return pd;
+}
+
+static void __init omap_init_fdif(void)
+{
+	if (cpu_is_omap44xx()) {
+		struct platform_device *pd;
+
+		pd = omap4_init_fdif();
+		if (!pd)
+			return;
+
+		pm_runtime_enable(&pd->dev);
+	}
+}
+
 /*-------------------------------------------------------------------------*/
 
 #if defined(CONFIG_HDQ_MASTER_OMAP) || defined(CONFIG_HDQ_MASTER_OMAP_MODULE)
@@ -808,6 +840,7 @@ static int __init omap2_init_devices(void)
 	omap_init_sham();
 	omap_init_aes();
 	omap_init_vout();
+	omap_init_fdif();
 
 	return 0;
 }
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/3] drivers/misc: introduce face detection module driver(fdif)
  2011-11-26  4:31 [PATCH 0/3] OMAP4&drivers/misc: introduce face detection module driver tom.leiming at gmail.com
  2011-11-26  4:31 ` [PATCH 1/3] omap4: introduce fdif(face detect module) hwmod tom.leiming at gmail.com
  2011-11-26  4:31 ` [PATCH 2/3] omap4: build fdif omap device from hwmod tom.leiming at gmail.com
@ 2011-11-26  4:31 ` tom.leiming at gmail.com
  2011-11-26 11:12   ` Alan Cox
  2011-11-26 22:16   ` Sylwester Nawrocki
  2 siblings, 2 replies; 10+ messages in thread
From: tom.leiming at gmail.com @ 2011-11-26  4:31 UTC (permalink / raw)
  To: linux-arm-kernel

From: Ming Lei <ming.lei@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.

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@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@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
-- 
1.7.5.4

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/3] drivers/misc: introduce face detection module driver(fdif)
  2011-11-26  4:31 ` [PATCH 3/3] drivers/misc: introduce face detection module driver(fdif) tom.leiming at gmail.com
@ 2011-11-26 11:12   ` Alan Cox
  2011-11-26 15:04     ` Greg KH
  2011-11-26 22:16   ` Sylwester Nawrocki
  1 sibling, 1 reply; 10+ messages in thread
From: Alan Cox @ 2011-11-26 11:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, 26 Nov 2011 12:31:44 +0800
tom.leiming at gmail.com wrote:

> From: Ming Lei <ming.lei@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.
> 
> 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@canonical.com>

If you are submitting it then it ought to have your sign off too. This
looks hardly ready for submission however.

> +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.

So we have a completelt device specific API for what is becoming a more
general feature and on some hardware is tied to camera and the like. This
seems wrong

IMHO this should be part of video4linux because that'll make the
integrated stuff work sanely and for your cases it still provided the
right kind of mmap and format interfaces you need.

Detailed review below but basically this isn't ready to merge and it
shouldn't be merged from an interface point of view. Please work out how
to integrate the face detection into video4linux2 with the V4L folks.

> +#define FDIF_DEV	MKDEV(FDIF_MAJOR, 0)
> +#define FDIF_MAX_MINORS	8

No custom majors please.


> +static irqreturn_t handle_detection(int irq, void *__fdif);

Minor thing but you could just order the users right ...

> +/*only support one fdif device now*/
> +static struct fdif	*g_fdif;

So why 8 minors ?


> +	fdif->irq = platform_get_irq(pdev, 0);
> +	if (fdif->irq < 0) {

0 would also be a fail here surely - you can't support polling

> +	dev_info(dev, "fdif version=%8x hwinfo=%08x\n",
> +			fdif_readl(fdif->base, FDIF_REVISION),
> +			fdif_readl(fdif->base, FDIF_HWINFO));

Should be debug for productioncode

> +	device_destroy(fdif_class, MKDEV(FDIF_MAJOR, 0));


> +/*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__);

dev_err

There are several that want fixing


> +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);

Pity the rest of the code uses a mutex for locking and the IRQ code
doesn't bother with locking at all. That doesn't look safe to me.

> +	} else if (irqsts & ERR_IRQ) {
> +		dev_err(&fdif->pdev->dev, "err irq!");

Not exactly user informative.

> +		softreset_fdif(fdif);
> +	} else {

This is only true if the IRQ is shared. Also the core IRQ code handles
spurious IRQ jams, and thirdly you shouldn't return HANDLED if you didn't
handle it.

> +/*
> + * 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;
> +	}

So if its misconfigured or not found it would be a good idea to allow
users to spew stuff in the logs. Just return the error code.

The !fdif case here is meaningless. Either it matters in which case your
open isn't properly locked versus a remove, or it doesn't, in which case
your test is bogus.

> +static int fdif_release(struct inode *inode, struct file *file)
> +{
> +	struct fdif *fdif = file->private_data;
> +
> +	if (!fdif)
> +		return -ENODEV;

What if I unplugged the device while it was in use... ? What is your
locking model for all this.

> +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;

Wrong error code - EFAULT is a bad address in userspace really, and it'll
confuse folks if they get other stuff.

> +	if (fdif->read_idx < 0) {
> +		ret = -EFAULT;
> +		goto err;

Ditto

> +		if (copy_to_user(buf, &fdif->faces[fdif->read_idx], len)) {
> +			ret = -EFAULT;
> +			goto err;
> +		}

If you copy multiple objects and at least one is consumed you should be
returning the bytes succesfully copied, and EFAULT only if none were.


> +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;

This could do with explanation as it seems a slightly odd permissions
arrangement.

> +	case FDIF_STOP:
> +		stop_detect(fdif);

If I call this twice in a row you unmapped existing unmapped memory. No
consideration appears to have been made of the most basic misbehaviour by
user space applications. This is a general problem and some of these look
exploitable.

> +	case FDIF_SET_MIN_FACE_SIZE:
> +		ret = get_user(val, (int __user *)arg) ? -EFAULT : 0;
> +		if (!ret)
> +			fdif->s.min_face_size = val;

No range checks needed ?

> +		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;
> +		}

Limits you to 16bit co-ordinates - hardly future proof

> +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;

Same commentd as everywhere else


> +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)

What locks fdif->face_cnt here ?


> +static int __init fdif_init(void)
> +{
> +	int retval;
> +
> +	fdif_class = class_create(THIS_MODULE, "fdif");

Please don't create new classes

> +	if (IS_ERR(fdif_class)) {
> +		printk(KERN_ERR "Unable to creat fdif class\n");

And use pr_err

> +	retval = platform_driver_register(&fdif_driver);
> +	if (retval) {
> +		printk(KERN_ERR "Unable to register fdif driver\n");

Ditto

> +		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);

And please don't make up major numbers. They are allocated via lanana and
you don't need one you can use a generic one - although if you slot this
within V4L2 that problem also goes away


> +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;
> +};

Changes size between 32 and 64bits. Please make all ioctl sizing stable
and 32/64bit safe.

> +struct fdif_result {
> +	unsigned short centerx;
> +	unsigned short centery;
> +	unsigned short angle;
> +	unsigned short size;
> +	unsigned short confidence;
> +};

So we have x and y as int above, and unsigned short here ??

> +#define FDIF_MAJOR		261

NAK

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 3/3] drivers/misc: introduce face detection module driver(fdif)
  2011-11-26 11:12   ` Alan Cox
@ 2011-11-26 15:04     ` Greg KH
  0 siblings, 0 replies; 10+ messages in thread
From: Greg KH @ 2011-11-26 15:04 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Nov 26, 2011 at 11:12:47AM +0000, Alan Cox wrote:
> On Sat, 26 Nov 2011 12:31:44 +0800
> tom.leiming at gmail.com wrote:
> 
> > From: Ming Lei <ming.lei@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.
> > 
> > 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@canonical.com>
> 
> If you are submitting it then it ought to have your sign off too. This
> looks hardly ready for submission however.
> 
> > +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.
> 
> So we have a completelt device specific API for what is becoming a more
> general feature and on some hardware is tied to camera and the like. This
> seems wrong
> 
> IMHO this should be part of video4linux because that'll make the
> integrated stuff work sanely and for your cases it still provided the
> right kind of mmap and format interfaces you need.

I agree, as-is, this isn't ok, sorry, please work with the v4l
developers on this.

greg k-h

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 3/3] drivers/misc: introduce face detection module driver(fdif)
  2011-11-26  4:31 ` [PATCH 3/3] drivers/misc: introduce face detection module driver(fdif) tom.leiming at gmail.com
  2011-11-26 11:12   ` Alan Cox
@ 2011-11-26 22:16   ` Sylwester Nawrocki
  2011-11-27  3:40     ` Ming Lei
  1 sibling, 1 reply; 10+ messages in thread
From: Sylwester Nawrocki @ 2011-11-26 22:16 UTC (permalink / raw)
  To: linux-arm-kernel

Cc: LMML

On 11/26/2011 05:31 AM, tom.leiming at gmail.com wrote:
> From: Ming Lei <ming.lei@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.

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.

> 
> 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@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@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

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 3/3] drivers/misc: introduce face detection module driver(fdif)
  2011-11-26 22:16   ` Sylwester Nawrocki
@ 2011-11-27  3:40     ` Ming Lei
  2011-11-27 16:41       ` Sylwester Nawrocki
  0 siblings, 1 reply; 10+ messages in thread
From: Ming Lei @ 2011-11-27  3:40 UTC (permalink / raw)
  To: linux-arm-kernel

Hi guys,

Thanks for your comment.

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

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 3/3] drivers/misc: introduce face detection module driver(fdif)
  2011-11-27  3:40     ` Ming Lei
@ 2011-11-27 16:41       ` Sylwester Nawrocki
  2011-11-28  3:42         ` Ming Lei
  0 siblings, 1 reply; 10+ messages in thread
From: Sylwester Nawrocki @ 2011-11-27 16:41 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Ming,

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

It's all good, however we need to ensure interoperability with existing
drivers. I mean it shouldn't be difficult to setup data processing
pipelines containing  various types of devices, like video capture,
resizers, filters, etc. The situation where each type of device makes up
their own interface is rather far from ideal.

>>
>> 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.

I think we need a new user interface for that, the closest match would be
the Video Output Interface [1], IMHO this could be used as a base. I got
a bit confused by the additional working memory requirement and thought
the FDIF needs input and output memory buffers to process single image
frame. But that's not the case so there is no reason to bother with
the mem-to-mem interface.

Unfortunately there is no example of virtual output device driver in v4l2
yet. There is only one for the capture devices - drivers/media/video/vivi.c

For video output one uses V4L2_BUF_TYPE_VIDEO_OUTPUT buffer type instead of
V4L2_BUF_TYPE_VIDEO_CAPTURE and at the kernel side .vidioc_*_out operations
of struct v4l2_ioctl_ops should be implemented, rather than .vidioc_*_cap.

> 
> 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)

It's up to the applications in what way the image data is loaded into
memory buffer, if it comes from a file or from other device. Once it is in
standard v4l2 buffer object it can be shared between v4l2 drivers,
still user space needs to queue/de-queue buffers. See QBUF/DQBUF ioctls
[4] for more information.

> - the input video format may be different for different FD IP

That's normally handled by each v4l2 device, see [2] for details at the
user side, and vidioc_s_fmt_vid_cap() in vivi for example.

> - one method is required to start or stop FD

I suppose VIDIOC_STREAMON/VIDIOC_STREAMOFF would do the job, see [3] and
start/stop_streaming functions in vivi.

> - one method is required to report the detection results to user space

Perhaps we need new ioctl(s) for that. And possibly some new controls,
or even new control class [5], [6].

It would also be good to get requirements for other hardware implementations
existing out there. I might be able to look at the Samsung ones eventually,
but cannot guarantee this.

> 
> Any suggestions on how to implement FD on v4l2?
> 
> thanks,
> --
> Ming Lei

-- 
Regards,
Sylwester

[1] http://linuxtv.org/downloads/v4l-dvb-apis/devices.html
[2] http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-g-fmt.html
[3] http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-streamon.html
[4] http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-qbuf.html
[5] http://linuxtv.org/downloads/v4l-dvb-apis/control.html
[6] http://linuxtv.org/downloads/v4l-dvb-apis/extended-controls.html

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 3/3] drivers/misc: introduce face detection module driver(fdif)
  2011-11-27 16:41       ` Sylwester Nawrocki
@ 2011-11-28  3:42         ` Ming Lei
  0 siblings, 0 replies; 10+ messages in thread
From: Ming Lei @ 2011-11-28  3:42 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Sylwester,

Great thanks for providing so detailed v4l2 background.

On Mon, Nov 28, 2011 at 12:41 AM, Sylwester Nawrocki <snjw23@gmail.com> wrote:
> Hi Ming,
>
> On 11/27/2011 04:40 AM, Ming Lei wrote:
>> Hi guys,
>>
>> Thanks for your comment.
>>
>> On Sun, Nov 27, 2011 at 6:16 AM, Sylwester Nawrocki <snjw23@gmail.com> wrote:
>>> Cc: LMML
>>>
>>> On 11/26/2011 05:31 AM, tom.leiming at gmail.com wrote:
>>>> From: Ming Lei <ming.lei@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.
>
> It's all good, however we need to ensure interoperability with existing
> drivers. I mean it shouldn't be difficult to setup data processing
> pipelines containing ?various types of devices, like video capture,
> resizers, filters, etc. The situation where each type of device makes up
> their own interface is rather far from ideal.

Yes, so it does make sense to implement FD based on v4l2 framework.

>
>>>
>>> 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.
>
> I think we need a new user interface for that, the closest match would be
> the Video Output Interface [1], IMHO this could be used as a base. I got
> a bit confused by the additional working memory requirement and thought

I think the working memory is used by FD HW module to run its built-in face
detection algorithm, so that lots of ram can be saved in the module.

> the FDIF needs input and output memory buffers to process single image
> frame. But that's not the case so there is no reason to bother with

I think only output memory buffers are enough for FD device, we can get
the detection result from ioctl, then let application to handle it.

> the mem-to-mem interface.
>
> Unfortunately there is no example of virtual output device driver in v4l2
> yet. There is only one for the capture devices - drivers/media/video/vivi.c
>
> For video output one uses V4L2_BUF_TYPE_VIDEO_OUTPUT buffer type instead of
> V4L2_BUF_TYPE_VIDEO_CAPTURE and at the kernel side .vidioc_*_out operations
> of struct v4l2_ioctl_ops should be implemented, rather than .vidioc_*_cap.

It is very helpful.

>>
>> 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)
>
> It's up to the applications in what way the image data is loaded into
> memory buffer, if it comes from a file or from other device. Once it is in
> standard v4l2 buffer object it can be shared between v4l2 drivers,
> still user space needs to queue/de-queue buffers. See QBUF/DQBUF ioctls
> [4] for more information.

If the image data comes from a device(capture device, or resize
post-processing),
face detect module should use the buffer directly and avoid
to copy image data from capture or resize buffer if the image format is
same with what FD requires and the buffer is physical continuous.

But I am not sure how to handle this case? Could we need to add a ioctl to
make FD device see the buffer from capture or resize device?

Also, could you give a introduction about how v4l2 handles resize if
HW is capable of resizing?

>
>> - the input video format may be different for different FD IP
>
> That's normally handled by each v4l2 device, see [2] for details at the
> user side, and vidioc_s_fmt_vid_cap() in vivi for example.

Agree.

>> - one method is required to start or stop FD
>
> I suppose VIDIOC_STREAMON/VIDIOC_STREAMOFF would do the job, see [3] and
> start/stop_streaming functions in vivi.

Agree.

>> - one method is required to report the detection results to user space
>
> Perhaps we need new ioctl(s) for that. And possibly some new controls,
> or even new control class [5], [6].

Agree.

> It would also be good to get requirements for other hardware implementations
> existing out there. I might be able to look at the Samsung ones eventually,
> but cannot guarantee this.

It is great for you to provide some information about FD on Samsung SoC,
so that we can figure out a more generic implementation to make support
for new FD IP easier.

>>
>> Any suggestions on how to implement FD on v4l2?
>>
>> thanks,
>> --
>> Ming Lei
>
> --
> Regards,
> Sylwester
>
> [1] http://linuxtv.org/downloads/v4l-dvb-apis/devices.html
> [2] http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-g-fmt.html
> [3] http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-streamon.html
> [4] http://linuxtv.org/downloads/v4l-dvb-apis/vidioc-qbuf.html
> [5] http://linuxtv.org/downloads/v4l-dvb-apis/control.html
> [6] http://linuxtv.org/downloads/v4l-dvb-apis/extended-controls.html
>


thanks,
-- 
Ming Lei

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2011-11-28  3:42 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-11-26  4:31 [PATCH 0/3] OMAP4&drivers/misc: introduce face detection module driver tom.leiming at gmail.com
2011-11-26  4:31 ` [PATCH 1/3] omap4: introduce fdif(face detect module) hwmod tom.leiming at gmail.com
2011-11-26  4:31 ` [PATCH 2/3] omap4: build fdif omap device from hwmod tom.leiming at gmail.com
2011-11-26  4:31 ` [PATCH 3/3] drivers/misc: introduce face detection module driver(fdif) tom.leiming at gmail.com
2011-11-26 11:12   ` Alan Cox
2011-11-26 15:04     ` Greg KH
2011-11-26 22:16   ` Sylwester Nawrocki
2011-11-27  3:40     ` Ming Lei
2011-11-27 16:41       ` Sylwester Nawrocki
2011-11-28  3:42         ` Ming Lei

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).