From premi at ti.com Wed Nov 2 05:34:41 2011 From: premi at ti.com (Premi, Sanjeev) Date: Wed, 2 Nov 2011 11:34:41 +0000 Subject: powertop on davinci In-Reply-To: References: Message-ID: > -----Original Message----- > From: davinci-linux-open-source-bounces at linux.davincidsp.com > [mailto:davinci-linux-open-source-bounces at linux.davincidsp.com > ] On Behalf Of Raffaele Recalcati > Sent: Friday, October 28, 2011 12:09 AM > To: davinci-linux-open-source > Subject: powertop on davinci > > It is possible to use this tool for dm365. > I'm now on 2.6.32 kernel and I'm not sure at all if CPU_FREQ_STAT, > CPU_FREQ_STAT_DETAILS > work properly. > Maybe it is not implemented. > > Can anybody points me to the right direction? This page illustrates use changes for powertop on omap3evm: http://processors.wiki.ti.com/index.php/Running_PowerTOP_on_OMAP35x_platform You may want to check if the changes related to P-states described in this page help you. Of course, exact changes for DM365 will differ. ~sanjeev > > > Raffaele > _______________________________________________ > Davinci-linux-open-source mailing list > Davinci-linux-open-source at linux.davincidsp.com > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source > From laurent.pinchart at ideasonboard.com Wed Nov 2 08:20:32 2011 From: laurent.pinchart at ideasonboard.com (Laurent Pinchart) Date: Wed, 2 Nov 2011 15:20:32 +0100 Subject: new mbus formats In-Reply-To: References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> <20110831112323.GL12368@valkosipuli.localdomain> Message-ID: <201111021520.32483.laurent.pinchart@ideasonboard.com> Hi Manju, On Sunday 23 October 2011 20:30:11 Hadli, Manjunath wrote: > Hi, > > I need a few mbus formats to be defined loosely for following. Please tell > me if anyone has already thought of taking care of them already. > > These are supported for Texas Instruments DM365 and DM355 SoCs. > > 1. RGB 888 parallel: How is that transfered ? Do you have a 24-bit bus ? Or do you use an 8-bit bus with 3 transfers per pixel ? In the first case you will need something like V4L2_MBUS_FMT_RGB888_1X24, in the second case V4L2_MBUS_FMT_RGB888_3X8 (and the corresponding BGR formats). > 2. YUV420 color separate: > 3. C plane 420: ( On the lines of Y plane: V4L2_MBUS_FMT_Y8_1X8) > 4. C plane 422 Could you please detail how those three formats are transferred on the bus ? > 5. 10 bit bayer with ALAW compression. Is that 10 bit compressed to 8 bit with alaw ? If so V4L2_MBUS_FMT_SBGGR10_ALAW8_1X8 V4L2_MBUS_FMT_SGBRG10_ALAW8_1X8 V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8 V4L2_MBUS_FMT_SRGGB10_ALAW8_1X8 should do. -- Regards, Laurent Pinchart From liuyebo999 at gmail.com Wed Nov 2 22:47:45 2011 From: liuyebo999 at gmail.com (liu yebo) Date: Thu, 3 Nov 2011 12:47:45 +0800 Subject: Speech on Dm365 Message-ID: Hi All, Now I am working on a Ti 's dm365 demo board, and I am debugging capturing PCM data from Audio audio interface. I type the following command on the console to caupture and compress the PCM data using encode demo after the original kernel (without any changes ) boots ./encode -s 1.g711 But, it says "DM365 encode demo: Can not create speech device" Is there someone point out what happens? I am able to observe that the audio driver had been load. I can also see the /dev/mixer and /dev/dsp existed Thanks and Regards, Money -------------- next part -------------- An HTML attachment was scrubbed... URL: From khilman at ti.com Mon Nov 7 09:09:38 2011 From: khilman at ti.com (Kevin Hilman) Date: Mon, 07 Nov 2011 07:09:38 -0800 Subject: 10-bit address support in i2c-omap and i2c-davinci In-Reply-To: <20111107094614.0062a3f7@endymion.delvare> (Jean Delvare's message of "Mon, 7 Nov 2011 09:46:14 +0100") References: <20111107094614.0062a3f7@endymion.delvare> Message-ID: <87hb2g0y19.fsf@ti.com> Adding linux-omap and linux-davinci lists Jean Delvare writes: > Both bus drivers i2c-omap and i2c-davinci apparently handle 10-bit addresses: > > (i2c-omap.c) > if (msg->flags & I2C_M_TEN) > w |= OMAP_I2C_CON_XA; > > (i2c-davinci.c) > /* if the slave address is ten bit address, enable XA bit */ > if (msg->flags & I2C_M_TEN) > flag |= DAVINCI_I2C_MDR_XA; > > However neither driver declares functionality flag I2C_FUNC_10BIT_ADDR, > so chip drivers would normally refuse to bind to these buses. If 10-bit > address support is incomplete or broken then it should be removed > completely. If it works then these drivers should declare so by adding > I2C_FUNC_10BIT_ADDR to the functionality flags they return. From khali at linux-fr.org Mon Nov 7 10:59:37 2011 From: khali at linux-fr.org (Jean Delvare) Date: Mon, 7 Nov 2011 17:59:37 +0100 Subject: 10-bit address support in i2c-omap and i2c-davinci In-Reply-To: <87hb2g0y19.fsf@ti.com> References: <20111107094614.0062a3f7@endymion.delvare> <87hb2g0y19.fsf@ti.com> Message-ID: <20111107175937.7099f6e9@endymion.delvare> On Mon, 07 Nov 2011 07:09:38 -0800, Kevin Hilman wrote: > Adding linux-omap and linux-davinci lists Good point, sorry for missing that. Maybe the following would help? List i2c-omap and i2c-davinci in MAINTAINERS. Signed-off-by: Jean Delvare --- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+) --- linux-3.2-rc0.orig/MAINTAINERS 2011-11-07 17:55:46.000000000 +0100 +++ linux-3.2-rc0/MAINTAINERS 2011-11-07 17:55:55.000000000 +0100 @@ -4675,6 +4675,8 @@ Q: http://patchwork.kernel.org/project/l T: git git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap-2.6.git S: Maintained F: arch/arm/*omap*/ +F: drivers/i2c/busses/i2c-omap.c +F: include/linux/i2c-omap.h OMAP CLOCK FRAMEWORK SUPPORT M: Paul Walmsley @@ -5940,6 +5942,7 @@ L: davinci-linux-open-source at linux.davin Q: http://patchwork.kernel.org/project/linux-davinci/list/ S: Supported F: arch/arm/mach-davinci +F: drivers/i2c/busses/i2c-davinci.c SIS 190 ETHERNET DRIVER M: Francois Romieu -- Jean Delvare From prakash.pm at ti.com Tue Nov 8 08:43:26 2011 From: prakash.pm at ti.com (Manjunathappa, Prakash) Date: Tue, 8 Nov 2011 20:13:26 +0530 Subject: [PATCH] da850:ASoC: change audio edma queue eventq to EVENTQ_0 Message-ID: <1320763406-31559-1-git-send-email-prakash.pm@ti.com> In a system scenario other modules were using eventq1 and McASP was using eventq 0. But fix in 48519f0ae03bc7e86b3dc93e56f1334d53803770 for driver to pick eventq specified via platform data leads to starvation issue on audio side because other module also uses same queue(EVENTQ_1). Patch fixes the issue by changing McASP edma queue to EVENTQ_0 because it is the eventq audio was using before fix in the above mentioned commit ID. Signed-off-by: Manjunathappa, Prakash --- arch/arm/mach-davinci/board-da850-evm.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index ec21663..230dcbc 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -753,7 +753,7 @@ static struct snd_platform_data da850_evm_snd_data = { .num_serializer = ARRAY_SIZE(da850_iis_serializer_direction), .tdm_slots = 2, .serial_dir = da850_iis_serializer_direction, - .asp_chan_q = EVENTQ_1, + .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_2, .txnumevt = 1, .rxnumevt = 1, -- 1.7.1 From vaibhav.bedia at ti.com Tue Nov 8 09:07:17 2011 From: vaibhav.bedia at ti.com (Bedia, Vaibhav) Date: Tue, 8 Nov 2011 15:07:17 +0000 Subject: [PATCH] da850:ASoC: change audio edma queue eventq to EVENTQ_0 In-Reply-To: <1320763406-31559-1-git-send-email-prakash.pm@ti.com> References: <1320763406-31559-1-git-send-email-prakash.pm@ti.com> Message-ID: On Tue, Nov 08, 2011 at 20:13:26, Manjunathappa, Prakash wrote: > In a system scenario other modules were using eventq1 and McASP was > using eventq 0. But fix in 48519f0ae03bc7e86b3dc93e56f1334d53803770 for > driver to pick eventq specified via platform data leads to starvation > issue on audio side because other module also uses same queue(EVENTQ_1). > Patch fixes the issue by changing McASP edma queue to EVENTQ_0 because > it is the eventq audio was using before fix in the above mentioned commit > ID. > The commit you indicated doesn't seem to have the changed the eventq for DA850. Is the patch description accurate? Regards, Vaibhav From sshtylyov at mvista.com Tue Nov 8 11:15:39 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Tue, 08 Nov 2011 19:15:39 +0200 Subject: [PATCH] da850:ASoC: change audio edma queue eventq to EVENTQ_0 In-Reply-To: References: <1320763406-31559-1-git-send-email-prakash.pm@ti.com> Message-ID: <4EB963BB.9020106@mvista.com> Hello. On 08.11.2011 17:07, Bedia, Vaibhav wrote: > On Tue, Nov 08, 2011 at 20:13:26, Manjunathappa, Prakash wrote: >> In a system scenario other modules were using eventq1 and McASP was >> using eventq 0. But fix in 48519f0ae03bc7e86b3dc93e56f1334d53803770 for >> driver to pick eventq specified via platform data leads to starvation >> issue on audio side because other module also uses same queue(EVENTQ_1). >> Patch fixes the issue by changing McASP edma queue to EVENTQ_0 because >> it is the eventq audio was using before fix in the above mentioned commit >> ID. >> > The commit you indicated doesn't seem to have the changed the eventq for DA850. > Is the patch description accurate? You should also cite that commit's summary in parens after its ID. WBR, Sergei From prakash.pm at ti.com Wed Nov 9 01:02:23 2011 From: prakash.pm at ti.com (Manjunathappa, Prakash) Date: Wed, 9 Nov 2011 07:02:23 +0000 Subject: [PATCH] da850:ASoC: change audio edma queue eventq to EVENTQ_0 In-Reply-To: References: <1320763406-31559-1-git-send-email-prakash.pm@ti.com> Message-ID: Hi Vaibhav, On Tue, Nov 08, 2011 at 20:37:17, Bedia, Vaibhav wrote: > On Tue, Nov 08, 2011 at 20:13:26, Manjunathappa, Prakash wrote: > > In a system scenario other modules were using eventq1 and McASP was > > using eventq 0. But fix in 48519f0ae03bc7e86b3dc93e56f1334d53803770 for > > driver to pick eventq specified via platform data leads to starvation > > issue on audio side because other module also uses same queue(EVENTQ_1). > > Patch fixes the issue by changing McASP edma queue to EVENTQ_0 because > > it is the eventq audio was using before fix in the above mentioned commit > > ID. > > > > The commit you indicated doesn't seem to have the changed the eventq for DA850. > Is the patch description accurate? I will update description with below and send out version2. Since there was bug in the audio driver, it was not picking the eventq specified(EVENTQ_1) via platform data and was using EVENTQ_0. And in system scenario other modules(say video) were using EVENTQ_1. 48519f0ae03bc7e86b3dc93e56f1334d53803770 fixes the bug in driver to pick specified eventq via platform data. As a result starvation issue is observed on audio side when audio/video uses same eventq for transfers. Patch fixes the issue by changing eventq number to EVENTQ_0 for audio. Thanks, Prakash > > Regards, > Vaibhav > From akshay.s at ti.com Wed Nov 9 06:25:20 2011 From: akshay.s at ti.com (Shankarmurthy,Akshay) Date: Wed, 9 Nov 2011 17:55:20 +0530 Subject: [PATCH] dm365: nand: Change offset in kernel to in-line with u-boot Message-ID: <1320841520-10605-1-git-send-email-akshay.s@ti.com> Current partition information maintained in kernel does not match with u-boot, this leads to corruption of u-boot env when we update uImage from kernel. Patch fixes it to match with u-boot partition information. Signed-off-by: Shankarmurthy,Akshay --- arch/arm/mach-davinci/board-dm365-evm.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c index 04c43ab..ab2381e 100644 --- a/arch/arm/mach-davinci/board-dm365-evm.c +++ b/arch/arm/mach-davinci/board-dm365-evm.c @@ -107,7 +107,7 @@ static struct mtd_partition davinci_nand_partitions[] = { /* UBL (a few copies) plus U-Boot */ .name = "bootloader", .offset = 0, - .size = 28 * NAND_BLOCK_SIZE, + .size = 30 * NAND_BLOCK_SIZE, .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { /* U-Boot environment */ -- 1.7.1 From prakash.pm at ti.com Wed Nov 9 23:47:25 2011 From: prakash.pm at ti.com (Manjunathappa, Prakash) Date: Thu, 10 Nov 2011 11:17:25 +0530 Subject: [PATCH v2] da850:ASoC: change audio edma queue eventq to EVENTQ_0 Message-ID: <1320904045-567-1-git-send-email-prakash.pm@ti.com> Since there was bug in the audio driver, it was not picking the eventq specified(EVENTQ_1) via platform data and was using EVENTQ_0. And in system scenario other modules(say video) were using EVENTQ_1. 48519f0ae03bc7e86b3dc93e56f1334d53803770 fixes the bug in driver to pick specified eventq via platform data. As a result starvation issue is observed on audio side when audio/video uses same eventq for transfers. Patch fixes the issue by changing eventq to EVENTQ_0 for audio. Signed-off-by: Manjunathappa, Prakash --- Since v1: Rewritten commit description. arch/arm/mach-davinci/board-da850-evm.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 1d7d249..6659a90 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -753,7 +753,7 @@ static struct snd_platform_data da850_evm_snd_data = { .num_serializer = ARRAY_SIZE(da850_iis_serializer_direction), .tdm_slots = 2, .serial_dir = da850_iis_serializer_direction, - .asp_chan_q = EVENTQ_1, + .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_2, .txnumevt = 1, .rxnumevt = 1, -- 1.7.1 From prakash.pm at ti.com Thu Nov 10 00:15:35 2011 From: prakash.pm at ti.com (Manjunathappa, Prakash) Date: Thu, 10 Nov 2011 06:15:35 +0000 Subject: [PATCH v2] da850:ASoC: change audio edma queue eventq to EVENTQ_0 In-Reply-To: <1320904045-567-1-git-send-email-prakash.pm@ti.com> References: <1320904045-567-1-git-send-email-prakash.pm@ti.com> Message-ID: Hi, On Thu, Nov 10, 2011 at 11:17:25, Manjunathappa, Prakash wrote: > Since there was bug in the audio driver, it was not picking the eventq > specified(EVENTQ_1) via platform data and was using EVENTQ_0. And in > system scenario other modules(say video) were using EVENTQ_1. > 48519f0ae03bc7e86b3dc93e56f1334d53803770 fixes the bug in driver to pick Somehow I don't see Sergei's mail on this patch in my Inbox. Thanks Sudhakar for letting me know. I will send out version3 by addressing Sergei's comment(You should also cite that commit's summary in parens after its ID). Thanks, Prakash > specified eventq via platform data. As a result starvation issue is > observed on audio side when audio/video uses same eventq for transfers. > Patch fixes the issue by changing eventq to EVENTQ_0 for audio. > > Signed-off-by: Manjunathappa, Prakash > --- > Since v1: > Rewritten commit description. > > arch/arm/mach-davinci/board-da850-evm.c | 2 +- > 1 files changed, 1 insertions(+), 1 deletions(-) > > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index 1d7d249..6659a90 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -753,7 +753,7 @@ static struct snd_platform_data da850_evm_snd_data = { > .num_serializer = ARRAY_SIZE(da850_iis_serializer_direction), > .tdm_slots = 2, > .serial_dir = da850_iis_serializer_direction, > - .asp_chan_q = EVENTQ_1, > + .asp_chan_q = EVENTQ_0, > .version = MCASP_VERSION_2, > .txnumevt = 1, > .rxnumevt = 1, > -- > 1.7.1 > > From prakash.pm at ti.com Thu Nov 10 00:13:21 2011 From: prakash.pm at ti.com (Manjunathappa, Prakash) Date: Thu, 10 Nov 2011 11:43:21 +0530 Subject: [PATCH v3] da850:ASoC: change audio edma queue eventq to EVENTQ_0 Message-ID: <1320905601-9369-1-git-send-email-prakash.pm@ti.com> Since there was bug in the audio driver, it was not picking the eventq specified(EVENTQ_1) via platform data and was using EVENTQ_0. And in system scenario other modules(say video) were using EVENTQ_1. 48519f0ae03bc7e86b3dc93e56f1334d53803770(ASoC: davinci: let platform data define edma queue numbers) fixes the bug in driver to pick specified eventq via platform data. As a result starvation issue is observed on audio side when audio/video uses same eventq for transfers. Patch fixes the issue by changing eventq to EVENTQ_0 for audio. Signed-off-by: Manjunathappa, Prakash --- Since v1,v2: Addressed review comments on patch description. arch/arm/mach-davinci/board-da850-evm.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 1d7d249..6659a90 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -753,7 +753,7 @@ static struct snd_platform_data da850_evm_snd_data = { .num_serializer = ARRAY_SIZE(da850_iis_serializer_direction), .tdm_slots = 2, .serial_dir = da850_iis_serializer_direction, - .asp_chan_q = EVENTQ_1, + .asp_chan_q = EVENTQ_0, .version = MCASP_VERSION_2, .txnumevt = 1, .rxnumevt = 1, -- 1.7.1 From manjunath.hadli at ti.com Sat Nov 12 07:18:54 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 12 Nov 2011 18:48:54 +0530 Subject: [RFC PATCH v4 0/8] RFC for Media Controller capture driver for DM365 Message-ID: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> These are a updated subset of patches for Media Controller implementation on DM365. Updates from last patch set: 1. Some header reorg. 2. Support for some extra formats - NV12 and other semiplanar. 3. ResizerB support Manjunath Hadli (8): davinci: vpfe: add dm3xx IPIPEIF hardware support module davinci: vpfe: add IPIPE hardware layer support davinci: vpfe: add IPIPE support for media controller driver davinci: vpfe: add support for CCDC hardware for dm365 davinci: vpfe: add ccdc driver with media controller interface davinci: vpfe: add v4l2 video driver support davinci: vpfe: v4l2 capture driver with media interface davinci: vpfe: build infrastructure for dm365 drivers/media/video/davinci/Kconfig | 46 +- drivers/media/video/davinci/Makefile | 17 +- drivers/media/video/davinci/ccdc_hw_device.h | 12 +- drivers/media/video/davinci/ccdc_types.h | 43 + drivers/media/video/davinci/dm365_ccdc.c | 1456 ++++++++++ drivers/media/video/davinci/dm365_ccdc.h | 91 + drivers/media/video/davinci/dm365_ccdc_regs.h | 309 ++ drivers/media/video/davinci/dm365_def_para.c | 310 ++ drivers/media/video/davinci/dm365_def_para.h | 39 + drivers/media/video/davinci/dm365_ipipe.c | 3844 +++++++++++++++++++++++++ drivers/media/video/davinci/dm365_ipipe.h | 378 +++ drivers/media/video/davinci/dm365_ipipe_hw.c | 935 ++++++ drivers/media/video/davinci/dm365_ipipe_hw.h | 539 ++++ drivers/media/video/davinci/dm3xx_ipipeif.c | 312 ++ drivers/media/video/davinci/dm3xx_ipipeif.h | 255 ++ drivers/media/video/davinci/imp_common.h | 86 + drivers/media/video/davinci/imp_hw_if.h | 171 ++ drivers/media/video/davinci/vpfe_capture.c | 796 +++++ drivers/media/video/davinci/vpfe_capture.h | 99 + drivers/media/video/davinci/vpfe_ccdc.c | 817 ++++++ drivers/media/video/davinci/vpfe_ccdc.h | 86 + drivers/media/video/davinci/vpfe_video.c | 1744 +++++++++++ drivers/media/video/davinci/vpfe_video.h | 146 + include/linux/dm365_ccdc.h | 611 ++++ include/linux/dm365_ipipe.h | 1029 +++++++ include/linux/dm3xx_ipipeif.h | 64 + include/linux/imp_common.h | 171 ++ include/media/davinci/vpfe.h | 93 + 28 files changed, 14486 insertions(+), 13 deletions(-) create mode 100644 drivers/media/video/davinci/ccdc_types.h create mode 100644 drivers/media/video/davinci/dm365_ccdc.c create mode 100644 drivers/media/video/davinci/dm365_ccdc.h create mode 100644 drivers/media/video/davinci/dm365_ccdc_regs.h create mode 100644 drivers/media/video/davinci/dm365_def_para.c create mode 100644 drivers/media/video/davinci/dm365_def_para.h create mode 100644 drivers/media/video/davinci/dm365_ipipe.c create mode 100644 drivers/media/video/davinci/dm365_ipipe.h create mode 100644 drivers/media/video/davinci/dm365_ipipe_hw.c create mode 100644 drivers/media/video/davinci/dm365_ipipe_hw.h create mode 100644 drivers/media/video/davinci/dm3xx_ipipeif.c create mode 100644 drivers/media/video/davinci/dm3xx_ipipeif.h create mode 100644 drivers/media/video/davinci/imp_common.h create mode 100644 drivers/media/video/davinci/imp_hw_if.h create mode 100644 drivers/media/video/davinci/vpfe_capture.c create mode 100644 drivers/media/video/davinci/vpfe_capture.h create mode 100644 drivers/media/video/davinci/vpfe_ccdc.c create mode 100644 drivers/media/video/davinci/vpfe_ccdc.h create mode 100644 drivers/media/video/davinci/vpfe_video.c create mode 100644 drivers/media/video/davinci/vpfe_video.h create mode 100644 include/linux/dm365_ccdc.h create mode 100644 include/linux/dm365_ipipe.h create mode 100644 include/linux/dm3xx_ipipeif.h create mode 100644 include/linux/imp_common.h create mode 100644 include/media/davinci/vpfe.h From manjunath.hadli at ti.com Sat Nov 12 07:19:02 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 12 Nov 2011 18:49:02 +0530 Subject: [RFC PATCH v4 8/8] davinci: vpfe: build infrastructure for dm365 In-Reply-To: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> References: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321103942-2778-9-git-send-email-manjunath.hadli@ti.com> add build infrastructure for dm365 specific modules such as IPIPE, AEW, AF. Signed-off-by: Manjunath Hadli Signed-off-by: Nagabhushana Netagunte --- drivers/media/video/davinci/Kconfig | 46 ++++++++++++++++++++++++++++++++- drivers/media/video/davinci/Makefile | 17 +++++++++++- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig index 6b19540..6f6da53 100644 --- a/drivers/media/video/davinci/Kconfig +++ b/drivers/media/video/davinci/Kconfig @@ -11,6 +11,48 @@ config DISPLAY_DAVINCI_DM646X_EVM To compile this driver as a module, choose M here: the module will be called vpif_display. +config VIDEO_DM365_3A_HW + tristate "DM365 Auto Focus, Auto Exposure/ White Balance HW module" + depends on ARCH_DAVINCI_DM365 + help + DM365 Auto Focus, Auto Exposure and Auto White Balancing HW module + + This module has functions which configure AEW/AF hardware, high level + AF module and AEW module use these functionalities. It collects metrics + about the image or video data + +config VIDEO_DM365_AF + tristate "DM365 Auto Focus Driver" + depends on ARCH_DAVINCI_DM365 + select VIDEO_DM365_3A_HW + help + DM365 Auto Focus hardware module. + + Auto Focus driver is used to support control loop for Auto Focus. + It collects metrics about the image or video data. This provides + hooks to AF subdevice driver. + +config VIDEO_DM365_AEW + tristate "DM365 Auto exposure /White Balance Driver" + depends on ARCH_DAVINCI_DM365 + select VIDEO_DM365_3A_HW + help + DM365 Auto Exposure and Auto White Balance hardware module. + + This is used to support the control loops for Auto Exposure + and Auto White Balance. It collects metrics about the image + or video data + +config DM365_IPIPE + depends on ARCH_DAVINCI && ARCH_DAVINCI_DM365 + tristate "DM365 IPIPE" + help + dm365 IPIPE hardware module. + + This is the hardware module that implements imp_hw_interface + for DM365. This hardware module provides previewer and resizer + functionality for image processing. + config CAPTURE_DAVINCI_DM646X_EVM tristate "DM646x EVM Video Capture" depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM @@ -51,7 +93,7 @@ config VIDEO_VPFE_CAPTURE config VIDEO_DM6446_CCDC tristate "DM6446 CCDC HW module" - depends on VIDEO_VPFE_CAPTURE + depends on VIDEO_VPFE_CAPTURE && ARCH_DAVINCI_DM644x select VIDEO_VPSS_SYSTEM default y help @@ -80,7 +122,7 @@ config VIDEO_DM355_CCDC module will be called vpfe. config VIDEO_ISIF - tristate "ISIF HW module" + tristate "DM365 ISIF HW module" depends on ARCH_DAVINCI_DM365 && VIDEO_VPFE_CAPTURE select VIDEO_VPSS_SYSTEM default y diff --git a/drivers/media/video/davinci/Makefile b/drivers/media/video/davinci/Makefile index a379557..8544040 100644 --- a/drivers/media/video/davinci/Makefile +++ b/drivers/media/video/davinci/Makefile @@ -12,7 +12,20 @@ obj-$(CONFIG_CAPTURE_DAVINCI_DM646X_EVM) += vpif_capture.o # Capture: DM6446 and DM355 obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o -obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o +obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o vpfe_ccdc.o \ + vpfe_resizer.o vpfe_previewer.o \ + vpfe_aew.o vpfe_af.o vpfe_video.o obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o -obj-$(CONFIG_VIDEO_ISIF) += isif.o +obj-$(CONFIG_VIDEO_ISIF) += dm365_ccdc.o + +dm365_a3_hw_driver-objs := dm365_a3_hw.o +obj-$(CONFIG_VIDEO_DM365_3A_HW) += dm365_a3_hw_driver.o +dm365_af_driver-objs := dm365_af.o +obj-$(CONFIG_VIDEO_DM365_AF) += dm365_af_driver.o +dm365_aew_driver-objs := dm365_aew.o +obj-$(CONFIG_VIDEO_DM365_AEW) += dm365_aew_driver.o + +dm365_imp-objs := dm365_ipipe.o dm365_def_para.o \ + dm365_ipipe_hw.o dm3xx_ipipeif.o +obj-$(CONFIG_DM365_IPIPE) += dm365_imp.o -- 1.6.2.4 From manjunath.hadli at ti.com Sat Nov 12 07:18:59 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 12 Nov 2011 18:48:59 +0530 Subject: [RFC PATCH v4 5/8] davinci: vpfe: add ccdc driver with media controller interface In-Reply-To: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> References: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321103942-2778-6-git-send-email-manjunath.hadli@ti.com> Add the CCDC driver for davinci Dm3XX SoCs. The driver supports CCDC as a media entity with 2 pads - 1 input and 1 output. The driver implements streaming support and subdev interface. The ccdc supports bayer and YUV formats. Signed-off-by: Manjunath Hadli Signed-off-by: Nagabhushana Netagunte --- drivers/media/video/davinci/ccdc_hw_device.h | 12 +- drivers/media/video/davinci/vpfe_ccdc.c | 817 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpfe_ccdc.h | 86 +++ 3 files changed, 906 insertions(+), 9 deletions(-) create mode 100644 drivers/media/video/davinci/vpfe_ccdc.c create mode 100644 drivers/media/video/davinci/vpfe_ccdc.h diff --git a/drivers/media/video/davinci/ccdc_hw_device.h b/drivers/media/video/davinci/ccdc_hw_device.h index 86b9b35..118c108 100644 --- a/drivers/media/video/davinci/ccdc_hw_device.h +++ b/drivers/media/video/davinci/ccdc_hw_device.h @@ -20,11 +20,10 @@ #ifndef _CCDC_HW_DEVICE_H #define _CCDC_HW_DEVICE_H -#ifdef __KERNEL__ #include #include -#include -#include +#include +#include "ccdc_types.h" /* * ccdc hw operations @@ -57,7 +56,7 @@ struct ccdc_hw_ops { */ int (*get_params) (void *params); /* Pointer to function to configure ccdc */ - int (*configure) (void); + int (*configure) (int mode); /* Pointer to function to set buffer type */ int (*set_buftype) (enum ccdc_buftype buf_type); @@ -102,9 +101,4 @@ struct ccdc_hw_device { struct ccdc_hw_ops hw_ops; }; -/* Used by CCDC module to register & unregister with vpfe capture driver */ -int vpfe_register_ccdc_device(struct ccdc_hw_device *dev); -void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev); - -#endif #endif diff --git a/drivers/media/video/davinci/vpfe_ccdc.c b/drivers/media/video/davinci/vpfe_ccdc.c new file mode 100644 index 0000000..5082f77 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_ccdc.c @@ -0,0 +1,817 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Nagabhushana Netagunte + */ +#include +#include +#include +#include +#include +#include "vpfe_capture.h" + +#include "ccdc_hw_device.h" + +#define MAX_WIDTH 4096 +#define MAX_HEIGHT 4096 + +static const unsigned int ccdc_fmts[] = { + V4L2_MBUS_FMT_YUYV8_2X8, + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_YUYV8_1X16, + V4L2_MBUS_FMT_YUYV10_1X20, + V4L2_MBUS_FMT_SBGGR10_1X10, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, +#ifdef CONFIG_ARCH_DAVINCI_DM365 + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, +#endif +}; + +/* + * CCDC helper functions + */ +/* get field id in ccdc hardware */ +enum v4l2_field ccdc_get_fid(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ccdc_device *ccdc = &vpfe_dev->vpfe_ccdc; + struct ccdc_hw_device *ccdc_dev = ccdc->ccdc_dev; + + return ccdc_dev->hw_ops.getfid(); +} + +/* Retrieve active or try pad format based on query */ +static struct v4l2_mbus_framefmt * +__ccdc_get_format(struct vpfe_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_subdev_format fmt; + + fmt.pad = pad; + fmt.which = which; + + return v4l2_subdev_get_try_format(fh, pad); + } else + return &ccdc->formats[pad]; +} + +/* configure format in ccdc hardware */ +static int vpfe_config_ccdc_format(struct vpfe_device *vpfe_dev, + unsigned int pad) +{ + struct ccdc_hw_device *ccdc_dev = vpfe_dev->vpfe_ccdc.ccdc_dev; + struct vpfe_ccdc_device *vpfe_ccdc = &vpfe_dev->vpfe_ccdc; + enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; + struct v4l2_pix_format format; + int ret = 0; + + v4l2_fill_pix_format(&format, &vpfe_dev->vpfe_ccdc.formats[pad]); + mbus_to_pix(&vpfe_dev->vpfe_ccdc.formats[pad], &format); + + if (ccdc_dev->hw_ops.set_pixel_format( + format.pixelformat) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "couldn't set pix format in ccdc\n"); + return -EINVAL; + } + + /* call for s_crop will override these values */ + vpfe_ccdc->crop.left = 0; + vpfe_ccdc->crop.top = 0; + vpfe_ccdc->crop.width = format.width; + vpfe_ccdc->crop.height = format.height; + + /* configure the image window */ + ccdc_dev->hw_ops.set_image_window(&vpfe_ccdc->crop); + + switch (vpfe_dev->vpfe_ccdc.formats[pad].field) { + case V4L2_FIELD_INTERLACED: + /* do nothing, since it is default */ + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_INTERLEAVED); + break; + case V4L2_FIELD_NONE: + frm_fmt = CCDC_FRMFMT_PROGRESSIVE; + /* buffer type only applicable for interlaced scan */ + break; + case V4L2_FIELD_SEQ_TB: + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_SEPARATED); + break; + default: + return -EINVAL; + } + + /* set the frame format */ + if (!ret) + ret = ccdc_dev->hw_ops.set_frame_format(frm_fmt); + + return ret; +} + +/* + * ccdc_try_format - Try video format on a pad + * @ccdc: VPFE CCDC device + * @fh : V4L2 subdev file handle + * @pad: Pad number + * @fmt: Format + */ +static void +ccdc_try_format(struct vpfe_ccdc_device *vpfe_ccdc, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + unsigned int width = fmt->format.width; + unsigned int height = fmt->format.height; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ccdc_fmts); i++) { + if (fmt->format.code == ccdc_fmts[i]) + break; + } + + /* If not found, use YUYV8_2x8 as default */ + if (i >= ARRAY_SIZE(ccdc_fmts)) + fmt->format.code = V4L2_MBUS_FMT_YUYV8_2X8; + + /* Clamp the size. */ + fmt->format.width = clamp_t(u32, width, 32, MAX_WIDTH); + fmt->format.height = clamp_t(u32, height, 32, MAX_HEIGHT); + + /* The data formatter truncates the number of horizontal output + * pixels to a multiple of 16. To avoid clipping data, allow + * callers to request an output size bigger than the input size + * up to the nearest multiple of 16. + */ + if (fmt->pad == CCDC_PAD_SOURCE) + fmt->format.width &= ~15; +} + +/* + * ccdc_buffer_isr - CCDC module non-progressive buffer scheduling isr + * @ccdc: CCDC device pointer + * + */ +void ccdc_buffer_isr(struct vpfe_ccdc_device *ccdc) +{ + struct vpfe_video_device *video = &ccdc->video_out; + struct ccdc_hw_device *ccdc_dev = ccdc->ccdc_dev; + enum v4l2_field field; + int fid; + + if (!video->started) + return; + + field = video->fmt.fmt.pix.field; + + /* reset sbl overblow bit */ + if (ccdc_dev->hw_ops.reset != NULL) + ccdc_dev->hw_ops.reset(); + + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + if (video->cur_frm != video->next_frm) + vpfe_process_buffer_complete(video); + return; + } + + /* interlaced or TB capture check which field we + * are in hardware + */ + fid = ccdc_dev->hw_ops.getfid(); + + /* switch the software maintained field id */ + video->field_id ^= 1; + if (fid == video->field_id) { + /* we are in-sync here,continue */ + if (fid == 0) { + /* + * One frame is just being captured. If the + * next frame is available, release the current + * frame and move on + */ + if (video->cur_frm != video->next_frm) + vpfe_process_buffer_complete(video); + /* + * based on whether the two fields are stored + * interleavely or separately in memory, + * reconfigure the CCDC memory address + */ + if (field == V4L2_FIELD_SEQ_TB) + vpfe_schedule_bottom_field(video); + + return; + } + /* + * if one field is just being captured configure + * the next frame get the next frame from the + * empty queue if no frame is available hold on + * to the current buffer + */ + spin_lock(&video->dma_queue_lock); + if (!list_empty(&video->dma_queue) && + video->cur_frm == video->next_frm) + vpfe_schedule_next_buffer(video); + spin_unlock(&video->dma_queue_lock); + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + video->field_id = fid; + } +} + +/* + * ccdc_vidint1_isr - CCDC module progressive buffer scheduling isr + * @ccdc: CCDC device pointer + * + */ +void ccdc_vidint1_isr(struct vpfe_ccdc_device *ccdc) +{ + struct vpfe_video_device *video = &ccdc->video_out; + + if (!video->started) + return; + + spin_lock(&video->dma_queue_lock); + if (video->fmt.fmt.pix.field == V4L2_FIELD_NONE && + !list_empty(&video->dma_queue) && video->cur_frm == video->next_frm) + vpfe_schedule_next_buffer(video); + + spin_unlock(&video->dma_queue_lock); +} + +/* + * VPFE video operations + */ + +static void ccdc_video_queue(struct vpfe_device *vpfe_dev, unsigned long addr) +{ + struct vpfe_ccdc_device *vpfe_ccdc = &vpfe_dev->vpfe_ccdc; + struct ccdc_hw_device *ccdc_dev = vpfe_ccdc->ccdc_dev; + + ccdc_dev->hw_ops.setfbaddr(addr); +} + +static const struct vpfe_video_operations ccdc_video_ops = { + .queue = ccdc_video_queue, +}; + + +/* + * V4L2 subdev operations + */ + +/* + * ccdc_ioctl - CCDC module private ioctl's + * @sd: VPFE CCDC V4L2 subdevice + * @cmd: ioctl command + * @arg: ioctl argument + * + * Return 0 on success or a negative error code otherwise. + */ +static long ccdc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct vpfe_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct ccdc_hw_device *ccdc_dev = ccdc->ccdc_dev; + int ret; + + switch (cmd) { + case VPFE_CMD_S_CCDC_RAW_PARAMS: + ret = ccdc_dev->hw_ops.set_params(arg); + break; + case VPFE_CMD_G_CCDC_RAW_PARAMS: + if (!ccdc_dev->hw_ops.get_params) { + ret = -EINVAL; + break; + } + ret = ccdc_dev->hw_ops.get_params(arg); + break; + + default: + ret = -ENOIOCTLCMD; + } + + return ret; +} + +/* + * ccdc_set_stream - Enable/Disable streaming on the CCDC module + * @sd: VPFE CCDC V4L2 subdevice + * @enable: Enable/disable stream + */ +static int ccdc_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct ccdc_hw_device *ccdc_dev = ccdc->ccdc_dev; + int ret; + + if (enable) { + ret = ccdc_dev->hw_ops.configure( + (ccdc->output == CCDC_OUTPUT_MEMORY) ? 0 : 1); + if (ret) + return ret; + + if ((ccdc_dev->hw_ops.enable_out_to_sdram) && + (ccdc->output == CCDC_OUTPUT_MEMORY)) + ccdc_dev->hw_ops.enable_out_to_sdram(1); + + ccdc_dev->hw_ops.enable(1); + } else { + + ccdc_dev->hw_ops.enable(0); + + if (ccdc_dev->hw_ops.enable_out_to_sdram) + ccdc_dev->hw_ops.enable_out_to_sdram(0); + } + + return 0; +} + +/* +* ccdc_set_format - set format on pad +* @sd : VPFE ccdc device +* @fh : V4L2 subdev file handle +* @fmt : pointer to v4l2 subdev format structure +* +* Return 0 on success or -EINVAL if format or pad is invalid +*/ +static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(ccdc); + struct v4l2_mbus_framefmt *format; + + format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + ccdc_try_format(ccdc, fh, fmt); + memcpy(format, &fmt->format, sizeof(*format)); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == CCDC_PAD_SOURCE) + return vpfe_config_ccdc_format(vpfe_dev, fmt->pad); + + return 0; +} + +/* + * ccdc_get_format - Retrieve the video format on a pad + * @sd : VPFE CCDC V4L2 subdevice + * @fh : V4L2 subdev file handle + * @fmt: Format + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ccdc_device *vpfe_ccdc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ccdc_get_format(vpfe_ccdc, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + memcpy(&fmt->format, format, sizeof(fmt->format)); + + return 0; +} + +/* + * ccdc_enum_frame_size - enum frame sizes on pads + * @sd: VPFE ccdc V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_frame_size_enum structure + */ +static int ccdc_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format format; + + if (fse->index != 0) + return -EINVAL; + + format.pad = fse->pad; + format.format.code = fse->code; + format.format.width = 1; + format.format.height = 1; + format.which = V4L2_SUBDEV_FORMAT_TRY; + ccdc_try_format(ccdc, fh, &format); + fse->min_width = format.format.width; + fse->min_height = format.format.height; + + if (format.format.code != fse->code) + return -EINVAL; + + format.pad = fse->pad; + format.format.code = fse->code; + format.format.width = -1; + format.format.height = -1; + format.which = V4L2_SUBDEV_FORMAT_TRY; + ccdc_try_format(ccdc, fh, &format); + fse->max_width = format.format.width; + fse->max_height = format.format.height; + + return 0; +} + +/* + * ccdc_enum_mbus_code - enum mbus codes for pads + * @sd: VPFE ccdc V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case CCDC_PAD_SINK: + case CCDC_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(ccdc_fmts)) + return -EINVAL; + + code->code = ccdc_fmts[code->index]; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * ccdc_pad_set_crop - set crop rectangle on pad + * @sd: VPFE ccdc V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Return 0 on success, -EINVAL if pad is invalid + */ +static int ccdc_pad_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct vpfe_ccdc_device *vpfe_ccdc = v4l2_get_subdevdata(sd); + struct ccdc_hw_device *ccdc_dev = vpfe_ccdc->ccdc_dev; + struct v4l2_mbus_framefmt *format; + + /* check wether its a valid pad */ + if (crop->pad != CCDC_PAD_SINK) + return -EINVAL; + + format = __ccdc_get_format(vpfe_ccdc, fh, crop->pad, crop->which); + if (format == NULL) + return -EINVAL; + + /* check wether crop rect is within limits */ + if (crop->rect.top < 0 || crop->rect.left < 0 || + (crop->rect.left + crop->rect.width > + vpfe_ccdc->formats[CCDC_PAD_SINK].width) || + (crop->rect.top + crop->rect.height > + vpfe_ccdc->formats[CCDC_PAD_SINK].height)) { + crop->rect.left = 0; + crop->rect.top = 0; + crop->rect.width = format->width; + crop->rect.height = format->height; + } + + /* adjust the width to 16 pixel boundry */ + crop->rect.width = ((crop->rect.width + 15) & ~0xf); + + vpfe_ccdc->crop = crop->rect; + + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ccdc_dev->hw_ops.set_image_window(&vpfe_ccdc->crop); + } else { + struct v4l2_rect *rect; + + rect = v4l2_subdev_get_try_crop(fh, CCDC_PAD_SINK); + memcpy(rect, &vpfe_ccdc->crop, sizeof(*rect)); + } + + return 0; +} + +/* + * ccdc_pad_get_crop - get crop rectangle on pad + * @sd: VPFE ccdc V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Return 0 on success, -EINVAL if pad is invalid + */ +static int ccdc_pad_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct vpfe_ccdc_device *vpfe_ccdc = v4l2_get_subdevdata(sd); + + /* check wether its a valid pad */ + if (crop->pad != CCDC_PAD_SINK) + return -EINVAL; + + if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_rect *rect; + rect = v4l2_subdev_get_try_crop(fh, CCDC_PAD_SINK); + memcpy(&crop->rect, rect, sizeof(*rect)); + } else { + crop->rect = vpfe_ccdc->crop; + } + + return 0; +} + +/* + * ccdc_init_formats - Initialize formats on all pads + * @sd: VPFE ccdc V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int ccdc_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + struct v4l2_subdev_crop crop; + + memset(&format, 0, sizeof(format)); + format.pad = CCDC_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SBGGR10_1X10; + format.format.width = MAX_WIDTH; + format.format.height = MAX_HEIGHT; + ccdc_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = CCDC_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SBGGR10_1X10; + format.format.width = MAX_WIDTH; + format.format.height = MAX_HEIGHT; + ccdc_set_format(sd, fh, &format); + + memset(&crop, 0, sizeof(crop)); + crop.pad = CCDC_PAD_SINK; + crop.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + crop.rect.width = MAX_WIDTH; + crop.rect.height = MAX_HEIGHT; + ccdc_pad_set_crop(sd, fh, &crop); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops ccdc_v4l2_core_ops = { + .ioctl = ccdc_ioctl, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_file_ops ccdc_v4l2_file_ops = { + .open = ccdc_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops ccdc_v4l2_video_ops = { + .s_stream = ccdc_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = { + .enum_mbus_code = ccdc_enum_mbus_code, + .enum_frame_size = ccdc_enum_frame_size, + .get_fmt = ccdc_get_format, + .set_fmt = ccdc_set_format, + .set_crop = ccdc_pad_set_crop, + .get_crop = ccdc_pad_get_crop, +}; + +/* v4l2 subdev operations */ +static const struct v4l2_subdev_ops ccdc_v4l2_ops = { + .core = &ccdc_v4l2_core_ops, + .file = &ccdc_v4l2_file_ops, + .video = &ccdc_v4l2_video_ops, + .pad = &ccdc_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * ccdc_link_setup - Setup CCDC connections + * @entity: CCDC media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int ccdc_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + + switch (local->index | media_entity_type(remote->entity)) { + case CCDC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* read from decoder/sensor */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ccdc->input = CCDC_INPUT_NONE; + break; + } + + if (ccdc->input != CCDC_INPUT_NONE) + return -EBUSY; + + ccdc->input = CCDC_INPUT_PARALLEL; + + break; + + case CCDC_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + /* write to memory */ + if (flags & MEDIA_LNK_FL_ENABLED) + ccdc->output = CCDC_OUTPUT_MEMORY; + else + ccdc->output = CCDC_OUTPUT_NONE; + break; + + case CCDC_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (flags & MEDIA_LNK_FL_ENABLED) + ccdc->output = CCDC_OUTPUT_PREVIEWER; + else + ccdc->output = CCDC_OUTPUT_NONE; + + break; + default: + return -EINVAL; + } + + return 0; +} +static const struct media_entity_operations ccdc_media_ops = { + .link_setup = ccdc_link_setup, +}; + +/* + * vpfe_ccdc_unregister_entities - CCDC subdevs/video + * driver unregistrations. + * @ccdc - pointer to ccdc subdevice structure. + */ +void vpfe_ccdc_unregister_entities(struct vpfe_ccdc_device *ccdc) +{ + struct ccdc_hw_device *ccdc_dev = ccdc->ccdc_dev; + struct device *dev = ccdc->subdev.v4l2_dev->dev; + + vpfe_video_unregister(&ccdc->video_out); + + if (ccdc_dev->hw_ops.close) + ccdc_dev->hw_ops.close(dev); + + /* cleanup entity */ + media_entity_cleanup(&ccdc->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&ccdc->subdev); +} + +/* + * vpfe_ccdc_register_entities - CCDC subdevs/video + * driver registrations. + * @ccdc - pointer to ccdc subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +int vpfe_ccdc_register_entities(struct vpfe_ccdc_device *ccdc, + struct v4l2_device *vdev) +{ + struct ccdc_hw_device *ccdc_dev = NULL; + struct vpfe_device *vpfe_dev = to_vpfe_device(ccdc); + struct device *dev = vdev->dev; + unsigned int flags; + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &ccdc->subdev); + if (ret < 0) + return ret; + + ccdc_dev = ccdc->ccdc_dev; + + ret = ccdc_dev->hw_ops.open(dev); + if (ret) + goto out_ccdc_open; + + ret = vpfe_video_register(&ccdc->video_out, vdev); + if (ret) { + printk(KERN_ERR "failed to register ccdc video out device\n"); + goto out_video_register; + } + + ccdc->video_out.vpfe_dev = vpfe_dev; + + flags = 0; + /* connect ccdc to video node */ + ret = media_entity_create_link(&ccdc->subdev.entity, 1, + &ccdc->video_out.video_dev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + + return 0; +out_create_link: + vpfe_video_unregister(&ccdc->video_out); +out_video_register: + if (ccdc_dev->hw_ops.close) + ccdc_dev->hw_ops.close(dev); + +out_ccdc_open: + v4l2_device_unregister_subdev(&ccdc->subdev); + + return ret; +} + +/* + * vpfe_ccdc_init - Initialize V4L2 subdev and media entity + * @ccdc: VPFE CCDC module + * + * Return 0 on success and a negative error code on failure. + */ +int vpfe_ccdc_init(struct vpfe_ccdc_device *ccdc, struct platform_device *pdev) +{ + struct v4l2_subdev *sd = &ccdc->subdev; + struct media_pad *pads = &ccdc->pads[0]; + struct media_entity *me = &sd->entity; + int ret; + + if (ccdc_init(pdev)) { + printk(KERN_ERR "vpfe_ccdc_init-not supported\n"); + return -1; + } + + /* queue ops */ + ccdc->video_out.ops = &ccdc_video_ops; + + v4l2_subdev_init(sd, &ccdc_v4l2_ops); + strlcpy(sd->name, "DAVINCI CCDC", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, ccdc); + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->nevents = DAVINCI_CCDC_NEVENTS; + pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; + pads[CCDC_PAD_SOURCE].flags = MEDIA_PAD_FL_OUTPUT; + + ccdc->input = CCDC_INPUT_NONE; + ccdc->output = CCDC_OUTPUT_NONE; + + me->ops = &ccdc_media_ops; + + ret = media_entity_init(me, CCDC_PADS_NUM, pads, 0); + if (ret) + goto out_davanci_init; + ccdc->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = vpfe_video_init(&ccdc->video_out, "CCDC"); + if (ret) { + printk(KERN_ERR "failed to init ccdc-out video device\n"); + goto out_davanci_init; + } + + ccdc->ccdc_dev = get_ccdc_dev(); + + return 0; + +out_davanci_init: + ccdc_remove(pdev); + return ret; +} + +/* + * vpfe_ccdc_cleanup - CCDC module cleanup. + * @dev: Device pointer specific to the VPFE. + */ +void vpfe_ccdc_cleanup(struct platform_device *pdev) +{ + ccdc_remove(pdev); +} diff --git a/drivers/media/video/davinci/vpfe_ccdc.h b/drivers/media/video/davinci/vpfe_ccdc.h new file mode 100644 index 0000000..19af898 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_ccdc.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _VPFE_CCDC_H +#define _VPFE_CCDC_H + +#define VPFE_CMD_S_CCDC_RAW_PARAMS _IOW('V', 1, \ + struct ccdc_config_params_raw) +#define VPFE_CMD_G_CCDC_RAW_PARAMS _IOR('V', 2, \ + struct ccdc_config_params_raw) + +#ifdef CONFIG_ARCH_DAVINCI_DM365 +#include "dm365_ccdc.h" +#endif + +#ifdef CONFIG_ARCH_DAVINCI_DM355 +#include "dm355_ccdc.h" +#endif + +#ifdef CONFIG_ARCH_DAVINCI_DM644x +#include "dm644x_ccdc.h" +#endif + +#define CCDC_PAD_SINK 0 +#define CCDC_PAD_SOURCE 1 + +#define CCDC_PADS_NUM 2 + +#define DAVINCI_CCDC_NEVENTS 0 + +enum ccdc_input_entity { + CCDC_INPUT_NONE, + CCDC_INPUT_PARALLEL, +}; + +#define CCDC_OUTPUT_NONE (0) +#define CCDC_OUTPUT_MEMORY (1 << 0) +#define CCDC_OUTPUT_RESIZER (1 << 1) +#define CCDC_OUTPUT_PREVIEWER (1 << 2) + +#define CCDC_NOT_CHAINED 0 +#define CCDC_CHAINED 1 + +struct vpfe_ccdc_device { + struct v4l2_subdev subdev; + struct media_pad pads[CCDC_PADS_NUM]; + struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM]; + enum ccdc_input_entity input; + unsigned int output; + + struct ccdc_hw_device *ccdc_dev; + struct v4l2_rect crop; + + /* independent video device */ + struct vpfe_video_device video_out; +}; + +enum v4l2_field ccdc_get_fid(struct vpfe_device *vpfe_dev); +void ccdc_remove(struct platform_device *pdev); +int ccdc_init(struct platform_device *pdev); +struct ccdc_hw_device *get_ccdc_dev(void); + +void vpfe_ccdc_unregister_entities(struct vpfe_ccdc_device *ccdc); +int vpfe_ccdc_register_entities(struct vpfe_ccdc_device *ccdc, + struct v4l2_device *v4l2_dev); +int vpfe_ccdc_init(struct vpfe_ccdc_device *vpfe_ccdc, + struct platform_device *pdev); +void vpfe_ccdc_cleanup(struct platform_device *pdev); +void ccdc_vidint1_isr(struct vpfe_ccdc_device *ccdc); +void ccdc_buffer_isr(struct vpfe_ccdc_device *ccdc); + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Sat Nov 12 07:18:55 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 12 Nov 2011 18:48:55 +0530 Subject: [RFC PATCH v4 1/8] davinci: vpfe: add dm3xx IPIPEIF hardware support module In-Reply-To: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> References: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321103942-2778-2-git-send-email-manjunath.hadli@ti.com> add support for dm3xx IPIPEIF hardware setup. This is the lowest software layer for the dm3x vpfe driver which directly accesses hardware. Add support for features like default pixel correction, dark frame substraction and hardware setup. Signed-off-by: Manjunath Hadli Signed-off-by: Nagabhushana Netagunte --- drivers/media/video/davinci/dm3xx_ipipeif.c | 312 +++++++++++++++++++++++++++ drivers/media/video/davinci/dm3xx_ipipeif.h | 255 ++++++++++++++++++++++ include/linux/dm3xx_ipipeif.h | 64 ++++++ 3 files changed, 631 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/dm3xx_ipipeif.c create mode 100644 drivers/media/video/davinci/dm3xx_ipipeif.h create mode 100644 include/linux/dm3xx_ipipeif.h diff --git a/drivers/media/video/davinci/dm3xx_ipipeif.c b/drivers/media/video/davinci/dm3xx_ipipeif.c new file mode 100644 index 0000000..a3a4554 --- /dev/null +++ b/drivers/media/video/davinci/dm3xx_ipipeif.c @@ -0,0 +1,312 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* ipipe module to hold common functionality across DM355 and DM365 +*/ +#include +#include +#include +#include +#include +#include "dm3xx_ipipeif.h" + +static void *__iomem ipipeif_base_addr; + +static inline u32 regr_if(u32 offset) +{ + return readl(ipipeif_base_addr + offset); +} + +static inline void regw_if(u32 val, u32 offset) +{ + writel(val, ipipeif_base_addr + offset); +} + +void ipipeif_set_enable(char en, unsigned int mode) +{ + regw_if(1, IPIPEIF_ENABLE); +} + +u32 ipipeif_get_enable(void) +{ + return regr_if(IPIPEIF_ENABLE); +} + +int ipipeif_set_address(struct ipipeif *params, unsigned int address) +{ + u32 val; + + if (params->source == 0) + return -EINVAL; + + val = (params->adofs >> 5) & IPIPEIF_ADOFS_LSB_MASK; + regw_if(val, IPIPEIF_ADOFS); + + /* lower sixteen bit */ + val = (address >> IPIPEIF_ADDRL_SHIFT) & IPIPEIF_ADDRL_MASK; + regw_if(val, IPIPEIF_ADDRL); + + /* upper next seven bit */ + val = (address >> IPIPEIF_ADDRU_SHIFT) & IPIPEIF_ADDRU_MASK; + regw_if(val, IPIPEIF_ADDRU); + + return 0; +} + +static void ipipeif_config_dpc(struct ipipeif_dpc *dpc) +{ + u32 val = 0; + + if (dpc->en) { + val = (dpc->en & 1) << IPIPEIF_DPC2_EN_SHIFT; + val |= dpc->thr & IPIPEIF_DPC2_THR_MASK; + } + + regw_if(val, IPIPEIF_DPC2); +} + +#define RD_DATA_15_2 0x7 +/* This function sets up IPIPEIF and is called from + * ipipe_hw_setup() + */ +int ipipeif_hw_setup(struct ipipeif *params, int device_type) +{ + enum v4l2_mbus_pixelcode isif_port_if; + unsigned int val; + + if (params == NULL) + return -EINVAL; + + /* Enable clock to IPIPEIF and IPIPE */ + if (device_type == DM365) + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); + + /* Combine all the fields to make CFG1 register of IPIPEIF */ + val = params->mode << ONESHOT_SHIFT; + val |= params->source << INPSRC_SHIFT; + val |= params->clock_select << CLKSEL_SHIFT; + val |= params->avg_filter << AVGFILT_SHIFT; + val |= params->decimation << DECIM_SHIFT; + + if (device_type == DM355) { + val |= params->var.if_base.ialaw << IALAW_SHIFT; + val |= params->var.if_base.pack_mode << PACK8IN_SHIFT; + val |= params->var.if_base.clk_div << CLKDIV_SHIFT; + val |= params->var.if_base.data_shift << DATASFT_SHIFT; + } else { + /* DM365 IPIPE 5.1 */ + val |= params->var.if_5_1.pack_mode << PACK8IN_SHIFT; + val |= params->var.if_5_1.source1 << INPSRC1_SHIFT; + if (params->source != IPIPEIF_SDRAM_YUV) + val |= params->var.if_5_1.data_shift << DATASFT_SHIFT; + else + val &= ~(RD_DATA_15_2 << DATASFT_SHIFT); + } + regw_if(val, IPIPEIF_CFG1); + + switch (params->source) { + case IPIPEIF_CCDC: + regw_if(params->gain, IPIPEIF_GAIN); + break; + case IPIPEIF_SDRAM_RAW: + case IPIPEIF_CCDC_DARKFM: + regw_if(params->gain, IPIPEIF_GAIN); + /* fall through */ + case IPIPEIF_SDRAM_YUV: + val |= params->var.if_5_1.data_shift << DATASFT_SHIFT; + regw_if(params->glob_hor_size, IPIPEIF_PPLN); + regw_if(params->glob_ver_size, IPIPEIF_LPFR); + regw_if(params->hnum, IPIPEIF_HNUM); + regw_if(params->vnum, IPIPEIF_VNUM); + break; + default: + /* Do nothing */ + return -EINVAL; + } + + /*check if decimation is enable or not */ + if (params->decimation) + regw_if(params->rsz, IPIPEIF_RSZ); + + if (device_type != DM365) + return 0; + + /* Setup sync alignment and initial rsz position */ + val = params->var.if_5_1.align_sync & 1; + val <<= IPIPEIF_INIRSZ_ALNSYNC_SHIFT; + val |= params->var.if_5_1.rsz_start & IPIPEIF_INIRSZ_MASK; + regw_if(val, IPIPEIF_INIRSZ); + + /* Enable DPCM decompression */ + switch (params->source) { + case IPIPEIF_SDRAM_RAW: + val = 0; + if (params->var.if_5_1.dpcm.en) { + val = params->var.if_5_1.dpcm.en & 1; + val |= (params->var.if_5_1.dpcm.type & 1) << + IPIPEIF_DPCM_BITS_SHIFT; + val |= (params->var.if_5_1.dpcm.pred & 1) << + IPIPEIF_DPCM_PRED_SHIFT; + } + regw_if(val, IPIPEIF_DPCM); + + /* set DPC */ + ipipeif_config_dpc(¶ms->var.if_5_1.dpc); + + regw_if(params->var.if_5_1.clip, IPIPEIF_OCLIP); + /* fall through for SDRAM YUV mode */ + isif_port_if = params->var.if_5_1.isif_port.if_type; + /* configure CFG2 */ + val = regr_if(IPIPEIF_CFG2); + switch (isif_port_if) { + case V4L2_MBUS_FMT_YUYV8_1X16: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + regw_if(val, IPIPEIF_CFG2); + break; + default: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + RESETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + regw_if(val, IPIPEIF_CFG2); + break; + } + break; + case IPIPEIF_SDRAM_YUV: + /* Set clock divider */ + if (params->clock_select == IPIPEIF_SDRAM_CLK) { + val = regr_if(IPIPEIF_CLKDIV); + val |= (params->var.if_5_1.clk_div.m - 1) << + IPIPEIF_CLKDIV_M_SHIFT; + val |= (params->var.if_5_1.clk_div.n - 1); + regw_if(val, IPIPEIF_CLKDIV); + } + break; + case IPIPEIF_CCDC: + case IPIPEIF_CCDC_DARKFM: + /* set DPC */ + ipipeif_config_dpc(¶ms->var.if_5_1.dpc); + + /* Set DF gain & threshold control */ + val = 0; + if (params->var.if_5_1.df_gain_en) { + val = params->var.if_5_1.df_gain_thr & + IPIPEIF_DF_GAIN_THR_MASK; + regw_if(val, IPIPEIF_DFSGTH); + val = (params->var.if_5_1.df_gain_en & 1) << + IPIPEIF_DF_GAIN_EN_SHIFT; + val |= params->var.if_5_1.df_gain & + IPIPEIF_DF_GAIN_MASK; + } + regw_if(val, IPIPEIF_DFSGVL); + isif_port_if = params->var.if_5_1.isif_port.if_type; + + /* configure CFG2 */ + val = params->var.if_5_1.isif_port.hdpol << + IPIPEIF_CFG2_HDPOL_SHIFT; + val |= params->var.if_5_1.isif_port.vdpol << + IPIPEIF_CFG2_VDPOL_SHIFT; + + switch (isif_port_if) { + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV10_1X20: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + break; + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + SETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + val |= params->var.if_5_1.pix_order << + IPIPEIF_CFG2_YUV8P_SHIFT; + break; + default: + /* Bayer */ + regw_if(params->var.if_5_1.clip, + IPIPEIF_OCLIP); + } + regw_if(val, IPIPEIF_CFG2); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int __devinit dm3xx_ipipeif_probe(struct platform_device *pdev) +{ + static resource_size_t res_len; + struct resource *res; + int status; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + res_len = resource_size(res); + + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + + ipipeif_base_addr = ioremap_nocache(res->start, res_len); + if (!ipipeif_base_addr) { + status = -EBUSY; + goto fail; + } + return 0; + +fail: + release_mem_region(res->start, res_len); + + return status; +} + +static int dm3xx_ipipeif_remove(struct platform_device *pdev) +{ + struct resource *res; + + iounmap(ipipeif_base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + release_mem_region(res->start, resource_size(res)); + return 0; +} + +static struct platform_driver dm3xx_ipipeif_driver = { + .driver = { + .name = "dm3xx_ipipeif", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(dm3xx_ipipeif_remove), + .probe = dm3xx_ipipeif_probe, +}; + +static int dm3xx_ipipeif_init(void) +{ + return platform_driver_register(&dm3xx_ipipeif_driver); +} + +static void dm3xx_ipipeif_exit(void) +{ + platform_driver_unregister(&dm3xx_ipipeif_driver); +} + +module_init(dm3xx_ipipeif_init); +module_exit(dm3xx_ipipeif_exit); + +MODULE_LICENSE("GPL2"); diff --git a/drivers/media/video/davinci/dm3xx_ipipeif.h b/drivers/media/video/davinci/dm3xx_ipipeif.h new file mode 100644 index 0000000..3995eb0 --- /dev/null +++ b/drivers/media/video/davinci/dm3xx_ipipeif.h @@ -0,0 +1,255 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _DM3XX_IPIPEIF_H +#define _DM3XX_IPIPEIF_H + +#include +#include +#include +#include +#include "vpss.h" + +/* Used to shift input image data based on the data lines connected + * to parallel port + */ +/* IPIPE base specific types */ +enum ipipeif_data_shift { + IPIPEIF_BITS15_2, + IPIPEIF_BITS14_1, + IPIPEIF_BITS13_0, + IPIPEIF_BITS12_0, + IPIPEIF_BITS11_0, + IPIPEIF_BITS10_0, + IPIPEIF_BITS9_0 +}; + +enum ipipeif_clkdiv { + IPIPEIF_DIVIDE_HALF, + IPIPEIF_DIVIDE_THIRD, + IPIPEIF_DIVIDE_FOURTH, + IPIPEIF_DIVIDE_FIFTH, + IPIPEIF_DIVIDE_SIXTH, + IPIPEIF_DIVIDE_EIGHTH, + IPIPEIF_DIVIDE_SIXTEENTH, + IPIPEIF_DIVIDE_THIRTY +}; + +enum ipipeif_clock { + IPIPEIF_PIXCEL_CLK, + IPIPEIF_SDRAM_CLK +}; + +enum ipipeif_pack_mode { + IPIPEIF_PACK_16_BIT, + IPIPEIF_PACK_8_BIT +}; + +enum ipipe_oper_mode { + IPIPEIF_CONTINUOUS, + IPIPEIF_ONE_SHOT +}; + +enum ipipeif_5_1_pack_mode { + IPIPEIF_5_1_PACK_16_BIT, + IPIPEIF_5_1_PACK_8_BIT, + IPIPEIF_5_1_PACK_8_BIT_A_LAW, + IPIPEIF_5_1_PACK_12_BIT +}; + +enum ipipeif_avg_filter { + IPIPEIF_AVG_OFF, + IPIPEIF_AVG_ON +}; + +enum ipipeif_input_source { + IPIPEIF_CCDC, + IPIPEIF_SDRAM_RAW, + IPIPEIF_CCDC_DARKFM, + IPIPEIF_SDRAM_YUV +}; + +enum ipipeif_ialaw { + IPIPEIF_ALAW_OFF, + IPIPEIF_ALAW_ON +}; + +struct ipipeif_base { + enum ipipeif_ialaw ialaw; + enum ipipeif_pack_mode pack_mode; + enum ipipeif_data_shift data_shift; + enum ipipeif_clkdiv clk_div; +}; + +enum ipipeif_input_src1 { + IPIPEIF_SRC1_PARALLEL_PORT, + IPIPEIF_SRC1_SDRAM_RAW, + IPIPEIF_SRC1_ISIF_DARKFM, + IPIPEIF_SRC1_SDRAM_YUV +}; + +enum ipipeif_dpcm_type { + IPIPEIF_DPCM_8BIT_10BIT, + IPIPEIF_DPCM_8BIT_12BIT +}; + +struct ipipeif_dpcm_decomp { + unsigned char en; + enum ipipeif_dpcm_type type; + enum ipipeif_dpcm_pred pred; +}; + +enum ipipeif_dfs_dir { + IPIPEIF_PORT_MINUS_SDRAM, + IPIPEIF_SDRAM_MINUS_PORT +}; + +struct ipipeif_5_1 { + enum ipipeif_5_1_pack_mode pack_mode; + enum ipipeif_5_1_data_shift data_shift; + enum ipipeif_input_src1 source1; + struct ipipeif_5_1_clkdiv clk_div; + /* Defect pixel correction */ + struct ipipeif_dpc dpc; + /* DPCM decompression */ + struct ipipeif_dpcm_decomp dpcm; + /* ISIF port pixel order */ + enum ipipeif_pixel_order pix_order; + /* interface parameters from isif */ + struct vpfe_hw_if_param isif_port; + /* clipped to this value */ + unsigned short clip; + /* Align HSync and VSync to rsz_start */ + unsigned char align_sync; + /* resizer start position */ + unsigned int rsz_start; + /* DF gain enable */ + unsigned char df_gain_en; + /* DF gain value */ + unsigned short df_gain; + /* DF gain threshold value */ + unsigned short df_gain_thr; +}; + +/* ipipeif structures common to DM350 and DM365 used by ipipeif API */ +struct ipipeif { + enum ipipe_oper_mode mode; + enum ipipeif_input_source source; + enum ipipeif_clock clock_select; + unsigned int glob_hor_size; + unsigned int glob_ver_size; + unsigned int hnum; + unsigned int vnum; + unsigned int adofs; + unsigned char rsz; + enum ipipeif_decimation decimation; + enum ipipeif_avg_filter avg_filter; + unsigned short gain; + /* IPIPE 5.1 */ + union var_part { + struct ipipeif_base if_base; + struct ipipeif_5_1 if_5_1; + } var; +}; + +/* IPIPEIF Register Offsets from the base address */ +#define IPIPEIF_ENABLE 0x00 +#define IPIPEIF_CFG1 0x04 +#define IPIPEIF_PPLN 0x08 +#define IPIPEIF_LPFR 0x0c +#define IPIPEIF_HNUM 0x10 +#define IPIPEIF_VNUM 0x14 +#define IPIPEIF_ADDRU 0x18 +#define IPIPEIF_ADDRL 0x1c +#define IPIPEIF_ADOFS 0x20 +#define IPIPEIF_RSZ 0x24 +#define IPIPEIF_GAIN 0x28 + +/* Below registers are available only on IPIPE 5.1 */ +#define IPIPEIF_DPCM 0x2c +#define IPIPEIF_CFG2 0x30 +#define IPIPEIF_INIRSZ 0x34 +#define IPIPEIF_OCLIP 0x38 +#define IPIPEIF_DTUDF 0x3c +#define IPIPEIF_CLKDIV 0x40 +#define IPIPEIF_DPC1 0x44 +#define IPIPEIF_DPC2 0x48 +#define IPIPEIF_DFSGVL 0x4c +#define IPIPEIF_DFSGTH 0x50 +#define IPIPEIF_RSZ3A 0x54 +#define IPIPEIF_INIRSZ3A 0x58 +#define IPIPEIF_RSZ_MIN 16 +#define IPIPEIF_RSZ_MAX 112 +#define IPIPEIF_RSZ_CONST 16 +#define SETBIT(reg, bit) (reg = ((reg) | ((0x00000001)<<(bit)))) +#define RESETBIT(reg, bit) (reg = ((reg) & (~(0x00000001<<(bit))))) + +#define IPIPEIF_ADOFS_LSB_MASK 0x1ff +#define IPIPEIF_ADOFS_LSB_SHIFT 5 +#define IPIPEIF_ADOFS_MSB_MASK 0x200 +#define IPIPEIF_ADDRU_MASK 0x7ff +#define IPIPEIF_ADDRL_SHIFT 5 +#define IPIPEIF_ADDRL_MASK 0xffff +#define IPIPEIF_ADDRU_SHIFT 21 +#define IPIPEIF_ADDRMSB_SHIFT 31 +#define IPIPEIF_ADDRMSB_LEFT_SHIFT 10 + +/* CFG1 Masks and shifts */ +#define ONESHOT_SHIFT 0 +#define DECIM_SHIFT 1 +#define INPSRC_SHIFT 2 +#define CLKDIV_SHIFT 4 +#define AVGFILT_SHIFT 7 +#define PACK8IN_SHIFT 8 +#define IALAW_SHIFT 9 +#define CLKSEL_SHIFT 10 +#define DATASFT_SHIFT 11 +#define INPSRC1_SHIFT 14 + +/* DPC2 */ +#define IPIPEIF_DPC2_EN_SHIFT 12 +#define IPIPEIF_DPC2_THR_MASK 0xfff +/* Applicable for IPIPE 5.1 */ +#define IPIPEIF_DF_GAIN_EN_SHIFT 10 +#define IPIPEIF_DF_GAIN_MASK 0x3ff +#define IPIPEIF_DF_GAIN_THR_MASK 0xfff +/* DPCM */ +#define IPIPEIF_DPCM_BITS_SHIFT 2 +#define IPIPEIF_DPCM_PRED_SHIFT 1 +/* CFG2 */ +#define IPIPEIF_CFG2_HDPOL_SHIFT 1 +#define IPIPEIF_CFG2_VDPOL_SHIFT 2 +#define IPIPEIF_CFG2_YUV8_SHIFT 6 +#define IPIPEIF_CFG2_YUV16_SHIFT 3 +#define IPIPEIF_CFG2_YUV8P_SHIFT 7 + +/* INIRSZ */ +#define IPIPEIF_INIRSZ_ALNSYNC_SHIFT 13 +#define IPIPEIF_INIRSZ_MASK 0x1fff + +/* CLKDIV */ +#define IPIPEIF_CLKDIV_M_SHIFT 8 + +int ipipeif_set_address(struct ipipeif *if_params, unsigned int address); +void ipipeif_set_enable(char en, unsigned int mode); +int ipipeif_hw_setup(struct ipipeif *if_params, int device_type); +u32 ipipeif_get_enable(void); + +#define DM355 0 +#define DM365 1 + +#endif diff --git a/include/linux/dm3xx_ipipeif.h b/include/linux/dm3xx_ipipeif.h new file mode 100644 index 0000000..a63ead5 --- /dev/null +++ b/include/linux/dm3xx_ipipeif.h @@ -0,0 +1,64 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _DM3XX_IPIPEIF_INCLUDE_H +#define _DM3XX_IPIPEIF_INCLUDE_H + +#include + +/* IPIPE 5.1 interface types */ +/* dpcm predicator for IPIPE 5.1 */ +enum ipipeif_dpcm_pred { + IPIPEIF_DPCM_SIMPLE_PRED, + IPIPEIF_DPCM_ADV_PRED +}; + +/* clockdiv for IPIPE 5.1 */ +struct ipipeif_5_1_clkdiv { + unsigned char m; + unsigned char n; +}; + +/* data shift for IPIPE 5.1 */ +enum ipipeif_5_1_data_shift { + IPIPEIF_5_1_BITS11_0, + IPIPEIF_5_1_BITS10_0, + IPIPEIF_5_1_BITS9_0, + IPIPEIF_5_1_BITS8_0, + IPIPEIF_5_1_BITS7_0, + IPIPEIF_5_1_BITS15_4, +}; + +enum ipipeif_decimation { + IPIPEIF_DECIMATION_OFF, + IPIPEIF_DECIMATION_ON +}; + +/* DPC at the if for IPIPE 5.1 */ +struct ipipeif_dpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* threshold */ + unsigned short thr; +}; + +enum ipipeif_pixel_order { + IPIPEIF_CBCR_Y, + IPIPEIF_Y_CBCR +}; + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Sat Nov 12 07:18:58 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 12 Nov 2011 18:48:58 +0530 Subject: [RFC PATCH v4 4/8] davinci: vpfe: add support for CCDC hardware for dm365 In-Reply-To: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> References: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321103942-2778-5-git-send-email-manjunath.hadli@ti.com> add support for ccdc on dm365 SoC. ccdc is responsible for capturing video data- both raw bayer through sync seperate signals and through BT656/1120 interfaces. This driver implements the hardware functionality. Mainly- setting of hardware, validation of parameters, and isr configuration. Signed-off-by: Manjunath Hadli Signed-off-by: Nagabhushana Netagunte --- drivers/media/video/davinci/ccdc_types.h | 43 + drivers/media/video/davinci/dm365_ccdc.c | 1456 +++++++++++++++++++++++++ drivers/media/video/davinci/dm365_ccdc.h | 91 ++ drivers/media/video/davinci/dm365_ccdc_regs.h | 309 ++++++ include/linux/dm365_ccdc.h | 611 +++++++++++ 5 files changed, 2510 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/ccdc_types.h create mode 100644 drivers/media/video/davinci/dm365_ccdc.c create mode 100644 drivers/media/video/davinci/dm365_ccdc.h create mode 100644 drivers/media/video/davinci/dm365_ccdc_regs.h create mode 100644 include/linux/dm365_ccdc.h diff --git a/drivers/media/video/davinci/ccdc_types.h b/drivers/media/video/davinci/ccdc_types.h new file mode 100644 index 0000000..5773874 --- /dev/null +++ b/drivers/media/video/davinci/ccdc_types.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + **************************************************************************/ +#ifndef _CCDC_TYPES_H +#define _CCDC_TYPES_H +enum ccdc_pixfmt { + CCDC_PIXFMT_RAW, + CCDC_PIXFMT_YCBCR_16BIT, + CCDC_PIXFMT_YCBCR_8BIT +}; + +enum ccdc_frmfmt { + CCDC_FRMFMT_PROGRESSIVE, + CCDC_FRMFMT_INTERLACED +}; + +/* PIXEL ORDER IN MEMORY from LSB to MSB */ +/* only applicable for 8-bit input mode */ +enum ccdc_pixorder { + CCDC_PIXORDER_YCBYCR, + CCDC_PIXORDER_CBYCRY, +}; + +enum ccdc_buftype { + CCDC_BUFTYPE_FLD_INTERLEAVED, + CCDC_BUFTYPE_FLD_SEPARATED +}; +#endif diff --git a/drivers/media/video/davinci/dm365_ccdc.c b/drivers/media/video/davinci/dm365_ccdc.c new file mode 100644 index 0000000..c3f3b19 --- /dev/null +++ b/drivers/media/video/davinci/dm365_ccdc.c @@ -0,0 +1,1456 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include +#include +#include +#include "dm365_ccdc.h" +#include "vpss.h" +#include "dm365_ccdc_regs.h" +#include "ccdc_hw_device.h" + +static struct device *dev; + +/* Defauts for module configuation paramaters */ +static struct ccdc_config_params_raw ccdc_config_defaults = { + .linearize = { + .corr_shft = CCDC_NO_SHIFT, + .scale_fact = {1, 0}, + }, + .gain_offset = { + .gain = { + .r_ye = {1, 0}, + .gr_cy = {1, 0}, + .gb_g = {1, 0}, + .b_mg = {1, 0}, + }, + }, + .culling = { + .hcpat_odd = 0xff, + .hcpat_even = 0xff, + .vcpat = 0xff + }, +}; + +/* ISIF operation configuration */ +struct ccdc_oper_config { + enum v4l2_mbus_pixelcode if_type; + struct ccdc_ycbcr_config ycbcr; + struct ccdc_params_raw bayer; + enum ccdc_data_pack data_pack; + void *__iomem base_addr; + void *__iomem linear_tbl0_addr; + void *__iomem linear_tbl1_addr; +}; + +static struct ccdc_oper_config ccdc_cfg = { + .ycbcr = { + .v4l2_pix_fmt = V4L2_PIX_FMT_UYVY, + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .win = CCDC_WIN_NTSC, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED, + }, + .bayer = { + .v4l2_pix_fmt = V4L2_PIX_FMT_SGRBG10ALAW8, + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .gain = { + .r_ye = {1, 0}, + .gr_cy = {1, 0}, + .gb_g = {1, 0}, + .b_mg = {1, 0}, + }, + .cfa_pat = CCDC_CFA_PAT_MOSAIC, + .data_msb = CCDC_BIT_MSB_11, + .config_params = { + .data_size = CCDC_12_BITS, + .data_shift = CCDC_NO_SHIFT, + .col_pat_field0 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED, + }, + .col_pat_field1 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED, + }, + }, + }, + .data_pack = CCDC_DATA_PACK8, +}; + +/* Raw Bayer formats */ +static u32 ccdc_raw_bayer_pix_formats[] = { V4L2_PIX_FMT_SGRBG10ALAW8, + V4L2_PIX_FMT_SGRBG10DPCM8, + V4L2_PIX_FMT_SBGGR16 }; + +/* Raw YUV formats */ +static u32 ccdc_raw_yuv_pix_formats[] = { V4L2_PIX_FMT_UYVY, + V4L2_PIX_FMT_YUYV }; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return readl(ccdc_cfg.base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + writel(val, ccdc_cfg.base_addr + offset); +} + +static inline u32 ccdc_merge(u32 mask, u32 val, u32 offset) +{ + u32 new_val = (regr(offset) & ~mask) | (val & mask); + + regw(new_val, offset); + + return new_val; +} + +static inline void regw_lin_tbl(u32 val, u32 offset, int i) +{ + if (!i) + writel(val, ccdc_cfg.linear_tbl0_addr + offset); + else + writel(val, ccdc_cfg.linear_tbl1_addr + offset); +} + +static void ccdc_disable_all_modules(void) +{ + /* disable BC */ + regw(0, CLAMPCFG); + /* disable vdfc */ + regw(0, DFCCTL); + /* disable CSC */ + regw(0, CSCCTL); + /* disable linearization */ + regw(0, LINCFG0); + /* disable other modules here as they are supported */ +} + +static void ccdc_enable(int en) +{ + if (!en) { + /* Before disable isif, disable all ISIF modules */ + ccdc_disable_all_modules(); + /** + * wait for next VD. Assume lowest scan rate is 12 Hz. So + * 100 msec delay is good enough + */ + } + msleep(100); + ccdc_merge(CCDC_SYNCEN_VDHDEN_MASK, en, SYNCEN); +} + +static void ccdc_enable_output_to_sdram(int en) +{ + ccdc_merge(CCDC_SYNCEN_WEN_MASK, en << CCDC_SYNCEN_WEN_SHIFT, SYNCEN); +} + +static void ccdc_config_culling(struct ccdc_cul *cul) +{ + u32 val; + + /* Horizontal pattern */ + val = cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT; + val |= cul->hcpat_odd; + regw(val, CULH); + + /* vertical pattern */ + regw(cul->vcpat, CULV); + + /* LPF */ + ccdc_merge((CCDC_LPF_MASK << CCDC_LPF_SHIFT), + (cul->en_lpf << CCDC_LPF_SHIFT), MODESET); +} + +static void ccdc_config_gain_offset(void) +{ + struct ccdc_gain_offsets_adj *gain_off_ptr = + &ccdc_cfg.bayer.config_params.gain_offset; + u32 val; + + val = ((gain_off_ptr->gain_sdram_en & 1) << GAIN_SDRAM_EN_SHIFT) | + ((gain_off_ptr->gain_ipipe_en & 1) << GAIN_IPIPE_EN_SHIFT) | + ((gain_off_ptr->gain_h3a_en & 1) << GAIN_H3A_EN_SHIFT) | + ((gain_off_ptr->offset_sdram_en & 1) << OFST_SDRAM_EN_SHIFT) | + ((gain_off_ptr->offset_ipipe_en & 1) << OFST_IPIPE_EN_SHIFT) | + ((gain_off_ptr->offset_h3a_en & 1) << OFST_H3A_EN_SHIFT); + + ccdc_merge(GAIN_OFFSET_EN_MASK, val, CGAMMAWD); + + val = (gain_off_ptr->gain.r_ye.integer & GAIN_INTEGER_MASK) + << GAIN_INTEGER_SHIFT; + val |= ccdc_cfg.bayer.config_params.gain_offset.gain.r_ye.decimal & + GAIN_DECIMAL_MASK; + regw(val, CRGAIN); + + val = (gain_off_ptr->gain.gr_cy.integer & GAIN_INTEGER_MASK) << + GAIN_INTEGER_SHIFT; + val |= gain_off_ptr->gain.gr_cy.decimal & GAIN_DECIMAL_MASK; + + regw(val, CGRGAIN); + + val = (gain_off_ptr->gain.gb_g.integer & GAIN_INTEGER_MASK) << + GAIN_INTEGER_SHIFT; + val |= gain_off_ptr->gain.gb_g.decimal & GAIN_DECIMAL_MASK; + regw(val, CGBGAIN); + + val = (gain_off_ptr->gain.b_mg.integer & GAIN_INTEGER_MASK) << + GAIN_INTEGER_SHIFT; + val |= gain_off_ptr->gain.b_mg.decimal & GAIN_DECIMAL_MASK; + regw(val, CBGAIN); + + regw((gain_off_ptr->offset & OFFSET_MASK), COFSTA); +} + +static void ccdc_restore_defaults(void) +{ + enum vpss_ccdc_source_sel source = VPSS_CCDCIN; + int i; + + memcpy(&ccdc_cfg.bayer.config_params, &ccdc_config_defaults, + sizeof(struct ccdc_config_params_raw)); + + dev_dbg(dev, "\nstarting ccdc_restore_defaults..."); + /* Enable clock to ISIF, IPIPEIF and BL */ + vpss_enable_clock(VPSS_CCDC_CLOCK, 1); + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); + vpss_enable_clock(VPSS_BL_CLOCK, 1); + + /* set all registers to default value */ + for (i = 0; i <= 0x1f8; i += 4) + regw(0, i); + + /* no culling support */ + regw(0xffff, CULH); + regw(0xff, CULV); + + /* Set default offset and gain */ + ccdc_config_gain_offset(); + + vpss_select_ccdc_source(source); + + dev_dbg(dev, "\nEnd of ccdc_restore_defaults..."); +} + +static int ccdc_open(struct device *device) +{ + dev = device; + ccdc_restore_defaults(); + + return 0; +} + +/* This function will configure the window size to be capture in CCDC reg */ +static void ccdc_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, int ppc, int mode) +{ + int horz_nr_pixels; + int vert_nr_lines; + int horz_start; + int vert_start; + int mid_img; + + dev_dbg(dev, "\nStarting ccdc_setwin..."); + /** + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = (image_win->width << (ppc - 1)) - 1; + + /* Writing the horizontal info into the registers */ + regw(horz_start & START_PX_HOR_MASK, SPH); + regw(horz_nr_pixels & NUM_PX_HOR_MASK, LNH); + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + } else { + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* configure VDINT0 and VDINT1 */ + mid_img = vert_start + (image_win->height / 2); + regw(mid_img, VDINT1); + } + + if (!mode) + regw(0, VDINT0); + else + regw(vert_nr_lines, VDINT0); + regw(vert_start & START_VER_ONE_MASK, SLV0); + regw(vert_start & START_VER_TWO_MASK, SLV1); + regw(vert_nr_lines & NUM_LINES_VER, LNV); +} + +static void ccdc_config_bclamp(struct ccdc_black_clamp *bc) +{ + u32 val; + + /** + * DC Offset is always added to image data irrespective of bc enable + * status + */ + val = bc->dc_offset & CCDC_BC_DCOFFSET_MASK; + regw(val, CLDCOFST); + + if (!bc->en) + return; + + val = (bc->bc_mode_color & CCDC_BC_MODE_COLOR_MASK) << + CCDC_BC_MODE_COLOR_SHIFT; + + /* Enable BC and horizontal clamp caculation paramaters */ + val = val | 1 | ((bc->horz.mode & CCDC_HORZ_BC_MODE_MASK) << + CCDC_HORZ_BC_MODE_SHIFT); + + regw(val, CLAMPCFG); + + if (bc->horz.mode != CCDC_HORZ_BC_DISABLE) { + /** + * Window count for calculation + * Base window selection + * pixel limit + * Horizontal size of window + * vertical size of the window + * Horizontal start position of the window + * Vertical start position of the window + */ + val = (bc->horz.win_count_calc & CCDC_HORZ_BC_WIN_COUNT_MASK) | + ((bc->horz.base_win_sel_calc & 1) << + CCDC_HORZ_BC_WIN_SEL_SHIFT) | + ((bc->horz.clamp_pix_limit & 1) << + CCDC_HORZ_BC_PIX_LIMIT_SHIFT) | + ((bc->horz.win_h_sz_calc & + CCDC_HORZ_BC_WIN_H_SIZE_MASK) << + CCDC_HORZ_BC_WIN_H_SIZE_SHIFT) | + ((bc->horz.win_v_sz_calc & + CCDC_HORZ_BC_WIN_V_SIZE_MASK) << + CCDC_HORZ_BC_WIN_V_SIZE_SHIFT); + + regw(val, CLHWIN0); + + val = bc->horz.win_start_h_calc & CCDC_HORZ_BC_WIN_START_H_MASK; + regw(val, CLHWIN1); + + val = bc->horz.win_start_v_calc & CCDC_HORZ_BC_WIN_START_V_MASK; + regw(val, CLHWIN2); + } + + /* vertical clamp caculation paramaters */ + + /* OB H Valid */ + val = bc->vert.ob_h_sz_calc & CCDC_VERT_BC_OB_H_SZ_MASK; + + /* Reset clamp value sel for previous line */ + val |= (bc->vert.reset_val_sel & CCDC_VERT_BC_RST_VAL_SEL_MASK) << + CCDC_VERT_BC_RST_VAL_SEL_SHIFT; + + /* Line average coefficient */ + val |= bc->vert.line_ave_coef << CCDC_VERT_BC_LINE_AVE_COEF_SHIFT; + regw(val, CLVWIN0); + + /* Configured reset value */ + if (bc->vert.reset_val_sel == CCDC_VERT_BC_USE_CONFIG_CLAMP_VAL) { + val = bc->vert.reset_clamp_val & CCDC_VERT_BC_RST_VAL_MASK; + regw(val, CLVRV); + } + + /* Optical Black horizontal start position */ + val = bc->vert.ob_start_h & CCDC_VERT_BC_OB_START_HORZ_MASK; + regw(val, CLVWIN1); + + /* Optical Black vertical start position */ + val = bc->vert.ob_start_v & CCDC_VERT_BC_OB_START_VERT_MASK; + regw(val, CLVWIN2); + + val = bc->vert.ob_v_sz_calc & CCDC_VERT_BC_OB_VERT_SZ_MASK; + regw(val, CLVWIN3); + + /* Vertical start position for BC subtraction */ + val = bc->vert_start_sub & CCDC_BC_VERT_START_SUB_V_MASK; + regw(val, CLSV); +} + +static void ccdc_config_linearization(struct ccdc_linearize *linearize) +{ + u32 val; + u32 i; + + if (!linearize->en) { + regw(0, LINCFG0); + return; + } + + /* shift value for correction */ + val = (linearize->corr_shft & CCDC_LIN_CORRSFT_MASK) << + CCDC_LIN_CORRSFT_SHIFT; + /* enable */ + val |= 1; + regw(val, LINCFG0); + + /* Scale factor */ + val = (linearize->scale_fact.integer & 1) << + CCDC_LIN_SCALE_FACT_INTEG_SHIFT; + val |= linearize->scale_fact.decimal & CCDC_LIN_SCALE_FACT_DECIMAL_MASK; + regw(val, LINCFG1); + + for (i = 0; i < CCDC_LINEAR_TAB_SIZE; i++) { + val = linearize->table[i] & CCDC_LIN_ENTRY_MASK; + if (i%2) + regw_lin_tbl(val, ((i >> 1) << 2), 1); + else + regw_lin_tbl(val, ((i >> 1) << 2), 0); + } +} + +static void ccdc_config_dfc(struct ccdc_dfc *vdfc) +{ +#define DFC_WRITE_WAIT_COUNT 1000 + u32 count = DFC_WRITE_WAIT_COUNT; + u32 val; + int i; + + if (!vdfc->en) + return; + + /* Correction mode */ + val = (vdfc->corr_mode & CCDC_VDFC_CORR_MOD_MASK) << + CCDC_VDFC_CORR_MOD_SHIFT; + + /* Correct whole line or partial */ + if (vdfc->corr_whole_line) + val |= 1 << CCDC_VDFC_CORR_WHOLE_LN_SHIFT; + + /* level shift value */ + val |= (vdfc->def_level_shift & CCDC_VDFC_LEVEL_SHFT_MASK) << + CCDC_VDFC_LEVEL_SHFT_SHIFT; + + regw(val, DFCCTL); + + /* Defect saturation level */ + val = vdfc->def_sat_level & CCDC_VDFC_SAT_LEVEL_MASK; + regw(val, VDFSATLV); + + regw(vdfc->table[0].pos_vert & CCDC_VDFC_POS_MASK, DFCMEM0); + regw(vdfc->table[0].pos_horz & CCDC_VDFC_POS_MASK, DFCMEM1); + if (vdfc->corr_mode == CCDC_VDFC_NORMAL || + vdfc->corr_mode == CCDC_VDFC_HORZ_INTERPOL_IF_SAT) { + regw(vdfc->table[0].level_at_pos, DFCMEM2); + regw(vdfc->table[0].level_up_pixels, DFCMEM3); + regw(vdfc->table[0].level_low_pixels, DFCMEM4); + } + + val = regr(DFCMEMCTL); + /* set DFCMARST and set DFCMWR */ + val |= 1 << CCDC_DFCMEMCTL_DFCMARST_SHIFT; + val |= 1; + regw(val, DFCMEMCTL); + + while (count && (regr(DFCMEMCTL) & 0x01)) + count--; + + val = regr(DFCMEMCTL); + if (!count) { + dev_dbg(dev, "defect table write timeout !!!\n"); + return; + } + + for (i = 1; i < vdfc->num_vdefects; i++) { + regw(vdfc->table[i].pos_vert & CCDC_VDFC_POS_MASK, DFCMEM0); + regw(vdfc->table[i].pos_horz & CCDC_VDFC_POS_MASK, DFCMEM1); + if (vdfc->corr_mode == CCDC_VDFC_NORMAL || + vdfc->corr_mode == CCDC_VDFC_HORZ_INTERPOL_IF_SAT) { + regw(vdfc->table[i].level_at_pos, DFCMEM2); + regw(vdfc->table[i].level_up_pixels, DFCMEM3); + regw(vdfc->table[i].level_low_pixels, DFCMEM4); + } + val = regr(DFCMEMCTL); + /* clear DFCMARST and set DFCMWR */ + val &= ~(1 << CCDC_DFCMEMCTL_DFCMARST_SHIFT); + val |= 1; + regw(val, DFCMEMCTL); + + count = DFC_WRITE_WAIT_COUNT; + while (count && (regr(DFCMEMCTL) & 0x01)) + count--; + + val = regr(DFCMEMCTL); + if (!count) { + dev_err(dev, "defect table write timeout !!!\n"); + return; + } + } + if (vdfc->num_vdefects < CCDC_VDFC_TABLE_SIZE) { + /* Extra cycle needed */ + regw(0, DFCMEM0); + regw(0x1FFF, DFCMEM1); + val = 1; + regw(val, DFCMEMCTL); + } + + /* enable VDFC */ + ccdc_merge((1 << CCDC_VDFC_EN_SHIFT), (1 << CCDC_VDFC_EN_SHIFT), + DFCCTL); + + ccdc_merge((1 << CCDC_VDFC_EN_SHIFT), (0 << CCDC_VDFC_EN_SHIFT), + DFCCTL); + + regw(0x6, DFCMEMCTL); + for (i = 0 ; i < vdfc->num_vdefects; i++) { + count = DFC_WRITE_WAIT_COUNT; + while (count && (regr(DFCMEMCTL) & 0x2)) + count--; + + val = regr(DFCMEMCTL); + if (!count) { + dev_err(dev, "defect table write timeout !!!\n"); + return; + } + + val = regr(DFCMEM0) | regr(DFCMEM1) | regr(DFCMEM2) | + regr(DFCMEM3) | regr(DFCMEM4); + regw(0x2, DFCMEMCTL); + } +} + +static void ccdc_config_csc(struct ccdc_df_csc *df_csc) +{ + u32 val1; + u32 val2; + u32 i; + + if (!df_csc->csc.en) { + regw(0, CSCCTL); + return; + } + + /* initialize all bits to 0 */ + val1 = 0; + + for (i = 0; i < CCDC_CSC_NUM_COEFF; i++) { + if ((i % 2) == 0) { + /* CSCM - LSB */ + val1 = ((df_csc->csc.coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) << + CCDC_CSC_COEF_INTEG_SHIFT) | + ((df_csc->csc.coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK)); + } else { + + /* CSCM - MSB */ + val2 = ((df_csc->csc.coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) << + CCDC_CSC_COEF_INTEG_SHIFT) | + ((df_csc->csc.coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK)); + val2 <<= CCDC_CSCM_MSB_SHIFT; + val2 |= val1; + regw(val2, (CSCM0 + ((i-1) << 1))); + } + } + + /* program the active area */ + regw(df_csc->start_pix & CCDC_DF_CSC_SPH_MASK, FMTSPH); + /** + * one extra pixel as required for CSC. Actually number of + * pixel - 1 should be configured in this register. So we + * need to subtract 1 before writing to FMTSPH, but we will + * not do this since csc requires one extra pixel + */ + regw((df_csc->num_pixels) & CCDC_DF_CSC_SPH_MASK, FMTLNH); + regw(df_csc->start_line & CCDC_DF_CSC_SPH_MASK, FMTSLV); + /** + * one extra line as required for CSC. See reason documented for + * num_pixels + */ + regw((df_csc->num_lines) & CCDC_DF_CSC_SPH_MASK, FMTLNV); + + /* Enable CSC */ + regw(1, CSCCTL); +} + +static int ccdc_config_raw(int mode) +{ + struct ccdc_params_raw *params = &ccdc_cfg.bayer; + struct ccdc_config_params_raw *module_params = + &ccdc_cfg.bayer.config_params; + struct vpss_pg_frame_size frame_size; + struct vpss_sync_pol sync; + u32 val; + + dev_dbg(dev, "\nStarting ccdc_config_raw..\n"); + + /* In case of user has set BT656IF earlier, it should be reset + when configuring for raw input. + */ + regw(0, REC656IF); + + /* Configure CCDCFG register */ + + /** + * Set CCD Not to swap input since input is RAW data + * Set FID detection function to Latch at V-Sync + * Set WENLOG - ccdc valid area + * Set TRGSEL + * Set EXTRG + * Packed to 8 or 16 bits + */ + + val = CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC | + CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN | + CCDC_CCDCFG_EXTRG_DISABLE | (ccdc_cfg.data_pack & + CCDC_DATA_PACK_MASK); + + dev_dbg(dev, "Writing 0x%x to ...CCDCFG\n", val); + regw(val, CCDCFG); + + /** + * Configure the vertical sync polarity(MODESET.VDPOL) + * Configure the horizontal sync polarity (MODESET.HDPOL) + * Configure frame id polarity (MODESET.FLDPOL) + * Configure data polarity + * Configure External WEN Selection + * Configure frame format(progressive or interlace) + * Configure pixel format (Input mode) + * Configure the data shift + */ + + val = CCDC_VDHDOUT_INPUT | ((params->vd_pol & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT) | ((params->hd_pol & CCDC_HD_POL_MASK) << + CCDC_HD_POL_SHIFT) | ((params->fid_pol & CCDC_FID_POL_MASK) << + CCDC_FID_POL_SHIFT) | ((CCDC_DATAPOL_NORMAL & + CCDC_DATAPOL_MASK) << CCDC_DATAPOL_SHIFT) | ((CCDC_EXWEN_DISABLE & + CCDC_EXWEN_MASK) << CCDC_EXWEN_SHIFT) | ((params->frm_fmt & + CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | ((params->pix_fmt & + CCDC_INPUT_MASK) << CCDC_INPUT_SHIFT) | ((params->config_params. + data_shift & CCDC_DATASFT_MASK) << CCDC_DATASFT_SHIFT); + + regw(val, MODESET); + dev_dbg(dev, "Writing 0x%x to MODESET...\n", val); + + /** + * Configure GAMMAWD register + * CFA pattern setting + */ + val = (params->cfa_pat & CCDC_GAMMAWD_CFA_MASK) << + CCDC_GAMMAWD_CFA_SHIFT; + + /* Gamma msb */ + if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10ALAW8) + val = val | CCDC_ALAW_ENABLE; + + val = val | ((params->data_msb & CCDC_ALAW_GAMA_WD_MASK) << + CCDC_ALAW_GAMA_WD_SHIFT); + + regw(val, CGAMMAWD); + + /* Configure DPCM compression settings */ + if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10DPCM8) { + val = 1 << CCDC_DPCM_EN_SHIFT; + val |= (module_params->pred & + CCDC_DPCM_PREDICTOR_MASK) << CCDC_DPCM_PREDICTOR_SHIFT; + } + + regw(val, MISC); + /* Configure Gain & Offset */ + + ccdc_config_gain_offset(); + + /* Configure Color pattern */ + val = params->config_params.col_pat_field0.olop | + (params->config_params.col_pat_field0.olep << 2) | + (params->config_params.col_pat_field0.elop << 4) | + (params->config_params.col_pat_field0.elep << 6) | + (params->config_params.col_pat_field1.olop << 8) | + (params->config_params.col_pat_field1.olep << 10) | + (params->config_params.col_pat_field1.elop << 12) | + (params->config_params.col_pat_field1.elep << 14); + regw(val, CCOLP); + dev_dbg(dev, "Writing %x to CCOLP ...\n", val); + + /* Configure HSIZE register */ + val = (params->horz_flip_en & CCDC_HSIZE_FLIP_MASK) << + CCDC_HSIZE_FLIP_SHIFT; + + /* calculate line offset in 32 bytes based on pack value */ + if (ccdc_cfg.data_pack == CCDC_PACK_8BIT) + val |= ((params->win.width + 31) >> 5) & CCDC_LINEOFST_MASK; + else if (ccdc_cfg.data_pack == CCDC_PACK_12BIT) + val |= ((((params->win.width + (params->win.width >> 2)) + + 31) >> 5) & CCDC_LINEOFST_MASK); + else + val |= (((params->win.width * 2) + 31) >> 5) & + CCDC_LINEOFST_MASK; + regw(val, HSIZE); + + /* Configure SDOFST register */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_en) { + /* For interlace inverse mode */ + regw(0x4B6D, SDOFST); + dev_dbg(dev, "Writing 0x4B6D to SDOFST...\n"); + } else { + /* For interlace non inverse mode */ + regw(0x0B6D, SDOFST); + dev_dbg(dev, "Writing 0x0B6D to SDOFST...\n"); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + if (params->image_invert_en) { + /* For progessive inverse mode */ + regw(0x4000, SDOFST); + dev_dbg(dev, "Writing 0x4000 to SDOFST...\n"); + } else { + /* For progessive non inverse mode */ + regw(0x0000, SDOFST); + dev_dbg(dev, "Writing 0x0000 to SDOFST...\n"); + } + } + + /* Configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 1, mode); + + /* Configure Black Clamp */ + ccdc_config_bclamp(&module_params->bclamp); + + /* Configure Vertical Defection Pixel Correction */ + ccdc_config_dfc(&module_params->dfc); + + if (!module_params->df_csc.df_or_csc) + /* Configure Color Space Conversion */ + ccdc_config_csc(&module_params->df_csc); + + ccdc_config_linearization(&module_params->linearize); + + /* Configure Culling */ + ccdc_config_culling(&module_params->culling); + + /* Configure Horizontal and vertical offsets(DFC,LSC,Gain) */ + val = module_params->horz_offset & CCDC_DATA_H_OFFSET_MASK; + regw(val, DATAHOFST); + + val = module_params->vert_offset & CCDC_DATA_V_OFFSET_MASK; + regw(val, DATAVOFST); + + /* Setup test pattern if enabled */ + if (params->config_params.test_pat_gen) { + /* Use the HD/VD pol settings from user */ + sync.ccdpg_hdpol = params->hd_pol & CCDC_HD_POL_MASK; + sync.ccdpg_vdpol = params->vd_pol & CCDC_VD_POL_MASK; + + vpss_set_sync_pol(sync); + + frame_size.hlpfr = ccdc_cfg.bayer.win.width; + frame_size.pplen = ccdc_cfg.bayer.win.height; + vpss_set_pg_frame_size(frame_size); + vpss_select_ccdc_source(VPSS_PGLPBK); + } + + dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n"); + + return 0; +} + +static int ccdc_validate_df_csc_params(struct ccdc_df_csc *df_csc) +{ + struct ccdc_color_space_conv *csc; + int err = -EINVAL; + int csc_df_en; + int i; + + if (!df_csc->df_or_csc) { + /* csc configuration */ + csc = &df_csc->csc; + if (csc->en) { + csc_df_en = 1; + for (i = 0; i < CCDC_CSC_NUM_COEFF; i++) { + if (csc->coeff[i].integer > + CCDC_CSC_COEF_INTEG_MASK || + csc->coeff[i].decimal > + CCDC_CSC_COEF_DECIMAL_MASK) { + dev_dbg(dev, + "invalid csc coefficients\n"); + return err; + } + } + } + } + + if (df_csc->start_pix > CCDC_DF_CSC_SPH_MASK) { + dev_dbg(dev, "invalid df_csc start pix value\n"); + return err; + } + if (df_csc->num_pixels > CCDC_DF_NUMPIX) { + dev_dbg(dev, "invalid df_csc num pixels value\n"); + return err; + } + if (df_csc->start_line > CCDC_DF_CSC_LNH_MASK) { + dev_dbg(dev, "invalid df_csc start_line value\n"); + return err; + } + if (df_csc->num_lines > CCDC_DF_NUMLINES) { + dev_dbg(dev, "invalid df_csc num_lines value\n"); + return err; + } + + return 0; +} + +static int ccdc_validate_dfc_params(struct ccdc_dfc *dfc) +{ + int err = -EINVAL; + int i; + + if (!dfc->en) + return 0; + + if (dfc->corr_whole_line > 1) { + dev_dbg(dev, "invalid corr_whole_line value\n"); + return err; + } + + if (dfc->def_level_shift > 4) { + dev_dbg(dev, "invalid def_level_shift value\n"); + return err; + } + + if (dfc->def_sat_level > 4095) { + dev_dbg(dev, "invalid def_sat_level value\n"); + return err; + } + + if (!dfc->num_vdefects || dfc->num_vdefects > 8) { + dev_dbg(dev, "invalid num_vdefects value\n"); + return err; + } + + for (i = 0; i < CCDC_VDFC_TABLE_SIZE; i++) { + if (dfc->table[i].pos_vert > 0x1fff) { + dev_dbg(dev, "invalid pos_vert value\n"); + return err; + } + + if (dfc->table[i].pos_horz > 0x1fff) { + dev_dbg(dev, "invalid pos_horz value\n"); + return err; + } + } + + return 0; +} + +static int ccdc_validate_bclamp_params(struct ccdc_black_clamp *bclamp) +{ + int err = -EINVAL; + + if (bclamp->dc_offset > 0x1fff) { + dev_dbg(dev, "invalid bclamp dc_offset value\n"); + return err; + } + + if (!bclamp->en) + return 0; + + if (bclamp->horz.clamp_pix_limit > 1) { + dev_dbg(dev, + "invalid bclamp horz clamp_pix_limit value\n"); + return err; + } + + if (bclamp->horz.win_count_calc < 1 || + bclamp->horz.win_count_calc > 32) { + dev_dbg(dev, "invalid bclamp horz win_count_calc value\n"); + return err; + } + + if (bclamp->horz.win_start_h_calc > 0x1fff) { + dev_dbg(dev, "invalid bclamp win_start_v_calc value\n"); + return err; + } + + if (bclamp->horz.win_start_v_calc > 0x1fff) { + dev_dbg(dev, "invalid bclamp win_start_v_calc value\n"); + return err; + } + + if (bclamp->vert.reset_clamp_val > 0xfff) { + dev_dbg(dev, "invalid bclamp reset_clamp_val value\n"); + return err; + } + + if (bclamp->vert.ob_v_sz_calc > 0x1fff) { + dev_dbg(dev, "invalid bclamp ob_v_sz_calc value\n"); + return err; + } + + if (bclamp->vert.ob_start_h > 0x1fff) { + dev_dbg(dev, "invalid bclamp ob_start_h value\n"); + return err; + } + + if (bclamp->vert.ob_start_v > 0x1fff) { + dev_dbg(dev, "invalid bclamp ob_start_h value\n"); + return err; + } + + return 0; +} + +static int ccdc_validate_gain_ofst_params(struct ccdc_gain_offsets_adj + *gain_offset) +{ + int err = -EINVAL; + + if (gain_offset->gain_sdram_en || gain_offset->gain_ipipe_en || + gain_offset->gain_h3a_en) { + if (gain_offset->gain.r_ye.integer > 7 || + gain_offset->gain.r_ye.decimal > 0x1ff) { + dev_dbg(dev, "invalid gain r_ye\n"); + return err; + } + if ((gain_offset->gain.gr_cy.integer > 7) || + (gain_offset->gain.gr_cy.decimal > 0x1ff)) { + dev_dbg(dev, "invalid gain gr_cy\n"); + return err; + } + if (gain_offset->gain.gb_g.integer > 7 || + gain_offset->gain.gb_g.decimal > 0x1ff) { + dev_dbg(dev, "invalid gain gb_g\n"); + return err; + } + if (gain_offset->gain.b_mg.integer > 7 || + gain_offset->gain.b_mg.decimal > 0x1ff) { + dev_dbg(dev, "invalid gain b_mg\n"); + return err; + } + } + if ((gain_offset->offset_sdram_en || gain_offset->offset_ipipe_en || + gain_offset->offset_h3a_en) && gain_offset->offset > 0xfff) { + dev_dbg(dev, "invalid gain b_mg\n"); + return err; + } + + return 0; +} + +static int +validate_ccdc_config_params_raw(struct ccdc_config_params_raw *params) +{ + int err; + + err = ccdc_validate_df_csc_params(¶ms->df_csc); + if (err) + goto exit; + err = ccdc_validate_dfc_params(¶ms->dfc); + if (err) + goto exit; + err = ccdc_validate_bclamp_params(¶ms->bclamp); + if (err) + goto exit; + err = ccdc_validate_gain_ofst_params(¶ms->gain_offset); +exit: + return err; +} + +static int ccdc_set_buftype(enum ccdc_buftype buf_type) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) + ccdc_cfg.bayer.buf_type = buf_type; + else + ccdc_cfg.ycbcr.buf_type = buf_type; + + return 0; +} + +static enum ccdc_buftype ccdc_get_buftype(void) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) + return ccdc_cfg.bayer.buf_type; + + return ccdc_cfg.ycbcr.buf_type; +} + +static int ccdc_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) { + if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { + *pix = ccdc_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { + *pix = ccdc_raw_yuv_pix_formats[i]; + ret = 0; + } + } + + return ret; +} + +static int ccdc_set_pixel_format(unsigned int pixfmt) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) { + if (pixfmt == V4L2_PIX_FMT_SBGGR16) + ccdc_cfg.data_pack = CCDC_PACK_16BIT; + else if ((pixfmt == V4L2_PIX_FMT_SGRBG10DPCM8) || + (pixfmt == V4L2_PIX_FMT_SGRBG10ALAW8)) + ccdc_cfg.data_pack = CCDC_PACK_8BIT; + else + return -EINVAL; + + ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + ccdc_cfg.bayer.v4l2_pix_fmt = pixfmt; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + + ccdc_cfg.data_pack = CCDC_PACK_8BIT; + ccdc_cfg.ycbcr.v4l2_pix_fmt = pixfmt; + } + + return 0; +} + +static u32 ccdc_get_pixel_format(void) +{ + u32 pixfmt; + + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) + pixfmt = ccdc_cfg.bayer.v4l2_pix_fmt; + else + pixfmt = ccdc_cfg.ycbcr.v4l2_pix_fmt; + + return pixfmt; +} + +static int ccdc_set_image_window(struct v4l2_rect *win) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) { + ccdc_cfg.bayer.win.top = win->top; + ccdc_cfg.bayer.win.left = win->left; + ccdc_cfg.bayer.win.width = win->width; + ccdc_cfg.bayer.win.height = win->height; + } else { + ccdc_cfg.ycbcr.win.top = win->top; + ccdc_cfg.ycbcr.win.left = win->left; + ccdc_cfg.ycbcr.win.width = win->width; + ccdc_cfg.ycbcr.win.height = win->height; + } + + return 0; +} + +static void ccdc_get_image_window(struct v4l2_rect *win) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) + *win = ccdc_cfg.bayer.win; + else + *win = ccdc_cfg.ycbcr.win; +} + +static unsigned int ccdc_get_line_length(void) +{ + unsigned int len; + + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) { + if (ccdc_cfg.data_pack == CCDC_PACK_8BIT) + len = ((ccdc_cfg.bayer.win.width)); + else if (ccdc_cfg.data_pack == CCDC_PACK_12BIT) + len = (ccdc_cfg.bayer.win.width * 2) + + (ccdc_cfg.bayer.win.width >> 2); + else + len = ccdc_cfg.bayer.win.width * 2; + } else { + len = ccdc_cfg.ycbcr.win.width * 2; + } + + return ALIGN(len, 32); +} + +static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) + ccdc_cfg.bayer.frm_fmt = frm_fmt; + else + ccdc_cfg.ycbcr.frm_fmt = frm_fmt; + + return 0; +} + +static enum ccdc_frmfmt ccdc_get_frame_format(void) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) + return ccdc_cfg.bayer.frm_fmt; + else + return ccdc_cfg.ycbcr.frm_fmt; +} + +static int ccdc_getfid(void) +{ + return (regr(MODESET) >> 15) & 0x1; +} + +/* misc operations */ +static void ccdc_setfbaddr(unsigned long addr) +{ + regw((addr >> 21) & 0x07ff, CADU); + regw((addr >> 5) & 0x0ffff, CADL); +} + +static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + ccdc_cfg.if_type = params->if_type; + + switch (params->if_type) { + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + case V4L2_MBUS_FMT_Y8_1X8: + ccdc_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT; + break; + case V4L2_MBUS_FMT_YUYV10_1X20: + case V4L2_MBUS_FMT_YUYV8_1X16: + ccdc_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_16BIT; + break; + case V4L2_MBUS_FMT_SBGGR10_1X10: + ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + break; + default: + dev_dbg(dev, "Invalid interface type\n"); + return -EINVAL; + } + + return 0; +} + +/* Parameter operations */ +static int ccdc_get_params(void *params) +{ + /* only raw module parameters can be set through the IOCTL */ + if (ccdc_cfg.if_type != V4L2_MBUS_FMT_SBGGR10_1X10) + return -EINVAL; + + memcpy(params, &ccdc_cfg.bayer.config_params, + sizeof(ccdc_cfg.bayer.config_params)); + + return 0; +} + +/* Parameter operations */ +static int ccdc_set_params(void *params) +{ + struct ccdc_config_params_raw ccdc_raw_params; + int ret = -EINVAL; + + /* only raw module parameters can be set through the IOCTL */ + if (ccdc_cfg.if_type != V4L2_MBUS_FMT_SBGGR10_1X10) + return ret; + + memcpy(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); + + if (!validate_ccdc_config_params_raw(&ccdc_raw_params)) { + memcpy(&ccdc_cfg.bayer.config_params, &ccdc_raw_params, + sizeof(ccdc_raw_params)); + ret = 0; + } + + return ret; +} + +/* This function will configure CCDC for YCbCr parameters. */ +static int ccdc_config_ycbcr(int mode) +{ + struct ccdc_ycbcr_config *params = &ccdc_cfg.ycbcr; + struct vpss_pg_frame_size frame_size; + struct vpss_sync_pol sync; + u32 modeset; + u32 ccdcfg; + + /** + * first reset the CCDC + * all registers have default values after reset + * This is important since we assume default values to be set in + * a lot of registers that we didn't touch + */ + dev_dbg(dev, "\nStarting ccdc_config_ycbcr..."); + + /* start with all bits zero */ + modeset = 0; + ccdcfg = 0; + + /* configure pixel format or input mode */ + modeset = modeset | ((params->pix_fmt & CCDC_INPUT_MASK) << + CCDC_INPUT_SHIFT) | ((params->frm_fmt & CCDC_FRM_FMT_MASK) << + CCDC_FRM_FMT_SHIFT) | (((params->fid_pol & + CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT)) | + (((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT)) | + (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT)); + + /* pack the data to 8-bit CCDCCFG */ + switch (ccdc_cfg.if_type) { + case V4L2_MBUS_FMT_YUYV8_2X8: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { + dev_dbg(dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + modeset |= ((VPFE_PINPOL_NEGATIVE & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT); + regw(3, REC656IF); + ccdcfg = ccdcfg | CCDC_DATA_PACK8 | CCDC_YCINSWP_YCBCR; + break; + case V4L2_MBUS_FMT_YUYV10_2X10: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { + dev_dbg(dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* setup BT.656, embedded sync */ + regw(3, REC656IF); + /* enable 10 bit mode in ccdcfg */ + ccdcfg = ccdcfg | CCDC_DATA_PACK8 | CCDC_YCINSWP_YCBCR | + CCDC_BW656_ENABLE; + break; + case V4L2_MBUS_FMT_YUYV10_1X20: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { + dev_dbg(dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + regw(3, REC656IF); + break; + + case V4L2_MBUS_FMT_Y8_1X8: + ccdcfg |= CCDC_DATA_PACK8; + ccdcfg |= CCDC_YCINSWP_YCBCR; + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { + dev_dbg(dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + case V4L2_MBUS_FMT_YUYV8_1X16: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { + dev_dbg(dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + default: + /* should never come here */ + dev_dbg(dev, "Invalid interface type\n"); + return -EINVAL; + } + + regw(modeset, MODESET); + + /* Set up pix order */ + ccdcfg |= (params->pix_order & CCDC_PIX_ORDER_MASK) << + CCDC_PIX_ORDER_SHIFT; + + regw(ccdcfg, CCDCFG); + + /* configure video window */ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_YUYV10_1X20 || + ccdc_cfg.if_type == V4L2_MBUS_FMT_YUYV8_1X16) + ccdc_setwin(¶ms->win, params->frm_fmt, 1, mode); + else + ccdc_setwin(¶ms->win, params->frm_fmt, 2, mode); + + /** + * configure the horizontal line offset + * this is done by rounding up width to a multiple of 16 pixels + * and multiply by two to account for y:cb:cr 4:2:2 data + */ + regw(((((params->win.width * 2) + 31) & 0xffffffe0) >> 5), HSIZE); + + /* configure the memory line offset */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED && + params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) { + /* two fields are interleaved in memory */ + regw(0x00000249, SDOFST); + } + + /* Setup test pattern if enabled */ + if (ccdc_cfg.bayer.config_params.test_pat_gen) { + sync.ccdpg_hdpol = (params->hd_pol & CCDC_HD_POL_MASK); + sync.ccdpg_vdpol = (params->vd_pol & CCDC_VD_POL_MASK); + vpss_set_sync_pol(sync); + vpss_set_pg_frame_size(frame_size); + } + + return 0; +} + +static int ccdc_configure(int mode) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) + return ccdc_config_raw(mode); + else + ccdc_config_ycbcr(mode); + + return 0; +} + +static int ccdc_close(struct device *device) +{ + /* copy defaults to module params */ + memcpy(&ccdc_cfg.bayer.config_params, &ccdc_config_defaults, + sizeof(struct ccdc_config_params_raw)); + + return 0; +} + +static struct ccdc_hw_device ccdc_hw_dev = { + .name = "dm365_isif", + .owner = THIS_MODULE, + .hw_ops = { + .open = ccdc_open, + .close = ccdc_close, + .enable = ccdc_enable, + .enable_out_to_sdram = ccdc_enable_output_to_sdram, + .set_hw_if_params = ccdc_set_hw_if_params, + .set_params = ccdc_set_params, + .get_params = ccdc_get_params, + .configure = ccdc_configure, + .set_buftype = ccdc_set_buftype, + .get_buftype = ccdc_get_buftype, + .enum_pix = ccdc_enum_pix, + .set_pixel_format = ccdc_set_pixel_format, + .get_pixel_format = ccdc_get_pixel_format, + .set_frame_format = ccdc_set_frame_format, + .get_frame_format = ccdc_get_frame_format, + .set_image_window = ccdc_set_image_window, + .get_image_window = ccdc_get_image_window, + .get_line_length = ccdc_get_line_length, + .setfbaddr = ccdc_setfbaddr, + .getfid = ccdc_getfid, + }, +}; + +struct ccdc_hw_device *get_ccdc_dev(void) +{ + return &ccdc_hw_dev; +} + +int ccdc_init(struct platform_device *pdev) +{ + static resource_size_t res_len; + struct resource *res; + void *__iomem addr; + int status; + int i; + + i = 0; + /* Get the ISIF base address, linearization table0 and table1 addr. */ + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) { + status = -ENOENT; + goto fail_nobase_res; + } + res_len = res->end - res->start + 1; + res = request_mem_region(res->start, res_len, res->name); + if (!res) { + status = -EBUSY; + goto fail_nobase_res; + } + addr = ioremap_nocache(res->start, res_len); + if (!addr) { + status = -EBUSY; + goto fail_base_iomap; + } + switch (i) { + case 0: + /* ISIF base address */ + ccdc_cfg.base_addr = addr; + break; + case 1: + /* ISIF linear tbl0 address */ + ccdc_cfg.linear_tbl0_addr = addr; + break; + default: + /* ISIF linear tbl0 address */ + ccdc_cfg.linear_tbl1_addr = addr; + break; + } + i++; + } + + davinci_cfg_reg(DM365_VIN_CAM_WEN); + davinci_cfg_reg(DM365_VIN_CAM_VD); + davinci_cfg_reg(DM365_VIN_CAM_HD); + davinci_cfg_reg(DM365_VIN_YIN4_7_EN); + davinci_cfg_reg(DM365_VIN_YIN0_3_EN); + + return 0; +fail_base_iomap: + release_mem_region(res->start, res_len); + i--; +fail_nobase_res: + if (ccdc_cfg.base_addr) + iounmap(ccdc_cfg.base_addr); + if (ccdc_cfg.linear_tbl0_addr) + iounmap(ccdc_cfg.linear_tbl0_addr); + + while (i >= 0) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + release_mem_region(res->start, res_len); + i--; + } + + return status; +} + +void ccdc_remove(struct platform_device *pdev) +{ + struct resource *res; + int i; + + iounmap(ccdc_cfg.base_addr); + iounmap(ccdc_cfg.linear_tbl0_addr); + iounmap(ccdc_cfg.linear_tbl1_addr); + + i = 0; + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (res) + release_mem_region(res->start, + res->end - res->start + 1); + i++; + } +} diff --git a/drivers/media/video/davinci/dm365_ccdc.h b/drivers/media/video/davinci/dm365_ccdc.h new file mode 100644 index 0000000..bf8ea86 --- /dev/null +++ b/drivers/media/video/davinci/dm365_ccdc.h @@ -0,0 +1,91 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* ccdc header file for DM365 ISIF +*/ +#ifndef _DM365_CCDC_H +#define _DM365_CCDC_H + +#include +#include +#include "ccdc_types.h" + +struct ccdc_ycbcr_config { + /* v4l2 pixel format */ + unsigned long v4l2_pix_fmt; + /* ccdc pixel format */ + enum ccdc_pixfmt pix_fmt; + /* ccdc frame format */ + enum ccdc_frmfmt frm_fmt; + /* CCDC crop window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* ccdc pix order. Only used for ycbcr capture */ + enum ccdc_pixorder pix_order; + /* ccdc buffer type. Only used for ycbcr capture */ + enum ccdc_buftype buf_type; +}; + +struct ccdc_params_raw { + /* v4l2 pixel format */ + unsigned long v4l2_pix_fmt; + /* ccdc pixel format */ + enum ccdc_pixfmt pix_fmt; + /* ccdc frame format */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* buffer type. Applicable for interlaced mode */ + enum ccdc_buftype buf_type; + /* Gain values */ + struct ccdc_gain gain; + /* cfa pattern */ + enum ccdc_cfa_pattern cfa_pat; + /* Data MSB position */ + enum ccdc_data_msb data_msb; + /* Enable horizontal flip */ + unsigned char horz_flip_en; + /* Enable image invert vertically */ + unsigned char image_invert_en; + + /*all the userland defined stuff*/ + struct ccdc_config_params_raw config_params; +}; + +enum ccdc_data_pack { + CCDC_PACK_16BIT, + CCDC_PACK_12BIT, + CCDC_PACK_8BIT +}; + +struct ccdc_hw_device *get_ccdc_dev(void); + +#define CCDC_WIN_NTSC {0, 0, 720, 480} +#define CCDC_WIN_VGA {0, 0, 640, 480} +#define ISP5_CCDCMUX 0x20 + +#endif diff --git a/drivers/media/video/davinci/dm365_ccdc_regs.h b/drivers/media/video/davinci/dm365_ccdc_regs.h new file mode 100644 index 0000000..9e734d5 --- /dev/null +++ b/drivers/media/video/davinci/dm365_ccdc_regs.h @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _DM365_CCDC_REGS_H +#define _DM365_CCDC_REGS_H + +/* ISIF registers relative offsets */ +#define SYNCEN 0x00 +#define MODESET 0x04 +#define HDW 0x08 +#define VDW 0x0c +#define PPLN 0x10 +#define LPFR 0x14 +#define SPH 0x18 +#define LNH 0x1c +#define SLV0 0x20 +#define SLV1 0x24 +#define LNV 0x28 +#define CULH 0x2c +#define CULV 0x30 +#define HSIZE 0x34 +#define SDOFST 0x38 +#define CADU 0x3c +#define CADL 0x40 +#define LINCFG0 0x44 +#define LINCFG1 0x48 +#define CCOLP 0x4c +#define CRGAIN 0x50 +#define CGRGAIN 0x54 +#define CGBGAIN 0x58 +#define CBGAIN 0x5c +#define COFSTA 0x60 +#define FLSHCFG0 0x64 +#define FLSHCFG1 0x68 +#define FLSHCFG2 0x6c +#define VDINT0 0x70 +#define VDINT1 0x74 +#define VDINT2 0x78 +#define MISC 0x7c +#define CGAMMAWD 0x80 +#define REC656IF 0x84 +#define CCDCFG 0x88 +/***************************************************** +* Defect Correction registers +*****************************************************/ +#define DFCCTL 0x8c +#define VDFSATLV 0x90 +#define DFCMEMCTL 0x94 +#define DFCMEM0 0x98 +#define DFCMEM1 0x9c +#define DFCMEM2 0xa0 +#define DFCMEM3 0xa4 +#define DFCMEM4 0xa8 +/**************************************************** +* Black Clamp registers +****************************************************/ +#define CLAMPCFG 0xac +#define CLDCOFST 0xb0 +#define CLSV 0xb4 +#define CLHWIN0 0xb8 +#define CLHWIN1 0xbc +#define CLHWIN2 0xc0 +#define CLVRV 0xc4 +#define CLVWIN0 0xc8 +#define CLVWIN1 0xcc +#define CLVWIN2 0xd0 +#define CLVWIN3 0xd4 +/**************************************************** +* Lense Shading Correction +****************************************************/ +#define DATAHOFST 0xd8 +#define DATAVOFST 0xdc +#define LSCHVAL 0xe0 +#define LSCVVAL 0xe4 +#define TWODLSCCFG 0xe8 +#define TWODLSCOFST 0xec +#define TWODLSCINI 0xf0 +#define TWODLSCGRBU 0xf4 +#define TWODLSCGRBL 0xf8 +#define TWODLSCGROF 0xfc +#define TWODLSCORBU 0x100 +#define TWODLSCORBL 0x104 +#define TWODLSCOROF 0x108 +#define TWODLSCIRQEN 0x10c +#define TWODLSCIRQST 0x110 +/**************************************************** +* Data formatter +****************************************************/ +#define FMTCFG 0x114 +#define FMTPLEN 0x118 +#define FMTSPH 0x11c +#define FMTLNH 0x120 +#define FMTSLV 0x124 +#define FMTLNV 0x128 +#define FMTRLEN 0x12c +#define FMTHCNT 0x130 +#define FMTAPTR_BASE 0x134 +/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */ +#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4)) +#define FMTPGMVF0 0x174 +#define FMTPGMVF1 0x178 +#define FMTPGMAPU0 0x17c +#define FMTPGMAPU1 0x180 +#define FMTPGMAPS0 0x184 +#define FMTPGMAPS1 0x188 +#define FMTPGMAPS2 0x18c +#define FMTPGMAPS3 0x190 +#define FMTPGMAPS4 0x194 +#define FMTPGMAPS5 0x198 +#define FMTPGMAPS6 0x19c +#define FMTPGMAPS7 0x1a0 +/************************************************ +* Color Space Converter +************************************************/ +#define CSCCTL 0x1a4 +#define CSCM0 0x1a8 +#define CSCM1 0x1ac +#define CSCM2 0x1b0 +#define CSCM3 0x1b4 +#define CSCM4 0x1b8 +#define CSCM5 0x1bc +#define CSCM6 0x1c0 +#define CSCM7 0x1c4 +#define OBWIN0 0x1c8 +#define OBWIN1 0x1cc +#define OBWIN2 0x1d0 +#define OBWIN3 0x1d4 +#define OBVAL0 0x1d8 +#define OBVAL1 0x1dc +#define OBVAL2 0x1e0 +#define OBVAL3 0x1e4 +#define OBVAL4 0x1e8 +#define OBVAL5 0x1ec +#define OBVAL6 0x1f0 +#define OBVAL7 0x1f4 +#define CLKCTL 0x1f8 + +#define CCDC_LINEAR_LUT0_ADDR 0x1c7c000 +#define CCDC_LINEAR_LUT1_ADDR 0x1c7c400 + +/* Masks & Shifts below */ +#define START_PX_HOR_MASK 0x7fff +#define NUM_PX_HOR_MASK 0x7fff +#define START_VER_ONE_MASK 0x7fff +#define START_VER_TWO_MASK 0x7fff +#define NUM_LINES_VER 0x7fff + +/* gain - offset masks */ +#define GAIN_INTEGER_MASK 0x7 +#define GAIN_INTEGER_SHIFT 0x9 +#define GAIN_DECIMAL_MASK 0x1ff +#define OFFSET_MASK 0xfff +#define GAIN_SDRAM_EN_SHIFT 12 +#define GAIN_IPIPE_EN_SHIFT 13 +#define GAIN_H3A_EN_SHIFT 14 +#define OFST_SDRAM_EN_SHIFT 8 +#define OFST_IPIPE_EN_SHIFT 9 +#define OFST_H3A_EN_SHIFT 10 +#define GAIN_OFFSET_EN_MASK 0x7700 + +/* Culling */ +#define CULL_PAT_EVEN_LINE_SHIFT 8 + +/* CCDCFG register */ +#define CCDC_YCINSWP_RAW (0x00 << 4) +#define CCDC_YCINSWP_YCBCR (0x01 << 4) +#define CCDC_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6) +#define CCDC_CCDCFG_WENLOG_AND (0x00 << 8) +#define CCDC_CCDCFG_TRGSEL_WEN (0x00 << 9) +#define CCDC_CCDCFG_EXTRG_DISABLE (0x00 << 10) +#define CCDC_LATCH_ON_VSYNC_DISABLE (0x01 << 15) +#define CCDC_LATCH_ON_VSYNC_ENABLE (0x00 << 15) +#define CCDC_DATA_PACK_MASK 0x03 +#define CCDC_DATA_PACK16 0x0 +#define CCDC_DATA_PACK12 0x1 +#define CCDC_DATA_PACK8 0x2 +#define CCDC_PIX_ORDER_SHIFT 11 +#define CCDC_PIX_ORDER_MASK 0x01 +#define CCDC_BW656_ENABLE (0x01 << 5) + +/* MODESET registers */ +#define CCDC_VDHDOUT_INPUT (0x00 << 0) +#define CCDC_INPUT_MASK 0x03 +#define CCDC_INPUT_SHIFT 12 +#define CCDC_RAW_INPUT_MODE 0x00 +#define CCDC_FID_POL_MASK 0x01 +#define CCDC_FID_POL_SHIFT 4 +#define CCDC_HD_POL_MASK 0x01 +#define CCDC_HD_POL_SHIFT 3 +#define CCDC_VD_POL_MASK 0x01 +#define CCDC_VD_POL_SHIFT 2 +#define CCDC_DATAPOL_NORMAL 0x00 +#define CCDC_DATAPOL_MASK 0x01 +#define CCDC_DATAPOL_SHIFT 6 +#define CCDC_EXWEN_DISABLE 0x00 +#define CCDC_EXWEN_MASK 0x01 +#define CCDC_EXWEN_SHIFT 5 +#define CCDC_FRM_FMT_MASK 0x01 +#define CCDC_FRM_FMT_SHIFT 7 +#define CCDC_DATASFT_MASK 0x07 +#define CCDC_DATASFT_SHIFT 8 +#define CCDC_LPF_SHIFT 14 +#define CCDC_LPF_MASK 0x1 + +/* GAMMAWD registers */ +#define CCDC_ALAW_GAMA_WD_MASK 0xf +#define CCDC_ALAW_GAMA_WD_SHIFT 1 +#define CCDC_ALAW_ENABLE 0x01 +#define CCDC_GAMMAWD_CFA_MASK 0x01 +#define CCDC_GAMMAWD_CFA_SHIFT 5 + +/* HSIZE registers */ +#define CCDC_HSIZE_FLIP_MASK 0x01 +#define CCDC_HSIZE_FLIP_SHIFT 12 +#define CCDC_LINEOFST_MASK 0xfff + +/* MISC registers */ +#define CCDC_DPCM_EN_SHIFT 12 +#define CCDC_DPCM_EN_MASK 1 +#define CCDC_DPCM_PREDICTOR_SHIFT 13 +#define CCDC_DPCM_PREDICTOR_MASK 1 + +/* Black clamp related */ +#define CCDC_BC_DCOFFSET_MASK 0x1fff +#define CCDC_BC_MODE_COLOR_MASK 1 +#define CCDC_BC_MODE_COLOR_SHIFT 4 +#define CCDC_HORZ_BC_MODE_MASK 3 +#define CCDC_HORZ_BC_MODE_SHIFT 1 +#define CCDC_HORZ_BC_WIN_COUNT_MASK 0x1f +#define CCDC_HORZ_BC_WIN_SEL_SHIFT 5 +#define CCDC_HORZ_BC_PIX_LIMIT_SHIFT 6 +#define CCDC_HORZ_BC_WIN_H_SIZE_MASK 3 +#define CCDC_HORZ_BC_WIN_H_SIZE_SHIFT 8 +#define CCDC_HORZ_BC_WIN_V_SIZE_MASK 3 +#define CCDC_HORZ_BC_WIN_V_SIZE_SHIFT 12 +#define CCDC_HORZ_BC_WIN_START_H_MASK 0x1fff +#define CCDC_HORZ_BC_WIN_START_V_MASK 0x1fff +#define CCDC_VERT_BC_OB_H_SZ_MASK 7 +#define CCDC_VERT_BC_RST_VAL_SEL_MASK 3 +#define CCDC_VERT_BC_RST_VAL_SEL_SHIFT 4 +#define CCDC_VERT_BC_LINE_AVE_COEF_SHIFT 8 +#define CCDC_VERT_BC_OB_START_HORZ_MASK 0x1fff +#define CCDC_VERT_BC_OB_START_VERT_MASK 0x1fff +#define CCDC_VERT_BC_OB_VERT_SZ_MASK 0x1fff +#define CCDC_VERT_BC_RST_VAL_MASK 0xfff +#define CCDC_BC_VERT_START_SUB_V_MASK 0x1fff + +/* VDFC registers */ +#define CCDC_VDFC_EN_SHIFT 4 +#define CCDC_VDFC_CORR_MOD_MASK 3 +#define CCDC_VDFC_CORR_MOD_SHIFT 5 +#define CCDC_VDFC_CORR_WHOLE_LN_SHIFT 7 +#define CCDC_VDFC_LEVEL_SHFT_MASK 7 +#define CCDC_VDFC_LEVEL_SHFT_SHIFT 8 +#define CCDC_VDFC_SAT_LEVEL_MASK 0xfff +#define CCDC_VDFC_POS_MASK 0x1fff +#define CCDC_DFCMEMCTL_DFCMARST_SHIFT 2 + +/* CSC registers */ +#define CCDC_CSC_COEF_INTEG_MASK 7 +#define CCDC_CSC_COEF_DECIMAL_MASK 0x1f +#define CCDC_CSC_COEF_INTEG_SHIFT 5 +#define CCDC_CSCM_MSB_SHIFT 8 +#define CCDC_DF_CSC_SPH_MASK 0x1fff +#define CCDC_DF_CSC_LNH_MASK 0x1fff +#define CCDC_DF_CSC_SLV_MASK 0x1fff +#define CCDC_DF_CSC_LNV_MASK 0x1fff +#define CCDC_DF_NUMLINES 0x7fff +#define CCDC_DF_NUMPIX 0x1fff + +/* Offsets for LSC/DFC/Gain */ +#define CCDC_DATA_H_OFFSET_MASK 0x1fff +#define CCDC_DATA_V_OFFSET_MASK 0x1fff + +/* Linearization */ +#define CCDC_LIN_CORRSFT_MASK 7 +#define CCDC_LIN_CORRSFT_SHIFT 4 +#define CCDC_LIN_SCALE_FACT_INTEG_SHIFT 10 +#define CCDC_LIN_SCALE_FACT_DECIMAL_MASK 0x3ff +#define CCDC_LIN_ENTRY_MASK 0x3ff + +#define CCDC_DF_FMTRLEN_MASK 0x1fff +#define CCDC_DF_FMTHCNT_MASK 0x1fff + +/* Pattern registers */ +#define CCDC_PG_EN (1 << 3) +#define CCDC_SEL_PG_SRC (3 << 4) +#define CCDC_PG_VD_POL_SHIFT 0 +#define CCDC_PG_HD_POL_SHIFT 1 + +/*masks and shifts*/ +#define CCDC_SYNCEN_VDHDEN_MASK (1 << 0) +#define CCDC_SYNCEN_WEN_MASK (1 << 1) +#define CCDC_SYNCEN_WEN_SHIFT 1 + +#endif diff --git a/include/linux/dm365_ccdc.h b/include/linux/dm365_ccdc.h new file mode 100644 index 0000000..99765f6 --- /dev/null +++ b/include/linux/dm365_ccdc.h @@ -0,0 +1,611 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _DM365_CCDC_INCLUDE_H +#define _DM365_CCDC_INCLUDE_H + +#include "types.h" + +#define VPFE_CMD_S_CCDC_RAW_PARAMS _IOW('V', 1, \ + struct ccdc_config_params_raw) +#define VPFE_CMD_G_CCDC_RAW_PARAMS _IOR('V', 2, \ + struct ccdc_config_params_raw) +/** + * ccdc float type S8Q8/U8Q8 + */ +struct ccdc_float_8 { + /* 8 bit integer part */ + __u8 integer; + /* 8 bit decimal part */ + __u8 decimal; +}; + +/** + * brief ccdc float type U16Q16/S16Q16 + */ +struct ccdc_float_16 { + /* 16 bit integer part */ + __u16 integer; + /* 16 bit decimal part */ + __u16 decimal; +}; + +/************************************************************************ + * Vertical Defect Correction parameters + ***********************************************************************/ + +/** + * vertical defect correction methods + */ +enum ccdc_vdfc_corr_mode { + /* Defect level subtraction. Just fed through if saturating */ + CCDC_VDFC_NORMAL, + /** + * Defect level subtraction. Horizontal interpolation ((i-2)+(i+2))/2 + * if data saturating + */ + CCDC_VDFC_HORZ_INTERPOL_IF_SAT, + /* Horizontal interpolation (((i-2)+(i+2))/2) */ + CCDC_VDFC_HORZ_INTERPOL +}; + +/** + * Max Size of the Vertical Defect Correction table + */ +#define CCDC_VDFC_TABLE_SIZE 8 + +/** + * Values used for shifting up the vdfc defect level + */ +enum ccdc_vdfc_shift { + /* No Shift */ + CCDC_VDFC_NO_SHIFT, + /* Shift by 1 bit */ + CCDC_VDFC_SHIFT_1, + /* Shift by 2 bit */ + CCDC_VDFC_SHIFT_2, + /* Shift by 3 bit */ + CCDC_VDFC_SHIFT_3, + /* Shift by 4 bit */ + CCDC_VDFC_SHIFT_4 +}; + +/** + * Defect Correction (DFC) table entry + */ +struct ccdc_vdfc_entry { + /* vertical position of defect */ + unsigned short pos_vert; + /* horizontal position of defect */ + unsigned short pos_horz; + /** + * Defect level of Vertical line defect position. This is subtracted + * from the data at the defect position + */ + unsigned char level_at_pos; + /** + * Defect level of the pixels upper than the vertical line defect. + * This is subtracted from the data + */ + unsigned char level_up_pixels; + /** + * Defect level of the pixels lower than the vertical line defect. + * This is subtracted from the data + */ + unsigned char level_low_pixels; +}; + +/** + * Structure for Defect Correction (DFC) parameter + */ +struct ccdc_dfc { + /* enable vertical defect correction */ + unsigned char en; + /* Correction methods */ + enum ccdc_vdfc_corr_mode corr_mode; + /** + * 0 - whole line corrected, 1 - not + * pixels upper than the defect + */ + unsigned char corr_whole_line; + /** + * defect level shift value. level_at_pos, level_upper_pos, + * and level_lower_pos can be shifted up by this value + */ + enum ccdc_vdfc_shift def_level_shift; + /* defect saturation level */ + unsigned short def_sat_level; + /* number of vertical defects. Max is CCDC_VDFC_TABLE_SIZE */ + short num_vdefects; + /* VDFC table ptr */ + struct ccdc_vdfc_entry table[CCDC_VDFC_TABLE_SIZE]; +}; + +/************************************************************************ +* Digital/Black clamp or DC Subtract parameters +************************************************************************/ +/** + * Horizontal Black Clamp modes + */ +enum ccdc_horz_bc_mode { + /** + * Horizontal clamp disabled. Only vertical clamp + * value is subtracted + */ + CCDC_HORZ_BC_DISABLE, + /** + * Horizontal clamp value is calculated and subtracted + * from image data along with vertical clamp value + */ + CCDC_HORZ_BC_CLAMP_CALC_ENABLED, + /** + * Horizontal clamp value calculated from previous image + * is subtracted from image data along with vertical clamp + * value. How the horizontal clamp value for the first image + * is calculated in this case ??? + */ + CCDC_HORZ_BC_CLAMP_NOT_UPDATED +}; + +/** + * Base window selection for Horizontal Black Clamp calculations + */ +enum ccdc_horz_bc_base_win_sel { + /* Select Most left window for bc calculation */ + CCDC_SEL_MOST_LEFT_WIN, + + /* Select Most right window for bc calculation */ + CCDC_SEL_MOST_RIGHT_WIN, +}; + +/* Size of window in horizontal direction for horizontal bc */ +enum ccdc_horz_bc_sz_h { + CCDC_HORZ_BC_SZ_H_2PIXELS, + CCDC_HORZ_BC_SZ_H_4PIXELS, + CCDC_HORZ_BC_SZ_H_8PIXELS, + CCDC_HORZ_BC_SZ_H_16PIXELS +}; + +/* Size of window in vertcal direction for vertical bc */ +enum ccdc_horz_bc_sz_v { + CCDC_HORZ_BC_SZ_H_32PIXELS, + CCDC_HORZ_BC_SZ_H_64PIXELS, + CCDC_HORZ_BC_SZ_H_128PIXELS, + CCDC_HORZ_BC_SZ_H_256PIXELS +}; + +/** + * Structure for Horizontal Black Clamp config params + */ +struct ccdc_horz_bclamp { + /* horizontal clamp mode */ + enum ccdc_horz_bc_mode mode; + /** + * pixel value limit enable. + * 0 - limit disabled + * 1 - pixel value limited to 1023 + */ + unsigned char clamp_pix_limit; + /** + * Select most left or right window for clamp val + * calculation + */ + enum ccdc_horz_bc_base_win_sel base_win_sel_calc; + /* Window count per color for calculation. range 1-32 */ + unsigned char win_count_calc; + /* Window start position - horizontal for calculation. 0 - 8191 */ + unsigned short win_start_h_calc; + /* Window start position - vertical for calculation 0 - 8191 */ + unsigned short win_start_v_calc; + /* Width of the sample window in pixels for calculation */ + enum ccdc_horz_bc_sz_h win_h_sz_calc; + /* Height of the sample window in pixels for calculation */ + enum ccdc_horz_bc_sz_v win_v_sz_calc; +}; + +/** + * Black Clamp vertical reset values + */ +enum ccdc_vert_bc_reset_val_sel { + /* Reset value used is the clamp value calculated */ + CCDC_VERT_BC_USE_HORZ_CLAMP_VAL, + /* Reset value used is reset_clamp_val configured */ + CCDC_VERT_BC_USE_CONFIG_CLAMP_VAL, + /* No update, previous image value is used */ + CCDC_VERT_BC_NO_UPDATE +}; + +enum ccdc_vert_bc_sz_h { + CCDC_VERT_BC_SZ_H_2PIXELS, + CCDC_VERT_BC_SZ_H_4PIXELS, + CCDC_VERT_BC_SZ_H_8PIXELS, + CCDC_VERT_BC_SZ_H_16PIXELS, + CCDC_VERT_BC_SZ_H_32PIXELS, + CCDC_VERT_BC_SZ_H_64PIXELS +}; + +/** + * Structure for Vetical Black Clamp configuration params + */ +struct ccdc_vert_bclamp { + /* Reset value selection for vertical clamp calculation */ + enum ccdc_vert_bc_reset_val_sel reset_val_sel; + /* U12 value if reset_sel = CCDC_BC_VERT_USE_CONFIG_CLAMP_VAL */ + unsigned short reset_clamp_val; + /** + * U8Q8. Line average coefficient used in vertical clamp + * calculation + */ + unsigned char line_ave_coef; + /* Width in pixels of the optical black region used for calculation. */ + enum ccdc_vert_bc_sz_h ob_h_sz_calc; + /* Height of the optical black region for calculation */ + unsigned short ob_v_sz_calc; + /* Optical black region start position - horizontal. 0 - 8191 */ + unsigned short ob_start_h; + /* Optical black region start position - vertical 0 - 8191 */ + unsigned short ob_start_v; +}; + +/** + * Structure for Black Clamp configuration params + */ +struct ccdc_black_clamp { + /** + * this offset value is added irrespective of the clamp + * enable status. S13 + */ + unsigned short dc_offset; + /** + * Enable black/digital clamp value to be subtracted + * from the image data + */ + unsigned char en; + /** + * black clamp mode. same/separate clamp for 4 colors + * 0 - disable - same clamp value for all colors + * 1 - clamp value calculated separately for all colors + */ + unsigned char bc_mode_color; + /* Vrtical start position for bc subtraction */ + unsigned short vert_start_sub; + /* Black clamp for horizontal direction */ + struct ccdc_horz_bclamp horz; + /* Black clamp for vertical direction */ + struct ccdc_vert_bclamp vert; +}; + +/************************************************************************* +** Color Space Convertion (CSC) +*************************************************************************/ +/** + * Number of Coefficient values used for CSC + */ +#define CCDC_CSC_NUM_COEFF 16 + +/************************************************************************* +** Color Space Conversion parameters +*************************************************************************/ +/** + * Structure used for CSC config params + */ +struct ccdc_color_space_conv { + /* Enable color space conversion */ + unsigned char en; + /** + * csc coeffient table. S8Q5, M00 at index 0, M01 at index 1, and + * so forth + */ + struct ccdc_float_8 coeff[CCDC_CSC_NUM_COEFF]; +}; + +enum ccdc_data_size { + /* 8 bits */ + CCDC_8_BITS, + /* 9 bits */ + CCDC_9_BITS, + /* 10 bits */ + CCDC_10_BITS, + /* 11 bits */ + CCDC_11_BITS, + /* 12 bits */ + CCDC_12_BITS, + /* 13 bits */ + CCDC_13_BITS, + /* 14 bits */ + CCDC_14_BITS, + /* 15 bits */ + CCDC_15_BITS, + /* 16 bits */ + CCDC_16_BITS +}; + +enum ccdc_datasft { + /* No Shift */ + CCDC_NO_SHIFT, + /* 1 bit Shift */ + CCDC_1BIT_SHIFT, + /* 2 bit Shift */ + CCDC_2BIT_SHIFT, + /* 3 bit Shift */ + CCDC_3BIT_SHIFT, + /* 4 bit Shift */ + CCDC_4BIT_SHIFT, + /* 5 bit Shift */ + CCDC_5BIT_SHIFT, + /* 6 bit Shift */ + CCDC_6BIT_SHIFT +}; + +enum ccdc_data_msb { + /* MSB b15 */ + CCDC_BIT_MSB_15, + /* MSB b14 */ + CCDC_BIT_MSB_14, + /* MSB b13 */ + CCDC_BIT_MSB_13, + /* MSB b12 */ + CCDC_BIT_MSB_12, + /* MSB b11 */ + CCDC_BIT_MSB_11, + /* MSB b10 */ + CCDC_BIT_MSB_10, + /* MSB b9 */ + CCDC_BIT_MSB_9, + /* MSB b8 */ + CCDC_BIT_MSB_8, + /* MSB b7 */ + CCDC_BIT_MSB_7 +}; + +/************************************************************************* +** Gain parameters +*************************************************************************/ +/** + * Structure for Gain parameters + */ +struct ccdc_gain { + /* Gain for Red or ye */ + struct ccdc_float_16 r_ye; + /* Gain for Gr or cy */ + struct ccdc_float_16 gr_cy; + /* Gain for Gb or g */ + struct ccdc_float_16 gb_g; + /* Gain for Blue or mg */ + struct ccdc_float_16 b_mg; +}; + +/** + * Predicator types for DPCM compression + */ +enum ccdc_dpcm_predictor { + /* Choose Predictor1 for DPCM compression */ + CCDC_DPCM_PRED1, + /* Choose Predictor2 for DPCM compression */ + CCDC_DPCM_PRED2 +}; + +#define CCDC_LINEAR_TAB_SIZE 192 +/************************************************************************* +** Linearization parameters +*************************************************************************/ +/** + * Structure for Sensor data linearization + */ +struct ccdc_linearize { + /* Enable or Disable linearization of data */ + unsigned char en; + /* Shift value applied */ + enum ccdc_datasft corr_shft; + /* scale factor applied U11Q10 */ + struct ccdc_float_16 scale_fact; + /* Size of the linear table */ + unsigned short table[CCDC_LINEAR_TAB_SIZE]; +}; + +enum ccdc_cfa_pattern { + CCDC_CFA_PAT_MOSAIC, + CCDC_CFA_PAT_STRIPE +}; + +enum ccdc_colpats { + CCDC_RED, + CCDC_GREEN_RED, + CCDC_GREEN_BLUE, + CCDC_BLUE +}; + +struct ccdc_col_pat { + enum ccdc_colpats olop; + enum ccdc_colpats olep; + enum ccdc_colpats elop; + enum ccdc_colpats elep; +}; + +/************************************************************************* +** CCDC Raw configuration parameters +*************************************************************************/ +enum ccdc_fmt_mode { + CCDC_SPLIT, + CCDC_COMBINE +}; + +enum ccdc_lnum { + CCDC_1LINE, + CCDC_2LINES, + CCDC_3LINES, + CCDC_4LINES +}; + +enum ccdc_line { + CCDC_1STLINE, + CCDC_2NDLINE, + CCDC_3RDLINE, + CCDC_4THLINE +}; + +struct ccdc_fmtplen { + /** + * number of program entries for SET0, range 1 - 16 + * when fmtmode is CCDC_SPLIT, 1 - 8 when fmtmode is + * CCDC_COMBINE + */ + unsigned short plen0; + /** + * number of program entries for SET1, range 1 - 16 + * when fmtmode is CCDC_SPLIT, 1 - 8 when fmtmode is + * CCDC_COMBINE + */ + unsigned short plen1; + /** + * number of program entries for SET2, range 1 - 16 + * when fmtmode is CCDC_SPLIT, 1 - 8 when fmtmode is + * CCDC_COMBINE + */ + unsigned short plen2; + /** + * number of program entries for SET3, range 1 - 16 + * when fmtmode is CCDC_SPLIT, 1 - 8 when fmtmode is + * CCDC_COMBINE + */ + unsigned short plen3; +}; + +struct ccdc_fmt_cfg { + /* Split or combine or line alternate */ + enum ccdc_fmt_mode fmtmode; + /* enable or disable line alternating mode */ + unsigned char ln_alter_en; + /* Split/combine line number */ + enum ccdc_lnum lnum; + /* Address increment Range 1 - 16 */ + unsigned int addrinc; +}; + +struct ccdc_fmt_addr_ptr { + /* Initial address */ + unsigned int init_addr; + /* output line number */ + enum ccdc_line out_line; +}; + +struct ccdc_fmtpgm_ap { + /* program address pointer */ + unsigned char pgm_aptr; + /* program address increment or decrement */ + unsigned char pgmupdt; +}; + +struct ccdc_data_formatter { + /* Enable/Disable data formatter */ + unsigned char en; + /* data formatter configuration */ + struct ccdc_fmt_cfg cfg; + /* Formatter program entries length */ + struct ccdc_fmtplen plen; + /* first pixel in a line fed to formatter */ + unsigned short fmtrlen; + /* HD interval for output line. Only valid when split line */ + unsigned short fmthcnt; + /* formatter address pointers */ + struct ccdc_fmt_addr_ptr fmtaddr_ptr[16]; + /* program enable/disable */ + unsigned char pgm_en[32]; + /* program address pointers */ + struct ccdc_fmtpgm_ap fmtpgm_ap[32]; +}; + +struct ccdc_df_csc { + /* Color Space Conversion confguration, 0 - csc, 1 - df */ + unsigned int df_or_csc; + /* csc configuration valid if df_or_csc is 0 */ + struct ccdc_color_space_conv csc; + /* data formatter configuration valid if df_or_csc is 1 */ + struct ccdc_data_formatter df; + /* start pixel in a line at the input */ + unsigned int start_pix; + /* number of pixels in input line */ + unsigned int num_pixels; + /* start line at the input */ + unsigned int start_line; + /* number of lines at the input */ + unsigned int num_lines; +}; + +struct ccdc_gain_offsets_adj { + /* Gain adjustment per color */ + struct ccdc_gain gain; + /* Offset adjustment */ + unsigned short offset; + /* Enable or Disable Gain adjustment for SDRAM data */ + unsigned char gain_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + unsigned char gain_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + unsigned char gain_h3a_en; + /* Enable or Disable Gain adjustment for SDRAM data */ + unsigned char offset_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + unsigned char offset_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + unsigned char offset_h3a_en; +}; + +struct ccdc_cul { + /* Horizontal Cull pattern for odd lines */ + unsigned char hcpat_odd; + /* Horizontal Cull pattern for even lines */ + unsigned char hcpat_even; + /* Vertical Cull pattern */ + unsigned char vcpat; + /* Enable or disable lpf. Apply when cull is enabled */ + unsigned char en_lpf; +}; + +/* all the stuff in this struct will be provided by userland */ +struct ccdc_config_params_raw { + /* Linearization parameters for image sensor data input */ + struct ccdc_linearize linearize; + /* Data formatter or CSC */ + struct ccdc_df_csc df_csc; + /* Defect Pixel Correction (DFC) confguration */ + struct ccdc_dfc dfc; + /* Black/Digital Clamp configuration */ + struct ccdc_black_clamp bclamp; + /* Gain, offset adjustments */ + struct ccdc_gain_offsets_adj gain_offset; + /* Culling */ + struct ccdc_cul culling; + /* Predictor for DPCM compression */ + enum ccdc_dpcm_predictor pred; + /* horizontal offset for Gain/LSC/DFC */ + unsigned short horz_offset; + /* vertical offset for Gain/LSC/DFC */ + unsigned short vert_offset; + /* color pattern for field 0 */ + struct ccdc_col_pat col_pat_field0; + /* color pattern for field 1 */ + struct ccdc_col_pat col_pat_field1; + /* data size from 8 to 16 bits */ + enum ccdc_data_size data_size; + /* Data shift applied before storing to SDRAM */ + enum ccdc_datasft data_shift; + /* enable input test pattern generation */ + unsigned char test_pat_gen; +}; + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Sat Nov 12 07:19:01 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 12 Nov 2011 18:49:01 +0530 Subject: [RFC PATCH v4 7/8] davinci: vpfe: v4l2 capture driver with media interface In-Reply-To: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> References: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321103942-2778-8-git-send-email-manjunath.hadli@ti.com> Add the vpfe capture driver which implements media controller interface. The driver suports all the setup functionality for all all units nnamely- ccdc, previewer, resizer, h3a, aew. The driver supports both dm365 and Dm355. The driver does isr registration, v4l2 device registration, media registration and platform driver registrations. It calls the appropriate subdevs from here to cerate the appropriate subdevices and media entities. Signed-off-by: Manjunath Hadli Signed-off-by: Nagabhushana Netagunte --- drivers/media/video/davinci/vpfe_capture.c | 796 ++++++++++++++++++++++++++++ drivers/media/video/davinci/vpfe_capture.h | 99 ++++ include/media/davinci/vpfe.h | 93 ++++ 3 files changed, 988 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpfe_capture.c create mode 100644 drivers/media/video/davinci/vpfe_capture.h create mode 100644 include/media/davinci/vpfe.h diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c new file mode 100644 index 0000000..2d3ce73 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -0,0 +1,796 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors:- + * Manjunath Hadli + * Nagabhushana Netagunte + * + * Driver name : VPFE Capture driver + * VPFE Capture driver allows applications to capture and stream video + * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as + * TVP5146 or Raw Bayer RGB image data from an image sensor + * such as Microns' MT9T001, MT9T031 etc. + * + * These SoCs have, in common, a Video Processing Subsystem (VPSS) that + * consists of a Video Processing Front End (VPFE) for capturing + * video/raw image data and Video Processing Back End (VPBE) for displaying + * YUV data through an in-built analog encoder or Digital LCD port. This + * driver is for capture through VPFE. A typical EVM using these SoCs have + * following high level configuration. + * + * decoder(TVP5146/ YUV/ + * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF) + * data input | | + * V | + * SDRAM | + * V + * Image Processor + * | + * V + * SDRAM + * The data flow happens from a decoder connected to the VPFE over a + * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface + * and to the input of VPFE through an optional MUX (if more inputs are + * to be interfaced on the EVM). The input data is first passed through + * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC + * does very little or no processing on YUV data and does pre-process Raw + * Bayer RGB data through modules such as Defect Pixel Correction (DFC) + * Color Space Conversion (CSC), data gain/offset etc. After this, data + * can be written to SDRAM or can be connected to the image processing + * block such as IPIPE (on DM355/DM365 only). + * + * Features supported + * - MMAP IO + * - USERPTR IO + * - Capture using TVP5146 over BT.656 + * - Support for interfacing decoders using sub device model + * - Work with DM365 or DM355 or DM6446 CCDC to do Raw Bayer + * RGB/YUV data capture to SDRAM. + * - Chaining of Image Processor + * - SINGLE-SHOT mode + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vpfe_capture.h" + +static int debug; +static int interface; +static u32 cont_bufoffset; +static u32 cont_bufsize; +static u32 en_serializer; + +module_param(interface, bool, S_IRUGO); +module_param(debug, bool, 0644); +module_param(cont_bufoffset, uint, S_IRUGO); +module_param(cont_bufsize, uint, S_IRUGO); +module_param(en_serializer, uint, S_IRUGO); + +/** + * VPFE capture can be used for capturing video such as from TVP5146 or TVP7002 + * and for capture raw bayer data from camera sensors such as mt9p031. At this + * point there is problem in co-existence of mt9p031 and tvp5146 due to i2c + * address collision. So set the variable below from bootargs to do either video + * capture or camera capture. + * interface = 0 - video capture (from TVP514x or such), + * interface = 1 - Camera capture (from mt9p031 or such) + * Re-visit this when we fix the co-existence issue + */ +MODULE_PARM_DESC(interface, "interface 0-1 (default:0)"); +MODULE_PARM_DESC(debug, "Debug level 0-1"); +MODULE_PARM_DESC(cont_bufoffset, "Capture buffer offset (default 0)"); +MODULE_PARM_DESC(cont_bufsize, "Capture buffer size (default 0)"); +MODULE_PARM_DESC(en_serializer, "enable IPIPE serializer (default:0)"); + +MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); + +/* map mbus_fmt to pixelformat */ +void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix) +{ + switch (mbus->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->bytesperline = pix->width * 2; + break; + case V4L2_MBUS_FMT_YUYV8_2X8: + pix->pixelformat = V4L2_PIX_FMT_YUYV; + pix->bytesperline = pix->width * 2; + break; + case V4L2_MBUS_FMT_YUYV10_1X20: + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->bytesperline = pix->width * 2; + break; + case V4L2_MBUS_FMT_SBGGR10_1X10: + pix->pixelformat = V4L2_PIX_FMT_SBGGR16; + pix->bytesperline = pix->width * 2; + break; + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + pix->pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8; + pix->bytesperline = pix->width; + break; + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + pix->pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8; + pix->bytesperline = pix->width; + break; + case V4L2_MBUS_FMT_NV12_1X20: + pix->pixelformat = V4L2_PIX_FMT_NV12; + pix->bytesperline = pix->width; + break; + case V4L2_MBUS_FMT_Y8_1X8: + pix->pixelformat = V4L2_PIX_FMT_GREY; + pix->bytesperline = pix->width; + break; + case V4L2_MBUS_FMT_UV8_1X8: + pix->pixelformat = V4L2_PIX_FMT_UV8; + pix->bytesperline = pix->width; + break; + default: + printk(KERN_ERR "invalid mbus code\n"); + } + + /* pitch should be 32 bytes aligned */ + pix->bytesperline = ALIGN(pix->bytesperline, 32); + + if (pix->pixelformat == V4L2_PIX_FMT_NV12) + pix->sizeimage = pix->bytesperline * pix->height + + ((pix->bytesperline * pix->height) >> 1); + else + pix->sizeimage = pix->bytesperline * pix->height; +} + +/* ISR for VINT0*/ +static irqreturn_t vpfe_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_isr\n"); + ccdc_buffer_isr(&vpfe_dev->vpfe_ccdc); + prv_buffer_isr(&vpfe_dev->vpfe_previewer); + rsz_buffer_isr(&vpfe_dev->vpfe_resizer); + + return IRQ_HANDLED; +} + +/* vpfe_vdint1_isr - isr handler for VINT1 interrupt */ +static irqreturn_t vpfe_vdint1_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_vdint1_isr\n"); + ccdc_vidint1_isr(&vpfe_dev->vpfe_ccdc); + + return IRQ_HANDLED; +} + +/* ISR for ipipe dma completion */ +static irqreturn_t vpfe_imp_dma_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_imp_dma_isr\n"); + prv_dma_isr(&vpfe_dev->vpfe_previewer); + rsz_dma_isr(&vpfe_dev->vpfe_resizer); + + return IRQ_HANDLED; +} + +/* set user setting of serializer in ipipe */ +static void vpfe_initialize(struct vpfe_device *vpfe_dev) +{ + /* inform user choice on serializer to ipipe */ + enable_serializer(vpfe_dev->ipipe, en_serializer); +} + +/** + * vpfe_disable_clock() - Disable clocks for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Disables clocks defined in vpfe configuration. The function + * assumes that at least one clock is to be defined which is + * true as of now. + */ +static void vpfe_disable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int i; + + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + clk_disable(vpfe_dev->clks[i]); + clk_put(vpfe_dev->clks[i]); + } + + kzfree(vpfe_dev->clks); + v4l2_info(vpfe_dev->pdev->driver, "vpfe capture clocks disabled\n"); +} + +/** + * vpfe_enable_clock() - Enable clocks for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Enables clocks defined in vpfe configuration. The function + * assumes that at least one clock is to be defined which is + * true as of now. + */ +static int vpfe_enable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int ret = -EFAULT; + int i; + + if (!vpfe_cfg->num_clocks) + return 0; + + vpfe_dev->clks = kzalloc(vpfe_cfg->num_clocks * + sizeof(struct clock *), GFP_KERNEL); + + if (vpfe_dev->clks == NULL) { + v4l2_err(vpfe_dev->pdev->driver, "Memory allocation failed\n"); + return -ENOMEM; + } + + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + if (vpfe_cfg->clocks[i] == NULL) { + v4l2_err(vpfe_dev->pdev->driver, + "clock %s is not defined in vpfe config\n", + vpfe_cfg->clocks[i]); + goto out; + } + + vpfe_dev->clks[i] = clk_get(vpfe_dev->pdev, + vpfe_cfg->clocks[i]); + if (vpfe_dev->clks[i] == NULL) { + v4l2_err(vpfe_dev->pdev->driver, + "Failed to get clock %s\n", + vpfe_cfg->clocks[i]); + goto out; + } + + if (clk_enable(vpfe_dev->clks[i])) { + v4l2_err(vpfe_dev->pdev->driver, + "vpfe clock %s not enabled\n", + vpfe_cfg->clocks[i]); + goto out; + } + + v4l2_info(vpfe_dev->pdev->driver, "vpss clock %s enabled", + vpfe_cfg->clocks[i]); + } + + return 0; +out: + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + if (vpfe_dev->clks[i]) + clk_put(vpfe_dev->clks[i]); + } + + v4l2_err(vpfe_dev->pdev->driver, "failed to enable clocks\n"); + + kzfree(vpfe_dev->clks); + return ret; +} + +/** + * vpfe_detach_irq() - Detach IRQs for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Detach all IRQs defined in vpfe configuration. + */ +static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) +{ + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); + free_irq(vpfe_dev->imp_dma_irq, vpfe_dev); +} + +/** + * vpfe_attach_irq() - Attach IRQs for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Attach all IRQs defined in vpfe configuration. + */ +static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) +{ + int ret = 0; + + ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED, + "vpfe_capture0", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting VINT0 interrupt\n"); + return ret; + } + + ret = request_irq(vpfe_dev->ccdc_irq1, vpfe_vdint1_isr, IRQF_DISABLED, + "vpfe_capture1", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting VINT1 interrupt\n"); + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + return ret; + } + + ret = request_irq(vpfe_dev->imp_dma_irq, vpfe_imp_dma_isr, + IRQF_DISABLED, "Imp_Sdram_Irq", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Error: requesting IMP" + " IRQ interrupt\n"); + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + return ret; + } + + return 0; +} + +/** + * register_i2c_devices() - register all i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * + * register all i2c v4l2 subdevs + */ +static int register_i2c_devices(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ext_subdev_info *sdinfo; + struct vpfe_config *vpfe_cfg; + struct i2c_adapter *i2c_adap; + unsigned int num_subdevs; + int ret; + int i; + int k; + + vpfe_cfg = vpfe_dev->cfg; + + i2c_adap = i2c_get_adapter(1); + num_subdevs = vpfe_cfg->num_subdevs; + + vpfe_dev->sd = kzalloc(sizeof(struct v4l2_subdev *) *num_subdevs, + GFP_KERNEL); + + if (vpfe_dev->sd == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, + "unable to allocate memory for subdevice pointers\n"); + return -ENOMEM; + } + + for (i = 0, k = 0; i < num_subdevs; i++) { + sdinfo = &vpfe_cfg->sub_devs[i]; + /** + * register subdevices based on interface setting. Currently + * tvp5146 and mt9p031 cannot co-exists due to i2c address + * conflicts. So only one of them is registered. Re-visit this + * once we have support for i2c switch handling in i2c driver + * framework + */ + + if (interface == sdinfo->is_camera) { + /* setup input path */ + if (vpfe_cfg->setup_input && + vpfe_cfg->setup_input(sdinfo->grp_id) < 0) { + ret = -EFAULT; + v4l2_info(&vpfe_dev->v4l2_dev, "could" + " not setup input for %s\n", + sdinfo->module_name); + goto probe_sd_out; + } + /* Load up the subdevice */ + vpfe_dev->sd[k] = + v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev, + i2c_adap, &sdinfo->board_info, + NULL, 1); + if (vpfe_dev->sd[k]) { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s registered\n", + sdinfo->module_name); + + vpfe_dev->sd[k]->grp_id = sdinfo->grp_id; + k++; + + sdinfo->registered = 1; + } + } else { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s is not registered\n", + sdinfo->module_name); + } + } + + vpfe_dev->num_ext_subdevs = k; + + return 0; + +probe_sd_out: + kzfree(vpfe_dev->sd); + + return ret; +} + +/** + * vpfe_register_entities() - register all v4l2 subdevs and media entities + * @vpfe_dev - ptr to vpfe capture device + * + * register all v4l2 subdevs, media entities, and creates links + * between entities + */ +static int vpfe_register_entities(struct vpfe_device *vpfe_dev) +{ + unsigned int flags = 0; + int ret; + int i; + + /* register i2c devices first */ + ret = register_i2c_devices(vpfe_dev); + if (ret) + return ret; + + /* register rest of the sub-devs */ + ret = vpfe_ccdc_register_entities(&vpfe_dev->vpfe_ccdc, + &vpfe_dev->v4l2_dev); + if (ret) + return ret; + + ret = vpfe_previewer_register_entities(&vpfe_dev->vpfe_previewer, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_ccdc_register; + + ret = vpfe_resizer_register_entities(&vpfe_dev->vpfe_resizer, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_previewer_register; + + ret = vpfe_aew_register_entities(&vpfe_dev->vpfe_aew, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_resizer_register; + + ret = vpfe_af_register_entities(&vpfe_dev->vpfe_af, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_aew_register; + + /* create links now, starting with external(i2c) entities */ + for (i = 0; i < vpfe_dev->num_ext_subdevs; i++) { + /* if entity has no pads (ex: amplifier), + cant establish link */ + if (vpfe_dev->sd[i]->entity.num_pads) { + ret = media_entity_create_link(&vpfe_dev->sd[i]->entity, + 0, &vpfe_dev->vpfe_ccdc.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + } + } + + ret = media_entity_create_link(&vpfe_dev->vpfe_ccdc.subdev.entity, + 1, &vpfe_dev->vpfe_aew.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ccdc.subdev.entity, + 1, &vpfe_dev->vpfe_af.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ccdc.subdev.entity, 1, + &vpfe_dev->vpfe_previewer.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_previewer.subdev.entity, + 1, &vpfe_dev->vpfe_resizer.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + return 0; + +out_aew_register: + vpfe_aew_unregister_entities(&vpfe_dev->vpfe_aew); +out_resizer_register: + vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer); +out_previewer_register: + vpfe_previewer_unregister_entities(&vpfe_dev->vpfe_previewer); +out_ccdc_register: + vpfe_ccdc_unregister_entities(&vpfe_dev->vpfe_ccdc); + return ret; +} + +/** + * vpfe_unregister_entities() - unregister all v4l2 subdevs and media entities + * @vpfe_dev - ptr to vpfe capture device + * + * unregister all v4l2 subdevs and media entities + */ +static void vpfe_unregister_entities(struct vpfe_device *vpfe_dev) +{ + vpfe_ccdc_unregister_entities(&vpfe_dev->vpfe_ccdc); + vpfe_previewer_unregister_entities(&vpfe_dev->vpfe_previewer); + vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer); + vpfe_aew_unregister_entities(&vpfe_dev->vpfe_aew); + vpfe_af_unregister_entities(&vpfe_dev->vpfe_af); +} + +/** + * vpfe_cleanup_modules() - cleanup all non-i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * @pdev - pointer to platform device + * + * cleanup all v4l2 subdevs + */ +static void vpfe_cleanup_modules(struct vpfe_device *vpfe_dev, + struct platform_device *pdev) +{ + vpfe_ccdc_cleanup(pdev); + vpfe_previewer_cleanup(pdev, vpfe_dev->ipipe); + vpfe_aew_cleanup(); + vpfe_af_cleanup(); +} + +/** + * vpfe_initialize_modules() - initialize all non-i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * @pdev - pointer to platform device + * + * intialize all v4l2 subdevs and media entities + */ +static int vpfe_initialize_modules(struct vpfe_device *vpfe_dev, + struct platform_device *pdev) +{ + int ret; + + ret = vpfe_ccdc_init(&vpfe_dev->vpfe_ccdc, pdev); + if (ret) + return ret; + + ret = vpfe_previewer_init(&vpfe_dev->vpfe_previewer, pdev); + if (ret) + goto out_ccdc_init; + + ret = vpfe_resizer_init(&vpfe_dev->vpfe_resizer, pdev); + if (ret) + goto out_previewer_init; + + ret = vpfe_aew_init(&vpfe_dev->vpfe_aew, pdev); + if (ret) + goto out_previewer_init; + + ret = vpfe_af_init(&vpfe_dev->vpfe_af, pdev); + if (ret) + goto out_aew_init; + + return 0; + +out_aew_init: + vpfe_aew_cleanup(); +out_previewer_init: + vpfe_previewer_cleanup(pdev, vpfe_dev->ipipe); +out_ccdc_init: + vpfe_ccdc_cleanup(pdev); + + return ret; +} + +/** + * vpfe_probe : vpfe probe function + * @pdev: platform device pointer + * + * This function creates device entries by register itself to the V4L2 driver + * and initializes fields of each device objects + */ +static __devinit int vpfe_probe(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev; + struct resource *res1; + unsigned long phys_end_kernel; + int ret = -ENOMEM; + int err; + size_t size; + + vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL); + if (!vpfe_dev) { + v4l2_err(pdev->dev.driver, + "Failed to allocate memory for vpfe_dev\n"); + return ret; + } + + if (pdev->dev.platform_data == NULL) { + v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + vpfe_dev->cfg = pdev->dev.platform_data; + + if (vpfe_dev->cfg->card_name == NULL || + vpfe_dev->cfg->sub_devs == NULL) { + v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + /* Get VINT0 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT0\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->ccdc_irq0 = res1->start; + + /* Get VINT1 irq resource */ + res1 = platform_get_resource(pdev, + IORESOURCE_IRQ, 1); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT1\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->ccdc_irq1 = res1->start; + + /* Get DMA irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 2); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for DMA\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->imp_dma_irq = res1->start; + + vpfe_dev->pdev = &pdev->dev; + + /* enable vpss clocks */ + ret = vpfe_enable_clock(vpfe_dev); + if (ret) + goto probe_free_dev_mem; + + if (vpfe_initialize_modules(vpfe_dev, pdev)) + goto probe_disable_clock; + + vpfe_initialize(vpfe_dev); + + vpfe_dev->media_dev.dev = vpfe_dev->pdev; + strcpy((char *)&vpfe_dev->media_dev.model, "davinci-media"); + ret = media_device_register(&vpfe_dev->media_dev); + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register media device.\n"); + goto probe_out_entities_cleanup; + } + + vpfe_dev->v4l2_dev.mdev = &vpfe_dev->media_dev; + + ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); + if (ret) { + v4l2_err(pdev->dev.driver, "Unable to register v4l2 device.\n"); + goto probe_out_media_unregister; + } + v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); + + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpfe_dev); + + /* register subdevs/entities */ + if (vpfe_register_entities(vpfe_dev)) + goto probe_out_v4l2_unregister; + + ret = vpfe_attach_irq(vpfe_dev); + if (ret) + goto probe_out_entities_unregister; + + if (cont_bufsize) { + /* attempt to determine the end of Linux kernel memory */ + phys_end_kernel = virt_to_phys((void *)PAGE_OFFSET) + + (num_physpages << PAGE_SHIFT); + size = cont_bufsize; + phys_end_kernel += cont_bufoffset; + err = dma_declare_coherent_memory(&pdev->dev, phys_end_kernel, + phys_end_kernel, size, + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE); + if (!err) { + dev_err(&pdev->dev, "Unable to declare MMAP memory.\n"); + ret = -ENOENT; + goto probe_detach_irq; + } + vpfe_dev->video_limit = size; + } + + return 0; + +probe_detach_irq: + vpfe_detach_irq(vpfe_dev); +probe_out_entities_unregister: + vpfe_unregister_entities(vpfe_dev); + kzfree(vpfe_dev->sd); +probe_out_v4l2_unregister: + v4l2_device_unregister(&vpfe_dev->v4l2_dev); +probe_out_media_unregister: + media_device_unregister(&vpfe_dev->media_dev); +probe_out_entities_cleanup: + vpfe_cleanup_modules(vpfe_dev, pdev); +probe_disable_clock: + vpfe_disable_clock(vpfe_dev); +probe_free_dev_mem: + kzfree(vpfe_dev); + + return ret; +} + +/* + * vpfe_remove : This function un-registers device from V4L2 driver + */ +static int vpfe_remove(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev); + + v4l2_info(pdev->dev.driver, "vpfe_remove\n"); + + kzfree(vpfe_dev->sd); + vpfe_detach_irq(vpfe_dev); + vpfe_unregister_entities(vpfe_dev); + vpfe_cleanup_modules(vpfe_dev, pdev); + v4l2_device_unregister(&vpfe_dev->v4l2_dev); + media_device_unregister(&vpfe_dev->media_dev); + vpfe_disable_clock(vpfe_dev); + kzfree(vpfe_dev); + + return 0; +} + +static struct platform_driver vpfe_driver = { + .driver = { + .name = CAPTURE_DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = vpfe_probe, + .remove = __devexit_p(vpfe_remove), +}; + +/** + * vpfe_init : This function registers device driver + */ +static __init int vpfe_init(void) +{ + /* Register driver to the kernel */ + return platform_driver_register(&vpfe_driver); +} + +/** + * vpfe_cleanup : This function un-registers device driver + */ +static void vpfe_cleanup(void) +{ + platform_driver_unregister(&vpfe_driver); +} + +module_init(vpfe_init); +module_exit(vpfe_cleanup); diff --git a/drivers/media/video/davinci/vpfe_capture.h b/drivers/media/video/davinci/vpfe_capture.h new file mode 100644 index 0000000..7219baf --- /dev/null +++ b/drivers/media/video/davinci/vpfe_capture.h @@ -0,0 +1,99 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _VPFE_CAPTURE_H +#define _VPFE_CAPTURE_H + +/* Header files */ +#include +#include +#include +#include +#include +#include +#include +#include "imp_hw_if.h" +#include "imp_common.h" + +#include "vpfe_video.h" +#include "vpfe_ccdc.h" +#include "vpfe_resizer.h" +#include "vpfe_previewer.h" +#include "vpfe_aew.h" +#include "vpfe_af.h" + +/* Macros */ +#define VPFE_MAJOR_RELEASE 0 +#define VPFE_MINOR_RELEASE 0 +#define VPFE_BUILD 1 +#define VPFE_CAPTURE_VERSION_CODE ((VPFE_MAJOR_RELEASE << 16) | \ + (VPFE_MINOR_RELEASE << 8) | \ + VPFE_BUILD) + +#define to_vpfe_device(ptr_module) \ + container_of(ptr_module, struct vpfe_device, vpfe_##ptr_module) +#define to_device(ptr_module) \ + (to_vpfe_device(ptr_module)->dev) + +struct vpfe_device { + /* external registered sub devices */ + struct v4l2_subdev **sd; + /* number of registered external subdevs */ + unsigned int num_ext_subdevs; + /* vpfe cfg */ + struct vpfe_config *cfg; + /* clock ptrs for vpfe capture */ + struct clk **clks; + /* V4l2 device */ + struct v4l2_device v4l2_dev; + /* parent device */ + struct device *pdev; + /* IRQ number for DMA transfer completion at the image processor */ + unsigned int imp_dma_irq; + /* CCDC IRQs used when CCDC/ISIF output to SDRAM */ + unsigned int ccdc_irq0; + unsigned int ccdc_irq1; + /* maximum video memory that is available*/ + unsigned int video_limit; + /* media device */ + struct media_device media_dev; + /* ccdc subdevice */ + struct vpfe_ccdc_device vpfe_ccdc; + /* resizer subdevice */ + struct vpfe_resizer_device vpfe_resizer; + /* previewer subdevice */ + struct vpfe_previewer_device vpfe_previewer; + /* aew subdevice */ + struct vpfe_aew_device vpfe_aew; + /* af subdevice */ + struct vpfe_af_device vpfe_af; + void *ipipe; +}; + +/* File handle structure */ +struct vpfe_fh { + struct vpfe_video_device *video; + /* Indicates whether this file handle is doing IO */ + u8 io_allowed; + /* Used to keep track priority of this instance */ + enum v4l2_priority prio; +}; + +void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix); + +#endif /* _DAVINCI_VPFE_H */ diff --git a/include/media/davinci/vpfe.h b/include/media/davinci/vpfe.h new file mode 100644 index 0000000..b55d318 --- /dev/null +++ b/include/media/davinci/vpfe.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VPFE_H +#define _VPFE_H + +#ifdef __KERNEL__ +#include +#include +#include + +#define CAPTURE_DRV_NAME "vpfe-capture" + +struct vpfe_route { + __u32 input; + __u32 output; +}; + +enum vpfe_subdev_id { + VPFE_SUBDEV_TVP5146 = 1, + VPFE_SUBDEV_MT9T031 = 2, + VPFE_SUBDEV_TVP7002 = 3, + VPFE_SUBDEV_MT9P031 = 4, +}; + +enum vpfe_pin_pol { + VPFE_PINPOL_POSITIVE, + VPFE_PINPOL_NEGATIVE +}; + +/* interface description */ +struct vpfe_hw_if_param { + enum v4l2_mbus_pixelcode if_type; + enum vpfe_pin_pol hdpol; + enum vpfe_pin_pol vdpol; +}; + +struct vpfe_ext_subdev_info { + /* v4l2 subdev */ + struct v4l2_subdev *subdev; + /* Sub device module name */ + char module_name[32]; + /* Sub device group id */ + int grp_id; + /* Number of inputs supported */ + int num_inputs; + /* inputs available at the sub device */ + struct v4l2_input *inputs; + /* Sub dev routing information for each input */ + struct vpfe_route *routes; + /* ccdc bus/interface configuration */ + struct vpfe_hw_if_param ccdc_if_params; + /* i2c subdevice board info */ + struct i2c_board_info board_info; + /* Is this a camera sub device ? */ + unsigned is_camera:1; + /* check if sub dev supports routing */ + unsigned can_route:1; + /* registered ? */ + unsigned registered:1; +}; + +struct vpfe_config { + /* Number of sub devices connected to vpfe */ + int num_subdevs; + /* information about each subdev */ + struct vpfe_ext_subdev_info *sub_devs; + /* evm card info */ + char *card_name; + /* setup function for the input path */ + int (*setup_input)(enum vpfe_subdev_id id); + /* number of clocks */ + int num_clocks; + /* clocks used for vpfe capture */ + char *clocks[]; +}; +#endif +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Sat Nov 12 07:19:00 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 12 Nov 2011 18:49:00 +0530 Subject: [RFC PATCH v4 6/8] davinci: vpfe: add v4l2 video driver support In-Reply-To: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> References: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321103942-2778-7-git-send-email-manjunath.hadli@ti.com> add a generic video driver functionality to be used by all the vpfe drivers for davinci SoCs. The functionality includes all the standard v4l2 interfaces including streaming. The video node interface can be used both as an input and output node for both continuous and single shot modes.Also supports dv_presets to include HD modes, wth support for both user pointer IO and mmap. Signed-off-by: Manjunath Hadli Signed-off-by: Nagabhushana Netagunte --- drivers/media/video/davinci/vpfe_video.c | 1744 ++++++++++++++++++++++++++++++ drivers/media/video/davinci/vpfe_video.h | 146 +++ 2 files changed, 1890 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpfe_video.c create mode 100644 drivers/media/video/davinci/vpfe_video.h diff --git a/drivers/media/video/davinci/vpfe_video.c b/drivers/media/video/davinci/vpfe_video.c new file mode 100644 index 0000000..8840d18 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_video.c @@ -0,0 +1,1744 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors:- + * Manjunath Hadli + * Nagabhushana Netagunte + */ + +#include +#include +#include +#include +#include +#include +#include +#include "vpfe_capture.h" +#include "ccdc_hw_device.h" + +/* minimum number of buffers needed in cont-mode */ +#define CONT_MIN_NUM_BUFFERS 3 + +static int debug; + +/* get v4l2 subdev pointer to external subdev which is active */ +static struct media_entity *vpfe_get_input_entity + (struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct media_pad *remote; + + remote = media_entity_remote_source(&vpfe_dev->vpfe_ccdc.pads[0]); + if (remote == NULL) { + printk(KERN_ERR "invalid media connection to ccdc\n"); + return NULL; + } + + return remote->entity; +} + +/* updates external subdev(sensor/decoder) which is active */ +static int vpfe_update_current_ext_subdev(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_config *vpfe_cfg; + struct v4l2_subdev *subdev; + struct media_pad *remote; + int i; + + remote = media_entity_remote_source(&vpfe_dev->vpfe_ccdc.pads[0]); + if (remote == NULL) { + printk(KERN_ERR "invalid media connection to ccdc\n"); + return -EINVAL; + } + + subdev = media_entity_to_v4l2_subdev(remote->entity); + + vpfe_cfg = vpfe_dev->pdev->platform_data; + + for (i = 0; i < vpfe_cfg->num_subdevs; i++) { + if (!strcmp(vpfe_cfg->sub_devs[i].module_name, subdev->name)) { + video->current_ext_subdev = &vpfe_cfg->sub_devs[i]; + break; + } + } + + /* if user not linked decoder/sensor to ccdc */ + if (i == vpfe_cfg->num_subdevs) { + printk(KERN_ERR "invalid media chain connection to ccdc\n"); + return -EINVAL; + } + + /* find the v4l2 subdev pointer */ + for (i = 0; i < vpfe_dev->num_ext_subdevs; i++) { + if (!strcmp(video->current_ext_subdev->module_name, + vpfe_dev->sd[i]->name)) + video->current_ext_subdev->subdev = vpfe_dev->sd[i]; + } + + return 0; +} + +/* get the subdev which is connected to the output video node */ +static struct v4l2_subdev * +vpfe_video_remote_subdev(struct vpfe_video_device *video, u32 *pad) +{ + struct media_pad *remote; + + remote = media_entity_remote_source(&video->pad); + + if (remote == NULL || remote->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + return NULL; + + if (pad) + *pad = remote->index; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +/* get the format set at ouput pad of the adjacent subdev */ +static int +__vpfe_video_get_format(struct vpfe_video_device *video, + struct v4l2_format *format) +{ + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + struct media_pad *remote; + u32 pad; + int ret; + + subdev = vpfe_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + remote = media_entity_remote_source(&video->pad); + fmt.pad = remote->index; + + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret == -ENOIOCTLCMD) + return -EINVAL; + + format->type = video->type; + /* convert mbus_format to v4l2_format */ + v4l2_fill_pix_format(&format->fmt.pix, &fmt.format); + mbus_to_pix(&fmt.format, &format->fmt.pix); + + return 0; +} + +/* make a note of pipeline details */ +static void vpfe_prepare_pipeline(struct vpfe_video_device *video) +{ + struct media_entity *entity = &video->video_dev.entity; + struct media_device *mdev = entity->parent; + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_video_device *far_end = NULL; + struct media_entity_graph graph; + + pipe->input_num = 0; + pipe->output_num = 0; + + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + pipe->inputs[pipe->input_num++] = video; + else + pipe->outputs[pipe->output_num++] = video; + + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + if (entity == &video->video_dev.entity) + continue; + + if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + continue; + + far_end = to_vpfe_video(media_entity_to_video_device(entity)); + + if (far_end->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + pipe->inputs[pipe->input_num++] = far_end; + else + pipe->outputs[pipe->output_num++] = far_end; + } + + mutex_unlock(&mdev->graph_mutex); +} + +/* update pipe state selected by user */ +static int vpfe_update_pipe_state(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int ret; + + vpfe_prepare_pipeline(video); + + /* Find out if there is any input video + if yes, it is single shot. + */ + if (pipe->input_num == 0) { + pipe->state = VPFE_PIPELINE_STREAM_CONTINUOUS; + ret = vpfe_update_current_ext_subdev(video); + if (ret) { + printk(KERN_ERR "invalid external subdev\n"); + return ret; + } + } else { + pipe->state = VPFE_PIPELINE_STREAM_SINGLESHOT; + } + + video->initialized = 1; + video->skip_frame_count = 1; + video->skip_frame_count_init = 1; + + return 0; +} + +/* checks wether pipeline is ready for enabling */ +int is_pipe_ready(struct vpfe_pipeline *pipe) +{ + int i; + + for (i = 0; i < pipe->input_num; i++) + if (!pipe->inputs[i]->started || + pipe->inputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED) + return 0; + + for (i = 0; i < pipe->output_num; i++) + if (!pipe->outputs[i]->started || + pipe->outputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED) + return 0; + + return 1; +} + +/** + * Validate a pipeline by checking both ends of all links for format + * discrepancies. + * + * Return 0 if all formats match, or -EPIPE if at least one link is found with + * different formats on its two ends. + */ +static int vpfe_video_validate_pipeline(struct vpfe_pipeline *pipe) +{ + struct v4l2_subdev_format fmt_source; + struct v4l2_subdev_format fmt_sink; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret; + + /* Should not matter if it is output[0] or 1 as + the general ideas is to traverse backwards and + the fact that the out video node always has the + format of the connected pad. + */ + subdev = vpfe_video_remote_subdev(pipe->outputs[0], NULL); + if (subdev == NULL) + return -EPIPE; + + while (1) { + /* Retrieve the sink format */ + pad = &subdev->entity.pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_INPUT)) + break; + + fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt_sink.pad = pad->index; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, + &fmt_sink); + + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + /* Retrieve the source format */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + pad->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + subdev = media_entity_to_v4l2_subdev(pad->entity); + + fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt_source.pad = pad->index; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + /* Check if the two ends match */ + if (fmt_source.format.code != fmt_sink.format.code || + fmt_source.format.width != fmt_sink.format.width || + fmt_source.format.height != fmt_sink.format.height) + return -EPIPE; + } + + return 0; +} + +/** + * vpfe_pipeline_enable - Enable streaming on a pipeline + * @vpfe_dev: vpfe device + * @pipe: vpfe pipeline + * + * Walk the entities chain starting at the pipeline output video node and start + * all modules in the chain in the given mode. + * + * Return 0 if successfull, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int vpfe_pipeline_enable(struct vpfe_pipeline *pipe) +{ + struct media_entity_graph graph; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_device *mdev; + int ret = 0; + + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + entity = vpfe_get_input_entity(pipe->outputs[0]); + else + entity = &pipe->inputs[0]->video_dev.entity; + + mdev = entity->parent; + + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + continue; + + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + } + mutex_unlock(&mdev->graph_mutex); + + return ret; +} + +/** + * vpfe_pipeline_disable - Disable streaming on a pipeline + * @vpfe_dev: vpfe device + * @pipe: VPFE pipeline + * + * Walk the entities chain starting at the pipeline output video node and stop + * all modules in the chain. + * + * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module + * can't be stopped. + */ +static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe) +{ + struct media_entity_graph graph; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_device *mdev; + int ret = 0; + + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + entity = vpfe_get_input_entity(pipe->outputs[0]); + else + entity = &pipe->inputs[0]->video_dev.entity; + + mdev = entity->parent; + + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + continue; + + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + } + + mutex_unlock(&mdev->graph_mutex); + + return (ret == 0) ? ret : -ETIMEDOUT ; +} + +/** + * vpfe_pipeline_set_stream - Enable/disable streaming on a pipeline + * @vpfe_dev: VPFE device + * @pipe: VPFE pipeline + * @state: Stream state (stopped or active) + * + * Set the pipeline to the given stream state. + * + * Return 0 if successfull, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int vpfe_pipeline_set_stream(struct vpfe_pipeline *pipe, + enum vpfe_pipeline_stream_state state) +{ + if (state == VPFE_PIPELINE_STREAM_STOPPED) + return vpfe_pipeline_disable(pipe); + + return vpfe_pipeline_enable(pipe); +} + +static int all_videos_stopped(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int i; + + for (i = 0; i < pipe->input_num; i++) + if (pipe->inputs[i]->started) + return 0; + + for (i = 0; i < pipe->output_num; i++) + if (pipe->outputs[i]->started) + return 0; + + return 1; +} + +/* + * vpfe_open - open video device + * @file: file pointer + * + * initialize media pipeline state, allocate memory for file hadle + * + * Return 0 if successfull, or the return -ENODEV otherwise. + */ +static int vpfe_open(struct file *file) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_fh *fh; + + /* Allocate memory for the file handle object */ + fh = kzalloc(sizeof(struct vpfe_fh), GFP_KERNEL); + + if (fh == NULL) + return -ENOMEM; + /* store pointer to fh in private_data member of file */ + file->private_data = fh; + fh->video = video; + mutex_lock(&video->lock); + /* If decoder is not initialized. initialize it */ + if (!video->initialized && vpfe_update_pipe_state(video)) { + mutex_unlock(&video->lock); + return -ENODEV; + } + /* Increment device usrs counter */ + video->usrs++; + /* Set io_allowed member to false */ + fh->io_allowed = 0; + /* Initialize priority of this instance to default priority */ + fh->prio = V4L2_PRIORITY_UNSET; + v4l2_prio_open(&video->prio, &fh->prio); + mutex_unlock(&video->lock); + + return 0; +} + +/* get the next buffer available from dma queue */ +unsigned long vpfe_get_next_buffer(struct vpfe_video_device *video) +{ + /* mark next buffer as active */ + video->next_frm = list_entry(video->dma_queue.next, + struct videobuf_buffer, queue); + + /* in single shot mode both curr_frm + and next_frm point to same buffer */ + video->cur_frm = video->next_frm; + list_del(&video->next_frm->queue); + video->next_frm->state = VIDEOBUF_ACTIVE; + + return videobuf_to_dma_contig(video->next_frm); +} + +/* schedule the next buffer which is available on dma queue */ +void vpfe_schedule_next_buffer(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + if (list_empty(&video->dma_queue)) + return; + + video->next_frm = list_entry(video->dma_queue.next, + struct videobuf_buffer, queue); + + if (VPFE_PIPELINE_STREAM_SINGLESHOT == video->pipe.state) + video->cur_frm = video->next_frm; + + list_del(&video->next_frm->queue); + video->next_frm->state = VIDEOBUF_ACTIVE; + addr = videobuf_to_dma_contig(video->next_frm); + + video->ops->queue(vpfe_dev, addr); + + video->state = VPFE_VIDEO_BUFFER_QUEUED; +} + +/* schedule the buffer for capturing bottom field */ +void vpfe_schedule_bottom_field(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + addr = videobuf_to_dma_contig(video->cur_frm); + addr += video->field_off; + + video->ops->queue(vpfe_dev, addr); +} + +/* make buffer available for dequeue */ +void vpfe_process_buffer_complete(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + struct timespec timespec; + s64 nsec; + + ktime_get_ts(×pec); + nsec = timespec_to_ns(×pec); + + video->cur_frm->ts = ns_to_timeval(nsec); + video->cur_frm->state = VIDEOBUF_DONE; + video->cur_frm->size = video->fmt.fmt.pix.sizeimage; + wake_up_interruptible(&video->cur_frm->done); + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + video->cur_frm = video->next_frm; +} + +/* vpfe_stop_capture: stop streaming */ +static void vpfe_stop_capture(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + + video->started = 0; + + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return; + + if (all_videos_stopped(video)) + vpfe_pipeline_set_stream(pipe, + VPFE_PIPELINE_STREAM_STOPPED); +} + +/* + * vpfe_release - release video device + * @file: file pointer + * + * deletes buffer queue, frees the buffers and the vpfe file handle + * + * Return 0 + */ +static int vpfe_release(struct file *file) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n"); + + /* Get the device lock */ + mutex_lock(&video->lock); + /* if this instance is doing IO */ + if (fh->io_allowed) { + if (video->started) { + vpfe_stop_capture(video); + /* mark pipe state as stopped in vpfe_release(), + as app might call streamon() after streamoff() + in which case driver has to start streaming. + */ + video->pipe.state = VPFE_PIPELINE_STREAM_STOPPED; + videobuf_streamoff(&video->buffer_queue); + } + video->io_usrs = 0; + } + + /* Decrement device usrs counter */ + video->usrs--; + /* Close the priority */ + v4l2_prio_close(&video->prio, fh->prio); + + /* If this is the last file handle */ + if (!video->usrs) + video->initialized = 0; + + mutex_unlock(&video->lock); + file->private_data = NULL; + /* Free memory allocated to file handle object */ + kzfree(fh); + + return 0; +} + +/* + * vpfe_mmap : It is used to map kernel space buffers + * into user spaces + */ +static int vpfe_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n"); + + return videobuf_mmap_mapper(&video->buffer_queue, vma); +} + +/* + * vpfe_poll: It is used for select/poll system call + */ +static unsigned int vpfe_poll(struct file *file, poll_table *wait) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n"); + + if (video->started) + return videobuf_poll_stream(file, + &video->buffer_queue, wait); + + return 0; +} + +/* vpfe capture driver file operations */ +static const struct v4l2_file_operations vpfe_fops = { + .owner = THIS_MODULE, + .open = vpfe_open, + .release = vpfe_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vpfe_mmap, + .poll = vpfe_poll +}; + +/* + * vpfe_querycap - query capabilities of video device + * @file: file pointer + * @priv: void pointer + * @cap: pointer to v4l2_capability structure + * + * fills v4l2 capabilities structure + * + * Return 0 + */ +static int vpfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n"); + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + else + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + + cap->version = VPFE_CAPTURE_VERSION_CODE; + strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); + strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); + + return 0; +} + +/* + * vpfe_g_fmt - get the format which is active on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * fills v4l2 format structure with active format + * + * Return 0 + */ +static int vpfe_g_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt\n"); + /* Fill in the information about format */ + *fmt = video->fmt; + + return 0; +} + +/* + * vpfe_enum_fmt - enum formats supported on media chain + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_fmtdesc structure + * + * fills v4l2_fmtdesc structure with output format set on adjacent subdev, + * only one format is enumearted as subdevs are already configured + * + * Return 0 if successfull, error code otherwise + */ +static int vpfe_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt mbus; + struct v4l2_subdev *subdev; + struct v4l2_format format; + struct media_pad *remote; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt\n"); + + /* since already subdev pad format is set, + only one pixel format is available */ + if (fmt->index > 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid index\n"); + return -EINVAL; + } + + /* get the remote pad */ + remote = media_entity_remote_source(&video->pad); + if (remote == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, "invalid remote pad \ + for video node\n"); + return -EINVAL; + } + + /* get the remote subdev */ + subdev = vpfe_video_remote_subdev(video, NULL); + if (subdev == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, "invalid remote subdev \ + for video node\n"); + return -EINVAL; + } + + sd_fmt.pad = remote->index; + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + /* get output format of remote subdev */ + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &sd_fmt); + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, "invalid remote subdev \ + for video node\n"); + return ret; + } + /* convert to pix format */ + mbus.code = sd_fmt.format.code; + mbus_to_pix(&mbus, &format.fmt.pix); + + /* copy the result */ + fmt->pixelformat = format.fmt.pix.pixelformat; + + return 0; +} + +/* + * vpfe_s_fmt - set the format on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * validate and set the format on video device + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_format format; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt\n"); + + /* If streaming is started, return error */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + + /* get adjacent subdev's output pad format */ + ret = __vpfe_video_get_format(video, &format); + if (ret) + return ret; + + *fmt = format; + + video->fmt = *fmt; + + return 0; +} + +/* + * vpfe_try_fmt - try the format on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * validate the format, update with correct format + * based on output format set on adjacent subdev + * + * Return 0 on success, error code otherwise + */ +static int vpfe_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_format format; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt\n"); + + /* get adjacent subdev's output pad format */ + ret = __vpfe_video_get_format(video, &format); + if (ret) + return ret; + + *fmt = format; + + return 0; +} + +/* + * vpfe_enum_input - enum inputs supported on media chain + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_fmtdesc structure + * + * fills v4l2_input structure with input available on media chain, + * only one input is enumearted as media chain is setup by this time + * + * Return 0 if successfull, -EINVAL is media chain is invalid + */ +static int vpfe_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_ext_subdev_info *sdinfo = video->current_ext_subdev; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n"); + + /* enumerate from the subdev user has choosen through mc */ + if (inp->index < sdinfo->num_inputs) { + memcpy(inp, &sdinfo->inputs[inp->index], + sizeof(struct v4l2_input)); + return 0; + } + + return -EINVAL; +} + +/* + * vpfe_g_input - get index of the input which is active + * @file: file pointer + * @priv: void pointer + * @index: pointer to unsigned int + * + * set index with input index which is active + * + * Return 0 + */ +static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n"); + + *index = video->current_input; + + return 0; +} + +/* + * vpfe_s_input - set input which is pointed by input index + * @file: file pointer + * @priv: void pointer + * @index: pointer to unsigned int + * + * set input on external subdev + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct ccdc_hw_device *ccdc_dev = vpfe_dev->vpfe_ccdc.ccdc_dev; + struct imp_hw_interface *imp_hw_if = vpfe_dev->vpfe_previewer.imp_hw_if; + struct vpfe_ext_subdev_info *sdinfo; + struct vpfe_route *route; + struct v4l2_input *inps; + u32 output; + u32 input; + int ret; + int i; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n"); + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + /* + * If streaming is started return device busy + * error + */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n"); + ret = -EBUSY; + goto unlock_out; + } + + sdinfo = video->current_ext_subdev; + + if (!sdinfo->registered) { + ret = -EINVAL; + goto unlock_out; + } + + if (vpfe_dev->cfg->setup_input && + vpfe_dev->cfg->setup_input(sdinfo->grp_id) < 0) { + ret = -EFAULT; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "couldn't setup input for %s\n", + sdinfo->module_name); + goto unlock_out; + } + + route = &sdinfo->routes[index]; + if (route && sdinfo->can_route) { + input = route->input; + output = route->output; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, video, + s_routing, input, output, 0); + + if (ret) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "s_input:error in setting input in decoder\n"); + ret = -EINVAL; + goto unlock_out; + } + } + + /* set standards set by subdev in video device */ + for (i = 0; i < sdinfo->num_inputs; i++) { + inps = &sdinfo->inputs[i]; + video->video_dev.tvnorms |= inps->std; + } + + /* set the bus/interface parameter for the sub device in ccdc */ + ret = ccdc_dev->hw_ops.set_hw_if_params(&sdinfo->ccdc_if_params); + if (ret) + goto unlock_out; + + /* update the if parameters to imp hw interface */ + if (imp_hw_if && imp_hw_if->set_hw_if_param) + ret = imp_hw_if->set_hw_if_param(vpfe_dev->ipipe, + &sdinfo->ccdc_if_params); + if (ret) + goto unlock_out; + + video->current_input = index; + +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_querystd - query std which is being input on external subdev + * @file: file pointer + * @priv: void pointer + * @std_id: pointer to v4l2_std_id structure + * + * call external subdev through v4l2_device_call_until_err to + * get the std that is being active. + * + * Return 0 on success, error code otherwise + */ +static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n"); + + ret = mutex_lock_interruptible(&video->lock); + sdinfo = video->current_ext_subdev; + if (ret) + return ret; + + /* Call querystd function of decoder device */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, querystd, std_id); + mutex_unlock(&video->lock); + + return ret; +} + +/* + * vpfe_s_std - set std on external subdev + * @file: file pointer + * @priv: void pointer + * @std_id: pointer to v4l2_std_id structure + * + * set std pointed by std_id on external subdev by calling it using + * v4l2_device_call_until_err + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n"); + + /* Call decoder driver function to set the standard */ + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + sdinfo = video->current_ext_subdev; + /* If streaming is started, return device busy error */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n"); + ret = -EBUSY; + goto unlock_out; + } + + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, s_std, *std_id); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); + goto unlock_out; + } + +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_enum_preset - enumerate dv_preset which are supported by + * to external subdev + * + * @file: file pointer + * @priv: void pointer + * @preset: pointer to v4l2_dv_enum_preset structure + * + * enum dv_preset's which are supported by external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int vpfe_enum_preset(struct file *file, void *fh, + struct v4l2_dv_enum_preset *preset) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_preset\n"); + + return v4l2_subdev_call(subdev, video, enum_dv_presets, preset); +} + +/* + * vpfe_query_preset - query the dv_preset which is being input + * to external subdev + * + * @file: file pointer + * @priv: void pointer + * @preset: pointer to v4l2_preset structure + * + * get dv_preset which is being input on external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int vpfe_query_preset(struct file *file, void *fh, + struct v4l2_dv_preset *preset) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_query_preset\n"); + + return v4l2_subdev_call(subdev, video, query_dv_preset, preset); +} + +/* + * vpfe_s_preset - set dv_preset on external subdev + * @file: file pointer + * @priv: void pointer + * @preset: pointer to v4l2_preset structure + * + * set dv_preset pointed by preset on external subdev through + * v4l2_device_call_until_err, this configures amplifier also + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_preset(struct file *file, void *fh, + struct v4l2_dv_preset *preset) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_preset\n"); + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + video->current_ext_subdev->grp_id, + video, s_dv_preset, preset); +} + +/* + * vpfe_g_preset - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @preset: pointer to v4l2_preset structure + * + * get dv_preset which is set on external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int vpfe_g_preset(struct file *file, void *fh, + struct v4l2_dv_preset *preset) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_preset\n"); + + return v4l2_subdev_call(subdev, video, query_dv_preset, preset); +} + +/* + * Videobuf operations + */ +static int vpfe_videobuf_setup(struct videobuf_queue *vq, + unsigned int *count, + unsigned int *size) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_setup\n"); + + *size = video->fmt.fmt.pix.sizeimage; + + if (vpfe_dev->video_limit) { + while (*size * *count > vpfe_dev->video_limit) + (*count)--; + } + + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) { + if (*count < CONT_MIN_NUM_BUFFERS) + *count = CONT_MIN_NUM_BUFFERS; + } + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "count=%d, size=%d\n", *count, *size); + + return 0; +} + +static int vpfe_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_videobuf_prepare\n"); + + if (VIDEOBUF_NEEDS_INIT != vb->state) + return 0; + + /* Initialize buffer */ + vb->width = video->fmt.fmt.pix.width; + vb->height = video->fmt.fmt.pix.height; + vb->size = video->fmt.fmt.pix.sizeimage; + vb->field = field; + + ret = videobuf_iolock(vq, vb, NULL); + if (ret < 0) + return ret; + + addr = videobuf_to_dma_contig(vb); + /* Make sure user addresses are aligned to 32 bytes */ + if (!ALIGN(addr, 32)) + return -EINVAL; + + vb->state = VIDEOBUF_PREPARED; + + return 0; +} + +static void vpfe_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and device object */ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + unsigned long flags; + unsigned long empty; + unsigned long addr; + + spin_lock_irqsave(&video->dma_queue_lock, flags); + empty = list_empty(&video->dma_queue); + + /* add the buffer to the DMA queue */ + list_add_tail(&vb->queue, &video->dma_queue); + spin_unlock_irqrestore(&video->dma_queue_lock, flags); + + /* Change state of the buffer */ + vb->state = VIDEOBUF_QUEUED; + + /* this case happens in case of single shot */ + if (empty && video->started && pipe->state == + VPFE_PIPELINE_STREAM_SINGLESHOT && + video->state == VPFE_VIDEO_BUFFER_NOT_QUEUED) { + spin_lock(&video->dma_queue_lock); + addr = vpfe_get_next_buffer(video); + video->ops->queue(vpfe_dev, addr); + + video->state = VPFE_VIDEO_BUFFER_QUEUED; + spin_unlock(&video->dma_queue_lock); + + /* enable h/w each time in single shot */ + if (is_pipe_ready(pipe)) + vpfe_pipeline_set_stream(pipe, + VPFE_PIPELINE_STREAM_SINGLESHOT); + } +} + +static void vpfe_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_videobuf_release\n"); + + if (video->memory == V4L2_MEMORY_MMAP) + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops vpfe_videobuf_qops = { + .buf_setup = vpfe_videobuf_setup, + .buf_prepare = vpfe_videobuf_prepare, + .buf_queue = vpfe_videobuf_queue, + .buf_release = vpfe_videobuf_release, +}; + +/* + * vpfe_reqbufs. supported REQBUF only once opening + * the device. + */ +static int vpfe_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + if (video->io_usrs != 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n"); + ret = -EBUSY; + goto unlock_out; + } + + video->memory = req_buf->memory; + videobuf_queue_dma_contig_init(&video->buffer_queue, + &vpfe_videobuf_qops, vpfe_dev->pdev, + &video->irqlock, req_buf->type, + video->fmt.fmt.pix.field, + sizeof(struct videobuf_buffer), + fh, NULL); + + fh->io_allowed = 1; + video->io_usrs = 1; + INIT_LIST_HEAD(&video->dma_queue); + ret = videobuf_reqbufs(&video->buffer_queue, req_buf); + +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_querybuf. query buffers for exchange + */ +static int vpfe_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (video->memory != V4L2_MEMORY_MMAP) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n"); + return -EINVAL; + } + + /* Call videobuf_querybuf to get information */ + return videobuf_querybuf(&video->buffer_queue, buf); +} + +/* + * vpfe_qbuf. queue buffers for capture or processing + */ +static int vpfe_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* + * If this file handle is not allowed to do IO, + * return error + */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + return videobuf_qbuf(&video->buffer_queue, p); +} + +/* + * vpfe_dqbuf. deque buffer which is done with processing + */ +static int vpfe_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + return videobuf_dqbuf(&video->buffer_queue, + buf, file->f_flags & O_NONBLOCK); +} + +/* vpfe_start_capture: start streaming on all the subdevs */ +static int vpfe_start_capture(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int ret = 0; + + video->started = 1; + + if (is_pipe_ready(pipe)) + ret = vpfe_pipeline_set_stream(pipe, pipe->state); + + return ret; +} + +/* + * vpfe_streamon - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @buf_type: enum v4l2_buf_type + * + * queue buffer onto hardware for capture/processing and + * start all the subdevs which are in media chain + * + * Return 0 on success, error code otherwise + */ +static int vpfe_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + struct vpfe_ext_subdev_info *sdinfo; + unsigned long addr; + + struct vpfe_pipeline *pipe = &video->pipe; + int ret = -EINVAL; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return ret; + } + + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + sdinfo = video->current_ext_subdev; + + /* If buffer queue is empty, return error */ + if (list_empty(&video->buffer_queue.stream)) { + v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n"); + return -EIO; + } + + /* Validate the pipeline */ + if (V4L2_BUF_TYPE_VIDEO_CAPTURE == buf_type) { + ret = vpfe_video_validate_pipeline(pipe); + if (ret < 0) + return ret; + } + + /* Call videobuf_streamon to start streaming * in videobuf */ + ret = videobuf_streamon(&video->buffer_queue); + if (ret) + return ret; + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + goto streamoff; + + /* Get the next frame from the buffer queue */ + video->next_frm = list_entry(video->dma_queue.next, + struct videobuf_buffer, queue); + video->cur_frm = video->next_frm; + /* Remove buffer from the buffer queue */ + list_del(&video->cur_frm->queue); + /* Mark state of the current frame to active */ + video->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + video->field_id = 0; + addr = videobuf_to_dma_contig(video->cur_frm); + + video->ops->queue(vpfe_dev, addr); + + video->state = VPFE_VIDEO_BUFFER_QUEUED; + + /* Image processor chained in the path */ + if (!cpu_is_davinci_dm365() && + !video->current_ext_subdev->is_camera) { + v4l2_err(&vpfe_dev->v4l2_dev, "Doesn't support chaining\n"); + goto unlock_out; + } + + ret = 0; + + ret = vpfe_start_capture(video); + if (ret) + goto unlock_out; + + mutex_unlock(&video->lock); + + return ret; +unlock_out: + mutex_unlock(&video->lock); +streamoff: + ret = videobuf_streamoff(&video->buffer_queue); + return ret; +} + +/* + * vpfe_streamoff - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @buf_type: enum v4l2_buf_type + * + * stop all the subdevs which are in media chain + * + * Return 0 on success, error code otherwise + */ +static int vpfe_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n"); + + if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + buf_type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "device is not started\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + vpfe_stop_capture(video); + + ret = videobuf_streamoff(&video->buffer_queue); + mutex_unlock(&video->lock); + + return ret; +} + +/* + * vpfe_queryctrl - query for v4l2 controls which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @ctrl: pointer to v4l2_control structure + * + * get the v4l2 controls active on external subdev through + * v4l2_device_call_until_err + * + * Return return value returned by v4l2_device_call_until_err + */ +static int vpfe_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sub_dev = video->current_ext_subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_queryctrl\n"); + + /* pass it to sub device */ + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sub_dev->grp_id, + core, queryctrl, qc); +} + +/* + * vpfe_g_ctrl - get the v4l2 controls which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @ctrl: pointer to v4l2_control structure + * + * get the v4l2 controls set on external subdev through + * v4l2_device_call_until_err + * + * Return return value returned by v4l2_device_call_until_err + */ +static int vpfe_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sub_dev = video->current_ext_subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_ctrl\n"); + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sub_dev->grp_id, + core, g_ctrl, ctrl); +} + +/* + * vpfe_s_ctrl - set the v4l2 controls on external subdev + * @file: file pointer + * @priv: void pointer + * @ctrl: pointer to v4l2_control structure + * + * call external subdev through v4l2_device_call_until_err to + * set v4l2 controls + * + * Return return value returned by v4l2_device_call_until_err + */ +static int vpfe_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sub_dev = video->current_ext_subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_ctrl\n"); + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sub_dev->grp_id, + core, s_ctrl, ctrl); +} + +/* vpfe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { + .vidioc_querycap = vpfe_querycap, + .vidioc_g_fmt_vid_cap = vpfe_g_fmt, + .vidioc_s_fmt_vid_cap = vpfe_s_fmt, + .vidioc_try_fmt_vid_cap = vpfe_try_fmt, + .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt, + .vidioc_g_fmt_vid_out = vpfe_g_fmt, + .vidioc_s_fmt_vid_out = vpfe_s_fmt, + .vidioc_try_fmt_vid_out = vpfe_try_fmt, + .vidioc_enum_fmt_vid_out = vpfe_enum_fmt, + .vidioc_enum_input = vpfe_enum_input, + .vidioc_g_input = vpfe_g_input, + .vidioc_s_input = vpfe_s_input, + .vidioc_querystd = vpfe_querystd, + .vidioc_s_std = vpfe_s_std, + .vidioc_enum_dv_presets = vpfe_enum_preset, + .vidioc_query_dv_preset = vpfe_query_preset, + .vidioc_s_dv_preset = vpfe_s_preset, + .vidioc_g_dv_preset = vpfe_g_preset, + .vidioc_reqbufs = vpfe_reqbufs, + .vidioc_querybuf = vpfe_querybuf, + .vidioc_qbuf = vpfe_qbuf, + .vidioc_dqbuf = vpfe_dqbuf, + .vidioc_streamon = vpfe_streamon, + .vidioc_streamoff = vpfe_streamoff, + .vidioc_queryctrl = vpfe_queryctrl, + .vidioc_g_ctrl = vpfe_g_ctrl, + .vidioc_s_ctrl = vpfe_s_ctrl, +}; + +/** + * VPFE video core operations + */ +static const struct vpfe_video_operations vpfe_video_dummy_ops = { +}; + +/* VPFE video init function */ +int vpfe_video_init(struct vpfe_video_device *video, const char *name) +{ + const char *direction; + int ret; + + switch (video->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + direction = "output"; + video->pad.flags = MEDIA_PAD_FL_INPUT; + video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + direction = "input"; + video->pad.flags = MEDIA_PAD_FL_OUTPUT; + video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + break; + default: + return -EINVAL; + } + + /* Initialize field of video device */ + video->video_dev.release = video_device_release; + video->video_dev.fops = &vpfe_fops; + video->video_dev.ioctl_ops = &vpfe_ioctl_ops; + video->video_dev.minor = -1; + video->video_dev.tvnorms = 0; + video->video_dev.current_norm = V4L2_STD_NTSC; + + snprintf(video->video_dev.name, sizeof(video->video_dev.name), + "DAVINCI VIDEO %s %s", name, direction); + + /* Initialize prio member of device object */ + v4l2_prio_init(&video->prio); + + spin_lock_init(&video->irqlock); + spin_lock_init(&video->dma_queue_lock); + mutex_init(&video->lock); + + ret = media_entity_init(&video->video_dev.entity, + 1, &video->pad, 0); + if (ret < 0) + return ret; + + video_set_drvdata(&video->video_dev, video); + + return 0; +} + +/* vpfe video device register function */ +int vpfe_video_register(struct vpfe_video_device *video, + struct v4l2_device *vdev) +{ + int ret; + + video->video_dev.v4l2_dev = vdev; + + ret = video_register_device(&video->video_dev, VFL_TYPE_GRABBER, -1); + if (ret < 0) + printk(KERN_ERR "%s: could not register video device (%d)\n", + __func__, ret); + + return ret; +} + +/* vpfe video device unregister function */ +void vpfe_video_unregister(struct vpfe_video_device *video) +{ + if (video_is_registered(&video->video_dev)) { + media_entity_cleanup(&video->video_dev.entity); + video_unregister_device(&video->video_dev); + } +} diff --git a/drivers/media/video/davinci/vpfe_video.h b/drivers/media/video/davinci/vpfe_video.h new file mode 100644 index 0000000..2bc10cc --- /dev/null +++ b/drivers/media/video/davinci/vpfe_video.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* All video device related structures will go here */ +#ifndef _VPFE_VIDEO_H +#define _VPFE_VIDEO_H + +#include +#include + +struct vpfe_device; + +/* + * struct vpfe_video_operations - VPFE video operations + * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF + * if there was no buffer previously queued. + */ +struct vpfe_video_operations { + void(*queue)(struct vpfe_device *vpfe_dev, unsigned long addr); +}; + +enum vpfe_pipeline_stream_state { + VPFE_PIPELINE_STREAM_STOPPED, + VPFE_PIPELINE_STREAM_CONTINUOUS, + VPFE_PIPELINE_STREAM_SINGLESHOT +}; + +enum vpfe_video_state { + /* indicates that buffer is not queued */ + VPFE_VIDEO_BUFFER_NOT_QUEUED = 0, + /* indicates that buffer is queued */ + VPFE_VIDEO_BUFFER_QUEUED = 1, +}; + +struct vpfe_pipeline { + /* media pipeline */ + struct media_pipeline *pipe; + /* state of the pipeline, continous, + single-shot or stopped */ + enum vpfe_pipeline_stream_state state; + /* number of active input video entities */ + unsigned int input_num; + /* number of active output video entities */ + unsigned int output_num; + /* input video nodes in case of single-shot mode */ + struct vpfe_video_device *inputs[10]; + /* capturing video nodes */ + struct vpfe_video_device *outputs[10]; +}; + +#define to_vpfe_pipeline(__e) \ + container_of((__e)->pipe, struct vpfe_pipeline, pipe) + +#define to_vpfe_video(vdev) \ + container_of(vdev, struct vpfe_video_device, video_dev) + +struct vpfe_video_device { + /* vpfe device */ + struct vpfe_device *vpfe_dev; + /* video dev */ + struct video_device video_dev; + /* media pad of video entity */ + struct media_pad pad; + /* video operations supported by video device */ + const struct vpfe_video_operations *ops; + /* type of the video buffers used by user */ + enum v4l2_buf_type type; + /* Indicates id of the field which is being captured */ + u32 field_id; + /* pipiline for which video device is part of */ + struct vpfe_pipeline pipe; + /* Indicates whether streaming started */ + u8 started; + /* Indicates state of the stream */ + unsigned int state; + /* current input at the sub device */ + int current_input; + /* + * This field keeps track of type of buffer exchange mechanism + * user has selected + */ + enum v4l2_memory memory; + /* Used to keep track of state of the priority */ + struct v4l2_prio_state prio; + /* number of open instances of the channel */ + u32 usrs; + /* flag to indicate whether decoder is initialized */ + u8 initialized; + /* skip frame count */ + u8 skip_frame_count; + /* skip frame count init value */ + u8 skip_frame_count_init; + /* time per frame for skipping */ + struct v4l2_fract timeperframe; + /* ptr to currently selected sub device */ + struct vpfe_ext_subdev_info *current_ext_subdev; + /* Pointer pointing to current v4l2_buffer */ + struct videobuf_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct videobuf_buffer *next_frm; + /* Used to store pixel format */ + struct v4l2_format fmt; + /* Buffer queue used in video-buf */ + struct videobuf_queue buffer_queue; + /* Queue of filled frames */ + struct list_head dma_queue; + /* Used in video-buf */ + spinlock_t irqlock; + /* IRQ lock for DMA queue */ + spinlock_t dma_queue_lock; + /* lock used to access this structure */ + struct mutex lock; + /* number of users performing IO */ + u32 io_usrs; + /* + * offset where second field starts from the starting of the + * buffer for field seperated YCbCr formats + */ + u32 field_off; +}; + +int is_pipe_ready(struct vpfe_pipeline *pipe); +void vpfe_video_unregister(struct vpfe_video_device *video); +int vpfe_video_register(struct vpfe_video_device *video, + struct v4l2_device *vdev); +int vpfe_video_init(struct vpfe_video_device *video, const char *name); + +void vpfe_process_buffer_complete(struct vpfe_video_device *video); +void vpfe_schedule_bottom_field(struct vpfe_video_device *video); +void vpfe_schedule_next_buffer(struct vpfe_video_device *video); +unsigned long vpfe_get_next_buffer(struct vpfe_video_device *video); +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Sat Nov 12 07:18:56 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 12 Nov 2011 18:48:56 +0530 Subject: [RFC PATCH v4 2/8] davinci: vpfe: add IPIPE hardware layer support In-Reply-To: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> References: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321103942-2778-3-git-send-email-manjunath.hadli@ti.com> add dm365 IPIPE hardware support. IPIPE is the hardware IP which implements the functionality required for resizer, previewer and the associated feature support. This is built along with the vpfe driver, and implements hardware setup including coeffcient programming for various hardware filters, gamma, cfa and clock enable. Signed-off-by: Manjunath Hadli Signed-off-by: Nagabhushana Netagunte --- drivers/media/video/davinci/dm365_ipipe_hw.c | 935 ++++++++++++++++++++++++++ drivers/media/video/davinci/dm365_ipipe_hw.h | 539 +++++++++++++++ 2 files changed, 1474 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/dm365_ipipe_hw.c create mode 100644 drivers/media/video/davinci/dm365_ipipe_hw.h diff --git a/drivers/media/video/davinci/dm365_ipipe_hw.c b/drivers/media/video/davinci/dm365_ipipe_hw.c new file mode 100644 index 0000000..7de9702 --- /dev/null +++ b/drivers/media/video/davinci/dm365_ipipe_hw.c @@ -0,0 +1,935 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include +#include +#include "dm365_ipipe.h" +#include "dm3xx_ipipeif.h" +#include "dm365_ipipe_hw.h" + +static void ipipe_clock_enable(void) +{ + /* enable IPIPE MMR for register write access */ + regw_ip(IPIPE_GCK_MMR_DEFAULT, IPIPE_GCK_MMR); + /* enable the clock wb,cfa,dfc,d2f,pre modules */ + regw_ip(IPIPE_GCK_PIX_DEFAULT, IPIPE_GCK_PIX); + /* enable RSZ MMR for register write access */ +} + +/* Set input channel format to either 420 Y or C format */ +void rsz_set_in_pix_format(unsigned char y_c) +{ + u32 val; + + val = regr_rsz(RSZ_SRC_FMT1); + val |= y_c & 1; + regw_rsz(val, RSZ_SRC_FMT1); +} + +static void rsz_set_common_params(struct ipipe_params *params) +{ + struct rsz_common_params *rsz_common = ¶ms->rsz_common; + u32 val; + + /* Set mode */ + regw_rsz(params->ipipe_mode, RSZ_SRC_MODE); + + /* data source selection and bypass */ + val = (rsz_common->passthrough << RSZ_BYPASS_SHIFT) | + rsz_common->source; + + regw_rsz(val, RSZ_SRC_FMT0); + val = regr_rsz(RSZ_SRC_MODE); + /* src image selection */ + val = (rsz_common->raw_flip & 1) | + (rsz_common->src_img_fmt << RSZ_SRC_IMG_FMT_SHIFT) | + ((rsz_common->y_c & 1) << RSZ_SRC_Y_C_SEL_SHIFT); + + regw_rsz(val, RSZ_SRC_FMT1); + regw_rsz(rsz_common->vps & IPIPE_RSZ_VPS_MASK, RSZ_SRC_VPS); + regw_rsz(rsz_common->hps & IPIPE_RSZ_HPS_MASK, RSZ_SRC_HPS); + regw_rsz(rsz_common->vsz & IPIPE_RSZ_VSZ_MASK, RSZ_SRC_VSZ); + regw_rsz(rsz_common->hsz & IPIPE_RSZ_HSZ_MASK, RSZ_SRC_HSZ); + regw_rsz(rsz_common->yuv_y_min, RSZ_YUV_Y_MIN); + regw_rsz(rsz_common->yuv_y_max, RSZ_YUV_Y_MAX); + regw_rsz(rsz_common->yuv_c_min, RSZ_YUV_C_MIN); + regw_rsz(rsz_common->yuv_c_max, RSZ_YUV_C_MAX); + /* chromatic position */ + regw_rsz(rsz_common->out_chr_pos, RSZ_YUV_PHS); + val = regr_rsz(RSZ_SRC_MODE); +} + +static void rsz_set_rsz_regs(unsigned int rsz_id, struct ipipe_params *params) +{ + struct ipipe_rsz_rescale_param *rsc_params; + struct ipipe_ext_mem_param *ext_mem; + struct ipipe_rsz_resize2rgb *rgb; + u32 reg_base; + u32 val; + + val = regr_rsz(RSZ_SEQ); + rsc_params = ¶ms->rsz_rsc_param[rsz_id]; + rgb = ¶ms->rsz2rgb[rsz_id]; + ext_mem = ¶ms->ext_mem_param[rsz_id]; + + if (rsz_id == RSZ_A) { + val = rsc_params->h_flip << RSZA_H_FLIP_SHIFT; + val |= rsc_params->v_flip << RSZA_V_FLIP_SHIFT; + reg_base = RSZ_EN_A; + } else { + val = rsc_params->h_flip << RSZB_H_FLIP_SHIFT; + val |= rsc_params->v_flip << RSZB_V_FLIP_SHIFT; + reg_base = RSZ_EN_B; + } + /* update flip settings */ + regw_rsz(val, RSZ_SEQ); + + regw_rsz(rsc_params->mode, reg_base + RSZ_MODE); + val = (rsc_params->cen << RSZ_CEN_SHIFT) | rsc_params->yen; + regw_rsz(val, reg_base + RSZ_420); + regw_rsz(rsc_params->i_vps & RSZ_VPS_MASK, reg_base + RSZ_I_VPS); + regw_rsz(rsc_params->i_hps & RSZ_HPS_MASK, reg_base + RSZ_I_HPS); + regw_rsz(rsc_params->o_vsz & RSZ_O_VSZ_MASK, reg_base + RSZ_O_VSZ); + regw_rsz(rsc_params->o_hsz & RSZ_O_HSZ_MASK, reg_base + RSZ_O_HSZ); + regw_rsz(rsc_params->v_phs_y & RSZ_V_PHS_MASK, reg_base + RSZ_V_PHS_Y); + regw_rsz(rsc_params->v_phs_c & RSZ_V_PHS_MASK, reg_base + RSZ_V_PHS_C); + /* keep this additional adjustment to zero for now */ + regw_rsz(rsc_params->v_dif & RSZ_V_DIF_MASK, reg_base + RSZ_V_DIF); + + val = (rsc_params->v_typ_y & 1) | ((rsc_params->v_typ_c & 1) << + RSZ_TYP_C_SHIFT); + regw_rsz(val, reg_base + RSZ_V_TYP); + + val = (rsc_params->v_lpf_int_y & RSZ_LPF_INT_MASK) | + ((rsc_params->v_lpf_int_c & RSZ_LPF_INT_MASK) << + RSZ_LPF_INT_C_SHIFT); + regw_rsz(val, reg_base + RSZ_V_LPF); + + regw_rsz(rsc_params->h_phs & RSZ_H_PHS_MASK, reg_base + RSZ_H_PHS); + regw_rsz(0, reg_base + RSZ_H_PHS_ADJ); + regw_rsz(rsc_params->h_dif & RSZ_H_DIF_MASK, reg_base + RSZ_H_DIF); + val = (rsc_params->h_typ_y & 1) | ((rsc_params->h_typ_c & 1) << + RSZ_TYP_C_SHIFT); + regw_rsz(val, reg_base + RSZ_H_TYP); + val = (rsc_params->h_lpf_int_y & RSZ_LPF_INT_MASK) | + ((rsc_params->h_lpf_int_c & RSZ_LPF_INT_MASK) << + RSZ_LPF_INT_C_SHIFT); + regw_rsz(val, reg_base + RSZ_H_LPF); + + regw_rsz(rsc_params->dscale_en & 1, reg_base + RSZ_DWN_EN); + val = rsc_params->h_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK; + val |= (rsc_params->v_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK) << + RSZ_DWN_SCALE_AV_SZ_V_SHIFT; + regw_rsz(val, reg_base + RSZ_DWN_AV); + + /* setting rgb conversion parameters */ + regw_rsz(rgb->rgb_en, reg_base + RSZ_RGB_EN); + val = (rgb->rgb_typ << RSZ_RGB_TYP_SHIFT) | + (rgb->rgb_msk0 << RSZ_RGB_MSK0_SHIFT) | + (rgb->rgb_msk1 << RSZ_RGB_MSK1_SHIFT); + regw_rsz(val, reg_base + RSZ_RGB_TYP); + regw_rsz(rgb->rgb_alpha_val & RSZ_RGB_ALPHA_MASK, + reg_base + RSZ_RGB_BLD); + + /* setting external memory parameters */ + regw_rsz(ext_mem->rsz_sdr_oft_y, reg_base + RSZ_SDR_Y_OFT); + regw_rsz(ext_mem->rsz_sdr_ptr_s_y, reg_base + RSZ_SDR_Y_PTR_S); + regw_rsz(ext_mem->rsz_sdr_ptr_e_y, reg_base + RSZ_SDR_Y_PTR_E); + regw_rsz(ext_mem->rsz_sdr_oft_c, reg_base + RSZ_SDR_C_OFT); + regw_rsz(ext_mem->rsz_sdr_ptr_s_c, reg_base + RSZ_SDR_C_PTR_S); + regw_rsz((ext_mem->rsz_sdr_ptr_e_c >> 1), reg_base + RSZ_SDR_C_PTR_E); +} + +/*set the registers of either RSZ0 or RSZ1 */ +static void ipipe_setup_resizer(struct ipipe_params *params) +{ + /* enable MMR gate to write to Resizer */ + regw_rsz(1, RSZ_GCK_MMR); + + /* Enable resizer if it is not in bypass mode */ + if (params->rsz_common.passthrough) + regw_rsz(0, RSZ_GCK_SDR); + else + regw_rsz(1, RSZ_GCK_SDR); + + rsz_set_common_params(params); + + regw_rsz(params->rsz_en[RSZ_A], RSZ_EN_A); + if (params->rsz_en[RSZ_A]) + /*setting rescale parameters */ + rsz_set_rsz_regs(RSZ_A, params); + + regw_rsz(params->rsz_en[RSZ_B], RSZ_EN_B); + if (params->rsz_en[RSZ_B]) + rsz_set_rsz_regs(RSZ_B, params); + + regr_rsz(RSZ_SRC_MODE); +} + +/* ipipe_hw_setup:It is used for Hardware Setup */ +int ipipe_hw_setup(struct ipipe_params *config) +{ + u32 data_format; + u32 val; + + if (!config) { + printk(KERN_ERR "NULL config block received\n"); + return -EINVAL; + } + + if (ipipeif_hw_setup(&config->ipipeif_param, DM365) < 0) { + printk(KERN_ERR "Unable to configure IPIPEIF"); + return -EINVAL; + } + + /* enable clock to IPIPE */ + vpss_enable_clock(VPSS_IPIPE_CLOCK, 1); + /* enable clock to MMR and modules before writting + * to ipipe registers + */ + ipipe_clock_enable(); + + if (config->rsz_common.source == IPIPEIF_DATA) { + /* we need to skip configuring IPIPE */ + regw_ip(0, IPIPE_SRC_EN); + } else { + /* enable ipipe mode to either one shot or continuous */ + val = config->ipipe_mode; + regw_ip(val, IPIPE_SRC_MODE); + data_format = config->ipipe_dpaths_fmt; + regw_ip(data_format, IPIPE_SRC_FMT); + /* set size */ + regw_ip(config->ipipe_vps & IPIPE_RSZ_VPS_MASK, IPIPE_SRC_VPS); + regw_ip(config->ipipe_hps & IPIPE_RSZ_HPS_MASK, IPIPE_SRC_HPS); + regw_ip(config->ipipe_vsz & IPIPE_RSZ_VSZ_MASK, IPIPE_SRC_VSZ); + regw_ip(config->ipipe_hsz & IPIPE_RSZ_HSZ_MASK, IPIPE_SRC_HSZ); + + if (data_format == IPIPE_RAW2YUV || + data_format == IPIPE_RAW2RAW) { + /* Combine all the fields to make COLPAT register + * of IPIPE + */ + val = config->ipipe_colpat_elep << COLPAT_EE_SHIFT; + val |= config->ipipe_colpat_elop << COLPAT_EO_SHIFT; + val |= config->ipipe_colpat_olep << COLPAT_OE_SHIFT; + val |= config->ipipe_colpat_olop << COLPAT_OO_SHIFT; + regw_ip(val, IPIPE_SRC_COL); + } + } + ipipe_setup_resizer(config); + + return 0; +} + +static void rsz_set_y_address(unsigned int address, unsigned int offset) +{ + u32 val; + + val = address & SET_LOW_ADD; + regw_rsz(val, offset + RSZ_SDR_Y_BAD_L); + regw_rsz(val, offset + RSZ_SDR_Y_SAD_L); + val = (address & SET_HIGH_ADD) >> 16; + regw_rsz(val, offset + RSZ_SDR_Y_BAD_H); + regw_rsz(val, offset + RSZ_SDR_Y_SAD_H); +} + +static void rsz_set_c_address(unsigned int address, unsigned int offset) +{ + u32 val; + + val = address & SET_LOW_ADD; + + regw_rsz(val, offset + RSZ_SDR_C_BAD_L); + regw_rsz(val, offset + RSZ_SDR_C_SAD_L); + val = (address & SET_HIGH_ADD) >> 16; + regw_rsz(val, offset + RSZ_SDR_C_BAD_H); + regw_rsz(val, offset + RSZ_SDR_C_SAD_H); +} + +/* Assume we get a valid params ptr and resize_no set to RSZ_A + * or RSZ_B. This could be called in the interrupt context and + * must be efficient + */ +void rsz_set_output_address(struct ipipe_params *params, + int resize_no, unsigned int address) +{ + unsigned int rsz_start_add; + unsigned int val; + + struct ipipe_ext_mem_param *mem_param = + ¶ms->ext_mem_param[resize_no]; + struct rsz_common_params *rsz_common = + ¶ms->rsz_common; + struct ipipe_rsz_rescale_param *rsc_param = + ¶ms->rsz_rsc_param[resize_no]; + + if (resize_no == RSZ_A) + rsz_start_add = RSZ_EN_A; + else + rsz_start_add = RSZ_EN_B; + /* y_c = 0 for y, = 1 for c */ + if (rsz_common->src_img_fmt == RSZ_IMG_420) { + if (rsz_common->y_c) { + /* C channel */ + val = address + mem_param->flip_ofst_c; + rsz_set_c_address(val, rsz_start_add); + } else { + val = address + mem_param->flip_ofst_y; + rsz_set_y_address(val, rsz_start_add); + } + } else { + if (rsc_param->cen && rsc_param->yen) { + /* 420 */ + val = address + mem_param->c_offset; + val = address + mem_param->c_offset + + mem_param->flip_ofst_c; + val += mem_param->user_y_ofst + + mem_param->user_c_ofst; + if (resize_no == RSZ_B) + val += + params->ext_mem_param[RSZ_A].user_y_ofst + + params->ext_mem_param[RSZ_A].user_c_ofst; + /* set C address */ + rsz_set_c_address(val, rsz_start_add); + } + val = address + mem_param->flip_ofst_y; + val += mem_param->user_y_ofst; + if (resize_no == RSZ_B) + val += params->ext_mem_param[RSZ_A].user_y_ofst + + params->ext_mem_param[RSZ_A].user_c_ofst; + /* set Y address */ + rsz_set_y_address(val, rsz_start_add); + } + /* resizer must be enabled */ + regw_rsz(params->rsz_en[resize_no], rsz_start_add); + +} + +void ipipe_set_lutdpc_regs(struct prev_lutdpc *dpc) +{ + u32 max_tbl_size = LUT_DPC_MAX_SIZE >> 1; + u32 lut_start_addr = DPC_TB0_START_ADDR; + u32 val; + u32 count; + + ipipe_clock_enable(); + regw_ip(dpc->en, DPC_LUT_EN); + if (dpc->en != 1) + return; + + /* if dpc is enabled */ + val = LUTDPC_TBL_256_EN; + val |= dpc->repl_white & 1; + regw_ip(val, DPC_LUT_SEL); + regw_ip(LUT_DPC_START_ADDR, DPC_LUT_ADR); + regw_ip(dpc->dpc_size, DPC_LUT_SIZ & LUT_DPC_SIZE_MASK); + + if (dpc->table == NULL) + return; + + for (count = 0; count < dpc->dpc_size; count++) { + if (count >= max_tbl_size) + lut_start_addr = DPC_TB1_START_ADDR; + val = dpc->table[count].horz_pos & LUT_DPC_H_POS_MASK; + val |= (dpc->table[count].vert_pos & LUT_DPC_V_POS_MASK) << + LUT_DPC_V_POS_SHIFT; + val |= dpc->table[count].method << LUT_DPC_CORR_METH_SHIFT; + w_ip_table(val, (lut_start_addr + + ((count % max_tbl_size) << 2))); + } +} + +static void set_dpc_thresholds(struct prev_otfdpc_2_0 *dpc_thr) +{ + regw_ip((dpc_thr->corr_thr.r & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2C_THR_R); + regw_ip((dpc_thr->corr_thr.gr & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2C_THR_GR); + regw_ip((dpc_thr->corr_thr.gb & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2C_THR_GB); + regw_ip((dpc_thr->corr_thr.b & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2C_THR_B); + regw_ip((dpc_thr->det_thr.r & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2D_THR_R); + regw_ip((dpc_thr->det_thr.gr & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2D_THR_GR); + regw_ip((dpc_thr->det_thr.gb & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2D_THR_GB); + regw_ip((dpc_thr->det_thr.b & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2D_THR_B); +} + +void ipipe_set_otfdpc_regs(struct prev_otfdpc *otfdpc) +{ + struct prev_otfdpc_2_0 *dpc_2_0 = &otfdpc->alg_cfg.dpc_2_0; + struct prev_otfdpc_3_0 *dpc_3_0 = &otfdpc->alg_cfg.dpc_3_0; + u32 val; + + ipipe_clock_enable(); + + regw_ip((otfdpc->en & 1), DPC_OTF_EN); + if (otfdpc->en != 1) + return; + + /* dpc enabled */ + val = otfdpc->det_method << OTF_DET_METHOD_SHIFT; + val |= otfdpc->alg; + regw_ip(val, DPC_OTF_TYP); + if (otfdpc->det_method == IPIPE_DPC_OTF_MIN_MAX) { + /* ALG= 0, TYP = 0, DPC_OTF_2D_THR_[x]=0 + * DPC_OTF_2C_THR_[x] = Maximum thresohld + * MinMax method + */ + dpc_2_0->det_thr.r = dpc_2_0->det_thr.gb = + dpc_2_0->det_thr.gr = dpc_2_0->det_thr.b = 0; + set_dpc_thresholds(dpc_2_0); + return; + } + /* MinMax2 */ + if (otfdpc->alg == IPIPE_OTFDPC_2_0) { + set_dpc_thresholds(dpc_2_0); + return; + } + regw_ip((dpc_3_0->act_adj_shf & OTF_DPC3_0_SHF_MASK), DPC_OTF_3_SHF); + /* Detection thresholds */ + regw_ip(((dpc_3_0->det_thr & OTF_DPC3_0_THR_MASK) << + OTF_DPC3_0_THR_SHIFT), DPC_OTF_3D_THR); + regw_ip((dpc_3_0->det_slp & OTF_DPC3_0_SLP_MASK), DPC_OTF_3D_SLP); + regw_ip((dpc_3_0->det_thr_min & OTF_DPC3_0_DET_MASK), DPC_OTF_3D_MIN); + regw_ip((dpc_3_0->det_thr_max & OTF_DPC3_0_DET_MASK), DPC_OTF_3D_MAX); + /* Correction thresholds */ + regw_ip(((dpc_3_0->corr_thr & OTF_DPC3_0_THR_MASK) << + OTF_DPC3_0_THR_SHIFT), DPC_OTF_3C_THR); + regw_ip((dpc_3_0->corr_slp & OTF_DPC3_0_SLP_MASK), DPC_OTF_3C_SLP); + regw_ip((dpc_3_0->corr_thr_min & OTF_DPC3_0_CORR_MASK), DPC_OTF_3C_MIN); + regw_ip((dpc_3_0->corr_thr_max & OTF_DPC3_0_CORR_MASK), DPC_OTF_3C_MAX); +} + +/* 2D Noise filter */ +void ipipe_set_d2f_regs(unsigned int id, struct prev_nf *noise_filter) +{ + + u32 offset = D2F_1ST; + int count; + u32 val; + + /* id = 0 , NF1 & id = 1, NF 2 */ + if (id) + offset = D2F_2ND; + ipipe_clock_enable(); + regw_ip(noise_filter->en & 1, offset + D2F_EN); + if (noise_filter->en != 1) + return; + + /*noise filter enabled */ + /* Combine all the fields to make D2F_CFG register of IPIPE */ + val = ((noise_filter->spread_val & D2F_SPR_VAL_MASK) << + D2F_SPR_VAL_SHIFT) | + ((noise_filter->shft_val & D2F_SHFT_VAL_MASK) << + D2F_SHFT_VAL_SHIFT) | + (noise_filter->gr_sample_meth << + D2F_SAMPLE_METH_SHIFT) | + ((noise_filter->apply_lsc_gain & 1) << + D2F_APPLY_LSC_GAIN_SHIFT) | D2F_USE_SPR_REG_VAL; + + regw_ip(val, offset + D2F_TYP); + /* edge detection minimum */ + regw_ip(noise_filter->edge_det_min_thr & D2F_EDGE_DET_THR_MASK, + offset + D2F_EDG_MIN); + /* edge detection maximum */ + regw_ip(noise_filter->edge_det_max_thr & D2F_EDGE_DET_THR_MASK, + offset + D2F_EDG_MAX); + for (count = 0; count < IPIPE_NF_STR_TABLE_SIZE; count++) { + regw_ip((noise_filter->str[count] & D2F_STR_VAL_MASK), + offset + D2F_STR + count * 4); + + } + for (count = 0; count < IPIPE_NF_THR_TABLE_SIZE; count++) { + regw_ip(noise_filter->thr[count] & D2F_THR_VAL_MASK, + offset + D2F_THR + count * 4); + } +} + +#define IPIPE_U8Q5(decimal, integer) \ + (((decimal & 0x1f) | ((integer & 0x7) << 5))) + +/* Green Imbalance Correction */ +void ipipe_set_gic_regs(struct prev_gic *gic) +{ + u32 val; + + ipipe_clock_enable(); + regw_ip(gic->en & 1, GIC_EN); + + if (!gic->en) + return; + + /*gic enabled */ + val = gic->wt_fn_type << GIC_TYP_SHIFT; + val |= gic->thr_sel << GIC_THR_SEL_SHIFT; + val |= (gic->apply_lsc_gain & 1) << GIC_APPLY_LSC_GAIN_SHIFT; + regw_ip(val, GIC_TYP); + regw_ip(gic->gain & GIC_GAIN_MASK, GIC_GAN); + + if (gic->gic_alg != IPIPE_GIC_ALG_ADAPT_GAIN) { + /* Constant Gain. Set threshold to maximum */ + regw_ip(GIC_THR_MASK, GIC_THR); + return; + } + + if (gic->thr_sel == IPIPE_GIC_THR_REG) { + regw_ip(gic->thr & GIC_THR_MASK, GIC_THR); + regw_ip(gic->slope & GIC_SLOPE_MASK, GIC_SLP); + } else { + /* Use NF thresholds */ + val = IPIPE_U8Q5(gic->nf2_thr_gain.decimal, + gic->nf2_thr_gain.integer); + regw_ip(val, GIC_NFGAN); + } +} + +#define IPIPE_U13Q9(decimal, integer) \ + (((decimal & 0x1ff) | ((integer & 0xf) << 9))) +/* White balance */ +void ipipe_set_wb_regs(struct prev_wb *wb) +{ + u32 val; + + ipipe_clock_enable(); + /* Ofsets. S12 */ + regw_ip(wb->ofst_r & WB_OFFSET_MASK, WB2_OFT_R); + regw_ip(wb->ofst_gr & WB_OFFSET_MASK, WB2_OFT_GR); + regw_ip(wb->ofst_gb & WB_OFFSET_MASK, WB2_OFT_GB); + regw_ip(wb->ofst_b & WB_OFFSET_MASK, WB2_OFT_B); + + /* Gains. U13Q9 */ + val = IPIPE_U13Q9(wb->gain_r.decimal, wb->gain_r.integer); + regw_ip(val, WB2_WGN_R); + val = IPIPE_U13Q9(wb->gain_gr.decimal, wb->gain_gr.integer); + regw_ip(val, WB2_WGN_GR); + val = IPIPE_U13Q9(wb->gain_gb.decimal, wb->gain_gb.integer); + regw_ip(val, WB2_WGN_GB); + val = IPIPE_U13Q9(wb->gain_b.decimal, wb->gain_b.integer); + regw_ip(val, WB2_WGN_B); +} + +/* CFA */ +void ipipe_set_cfa_regs(struct prev_cfa *cfa) +{ + ipipe_clock_enable(); + regw_ip(cfa->alg, CFA_MODE); + regw_ip(cfa->hpf_thr_2dir & CFA_HPF_THR_2DIR_MASK, CFA_2DIR_HPF_THR); + regw_ip(cfa->hpf_slp_2dir & CFA_HPF_SLOPE_2DIR_MASK, CFA_2DIR_HPF_SLP); + regw_ip(cfa->hp_mix_thr_2dir & CFA_HPF_MIX_THR_2DIR_MASK, + CFA_2DIR_MIX_THR); + regw_ip(cfa->hp_mix_slope_2dir & CFA_HPF_MIX_SLP_2DIR_MASK, + CFA_2DIR_MIX_SLP); + regw_ip(cfa->dir_thr_2dir & CFA_DIR_THR_2DIR_MASK, CFA_2DIR_DIR_THR); + regw_ip(cfa->dir_slope_2dir & CFA_DIR_SLP_2DIR_MASK, CFA_2DIR_DIR_SLP); + regw_ip(cfa->nd_wt_2dir & CFA_ND_WT_2DIR_MASK, CFA_2DIR_NDWT); + regw_ip(cfa->hue_fract_daa & CFA_DAA_HUE_FRA_MASK, CFA_MONO_HUE_FRA); + regw_ip(cfa->edge_thr_daa & CFA_DAA_EDG_THR_MASK, CFA_MONO_EDG_THR); + regw_ip(cfa->thr_min_daa & CFA_DAA_THR_MIN_MASK, CFA_MONO_THR_MIN); + regw_ip(cfa->thr_slope_daa & CFA_DAA_THR_SLP_MASK, CFA_MONO_THR_SLP); + regw_ip(cfa->slope_min_daa & CFA_DAA_SLP_MIN_MASK, CFA_MONO_SLP_MIN); + regw_ip(cfa->slope_slope_daa & CFA_DAA_SLP_SLP_MASK, CFA_MONO_SLP_SLP); + regw_ip(cfa->lp_wt_daa & CFA_DAA_LP_WT_MASK, CFA_MONO_LPWT); +} + +void ipipe_set_rgb2rgb_regs(unsigned int id, struct prev_rgb2rgb *rgb) +{ + u32 offset_mask = RGB2RGB_1_OFST_MASK; + u32 offset = RGB1_MUL_BASE; + u32 integ_mask = 0xf; + u32 val; + + ipipe_clock_enable(); + + if (id) { + /* For second RGB module, gain integer is 3 bits instead + of 4, offset has 11 bits insread of 13 */ + offset = RGB2_MUL_BASE; + integ_mask = 0x7; + offset_mask = RGB2RGB_2_OFST_MASK; + } + /* Gains */ + val = (rgb->coef_rr.decimal & 0xff) | + ((rgb->coef_rr.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_RR); + val = (rgb->coef_gr.decimal & 0xff) | + ((rgb->coef_gr.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_GR); + val = (rgb->coef_br.decimal & 0xff) | + ((rgb->coef_br.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_BR); + val = (rgb->coef_rg.decimal & 0xff) | + ((rgb->coef_rg.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_RG); + val = (rgb->coef_gg.decimal & 0xff) | + ((rgb->coef_gg.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_GG); + val = (rgb->coef_bg.decimal & 0xff) | + ((rgb->coef_bg.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_BG); + val = (rgb->coef_rb.decimal & 0xff) | + ((rgb->coef_rb.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_RB); + val = (rgb->coef_gb.decimal & 0xff) | + ((rgb->coef_gb.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_GB); + val = (rgb->coef_bb.decimal & 0xff) | + ((rgb->coef_bb.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_BB); + + /* Offsets */ + regw_ip(rgb->out_ofst_r & offset_mask, offset + RGB_OFT_OR); + regw_ip(rgb->out_ofst_g & offset_mask, offset + RGB_OFT_OG); + regw_ip(rgb->out_ofst_b & offset_mask, offset + RGB_OFT_OB); +} + +static void ipipe_update_gamma_tbl(struct ipipe_gamma_entry *table, + int size, u32 addr) +{ + int count; + u32 val; + + for (count = 0; count < size; count++) { + val = table[count].slope & GAMMA_MASK; + val |= (table[count].offset & GAMMA_MASK) << GAMMA_SHIFT; + w_ip_table(val, (addr + (count * 4))); + } +} + +/* Gamma correction */ +void ipipe_set_gamma_regs(struct prev_gamma *gamma) +{ + int table_size; + u32 val; + + ipipe_clock_enable(); + val = (gamma->bypass_r << GAMMA_BYPR_SHIFT) | + (gamma->bypass_b << GAMMA_BYPG_SHIFT) | + (gamma->bypass_g << GAMMA_BYPB_SHIFT) | + (gamma->tbl_sel << GAMMA_TBL_SEL_SHIFT) | + (gamma->tbl_size << GAMMA_TBL_SIZE_SHIFT); + + regw_ip(val, GMM_CFG); + + if (gamma->tbl_sel != IPIPE_GAMMA_TBL_RAM) + return; + + table_size = gamma->tbl_size; + + if (!gamma->bypass_r && gamma->table_r != NULL) { + ipipe_update_gamma_tbl(gamma->table_r, table_size, + GAMMA_R_START_ADDR); + } + if (!gamma->bypass_b && gamma->table_b != NULL) { + ipipe_update_gamma_tbl(gamma->table_b, table_size, + GAMMA_B_START_ADDR); + } + if (!gamma->bypass_g && gamma->table_g != NULL) { + ipipe_update_gamma_tbl(gamma->table_g, table_size, + GAMMA_G_START_ADDR); + } +} + +/* 3D LUT */ +void ipipe_set_3d_lut_regs(struct prev_3d_lut *lut_3d) +{ + struct ipipe_3d_lut_entry *tbl; + u32 bnk_index; + u32 tbl_index; + u32 val; + u32 i; + + ipipe_clock_enable(); + regw_ip(lut_3d->en, D3LUT_EN); + + if (!lut_3d->en) + return; + + /* lut_3d enabled */ + if (!lut_3d->table) + return; + + /* valied table */ + tbl = lut_3d->table; + for (i = 0 ; i < MAX_SIZE_3D_LUT; i++) { + /* Each entry has 0-9 (B), 10-19 (G) and + 20-29 R values */ + val = tbl[i].b & D3_LUT_ENTRY_MASK; + val |= (tbl[i].g & D3_LUT_ENTRY_MASK) << + D3_LUT_ENTRY_G_SHIFT; + val |= (tbl[i].r & D3_LUT_ENTRY_MASK) << + D3_LUT_ENTRY_R_SHIFT; + bnk_index = i % 4; + tbl_index = i >> 2; + tbl_index <<= 2; + if (bnk_index == 0) + w_ip_table(val, tbl_index + D3L_TB0_START_ADDR); + else if (bnk_index == 1) + w_ip_table(val, tbl_index + D3L_TB1_START_ADDR); + else if (bnk_index == 2) + w_ip_table(val, tbl_index + D3L_TB2_START_ADDR); + else + w_ip_table(val, tbl_index + D3L_TB3_START_ADDR); + } +} + +/* Lumina adjustments */ +void ipipe_set_lum_adj_regs(struct prev_lum_adj *lum_adj) +{ + u32 val; + + ipipe_clock_enable(); + /* combine fields of YUV_ADJ to set brightness and contrast */ + val = lum_adj->contrast << LUM_ADJ_CONTR_SHIFT | + lum_adj->brightness << LUM_ADJ_BRIGHT_SHIFT; + regw_ip(val, YUV_ADJ); +} + +#define IPIPE_S12Q8(decimal, integer) \ + (((decimal & 0xff) | ((integer & 0xf) << 8))) +/* RGB2YUV */ +void ipipe_set_rgb2ycbcr_regs(struct prev_rgb2yuv *yuv) +{ + u32 val; + + /* S10Q8 */ + ipipe_clock_enable(); + val = IPIPE_S12Q8(yuv->coef_ry.decimal, yuv->coef_ry.integer); + regw_ip(val, YUV_MUL_RY); + val = IPIPE_S12Q8(yuv->coef_gy.decimal, yuv->coef_gy.integer); + regw_ip(val, YUV_MUL_GY); + val = IPIPE_S12Q8(yuv->coef_by.decimal, yuv->coef_by.integer); + regw_ip(val, YUV_MUL_BY); + val = IPIPE_S12Q8(yuv->coef_rcb.decimal, yuv->coef_rcb.integer); + regw_ip(val, YUV_MUL_RCB); + val = IPIPE_S12Q8(yuv->coef_gcb.decimal, yuv->coef_gcb.integer); + regw_ip(val, YUV_MUL_GCB); + val = IPIPE_S12Q8(yuv->coef_bcb.decimal, yuv->coef_bcb.integer); + regw_ip(val, YUV_MUL_BCB); + val = IPIPE_S12Q8(yuv->coef_rcr.decimal, yuv->coef_rcr.integer); + regw_ip(val, YUV_MUL_RCR); + val = IPIPE_S12Q8(yuv->coef_gcr.decimal, yuv->coef_gcr.integer); + regw_ip(val, YUV_MUL_GCR); + val = IPIPE_S12Q8(yuv->coef_bcr.decimal, yuv->coef_bcr.integer); + regw_ip(val, YUV_MUL_BCR); + regw_ip(yuv->out_ofst_y & RGB2YCBCR_OFST_MASK, YUV_OFT_Y); + regw_ip(yuv->out_ofst_cb & RGB2YCBCR_OFST_MASK, YUV_OFT_CB); + regw_ip(yuv->out_ofst_cr & RGB2YCBCR_OFST_MASK, YUV_OFT_CR); +} + +/* YUV 422 conversion */ +void ipipe_set_yuv422_conv_regs(struct prev_yuv422_conv *conv) +{ + u32 val; + + ipipe_clock_enable(); + /* Combine all the fields to make YUV_PHS register of IPIPE */ + val = (conv->chrom_pos << 0) | (conv->en_chrom_lpf << 1); + regw_ip(val, YUV_PHS); +} + +/* GBCE */ +void ipipe_set_gbce_regs(struct prev_gbce *gbce) +{ + unsigned int tbl_index; + unsigned int count; + u32 mask = GBCE_Y_VAL_MASK; + u32 val; + + if (gbce->type == IPIPE_GBCE_GAIN_TBL) + mask = GBCE_GAIN_VAL_MASK; + + ipipe_clock_enable(); + regw_ip(gbce->en & 1, GBCE_EN); + + if (!gbce->en) + return; + + regw_ip(gbce->type, GBCE_TYP); + + if (!gbce->table) + return; + + /* set to 0 */ + val = 0; + + for (count = 0; count < MAX_SIZE_GBCE_LUT; count++) { + tbl_index = count >> 1; + tbl_index <<= 2; + /* Each table has 2 LUT entries, first in LS + * and second in MS positions + */ + if (count % 2) { + val |= + (gbce->table[count] & mask) << + GBCE_ENTRY_SHIFT; + w_ip_table(val, tbl_index + GBCE_TB_START_ADDR); + } else { + val = gbce->table[count] & mask; + } + } +} + +/* Edge Enhancement */ +void ipipe_set_ee_regs(struct prev_yee *ee) +{ + unsigned int tbl_index; + unsigned int count; + u32 val; + + ipipe_clock_enable(); + regw_ip(ee->en, YEE_EN); + + if (!ee->en) + return; + + val = ee->en_halo_red & 1; + val |= ee->merge_meth << YEE_HALO_RED_EN_SHIFT; + regw_ip(val, YEE_TYP); + regw_ip(ee->hpf_shft, YEE_SHF); + regw_ip(ee->hpf_coef_00 & YEE_COEF_MASK, YEE_MUL_00); + regw_ip(ee->hpf_coef_01 & YEE_COEF_MASK, YEE_MUL_01); + regw_ip(ee->hpf_coef_02 & YEE_COEF_MASK, YEE_MUL_02); + regw_ip(ee->hpf_coef_10 & YEE_COEF_MASK, YEE_MUL_10); + regw_ip(ee->hpf_coef_11 & YEE_COEF_MASK, YEE_MUL_11); + regw_ip(ee->hpf_coef_12 & YEE_COEF_MASK, YEE_MUL_12); + regw_ip(ee->hpf_coef_20 & YEE_COEF_MASK, YEE_MUL_20); + regw_ip(ee->hpf_coef_21 & YEE_COEF_MASK, YEE_MUL_21); + regw_ip(ee->hpf_coef_22 & YEE_COEF_MASK, YEE_MUL_22); + regw_ip(ee->yee_thr & YEE_THR_MASK, YEE_THR); + regw_ip(ee->es_gain & YEE_ES_GAIN_MASK, YEE_E_GAN); + regw_ip(ee->es_thr1 & YEE_ES_THR1_MASK, YEE_E_THR1); + regw_ip(ee->es_thr2 & YEE_THR_MASK, YEE_E_THR2); + regw_ip(ee->es_gain_grad & YEE_THR_MASK, YEE_G_GAN); + regw_ip(ee->es_ofst_grad & YEE_THR_MASK, YEE_G_OFT); + + if (ee->table == NULL) + return; + + for (count = 0; count < MAX_SIZE_YEE_LUT; count++) { + tbl_index = count >> 1; + tbl_index <<= 2; + /* Each table has 2 LUT entries, first in LS + * and second in MS positions + */ + if (count % 2) { + val |= (ee->table[count] & YEE_ENTRY_MASK) << + YEE_ENTRY_SHIFT; + w_ip_table(val, tbl_index + YEE_TB_START_ADDR); + } else { + val = ee->table[count] & YEE_ENTRY_MASK; + } + } +} + +/* Chromatic Artifact Correction. CAR */ +static void ipipe_set_mf(void) +{ + /* typ to dynamic switch */ + regw_ip(IPIPE_CAR_DYN_SWITCH, CAR_TYP); + /* Set SW0 to maximum */ + regw_ip(CAR_MF_THR, CAR_SW); +} + +static void ipipe_set_gain_ctrl(struct prev_car *car) +{ + regw_ip(IPIPE_CAR_CHR_GAIN_CTRL, CAR_TYP); + regw_ip(car->hpf, CAR_HPF_TYP); + regw_ip(car->hpf_shft & CAR_HPF_SHIFT_MASK, CAR_HPF_SHF); + regw_ip(car->hpf_thr, CAR_HPF_THR); + regw_ip(car->gain1.gain, CAR_GN1_GAN); + regw_ip(car->gain1.shft & CAR_GAIN1_SHFT_MASK, CAR_GN1_SHF); + regw_ip(car->gain1.gain_min & CAR_GAIN_MIN_MASK, CAR_GN1_MIN); + regw_ip(car->gain2.gain, CAR_GN2_GAN); + regw_ip(car->gain2.shft & CAR_GAIN2_SHFT_MASK, CAR_GN2_SHF); + regw_ip(car->gain2.gain_min & CAR_GAIN_MIN_MASK, CAR_GN2_MIN); +} + +void ipipe_set_car_regs(struct prev_car *car) +{ + u32 val; + + ipipe_clock_enable(); + regw_ip(car->en, CAR_EN); + + if (!car->en) + return; + + switch (car->meth) { + case IPIPE_CAR_MED_FLTR: + ipipe_set_mf(); + break; + case IPIPE_CAR_CHR_GAIN_CTRL: + ipipe_set_gain_ctrl(car); + break; + default: + /* Dynamic switch between MF and Gain Ctrl. */ + ipipe_set_mf(); + ipipe_set_gain_ctrl(car); + /* Set the threshold for switching between + * the two Here we overwrite the MF SW0 value + */ + regw_ip(IPIPE_CAR_DYN_SWITCH, CAR_TYP); + val = car->sw1; + val <<= CAR_SW1_SHIFT; + val |= car->sw0; + regw_ip(val, CAR_SW); + } +} + +/* Chromatic Gain Suppression */ +void ipipe_set_cgs_regs(struct prev_cgs *cgs) +{ + ipipe_clock_enable(); + regw_ip(cgs->en, CGS_EN); + + if (!cgs->en) + return; + + /* Set the bright side parameters */ + regw_ip(cgs->h_thr, CGS_GN1_H_THR); + regw_ip(cgs->h_slope, CGS_GN1_H_GAN); + regw_ip(cgs->h_shft & CAR_SHIFT_MASK, CGS_GN1_H_SHF); + regw_ip(cgs->h_min, CGS_GN1_H_MIN); +} + +void rsz_src_enable(int enable) +{ + regw_rsz(enable, RSZ_SRC_EN); +} + +int rsz_enable(int rsz_id, int enable) +{ + if (rsz_id == RSZ_A) { + regw_rsz(enable, RSZ_EN_A); + /* We always enable RSZ_A. RSZ_B is enable upon request from + * application. So enable RSZ_SRC_EN along with RSZ_A + */ + regw_rsz(enable, RSZ_SRC_EN); + } else if (rsz_id == RSZ_B) { + regw_rsz(enable, RSZ_EN_B); + } else { + return -EINVAL; + } + + return 0; +} diff --git a/drivers/media/video/davinci/dm365_ipipe_hw.h b/drivers/media/video/davinci/dm365_ipipe_hw.h new file mode 100644 index 0000000..a999b8a --- /dev/null +++ b/drivers/media/video/davinci/dm365_ipipe_hw.h @@ -0,0 +1,539 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef _DM365_IPIPE_HW_H +#define _DM365_IPIPE_HW_H + +#include +#include + +#define IPIPE_IOBASE_VADDR IO_ADDRESS(0x01c70800) +#define RSZ_IOBASE_VADDR IO_ADDRESS(0x01c70400) +#define IPIPE_INT_TABLE_IOBASE_VADDR IO_ADDRESS(0x01c70000) + +#define SET_LOW_ADD 0x0000ffff +#define SET_HIGH_ADD 0xffff0000 + +/* Below are the internal tables */ +#define DPC_TB0_START_ADDR 0x8000 +#define DPC_TB1_START_ADDR 0x8400 + +#define GAMMA_R_START_ADDR 0xa800 +#define GAMMA_G_START_ADDR 0xb000 +#define GAMMA_B_START_ADDR 0xb800 + +/* RAM table addresses for edge enhancement correction*/ +#define YEE_TB_START_ADDR 0x8800 + +/* RAM table address for GBC LUT */ +#define GBCE_TB_START_ADDR 0x9000 + +/* RAM table for 3D NF LUT */ +#define D3L_TB0_START_ADDR 0x9800 +#define D3L_TB1_START_ADDR 0x9c00 +#define D3L_TB2_START_ADDR 0xa000 +#define D3L_TB3_START_ADDR 0xa400 + +/* IPIPE Register Offsets from the base address */ +#define IPIPE_SRC_EN 0x0000 +#define IPIPE_SRC_MODE 0x0004 +#define IPIPE_SRC_FMT 0x0008 +#define IPIPE_SRC_COL 0x000c +#define IPIPE_SRC_VPS 0x0010 +#define IPIPE_SRC_VSZ 0x0014 +#define IPIPE_SRC_HPS 0x0018 +#define IPIPE_SRC_HSZ 0x001c + +#define IPIPE_SEL_SBU 0x0020 + +#define IPIPE_DMA_STA 0x0024 +#define IPIPE_GCK_MMR 0x0028 +#define IPIPE_GCK_PIX 0x002c +#define IPIPE_RESERVED0 0x0030 + +/* Defect Correction */ +#define DPC_LUT_EN 0x0034 +#define DPC_LUT_SEL 0x0038 +#define DPC_LUT_ADR 0x003c +#define DPC_LUT_SIZ 0x0040 +#define DPC_OTF_EN 0x0044 +#define DPC_OTF_TYP 0x0048 +#define DPC_OTF_2D_THR_R 0x004c +#define DPC_OTF_2D_THR_GR 0x0050 +#define DPC_OTF_2D_THR_GB 0x0054 +#define DPC_OTF_2D_THR_B 0x0058 +#define DPC_OTF_2C_THR_R 0x005c +#define DPC_OTF_2C_THR_GR 0x0060 +#define DPC_OTF_2C_THR_GB 0x0064 +#define DPC_OTF_2C_THR_B 0x0068 +#define DPC_OTF_3_SHF 0x006c +#define DPC_OTF_3D_THR 0x0070 +#define DPC_OTF_3D_SLP 0x0074 +#define DPC_OTF_3D_MIN 0x0078 +#define DPC_OTF_3D_MAX 0x007c +#define DPC_OTF_3C_THR 0x0080 +#define DPC_OTF_3C_SLP 0x0084 +#define DPC_OTF_3C_MIN 0x0088 +#define DPC_OTF_3C_MAX 0x008c + +/* Lense Shading Correction */ +#define LSC_VOFT 0x90 +#define LSC_VA2 0x94 +#define LSC_VA1 0x98 +#define LSC_VS 0x9c +#define LSC_HOFT 0xa0 +#define LSC_HA2 0xa4 +#define LSC_HA1 0xa8 +#define LSC_HS 0xac +#define LSC_GAIN_R 0xb0 +#define LSC_GAIN_GR 0xb4 +#define LSC_GAIN_GB 0xb8 +#define LSC_GAIN_B 0xbc +#define LSC_OFT_R 0xc0 +#define LSC_OFT_GR 0xc4 +#define LSC_OFT_GB 0xc8 +#define LSC_OFT_B 0xcc +#define LSC_SHF 0xd0 +#define LSC_MAX 0xd4 + +/* Noise Filter 1. Ofsets from start address given */ +#define D2F_1ST 0xd8 +#define D2F_EN 0x0 +#define D2F_TYP 0x4 +#define D2F_THR 0x8 +#define D2F_STR 0x28 +#define D2F_SPR 0x48 +#define D2F_EDG_MIN 0x68 +#define D2F_EDG_MAX 0x6c + +/* Noise Filter 2 */ +#define D2F_2ND 0x148 + +/* GIC */ +#define GIC_EN 0x1b8 +#define GIC_TYP 0x1bc +#define GIC_GAN 0x1c0 +#define GIC_NFGAN 0x1c4 +#define GIC_THR 0x1c8 +#define GIC_SLP 0x1cc + +/* White Balance */ +#define WB2_OFT_R 0x1d0 +#define WB2_OFT_GR 0x1d4 +#define WB2_OFT_GB 0x1d8 +#define WB2_OFT_B 0x1dc +#define WB2_WGN_R 0x1e0 +#define WB2_WGN_GR 0x1e4 +#define WB2_WGN_GB 0x1e8 +#define WB2_WGN_B 0x1ec + +/* CFA interpolation */ +#define CFA_MODE 0x1f0 +#define CFA_2DIR_HPF_THR 0x1f4 +#define CFA_2DIR_HPF_SLP 0x1f8 +#define CFA_2DIR_MIX_THR 0x1fc +#define CFA_2DIR_MIX_SLP 0x200 +#define CFA_2DIR_DIR_THR 0x204 +#define CFA_2DIR_DIR_SLP 0x208 +#define CFA_2DIR_NDWT 0x20c +#define CFA_MONO_HUE_FRA 0x210 +#define CFA_MONO_EDG_THR 0x214 +#define CFA_MONO_THR_MIN 0x218 +#define CFA_MONO_THR_SLP 0x21c +#define CFA_MONO_SLP_MIN 0x220 +#define CFA_MONO_SLP_SLP 0x224 +#define CFA_MONO_LPWT 0x228 + +/* RGB to RGB conversiona - 1st */ +#define RGB1_MUL_BASE 0x22c +/* Offsets from base */ +#define RGB_MUL_RR 0x0 +#define RGB_MUL_GR 0x4 +#define RGB_MUL_BR 0x8 +#define RGB_MUL_RG 0xc +#define RGB_MUL_GG 0x10 +#define RGB_MUL_BG 0x14 +#define RGB_MUL_RB 0x18 +#define RGB_MUL_GB 0x1c +#define RGB_MUL_BB 0x20 +#define RGB_OFT_OR 0x24 +#define RGB_OFT_OG 0x28 +#define RGB_OFT_OB 0x2c + +/* Gamma */ +#define GMM_CFG 0x25c + +/* RGB to RGB conversiona - 2nd */ +#define RGB2_MUL_BASE 0x260 + +/* 3D LUT */ +#define D3LUT_EN 0x290 + +/* RGB to YUV(YCbCr) conversion */ +#define YUV_ADJ 0x294 +#define YUV_MUL_RY 0x298 +#define YUV_MUL_GY 0x29c +#define YUV_MUL_BY 0x2a0 +#define YUV_MUL_RCB 0x2a4 +#define YUV_MUL_GCB 0x2a8 +#define YUV_MUL_BCB 0x2ac +#define YUV_MUL_RCR 0x2b0 +#define YUV_MUL_GCR 0x2b4 +#define YUV_MUL_BCR 0x2b8 +#define YUV_OFT_Y 0x2bc +#define YUV_OFT_CB 0x2c0 +#define YUV_OFT_CR 0x2c4 +#define YUV_PHS 0x2c8 + +/* Global Brightness and Contrast */ +#define GBCE_EN 0x2cc +#define GBCE_TYP 0x2d0 + +/* Edge Enhancer */ +#define YEE_EN 0x2d4 +#define YEE_TYP 0x2d8 +#define YEE_SHF 0x2dc +#define YEE_MUL_00 0x2e0 +#define YEE_MUL_01 0x2e4 +#define YEE_MUL_02 0x2e8 +#define YEE_MUL_10 0x2ec +#define YEE_MUL_11 0x2f0 +#define YEE_MUL_12 0x2f4 +#define YEE_MUL_20 0x2f8 +#define YEE_MUL_21 0x2fc +#define YEE_MUL_22 0x300 +#define YEE_THR 0x304 +#define YEE_E_GAN 0x308 +#define YEE_E_THR1 0x30c +#define YEE_E_THR2 0x310 +#define YEE_G_GAN 0x314 +#define YEE_G_OFT 0x318 + +/* Chroma Artifact Reduction */ +#define CAR_EN 0x31c +#define CAR_TYP 0x320 +#define CAR_SW 0x324 +#define CAR_HPF_TYP 0x328 +#define CAR_HPF_SHF 0x32c +#define CAR_HPF_THR 0x330 +#define CAR_GN1_GAN 0x334 +#define CAR_GN1_SHF 0x338 +#define CAR_GN1_MIN 0x33c +#define CAR_GN2_GAN 0x340 +#define CAR_GN2_SHF 0x344 +#define CAR_GN2_MIN 0x348 + +/* Chroma Gain Suppression */ +#define CGS_EN 0x34c +#define CGS_GN1_L_THR 0x350 +#define CGS_GN1_L_GAN 0x354 +#define CGS_GN1_L_SHF 0x358 +#define CGS_GN1_L_MIN 0x35c +#define CGS_GN1_H_THR 0x360 +#define CGS_GN1_H_GAN 0x364 +#define CGS_GN1_H_SHF 0x368 +#define CGS_GN1_H_MIN 0x36c +#define CGS_GN2_L_THR 0x370 +#define CGS_GN2_L_GAN 0x374 +#define CGS_GN2_L_SHF 0x378 +#define CGS_GN2_L_MIN 0x37c + +/* Resizer */ +#define RSZ_SRC_EN 0x0 +#define RSZ_SRC_MODE 0x4 +#define RSZ_SRC_FMT0 0x8 +#define RSZ_SRC_FMT1 0xc +#define RSZ_SRC_VPS 0x10 +#define RSZ_SRC_VSZ 0x14 +#define RSZ_SRC_HPS 0x18 +#define RSZ_SRC_HSZ 0x1c +#define RSZ_DMA_RZA 0x20 +#define RSZ_DMA_RZB 0x24 +#define RSZ_DMA_STA 0x28 +#define RSZ_GCK_MMR 0x2c +#define RSZ_RESERVED0 0x30 +#define RSZ_GCK_SDR 0x34 +#define RSZ_IRQ_RZA 0x38 +#define RSZ_IRQ_RZB 0x3c +#define RSZ_YUV_Y_MIN 0x40 +#define RSZ_YUV_Y_MAX 0x44 +#define RSZ_YUV_C_MIN 0x48 +#define RSZ_YUV_C_MAX 0x4c +#define RSZ_YUV_PHS 0x50 +#define RSZ_SEQ 0x54 + +/* Resizer Rescale Parameters */ +#define RSZ_EN_A 0x58 +#define RSZ_EN_B 0xe8 +/* offset of the registers to be added with base register of + either RSZ0 or RSZ1 +*/ +#define RSZ_MODE 0x4 +#define RSZ_420 0x8 +#define RSZ_I_VPS 0xc +#define RSZ_I_HPS 0x10 +#define RSZ_O_VSZ 0x14 +#define RSZ_O_HSZ 0x18 +#define RSZ_V_PHS_Y 0x1c +#define RSZ_V_PHS_C 0x20 +#define RSZ_V_DIF 0x24 +#define RSZ_V_TYP 0x28 +#define RSZ_V_LPF 0x2c +#define RSZ_H_PHS 0x30 +#define RSZ_H_PHS_ADJ 0x34 +#define RSZ_H_DIF 0x38 +#define RSZ_H_TYP 0x3c +#define RSZ_H_LPF 0x40 +#define RSZ_DWN_EN 0x44 +#define RSZ_DWN_AV 0x48 + +/* Resizer RGB Conversion Parameters */ +#define RSZ_RGB_EN 0x4c +#define RSZ_RGB_TYP 0x50 +#define RSZ_RGB_BLD 0x54 + +/* Resizer External Memory Parameters */ +#define RSZ_SDR_Y_BAD_H 0x58 +#define RSZ_SDR_Y_BAD_L 0x5c +#define RSZ_SDR_Y_SAD_H 0x60 +#define RSZ_SDR_Y_SAD_L 0x64 +#define RSZ_SDR_Y_OFT 0x68 +#define RSZ_SDR_Y_PTR_S 0x6c +#define RSZ_SDR_Y_PTR_E 0x70 +#define RSZ_SDR_C_BAD_H 0x74 +#define RSZ_SDR_C_BAD_L 0x78 +#define RSZ_SDR_C_SAD_H 0x7c +#define RSZ_SDR_C_SAD_L 0x80 +#define RSZ_SDR_C_OFT 0x84 +#define RSZ_SDR_C_PTR_S 0x88 +#define RSZ_SDR_C_PTR_E 0x8c + +/* Macro for resizer */ +#define IPIPE_RESIZER_A(i) (RSZ_IOBASE_VADDR + RSZ_EN_A + i) +#define IPIPE_RESIZER_B(i) (RSZ_IOBASE_VADDR + RSZ_EN_B + i) + +#define RSZ_YUV_Y_MIN 0x40 +#define RSZ_YUV_Y_MAX 0x44 +#define RSZ_YUV_C_MIN 0x48 +#define RSZ_YUV_C_MAX 0x4c + +#define IPIPE_GCK_MMR_DEFAULT 1 +#define IPIPE_GCK_PIX_DEFAULT 0xe +#define RSZ_GCK_MMR_DEFAULT 1 +#define RSZ_GCK_SDR_DEFAULT 1 + +/* Below defines for masks and shifts */ +#define COLPAT_EE_SHIFT 0 +#define COLPAT_EO_SHIFT 2 +#define COLPAT_OE_SHIFT 4 +#define COLPAT_OO_SHIFT 6 + +/* LUTDPC */ +#define LUTDPC_TBL_256_EN 0 +#define LUTDPC_INF_TBL_EN 1 +#define LUT_DPC_START_ADDR 0 +#define LUT_DPC_H_POS_MASK 0x1fff +#define LUT_DPC_V_POS_MASK 0x1fff +#define LUT_DPC_V_POS_SHIFT 13 +#define LUT_DPC_CORR_METH_SHIFT 26 +#define LUT_DPC_MAX_SIZE 256 +#define LUT_DPC_SIZE_MASK 0x3ff + +/* OTFDPC */ +#define OTFDPC_DPC2_THR_MASK 0xfff +#define OTF_DET_METHOD_SHIFT 1 +#define OTF_DPC3_0_SHF_MASK 3 +#define OTF_DPC3_0_THR_SHIFT 6 +#define OTF_DPC3_0_THR_MASK 0x3f +#define OTF_DPC3_0_SLP_MASK 0x3f +#define OTF_DPC3_0_DET_MASK 0xfff +#define OTF_DPC3_0_CORR_MASK 0xfff + +/* NF (D2F) */ +#define D2F_SPR_VAL_MASK 0x1f +#define D2F_SPR_VAL_SHIFT 0 +#define D2F_SHFT_VAL_MASK 3 +#define D2F_SHFT_VAL_SHIFT 5 +#define D2F_SAMPLE_METH_SHIFT 7 +#define D2F_APPLY_LSC_GAIN_SHIFT 8 +#define D2F_USE_SPR_REG_VAL 0 +#define D2F_STR_VAL_MASK 0x1f +#define D2F_THR_VAL_MASK 0x3ff +#define D2F_EDGE_DET_THR_MASK 0x7ff + +/* Green Imbalance Correction */ +#define GIC_TYP_SHIFT 0 +#define GIC_THR_SEL_SHIFT 1 +#define GIC_APPLY_LSC_GAIN_SHIFT 2 +#define GIC_GAIN_MASK 0xff +#define GIC_THR_MASK 0xfff +#define GIC_SLOPE_MASK 0xfff +#define GIC_NFGAN_INT_MASK 7 +#define GIC_NFGAN_DECI_MASK 0x1f + +/* WB */ +#define WB_OFFSET_MASK 0xfff +#define WB_GAIN_INT_MASK 0xf +#define WB_GAIN_DECI_MASK 0x1ff + +/* CFA */ +#define CFA_HPF_THR_2DIR_MASK 0x1fff +#define CFA_HPF_SLOPE_2DIR_MASK 0x3ff +#define CFA_HPF_MIX_THR_2DIR_MASK 0x1fff +#define CFA_HPF_MIX_SLP_2DIR_MASK 0x3ff +#define CFA_DIR_THR_2DIR_MASK 0x3ff +#define CFA_DIR_SLP_2DIR_MASK 0x7f +#define CFA_ND_WT_2DIR_MASK 0x3f +#define CFA_DAA_HUE_FRA_MASK 0x3f +#define CFA_DAA_EDG_THR_MASK 0xff +#define CFA_DAA_THR_MIN_MASK 0x3ff +#define CFA_DAA_THR_SLP_MASK 0x3ff +#define CFA_DAA_SLP_MIN_MASK 0x3ff +#define CFA_DAA_SLP_SLP_MASK 0x3ff +#define CFA_DAA_LP_WT_MASK 0x3f + +/* RGB2RGB */ +#define RGB2RGB_1_OFST_MASK 0x1fff +#define RGB2RGB_1_GAIN_INT_MASK 0xf +#define RGB2RGB_GAIN_DECI_MASK 0xff +#define RGB2RGB_2_OFST_MASK 0x7ff +#define RGB2RGB_2_GAIN_INT_MASK 0x7 + +/* Gamma */ +#define GAMMA_BYPR_SHIFT 0 +#define GAMMA_BYPG_SHIFT 1 +#define GAMMA_BYPB_SHIFT 2 +#define GAMMA_TBL_SEL_SHIFT 4 +#define GAMMA_TBL_SIZE_SHIFT 5 +#define GAMMA_MASK 0x3ff +#define GAMMA_SHIFT 10 + +/* 3D LUT */ +#define D3_LUT_ENTRY_MASK 0x3ff +#define D3_LUT_ENTRY_R_SHIFT 20 +#define D3_LUT_ENTRY_G_SHIFT 10 +#define D3_LUT_ENTRY_B_SHIFT 0 + +/* Lumina adj */ +#define LUM_ADJ_CONTR_SHIFT 0 +#define LUM_ADJ_BRIGHT_SHIFT 8 + +/* RGB2YCbCr */ +#define RGB2YCBCR_OFST_MASK 0x7ff +#define RGB2YCBCR_COEF_INT_MASK 0xf +#define RGB2YCBCR_COEF_DECI_MASK 0xff + +/* GBCE */ +#define GBCE_Y_VAL_MASK 0xff +#define GBCE_GAIN_VAL_MASK 0x3ff +#define GBCE_ENTRY_SHIFT 10 + +/* Edge Enhancements */ +#define YEE_HALO_RED_EN_SHIFT 1 +#define YEE_HPF_SHIFT_MASK 0xf +#define YEE_COEF_MASK 0x3ff +#define YEE_THR_MASK 0x3f +#define YEE_ES_GAIN_MASK 0xfff +#define YEE_ES_THR1_MASK 0xfff +#define YEE_ENTRY_SHIFT 9 +#define YEE_ENTRY_MASK 0x1ff + +/* CAR */ +#define CAR_MF_THR 0xff +#define CAR_SW1_SHIFT 8 +#define CAR_GAIN1_SHFT_MASK 7 +#define CAR_GAIN_MIN_MASK 0x1ff +#define CAR_GAIN2_SHFT_MASK 0xf +#define CAR_HPF_SHIFT_MASK 3 + +/* CGS */ +#define CAR_SHIFT_MASK 3 + +/* Resizer */ +#define RSZ_BYPASS_SHIFT 1 +#define RSZ_SRC_IMG_FMT_SHIFT 1 +#define RSZ_SRC_Y_C_SEL_SHIFT 2 +#define IPIPE_RSZ_VPS_MASK 0xffff +#define IPIPE_RSZ_HPS_MASK 0xffff +#define IPIPE_RSZ_VSZ_MASK 0x1fff +#define IPIPE_RSZ_HSZ_MASK 0x1fff +#define RSZ_HPS_MASK 0x1fff +#define RSZ_VPS_MASK 0x1fff +#define RSZ_O_HSZ_MASK 0x1fff +#define RSZ_O_VSZ_MASK 0x1fff +#define RSZ_V_PHS_MASK 0x3fff +#define RSZ_V_DIF_MASK 0x3fff + +#define RSZA_H_FLIP_SHIFT 0 +#define RSZA_V_FLIP_SHIFT 1 +#define RSZB_H_FLIP_SHIFT 2 +#define RSZB_V_FLIP_SHIFT 3 +#define RSZ_A 0 +#define RSZ_B 1 +#define RSZ_CEN_SHIFT 1 +#define RSZ_YEN_SHIFT 0 +#define RSZ_TYP_Y_SHIFT 0 +#define RSZ_TYP_C_SHIFT 1 +#define RSZ_LPF_INT_MASK 0x3f +#define RSZ_LPF_INT_MASK 0x3f +#define RSZ_LPF_INT_C_SHIFT 6 +#define RSZ_H_PHS_MASK 0x3fff +#define RSZ_H_DIF_MASK 0x3fff +#define RSZ_DIFF_DOWN_THR 256 +#define RSZ_DWN_SCALE_AV_SZ_V_SHIFT 3 +#define RSZ_DWN_SCALE_AV_SZ_MASK 7 +#define RSZ_RGB_MSK1_SHIFT 2 +#define RSZ_RGB_MSK0_SHIFT 1 +#define RSZ_RGB_TYP_SHIFT 0 +#define RSZ_RGB_ALPHA_MASK 0xff + +static inline u32 regr_ip(u32 offset) +{ + return readl(IPIPE_IOBASE_VADDR + offset); +} + +static inline u32 regw_ip(u32 val, u32 offset) +{ + writel(val, IPIPE_IOBASE_VADDR + offset); + + return val; +} + +static inline u32 r_ip_table(u32 offset) +{ + return readl(IPIPE_INT_TABLE_IOBASE_VADDR + offset); +} + +static inline u32 w_ip_table(u32 val, u32 offset) +{ + writel(val, IPIPE_INT_TABLE_IOBASE_VADDR + offset); + + return val; +} + +static inline u32 regr_rsz(u32 offset) +{ + return readl(RSZ_IOBASE_VADDR + offset); +} + +static inline u32 regw_rsz(u32 val, u32 offset) +{ + writel(val, RSZ_IOBASE_VADDR + offset); + + return val; +} + +#endif /* End of #ifdef _DM365_IPIPE_HW_H */ -- 1.6.2.4 From manjunath.hadli at ti.com Sat Nov 12 07:18:57 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 12 Nov 2011 18:48:57 +0530 Subject: [RFC PATCH v4 3/8] davinci: vpfe: add IPIPE support for media controller driver In-Reply-To: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> References: <1321103942-2778-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321103942-2778-4-git-send-email-manjunath.hadli@ti.com> Add the IPIPE interfacing layer to the vpfe driver. This patch adds dm365 specific implementation of the genric imp_hw_interface interface for programming the IPIPE block, mainly setting the resizer and previewer configuration parameters. This is built as an independent module. Signed-off-by: Manjunath Hadli Signed-off-by: Nagabhushana Netagunte --- drivers/media/video/davinci/dm365_def_para.c | 310 +++ drivers/media/video/davinci/dm365_def_para.h | 39 + drivers/media/video/davinci/dm365_ipipe.c | 3844 ++++++++++++++++++++++++++ drivers/media/video/davinci/dm365_ipipe.h | 378 +++ drivers/media/video/davinci/imp_common.h | 86 + drivers/media/video/davinci/imp_hw_if.h | 171 ++ include/linux/dm365_ipipe.h | 1029 +++++++ include/linux/imp_common.h | 171 ++ 8 files changed, 6028 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/dm365_def_para.c create mode 100644 drivers/media/video/davinci/dm365_def_para.h create mode 100644 drivers/media/video/davinci/dm365_ipipe.c create mode 100644 drivers/media/video/davinci/dm365_ipipe.h create mode 100644 drivers/media/video/davinci/imp_common.h create mode 100644 drivers/media/video/davinci/imp_hw_if.h create mode 100644 include/linux/dm365_ipipe.h create mode 100644 include/linux/imp_common.h diff --git a/drivers/media/video/davinci/dm365_def_para.c b/drivers/media/video/davinci/dm365_def_para.c new file mode 100644 index 0000000..3ba9785 --- /dev/null +++ b/drivers/media/video/davinci/dm365_def_para.c @@ -0,0 +1,310 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include "dm365_ipipe.h" + +/* Defaults for lutdpc */ +struct prev_lutdpc dm365_lutdpc_defaults; + +/* Defaults for otfdpc */ +struct prev_lutdpc dm365_otfdpc_defaults; + +/* Defaults for 2D - nf */ +struct prev_nf dm365_nf_defaults; + +/* defaults for GIC */ +struct prev_gic dm365_gic_defaults; + +/* Defaults for white balance */ +struct prev_wb dm365_wb_defaults = { + .gain_r = {2, 0x00}, + .gain_gr = {2, 0x00}, + .gain_gb = {2, 0x00}, + .gain_b = {2, 0x00} +}; + +/* Defaults for CFA */ +struct prev_cfa dm365_cfa_defaults = { + .alg = IPIPE_CFA_ALG_2DIRAC, +}; + +/* Defaults for rgb2rgb */ +struct prev_rgb2rgb dm365_rgb2rgb_defaults = { + .coef_rr = {1, 0}, /* 256 */ + .coef_gr = {0, 0}, + .coef_br = {0, 0}, + .coef_rg = {0, 0}, + .coef_gg = {1, 0}, /* 256 */ + .coef_bg = {0, 0}, + .coef_rb = {0, 0}, + .coef_gb = {0, 0}, + .coef_bb = {1, 0}, /* 256 */ +}; + +/* Defaults for gamma correction */ +struct prev_gamma dm365_gamma_defaults = { + .tbl_sel = IPIPE_GAMMA_TBL_ROM +}; + +/* Defaults for 3d lut */ +struct prev_3d_lut dm365_3d_lut_defaults; + +/* Defaults for lumina adjustments */ +struct prev_lum_adj dm365_lum_adj_defaults = { + .contrast = 16 +}; + +/* Defaults for rgb2yuv conversion */ +struct prev_rgb2yuv dm365_rgb2yuv_defaults = { + .coef_ry = {0, 0x4d}, + .coef_gy = {0, 0x96}, + .coef_by = {0, 0x1d}, + .coef_rcb = {0xf, 0xd5}, + .coef_gcb = {0xf, 0xab}, + .coef_bcb = {0, 0x80}, + .coef_rcr = {0, 0x80}, + .coef_gcr = {0xf, 0x95}, + .coef_bcr = {0xf, 0xeb}, + .out_ofst_cb = 0x80, + .out_ofst_cr = 0x80 +}; + +/* Defaults for GBCE */ +struct prev_gbce dm365_gbce_defaults; + +/* Defaults for yuv 422 conversion */ +struct prev_yuv422_conv dm365_yuv422_conv_defaults = { + .chrom_pos = IPIPE_YUV422_CHR_POS_COSITE +}; + +/* Defaults for Edge Ehnancements */ +struct prev_yee dm365_yee_defaults; + +/* Defaults for CAR conversion */ +struct prev_car dm365_car_defaults; + +/* Defaults for CGS */ +struct prev_cgs dm365_cgs_defaults; + +#define WIDTH_I 640 +#define HEIGHT_I 480 +#define WIDTH_O 640 +#define HEIGHT_O 480 + +/* default ipipeif settings */ +struct ipipeif_5_1 ipipeif_5_1_defaults = { + .pack_mode = IPIPEIF_5_1_PACK_16_BIT, + .data_shift = IPIPEIF_BITS11_0, + .source1 = IPIPEIF_SRC1_PARALLEL_PORT, + .clk_div = { + .m = 1, /* clock = sdram clock * (m/n) */ + .n = 6 + }, + .dpcm = { + .type = IPIPEIF_DPCM_8BIT_12BIT, + .pred = IPIPEIF_DPCM_SIMPLE_PRED + }, + .pix_order = IPIPEIF_CBCR_Y, + .isif_port = { + .if_type = V4L2_MBUS_FMT_SBGGR10_1X10, + .hdpol = VPFE_PINPOL_POSITIVE, + .vdpol = VPFE_PINPOL_POSITIVE + }, + .clip = 4095, +}; + +struct ipipe_params dm365_ipipe_defs = { + .ipipeif_param = { + .mode = IPIPEIF_ONE_SHOT, + .source = IPIPEIF_SDRAM_RAW, + .clock_select = IPIPEIF_SDRAM_CLK, + .glob_hor_size = WIDTH_I + 8, + .glob_ver_size = HEIGHT_I + 10, + .hnum = WIDTH_I, + .vnum = HEIGHT_I, + .adofs = WIDTH_I * 2, + .rsz = 16, /* resize ratio 16/rsz */ + .decimation = IPIPEIF_DECIMATION_OFF, + .avg_filter = IPIPEIF_AVG_OFF, + .gain = 0x200, /* U10Q9 */ + }, + .ipipe_mode = IPIPEIF_ONE_SHOT, + .ipipe_dpaths_fmt = IPIPE_RAW2YUV, + .ipipe_colpat_olop = IPIPE_GREEN_BLUE, + .ipipe_colpat_olep = IPIPE_BLUE, + .ipipe_colpat_elop = IPIPE_RED, + .ipipe_colpat_elep = IPIPE_GREEN_RED, + .ipipe_vsz = HEIGHT_I - 1, + .ipipe_hsz = WIDTH_I - 1, + .rsz_common = { + .vsz = HEIGHT_I - 1, + .hsz = WIDTH_I - 1, + .src_img_fmt = RSZ_IMG_422, + .raw_flip = 1, /* flip preserve Raw format */ + .source = IPIPE_DATA, + .passthrough = IPIPE_BYPASS_OFF, + .yuv_y_max = 255, + .yuv_c_max = 255, + .rsz_seq_crv = DISABLE, + .out_chr_pos = IPIPE_YUV422_CHR_POS_COSITE + }, + .rsz_rsc_param = { + { + .mode = IPIPEIF_ONE_SHOT, + .h_flip = DISABLE, + .v_flip = DISABLE, + .cen = DISABLE, + .yen = DISABLE, + .o_vsz = HEIGHT_O - 1, + .o_hsz = WIDTH_O - 1, + .v_dif = 256, + .v_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dif = 256, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + }, + { + .mode = IPIPEIF_ONE_SHOT, + .h_flip = DISABLE, + .v_flip = DISABLE, + .cen = DISABLE, + .yen = DISABLE, + .o_vsz = HEIGHT_O - 1, + .o_hsz = WIDTH_O - 1, + .v_dif = 256, + .v_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dif = 256, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + }, + }, + .rsz2rgb = { + { + .rgb_en = DISABLE + }, + { + .rgb_en = DISABLE + } + }, + .ext_mem_param = { + { + .rsz_sdr_oft_y = WIDTH_O << 1, + .rsz_sdr_ptr_e_y = HEIGHT_O, + .rsz_sdr_oft_c = WIDTH_O, + .rsz_sdr_ptr_e_c = HEIGHT_O >> 1, + }, + { + .rsz_sdr_oft_y = WIDTH_O << 1, + .rsz_sdr_ptr_e_y = HEIGHT_O, + .rsz_sdr_oft_c = WIDTH_O, + .rsz_sdr_ptr_e_c = HEIGHT_O, + }, + }, + .rsz_en[0] = ENABLE, + .rsz_en[1] = DISABLE +}; + +struct prev_single_shot_config dm365_prev_ss_config_defs = { + .bypass = IPIPE_BYPASS_OFF, + .input = { + .ppln = WIDTH_I + 8, + .lpfr = HEIGHT_I + 10, + .pred = IPIPEIF_DPCM_SIMPLE_PRED, + .clk_div = {1, 6}, + .data_shift = IPIPEIF_BITS11_0, + .rsz = 16, /* resize ratio 16/rsz */ + .avg_filter_en = IPIPEIF_AVG_OFF, + .dpc = {0, 0}, + .gain = 512, + .clip = 4095, + .colp_olop = IPIPE_GREEN_BLUE, + .colp_olep = IPIPE_BLUE, + .colp_elop = IPIPE_RED, + .colp_elep = IPIPE_GREEN_RED + }, +}; + +struct prev_continuous_config dm365_prev_cont_config_defs = { + .bypass = IPIPE_BYPASS_OFF, + .input = { + .rsz = 16, + .avg_filter_en = IPIPEIF_AVG_OFF, + .gain = 512, + .clip = 4095, + .colp_olop = IPIPE_GREEN_BLUE, + .colp_olep = IPIPE_BLUE, + .colp_elop = IPIPE_RED, + .colp_elep = IPIPE_GREEN_RED + }, +}; + +struct rsz_single_shot_config dm365_rsz_ss_config_defs = { + .input = { + .ppln = WIDTH_I + 8, + .lpfr = HEIGHT_I + 10, + .clk_div = {1, 6}, + .rsz = 16, /* resize ratio 16/rsz */ + .avg_filter_en = IPIPEIF_AVG_OFF, + }, + .output1 = { + .v_typ_y = RSZ_INTP_CUBIC, + .v_typ_c = RSZ_INTP_CUBIC, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + }, + .output2 = { + .v_typ_y = RSZ_INTP_CUBIC, + .v_typ_c = RSZ_INTP_CUBIC, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + }, + .yuv_y_max = 255, + .yuv_c_max = 255, + .out_chr_pos = IPIPE_YUV422_CHR_POS_COSITE, +}; + +struct rsz_continuous_config dm365_rsz_cont_config_defs = { + .output1 = { + .v_typ_y = RSZ_INTP_CUBIC, + .v_typ_c = RSZ_INTP_CUBIC, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + }, + .output2 = { + .v_typ_y = RSZ_INTP_CUBIC, + .v_typ_c = RSZ_INTP_CUBIC, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + }, + .yuv_y_max = 255, + .yuv_c_max = 255, + .out_chr_pos = IPIPE_YUV422_CHR_POS_COSITE, +}; diff --git a/drivers/media/video/davinci/dm365_def_para.h b/drivers/media/video/davinci/dm365_def_para.h new file mode 100644 index 0000000..872b3cb --- /dev/null +++ b/drivers/media/video/davinci/dm365_def_para.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "dm365_ipipe.h" +extern struct prev_lutdpc dm365_lutdpc_defaults; +extern struct prev_otfdpc dm365_otfdpc_defaults; +extern struct prev_nf dm365_nf_defaults; +extern struct prev_gic dm365_gic_defaults; +extern struct prev_wb dm365_wb_defaults; +extern struct prev_cfa dm365_cfa_defaults; +extern struct prev_rgb2rgb dm365_rgb2rgb_defaults; +extern struct prev_gamma dm365_gamma_defaults; +extern struct prev_3d_lut dm365_3d_lut_defaults; +extern struct prev_lum_adj dm365_lum_adj_defaults; +extern struct prev_rgb2yuv dm365_rgb2yuv_defaults; +extern struct prev_yuv422_conv dm365_yuv422_conv_defaults; +extern struct prev_gbce dm365_gbce_defaults; +extern struct prev_yee dm365_yee_defaults; +extern struct prev_car dm365_car_defaults; +extern struct prev_cgs dm365_cgs_defaults; +extern struct ipipe_params dm365_ipipe_defs; +extern struct prev_single_shot_config dm365_prev_ss_config_defs; +extern struct prev_continuous_config dm365_prev_cont_config_defs; +extern struct rsz_single_shot_config dm365_rsz_ss_config_defs; +extern struct rsz_continuous_config dm365_rsz_cont_config_defs; +extern struct ipipeif_5_1 ipipeif_5_1_defaults; diff --git a/drivers/media/video/davinci/dm365_ipipe.c b/drivers/media/video/davinci/dm365_ipipe.c new file mode 100644 index 0000000..00ef550 --- /dev/null +++ b/drivers/media/video/davinci/dm365_ipipe.c @@ -0,0 +1,3844 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dm365_ipipe.h" +#include "imp_hw_if.h" +#include "dm365_ipipe_hw.h" +#include "dm365_def_para.h" + +static int ipipe_enum_pix(void *ipipe, u32 *pix, int i) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + if (i >= ARRAY_SIZE(oper_state->ipipe_raw_yuv_pix_formats)) + return -EINVAL; + + *pix = oper_state->ipipe_raw_yuv_pix_formats[i]; + return 0; +} + +/* IPIPE hardware limits */ +#define IPIPE_MAX_OUTPUT_WIDTH_A 2176 +#define IPIPE_MAX_OUTPUT_WIDTH_B 640 + +static int ipipe_get_max_output_width(int rsz) +{ + if (rsz == RSZ_A) + return IPIPE_MAX_OUTPUT_WIDTH_A; + return IPIPE_MAX_OUTPUT_WIDTH_B; +} + +/* Based on max resolution supported. QXGA */ +#define IPIPE_MAX_OUTPUT_HEIGHT_A 1536 +/* Based on max resolution supported. VGA */ +#define IPIPE_MAX_OUTPUT_HEIGHT_B 480 + +static int ipipe_get_max_output_height(int rsz) +{ + if (rsz == RSZ_A) + return IPIPE_MAX_OUTPUT_HEIGHT_A; + return IPIPE_MAX_OUTPUT_HEIGHT_B; +} + +static int ipipe_serialize(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return oper_state->en_serializer; +} + +static int ipipe_set_ipipe_if_address(void *config, unsigned int address) +{ + struct ipipeif *if_params; + + if (!config) + return -EINVAL; + + if_params = &((struct ipipe_params *)config)->ipipeif_param; + + return ipipeif_set_address(if_params, address); +} + +static void ipipe_lock_chain(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + mutex_lock(&oper_state->lock); + oper_state->resource_in_use = 1; + mutex_unlock(&oper_state->lock); +} + +static void ipipe_unlock_chain(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + mutex_lock(&oper_state->lock); + oper_state->resource_in_use = 0; + oper_state->prev_config_state = STATE_NOT_CONFIGURED; + oper_state->rsz_config_state = STATE_NOT_CONFIGURED; + oper_state->rsz_chained = 0; + mutex_unlock(&oper_state->lock); +} +static int ipipe_process_pix_fmts(enum v4l2_mbus_pixelcode in_pix_fmt, + enum v4l2_mbus_pixelcode out_pix_fmt, + struct ipipe_params *param) +{ + enum v4l2_mbus_pixelcode temp_pix_fmt; + + switch (in_pix_fmt) { + case V4L2_MBUS_FMT_SBGGR8_1X8: + temp_pix_fmt = V4L2_MBUS_FMT_SBGGR10_1X10; + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_8_BIT; + break; + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_8_BIT_A_LAW; + temp_pix_fmt = V4L2_MBUS_FMT_SBGGR10_1X10; + break; + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_8_BIT; + param->ipipeif_param.var.if_5_1.dpcm.en = 1; + temp_pix_fmt = V4L2_MBUS_FMT_SBGGR10_1X10; + break; + case V4L2_MBUS_FMT_SBGGR10_1X10: + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_16_BIT; + temp_pix_fmt = V4L2_MBUS_FMT_SBGGR10_1X10; + break; + case V4L2_MBUS_FMT_SBGGR12_1X12: + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_12_BIT; + temp_pix_fmt = V4L2_MBUS_FMT_SBGGR10_1X10; + break; + case V4L2_MBUS_FMT_Y8_1X8: + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_8_BIT; + temp_pix_fmt = V4L2_MBUS_FMT_UYVY8_2X8; + break; + case V4L2_MBUS_FMT_UV8_1X8: + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_8_BIT; + temp_pix_fmt = V4L2_MBUS_FMT_UYVY8_2X8; + break; + default: + temp_pix_fmt = V4L2_MBUS_FMT_UYVY8_2X8; + } + + if (temp_pix_fmt == V4L2_MBUS_FMT_SBGGR10_1X10) { + if (out_pix_fmt == V4L2_MBUS_FMT_SBGGR10_1X10) + param->ipipe_dpaths_fmt = IPIPE_RAW2RAW; + else if (out_pix_fmt == V4L2_MBUS_FMT_UYVY8_2X8 || + out_pix_fmt == V4L2_MBUS_FMT_NV12_1X20) + param->ipipe_dpaths_fmt = IPIPE_RAW2YUV; + else + return -EINVAL; + } else if (temp_pix_fmt == V4L2_MBUS_FMT_UYVY8_2X8) { + switch (out_pix_fmt) { + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_NV12_1X20: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_UV8_1X8: + param->ipipe_dpaths_fmt = IPIPE_YUV2YUV; + break; + default: + return -EINVAL; + } + } + return 0; +} + +/* + * calculate_resize_ratios() + * calculates resize ratio for resizer A or B. This is called after setting + * the input size or output size + */ +static void calculate_resize_ratios(struct ipipe_params *param, int index) +{ + param->rsz_rsc_param[index].h_dif = ((param->ipipe_hsz + 1) * 256) / + (param->rsz_rsc_param[index].o_hsz + 1); + param->rsz_rsc_param[index].v_dif = ((param->ipipe_vsz + 1) * 256) / + (param->rsz_rsc_param[index].o_vsz + 1); +} + +static int ipipe_do_hw_setup(struct device *dev, void *ipipe, void *config) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct ipipe_params *param = (struct ipipe_params *)config; + int ret; + + dev_dbg(dev, "ipipe_do_hw_setup\n"); + ret = mutex_lock_interruptible(&oper_state->lock); + if (ret) + return ret; + + if (!config && oper_state->oper_mode == IMP_MODE_CONTINUOUS) { + /* continuous mode */ + param = oper_state->shared_config_param; + if (param->rsz_en[RSZ_A]) + calculate_resize_ratios(param, RSZ_A); + if (param->rsz_en[RSZ_B]) + calculate_resize_ratios(param, RSZ_B); + ret = ipipe_hw_setup(param); + } + mutex_unlock(&oper_state->lock); + + return ret; +} + +static unsigned int ipipe_rsz_chain_state(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return oper_state->rsz_chained; +} + +static void ipipe_update_outbuf1_address(void *ipipe, void *config, + unsigned int address) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + if (!config && oper_state->oper_mode == IMP_MODE_CONTINUOUS) + return rsz_set_output_address(oper_state->shared_config_param, + 0, address); + + rsz_set_output_address((struct ipipe_params *)config, 0, address); +} + +static void ipipe_update_outbuf2_address(void *ipipe, void *config, + unsigned int address) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + if (!config && oper_state->oper_mode == IMP_MODE_CONTINUOUS) + return rsz_set_output_address(oper_state->shared_config_param, + 1, address); + + rsz_set_output_address((struct ipipe_params *)config, 1, address); +} + +static void ipipe_enable(void *ipipe, unsigned char en, void *config) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct ipipe_params *param = (struct ipipe_params *)config; + unsigned char ret; + + if (en) + en = !!en; + + if (oper_state->oper_mode == IMP_MODE_CONTINUOUS) + param = oper_state->shared_config_param; + + if (oper_state->oper_mode == IMP_MODE_SINGLE_SHOT && en) { + /* for single-shot mode, need to wait for h/w to + reset many register bits */ + + if (param->rsz_common.source == IPIPE_DATA) { + do { + ret = regr_ip(IPIPE_SRC_EN); + } while (ret); + } + + do { + ret = regr_rsz(RSZ_SRC_EN); + } while (ret); + + if (param->rsz_en[RSZ_A]) { + do { + ret = regr_rsz(RSZ_A); + } while (ret); + } + + if (en && param->rsz_en[RSZ_B]) { + do { + ret = regr_rsz(RSZ_B); + } while (ret); + } + + do { + ret = ipipeif_get_enable(); + } while (ret & 0x1); + } + + if (param->rsz_common.source == IPIPE_DATA) + regw_ip(en, IPIPE_SRC_EN); + + if (param->rsz_en[RSZ_A]) + rsz_enable(RSZ_A, en); + + if (param->rsz_en[RSZ_B]) + rsz_enable(RSZ_B, en); + + if (oper_state->oper_mode == IMP_MODE_SINGLE_SHOT) + ipipeif_set_enable(en, oper_state->oper_mode); +} + +static int validate_lutdpc_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + int i; + + if (oper_state->lutdpc.en > 1 || + oper_state->lutdpc.repl_white > 1 || + oper_state->lutdpc.dpc_size > LUT_DPC_MAX_SIZE) + return -EINVAL; + + if (oper_state->lutdpc.en && !oper_state->lutdpc.table) + return -EINVAL; + + for (i = 0; i < oper_state->lutdpc.dpc_size; i++) { + if (oper_state->lutdpc.table[i].horz_pos > LUT_DPC_H_POS_MASK || + oper_state->lutdpc.table[i].vert_pos > LUT_DPC_V_POS_MASK) + return -EINVAL; + } + return 0; +} + +static int set_lutdpc_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct ipipe_lutdpc_entry *temp_lutdpc; + struct prev_lutdpc dpc_param; + + if (!param) { + /* Copy defaults for dfc */ + temp_lutdpc = oper_state->lutdpc.table; + memcpy((void *)&oper_state->lutdpc, + (void *)&dm365_lutdpc_defaults, + sizeof(struct prev_lutdpc)); + oper_state->lutdpc.table = temp_lutdpc; + goto success; + } + + if (len != sizeof(struct prev_lutdpc)) { + dev_err(dev, "set_lutdpc_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + + if (copy_from_user(&dpc_param, (struct prev_lutdpc *)param, + sizeof(struct prev_lutdpc))) { + dev_err(dev, "set_lutdpc_params: Error in copy to kernel\n"); + return -EFAULT; + } + + if (!dpc_param.table) { + dev_err(dev, "Invalid user dpc table ptr\n"); + return -EINVAL; + } + + oper_state->lutdpc.en = dpc_param.en; + oper_state->lutdpc.repl_white = dpc_param.repl_white; + oper_state->lutdpc.dpc_size = dpc_param.dpc_size; + + if (copy_from_user(oper_state->lutdpc.table, (struct ipipe_dpc_entry *) + dpc_param.table, (oper_state->lutdpc.dpc_size * + sizeof(struct ipipe_lutdpc_entry)))) { + dev_err(dev, "set_lutdpc_params: Error in copying " + "dfc table to kernel\n"); + return -EFAULT; + } + + if (validate_lutdpc_params(dev, oper_state) < 0) + return -EINVAL; + +success: + ipipe_set_lutdpc_regs(&oper_state->lutdpc); + + return 0; +} + +static int get_lutdpc_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_lutdpc *lut_param = (struct prev_lutdpc *)param; + struct prev_lutdpc user_lutdpc; + + if (!lut_param) { + dev_err(dev, "get_lutdpc_params: invalid user ptr"); + return -EINVAL; + } + + if (len != sizeof(struct prev_lutdpc)) { + dev_err(dev, + "get_lutdpc_params: param struct length mismatch\n"); + return -EINVAL; + } + + if (copy_from_user(&user_lutdpc, lut_param, + sizeof(struct prev_lutdpc))) { + dev_err(dev, "get_lutdpc_params: Error in copy to kernel\n"); + return -EFAULT; + } + + user_lutdpc.en = oper_state->lutdpc.en; + user_lutdpc.repl_white = oper_state->lutdpc.repl_white; + user_lutdpc.dpc_size = oper_state->lutdpc.dpc_size; + if (!user_lutdpc.table) { + dev_err(dev, "get_lutdpc_params:" " Invalid table ptr"); + return -EINVAL; + } + if (copy_to_user(user_lutdpc.table, + oper_state->lutdpc.table, + (oper_state->lutdpc.dpc_size * + sizeof(struct ipipe_lutdpc_entry)))) { + dev_err(dev, + "get_lutdpc_params:Table Error in" " copy to user\n"); + return -EFAULT; + } + + if (copy_to_user(lut_param, &user_lutdpc, sizeof(struct prev_lutdpc))) { + dev_err(dev, "get_lutdpc_params: Error in copy" " to user\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_otfdpc_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + struct prev_otfdpc *dpc_param = + (struct prev_otfdpc *)&oper_state->otfdpc; + struct prev_otfdpc_2_0 *dpc_2_0; + struct prev_otfdpc_3_0 *dpc_3_0; + + if (dpc_param->en > 1) + return -EINVAL; + if (dpc_param->alg == IPIPE_OTFDPC_2_0) { + dpc_2_0 = &dpc_param->alg_cfg.dpc_2_0; + if (dpc_2_0->det_thr.r > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.gr > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.gb > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.b > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.r > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.gr > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.gb > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.b > OTFDPC_DPC2_THR_MASK) + return -EINVAL; + return 0; + } + + dpc_3_0 = &dpc_param->alg_cfg.dpc_3_0; + if (dpc_3_0->act_adj_shf > OTF_DPC3_0_SHF_MASK || + dpc_3_0->det_thr > OTF_DPC3_0_DET_MASK || + dpc_3_0->det_slp > OTF_DPC3_0_SLP_MASK || + dpc_3_0->det_thr_min > OTF_DPC3_0_DET_MASK || + dpc_3_0->det_thr_max > OTF_DPC3_0_DET_MASK || + dpc_3_0->corr_thr > OTF_DPC3_0_CORR_MASK || + dpc_3_0->corr_slp > OTF_DPC3_0_SLP_MASK || + dpc_3_0->corr_thr_min > OTF_DPC3_0_CORR_MASK || + dpc_3_0->corr_thr_max > OTF_DPC3_0_CORR_MASK) + return -EINVAL; + + return 0; +} + +static int set_otfdpc_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_otfdpc *dpc_param = (struct prev_otfdpc *)param; + + if (!param) { + /* Copy defaults for dpc2.0 defaults */ + memcpy((void *)&oper_state->otfdpc, + (void *)&dm365_otfdpc_defaults, + sizeof(struct ipipe_otfdpc_2_0)); + } else { + if (len != sizeof(struct prev_otfdpc)) { + dev_err(dev, "set_otfdpc_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->otfdpc, dpc_param, + sizeof(struct prev_otfdpc))) { + dev_err(dev, "set_otfdpc_params: Error in " + "copy to kernel\n"); + return -EFAULT; + } + + if (validate_otfdpc_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_otfdpc_regs(&oper_state->otfdpc); + + return 0; +} + +static int get_otfdpc_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_otfdpc *dpc_param = (struct prev_otfdpc *)param; + + if (!dpc_param) { + dev_err(dev, "get_otfdpc_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_otfdpc)) { + dev_err(dev, + "get_otfdpc_params: param struct length mismatch\n"); + return -EINVAL; + } + if (copy_to_user(dpc_param, &oper_state->otfdpc, + sizeof(struct prev_otfdpc))) { + dev_err(dev, "get_otfdpc_params: Error in copy dpc " + "table to user\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_nf_params(struct device *dev, + struct ipipe_oper_state *oper_state, + unsigned int id) +{ + struct prev_nf *nf_param = &oper_state->nf1; + int i; + + if (id) + nf_param = &oper_state->nf2; + if (nf_param->en > 1 || + nf_param->shft_val > D2F_SHFT_VAL_MASK || + nf_param->spread_val > D2F_SPR_VAL_MASK || + nf_param->apply_lsc_gain > 1 || + nf_param->edge_det_min_thr > D2F_EDGE_DET_THR_MASK || + nf_param->edge_det_max_thr > D2F_EDGE_DET_THR_MASK) + return -EINVAL; + + for (i = 0; i < IPIPE_NF_THR_TABLE_SIZE; i++) + if (nf_param->thr[i] > D2F_THR_VAL_MASK) + return -EINVAL; + for (i = 0; i < IPIPE_NF_STR_TABLE_SIZE; i++) + if (nf_param->str[i] > D2F_STR_VAL_MASK) + return -EINVAL; + return 0; +} + +static int set_nf_params(struct device *dev, + struct ipipe_oper_state *oper_state, + unsigned int id, void *param, int len) +{ + struct prev_nf *nf_param = (struct prev_nf *)param; + struct prev_nf *nf = &oper_state->nf1; + + if (id) + nf = &oper_state->nf2; + + if (!nf_param) { + /* Copy defaults for nf */ + memcpy((void *)nf, (void *)&dm365_nf_defaults, + sizeof(struct prev_nf)); + memset((void *)nf->thr, 0, IPIPE_NF_THR_TABLE_SIZE); + memset((void *)nf->str, 0, IPIPE_NF_THR_TABLE_SIZE); + } else { + if (len != sizeof(struct prev_nf)) { + dev_err(dev, "set_nf_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + if (copy_from_user(nf, nf_param, sizeof(struct prev_nf))) { + dev_err(dev, + "set_nf_params: Error in copy to kernel\n"); + return -EFAULT; + } + if (validate_nf_params(dev, oper_state, id) < 0) + return -EINVAL; + } + /* Now set the values in the hw */ + ipipe_set_d2f_regs(id, nf); + + return 0; +} + +static int set_nf1_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return set_nf_params(dev, oper_state, 0, param, len); +} + +static int set_nf2_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return set_nf_params(dev, oper_state, 1, param, len); +} + +static int get_nf_params(struct device *dev, + struct ipipe_oper_state *oper_state, + unsigned int id, void *param, int len) +{ + struct prev_nf *nf_param = (struct prev_nf *)param; + struct prev_nf *nf = &oper_state->nf1; + + if (!nf_param) { + dev_err(dev, "get_nf_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_nf)) { + dev_err(dev, "get_nf_params: param struct length mismatch\n"); + return -EINVAL; + } + if (id) + nf = &oper_state->nf2; + if (copy_to_user((struct prev_nf *)nf_param, nf, + sizeof(struct prev_nf))) { + dev_err(dev, "get_nf_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int get_nf1_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return get_nf_params(dev, oper_state, 0, param, len); +} + +static int get_nf2_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return get_nf_params(dev, oper_state, 1, param, len); +} + +static int validate_gic_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + if (oper_state->gic.en > 1 || + oper_state->gic.gain > GIC_GAIN_MASK || + oper_state->gic.thr > GIC_THR_MASK || + oper_state->gic.slope > GIC_SLOPE_MASK || + oper_state->gic.apply_lsc_gain > 1 || + oper_state->gic.nf2_thr_gain.integer > GIC_NFGAN_INT_MASK || + oper_state->gic.nf2_thr_gain.decimal > GIC_NFGAN_DECI_MASK) + return -EINVAL; + return 0; +} + +static int set_gic_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_gic *gic_param = (struct prev_gic *)param; + + if (!gic_param) { + /* Copy defaults for nf */ + memcpy((void *)&oper_state->gic, (void *)&dm365_gic_defaults, + sizeof(struct prev_gic)); + } else { + if (len != sizeof(struct prev_gic)) { + dev_err(dev, "set_gic_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->gic, gic_param, + sizeof(struct prev_gic))) { + dev_err(dev, + "set_gic_params: Error in copy to kernel\n"); + return -EFAULT; + } + if (validate_gic_params(dev, oper_state) < 0) + return -EINVAL; + } + /* Now set the values in the hw */ + ipipe_set_gic_regs(&oper_state->gic); + + return 0; +} + +static int get_gic_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_gic *gic_param = (struct prev_gic *)param; + + if (!gic_param) { + dev_err(dev, "get_gic_params: invalid user ptr"); + return -EINVAL; + } + + if (len != sizeof(struct prev_gic)) { + dev_err(dev, "get_gic_params: param struct length mismatch\n"); + return -EINVAL; + } + + if (copy_to_user((struct prev_gic *)gic_param, &oper_state->gic, + sizeof(struct prev_gic))) { + dev_err(dev, "get_gic_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_wb_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + if (oper_state->wb.ofst_r > WB_OFFSET_MASK || + oper_state->wb.ofst_gr > WB_OFFSET_MASK || + oper_state->wb.ofst_gb > WB_OFFSET_MASK || + oper_state->wb.ofst_b > WB_OFFSET_MASK || + oper_state->wb.gain_r.integer > WB_GAIN_INT_MASK || + oper_state->wb.gain_r.decimal > WB_GAIN_DECI_MASK || + oper_state->wb.gain_gr.integer > WB_GAIN_INT_MASK || + oper_state->wb.gain_gr.decimal > WB_GAIN_DECI_MASK || + oper_state->wb.gain_gb.integer > WB_GAIN_INT_MASK || + oper_state->wb.gain_gb.decimal > WB_GAIN_DECI_MASK || + oper_state->wb.gain_b.integer > WB_GAIN_INT_MASK || + oper_state->wb.gain_b.decimal > WB_GAIN_DECI_MASK) + return -EINVAL; + return 0; +} + +static int set_wb_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_wb *wb_param = (struct prev_wb *)param; + + dev_dbg(dev, "set_wb_params"); + if (!wb_param) { + /* Copy defaults for wb */ + memcpy((void *)&oper_state->wb, (void *)&dm365_wb_defaults, + sizeof(struct prev_wb)); + } else { + if (len != sizeof(struct prev_wb)) { + dev_err(dev, "set_wb_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->wb, wb_param, + sizeof(struct prev_wb))) { + dev_err(dev, + "set_wb_params: Error in copy to kernel\n"); + return -EFAULT; + } + if (validate_wb_params(dev, oper_state) < 0) + return -EINVAL; + } + /* Now set the values in the hw */ + ipipe_set_wb_regs(&oper_state->wb); + + return 0; +} + +static int get_wb_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_wb *wb_param = (struct prev_wb *)param; + + if (!wb_param) { + dev_err(dev, "get_wb_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_wb)) { + dev_err(dev, "get_wb_params: param struct length mismatch\n"); + return -EINVAL; + } + if (copy_to_user((struct prev_wb *)wb_param, &oper_state->wb, + sizeof(struct prev_wb))) { + dev_err(dev, "get_wb_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_cfa_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + if (oper_state->cfa.hpf_thr_2dir > CFA_HPF_THR_2DIR_MASK || + oper_state->cfa.hpf_slp_2dir > CFA_HPF_SLOPE_2DIR_MASK || + oper_state->cfa.hp_mix_thr_2dir > CFA_HPF_MIX_THR_2DIR_MASK || + oper_state->cfa.hp_mix_slope_2dir > CFA_HPF_MIX_SLP_2DIR_MASK || + oper_state->cfa.dir_thr_2dir > CFA_DIR_THR_2DIR_MASK || + oper_state->cfa.dir_slope_2dir > CFA_DIR_SLP_2DIR_MASK || + oper_state->cfa.nd_wt_2dir > CFA_ND_WT_2DIR_MASK || + oper_state->cfa.hue_fract_daa > CFA_DAA_HUE_FRA_MASK || + oper_state->cfa.edge_thr_daa > CFA_DAA_EDG_THR_MASK || + oper_state->cfa.thr_min_daa > CFA_DAA_THR_MIN_MASK || + oper_state->cfa.thr_slope_daa > CFA_DAA_THR_SLP_MASK || + oper_state->cfa.slope_min_daa > CFA_DAA_SLP_MIN_MASK || + oper_state->cfa.slope_slope_daa > CFA_DAA_SLP_SLP_MASK || + oper_state->cfa.lp_wt_daa > CFA_DAA_LP_WT_MASK) + return -EINVAL; + return 0; +} + +static int set_cfa_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_cfa *cfa_param = (struct prev_cfa *)param; + + dev_dbg(dev, "set_cfa_params"); + if (!cfa_param) { + /* Copy defaults for wb */ + memcpy((void *)&oper_state->cfa, (void *)&dm365_cfa_defaults, + sizeof(struct prev_cfa)); + } else { + if (len != sizeof(struct prev_cfa)) { + dev_err(dev, "set_cfa_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->cfa, cfa_param, + sizeof(struct prev_cfa))) { + dev_err(dev, + "set_cfa_params: Error in copy to kernel\n"); + return -EFAULT; + } + if (validate_cfa_params(dev, oper_state) < 0) + return -EINVAL; + } + /* Now set the values in the hw */ + ipipe_set_cfa_regs(&oper_state->cfa); + + return 0; +} + +static int get_cfa_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_cfa *cfa_param = (struct prev_cfa *)param; + + dev_dbg(dev, "get_cfa_params\n"); + if (!cfa_param) { + dev_err(dev, "get_cfa_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_cfa)) { + dev_err(dev, "get_cfa_params: param struct length mismatch\n"); + return -EINVAL; + } + if (copy_to_user((struct prev_cfa *)cfa_param, &oper_state->cfa, + sizeof(struct prev_cfa))) { + dev_err(dev, "get_cfa_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_rgb2rgb_params(struct device *dev, + struct ipipe_oper_state *oper_state, + unsigned int id) +{ + struct prev_rgb2rgb *rgb2rgb = &oper_state->rgb2rgb_1; + u32 gain_int_upper = RGB2RGB_1_GAIN_INT_MASK; + u32 offset_upper = RGB2RGB_1_OFST_MASK; + + if (id) { + rgb2rgb = &oper_state->rgb2rgb_2; + offset_upper = RGB2RGB_2_OFST_MASK; + gain_int_upper = RGB2RGB_2_GAIN_INT_MASK; + } + if (rgb2rgb->coef_rr.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rr.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gr.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gr.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_br.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_br.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_rg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_bg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_bg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_rb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_bb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_bb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->out_ofst_r > offset_upper || + rgb2rgb->out_ofst_g > offset_upper || + rgb2rgb->out_ofst_b > offset_upper) + return -EINVAL; + return 0; +} + +static int set_rgb2rgb_params(struct device *dev, + struct ipipe_oper_state *oper_state, + unsigned int id, + void *param, int len) +{ + struct prev_rgb2rgb *rgb2rgb_param = (struct prev_rgb2rgb *)param; + struct prev_rgb2rgb *rgb2rgb = &oper_state->rgb2rgb_1; + + if (id) + rgb2rgb = &oper_state->rgb2rgb_2; + if (!rgb2rgb_param) { + /* Copy defaults for rgb2rgb conversion */ + memcpy((void *)rgb2rgb, (void *)&dm365_rgb2rgb_defaults, + sizeof(struct prev_rgb2rgb)); + } else { + + if (len != sizeof(struct prev_rgb2rgb)) { + dev_err(dev, "set_rgb2rgb_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + + if (copy_from_user(rgb2rgb, rgb2rgb_param, + sizeof(struct prev_rgb2rgb))) { + dev_err(dev, "set_rgb2rgb_params: Error in " + "copy to kernel\n"); + return -EFAULT; + } + if (validate_rgb2rgb_params(dev, oper_state, id) < 0) + return -EINVAL; + } + ipipe_set_rgb2rgb_regs(id, rgb2rgb); + + return 0; +} + +static int set_rgb2rgb_1_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return set_rgb2rgb_params(dev, oper_state, 0, param, len); +} + +static int set_rgb2rgb_2_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return set_rgb2rgb_params(dev, oper_state, 1, param, len); +} + +static int get_rgb2rgb_params(struct device *dev, + struct ipipe_oper_state *oper_state, + unsigned int id, + void *param, int len) +{ + struct prev_rgb2rgb *rgb2rgb_param = (struct prev_rgb2rgb *)param; + struct prev_rgb2rgb *rgb2rgb = &oper_state->rgb2rgb_1; + + if (!rgb2rgb_param) { + dev_err(dev, "get_rgb2rgb_params: invalid user ptr"); + return -EINVAL; + } + + if (len != sizeof(struct prev_rgb2rgb)) { + dev_err(dev, + "get_rgb2rgb_params: param struct length mismatch\n"); + return -EINVAL; + } + + if (id) + rgb2rgb = &oper_state->rgb2rgb_2; + if (copy_to_user((struct prev_rgb2rgb *)rgb2rgb_param, rgb2rgb, + sizeof(struct prev_rgb2rgb))) { + dev_err(dev, "get_rgb2rgb_params: Error in copy to user\n"); + return -EFAULT; + } + + return 0; +} + +static int get_rgb2rgb_1_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return get_rgb2rgb_params(dev, oper_state, 0, param, len); +} + +static int get_rgb2rgb_2_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return get_rgb2rgb_params(dev, oper_state, 1, param, len); +} + +static int validate_gamma_entry(struct ipipe_gamma_entry *table, int size) +{ + int i; + + if (!table) + return -EINVAL; + + for (i = 0; i < size; i++) { + if (table[i].slope > GAMMA_MASK || + table[i].offset > GAMMA_MASK) + return -EINVAL; + } + return 0; +} + +static int validate_gamma_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + int table_size; + int err; + + if (oper_state->gamma.bypass_r > 1 || + oper_state->gamma.bypass_b > 1 || + oper_state->gamma.bypass_g > 1) + return -EINVAL; + + if (oper_state->gamma.tbl_sel != IPIPE_GAMMA_TBL_RAM) + return 0; + + table_size = oper_state->gamma.tbl_size; + if (!oper_state->gamma.bypass_r) { + err = validate_gamma_entry(oper_state->gamma.table_r, + table_size); + if (err) { + dev_err(dev, "GAMMA R - table entry invalid\n"); + return err; + } + } + if (!oper_state->gamma.bypass_b) { + err = validate_gamma_entry(oper_state->gamma.table_b, + table_size); + if (err) { + dev_err(dev, "GAMMA B - table entry invalid\n"); + return err; + } + } + if (!oper_state->gamma.bypass_g) { + err = validate_gamma_entry(oper_state->gamma.table_g, + table_size); + if (err) { + dev_err(dev, "GAMMA G - table entry invalid\n"); + return err; + } + } + return 0; +} + +static int set_gamma_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_gamma *gamma_param = (struct prev_gamma *)param; + struct prev_gamma user_gamma; + int table_size; + + if (!gamma_param) { + /* Copy defaults for gamma */ + oper_state->gamma.bypass_r = dm365_gamma_defaults.bypass_r; + oper_state->gamma.bypass_g = dm365_gamma_defaults.bypass_g; + oper_state->gamma.bypass_b = dm365_gamma_defaults.bypass_b; + oper_state->gamma.tbl_sel = dm365_gamma_defaults.tbl_sel; + oper_state->gamma.tbl_size = dm365_gamma_defaults.tbl_size; + /* By default, we bypass the gamma correction. + * So no values by default for tables + */ + goto success; + } + + if (len != sizeof(struct prev_gamma)) { + dev_err(dev, "set_gamma_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_gamma, gamma_param, + sizeof(struct prev_gamma))) { + dev_err(dev, "set_gamma_params: Error in copy to kernel\n"); + return -EFAULT; + } + + if (validate_gamma_params(dev, oper_state) < 0) + return -EINVAL; + + oper_state->gamma.bypass_r = user_gamma.bypass_r; + oper_state->gamma.bypass_b = user_gamma.bypass_b; + oper_state->gamma.bypass_g = user_gamma.bypass_g; + oper_state->gamma.tbl_sel = user_gamma.tbl_sel; + oper_state->gamma.tbl_size = user_gamma.tbl_size; + + if (user_gamma.tbl_sel != IPIPE_GAMMA_TBL_RAM) + goto success; + + table_size = user_gamma.tbl_size; + + if (!user_gamma.bypass_r) { + if (!user_gamma.table_r) { + dev_err(dev, "set_gamma_params: Invalid" + " table ptr for R\n"); + return -EINVAL; + } + if (copy_from_user(oper_state->gamma.table_r, + user_gamma.table_r, (table_size * + sizeof(struct ipipe_gamma_entry)))) { + dev_err(dev, "set_gamma_params: R-Error" + " in copy to kernel\n"); + return -EFAULT; + } + } + + if (!user_gamma.bypass_b) { + if (!user_gamma.table_b) { + dev_err(dev, "set_gamma_params: Invalid" + " table ptr for B\n"); + return -EINVAL; + } + if (copy_from_user(oper_state->gamma.table_b, + user_gamma.table_b, (table_size * + sizeof(struct ipipe_gamma_entry)))) { + dev_err(dev, "set_gamma_params: B-Error" + " in copy to kernel\n"); + return -EFAULT; + } + } + + if (!user_gamma.bypass_g) { + if (!user_gamma.table_g) { + dev_err(dev, "set_gamma_params: Invalid" + " table ptr for G\n"); + return -EINVAL; + } + if (copy_from_user(oper_state->gamma.table_g, + user_gamma.table_g, (table_size * + sizeof(struct ipipe_gamma_entry)))) { + dev_err(dev, "set_gamma_params: G-Error " + "in copy to kernel\n"); + return -EFAULT; + } + } + +success: + ipipe_set_gamma_regs(&oper_state->gamma); + + return 0; +} + +static int get_gamma_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_gamma *gamma_param = (struct prev_gamma *)param; + struct prev_gamma user_gamma; + int table_size; + + if (!gamma_param) { + dev_err(dev, "get_gamma_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_gamma)) { + dev_err(dev, + "get_gamma_params: param struct length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_gamma, gamma_param, + sizeof(struct prev_gamma))) { + dev_err(dev, "get_gamma_params: Error in copy to kernel\n"); + return -EFAULT; + } + + user_gamma.bypass_r = oper_state->gamma.bypass_r; + user_gamma.bypass_g = oper_state->gamma.bypass_g; + user_gamma.bypass_b = oper_state->gamma.bypass_b; + user_gamma.tbl_sel = oper_state->gamma.tbl_sel; + user_gamma.tbl_size = oper_state->gamma.tbl_size; + + if (oper_state->gamma.tbl_sel != IPIPE_GAMMA_TBL_RAM) + goto copy_user_gama; + + table_size = oper_state->gamma.tbl_size; + + if (!oper_state->gamma.bypass_r && !user_gamma.table_r) { + dev_err(dev, + "get_gamma_params: table ptr empty for R\n"); + return -EINVAL; + } + if (copy_to_user(user_gamma.table_r, oper_state->gamma.table_r, + (table_size * sizeof(struct ipipe_gamma_entry)))) { + dev_err(dev, "set_gamma_params: R-Table Error " + "in copy to user\n"); + return -EFAULT; + } + + if (!oper_state->gamma.bypass_b && !user_gamma.table_b) { + dev_err(dev, "get_gamma_params: table ptr empty for B\n"); + return -EINVAL; + } + if (copy_to_user(user_gamma.table_b, oper_state->gamma.table_b, + (table_size * sizeof(struct ipipe_gamma_entry)))) { + dev_err(dev, + "set_gamma_params: B-Table Error in copy to user\n"); + return -EFAULT; + } + + if (!oper_state->gamma.bypass_g && !user_gamma.table_g) { + dev_err(dev, "get_gamma_params: table ptr empty for G\n"); + return -EINVAL; + } + if (copy_from_user(oper_state->gamma.table_g, user_gamma.table_g, + (table_size * sizeof(struct ipipe_gamma_entry)))) { + dev_err(dev, "set_gamma_params: G-Table copy error\n"); + return -EFAULT; + } + +copy_user_gama: + if (copy_to_user(gamma_param, &user_gamma, sizeof(struct prev_gamma))) { + dev_err(dev, "get_dfc_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_3d_lut_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + int i; + + if (!oper_state->lut_3d.en) + return 0; + + for (i = 0; i < MAX_SIZE_3D_LUT; i++) { + if (oper_state->lut_3d.table[i].r > D3_LUT_ENTRY_MASK || + oper_state->lut_3d.table[i].g > D3_LUT_ENTRY_MASK || + oper_state->lut_3d.table[i].b > D3_LUT_ENTRY_MASK) + return -EINVAL; + } + return 0; +} + +static int set_3d_lut_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_3d_lut *lut_param = (struct prev_3d_lut *)param; + struct prev_3d_lut user_3d_lut; + + if (!lut_param) { + /* Copy defaults for gamma */ + oper_state->lut_3d.en = dm365_3d_lut_defaults.en; + /* By default, 3D lut is disabled + */ + } else { + if (len != sizeof(struct prev_3d_lut)) { + dev_err(dev, "set_3d_lut_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_3d_lut, lut_param, + sizeof(struct prev_3d_lut))) { + dev_err(dev, "set_3d_lut_params: Error in copy to" + " kernel\n"); + return -EFAULT; + } + oper_state->lut_3d.en = user_3d_lut.en; + if (!user_3d_lut.table) { + dev_err(dev, "set_3d_lut_params: Invalid table ptr"); + return -EINVAL; + } + if (copy_from_user(oper_state->lut_3d.table, user_3d_lut.table, + (MAX_SIZE_3D_LUT * + sizeof(struct ipipe_3d_lut_entry)))) { + dev_err(dev, "set_3d_lut_params:Error" + " in copy to kernel\n"); + return -EFAULT; + } + + if (validate_3d_lut_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_3d_lut_regs(&oper_state->lut_3d); + + return 0; +} +static int get_3d_lut_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_3d_lut *lut_param = (struct prev_3d_lut *)param; + struct prev_3d_lut user_3d_lut; + + if (!lut_param) { + dev_err(dev, "get_3d_lut_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_3d_lut)) { + dev_err(dev, + "get_3d_lut_params: param struct length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_3d_lut, lut_param, + sizeof(struct prev_3d_lut))) { + dev_err(dev, "get_3d_lut_params: Error in copy to kernel\n"); + return -EFAULT; + } + + user_3d_lut.en = oper_state->lut_3d.en; + if (!user_3d_lut.table) { + dev_err(dev, "get_3d_lut_params:" " Invalid table ptr"); + return -EINVAL; + } + if (copy_to_user(user_3d_lut.table, oper_state->lut_3d.table, + (MAX_SIZE_3D_LUT * + sizeof(struct ipipe_3d_lut_entry)))) { + dev_err(dev, + "get_3d_lut_params:Table Error in" " copy to user\n"); + return -EFAULT; + } + + if (copy_to_user(lut_param, &user_3d_lut, + sizeof(struct prev_3d_lut))) { + dev_err(dev, "get_3d_lut_params: Error in copy" " to user\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_lum_adj_params(struct device *dev) +{ + /* nothing to validate */ + return 0; +} + +static int set_lum_adj_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_lum_adj *lum_adj_param = (struct prev_lum_adj *)param; + + if (!lum_adj_param) { + /* Copy defaults for Luminance adjustments */ + memcpy((void *)&oper_state->lum_adj, + (void *)&dm365_lum_adj_defaults, + sizeof(struct prev_lum_adj)); + } else { + if (len != sizeof(struct prev_lum_adj)) { + dev_err(dev, "set_lum_adj_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->lum_adj, lum_adj_param, + sizeof(struct prev_lum_adj))) { + dev_err(dev, "set_lum_adj_params: Error in copy" + " from user\n"); + return -EFAULT; + } + if (validate_lum_adj_params(dev) < 0) + return -EINVAL; + } + + ipipe_set_lum_adj_regs(&oper_state->lum_adj); + + return 0; +} + +static int get_lum_adj_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_lum_adj *lum_adj_param = (struct prev_lum_adj *)param; + + if (!lum_adj_param) { + dev_err(dev, "get_lum_adj_params: invalid user ptr"); + return -EINVAL; + } + + if (len != sizeof(struct prev_lum_adj)) { + dev_err(dev, + "get_lum_adj_params: param struct length mismatch\n"); + return -EINVAL; + } + + if (copy_to_user(lum_adj_param, &oper_state->lum_adj, + sizeof(struct prev_lum_adj))) { + dev_err(dev, "get_lum_adj_params: Error in copy to" " user\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_rgb2yuv_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + if (oper_state->rgb2yuv.coef_ry.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_ry.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_gy.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_gy.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_by.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_by.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_rcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_rcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_gcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_gcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_bcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_bcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_rcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_rcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_gcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_gcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_bcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_bcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.out_ofst_y > RGB2YCBCR_OFST_MASK || + oper_state->rgb2yuv.out_ofst_cb > RGB2YCBCR_OFST_MASK || + oper_state->rgb2yuv.out_ofst_cr > RGB2YCBCR_OFST_MASK) + return -EINVAL; + return 0; +} + +static int set_rgb2yuv_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_rgb2yuv *rgb2yuv_param = (struct prev_rgb2yuv *)param; + + if (!rgb2yuv_param) { + /* Copy defaults for rgb2yuv conversion */ + memcpy((void *)&oper_state->rgb2yuv, + (void *)&dm365_rgb2yuv_defaults, + sizeof(struct prev_rgb2yuv)); + } else { + if (len != sizeof(struct prev_rgb2yuv)) { + dev_err(dev, "set_rgb2yuv_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->rgb2yuv, + rgb2yuv_param, sizeof(struct prev_rgb2yuv))) { + dev_err(dev, "set_rgb2yuv_params: Error in copy from" + " user\n"); + return -EFAULT; + } + if (validate_rgb2yuv_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_rgb2ycbcr_regs(&oper_state->rgb2yuv); + + return 0; +} + +static int get_rgb2yuv_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_rgb2yuv *rgb2yuv_param = (struct prev_rgb2yuv *)param; + + if (!rgb2yuv_param) { + dev_err(dev, "get_rgb2yuv_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_rgb2yuv)) { + dev_err(dev, + "get_rgb2yuv_params: param struct length mismatch\n"); + return -EINVAL; + } + if (copy_to_user((struct prev_rgb2yuv *)rgb2yuv_param, + &oper_state->rgb2yuv, + sizeof(struct prev_rgb2yuv))) { + dev_err(dev, "get_rgb2yuv_params: Error in copy from" + " kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_gbce_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + u32 max = GBCE_Y_VAL_MASK; + int i; + + if (!oper_state->gbce.en) + return 0; + + if (oper_state->gbce.type == IPIPE_GBCE_GAIN_TBL) + max = GBCE_GAIN_VAL_MASK; + for (i = 0; i < MAX_SIZE_GBCE_LUT; i++) + if (oper_state->gbce.table[i] > max) + return -EINVAL; + return 0; +} +static int set_gbce_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_gbce *gbce_param = (struct prev_gbce *)param; + struct prev_gbce user_gbce; + + if (!gbce_param) { + /* Copy defaults for gamma */ + oper_state->gbce.en = dm365_gbce_defaults.en; + /* By default, GBCE is disabled + */ + } else { + if (len != sizeof(struct prev_gbce)) { + dev_err(dev, "set_gbce_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_gbce, gbce_param, + sizeof(struct prev_gbce))) { + dev_err(dev, "set_gbce_params: Error in copy to" + " kernel\n"); + return -EFAULT; + } + oper_state->gbce.en = user_gbce.en; + oper_state->gbce.type = user_gbce.type; + if (!user_gbce.table) { + dev_err(dev, "set_gbce_params:" " Invalid table ptr"); + return -EINVAL; + } + + if (copy_from_user(oper_state->gbce.table, + user_gbce.table, (MAX_SIZE_GBCE_LUT * + sizeof(unsigned short)))) { + dev_err(dev, "set_gbce_params:Error" + " in copy to kernel\n"); + return -EFAULT; + } + if (validate_gbce_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_gbce_regs(&oper_state->gbce); + + return 0; +} +static int get_gbce_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_gbce *gbce_param = (struct prev_gbce *)param; + struct prev_gbce user_gbce; + + if (!gbce_param) { + dev_err(dev, "get_gbce_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_gbce)) { + dev_err(dev, + "get_gbce_params: param struct length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_gbce, gbce_param, sizeof(struct prev_gbce))) { + dev_err(dev, "get_gbce_params: Error in copy to" " kernel\n"); + return -EFAULT; + } + + user_gbce.en = oper_state->gbce.en; + user_gbce.type = oper_state->gbce.type; + if (!user_gbce.table) { + dev_err(dev, "get_gbce_params:" " Invalid table ptr"); + return -EINVAL; + } + if (copy_to_user(user_gbce.table, oper_state->gbce.table, + (MAX_SIZE_GBCE_LUT * sizeof(unsigned short)))) { + dev_err(dev, + "get_gbce_params:Table Error in" " copy to user\n"); + return -EFAULT; + } + + if (copy_to_user(gbce_param, &user_gbce, sizeof(struct prev_gbce))) { + dev_err(dev, "get_gbce_params: Error in copy" " to user\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_yuv422_conv_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + if (oper_state->yuv422_conv.en_chrom_lpf > 1) + return -EINVAL; + return 0; +} + +static int set_yuv422_conv_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_yuv422_conv *yuv422_conv_param = + (struct prev_yuv422_conv *)param; + + if (!yuv422_conv_param) { + /* Copy defaults for yuv 422 conversion */ + memcpy((void *)&oper_state->yuv422_conv, + (void *)&dm365_yuv422_conv_defaults, + sizeof(struct prev_yuv422_conv)); + } else { + if (len != sizeof(struct prev_yuv422_conv)) { + dev_err(dev, "set_yuv422_conv_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->yuv422_conv, yuv422_conv_param, + sizeof(struct prev_yuv422_conv))) { + dev_err(dev, "set_yuv422_conv_params: Error in copy" + " from user\n"); + return -EFAULT; + } + if (validate_yuv422_conv_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_yuv422_conv_regs(&oper_state->yuv422_conv); + + return 0; +} +static int get_yuv422_conv_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_yuv422_conv *yuv422_conv_param = + (struct prev_yuv422_conv *)param; + + if (!yuv422_conv_param) { + dev_err(dev, "get_yuv422_conv_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_yuv422_conv)) { + dev_err(dev, "get_yuv422_conv_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_to_user(yuv422_conv_param, &oper_state->yuv422_conv, + sizeof(struct prev_yuv422_conv))) { + dev_err(dev, + "get_yuv422_conv_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_yee_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + int i; + + if (oper_state->yee.en > 1 || + oper_state->yee.en_halo_red > 1 || + oper_state->yee.hpf_shft > YEE_HPF_SHIFT_MASK) + return -EINVAL; + + if (oper_state->yee.hpf_coef_00 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_01 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_02 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_10 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_11 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_12 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_20 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_21 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_22 > YEE_COEF_MASK) + return -EINVAL; + + if (oper_state->yee.yee_thr > YEE_THR_MASK || + oper_state->yee.es_gain > YEE_ES_GAIN_MASK || + oper_state->yee.es_thr1 > YEE_ES_THR1_MASK || + oper_state->yee.es_thr2 > YEE_THR_MASK || + oper_state->yee.es_gain_grad > YEE_THR_MASK || + oper_state->yee.es_ofst_grad > YEE_THR_MASK) + return -EINVAL; + + for (i = 0; i < MAX_SIZE_YEE_LUT ; i++) + if (oper_state->yee.table[i] > YEE_ENTRY_MASK) + return -EINVAL; + return 0; +} +static int set_yee_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_yee *yee_param = (struct prev_yee *)param; + struct prev_yee user_yee; + short *temp_table; + + if (!yee_param) { + temp_table = oper_state->yee.table; + /* Copy defaults for ns */ + memcpy((void *)&oper_state->yee, (void *)&dm365_yee_defaults, + sizeof(struct prev_yee)); + oper_state->yee.table = temp_table; + } else { + if (len != sizeof(struct prev_yee)) { + dev_err(dev, "set_yee_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_yee, yee_param, + sizeof(struct prev_yee))) { + dev_err(dev, + "set_yee_params: Error in copy from user\n"); + return -EFAULT; + } + if (!user_yee.table) { + dev_err(dev, "set_yee_params: yee table ptr null\n"); + return -EINVAL; + } + if (copy_from_user(oper_state->yee.table, user_yee.table, + (MAX_SIZE_YEE_LUT * sizeof(short)))) { + dev_err(dev, + "set_yee_params: Error in copy from user\n"); + return -EFAULT; + } + temp_table = oper_state->yee.table; + memcpy(&oper_state->yee, &user_yee, sizeof(struct prev_yee)); + oper_state->yee.table = temp_table; + if (validate_yee_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_ee_regs(&oper_state->yee); + + return 0; +} +static int get_yee_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_yee *yee_param = (struct prev_yee *)param; + struct prev_yee user_yee; + short *temp_table; + + if (!yee_param) { + dev_err(dev, "get_yee_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_yee)) { + dev_err(dev, "get_yee_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_yee, yee_param, sizeof(struct prev_yee))) { + dev_err(dev, "get_yee_params: Error in copy to kernel\n"); + return -EFAULT; + } + if (!user_yee.table) { + dev_err(dev, "get_yee_params: yee table ptr null\n"); + return -EINVAL; + } + if (copy_to_user(user_yee.table, oper_state->yee.table, + (MAX_SIZE_YEE_LUT * sizeof(short)))) { + dev_err(dev, "get_yee_params: Error in copy from kernel\n"); + return -EFAULT; + } + temp_table = user_yee.table; + memcpy(&user_yee, &oper_state->yee, sizeof(struct prev_yee)); + user_yee.table = temp_table; + + if (copy_to_user(yee_param, &user_yee, sizeof(struct prev_yee))) { + dev_err(dev, "get_yee_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_car_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + if (oper_state->car.en > 1 || + oper_state->car.hpf_shft > CAR_HPF_SHIFT_MASK || + oper_state->car.gain1.shft > CAR_GAIN1_SHFT_MASK || + oper_state->car.gain1.gain_min > CAR_GAIN_MIN_MASK || + oper_state->car.gain2.shft > CAR_GAIN2_SHFT_MASK || + oper_state->car.gain2.gain_min > CAR_GAIN_MIN_MASK) + return -EINVAL; + return 0; +} + +static int set_car_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_car *car_param = (struct prev_car *)param; + + if (!car_param) { + /* Copy defaults for ns */ + memcpy((void *)&oper_state->car, (void *)&dm365_car_defaults, + sizeof(struct prev_car)); + } else { + if (len != sizeof(struct prev_car)) { + dev_err(dev, "set_car_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->car, car_param, + sizeof(struct prev_car))) { + dev_err(dev, + "set_car_params: Error in copy from user\n"); + return -EFAULT; + } + if (validate_car_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_car_regs(&oper_state->car); + + return 0; +} +static int get_car_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_car *car_param = (struct prev_car *)param; + + if (!car_param) { + dev_err(dev, "get_car_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_car)) { + dev_err(dev, "get_car_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_to_user(car_param, &oper_state->car, + sizeof(struct prev_car))) { + dev_err(dev, "get_car_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_cgs_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + if (oper_state->cgs.en > 1 || + oper_state->cgs.h_shft > CAR_SHIFT_MASK) + return -EINVAL; + return 0; +} + +static int set_cgs_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_cgs *cgs_param = (struct prev_cgs *)param; + + if (!cgs_param) { + /* Copy defaults for ns */ + memcpy((void *)&oper_state->cgs, (void *)&dm365_cgs_defaults, + sizeof(struct prev_cgs)); + } else { + if (len != sizeof(struct prev_cgs)) { + dev_err(dev, "set_cgs_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->cgs, cgs_param, + sizeof(struct prev_cgs))) { + dev_err(dev, + "set_cgs_params: Error in copy from user\n"); + return -EFAULT; + } + if (validate_cgs_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_cgs_regs(&oper_state->cgs); + + return 0; +} + +static int get_cgs_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_cgs *cgs_param = (struct prev_cgs *)param; + + if (!cgs_param) { + dev_err(dev, "get_cgs_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_cgs)) { + dev_err(dev, "get_cgs_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_to_user(cgs_param, &oper_state->cgs, + sizeof(struct prev_cgs))) { + dev_err(dev, "get_cgs_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static struct prev_module_if prev_modules[PREV_MAX_MODULES] = { + { + .version = "5.1", + .module_id = PREV_LUTDPC, + .module_name = "LUT Defect Correction", + .control = 0, + .path = IMP_RAW2RAW | IMP_RAW2YUV, + .set = set_lutdpc_params, + .get = get_lutdpc_params + }, + { + .version = "5.1", + .module_id = PREV_OTFDPC, + .module_name = "OTF Defect Pixel Correction", + .control = 0, + .path = IMP_RAW2RAW | IMP_RAW2YUV, + .set = set_otfdpc_params, + .get = get_otfdpc_params + }, + { + .version = "5.1", + .module_id = PREV_NF1, + .module_name = "2-D Noise filter - 1", + .control = 0, + .path = IMP_RAW2RAW | IMP_RAW2YUV, + .set = set_nf1_params, + .get = get_nf1_params + }, + { + .version = "5.1", + .module_id = PREV_NF2, + .module_name = "2-D Noise filter - 2", + .control = 0, + .path = IMP_RAW2RAW | IMP_RAW2YUV, + .set = set_nf2_params, + .get = get_nf2_params + }, + { + .version = "5.1", + .module_id = PREV_GIC, + .module_name = "Green Imbalance Correction", + .control = 0, + .path = IMP_RAW2RAW | IMP_RAW2YUV, + .set = set_gic_params, + .get = get_gic_params + }, + { + .version = "5.1", + .module_id = PREV_WB, + .module_name = "White balance", + .control = 1, + .path = IMP_RAW2RAW | IMP_RAW2YUV, + .set = set_wb_params, + .get = get_wb_params + }, + { + .version = "5.1", + .module_id = PREV_CFA, + .module_name = "CFA Interpolation", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_cfa_params, + .get = get_cfa_params + }, + { + .version = "5.1", + .module_id = PREV_RGB2RGB_1, + .module_name = "RGB-RGB Conversion - 1", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_rgb2rgb_1_params, + .get = get_rgb2rgb_1_params + }, + { + .version = "5.1", + .module_id = PREV_GAMMA, + .module_name = "Gamma Correction", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_gamma_params, + .get = get_gamma_params + }, + { + .version = "5.1", + .module_id = PREV_RGB2RGB_2, + .module_name = "RGB-RGB Conversion - 2", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_rgb2rgb_2_params, + .get = get_rgb2rgb_2_params + }, + { + .version = "5.1", + .module_id = PREV_3D_LUT, + .module_name = "3D LUT", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_3d_lut_params, + .get = get_3d_lut_params + }, + { + .version = "5.1", + .module_id = PREV_RGB2YUV, + .module_name = "RGB-YCbCr conversion", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_rgb2yuv_params, + .get = get_rgb2yuv_params + }, + { + .version = "5.1", + .module_id = PREV_GBCE, + .module_name = "Global Brightness,Contrast Control", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_gbce_params, + .get = get_gbce_params + }, + { + .version = "5.1", + .module_id = PREV_YUV422_CONV, + .module_name = "YUV 422 conversion", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_yuv422_conv_params, + .get = get_yuv422_conv_params + }, + { + .version = "5.1", + .module_id = PREV_LUM_ADJ, + .module_name = "Luminance Adjustment", + .control = 1, + .path = IMP_RAW2YUV, + .set = set_lum_adj_params, + .get = get_lum_adj_params + }, + { + .version = "5.1", + .module_id = PREV_YEE, + .module_name = "Edge Enhancer", + .control = 1, + .path = IMP_RAW2YUV | IMP_YUV2YUV, + .set = set_yee_params, + .get = get_yee_params + }, + { + .version = "5.1", + .module_id = PREV_CAR, + .module_name = "Chromatic Artifact Reduction", + .control = 1, + .path = IMP_RAW2YUV | IMP_YUV2YUV, + .set = set_car_params, + .get = get_car_params + }, + { + .version = "5.1", + .module_id = PREV_CGS, + .module_name = "Chromatic Gain Suppression", + .control = 1, + .path = IMP_RAW2YUV | IMP_YUV2YUV, + .set = set_cgs_params, + .get = get_cgs_params + } +}; + +static struct prev_module_if *prev_enum_preview_cap(struct device *dev, + unsigned int index) +{ + dev_dbg(dev, "prev_enum_preview_cap: index = %d\n", index); + + if (index < 0 || index >= PREV_MAX_MODULES) + return NULL; + + return &prev_modules[index]; +} + +static int ipipe_set_oper_mode(void *ipipe, unsigned int mode) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + if (oper_state->oper_mode != IMP_MODE_NOT_CONFIGURED) { + printk(KERN_ERR "IPIPE is already active!\n"); + return -EINVAL; + } + oper_state->oper_mode = mode; + + return 0; +} + +static void ipipe_reset_oper_mode(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + oper_state->oper_mode = IMP_MODE_NOT_CONFIGURED; + oper_state->prev_config_state = STATE_NOT_CONFIGURED; + oper_state->rsz_config_state = STATE_NOT_CONFIGURED; + oper_state->rsz_chained = 0; + oper_state->rsz_output_enabled[RSZ_A] = DISABLE; + oper_state->rsz_output_enabled[RSZ_B] = DISABLE; +} + +static unsigned int prev_get_oper_mode(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return oper_state->oper_mode; +} + +static unsigned int ipipe_get_oper_state(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return oper_state->state; +} + +static void ipipe_set_oper_state(void *ipipe, unsigned int state) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + mutex_lock(&oper_state->lock); + oper_state->state = state; + mutex_unlock(&oper_state->lock); +} + +static unsigned int ipipe_get_prev_config_state(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return oper_state->prev_config_state; +} + +static unsigned int ipipe_get_rsz_config_state(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return oper_state->rsz_config_state; +} + +/* function: calculate_normal_f_div_param + * Algorithm to calculate the frame division parameters for resizer. + * in normal mode. Please refer the application note in DM360 functional + * spec for details of the algorithm + */ +static int calculate_normal_f_div_param(struct device *dev, + int input_width, + int output_width, + struct ipipe_rsz_rescale_param *param) +{ + /* rsz = R, input_width = H, output width = h in the equation */ + unsigned int val1; + unsigned int rsz; + unsigned int val; + unsigned int h1; + unsigned int h2; + unsigned int o; + + if (output_width > input_width) { + dev_err(dev, "frame div mode is used for scale down only\n"); + return -EINVAL; + } + + rsz = (input_width << 8) / output_width; + val = rsz << 1; + val = ((input_width << 8) / val) + 1; + o = 14; + if (!(val % 2)) { + h1 = val; + } else { + val = (input_width << 7); + val -= rsz >> 1; + val /= rsz << 1; + val <<= 1; + val += 2; + o += ((CEIL(rsz, 1024)) << 1); + h1 = val; + } + h2 = output_width - h1; + /* phi */ + val = (h1 * rsz) - (((input_width >> 1) - o) << 8); + /* skip */ + val1 = ((val - 1024) >> 9) << 1; + param->f_div.num_passes = IPIPE_MAX_PASSES; + param->f_div.pass[0].o_hsz = h1 - 1; + param->f_div.pass[0].i_hps = 0; + param->f_div.pass[0].h_phs = 0; + param->f_div.pass[0].src_hps = 0; + param->f_div.pass[0].src_hsz = (input_width >> 2) + o; + param->f_div.pass[1].o_hsz = h2 - 1; + param->f_div.pass[1].i_hps = val1; + param->f_div.pass[1].h_phs = (val - (val1 << 8)); + param->f_div.pass[1].src_hps = (input_width >> 2) - o; + param->f_div.pass[1].src_hsz = (input_width >> 2) + o; + + return 0; +} + +/* function: calculate_down_scale_f_div_param + * Algorithm to calculate the frame division parameters for resizer in + * downscale mode. Please refer the application note in DM360 functional + * spec for details of the algorithm + */ +static int calculate_down_scale_f_div_param(struct device *dev, + int input_width, + int output_width, + struct ipipe_rsz_rescale_param + *param) +{ + /* rsz = R, input_width = H, output width = h in the equation */ + unsigned int two_power; + unsigned int upper_h1; + unsigned int upper_h2; + unsigned int val1; + unsigned int val; + unsigned int rsz; + unsigned int h1; + unsigned int h2; + unsigned int o; + unsigned int n; + + upper_h1 = input_width >> 1; + n = param->h_dscale_ave_sz; + /* 2 ^ (scale+1) */ + two_power = 1 << (n + 1); + upper_h1 = (upper_h1 >> (n + 1)) << (n + 1); + upper_h2 = input_width - upper_h1; + if (upper_h2 % two_power) { + dev_err(dev, "frame halves to be a multiple of 2 power n+1\n"); + return -EINVAL; + } + two_power = 1 << n; + rsz = (input_width << 8) / output_width; + val = rsz * two_power; + val = ((upper_h1 << 8) / val) + 1; + if (!(val % 2)) + h1 = val; + else { + val = upper_h1 << 8; + val >>= n + 1; + val -= rsz >> 1; + val /= rsz << 1; + val <<= 1; + val += 2; + h1 = val; + } + o = 10 + (two_power << 2); + if (((input_width << 7) / rsz) % 2) + o += (((CEIL(rsz, 1024)) << 1) << n); + h2 = output_width - h1; + /* phi */ + val = (h1 * rsz) - (((upper_h1 - (o - 10)) / two_power) << 8); + /* skip */ + val1 = ((val - 1024) >> 9) << 1; + param->f_div.num_passes = IPIPE_MAX_PASSES; + param->f_div.pass[0].o_hsz = h1 - 1; + param->f_div.pass[0].i_hps = 0; + param->f_div.pass[0].h_phs = 0; + param->f_div.pass[0].src_hps = 0; + param->f_div.pass[0].src_hsz = upper_h1 + o; + param->f_div.pass[1].o_hsz = h2 - 1; + param->f_div.pass[1].i_hps = 10 + (val1 * two_power); + param->f_div.pass[1].h_phs = (val - (val1 << 8)); + param->f_div.pass[1].src_hps = upper_h1 - o; + param->f_div.pass[1].src_hsz = upper_h2 + o; + + return 0; +} + +/* update the parameter in param for a given input and output width */ +static int update_preview_f_div_params(struct device *dev, + int input_width, + int output_width, + struct ipipe_rsz_rescale_param *param) +{ + unsigned int val; + + val = input_width >> 1; + if (val < 8) { + dev_err(dev, "input width must me atleast 16 pixels\n"); + return -EINVAL; + } + param->f_div.en = 1; + param->f_div.num_passes = IPIPE_MAX_PASSES; + param->f_div.pass[0].o_hsz = val; + param->f_div.pass[0].i_hps = 0; + param->f_div.pass[0].h_phs = 0; + param->f_div.pass[0].src_hps = 0; + param->f_div.pass[0].src_hsz = val + 10; + param->f_div.pass[1].o_hsz = val; + param->f_div.pass[1].i_hps = 0; + param->f_div.pass[1].h_phs = 0; + param->f_div.pass[1].src_hps = val - 8; + param->f_div.pass[1].src_hsz = val + 10; + + return 0; +} + +/* Use shared to allocate exclusive blocks as required + * by resize applications in single shot mode + */ +static void *ipipe_alloc_config_block(struct device *dev, void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + /* return common data block */ + mutex_lock(&oper_state->lock); + if (oper_state->resource_in_use) { + dev_err(dev, "resource in use\n"); + mutex_unlock(&oper_state->lock); + return NULL; + } + mutex_unlock(&oper_state->lock); + + return oper_state->shared_config_param; +} + +/* Used to free only non-shared config block allocated through + * imp_alloc_config_block + */ +static void ipipe_dealloc_config_block(struct device *dev, void *ipipe, + void *config_block) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + if (!config_block) + return; + + if (config_block != oper_state->shared_config_param) + kfree(config_block); + else + dev_err(dev, "Trying to free shared config block\n"); +} + +static void ipipe_dealloc_user_config_block(struct device *dev, void *ipipe, + void *config_block) +{ + kfree(config_block); +} + +static void *ipipe_alloc_user_config_block(struct device *dev, void *ipipe, + enum imp_log_chan_t chan_type, + int *len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + void *config = NULL; + + if (oper_state->oper_mode == IMP_MODE_SINGLE_SHOT) { + if (chan_type == IMP_PREVIEWER) { + config = + kmalloc(sizeof(struct prev_single_shot_config), + GFP_KERNEL); + *len = sizeof(struct prev_single_shot_config); + } else if (chan_type == IMP_RESIZER) { + config = + kmalloc(sizeof(struct rsz_single_shot_config), + GFP_KERNEL); + *len = sizeof(struct rsz_single_shot_config); + } + return config; + } + + if (chan_type == IMP_PREVIEWER) { + config = kmalloc(sizeof(struct prev_continuous_config), + GFP_KERNEL); + *len = sizeof(struct prev_continuous_config); + } else if (chan_type == IMP_RESIZER) { + config = kmalloc(sizeof(struct rsz_continuous_config), + GFP_KERNEL); + *len = sizeof(struct rsz_continuous_config); + } + + return config; +} + +static void ipipe_set_user_config_defaults(struct device *dev, void *ipipe, + enum imp_log_chan_t chan_type, + void *config) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + dev_dbg(dev, "ipipe_set_user_config_defaults\n"); + + if (oper_state->oper_mode == IMP_MODE_SINGLE_SHOT) { + if (chan_type == IMP_PREVIEWER) { + dev_dbg(dev, "SS-Preview\n"); + /* preview channel in single shot mode */ + memcpy(config, (void *)&dm365_prev_ss_config_defs, + sizeof(struct prev_single_shot_config)); + } else { + dev_dbg(dev, "SS-Resize\n"); + /* resizer channel in single shot mode */ + memcpy(config, (void *)&dm365_rsz_ss_config_defs, + sizeof(struct rsz_single_shot_config)); + } + } else if (oper_state->oper_mode == IMP_MODE_CONTINUOUS) { + /* Continuous mode */ + if (chan_type == IMP_PREVIEWER) { + dev_dbg(dev, "Cont Preview\n"); + /* previewer defaults */ + memcpy(config, (void *)&dm365_prev_cont_config_defs, + sizeof(struct prev_continuous_config)); + } else { + dev_dbg(dev, "Cont resize\n"); + /* resizer defaults */ + memcpy(config, (void *)&dm365_rsz_cont_config_defs, + sizeof(struct rsz_continuous_config)); + } + } else { + dev_err(dev, "Incorrect mode used\n"); + } +} + +/* function :calculate_sdram_offsets() + * This function calculates the offsets from start of buffer for the C + * plane when output format is YUV420SP. It also calculates the offsets + * from the start of the buffer when the image is flipped vertically + * or horizontally for ycbcr/y/c planes + */ +static int calculate_sdram_offsets(struct ipipe_params *param, int index) +{ + int bytesperpixel = 2; + int image_height; + int image_width; + int yuv_420; + int offset; + + if (!param->rsz_en[index]) + return -EINVAL; + + image_height = param->rsz_rsc_param[index].o_vsz + 1; + image_width = param->rsz_rsc_param[index].o_hsz + 1; + param->ext_mem_param[index].c_offset = 0; + param->ext_mem_param[index].flip_ofst_y = 0; + param->ext_mem_param[index].flip_ofst_c = 0; + if (param->ipipe_dpaths_fmt != IPIPE_RAW2RAW && + param->ipipe_dpaths_fmt != IPIPE_RAW2BOX && + param->rsz_rsc_param[index].cen && + param->rsz_rsc_param[index].yen) { + /* YUV 420 */ + yuv_420 = 1; + bytesperpixel = 1; + } + + /* set offset value */ + offset = 0; + + if (param->rsz_rsc_param[index].h_flip) + /* width * bytesperpixel - 1 */ + offset = (image_width * bytesperpixel) - 1; + if (param->rsz_rsc_param[index].v_flip) + offset += (image_height - 1) * + param->ext_mem_param[index].rsz_sdr_oft_y; + param->ext_mem_param[index].flip_ofst_y = offset; + if (yuv_420) { + offset = 0; + /* half height for c-plane */ + if (param->rsz_rsc_param[index].h_flip) + /* width * bytesperpixel - 1 */ + offset = image_width - 1; + if (param->rsz_rsc_param[index].v_flip) + offset += (((image_height >> 1) - 1) * + param->ext_mem_param[index]. + rsz_sdr_oft_c); + param->ext_mem_param[index].flip_ofst_c = + offset; + param->ext_mem_param[index].c_offset = + param->ext_mem_param[index]. + rsz_sdr_oft_y * image_height; + } + + return 0; +} + +static void enable_422_420_conversion(struct ipipe_params *param, + int index, boolean_t en) +{ + /* Enable 422 to 420 conversion */ + param->rsz_rsc_param[index].cen = en; + param->rsz_rsc_param[index].yen = en; +} + +static void configure_resizer_out_params(struct ipipe_oper_state *oper_state, + struct ipipe_params *param, + int index, void *output_spec, + unsigned char partial, unsigned flag) +{ + struct rsz_output_spec *output; + + if (partial) { + struct rsz_part_output_spec *partial_output = + (struct rsz_part_output_spec *)output_spec; + if (!oper_state->rsz_output_enabled[index]) { + param->rsz_en[index] = DISABLE; + return; + } + param->rsz_en[index] = ENABLE; + param->rsz_rsc_param[index].h_flip = partial_output->h_flip; + param->rsz_rsc_param[index].v_flip = partial_output->v_flip; + param->rsz_rsc_param[index].v_typ_y = partial_output->v_typ_y; + param->rsz_rsc_param[index].v_typ_c = partial_output->v_typ_c; + param->rsz_rsc_param[index].v_lpf_int_y = + partial_output->v_lpf_int_y; + param->rsz_rsc_param[index].v_lpf_int_c = + partial_output->v_lpf_int_c; + param->rsz_rsc_param[index].h_typ_y = partial_output->h_typ_y; + param->rsz_rsc_param[index].h_typ_c = partial_output->h_typ_c; + param->rsz_rsc_param[index].h_lpf_int_y = + partial_output->h_lpf_int_y; + param->rsz_rsc_param[index].h_lpf_int_c = + partial_output->h_lpf_int_c; + param->rsz_rsc_param[index].dscale_en = + partial_output->en_down_scale; + param->rsz_rsc_param[index].h_dscale_ave_sz = + partial_output->h_dscale_ave_sz; + param->rsz_rsc_param[index].v_dscale_ave_sz = + partial_output->v_dscale_ave_sz; + param->ext_mem_param[index].user_y_ofst = + (partial_output->user_y_ofst + 31) & ~0x1f; + param->ext_mem_param[index].user_c_ofst = + (partial_output->user_c_ofst + 31) & ~0x1f; + return; + } + + output = (struct rsz_output_spec *)output_spec; + + if (!oper_state->rsz_output_enabled[index]) { + param->rsz_en[index] = DISABLE; + return; + } + + param->rsz_en[index] = ENABLE; + param->rsz_rsc_param[index].o_vsz = + oper_state->out_mbus_format[index].height - 1; + param->rsz_rsc_param[index].o_hsz = + oper_state->out_mbus_format[index].width - 1; + param->ext_mem_param[index].rsz_sdr_ptr_s_y = + output->vst_y; + param->ext_mem_param[index].rsz_sdr_ptr_e_y = + oper_state->out_mbus_format[index].height; + param->ext_mem_param[index].rsz_sdr_ptr_s_c = + output->vst_c; + param->ext_mem_param[index].rsz_sdr_ptr_e_c = + oper_state->out_mbus_format[index].height; + + if (!flag) + return; + /* update common parameters */ + param->rsz_rsc_param[index].h_flip = output->h_flip; + param->rsz_rsc_param[index].v_flip = output->v_flip; + param->rsz_rsc_param[index].v_typ_y = output->v_typ_y; + param->rsz_rsc_param[index].v_typ_c = output->v_typ_c; + param->rsz_rsc_param[index].v_lpf_int_y = output->v_lpf_int_y; + param->rsz_rsc_param[index].v_lpf_int_c = output->v_lpf_int_c; + param->rsz_rsc_param[index].h_typ_y = output->h_typ_y; + param->rsz_rsc_param[index].h_typ_c = output->h_typ_c; + param->rsz_rsc_param[index].h_lpf_int_y = output->h_lpf_int_y; + param->rsz_rsc_param[index].h_lpf_int_c = output->h_lpf_int_c; + param->rsz_rsc_param[index].dscale_en = output->en_down_scale; + param->rsz_rsc_param[index].h_dscale_ave_sz = output->h_dscale_ave_sz; + param->rsz_rsc_param[index].v_dscale_ave_sz = output->h_dscale_ave_sz; + param->ext_mem_param[index].user_y_ofst = + (output->user_y_ofst + 31) & ~0x1f; + param->ext_mem_param[index].user_c_ofst = + (output->user_c_ofst + 31) & ~0x1f; +} + +/* function :calculate_line_length() + * This function calculates the line length of various image + * planes at the input and output + */ +static void calculate_line_length(enum v4l2_mbus_pixelcode pix, + int width, + int height, int *line_len, int *line_len_c) +{ + *line_len = 0; + *line_len_c = 0; + + if (pix == V4L2_MBUS_FMT_UYVY8_2X8 || + pix == V4L2_MBUS_FMT_SBGGR10_1X10) { + *line_len = width << 1; + } else if (pix == V4L2_MBUS_FMT_Y8_1X8 || + pix == V4L2_MBUS_FMT_UV8_1X8) { + *line_len = width; + *line_len_c = width; + } else { + /* YUV 420 */ + /* round width to upper 32 byte boundary */ + *line_len = width; + *line_len_c = width; + } + /* adjust the line len to be a multiple of 32 */ + *line_len += 31; + *line_len &= ~0x1f; + *line_len_c += 31; + *line_len_c &= ~0x1f; +} + +static inline int rsz_validate_input_image_format(struct device *dev, + enum v4l2_mbus_pixelcode pix, + int width, + int height, int *line_len) +{ + int val; + + if (pix != V4L2_MBUS_FMT_UYVY8_2X8 && pix != V4L2_MBUS_FMT_Y8_1X8 && + pix != V4L2_MBUS_FMT_UV8_1X8) { + dev_err(dev, "rsz_validate_in_pix_formats" + "pix format not supported, %d\n", pix); + return -EINVAL; + } + + if (width == 0 || height == 0) { + dev_err(dev, "validate_line_length: invalid " + "width or height\n"); + return -EINVAL; + } + + if (pix == V4L2_MBUS_FMT_UV8_1X8) + calculate_line_length(pix, width, height, &val, line_len); + else + calculate_line_length(pix, width, height, line_len, &val); + + return 0; +} + +static inline int rsz_validate_output_image_format(struct device *dev, + enum v4l2_mbus_pixelcode pix, int width, + int height, int *in_line_len, + int *in_line_len_c) +{ + if (pix != V4L2_MBUS_FMT_UYVY8_2X8 && pix != V4L2_MBUS_FMT_Y8_1X8 && + pix != V4L2_MBUS_FMT_UV8_1X8 && pix != V4L2_MBUS_FMT_NV12_1X20 && + pix != V4L2_MBUS_FMT_SBGGR10_1X10) { + dev_err(dev, "rsz_validate_out_pix_formats" + "pix format not supported, %d\n", pix); + return -EINVAL; + } + + if (width == 0 || height == 0) { + dev_err(dev, "validate_line_length: invalid" + " width or height\n"); + return -EINVAL; + } + + calculate_line_length(pix, width, height, in_line_len, in_line_len_c); + return 0; +} + +static void configure_common_rsz_params(struct device *dev, + struct ipipe_params *param, + struct rsz_single_shot_config *ss_config) +{ + param->rsz_common.yuv_y_min = ss_config->yuv_y_min; + param->rsz_common.yuv_y_max = ss_config->yuv_y_max; + param->rsz_common.yuv_c_min = ss_config->yuv_c_min; + param->rsz_common.yuv_c_max = ss_config->yuv_c_max; + param->rsz_common.out_chr_pos = ss_config->out_chr_pos; + param->rsz_common.rsz_seq_crv = ss_config->chroma_sample_even; + +} + +static int configure_common_rsz_in_params(struct device *dev, + struct ipipe_oper_state *oper_state, + struct ipipe_params *param, + int flag, int rsz_chained, + void *input_spec) +{ + enum v4l2_mbus_pixelcode pix; + + pix = oper_state->in_mbus_format.code; + + if (!flag) { + param->rsz_common.vsz = oper_state->in_mbus_format.width - 1; + param->rsz_common.hsz = oper_state->in_mbus_format.height - 1; + } else { + struct rsz_ss_input_spec *in_specs = + (struct rsz_ss_input_spec *)input_spec; + if (!rsz_chained) { + param->rsz_common.vps = in_specs->vst; + param->rsz_common.hps = in_specs->hst; + } + param->rsz_common.vsz = oper_state->in_mbus_format.height - 1; + param->rsz_common.hsz = oper_state->in_mbus_format.width - 1; + } + switch (pix) { + case V4L2_MBUS_FMT_SBGGR8_1X8: + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + case V4L2_MBUS_FMT_SBGGR12_1X12: + case V4L2_MBUS_FMT_SBGGR10_1X10: + param->rsz_common.src_img_fmt = RSZ_IMG_422; + param->rsz_common.source = IPIPE_DATA; + break; + case V4L2_MBUS_FMT_UYVY8_2X8: + param->rsz_common.src_img_fmt = RSZ_IMG_422; + if (rsz_chained) + param->rsz_common.source = IPIPE_DATA; + else + param->rsz_common.source = IPIPEIF_DATA; + param->rsz_common.raw_flip = 0; + break; + case V4L2_MBUS_FMT_Y8_1X8: + param->rsz_common.src_img_fmt = RSZ_IMG_420; + /* Select y */ + param->rsz_common.y_c = 0; + param->rsz_common.source = IPIPEIF_DATA; + param->rsz_common.raw_flip = 0; + break; + case V4L2_MBUS_FMT_UV8_1X8: + param->rsz_common.src_img_fmt = RSZ_IMG_420; + /* Select y */ + param->rsz_common.y_c = 1; + param->rsz_common.source = IPIPEIF_DATA; + param->rsz_common.raw_flip = 0; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int validate_ipipeif_decimation(struct device *dev, + enum ipipeif_decimation dec_en, + unsigned char rsz, + unsigned char frame_div_mode_en, + int width) +{ + if (dec_en && frame_div_mode_en) { + dev_err(dev, "Both dec_en & frame_div_mode_en" + "can not enabled simultaneously\n"); + return -EINVAL; + } + if (frame_div_mode_en) { + dev_err(dev, "frame_div_mode mode not supported"); + return -EINVAL; + } + if (!dec_en) + return 0; + + if (width <= IPIPE_MAX_INPUT_WIDTH) { + dev_err(dev, "image width to be more than" + " %d for decimation\n", IPIPE_MAX_INPUT_WIDTH); + return -EINVAL; + } + if (rsz < IPIPEIF_RSZ_MIN || rsz > IPIPEIF_RSZ_MAX) { + dev_err(dev, "rsz range is %d to %d\n", + IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX); + return -EINVAL; + } + + return 0; +} + +static int ipipe_set_input_win(struct ipipe_oper_state *oper_state, + struct v4l2_mbus_framefmt *format) +{ + struct ipipe_params *param = oper_state->shared_config_param; + struct imp_window win; + + if (oper_state->oper_mode == IMP_MODE_SINGLE_SHOT) + return 0; + + /* init */ + win.width = format->width; + win.height = format->height; + win.hst = 0; + win.vst = 1; + + if (param->ipipeif_param.decimation) + param->ipipe_hsz = ((win.width * IPIPEIF_RSZ_CONST) / + param->ipipeif_param.rsz) - 1; + else + param->ipipe_hsz = win.width - 1; + + if (oper_state->in_mbus_format.field == V4L2_FIELD_INTERLACED) { + param->ipipe_vsz = (win.height >> 1) - 1; + param->ipipe_vps = win.vst >> 1; + } else { + param->ipipe_vsz = win.height - 1; + param->ipipe_vps = win.vst; + } + param->ipipe_hps = win.hst; + param->rsz_common.vsz = param->ipipe_vsz; + param->rsz_common.hsz = param->ipipe_hsz; + + return 0; +} + +static int ipipe_set_out_pixel_format(struct ipipe_oper_state *oper_state, + struct v4l2_mbus_framefmt *format) +{ + struct ipipe_params *param = oper_state->shared_config_param; + enum v4l2_mbus_pixelcode output_pix; + enum v4l2_mbus_pixelcode input_pix; + + input_pix = oper_state->in_mbus_format.code; + output_pix = oper_state->out_mbus_format[RSZ_A].code; + + /* if image is RAW, preserve raw image format while flipping. + * otherwise preserve, preserve ycbcr format while flipping + */ + if (output_pix == V4L2_MBUS_FMT_SBGGR10_1X10) + param->rsz_common.raw_flip = 1; + else + param->rsz_common.raw_flip = 0; + + return ipipe_process_pix_fmts(input_pix, output_pix, param); +} + +static int ipipe_set_output_win(struct ipipe_oper_state *oper_state, + struct v4l2_mbus_framefmt *format) +{ + struct ipipe_params *param = oper_state->shared_config_param; + struct rsz_output_spec output_specs; + enum v4l2_mbus_pixelcode out_pix; + struct imp_window win; + int ret = -EINVAL; + int line_len_c; + int line_len; + + /* for single-shot output window settings + are done as part of set config */ + if (oper_state->oper_mode == IMP_MODE_SINGLE_SHOT) + return 0; + + win.width = format->width; + /* Always set output height same as in height + for de-interlacing + */ + win.height = format->height; + win.hst = 0; + win.vst = 0; + + if (!param->rsz_en[RSZ_A]) { + printk(KERN_ERR "resizer output1 not enabled\n"); + return ret; + } + + output_specs.vst_y = win.vst; + + if (oper_state->out_mbus_format[RSZ_A].code == V4L2_MBUS_FMT_NV12_1X20) + output_specs.vst_c = win.vst; + + configure_resizer_out_params(oper_state, param, RSZ_A, + &output_specs, 0, 0); + out_pix = oper_state->out_mbus_format[RSZ_A].code; + calculate_line_length(out_pix, param->rsz_rsc_param[0].o_hsz + 1, + param->rsz_rsc_param[0].o_vsz + 1, &line_len, + &line_len_c); + param->ext_mem_param[0].rsz_sdr_oft_y = line_len; + param->ext_mem_param[0].rsz_sdr_oft_c = line_len_c; + calculate_resize_ratios(param, RSZ_A); + + if (param->rsz_en[RSZ_B]) + calculate_resize_ratios(param, RSZ_B); + + if (oper_state->out_mbus_format[RSZ_A].code == V4L2_MBUS_FMT_NV12_1X20) + enable_422_420_conversion(param, RSZ_A, ENABLE); + else + enable_422_420_conversion(param, RSZ_A, DISABLE); + + ret = calculate_sdram_offsets(param, RSZ_A); + if (param->rsz_en[RSZ_B]) + ret = calculate_sdram_offsets(param, RSZ_B); + + if (ret) + printk(KERN_ERR "error in calculating sdram offsets\n"); + + return ret; +} + +static int configure_formats_in_cont_mode(struct ipipe_oper_state *oper_state) +{ + struct v4l2_mbus_framefmt *in_format = &oper_state->in_mbus_format; + struct v4l2_mbus_framefmt *out_format = + &oper_state->out_mbus_format[RSZ_A]; + struct ipipe_params *param = oper_state->shared_config_param; + int ret = 0; + + param->rsz_common.src_img_fmt = RSZ_IMG_422; + + ret = ipipe_set_input_win(oper_state, in_format); + if (ret) + return ret; + + /* output format */ + ret = ipipe_set_out_pixel_format(oper_state, out_format); + if (ret) + return ret; + + return ipipe_set_output_win(oper_state, out_format); +} + +static int configure_resizer_in_ss_mode(struct device *dev, + struct ipipe_oper_state *oper_state, + void *user_config, + int resizer_chained, + struct ipipe_params *param) +{ + /* resizer in standalone mode. In this mode if serializer + * is enabled, we need to set config params in the hw. + */ + struct rsz_single_shot_config *ss_config = + (struct rsz_single_shot_config *)user_config; + enum v4l2_mbus_pixelcode output1_pix; + enum v4l2_mbus_pixelcode output2_pix; + enum v4l2_mbus_pixelcode input_pix; + int line_len_c; + int line_len; + int ret; + + input_pix = oper_state->in_mbus_format.code; + /* If input to IPIPE is BAYER format, for resizer it will + be UYVY. + */ + if (input_pix == V4L2_MBUS_FMT_SBGGR10_1X10) + input_pix = V4L2_MBUS_FMT_UYVY8_2X8; + + ret = rsz_validate_input_image_format(dev, input_pix, oper_state-> + in_mbus_format.width, + oper_state->in_mbus_format.height, + &line_len); + + if (ret) + return -EINVAL; + + ret = mutex_lock_interruptible(&oper_state->lock); + if (ret) + return ret; + if (!ss_config->input.line_length) + param->ipipeif_param.adofs = line_len; + else { + param->ipipeif_param.adofs = ss_config->input.line_length; + param->ipipeif_param.adofs = + (param->ipipeif_param.adofs + 31) & ~0x1f; + } + + if (oper_state->rsz_output_enabled[RSZ_A]) { + param->rsz_en[RSZ_A] = ENABLE; + param->rsz_rsc_param[RSZ_A].mode = IPIPEIF_ONE_SHOT; + + output1_pix = oper_state->out_mbus_format[RSZ_A].code; + + ret = rsz_validate_output_image_format(dev, output1_pix, + oper_state-> + out_mbus_format[RSZ_A]. + width, oper_state-> + out_mbus_format[RSZ_A]. + height, &line_len, &line_len_c); + if (ret) { + mutex_unlock(&oper_state->lock); + return ret; + } + param->ext_mem_param[RSZ_A].rsz_sdr_oft_y = line_len; + param->ext_mem_param[RSZ_A].rsz_sdr_oft_c = line_len_c; + configure_resizer_out_params(oper_state, param, RSZ_A, + &ss_config->output1, 0, 1); + + if (output1_pix == V4L2_MBUS_FMT_SBGGR10_1X10) + param->rsz_common.raw_flip = 1; + else + param->rsz_common.raw_flip = 0; + + if (output1_pix == V4L2_MBUS_FMT_NV12_1X20) + enable_422_420_conversion(param, RSZ_A, ENABLE); + else + enable_422_420_conversion(param, RSZ_A, DISABLE); + } + + if (oper_state->rsz_output_enabled[RSZ_B]) { + param->rsz_en[RSZ_B] = ENABLE; + param->rsz_rsc_param[RSZ_B].mode = IPIPEIF_ONE_SHOT; + + output2_pix = oper_state->out_mbus_format[RSZ_B].code; + ret = rsz_validate_output_image_format(dev, output2_pix, + oper_state-> + out_mbus_format[RSZ_B]. + width, oper_state-> + out_mbus_format[RSZ_B]. + height, &line_len, &line_len_c); + if (ret) { + mutex_unlock(&oper_state->lock); + return ret; + } + param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c; + configure_resizer_out_params(oper_state, param, RSZ_B, + &ss_config->output2, 0, 1); + if (output2_pix == V4L2_MBUS_FMT_NV12_1X20) + enable_422_420_conversion(param, RSZ_B, ENABLE); + else + enable_422_420_conversion(param, RSZ_B, DISABLE); + } + configure_common_rsz_params(dev, param, ss_config); + if (resizer_chained) { + oper_state->rsz_chained = 1; + oper_state->rsz_config_state = STATE_CONFIGURED; + } else { + oper_state->rsz_chained = 0; + ret = validate_ipipeif_decimation(dev, ss_config->input.dec_en, + ss_config->input.rsz, + ss_config->input. + frame_div_mode_en, + oper_state-> + in_mbus_format.width); + if (ret) { + mutex_unlock(&oper_state->lock); + return ret; + } + + if (ipipe_process_pix_fmts(input_pix, output1_pix, param) < 0) { + dev_err(dev, "error in input or output pix format\n"); + mutex_unlock(&oper_state->lock); + return -EINVAL; + } + + param->ipipeif_param.source = IPIPEIF_SDRAM_YUV; + param->ipipeif_param.glob_hor_size = ss_config->input.ppln; + param->ipipeif_param.glob_ver_size = ss_config->input.lpfr; + param->ipipeif_param.hnum = oper_state->in_mbus_format.width; + param->ipipeif_param.vnum = oper_state->in_mbus_format.height; + param->ipipeif_param.var.if_5_1.clk_div = + ss_config->input.clk_div; + if (ss_config->input.dec_en) { + param->ipipeif_param.decimation = IPIPEIF_DECIMATION_ON; + param->ipipeif_param.rsz = ss_config->input.rsz; + param->ipipeif_param.avg_filter = + (enum ipipeif_avg_filter)ss_config->input. + avg_filter_en; + param->ipipe_hsz = + (((oper_state->in_mbus_format.width * + IPIPEIF_RSZ_CONST) / ss_config->input.rsz) - 1); + } + if (input_pix == V4L2_MBUS_FMT_Y8_1X8 || + input_pix == V4L2_MBUS_FMT_UV8_1X8) { + param->ipipeif_param.var.if_5_1.pack_mode = + IPIPEIF_5_1_PACK_8_BIT; + param->ipipeif_param.var.if_5_1.source1 = IPIPEIF_CCDC; + param->ipipeif_param.var.if_5_1.isif_port.if_type = + V4L2_MBUS_FMT_YUYV8_1X16; + param->ipipeif_param.var.if_5_1.data_shift = + IPIPEIF_5_1_BITS11_0; + param->ipipeif_param.source = IPIPEIF_SDRAM_RAW; + } + if (input_pix == V4L2_MBUS_FMT_UV8_1X8) + param->ipipeif_param.var.if_5_1.isif_port.if_type = + V4L2_MBUS_FMT_SBGGR10_1X10; + param->ipipe_hsz = oper_state->in_mbus_format.width - 1; + param->ipipe_vsz = oper_state->in_mbus_format.height - 1; + param->ipipe_vps = ss_config->input.vst; + param->ipipe_hps = ss_config->input.hst; + param->ipipe_dpaths_fmt = IPIPE_YUV2YUV; + configure_common_rsz_in_params(dev, oper_state, param, 1, + resizer_chained, + &ss_config->input); + if (param->rsz_en[RSZ_A]) { + calculate_resize_ratios(param, RSZ_A); + calculate_sdram_offsets(param, RSZ_A); + /* Overriding resize ratio calculation */ + if (input_pix == V4L2_MBUS_FMT_UV8_1X8) { + param->rsz_rsc_param[RSZ_A].v_dif = + (((param->ipipe_vsz + 1) * 2) * 256) / + (param->rsz_rsc_param[RSZ_A].o_vsz + 1); + } + } + + if (param->rsz_en[RSZ_B]) { + calculate_resize_ratios(param, RSZ_B); + calculate_sdram_offsets(param, RSZ_B); + /* Overriding resize ratio calculation */ + if (input_pix == V4L2_MBUS_FMT_UV8_1X8) { + param->rsz_rsc_param[RSZ_B].v_dif = + (((param->ipipe_vsz + 1) * 2) * 256) / + (param->rsz_rsc_param[RSZ_B].o_vsz + 1); + } + } + } + mutex_unlock(&oper_state->lock); + + return 0; +} + +static int configure_resizer_in_cont_mode(struct device *dev, + struct ipipe_oper_state *oper_state, + void *user_config, + int resizer_chained, + struct ipipe_params *param) +{ + /* Continuous mode. This is a shared config block */ + struct rsz_continuous_config *cont_config = + (struct rsz_continuous_config *)user_config; + int line_len_c; + int line_len; + int ret; + + if (!resizer_chained) { + dev_err(dev, "Resizer cannot be configured in standalone" + "for continuous mode\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&oper_state->lock); + if (ret) + return ret; + + if (!oper_state->rsz_output_enabled[RSZ_A]) { + dev_err(dev, "enable resizer - 0\n"); + mutex_unlock(&oper_state->lock); + return -EINVAL; + } + param->rsz_en[RSZ_A] = ENABLE; + param->rsz_rsc_param[RSZ_A].mode = IPIPEIF_CONTINUOUS; + configure_resizer_out_params(oper_state, param, RSZ_A, + &cont_config->output1, 1, 0); + param->rsz_en[RSZ_B] = DISABLE; + + if (oper_state->rsz_output_enabled[RSZ_B]) { + enum v4l2_mbus_pixelcode out2_pix; + + out2_pix = oper_state->out_mbus_format[RSZ_B].code; + param->rsz_rsc_param[RSZ_B].mode = IPIPEIF_CONTINUOUS; + ret = rsz_validate_output_image_format(dev, out2_pix, + oper_state-> + out_mbus_format[RSZ_B]. + width, oper_state-> + out_mbus_format[RSZ_B]. + height, &line_len, + &line_len_c); + if (ret) { + mutex_unlock(&oper_state->lock); + return ret; + } + param->ext_mem_param[RSZ_B].rsz_sdr_oft_y = line_len; + param->ext_mem_param[RSZ_B].rsz_sdr_oft_c = line_len_c; + configure_resizer_out_params(oper_state, param, RSZ_B, + &cont_config->output2, 0, 1); + if (out2_pix == V4L2_MBUS_FMT_NV12_1X20) + enable_422_420_conversion(param, RSZ_B, ENABLE); + else + enable_422_420_conversion(param, RSZ_B, DISABLE); + } + oper_state->rsz_chained = 1; + oper_state->rsz_config_state = STATE_CONFIGURED; + mutex_unlock(&oper_state->lock); + + return 0; +} + +static int ipipe_set_resize_config(struct device *dev, void *ipipe, + int resizer_chained, + void *user_config, void *config) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct ipipe_params *param = (struct ipipe_params *)config; + int ret; + + dev_dbg(dev, "ipipe_set_resize_config, resizer_chained = %d\n", + resizer_chained); + if (!user_config || !config) { + dev_err(dev, "Invalid user_config or config ptr\n"); + return -EINVAL; + } + + memcpy((void *)config, + (void *)&dm365_ipipe_defs, + sizeof(struct ipipe_params)); + + /* restore if_param */ + if (oper_state->oper_mode == IMP_MODE_CONTINUOUS) + param->ipipeif_param.var.if_5_1.isif_port = + oper_state->if_param; + + if (oper_state->oper_mode != IMP_MODE_SINGLE_SHOT) { + return configure_resizer_in_cont_mode(dev, oper_state, + user_config, resizer_chained, param); + + /* In continuous mode, after setting config, + set input and output format for IPIPE */ + ret = configure_formats_in_cont_mode(oper_state); + } + + ret = configure_resizer_in_ss_mode(dev, oper_state, + user_config, resizer_chained, param); + if (!ret && !oper_state->en_serializer && !resizer_chained) + ret = ipipe_hw_setup(config); + + return ret; +} + +static void configure_resize_passthru(struct ipipe_params *param, int bypass) +{ + param->rsz_rsc_param[RSZ_A].cen = DISABLE; + param->rsz_rsc_param[RSZ_A].yen = DISABLE; + param->rsz_rsc_param[RSZ_A].v_phs_y = 0; + param->rsz_rsc_param[RSZ_A].v_phs_c = 0; + param->rsz_rsc_param[RSZ_A].v_dif = 256; + param->rsz_rsc_param[RSZ_A].v_lpf_int_y = 0; + param->rsz_rsc_param[RSZ_A].v_lpf_int_c = 0; + param->rsz_rsc_param[RSZ_A].h_phs = 0; + param->rsz_rsc_param[RSZ_A].h_dif = 256; + param->rsz_rsc_param[RSZ_A].h_lpf_int_y = 0; + param->rsz_rsc_param[RSZ_A].h_lpf_int_c = 0; + param->rsz_rsc_param[RSZ_A].dscale_en = DISABLE; + param->rsz2rgb[RSZ_A].rgb_en = DISABLE; + param->rsz_en[RSZ_A] = ENABLE; + param->rsz_en[RSZ_B] = DISABLE; + if (bypass) { + param->rsz_rsc_param[RSZ_A].i_vps = 0; + param->rsz_rsc_param[RSZ_A].i_hps = 0; + /* Raw Bypass */ + param->rsz_common.passthrough = IPIPE_BYPASS_ON; + } +} + +static inline int prev_validate_output_image_format(struct device *dev, + enum v4l2_mbus_pixelcode pix, + int *line_len, + int in_width, int in_height) +{ + if (pix != V4L2_MBUS_FMT_UYVY8_2X8 && + pix != V4L2_MBUS_FMT_SBGGR10_1X10) { + dev_err(dev, "prev_validate_output_image_format" + "pix format not supported, %d\n", pix); + return -EINVAL; + } + + if (in_width == 0 || in_height == 0) { + dev_err(dev, "prev_validate_output_image_format:" + " invalid width or height\n"); + return -EINVAL; + } + + *line_len = in_width * 2; + + /* Adjust line length to be a multiple of 32 */ + *line_len += 31; + *line_len &= ~0x1f; + + return 0; +} + +static inline int validate_preview_input_spec(struct device *dev, + enum v4l2_mbus_pixelcode pix, + int width, + int height, int *line_len) +{ + if (pix != V4L2_MBUS_FMT_UYVY8_2X8 && + pix != V4L2_MBUS_FMT_SBGGR10_1X10 && + pix != V4L2_MBUS_FMT_SBGGR8_1X8 && + pix != V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8 && + pix != V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 && + pix != V4L2_MBUS_FMT_SBGGR12_1X12) { + dev_err(dev, "validate_preview_input_spec:" + "pix format not supported, %d\n", pix); + return -EINVAL; + } + if (width == 0 || height == 0) { + dev_err(dev, "rsz_validate_out_image_formats: " + "invalid width or height\n"); + return -EINVAL; + } + + if (pix == V4L2_MBUS_FMT_UYVY8_2X8 || pix == V4L2_MBUS_FMT_SBGGR10_1X10) + *line_len = width * 2; + else if (pix == V4L2_MBUS_FMT_SBGGR8_1X8 || + pix == V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8 || + pix == V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8) + *line_len = width; + else + /* 12 bit */ + *line_len = width + (width >> 1); + /* Adjust line length to be a multiple of 32 */ + *line_len += 31; + *line_len &= ~0x1f; + + return 0; +} + +static int configure_previewer_in_cont_mode(struct device *dev, + struct ipipe_oper_state *oper_state, + void *user_config, + struct ipipe_params *param) +{ + struct prev_continuous_config *cont_config = + (struct prev_continuous_config *)user_config; + int ret; + + if (cont_config->input.en_df_sub) { + dev_err(dev, "DF subtraction is not supported\n"); + return -EINVAL; + } + if (cont_config->input.dec_en && (cont_config->input.rsz < + IPIPEIF_RSZ_MIN || cont_config->input.rsz > + IPIPEIF_RSZ_MAX)) { + dev_err(dev, "rsz range is %d to %d\n", + IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX); + return -EINVAL; + } + ret = mutex_lock_interruptible(&oper_state->lock); + if (ret) + return ret; + param->rsz_common.passthrough = cont_config->bypass; + param->ipipeif_param.source = IPIPEIF_CCDC; + param->ipipeif_param.clock_select = IPIPEIF_PIXCEL_CLK; + param->ipipeif_param.mode = IPIPEIF_CONTINUOUS; + if (cont_config->input.dec_en) { + param->ipipeif_param.decimation = IPIPEIF_DECIMATION_ON; + param->ipipeif_param.rsz = cont_config->input.rsz; + param->ipipeif_param.avg_filter = + (enum ipipeif_avg_filter)cont_config->input.avg_filter_en; + } + /* IPIPE mode */ + param->ipipe_mode = IPIPEIF_CONTINUOUS; + param->ipipe_colpat_olop = cont_config->input.colp_olop; + param->ipipe_colpat_olep = cont_config->input.colp_olep; + param->ipipe_colpat_elop = cont_config->input.colp_elop; + param->ipipe_colpat_elep = cont_config->input.colp_elep; + param->ipipeif_param.gain = cont_config->input.gain; + param->ipipeif_param.var.if_5_1.clip = cont_config->input.clip; + param->ipipeif_param.var.if_5_1.dpc = cont_config->input.dpc; + param->ipipeif_param.var.if_5_1.align_sync = + cont_config->input.align_sync; + param->ipipeif_param.var.if_5_1.rsz_start = + cont_config->input.rsz_start; + if (!oper_state->rsz_chained) { + param->rsz_rsc_param[0].mode = IPIPEIF_CONTINUOUS; + /* setup bypass resizer */ + configure_resize_passthru(param, 0); + } + if (cont_config->bypass) + configure_resize_passthru(param, 1); + oper_state->prev_config_state = STATE_CONFIGURED; + mutex_unlock(&oper_state->lock); + + return 0; +} + +static int configure_previewer_in_ss_mode(struct device *dev, + struct ipipe_oper_state *oper_state, + void *user_config, + struct ipipe_params *param) +{ + struct prev_single_shot_config *ss_config = + (struct prev_single_shot_config *)user_config; + enum v4l2_mbus_pixelcode output_pix; + enum v4l2_mbus_pixelcode input_pix; + int line_len; + int ret; + + output_pix = oper_state->out_mbus_format[RSZ_A].code; + input_pix = oper_state->in_mbus_format.code; + + ret = validate_preview_input_spec(dev, input_pix, + oper_state->in_mbus_format.width, + oper_state->in_mbus_format.height, + &line_len); + if (ret) + return -EINVAL; + + ret = mutex_lock_interruptible(&oper_state->lock); + if (ret) + return ret; + + if (!ss_config->input.line_length) { + param->ipipeif_param.adofs = line_len; + } else { + param->ipipeif_param.adofs = ss_config->input.line_length; + param->ipipeif_param.adofs = + (param->ipipeif_param.adofs + 31) & ~0x1f; + } + if (ss_config->input.dec_en && ss_config->input.frame_div_mode_en) { + dev_err(dev, "Both dec_en & frame_div_mode_en" + "can not enabled simultaneously\n"); + mutex_unlock(&oper_state->lock); + return -EINVAL; + } + + ret = validate_ipipeif_decimation(dev, ss_config->input.dec_en, + ss_config->input.rsz, + ss_config->input.frame_div_mode_en, + oper_state->in_mbus_format.width); + if (ret) { + mutex_unlock(&oper_state->lock); + return -EINVAL; + } + + if (!oper_state->rsz_chained) { + ret = prev_validate_output_image_format(dev, output_pix, + &line_len, + oper_state-> + in_mbus_format.width, + oper_state-> + in_mbus_format.height); + if (ret) { + mutex_unlock(&oper_state->lock); + return -EINVAL; + } + param->ext_mem_param[RSZ_A].rsz_sdr_oft_y = line_len; + if (ss_config->input.frame_div_mode_en) + ret = update_preview_f_div_params(dev, oper_state-> + in_mbus_format.width, + oper_state-> + in_mbus_format.width, + ¶m-> + rsz_rsc_param[RSZ_A]); + if (ret) { + mutex_unlock(&oper_state->lock); + return -EINVAL; + } + } else { + if (ss_config->input.frame_div_mode_en && + param->rsz_en[RSZ_A]) { + if (!param->rsz_rsc_param[RSZ_A].dscale_en) + ret = calculate_normal_f_div_param(dev, + oper_state-> + in_mbus_format.width, + param->rsz_rsc_param + [RSZ_A]. + o_vsz + 1, + ¶m->rsz_rsc_param + [RSZ_A]); + else + ret = calculate_down_scale_f_div_param(dev, + oper_state-> + in_mbus_format.width, + param->rsz_rsc_param + [RSZ_A].o_vsz + 1, + ¶m->rsz_rsc_param + [RSZ_A]); + if (ret) { + mutex_unlock(&oper_state->lock); + return -EINVAL; + } + } + if (ss_config->input.frame_div_mode_en && + param->rsz_en[RSZ_B]) { + if (!param->rsz_rsc_param[RSZ_B].dscale_en) + ret = calculate_normal_f_div_param(dev, + oper_state-> + in_mbus_format.width, + param->rsz_rsc_param + [RSZ_B].o_vsz + 1, + ¶m->rsz_rsc_param + [RSZ_B]); + else + ret = calculate_down_scale_f_div_param(dev, + oper_state-> + in_mbus_format.width, + param->rsz_rsc_param + [RSZ_B].o_vsz + 1, + ¶m->rsz_rsc_param + [RSZ_B]); + if (ret) { + mutex_unlock(&oper_state->lock); + return -EINVAL; + } + } + } + if (ipipe_process_pix_fmts(input_pix, output_pix, param) < 0) { + dev_err(dev, "error in input or output pix format\n"); + mutex_unlock(&oper_state->lock); + return -EINVAL; + } + param->ipipeif_param.hnum = oper_state->in_mbus_format.width; + param->ipipeif_param.vnum = oper_state->in_mbus_format.height; + param->ipipeif_param.glob_hor_size = ss_config->input.ppln; + param->ipipeif_param.glob_ver_size = ss_config->input.lpfr; + param->ipipeif_param.var.if_5_1.clk_div = ss_config->input.clk_div; + param->ipipeif_param.var.if_5_1.pix_order = ss_config->input.pix_order; + param->ipipeif_param.var.if_5_1.align_sync = + ss_config->input.align_sync; + param->ipipeif_param.var.if_5_1.rsz_start = ss_config->input.rsz_start; + if (param->ipipeif_param.var.if_5_1.dpcm.en) { + param->ipipeif_param.var.if_5_1.dpcm.pred = + ss_config->input.pred; + param->ipipeif_param.var.if_5_1.dpcm.type = + IPIPEIF_DPCM_8BIT_12BIT; + } + param->ipipeif_param.var.if_5_1.data_shift = + ss_config->input.data_shift; + + param->ipipe_hsz = oper_state->in_mbus_format.width - 1; + if (ss_config->input.dec_en) { + if (ss_config->input.rsz < IPIPEIF_RSZ_MIN || + ss_config->input.rsz > IPIPEIF_RSZ_MAX) { + dev_err(dev, "rsz range is %d to %d\n", + IPIPEIF_RSZ_MIN, IPIPEIF_RSZ_MAX); + mutex_unlock(&oper_state->lock); + return -EINVAL; + } + param->ipipeif_param.decimation = IPIPEIF_DECIMATION_ON; + param->ipipeif_param.rsz = ss_config->input.rsz; + param->ipipeif_param.avg_filter = + (enum ipipeif_avg_filter)ss_config->input.avg_filter_en; + param->ipipe_hsz = + (((oper_state->in_mbus_format.width * IPIPEIF_RSZ_CONST) / + ss_config->input.rsz) - 1); + } + param->ipipeif_param.gain = ss_config->input.gain; + param->ipipeif_param.var.if_5_1.clip = ss_config->input.clip; + param->ipipeif_param.var.if_5_1.dpc = ss_config->input.dpc; + param->ipipe_colpat_olop = ss_config->input.colp_olop; + param->ipipe_colpat_olep = ss_config->input.colp_olep; + param->ipipe_colpat_elop = ss_config->input.colp_elop; + param->ipipe_colpat_elep = ss_config->input.colp_elep; + param->ipipe_vps = ss_config->input.vst; + param->ipipe_hps = ss_config->input.hst; + param->ipipe_vsz = oper_state->in_mbus_format.height - 1; + if (input_pix == V4L2_MBUS_FMT_UYVY8_2X8) + param->ipipeif_param.source = IPIPEIF_SDRAM_YUV; + else + param->ipipeif_param.source = IPIPEIF_SDRAM_RAW; + + configure_common_rsz_in_params(dev, oper_state, param, 1, + oper_state->rsz_chained, + &ss_config->input); + + param->rsz_common.passthrough = ss_config->bypass; + /* update the resize parameters */ + if (ss_config->bypass == IPIPE_BYPASS_ON || + param->ipipe_dpaths_fmt == IPIPE_RAW2RAW) { + /* Bypass resizer */ + configure_resize_passthru(param, 1); + } else { + if (oper_state->rsz_chained) { + if (param->rsz_en[RSZ_A]) { + calculate_resize_ratios(param, RSZ_A); + calculate_sdram_offsets(param, RSZ_A); + } + if (param->rsz_en[RSZ_B]) { + calculate_resize_ratios(param, RSZ_B); + calculate_sdram_offsets(param, RSZ_B); + } + } else { + struct rsz_output_spec *output_specs = + kmalloc(sizeof(struct rsz_output_spec), + GFP_KERNEL); + if (!output_specs) { + dev_err(dev, "Memory Alloc failure\n"); + mutex_unlock(&oper_state->lock); + return -EINVAL; + } + /* Using resizer as pass through */ + configure_resize_passthru(param, 0); + memset((void *)output_specs, 0, + sizeof(struct rsz_output_spec)); + + output_specs->vst_y = ss_config->input.vst; + configure_resizer_out_params(oper_state, param, RSZ_A, + output_specs, 0, 0); + calculate_sdram_offsets(param, RSZ_A); + kfree(output_specs); + } + } + mutex_unlock(&oper_state->lock); + + return 0; +} + +static int ipipe_set_preview_config(struct device *dev, void *ipipe, + void *user_config, void *config) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct ipipe_params *param = (struct ipipe_params *)config; + int ret; + + dev_dbg(dev, "ipipe_set_preview_config\n"); + + if (!user_config || !config) { + dev_err(dev, "Invalid user_config or config ptr\n"); + return -EINVAL; + } + + if (!oper_state->rsz_chained) { + /* For chained resizer, defaults are set by resizer */ + memcpy((void *)config, (void *)&dm365_ipipe_defs, + sizeof(struct ipipe_params)); + + /* restore if_param */ + if (oper_state->oper_mode == IMP_MODE_CONTINUOUS) + param->ipipeif_param.var.if_5_1.isif_port = + oper_state->if_param; + + /* for previewer only continuous mode, RSZ_A should be on */ + oper_state->rsz_output_enabled[RSZ_A] = 1; + } + + /* continuous mode */ + if (oper_state->oper_mode == IMP_MODE_CONTINUOUS) { + ret = configure_previewer_in_cont_mode(dev, oper_state, + user_config, param); + if (ret) + return ret; + + /* In continuous mode, after setting config, + set input and output format for IPIPE */ + return configure_formats_in_cont_mode(oper_state); + } + + /* previewer in standalone mode. In this mode if serializer + * is enabled, we need to set config params for hw. + */ + ret = configure_previewer_in_ss_mode(dev, oper_state, + user_config, param); + + if (!ret && !oper_state->en_serializer) + ret = ipipe_hw_setup(config); + + return ret; +} + +static void ipipe_set_in_mbus_format(void *ipipe, + struct v4l2_mbus_framefmt *format) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + memcpy((void *)&oper_state->in_mbus_format, (void *)format, + sizeof(struct v4l2_mbus_framefmt)); +} + +static void ipipe_set_out_mbus_format(void *ipipe, + struct v4l2_mbus_framefmt *format) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + memcpy((void *)&oper_state->out_mbus_format[RSZ_A], (void *)format, + sizeof(struct v4l2_mbus_framefmt)); + + oper_state->rsz_output_enabled[RSZ_A] = ENABLE; +} + +static void ipipe_set_out2_mbus_format(void *ipipe, + struct v4l2_mbus_framefmt *format) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + memcpy((void *)&oper_state->out_mbus_format[RSZ_B], (void *)format, + sizeof(struct v4l2_mbus_framefmt)); + + oper_state->rsz_output_enabled[RSZ_B] = ENABLE; +} + +static int ipipe_get_output_state(void *ipipe, unsigned char out_sel) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct ipipe_params *param = oper_state->shared_config_param; + + if (out_sel != RSZ_A && out_sel != RSZ_B) + return 0; + + return param->rsz_en[out_sel]; +} + +/* This should be called only after setting the output + * window params. This also assumes the corresponding + * output is configured prior to calling this. + */ +static int ipipe_get_line_length(void *ipipe, unsigned char out_sel) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct ipipe_params *param = oper_state->shared_config_param; + + if (out_sel != RSZ_A && out_sel != RSZ_B) + return -EINVAL; + /* assume output is always UYVY. Change this if we + * support RGB + */ + if (!param->rsz_en[out_sel]) + return -EINVAL; + + return param->ext_mem_param[out_sel].rsz_sdr_oft_y; +} + +static int ipipe_get_image_height(void *ipipe, unsigned char out_sel) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct ipipe_params *param = oper_state->shared_config_param; + + if (out_sel != RSZ_A && out_sel != RSZ_B) + return -EINVAL; + if (!param->rsz_en[out_sel]) + return -EINVAL; + + return param->rsz_rsc_param[out_sel].o_vsz + 1; +} + +/* Assume valid param ptr */ +static int ipipe_set_hw_if_param(void *ipipe, struct vpfe_hw_if_param *if_param) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + int ret; + + ret = mutex_lock_interruptible(&oper_state->lock); + if (ret) + return ret; + + /* save if_param locally */ + memcpy((void *)&oper_state->if_param, (void *)if_param, + sizeof(struct vpfe_hw_if_param)); + + mutex_unlock(&oper_state->lock); + + return 0; +} + +static struct imp_hw_interface dm365_ipipe_interface = { + .name = "DM365 IPIPE", + .owner = THIS_MODULE, + .prev_enum_modules = prev_enum_preview_cap, + .set_oper_mode = ipipe_set_oper_mode, + .reset_oper_mode = ipipe_reset_oper_mode, + .get_oper_mode = prev_get_oper_mode, + .get_hw_state = ipipe_get_oper_state, + .set_hw_state = ipipe_set_oper_state, + .resizer_chain = ipipe_rsz_chain_state, + .lock_chain = ipipe_lock_chain, + .unlock_chain = ipipe_unlock_chain, + .serialize = ipipe_serialize, + .alloc_config_block = ipipe_alloc_config_block, + .dealloc_config_block = ipipe_dealloc_config_block, + .alloc_user_config_block = ipipe_alloc_user_config_block, + .dealloc_config_block = ipipe_dealloc_user_config_block, + .set_user_config_defaults = ipipe_set_user_config_defaults, + .set_preview_config = ipipe_set_preview_config, + .set_resizer_config = ipipe_set_resize_config, + .update_inbuf_address = ipipe_set_ipipe_if_address, + .update_outbuf1_address = ipipe_update_outbuf1_address, + .update_outbuf2_address = ipipe_update_outbuf2_address, + .enable = ipipe_enable, + .enable_resize = rsz_src_enable, + .hw_setup = ipipe_do_hw_setup, + .get_resizer_config_state = ipipe_get_rsz_config_state, + .get_previewer_config_state = ipipe_get_prev_config_state, + .set_hw_if_param = ipipe_set_hw_if_param, + .set_in_mbus_format = ipipe_set_in_mbus_format, + .set_out_mbus_format = ipipe_set_out_mbus_format, + .set_out2_mbus_format = ipipe_set_out2_mbus_format, + .get_output_state = ipipe_get_output_state, + .get_line_length = ipipe_get_line_length, + .get_image_height = ipipe_get_image_height, + .get_image_height = ipipe_get_image_height, + .get_max_output_width = ipipe_get_max_output_width, + .get_max_output_height = ipipe_get_max_output_height, + .enum_pix = ipipe_enum_pix, +}; + +struct imp_hw_interface *imp_get_hw_if(void) +{ + return &dm365_ipipe_interface; +} + +void enable_serializer(void *ipipe, int val) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + oper_state->en_serializer = val; +} + +int ipipe_init(void **ipipe) +{ + struct ipipe_oper_state *oper_state; + + *ipipe = kmalloc(sizeof(struct ipipe_oper_state), GFP_KERNEL); + if (!*ipipe) { + printk(KERN_ERR "dm365_ipipe_init: Failed to allocate memory " + "for ipipe\n"); + return -ENOMEM; + } + oper_state = (struct ipipe_oper_state *)*ipipe; + + /* ipipe module operation state & configuration */ + oper_state->oper_mode = IMP_MODE_NOT_CONFIGURED; + oper_state->ipipe_raw_yuv_pix_formats[0] = V4L2_PIX_FMT_UYVY; + oper_state->ipipe_raw_yuv_pix_formats[1] = V4L2_PIX_FMT_NV12; + + oper_state->shared_config_param = + kmalloc(sizeof(struct ipipe_params), GFP_KERNEL); + + if (!oper_state->shared_config_param) { + printk(KERN_ERR + "dm365_ipipe_init: failed to allocate memory\n"); + return -ENOMEM; + } + memcpy(&dm365_ipipe_defs.ipipeif_param.var.if_5_1, + &ipipeif_5_1_defaults, sizeof(struct ipipeif_5_1)); + oper_state->lutdpc.table = oper_state->ipipe_lutdpc_table; + oper_state->lut_3d.table = oper_state->ipipe_3d_lut_table; + oper_state->gbce.table = oper_state->ipipe_gbce_table; + oper_state->gamma.table_r = oper_state->ipipe_gamma_table_r; + oper_state->gamma.table_b = oper_state->ipipe_gamma_table_b; + oper_state->gamma.table_g = oper_state->ipipe_gamma_table_g; + oper_state->yee.table = oper_state->ipipe_yee_table; + mutex_init(&oper_state->lock); + oper_state->state = CHANNEL_FREE; + oper_state->prev_config_state = STATE_NOT_CONFIGURED; + oper_state->rsz_config_state = STATE_NOT_CONFIGURED; + oper_state->out_mbus_format[RSZ_A].code = V4L2_MBUS_FMT_UYVY8_2X8; + oper_state->out_mbus_format[RSZ_B].code = V4L2_MBUS_FMT_UYVY8_2X8; + oper_state->resource_in_use = 0; + oper_state->rsz_chained = 0; + + return 0; +} + +void ipipe_cleanup(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + kfree(oper_state->shared_config_param); +} diff --git a/drivers/media/video/davinci/dm365_ipipe.h b/drivers/media/video/davinci/dm365_ipipe.h new file mode 100644 index 0000000..abcf60e --- /dev/null +++ b/drivers/media/video/davinci/dm365_ipipe.h @@ -0,0 +1,378 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* Feature description +* =================== +* +* VPFE hardware setup +* +* case 1: Capture to SDRAM with out IPIPE +* **************************************** +* +* parallel +* port +* +* Image sensor/ ________ +* Yuv decoder ---->| CCDC |--> SDRAM +* |______| +* +* case 2: Capture to SDRAM with IPIPE Preview modules in Continuous +* (On the Fly mode) +* +* Image sensor/ ________ ____________________ +* Yuv decoder ---->| CCDC |--> | Previewer modules |--> SDRAM +* |______| |___________________| +* +* case 3: Capture to SDRAM with IPIPE Preview modules & Resizer +* in continuous (On the Fly mode) +* +* Image sensor/ ________ _____________ ___________ +* Yuv decoder ---->| CCDC |--> | Previewer |->| Resizer |-> SDRAM +* |______| |____________| |__________| +* +* case 4: Capture to SDRAM with IPIPE Resizer +* in continuous (On the Fly mode) +* +* Image sensor/ ________ ___________ +* Yuv decoder ---->| CCDC |--> | Resizer |-> SDRAM +* |______| |__________| +* +* case 5: Read from SDRAM and do preview and/or Resize +* in Single shot mode +* +* _____________ ___________ +* SDRAM ----> | Previewer |->| Resizer |-> SDRAM +* |____________| |__________| +* +* +* Previewer allows fine tuning of the input image using different +* tuning modules in IPIPE. Some examples :- Noise filter, Defect +* pixel correction etc. It essentially operate on Bayer Raw data +* or YUV raw data. To do image tuning, application call, +* PREV_QUERY_CAP, and then call PREV_SET_PARAM to set parameter +* for a module. +* +* +* Resizer allows upscaling or downscaling a image to a desired +* resolution. There are 2 resizer modules. both operating on the +* same input image, but can have different output resolution. +*/ + +#ifndef DM365_IPIPE_H +#define DM365_IPIPE_H + +#include +#include "dm3xx_ipipeif.h" + +#define CEIL(a, b) (((a) + (b-1)) / (b)) + +/* Used for driver storage */ +struct ipipe_otfdpc_2_0 { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* defect detection method */ + enum ipipe_otfdpc_det_meth det_method; + /* Algorith used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is + * used + */ + enum ipipe_otfdpc_alg alg; + struct prev_otfdpc_2_0 otfdpc_2_0; +}; + +struct ipipe_otfdpc_3_0 { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* defect detection method */ + enum ipipe_otfdpc_det_meth det_method; + /* Algorith used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is + * used + */ + enum ipipe_otfdpc_alg alg; + struct prev_otfdpc_3_0 otfdpc_3_0; +}; + +struct f_div_pass { + unsigned int o_hsz; + unsigned int i_hps; + unsigned int h_phs; + unsigned int src_hps; + unsigned int src_hsz; +}; + +#define IPIPE_MAX_PASSES 2 + +struct f_div_param { + unsigned char en; + unsigned int num_passes; + struct f_div_pass pass[IPIPE_MAX_PASSES]; +}; + +#define boolean_t int +#define ENABLE 1 +#define DISABLE (!ENABLE) + +/* Resizer Rescale Parameters*/ +struct ipipe_rsz_rescale_param { + enum ipipe_oper_mode mode; + boolean_t h_flip; + boolean_t v_flip; + boolean_t cen; + boolean_t yen; + unsigned short i_vps; + unsigned short i_hps; + unsigned short o_vsz; + unsigned short o_hsz; + unsigned short v_phs_y; + unsigned short v_phs_c; + unsigned short v_dif; + /* resize method - Luminance */ + enum rsz_intp_t v_typ_y; + /* resize method - Chrominance */ + enum rsz_intp_t v_typ_c; + /* vertical lpf intensity - Luminance */ + unsigned char v_lpf_int_y; + /* vertical lpf intensity - Chrominance */ + unsigned char v_lpf_int_c; + unsigned short h_phs; + unsigned short h_dif; + /* resize method - Luminance */ + enum rsz_intp_t h_typ_y; + /* resize method - Chrominance */ + enum rsz_intp_t h_typ_c; + /* horizontal lpf intensity - Luminance */ + unsigned char h_lpf_int_y; + /* horizontal lpf intensity - Chrominance */ + unsigned char h_lpf_int_c; + boolean_t dscale_en; + enum down_scale_ave_sz h_dscale_ave_sz; + enum down_scale_ave_sz v_dscale_ave_sz; + /* store the calculated frame division parameter */ + struct f_div_param f_div; +}; + +enum ipipe_rsz_rgb_t { + OUTPUT_32BIT, + OUTPUT_16BIT +}; + +enum ipipe_rsz_rgb_msk_t { + NOMASK, + MASKLAST2 +}; + +/* Resizer RGB Conversion Parameters */ +struct ipipe_rsz_resize2rgb { + boolean_t rgb_en; + enum ipipe_rsz_rgb_t rgb_typ; + enum ipipe_rsz_rgb_msk_t rgb_msk0; + enum ipipe_rsz_rgb_msk_t rgb_msk1; + unsigned int rgb_alpha_val; +}; + +/* Resizer External Memory Parameters */ +struct ipipe_ext_mem_param { + unsigned int rsz_sdr_oft_y; + unsigned int rsz_sdr_ptr_s_y; + unsigned int rsz_sdr_ptr_e_y; + unsigned int rsz_sdr_oft_c; + unsigned int rsz_sdr_ptr_s_c; + unsigned int rsz_sdr_ptr_e_c; + /* offset to be added to buffer start when flipping for y/ycbcr */ + unsigned int flip_ofst_y; + /* offset to be added to buffer start when flipping for c */ + unsigned int flip_ofst_c; + /* c offset for YUV 420SP */ + unsigned int c_offset; + /* User Defined Y offset for YUV 420SP or YUV420ILE data */ + unsigned int user_y_ofst; + /* User Defined C offset for YUV 420SP data */ + unsigned int user_c_ofst; +}; + +enum rsz_data_source { + IPIPE_DATA, + IPIPEIF_DATA +}; + +/* data paths */ +enum ipipe_data_paths { + IPIPE_RAW2YUV, + /* Bayer RAW input to YCbCr output */ + IPIPE_RAW2RAW, + /* Bayer Raw to Bayer output */ + IPIPE_RAW2BOX, + /* Bayer Raw to Boxcar output */ + IPIPE_YUV2YUV + /* YUV Raw to YUV Raw output */ +}; + +enum rsz_src_img_fmt { + RSZ_IMG_422, + RSZ_IMG_420 +}; + +struct rsz_common_params { + unsigned int vps; + unsigned int vsz; + unsigned int hps; + unsigned int hsz; + /* 420 or 422 */ + enum rsz_src_img_fmt src_img_fmt; + /* Y or C when src_fmt is 420, 0 - y, 1 - c */ + unsigned char y_c; + /* flip raw or ycbcr */ + unsigned char raw_flip; + /* IPIPE or IPIPEIF data */ + enum rsz_data_source source; + enum ipipe_dpaths_bypass_t passthrough; + unsigned char yuv_y_min; + unsigned char yuv_y_max; + unsigned char yuv_c_min; + unsigned char yuv_c_max; + boolean_t rsz_seq_crv; + enum ipipe_chr_pos out_chr_pos; +}; + +struct ipipe_params { + struct ipipeif ipipeif_param; + enum ipipe_oper_mode ipipe_mode; + /* input/output datapath through IPIPE */ + enum ipipe_data_paths ipipe_dpaths_fmt; + /* color pattern register */ + enum ipipe_colpat_t ipipe_colpat_elep; + enum ipipe_colpat_t ipipe_colpat_elop; + enum ipipe_colpat_t ipipe_colpat_olep; + enum ipipe_colpat_t ipipe_colpat_olop; + /* horizontal/vertical start, horizontal/vertical size + * for both IPIPE and RSZ input + */ + unsigned int ipipe_vps; + unsigned int ipipe_vsz; + unsigned int ipipe_hps; + unsigned int ipipe_hsz; + + struct rsz_common_params rsz_common; + struct ipipe_rsz_rescale_param rsz_rsc_param[2]; + struct ipipe_rsz_resize2rgb rsz2rgb[2]; + struct ipipe_ext_mem_param ext_mem_param[2]; + boolean_t rsz_en[2]; +}; + +#define MAX_IPIPE_RAW_YUV_PIX_FORMATS 2 + +/* IPIPE module operation state */ +struct ipipe_oper_state { + /* Channel state 0 - free, 1 - busy */ + unsigned int state; + /* Semaphore to protect the common hardware configuration */ + struct mutex lock; + /* previewer config state */ + unsigned int prev_config_state; + /* Shared configuration of the hardware */ + struct ipipe_params *shared_config_param; + /* shared resource in use */ + unsigned int resource_in_use; + /* resizer config state */ + unsigned int rsz_config_state; + /* resizer chained with previewer */ + unsigned int rsz_chained; + /* resizer output A/B is enabled? */ + unsigned int rsz_output_enabled[2]; + /* input mbus frame format */ + struct v4l2_mbus_framefmt in_mbus_format; + /* output mbus frame format */ + struct v4l2_mbus_framefmt out_mbus_format[2]; + /* if_param for IPIPEIF */ + struct vpfe_hw_if_param if_param; + /* Operation mode of image processor (imp) + * 0 - continuous mode, 1 - single shot mode + * 2 - invalid mode, 4 - not configured + */ + u32 oper_mode; + /* enable/disable serializer */ + u32 en_serializer; + /* LUT Defect pixel correction data */ + struct prev_lutdpc lutdpc; + /* LUT Defect pixel correction data */ + struct prev_otfdpc otfdpc; + /* Noise filter */ + struct prev_nf nf1; + struct prev_nf nf2; + /* Green Imbalance Correction */ + struct prev_gic gic; + /* White Balance */ + struct prev_wb wb; + /* CFA */ + struct prev_cfa cfa; + /* RGB2RGB conversion */ + struct prev_rgb2rgb rgb2rgb_1; + struct prev_rgb2rgb rgb2rgb_2; + /* Gamma correction */ + struct prev_gamma gamma; + /* 3D LUT */ + struct prev_3d_lut lut_3d; + /* Lumina Adjustment */ + struct prev_lum_adj lum_adj; + /* RGB2YUV conversion */ + struct prev_rgb2yuv rgb2yuv; + /* YUV 422 conversion */ + struct prev_yuv422_conv yuv422_conv; + /* GBCE */ + struct prev_gbce gbce; + /* Edge Enhancement */ + struct prev_yee yee; + /* Chromatic Artifact Reduction, CAR */ + struct prev_car car; + /* Chromatic Artifact Reduction, CAR */ + struct prev_cgs cgs; + /* Tables for various tuning modules */ + struct ipipe_lutdpc_entry ipipe_lutdpc_table[MAX_SIZE_DPC]; + struct ipipe_3d_lut_entry ipipe_3d_lut_table[MAX_SIZE_3D_LUT]; + unsigned short ipipe_gbce_table[MAX_SIZE_GBCE_LUT]; + struct ipipe_gamma_entry ipipe_gamma_table_r[MAX_SIZE_GAMMA]; + struct ipipe_gamma_entry ipipe_gamma_table_b[MAX_SIZE_GAMMA]; + struct ipipe_gamma_entry ipipe_gamma_table_g[MAX_SIZE_GAMMA]; + short ipipe_yee_table[MAX_SIZE_YEE_LUT]; + + /* Raw YUV formats */ + u32 ipipe_raw_yuv_pix_formats[MAX_IPIPE_RAW_YUV_PIX_FORMATS]; +}; + + +void ipipe_set_d2f_regs(unsigned int id, struct prev_nf *noise_filter); +void ipipe_set_rgb2rgb_regs(unsigned int id, struct prev_rgb2rgb *rgb); +void rsz_set_output_address(struct ipipe_params *params, + int resize_no, unsigned int address); +void ipipe_set_yuv422_conv_regs(struct prev_yuv422_conv *conv); +void ipipe_set_lum_adj_regs(struct prev_lum_adj *lum_adj); +void ipipe_set_rgb2ycbcr_regs(struct prev_rgb2yuv *yuv); +void ipipe_set_lutdpc_regs(struct prev_lutdpc *lutdpc); +void ipipe_set_otfdpc_regs(struct prev_otfdpc *otfdpc); +void ipipe_set_3d_lut_regs(struct prev_3d_lut *lut_3d); +void ipipe_set_gamma_regs(struct prev_gamma *gamma); +int ipipe_hw_setup(struct ipipe_params *config); +void ipipe_set_gbce_regs(struct prev_gbce *gbce); +void ipipe_set_gic_regs(struct prev_gic *gic); +void ipipe_set_cfa_regs(struct prev_cfa *cfa); +void ipipe_set_car_regs(struct prev_car *car); +void ipipe_set_cgs_regs(struct prev_cgs *cgs); +void rsz_set_in_pix_format(unsigned char y_c); +void ipipe_set_ee_regs(struct prev_yee *ee); +void ipipe_set_wb_regs(struct prev_wb *wb); +int rsz_enable(int rsz_id, int enable); +void rsz_src_enable(int enable); + +#endif diff --git a/drivers/media/video/davinci/imp_common.h b/drivers/media/video/davinci/imp_common.h new file mode 100644 index 0000000..722098c --- /dev/null +++ b/drivers/media/video/davinci/imp_common.h @@ -0,0 +1,86 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* imp_common.h file +*/ +#ifndef _IMP_COMMON_H +#define _IMP_COMMON_H + +#include +#include +#include +#include +#define MAX_CHANNELS 2 +#define MAX_BUFFERS 6 +#define MAX_PRIORITY 5 +#define MIN_PRIORITY 0 +#define DEFAULT_PRIORITY 3 +#define ENABLED 1 +#define DISABLED 0 +#define CHANNEL_BUSY 1 +#define CHANNEL_FREE 0 +#define ISNULL(val) ((val == NULL) ? 1 : 0) + +#define IMP_MODE_CONTINUOUS 0 +#define IMP_MODE_SINGLE_SHOT 1 +#define IMP_MODE_INVALID 2 +#define IMP_MODE_NOT_CONFIGURED 4 + +/* driver configured by application */ +#define STATE_CONFIGURED 1 +/* driver not configured by application */ +#define STATE_NOT_CONFIGURED 0 + +enum imp_log_chan_t { + IMP_PREVIEWER, + IMP_RESIZER, + IMP_HISTOGRAM, + IMP_BOXCAR +}; + +/* IMP channel structure */ +struct imp_logical_channel { + /* channel type */ + enum imp_log_chan_t type; + /* If this channel is chained with another channel, this is set */ + char chained; + /* Set if there is a primary user of this channel */ + char primary_user; + /* channel configuration for this logial channel */ + void *config; + /* Size of the user configuration block */ + int user_config_size; + /* Saves the user configuration */ + void *user_config; + /* configure State of the channel */ + unsigned int config_state; +}; + +/* Where hardware channel is shared, this is used for serialisation */ +struct imp_serializer { + /* channel config array for serialization */ + struct imp_logical_channel *channel_config[MAX_CHANNELS]; + /* number of elements in the array */ + int array_count; + /* Semaphore for above config array */ + struct mutex array_sem; + /* Completion semaphore when hw channel is common + * Use device specific completion semaphore when request is serialized + */ + struct completion sem_isr; +}; + +#endif diff --git a/drivers/media/video/davinci/imp_hw_if.h b/drivers/media/video/davinci/imp_hw_if.h new file mode 100644 index 0000000..a8e10e6 --- /dev/null +++ b/drivers/media/video/davinci/imp_hw_if.h @@ -0,0 +1,171 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _IMP_HW_IF_H +#define _IMP_HW_IF_H + +#include +#include +#include "imp_common.h" + +struct prev_module_if { + /* Version of the preview module */ + char *version; + /* Module IDs as defined above */ + unsigned short module_id; + /* Name of the module */ + char *module_name; + /* control allowed in continous mode ? 1 - allowed , 0 - not allowed */ + char control; + /* path in which module sits */ + enum imp_data_paths path; + int (*set)(struct device *dev, void *ipipe, void *param, int len); + int (*get)(struct device *dev, void *ipipe, void *param, int len); +}; + +struct imp_hw_interface { + /* Name of the image processor hardware */ + char *name; + /* module owner */ + struct module *owner; + /* + * enumerate preview modules. Return interface to the + * the module + */ + struct prev_module_if *(*prev_enum_modules) (struct device *dev, + unsigned int index); + /* + * set operating mode for IPIPE; 1-single shot, 0-continous + */ + int (*set_oper_mode) (void *ipipe, unsigned int mode); + /* + * reset operating mode for IPIPE; + */ + void (*reset_oper_mode) (void *ipipe); + /* + * get IPIPE operation mode + */ + unsigned int (*get_oper_mode) (void *ipipe); + /* check if hw is busy in continuous mode. + * Used for checking if hw is used by ccdc driver in + * continuous mode. If streaming is ON, this will be + * set to busy + */ + unsigned int (*get_hw_state) (void *ipipe); + /* set hw state */ + void (*set_hw_state) (void *ipipe, unsigned int state); + /* is resizer chained ? */ + unsigned int (*resizer_chain) (void *ipipe); + /* this is used to lock shared resource */ + void (*lock_chain) (void *ipipe); + /* this is used unlock shared resouce */ + void (*unlock_chain) (void *ipipe); + /* Allocate a shared or exclusive config block for hardware + * configuration + */ + void *(*alloc_config_block) (struct device *dev, void *ipipe); + /* hw serialization enabled ?? */ + int (*serialize) (void *ipipe); + /* De-allocate the exclusive config block */ + void (*dealloc_config_block) (struct device *dev, void *ipipe, + void *config); + /* Allocate a user confguration block */ + void *(*alloc_user_config_block) (struct device *dev, void *ipipe, + enum imp_log_chan_t chan_type, + int *len); + + /* de-allocate user config block */ + void (*dealloc_user_config_block) (struct device *dev, void *ipipe, + void *config); + + /* set default configuration in the config block */ + void (*set_user_config_defaults) (struct device *dev, void *ipipe, + enum imp_log_chan_t chan_type, + void *user_config); + /* set user configuration for preview */ + int (*set_preview_config) (struct device *dev, void *ipipe, + void *user_config, void *config); + /* set user configuration for resize */ + int (*set_resizer_config) (struct device *dev, void *ipipe, + int resizer_chained, + void *user_config, void *config); + + int (*reconfig_resizer) (struct device *dev, void *ipipe, + struct rsz_reconfig *user_config, + void *config); + + /* update output buffer address for a channel + * if config is NULL, the shared config is assumed + * this is used only in single shot mode + */ + int (*update_inbuf_address) (void *config, unsigned int address); + /* update output buffer address for a channel + * if config is NULL, the shared config is assumed + */ + void (*update_outbuf1_address) (void *ipipe, void *config, + unsigned int address); + /* update output buffer address for a channel + * if config is NULL, the shared config is assumed + */ + void (*update_outbuf2_address) (void *ipipe, void *config, + unsigned int address); + /* enable or disable hw */ + void (*enable) (void *ipipe, unsigned char en, void *config); + /* enable or disable resizer to allow frame by frame resize in + * continuous mode + */ + void (*enable_resize) (int en); + /* setup hardware for processing. if config is NULL, + * shared channel is assumed + */ + int (*hw_setup) (struct device *dev, void *ipipe, void *config); + /* Get configuration state of resizer in continuous mode */ + unsigned int (*get_resizer_config_state) (void *ipipe); + /* Get configuration state of previewer in continuous mode */ + unsigned int (*get_previewer_config_state) (void *ipipe); + /* Get current input crop window param at the IMP */ + int (*get_input_win) (void *ipipe, struct imp_window *win); + /* Set interface parameter at IPIPEIF. Only valid for DM360 */ + int (*set_hw_if_param) (void *ipipe, struct vpfe_hw_if_param *param); + /* Set input mbus format */ + void (*set_in_mbus_format) (void *ipipe, + struct v4l2_mbus_framefmt *format); + /* set output mbus format */ + void (*set_out_mbus_format) (void *ipipe, + struct v4l2_mbus_framefmt *format); + /* set output2(resizer-B) mbus format */ + void (*set_out2_mbus_format) (void *ipipe, + struct v4l2_mbus_framefmt *format); + /* Get output enable/disable status */ + int (*get_output_state) (void *ipipe, unsigned char out_sel); + /* Get output line lenght */ + int (*get_line_length) (void *ipipe, unsigned char out_sel); + /* Get the output image height */ + int (*get_image_height) (void *ipipe, unsigned char out_sel); + /* Get current output window param at the IMP */ + int (*get_output_win) (void *ipipe, struct imp_window *win); + /* get maximum output width of rsz-a or rsz_b*/ + int (*get_max_output_width) (int rsz); + /* get maximum output height of rsa-a or rsz-b */ + int (*get_max_output_height) (int rsz); + /* Enumerate pixel format for a given input format */ + int (*enum_pix) (void *ipipe, u32 *output_pix, int index); +}; + +struct imp_hw_interface *imp_get_hw_if(void); + +#endif diff --git a/include/linux/dm365_ipipe.h b/include/linux/dm365_ipipe.h new file mode 100644 index 0000000..8742671 --- /dev/null +++ b/include/linux/dm365_ipipe.h @@ -0,0 +1,1029 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _DM365_IPIPE_H +#define _DM365_IPIPE_H + +#include "dm3xx_ipipeif.h" + +/********************************************************************** +** Previewer API Structures +**********************************************************************/ + +/* Previewer module IDs used in PREV_SET/GET_PARAM IOCTL. Some + * modules can be also be updated during IPIPE operation. They are + * marked as control ID + */ +/* LUT based Defect Pixel Correction */ +#define PREV_LUTDPC 1 +/* On the fly (OTF) Defect Pixel Correction */ +#define PREV_OTFDPC 2 +/* Noise Filter - 1 */ +#define PREV_NF1 3 +/* Noise Filter - 2 */ +#define PREV_NF2 4 +/* White Balance. Also a control ID */ +#define PREV_WB 5 +/* 1st RGB to RBG Blend module */ +#define PREV_RGB2RGB_1 6 +/* 2nd RGB to RBG Blend module */ +#define PREV_RGB2RGB_2 7 +/* Gamma Correction */ +#define PREV_GAMMA 8 +/* 3D LUT color conversion */ +#define PREV_3D_LUT 9 +/* RGB to YCbCr module */ +#define PREV_RGB2YUV 10 +/* YUV 422 conversion module */ +#define PREV_YUV422_CONV 11 +/* Luminance Adjustment module. Also a control ID */ +#define PREV_LUM_ADJ 12 +/* Edge Enhancement */ +#define PREV_YEE 13 +/* Green Imbalance Correction */ +#define PREV_GIC 14 +/* CFA Interpolation */ +#define PREV_CFA 15 +/* Chroma Artifact Reduction */ +#define PREV_CAR 16 +/* Chroma Gain Suppression */ +#define PREV_CGS 17 +/* Global brighness and contrast control */ +#define PREV_GBCE 18 +/* Last module ID */ +#define PREV_MAX_MODULES 18 + +struct ipipe_float_u16 { + unsigned short integer; + unsigned short decimal; +}; + +struct ipipe_float_s16 { + short integer; + unsigned short decimal; +}; + +struct ipipe_float_u8 { + unsigned char integer; + unsigned char decimal; +}; + +struct ipipe_win { + /* vertical start line */ + unsigned int vst; + /* horizontal start pixel */ + unsigned int hst; + /* width */ + unsigned int width; + /* height */ + unsigned int height; +}; + +/* Copy method selection for vertical correction + * Used when ipipe_dfc_corr_meth is PREV_DPC_CTORB_AFTER_HINT + */ +enum ipipe_dpc_corr_meth { + /* replace by black or white dot specified by repl_white */ + IPIPE_DPC_REPL_BY_DOT = 0, + /* Copy from left */ + IPIPE_DPC_CL, + /* Copy from right */ + IPIPE_DPC_CR, + /* Horizontal interpolation */ + IPIPE_DPC_H_INTP, + /* Vertical interpolation */ + IPIPE_DPC_V_INTP, + /* Copy from top */ + IPIPE_DPC_CT, + /* Copy from bottom */ + IPIPE_DPC_CB, + /* 2D interpolation */ + IPIPE_DPC_2D_INTP, +}; + +struct ipipe_lutdpc_entry { + /* Horizontal position */ + unsigned short horz_pos; + /* vertical position */ + unsigned short vert_pos; + enum ipipe_dpc_corr_meth method; +}; + +#define MAX_SIZE_DPC 256 +/* Struct for configuring DPC module */ +struct prev_lutdpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* 0 - replace with black dot, 1 - white dot when correction + * method is IPIPE_DFC_REPL_BY_DOT=0, + */ + unsigned char repl_white; + /* number of entries in the correction table. Currently only + * support upto 256 entries. infinite mode is not supported + */ + unsigned short dpc_size; + struct ipipe_lutdpc_entry *table; +}; + +enum ipipe_otfdpc_det_meth { + IPIPE_DPC_OTF_MIN_MAX, + IPIPE_DPC_OTF_MIN_MAX2 +}; + +struct ipipe_otfdpc_thr { + unsigned short r; + unsigned short gr; + unsigned short gb; + unsigned short b; +}; + +enum ipipe_otfdpc_alg { + IPIPE_OTFDPC_2_0, + IPIPE_OTFDPC_3_0 +}; + +struct prev_otfdpc_2_0 { + /* defect detection threshold for MIN_MAX2 method (DPC 2.0 alg) */ + struct ipipe_otfdpc_thr det_thr; + /* defect correction threshold for MIN_MAX2 method (DPC 2.0 alg) or + * maximum value for MIN_MAX method + */ + struct ipipe_otfdpc_thr corr_thr; +}; + +struct prev_otfdpc_3_0 { + /* DPC3.0 activity adj shf. activity = (max2-min2) >> (6 -shf) + */ + unsigned char act_adj_shf; + /* DPC3.0 detection threshold, THR */ + unsigned short det_thr; + /* DPC3.0 detection threshold slope, SLP */ + unsigned short det_slp; + /* DPC3.0 detection threshold min, MIN */ + unsigned short det_thr_min; + /* DPC3.0 detection threshold max, MAX */ + unsigned short det_thr_max; + /* DPC3.0 correction threshold, THR */ + unsigned short corr_thr; + /* DPC3.0 correction threshold slope, SLP */ + unsigned short corr_slp; + /* DPC3.0 correction threshold min, MIN */ + unsigned short corr_thr_min; + /* DPC3.0 correction threshold max, MAX */ + unsigned short corr_thr_max; +}; + +struct prev_otfdpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* defect detection method */ + enum ipipe_otfdpc_det_meth det_method; + /* Algorith used. Applicable only when IPIPE_DPC_OTF_MIN_MAX2 is + * used + */ + enum ipipe_otfdpc_alg alg; + union { + /* if alg is IPIPE_OTFDPC_2_0 */ + struct prev_otfdpc_2_0 dpc_2_0; + /* if alg is IPIPE_OTFDPC_3_0 */ + struct prev_otfdpc_3_0 dpc_3_0; + } alg_cfg; +}; + +/* Threshold values table size */ +#define IPIPE_NF_THR_TABLE_SIZE 8 +/* Intensity values table size */ +#define IPIPE_NF_STR_TABLE_SIZE 8 + +/* NF, sampling method for green pixels */ +enum ipipe_nf_sampl_meth { + /* Same as R or B */ + IPIPE_NF_BOX, + /* Diamond mode */ + IPIPE_NF_DIAMOND +}; + +/* Struct for configuring NF module */ +struct prev_nf { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* Sampling method for green pixels */ + enum ipipe_nf_sampl_meth gr_sample_meth; + /* Down shift value in LUT reference address + */ + unsigned char shft_val; + /* Spread value in NF algorithm + */ + unsigned char spread_val; + /* Apply LSC gain to threshold. Enable this only if + * LSC is enabled in ISIF + */ + unsigned char apply_lsc_gain; + /* Threshold values table */ + unsigned short thr[IPIPE_NF_THR_TABLE_SIZE]; + /* intensity values table */ + unsigned char str[IPIPE_NF_STR_TABLE_SIZE]; + /* Edge detection minimum threshold */ + unsigned short edge_det_min_thr; + /* Edge detection maximum threshold */ + unsigned short edge_det_max_thr; +}; + +enum ipipe_gic_alg { + IPIPE_GIC_ALG_CONST_GAIN, + IPIPE_GIC_ALG_ADAPT_GAIN +}; + +enum ipipe_gic_thr_sel { + IPIPE_GIC_THR_REG, + IPIPE_GIC_THR_NF +}; + +enum ipipe_gic_wt_fn_type { + /* Use difference as index */ + IPIPE_GIC_WT_FN_TYP_DIF, + /* Use weight function as index */ + IPIPE_GIC_WT_FN_TYP_HP_VAL +}; + +/* structure for Green Imbalance Correction */ +struct prev_gic { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* 0 - Constant gain , 1 - Adaptive gain algorithm */ + enum ipipe_gic_alg gic_alg; + /* GIC gain or weight. Used for Constant gain and Adaptive algorithms + */ + unsigned short gain; + /* Threshold selection. GIC register values or NF2 thr table */ + enum ipipe_gic_thr_sel thr_sel; + /* thr1. Used when thr_sel is IPIPE_GIC_THR_REG */ + unsigned short thr; + /* this value is used for thr2-thr1, thr3-thr2 or + * thr4-thr3 when wt_fn_type is index. Otherwise it + * is the + */ + unsigned short slope; + /* Apply LSC gain to threshold. Enable this only if + * LSC is enabled in ISIF & thr_sel is IPIPE_GIC_THR_REG + */ + unsigned char apply_lsc_gain; + /* Multiply Nf2 threshold by this gain. Use this when thr_sel + * is IPIPE_GIC_THR_NF + */ + struct ipipe_float_u8 nf2_thr_gain; + /* Weight function uses difference as index or high pass value. + * Used for adaptive gain algorithm + */ + enum ipipe_gic_wt_fn_type wt_fn_type; +}; + +/* Struct for configuring WB module */ +struct prev_wb { + /* Offset (S12) for R */ + short ofst_r; + /* Offset (S12) for Gr */ + short ofst_gr; + /* Offset (S12) for Gb */ + short ofst_gb; + /* Offset (S12) for B */ + short ofst_b; + /* Gain (U13Q9) for Red */ + struct ipipe_float_u16 gain_r; + /* Gain (U13Q9) for Gr */ + struct ipipe_float_u16 gain_gr; + /* Gain (U13Q9) for Gb */ + struct ipipe_float_u16 gain_gb; + /* Gain (U13Q9) for Blue */ + struct ipipe_float_u16 gain_b; +}; + +enum ipipe_cfa_alg { + /* Algorithm is 2DirAC */ + IPIPE_CFA_ALG_2DIRAC, + /* Algorithm is 2DirAC + Digital Antialiasing (DAA) */ + IPIPE_CFA_ALG_2DIRAC_DAA, + /* Algorithm is DAA */ + IPIPE_CFA_ALG_DAA +}; + +/* Structure for CFA Interpolation */ +struct prev_cfa { + /* 2DirAC or 2DirAC + DAA */ + enum ipipe_cfa_alg alg; + /* 2Dir CFA HP value Low Threshold */ + unsigned short hpf_thr_2dir; + /* 2Dir CFA HP value slope */ + unsigned short hpf_slp_2dir; + /* 2Dir CFA HP mix threshold */ + unsigned short hp_mix_thr_2dir; + /* 2Dir CFA HP mix slope */ + unsigned short hp_mix_slope_2dir; + /* 2Dir Direction threshold */ + unsigned short dir_thr_2dir; + /* 2Dir Direction slope */ + unsigned short dir_slope_2dir; + /* 2Dir NonDirectional Weight */ + unsigned short nd_wt_2dir; + /* DAA Mono Hue Fraction */ + unsigned short hue_fract_daa; + /* DAA Mono Edge threshold */ + unsigned short edge_thr_daa; + /* DAA Mono threshold minimum */ + unsigned short thr_min_daa; + /* DAA Mono threshold slope */ + unsigned short thr_slope_daa; + /* DAA Mono slope minimum */ + unsigned short slope_min_daa; + /* DAA Mono slope slope */ + unsigned short slope_slope_daa; + /* DAA Mono LP wight */ + unsigned short lp_wt_daa; +}; + +/* Struct for configuring RGB2RGB blending module */ +struct prev_rgb2rgb { + /* Matrix coefficient for RR S12Q8 for ID = 1 and S11Q8 for ID = 2 */ + struct ipipe_float_s16 coef_rr; + /* Matrix coefficient for GR S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_gr; + /* Matrix coefficient for BR S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_br; + /* Matrix coefficient for RG S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_rg; + /* Matrix coefficient for GG S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_gg; + /* Matrix coefficient for BG S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_bg; + /* Matrix coefficient for RB S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_rb; + /* Matrix coefficient for GB S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_gb; + /* Matrix coefficient for BB S12Q8/S11Q8 */ + struct ipipe_float_s16 coef_bb; + /* Output offset for R S13/S11 */ + int out_ofst_r; + /* Output offset for G S13/S11 */ + int out_ofst_g; + /* Output offset for B S13/S11 */ + int out_ofst_b; +}; + +#define MAX_SIZE_GAMMA 512 + +enum ipipe_gamma_tbl_size { + IPIPE_GAMMA_TBL_SZ_64 = 64, + IPIPE_GAMMA_TBL_SZ_128 = 128, + IPIPE_GAMMA_TBL_SZ_256 = 256, + IPIPE_GAMMA_TBL_SZ_512 = 512 +}; + +enum ipipe_gamma_tbl_sel { + IPIPE_GAMMA_TBL_RAM, + IPIPE_GAMMA_TBL_ROM +}; + +struct ipipe_gamma_entry { + /* 10 bit slope */ + short slope; + /* 10 bit offset */ + unsigned short offset; +}; + +/* Struct for configuring Gamma correction module */ +struct prev_gamma { + /* 0 - Enable Gamma correction for Red + * 1 - bypass Gamma correction. Data is divided by 16 + */ + unsigned char bypass_r; + /* 0 - Enable Gamma correction for Blue + * 1 - bypass Gamma correction. Data is divided by 16 + */ + unsigned char bypass_b; + /* 0 - Enable Gamma correction for Green + * 1 - bypass Gamma correction. Data is divided by 16 + */ + unsigned char bypass_g; + /* PREV_GAMMA_TBL_RAM or PREV_GAMMA_TBL_ROM */ + enum ipipe_gamma_tbl_sel tbl_sel; + /* Table size for RAM gamma table. + */ + enum ipipe_gamma_tbl_size tbl_size; + /* R table */ + struct ipipe_gamma_entry *table_r; + /* Blue table */ + struct ipipe_gamma_entry *table_b; + /* Green table */ + struct ipipe_gamma_entry *table_g; +}; + +#define MAX_SIZE_3D_LUT (729) + +struct ipipe_3d_lut_entry { + /* 10 bit entry for red */ + unsigned short r; + /* 10 bit entry for green */ + unsigned short g; + /* 10 bit entry for blue */ + unsigned short b; +}; + +/* structure for 3D-LUT */ +struct prev_3d_lut { + /* enable/disable 3D lut */ + unsigned char en; + /* 3D - LUT table entry */ + struct ipipe_3d_lut_entry *table; +}; + +/* Struct for configuring Luminance Adjustment module */ +struct prev_lum_adj { + /* Brightness adjustments */ + unsigned char brightness; + /* contrast adjustments */ + unsigned char contrast; +}; + +/* Struct for configuring rgb2ycbcr module */ +struct prev_rgb2yuv { + /* Matrix coefficient for RY S12Q8 */ + struct ipipe_float_s16 coef_ry; + /* Matrix coefficient for GY S12Q8 */ + struct ipipe_float_s16 coef_gy; + /* Matrix coefficient for BY S12Q8 */ + struct ipipe_float_s16 coef_by; + /* Matrix coefficient for RCb S12Q8 */ + struct ipipe_float_s16 coef_rcb; + /* Matrix coefficient for GCb S12Q8 */ + struct ipipe_float_s16 coef_gcb; + /* Matrix coefficient for BCb S12Q8 */ + struct ipipe_float_s16 coef_bcb; + /* Matrix coefficient for RCr S12Q8 */ + struct ipipe_float_s16 coef_rcr; + /* Matrix coefficient for GCr S12Q8 */ + struct ipipe_float_s16 coef_gcr; + /* Matrix coefficient for BCr S12Q8 */ + struct ipipe_float_s16 coef_bcr; + /* Output offset for R S11 */ + int out_ofst_y; + /* Output offset for Cb S11 */ + int out_ofst_cb; + /* Output offset for Cr S11 */ + int out_ofst_cr; +}; + +enum ipipe_gbce_type { + IPIPE_GBCE_Y_VAL_TBL, + IPIPE_GBCE_GAIN_TBL +}; + +#define MAX_SIZE_GBCE_LUT 1024 + +/* structure for Global brighness and Contrast */ +struct prev_gbce { + /* enable/disable GBCE */ + unsigned char en; + /* Y - value table or Gain table */ + enum ipipe_gbce_type type; + /* ptr to LUT for GBCE with 1024 entries */ + unsigned short *table; +}; + +/* Chrominance position. Applicable only for YCbCr input + * Applied after edge enhancement + */ +enum ipipe_chr_pos { + /* Cositing, same position with luminance */ + IPIPE_YUV422_CHR_POS_COSITE, + /* Centering, In the middle of luminance */ + IPIPE_YUV422_CHR_POS_CENTRE +}; + +/* Struct for configuring yuv422 conversion module */ +struct prev_yuv422_conv { + /* Max Chrominance value */ + unsigned char en_chrom_lpf; + /* 1 - enable LPF for chrminance, 0 - disable */ + enum ipipe_chr_pos chrom_pos; +}; + +#define MAX_SIZE_YEE_LUT 1024 + +enum ipipe_yee_merge_meth { + IPIPE_YEE_ABS_MAX, + IPIPE_YEE_EE_ES +}; + +/* Struct for configuring YUV Edge Enhancement module */ +struct prev_yee { + /* 1 - enable enhancement, 0 - disable */ + unsigned char en; + /* enable/disable halo reduction in edge sharpner */ + unsigned char en_halo_red; + /* Merge method between Edge Enhancer and Edge sharpner */ + enum ipipe_yee_merge_meth merge_meth; + /* HPF Shift length */ + unsigned char hpf_shft; + /* HPF Coefficient 00, S10 */ + short hpf_coef_00; + /* HPF Coefficient 01, S10 */ + short hpf_coef_01; + /* HPF Coefficient 02, S10 */ + short hpf_coef_02; + /* HPF Coefficient 10, S10 */ + short hpf_coef_10; + /* HPF Coefficient 11, S10 */ + short hpf_coef_11; + /* HPF Coefficient 12, S10 */ + short hpf_coef_12; + /* HPF Coefficient 20, S10 */ + short hpf_coef_20; + /* HPF Coefficient 21, S10 */ + short hpf_coef_21; + /* HPF Coefficient 22, S10 */ + short hpf_coef_22; + /* Lower threshold before refering to LUT */ + unsigned short yee_thr; + /* Edge sharpener Gain */ + unsigned short es_gain; + /* Edge sharpener lowe threshold */ + unsigned short es_thr1; + /* Edge sharpener upper threshold */ + unsigned short es_thr2; + /* Edge sharpener gain on gradient */ + unsigned short es_gain_grad; + /* Edge sharpener offset on gradient */ + unsigned short es_ofst_grad; + /* Ptr to EE table. Must have 1024 entries */ + short *table; +}; + +enum ipipe_car_meth { + /* Chromatic Gain Control */ + IPIPE_CAR_CHR_GAIN_CTRL, + /* Dynamic switching between CHR_GAIN_CTRL + * and MED_FLTR + */ + IPIPE_CAR_DYN_SWITCH, + /* Median Filter */ + IPIPE_CAR_MED_FLTR +}; + +enum ipipe_car_hpf_type { + IPIPE_CAR_HPF_Y, + IPIPE_CAR_HPF_H, + IPIPE_CAR_HPF_V, + IPIPE_CAR_HPF_2D, + /* 2D HPF from YUV Edge Enhancement */ + IPIPE_CAR_HPF_2D_YEE +}; + +struct ipipe_car_gain { + /* csup_gain */ + unsigned char gain; + /* csup_shf. */ + unsigned char shft; + /* gain minimum */ + unsigned short gain_min; +}; + +/* Structure for Chromatic Artifact Reduction */ +struct prev_car { + /* enable/disable */ + unsigned char en; + /* Gain control or Dynamic switching */ + enum ipipe_car_meth meth; + /* Gain1 function configuration for Gain control */ + struct ipipe_car_gain gain1; + /* Gain2 function configuration for Gain control */ + struct ipipe_car_gain gain2; + /* HPF type used for CAR */ + enum ipipe_car_hpf_type hpf; + /* csup_thr: HPF threshold for Gain control */ + unsigned char hpf_thr; + /* Down shift value for hpf. 2 bits */ + unsigned char hpf_shft; + /* switch limit for median filter */ + unsigned char sw0; + /* switch coefficient for Gain control */ + unsigned char sw1; +}; + +/* structure for Chromatic Gain Suppression */ +struct prev_cgs { + /* enable/disable */ + unsigned char en; + /* gain1 bright side threshold */ + unsigned char h_thr; + /* gain1 bright side slope */ + unsigned char h_slope; + /* gain1 down shift value for bright side */ + unsigned char h_shft; + /* gain1 bright side minimum gain */ + unsigned char h_min; +}; + +enum ipipe_dpaths_bypass_t { + IPIPE_BYPASS_OFF, + IPIPE_BYPASS_ON +}; + +enum ipipe_colpat_t { + IPIPE_RED, + IPIPE_GREEN_RED, + IPIPE_GREEN_BLUE, + IPIPE_BLUE +}; + +enum down_scale_ave_sz { + IPIPE_DWN_SCALE_1_OVER_2, + IPIPE_DWN_SCALE_1_OVER_4, + IPIPE_DWN_SCALE_1_OVER_8, + IPIPE_DWN_SCALE_1_OVER_16, + IPIPE_DWN_SCALE_1_OVER_32, + IPIPE_DWN_SCALE_1_OVER_64, + IPIPE_DWN_SCALE_1_OVER_128, + IPIPE_DWN_SCALE_1_OVER_256 +}; + +/* Max pixels allowed in the input. If above this either decimation + * or frame division mode to be enabled + */ +#define IPIPE_MAX_INPUT_WIDTH 2600 + +/* Max pixels in resizer - A output. In downscale + * (DSCALE) mode, image quality is better, but has lesser + * maximum width allowed + */ +#define IPIPE_MAX_OUTPUT1_WIDTH_NORMAL 2176 +#define IPIPE_MAX_OUTPUT1_WIDTH_DSCALE 1088 + +/* Max pixels in resizer - B output. In downscale + * (DSCALE) mode, image quality is better, but has lesser + * maximum width allowed + */ +#define IPIPE_MAX_OUTPUT2_WIDTH_NORMAL 1088 +#define IPIPE_MAX_OUTPUT2_WIDTH_DSCALE 544 + +/* Structure for configuring Single Shot mode in the previewer + * channel + */ +struct prev_ss_input_spec { + /* line length. This will allow application to set a + * different line length than that calculated based on + * width. Set it to zero, if not used, + */ + unsigned int line_length; + /* vertical start position of the image + * data to IPIPE + */ + unsigned int vst; + /* horizontal start position of the image + * data to IPIPE + */ + unsigned int hst; + /* Global frame HD rate */ + unsigned int ppln; + /* Global frame VD rate */ + unsigned int lpfr; + /* dpcm predicator selection */ + enum ipipeif_dpcm_pred pred; + /* clock divide to bring down the pixel clock */ + struct ipipeif_5_1_clkdiv clk_div; + /* Shift data as per image sensor capture format + * only applicable for RAW Bayer inputs + */ + enum ipipeif_5_1_data_shift data_shift; + /* Enable decimation 1 - enable, 0 - disable + * This is used when image width is greater than + * ipipe line buffer size + */ + enum ipipeif_decimation dec_en; + /* used when en_dec = 1. Resize ratio for decimation + * when frame size is greater than what hw can handle. + * 16 to 112. IPIPE input width is calculated as follows. + * width = image_width * 16/ipipeif_rsz. For example + * if image_width is 1920 and user want to scale it down + * to 1280, use ipipeif_rsz = 24. 1920*16/24 = 1280 + */ + unsigned char rsz; + /* When input image width is greater that line buffer + * size, use this to do resize using frame division. The + * frame is divided into two vertical slices and resize + * is performed on each slice. Use either frame division + * mode or decimation, NOT both + */ + unsigned char frame_div_mode_en; + /* Enable/Disable avg filter at IPIPEIF. + * 1 - enable, 0 - disable + */ + unsigned char avg_filter_en; + /* Simple defect pixel correction based on a threshold value */ + struct ipipeif_dpc dpc; + /* gain applied to the ipipeif output */ + unsigned short gain; + /* clipped to this value at the ipipeif */ + unsigned short clip; + /* Align HSync and VSync to rsz_start */ + unsigned char align_sync; + /* ipipeif resize start position */ + unsigned int rsz_start; + /* pix order for YUV */ + enum ipipeif_pixel_order pix_order; + /* Color pattern for odd line, odd pixel */ + enum ipipe_colpat_t colp_olop; + /* Color pattern for odd line, even pixel */ + enum ipipe_colpat_t colp_olep; + /* Color pattern for even line, odd pixel */ + enum ipipe_colpat_t colp_elop; + /* Color pattern for even line, even pixel */ + enum ipipe_colpat_t colp_elep; +}; + +struct prev_single_shot_config { + /* Bypass image processing. RAW -> RAW */ + enum ipipe_dpaths_bypass_t bypass; + /* Input specification for the image data */ + struct prev_ss_input_spec input; +}; + +struct prev_cont_input_spec { + /* 1 - enable, 0 - disable df subtraction */ + unsigned char en_df_sub; + /* DF gain enable */ + unsigned char en_df_gain; + /* DF gain value */ + unsigned int df_gain; + /* DF gain threshold value */ + unsigned short df_gain_thr; + /* Enable decimation 1 - enable, 0 - disable + * This is used for bringing down the line size + * to that supported by IPIPE. DM355 IPIPE + * can process only 1344 pixels per line. + */ + enum ipipeif_decimation dec_en; + /* used when en_dec = 1. Resize ratio for decimation + * when frame size is greater than what hw can handle. + * 16 to 112. IPIPE input width is calculated as follows. + * width = image_width * 16/ipipeif_rsz. For example + * if image_width is 1920 and user want to scale it down + * to 1280, use ipipeif_rsz = 24. 1920*16/24 = 1280 + */ + unsigned char rsz; + /* Enable/Disable avg filter at IPIPEIF. + * 1 - enable, 0 - disable + */ + unsigned char avg_filter_en; + /* Gain applied at IPIPEIF. 1 - 1023. divided by 512. + * So can be from 1/512 to 1/1023. + */ + unsigned short gain; + /* clipped to this value at the output of IPIPEIF */ + unsigned short clip; + /* Align HSync and VSync to rsz_start */ + unsigned char align_sync; + /* ipipeif resize start position */ + unsigned int rsz_start; + /* Simple defect pixel correction based on a threshold value */ + struct ipipeif_dpc dpc; + /* Color pattern for odd line, odd pixel */ + enum ipipe_colpat_t colp_olop; + /* Color pattern for odd line, even pixel */ + enum ipipe_colpat_t colp_olep; + /* Color pattern for even line, odd pixel */ + enum ipipe_colpat_t colp_elop; + /* Color pattern for even line, even pixel */ + enum ipipe_colpat_t colp_elep; +}; + +/* Structure for configuring Continuous mode in the previewer + * channel . In continuous mode, only following parameters are + * available for configuration from user. Rest are configured + * through S_CROP and S_FMT IOCTLs in CCDC driver. In this mode + * data to IPIPEIF comes from CCDC + */ +struct prev_continuous_config { + /* Bypass image processing. RAW -> RAW */ + enum ipipe_dpaths_bypass_t bypass; + /* Input specification for the image data */ + struct prev_cont_input_spec input; +}; + +/******************************************************************* +** Resizer API structures +*******************************************************************/ +/* Interpolation types used for horizontal rescale */ +enum rsz_intp_t { + RSZ_INTP_CUBIC, + RSZ_INTP_LINEAR +}; + +/* Horizontal LPF intensity selection */ +enum rsz_h_lpf_lse_t { + RSZ_H_LPF_LSE_INTERN, + RSZ_H_LPF_LSE_USER_VAL +}; + +/* Structure for configuring resizer in single shot mode. + * This structure is used when operation mode of the + * resizer is single shot. The related IOCTL is + * RSZ_S_CONFIG & RSZ_G_CONFIG. When chained, data to + * resizer comes from previewer. When not chained, only + * UYVY data input is allowed for resizer operation. + * To operate on RAW Bayer data from CCDC, chain resizer + * with previewer by setting chain field in the + * rsz_channel_config structure. + */ + +struct rsz_ss_input_spec { + /* line length. This will allow application to set a + * different line length than that calculated based on + * width. Set it to zero, if not used, + */ + unsigned int line_length; + /* vertical start position of the image + * data to IPIPE + */ + unsigned int vst; + /* horizontal start position of the image + * data to IPIPE + */ + unsigned int hst; + /* Global frame HD rate */ + unsigned int ppln; + /* Global frame VD rate */ + unsigned int lpfr; + /* clock divide to bring down the pixel clock */ + struct ipipeif_5_1_clkdiv clk_div; + /* Enable decimation 1 - enable, 0 - disable. + * Used when input image width is greater than ipipe + * line buffer size, this is enabled to do resize + * at the input of the IPIPE to clip the size + */ + enum ipipeif_decimation dec_en; + /* used when en_dec = 1. Resize ratio for decimation + * when frame size is greater than what hw can handle. + * 16 to 112. IPIPE input width is calculated as follows. + * width = image_width * 16/ipipeif_rsz. For example + * if image_width is 1920 and user want to scale it down + * to 1280, use ipipeif_rsz = 24. 1920*16/24 = 1280 + */ + unsigned char rsz; + /* When input image width is greater that line buffer + * size, use this to do resize using frame division. The + * frame is divided into two vertical slices and resize + * is performed on each slice + */ + unsigned char frame_div_mode_en; + /* Enable/Disable avg filter at IPIPEIF. + * 1 - enable, 0 - disable + */ + unsigned char avg_filter_en; + /* Align HSync and VSync to rsz_start */ + unsigned char align_sync; + /* ipipeif resize start position */ + unsigned int rsz_start; +}; + +struct rsz_output_spec { + /* enable horizontal flip */ + unsigned char h_flip; + /* enable vertical flip */ + unsigned char v_flip; + /* line start offset for y. */ + unsigned int vst_y; + /* line start offset for c. Only for 420 */ + unsigned int vst_c; + /* vertical rescale interpolation type, YCbCr or Luminance */ + enum rsz_intp_t v_typ_y; + /* vertical rescale interpolation type for Chrominance */ + enum rsz_intp_t v_typ_c; + /* vertical lpf intensity - Luminance */ + unsigned char v_lpf_int_y; + /* vertical lpf intensity - Chrominance */ + unsigned char v_lpf_int_c; + /* horizontal rescale interpolation types, YCbCr or Luminance */ + enum rsz_intp_t h_typ_y; + /* horizontal rescale interpolation types, Chrominance */ + enum rsz_intp_t h_typ_c; + /* horizontal lpf intensity - Luminance */ + unsigned char h_lpf_int_y; + /* horizontal lpf intensity - Chrominance */ + unsigned char h_lpf_int_c; + /* Use down scale mode for scale down */ + unsigned char en_down_scale; + /* if downscale, set the downscale more average size for horizontal + * direction. Used only if output width and height is less than + * input sizes + */ + enum down_scale_ave_sz h_dscale_ave_sz; + /* if downscale, set the downscale more average size for vertical + * direction. Used only if output width and height is less than + * input sizes + */ + enum down_scale_ave_sz v_dscale_ave_sz; + /* Y offset. If set, the offset would be added to the base address + */ + unsigned int user_y_ofst; + /* C offset. If set, the offset would be added to the base address + */ + unsigned int user_c_ofst; +}; + +/* In continuous mode, few parameters are set by ccdc driver. So only + * part of the output spec is available for user configuration + */ +struct rsz_part_output_spec { + /* enable horizontal flip */ + unsigned char h_flip; + /* vertical rescale interpolation type, YCbCr or Luminance */ + unsigned char v_flip; + /* vertical rescale interpolation type for Chrominance */ + enum rsz_intp_t v_typ_y; + /* vertical rescale interpolation types */ + enum rsz_intp_t v_typ_c; + /* vertical lpf intensity - Luminance */ + unsigned char v_lpf_int_y; + /* horizontal rescale interpolation types, YCbCr or Luminance */ + unsigned char v_lpf_int_c; + /* horizontal rescale interpolation types, Chrominance */ + enum rsz_intp_t h_typ_y; + /* vertical lpf intensity - Chrominance */ + enum rsz_intp_t h_typ_c; + /* horizontal lpf intensity - Luminance */ + unsigned char h_lpf_int_y; + /* Use down scale mode for scale down */ + unsigned char h_lpf_int_c; + /* horizontal lpf intensity - Chrominance */ + unsigned char en_down_scale; + /* if downscale, set the downscale more average size for horizontal + * direction. Used only if output width and height is less than + * input sizes + */ + enum down_scale_ave_sz h_dscale_ave_sz; + /* if downscale, set the downscale more average size for vertical + * direction. Used only if output width and height is less than + * input sizes + */ + enum down_scale_ave_sz v_dscale_ave_sz; + /* Y offset. If set, the offset would be added to the base address + */ + unsigned int user_y_ofst; + /* C offset. If set, the offset would be added to the base address + */ + unsigned int user_c_ofst; +}; + +struct rsz_single_shot_config { + /* input spec of the image data (UYVY). non-chained + * mode. Only valid when not chained. For chained + * operation, previewer settings are used + */ + struct rsz_ss_input_spec input; + /* output spec of the image data coming out of resizer - 0(UYVY). + */ + struct rsz_output_spec output1; + /* output spec of the image data coming out of resizer - 1(UYVY). + */ + struct rsz_output_spec output2; + /* 0 , chroma sample at odd pixel, 1 - even pixel */ + unsigned char chroma_sample_even; + unsigned char yuv_y_min; + unsigned char yuv_y_max; + unsigned char yuv_c_min; + unsigned char yuv_c_max; + enum ipipe_chr_pos out_chr_pos; +}; + +struct rsz_continuous_config { + /* A subset of output spec is configured by application. + * Others such as size, position etc are set by CCDC driver + */ + struct rsz_part_output_spec output1; + struct rsz_output_spec output2; + /* output spec of the image data coming out of resizer - 1(UYVY). + */ + unsigned char chroma_sample_even; + /* 0 , chroma sample at odd pixel, 1 - even pixel */ + unsigned char yuv_y_min; + unsigned char yuv_y_max; + unsigned char yuv_c_min; + unsigned char yuv_c_max; + enum ipipe_chr_pos out_chr_pos; +}; + +#endif diff --git a/include/linux/imp_common.h b/include/linux/imp_common.h new file mode 100644 index 0000000..0b2c78e --- /dev/null +++ b/include/linux/imp_common.h @@ -0,0 +1,171 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef IMP_COMMON_H +#define IMP_COMMON_H + +#include "v4l2-mediabus.h" +#define IMP_MAX_NAME_SIZE 40 + +enum imp_data_paths { + IMP_RAW2RAW = 1, + IMP_RAW2YUV = 2, + IMP_YUV2YUV = 4 +}; + +struct imp_window { + /* horizontal size */ + unsigned int width; + /* vertical size */ + unsigned int height; + /* horizontal start position */ + unsigned int hst; + /* vertical start position */ + unsigned int vst; +}; + +/* structure used by application to query the modules + * available in the image processorr for preview the input + * image. Used for PREV_QUERY_CAP IOCTL + */ +struct prev_cap { + /* application use this to iterate over the available + * modules. stop when -EINVAL return code is returned by + * the driver + */ + unsigned short index; + /* Version of the preview module */ + char version[IMP_MAX_NAME_SIZE]; + /* Module IDs as defined above */ + unsigned short module_id; + /* control operation allowed in continuous mode ? + * 1 - allowed, 0 - not allowed + */ + char control; + /* path on which the module is sitting */ + enum imp_data_paths path; + char module_name[IMP_MAX_NAME_SIZE]; +}; + +/* struct to configure preview modules for which structures + * are defined above. Used by PREV_SET_PARAM or PREV_GET_PARAM IOCTLs. + */ +struct prev_module_param { + /* Version of the preview module */ + char version[IMP_MAX_NAME_SIZE]; + /* Length of the module config structure */ + unsigned short len; + /* Module IDs as defined above */ + unsigned short module_id; + /* Ptr to module config parameter. If SET command and is NULL + * module is reset to power on reset values + */ + void *param; +}; + +/* Structure for configuring the previewer driver. + * Used in PREV_SET_CONFIG/PREV_GET_CONFIG IOCTLs + */ +struct prev_channel_config { + /* Length of the user configuration */ + unsigned short len; + /* Ptr to either preview_single_shot_config or + * preview_continuous_config depending on oper_mode + */ + void *config; +}; + +struct prev_control { + /* Version of the preview module */ + char version[IMP_MAX_NAME_SIZE]; + /* Length of the module config structure */ + unsigned short len; + /* Module IDs as defined above */ + unsigned short module_id; + /* Ptr to module config parameter. If SET command and is NULL + * module is reset to power on reset values + */ + void *param; +}; + +/* Structure for RSZ_SET_CONFIG and RSZ_GET_CONFIG IOCTLs */ +struct rsz_channel_config { + /* Chain this resizer at the previewer output */ + unsigned char chain; + /* Length of the user configuration */ + unsigned short len; + /* ptr to either rsz_single_shot_config or rsz_continuous_config + * depending on oper_mode + */ + void *config; +}; + +/* RSZ_RECONFIG IOCTL. Used for re-configuring resizer + * before doing RSZ_RESIZE. This is a IOCTL to do fast reconfiguration + * of resizer. This assumes that corresponding resizer is already enabled + * through SET_CONFIG. This is used when the input image to be resized + * is either Y or C plane of a YUV 420 image. Typically, when channel is + * first configured, it is set up to resize Y plane. Then if application + * needs to resize C plane, this ioctl is called to switch the channel + * to resize C plane. + */ +struct rsz_reconfig { + enum v4l2_mbus_pixelcode pix_format; +}; + +/* Structure for setting dark frame in the IPIPE interface. + * Used in the PREV_SET_DARK_FRAME IOCTL + * Application captures a dark frame from CCDC with camera shutter + * closed and then call this ioctl to set this frame in ipipe interface. + * IPIPE operates in continuous mode for dark frame subtraction. + */ +struct prev_dark_frame { + /* address of the buffer used in the mmap system call. + This buffer has user captured dark frame + */ + int offset; + /* size of the buffer */ + int size; + /* width of the dark frame. Should match with current + * input capture area at CCDC driver + */ + int width; + /* height of the dark frame. Should match with current + * input capture area at the ccdc driver + */ + int height; +}; + +/* ioctls definition for previewer operations */ +#define PREV_IOC_BASE 'P' +#define PREV_S_PARAM _IOWR(PREV_IOC_BASE, 1,\ + struct prev_module_param) +#define PREV_G_PARAM _IOWR(PREV_IOC_BASE, 2,\ + struct prev_module_param) +#define PREV_ENUM_CAP _IOWR(PREV_IOC_BASE, 3, struct prev_cap) +#define PREV_S_CONFIG _IOWR(PREV_IOC_BASE, 4,\ + struct prev_channel_config) +#define PREV_G_CONFIG _IOWR(PREV_IOC_BASE, 5,\ + struct prev_channel_config) + +/* ioctls definitions for resizer operations */ +#define RSZ_IOC_BASE 'R' +#define RSZ_S_CONFIG _IOWR(RSZ_IOC_BASE, 1,\ + struct rsz_channel_config) +#define RSZ_G_CONFIG _IOWR(RSZ_IOC_BASE, 2,\ + struct rsz_channel_config) +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Sat Nov 12 09:06:02 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 12 Nov 2011 20:36:02 +0530 Subject: [PATCH RESEND] davinci: dm646x: move vpif related code to driver core header from platform Message-ID: <1321110362-6699-1-git-send-email-manjunath.hadli@ti.com> move vpif related code for capture and display drivers from dm646x platform header file to vpif_types.h as these definitions are related to driver code more than the platform or board. Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/include/mach/dm646x.h | 53 +------------------- drivers/media/video/davinci/vpif.h | 1 + drivers/media/video/davinci/vpif_capture.h | 2 +- drivers/media/video/davinci/vpif_display.h | 1 + include/media/davinci/vpif_types.h | 71 +++++++++++++++++++++++++++ 5 files changed, 75 insertions(+), 53 deletions(-) create mode 100644 include/media/davinci/vpif_types.h diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h index 2a00fe5..a8ee6c9 100644 --- a/arch/arm/mach-davinci/include/mach/dm646x.h +++ b/arch/arm/mach-davinci/include/mach/dm646x.h @@ -16,6 +16,7 @@ #include #include #include +#include #define DM646X_EMAC_BASE (0x01C80000) #define DM646X_EMAC_MDIO_BASE (DM646X_EMAC_BASE + 0x4000) @@ -34,58 +35,6 @@ int __init dm646x_init_edma(struct edma_rsv_info *rsv); void dm646x_video_init(void); -enum vpif_if_type { - VPIF_IF_BT656, - VPIF_IF_BT1120, - VPIF_IF_RAW_BAYER -}; - -struct vpif_interface { - enum vpif_if_type if_type; - unsigned hd_pol:1; - unsigned vd_pol:1; - unsigned fid_pol:1; -}; - -struct vpif_subdev_info { - const char *name; - struct i2c_board_info board_info; - u32 input; - u32 output; - unsigned can_route:1; - struct vpif_interface vpif_if; -}; - -struct vpif_display_config { - int (*set_clock)(int, int); - struct vpif_subdev_info *subdevinfo; - int subdev_count; - const char **output; - int output_count; - const char *card_name; -}; - -struct vpif_input { - struct v4l2_input input; - const char *subdev_name; -}; - -#define VPIF_CAPTURE_MAX_CHANNELS 2 - -struct vpif_capture_chan_config { - const struct vpif_input *inputs; - int input_count; -}; - -struct vpif_capture_config { - int (*setup_input_channel_mode)(int); - int (*setup_input_path)(int, const char *); - struct vpif_capture_chan_config chan_config[VPIF_CAPTURE_MAX_CHANNELS]; - struct vpif_subdev_info *subdev_info; - int subdev_count; - const char *card_name; -}; - void dm646x_setup_vpif(struct vpif_display_config *, struct vpif_capture_config *); diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h index 10550bd..25036cb 100644 --- a/drivers/media/video/davinci/vpif.h +++ b/drivers/media/video/davinci/vpif.h @@ -20,6 +20,7 @@ #include #include #include +#include /* Maximum channel allowed */ #define VPIF_NUM_CHANNELS (4) diff --git a/drivers/media/video/davinci/vpif_capture.h b/drivers/media/video/davinci/vpif_capture.h index 064550f..a693d4e 100644 --- a/drivers/media/video/davinci/vpif_capture.h +++ b/drivers/media/video/davinci/vpif_capture.h @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include "vpif.h" diff --git a/drivers/media/video/davinci/vpif_display.h b/drivers/media/video/davinci/vpif_display.h index 5d1936d..56879d1 100644 --- a/drivers/media/video/davinci/vpif_display.h +++ b/drivers/media/video/davinci/vpif_display.h @@ -22,6 +22,7 @@ #include #include #include +#include #include "vpif.h" diff --git a/include/media/davinci/vpif_types.h b/include/media/davinci/vpif_types.h new file mode 100644 index 0000000..9929b05 --- /dev/null +++ b/include/media/davinci/vpif_types.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _VPIF_TYPES_H +#define _VPIF_TYPES_H + +#define VPIF_CAPTURE_MAX_CHANNELS 2 + +enum vpif_if_type { + VPIF_IF_BT656, + VPIF_IF_BT1120, + VPIF_IF_RAW_BAYER +}; + +struct vpif_interface { + enum vpif_if_type if_type; + unsigned hd_pol:1; + unsigned vd_pol:1; + unsigned fid_pol:1; +}; + +struct vpif_subdev_info { + const char *name; + struct i2c_board_info board_info; + u32 input; + u32 output; + unsigned can_route:1; + struct vpif_interface vpif_if; +}; + +struct vpif_display_config { + int (*set_clock)(int, int); + struct vpif_subdev_info *subdevinfo; + int subdev_count; + const char **output; + int output_count; + const char *card_name; +}; + +struct vpif_input { + struct v4l2_input input; + const char *subdev_name; +}; + +struct vpif_capture_chan_config { + const struct vpif_input *inputs; + int input_count; +}; + +struct vpif_capture_config { + int (*setup_input_channel_mode)(int); + int (*setup_input_path)(int, const char *); + struct vpif_capture_chan_config chan_config[VPIF_CAPTURE_MAX_CHANNELS]; + struct vpif_subdev_info *subdev_info; + int subdev_count; + const char *card_name; +}; +#endif /* _VPIF_TYPES_H */ -- 1.6.2.4 From manjunath.hadli at ti.com Mon Nov 14 09:09:12 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Mon, 14 Nov 2011 20:39:12 +0530 Subject: [PATCH v2 0/5] davinci: re-arrange definitions to have a common davinci header Message-ID: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> Re-arrange definitions and remove unnecessary code so that we can have a common header for all davinci platforms. This will enable us to share defines and enable common routines to be used without polluting hardware.h. This patch set forms the base for a later set of patches for having a common system module base address (DAVINCI_SYSTEM_MODULE_BASE). Manjunath Hadli (5): davinci: dm644x: remove the macros from the header to move to c file davinci: dm365: remove the macros from the header to move to c file davinci: dm646x: remove the macros from the header to move to c file davinci: create new common platform header for davinci davinci: delete individual platform header files and use a common header arch/arm/mach-davinci/board-dm355-evm.c | 2 +- arch/arm/mach-davinci/board-dm355-leopard.c | 2 +- arch/arm/mach-davinci/board-dm365-evm.c | 2 +- arch/arm/mach-davinci/board-dm644x-evm.c | 2 +- arch/arm/mach-davinci/board-dm646x-evm.c | 2 +- arch/arm/mach-davinci/board-neuros-osd2.c | 2 +- arch/arm/mach-davinci/board-sffsdr.c | 2 +- arch/arm/mach-davinci/dm355.c | 2 +- arch/arm/mach-davinci/dm365.c | 18 ++++- arch/arm/mach-davinci/dm644x.c | 8 ++- arch/arm/mach-davinci/dm646x.c | 9 ++- arch/arm/mach-davinci/include/mach/dm355.h | 32 ------- arch/arm/mach-davinci/include/mach/dm365.h | 52 ------------ arch/arm/mach-davinci/include/mach/dm644x.h | 47 ----------- arch/arm/mach-davinci/include/mach/dm646x.h | 41 --------- drivers/media/video/davinci/vpif.h | 3 +- drivers/media/video/davinci/vpif_capture.h | 1 + drivers/media/video/davinci/vpif_display.c | 2 +- 19 files changed, 131 insertions(+), 186 deletions(-) create mode 100644 arch/arm/mach-davinci/include/mach/davinci_common.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm355.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm365.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm644x.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm646x.h From manjunath.hadli at ti.com Mon Nov 14 09:09:14 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Mon, 14 Nov 2011 20:39:14 +0530 Subject: [PATCH v2 2/5] davinci: dm365: remove the macros from the header to move to c file In-Reply-To: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> References: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321283357-27698-3-git-send-email-manjunath.hadli@ti.com> move the register base addresses and offsets used only by dm365 platform file from platform header dm365.h to dm365.c as they are used only in the c file. Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/dm365.c | 16 ++++++++++++++++ arch/arm/mach-davinci/include/mach/dm365.h | 16 ---------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 679e168..77edee8 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -40,6 +40,22 @@ #define DM365_REF_FREQ 24000000 /* 24 MHz on the DM365 EVM */ +/* Base of key scan register bank */ +#define DM365_KEYSCAN_BASE 0x01c69400 + +#define DM365_RTC_BASE 0x01c69000 + +#define DAVINCI_DM365_VC_BASE 0x01d0c000 +#define DAVINCI_DMA_VC_TX 2 +#define DAVINCI_DMA_VC_RX 3 + +#define DM365_EMAC_BASE 0x01d07000 +#define DM365_EMAC_MDIO_BASE (DM365_EMAC_BASE + 0x4000) +#define DM365_EMAC_CNTRL_OFFSET 0x0000 +#define DM365_EMAC_CNTRL_MOD_OFFSET 0x3000 +#define DM365_EMAC_CNTRL_RAM_OFFSET 0x1000 +#define DM365_EMAC_CNTRL_RAM_SIZE 0x2000 + static struct pll_data pll1_data = { .num = 1, .phys_base = DAVINCI_PLL1_BASE, diff --git a/arch/arm/mach-davinci/include/mach/dm365.h b/arch/arm/mach-davinci/include/mach/dm365.h index 2563bf4..51924de 100644 --- a/arch/arm/mach-davinci/include/mach/dm365.h +++ b/arch/arm/mach-davinci/include/mach/dm365.h @@ -20,22 +20,6 @@ #include #include -#define DM365_EMAC_BASE (0x01D07000) -#define DM365_EMAC_MDIO_BASE (DM365_EMAC_BASE + 0x4000) -#define DM365_EMAC_CNTRL_OFFSET (0x0000) -#define DM365_EMAC_CNTRL_MOD_OFFSET (0x3000) -#define DM365_EMAC_CNTRL_RAM_OFFSET (0x1000) -#define DM365_EMAC_CNTRL_RAM_SIZE (0x2000) - -/* Base of key scan register bank */ -#define DM365_KEYSCAN_BASE (0x01C69400) - -#define DM365_RTC_BASE (0x01C69000) - -#define DAVINCI_DM365_VC_BASE (0x01D0C000) -#define DAVINCI_DMA_VC_TX 2 -#define DAVINCI_DMA_VC_RX 3 - #define DM365_ASYNC_EMIF_CONTROL_BASE 0x01D10000 #define DM365_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 #define DM365_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 -- 1.6.2.4 From manjunath.hadli at ti.com Mon Nov 14 09:09:13 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Mon, 14 Nov 2011 20:39:13 +0530 Subject: [PATCH v2 1/5] davinci: dm644x: remove the macros from the header to move to c file In-Reply-To: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> References: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321283357-27698-2-git-send-email-manjunath.hadli@ti.com> move the register base addresses and offsets used only by dm644x platform file from platform header dm644x.h to dm644x.c as they are used only in the c file. Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/dm644x.c | 6 ++++++ arch/arm/mach-davinci/include/mach/dm644x.h | 7 ------- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 3470983..1b4b911 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -34,6 +34,12 @@ * Device specific clocks */ #define DM644X_REF_FREQ 27000000 +#define DM644X_EMAC_BASE 0x01c80000 +#define DM644X_EMAC_MDIO_BASE (DM644X_EMAC_BASE + 0x4000) +#define DM644X_EMAC_CNTRL_OFFSET 0x0000 +#define DM644X_EMAC_CNTRL_MOD_OFFSET 0x1000 +#define DM644X_EMAC_CNTRL_RAM_OFFSET 0x2000 +#define DM644X_EMAC_CNTRL_RAM_SIZE 0x2000 static struct pll_data pll1_data = { .num = 1, diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h index 5a1b26d..724377f 100644 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ b/arch/arm/mach-davinci/include/mach/dm644x.h @@ -27,13 +27,6 @@ #include #include -#define DM644X_EMAC_BASE (0x01C80000) -#define DM644X_EMAC_MDIO_BASE (DM644X_EMAC_BASE + 0x4000) -#define DM644X_EMAC_CNTRL_OFFSET (0x0000) -#define DM644X_EMAC_CNTRL_MOD_OFFSET (0x1000) -#define DM644X_EMAC_CNTRL_RAM_OFFSET (0x2000) -#define DM644X_EMAC_CNTRL_RAM_SIZE (0x2000) - #define DM644X_ASYNC_EMIF_CONTROL_BASE 0x01E00000 #define DM644X_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 #define DM644X_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 -- 1.6.2.4 From manjunath.hadli at ti.com Mon Nov 14 09:09:16 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Mon, 14 Nov 2011 20:39:16 +0530 Subject: [PATCH v2 4/5] davinci: create new common platform header for davinci In-Reply-To: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> References: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321283357-27698-5-git-send-email-manjunath.hadli@ti.com> remove the code from individual platform header files for dm365, dm355, dm644x and dm646x and consolidate it into a single and common header file davinci_common.h. Include the new header file in individual platform header files as a pre-cursor for deleting these headers in follow up patches. Signed-off-by: Manjunath Hadli --- .../arm/mach-davinci/include/mach/davinci_common.h | 88 ++++++++++++++++++++ arch/arm/mach-davinci/include/mach/dm355.h | 18 +---- arch/arm/mach-davinci/include/mach/dm365.h | 20 +---- arch/arm/mach-davinci/include/mach/dm644x.h | 15 +--- arch/arm/mach-davinci/include/mach/dm646x.h | 20 +---- 5 files changed, 92 insertions(+), 69 deletions(-) create mode 100644 arch/arm/mach-davinci/include/mach/davinci_common.h diff --git a/arch/arm/mach-davinci/include/mach/davinci_common.h b/arch/arm/mach-davinci/include/mach/davinci_common.h new file mode 100644 index 0000000..a859318 --- /dev/null +++ b/arch/arm/mach-davinci/include/mach/davinci_common.h @@ -0,0 +1,88 @@ +/* + * This file contains the processor specific definitions + * of the TI DM644x, DM355, DM365, and DM646X. + * + * Copyright (C) 2011 Texas Instruments Incorporated + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __DAVINCI_COMMON_H +#define __DAVINCI_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* DM355 base addresses */ +#define DM355_ASYNC_EMIF_CONTROL_BASE 0x01e10000 +#define DM355_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 + +#define ASP1_TX_EVT_EN 1 +#define ASP1_RX_EVT_EN 2 + +/* DM365 base addresses */ +#define DM365_ASYNC_EMIF_CONTROL_BASE 0x01d10000 +#define DM365_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 +#define DM365_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 + +/* DM644x base addresses */ +#define DM644X_ASYNC_EMIF_CONTROL_BASE 0x01e00000 +#define DM644X_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 +#define DM644X_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 +#define DM644X_ASYNC_EMIF_DATA_CE2_BASE 0x06000000 +#define DM644X_ASYNC_EMIF_DATA_CE3_BASE 0x08000000 + +/* DM646x base addresses */ +#define DM646X_ASYNC_EMIF_CONTROL_BASE 0x20008000 +#define DM646X_ASYNC_EMIF_CS2_SPACE_BASE 0x42000000 + +/* DM355 function declarations */ +struct spi_board_info; + +void __init dm355_init(void); +void dm355_init_spi0(unsigned chipselect_mask, + struct spi_board_info *info, unsigned len); +void __init dm355_init_asp1(u32 evt_enable, struct snd_platform_data *pdata); +void dm355_set_vpfe_config(struct vpfe_config *cfg); + +/* DM365 function declarations */ +void __init dm365_init(void); +void __init dm365_init_asp(struct snd_platform_data *pdata); +void __init dm365_init_vc(struct snd_platform_data *pdata); +void __init dm365_init_ks(struct davinci_ks_platform_data *pdata); +void __init dm365_init_rtc(void); +void dm365_init_spi0(unsigned chipselect_mask, + struct spi_board_info *info, unsigned len); + +void dm365_set_vpfe_config(struct vpfe_config *cfg); + +/* DM644X function declarations */ +void __init dm644x_init(void); +void __init dm644x_init_asp(struct snd_platform_data *pdata); +void dm644x_set_vpfe_config(struct vpfe_config *cfg); + +/* DM646X function declarations */ +void __init dm646x_init(void); +void __init dm646x_init_mcasp0(struct snd_platform_data *pdata); +void __init dm646x_init_mcasp1(struct snd_platform_data *pdata); +void __init dm646x_board_setup_refclk(struct clk *clk); +int __init dm646x_init_edma(struct edma_rsv_info *rsv); + +void dm646x_video_init(void); + +void dm646x_setup_vpif(struct vpif_display_config *, + struct vpif_capture_config *); +#endif /*__DAVINCI_COMMON_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm355.h b/arch/arm/mach-davinci/include/mach/dm355.h index 36dff4a..40c298b 100644 --- a/arch/arm/mach-davinci/include/mach/dm355.h +++ b/arch/arm/mach-davinci/include/mach/dm355.h @@ -11,22 +11,6 @@ #ifndef __ASM_ARCH_DM355_H #define __ASM_ARCH_DM355_H -#include -#include -#include - -#define DM355_ASYNC_EMIF_CONTROL_BASE 0x01E10000 -#define DM355_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 - -#define ASP1_TX_EVT_EN 1 -#define ASP1_RX_EVT_EN 2 - -struct spi_board_info; - -void __init dm355_init(void); -void dm355_init_spi0(unsigned chipselect_mask, - struct spi_board_info *info, unsigned len); -void __init dm355_init_asp1(u32 evt_enable, struct snd_platform_data *pdata); -void dm355_set_vpfe_config(struct vpfe_config *cfg); +#include #endif /* __ASM_ARCH_DM355_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm365.h b/arch/arm/mach-davinci/include/mach/dm365.h index 51924de..3c30934 100644 --- a/arch/arm/mach-davinci/include/mach/dm365.h +++ b/arch/arm/mach-davinci/include/mach/dm365.h @@ -13,24 +13,6 @@ #ifndef __ASM_ARCH_DM365_H #define __ASM_ARCH_DM665_H -#include -#include -#include -#include -#include -#include +#include -#define DM365_ASYNC_EMIF_CONTROL_BASE 0x01D10000 -#define DM365_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 -#define DM365_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 - -void __init dm365_init(void); -void __init dm365_init_asp(struct snd_platform_data *pdata); -void __init dm365_init_vc(struct snd_platform_data *pdata); -void __init dm365_init_ks(struct davinci_ks_platform_data *pdata); -void __init dm365_init_rtc(void); -void dm365_init_spi0(unsigned chipselect_mask, - struct spi_board_info *info, unsigned len); - -void dm365_set_vpfe_config(struct vpfe_config *cfg); #endif /* __ASM_ARCH_DM365_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h index 724377f..186fabb 100644 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ b/arch/arm/mach-davinci/include/mach/dm644x.h @@ -22,19 +22,6 @@ #ifndef __ASM_ARCH_DM644X_H #define __ASM_ARCH_DM644X_H -#include -#include -#include -#include - -#define DM644X_ASYNC_EMIF_CONTROL_BASE 0x01E00000 -#define DM644X_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 -#define DM644X_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 -#define DM644X_ASYNC_EMIF_DATA_CE2_BASE 0x06000000 -#define DM644X_ASYNC_EMIF_DATA_CE3_BASE 0x08000000 - -void __init dm644x_init(void); -void __init dm644x_init_asp(struct snd_platform_data *pdata); -void dm644x_set_vpfe_config(struct vpfe_config *cfg); +#include #endif /* __ASM_ARCH_DM644X_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h index eb95864..216e7c5 100644 --- a/arch/arm/mach-davinci/include/mach/dm646x.h +++ b/arch/arm/mach-davinci/include/mach/dm646x.h @@ -11,24 +11,6 @@ #ifndef __ASM_ARCH_DM646X_H #define __ASM_ARCH_DM646X_H -#include -#include -#include -#include -#include -#include - -#define DM646X_ASYNC_EMIF_CONTROL_BASE 0x20008000 -#define DM646X_ASYNC_EMIF_CS2_SPACE_BASE 0x42000000 - -void __init dm646x_init(void); -void __init dm646x_init_mcasp0(struct snd_platform_data *pdata); -void __init dm646x_init_mcasp1(struct snd_platform_data *pdata); -int __init dm646x_init_edma(struct edma_rsv_info *rsv); - -void dm646x_video_init(void); - -void dm646x_setup_vpif(struct vpif_display_config *, - struct vpif_capture_config *); +#include #endif /* __ASM_ARCH_DM646X_H */ -- 1.6.2.4 From manjunath.hadli at ti.com Mon Nov 14 09:09:15 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Mon, 14 Nov 2011 20:39:15 +0530 Subject: [PATCH v2 3/5] davinci: dm646x: remove the macros from the header to move to c file In-Reply-To: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> References: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321283357-27698-4-git-send-email-manjunath.hadli@ti.com> move the register base addresses and offsets used only by dm646x platform file from platform header dm646x.h to dm646x.c as they are used only in the c file. Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/dm646x.c | 7 +++++++ arch/arm/mach-davinci/include/mach/dm646x.h | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 0b68ed5..0560e82 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -46,6 +46,13 @@ #define DM646X_REF_FREQ 27000000 #define DM646X_AUX_FREQ 24000000 +#define DM646X_EMAC_BASE 0x01c80000 +#define DM646X_EMAC_MDIO_BASE (DM646X_EMAC_BASE + 0x4000) +#define DM646X_EMAC_CNTRL_OFFSET 0x0000 +#define DM646X_EMAC_CNTRL_MOD_OFFSET 0x1000 +#define DM646X_EMAC_CNTRL_RAM_OFFSET 0x2000 +#define DM646X_EMAC_CNTRL_RAM_SIZE 0x2000 + static struct pll_data pll1_data = { .num = 1, .phys_base = DAVINCI_PLL1_BASE, diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h index a8ee6c9..eb95864 100644 --- a/arch/arm/mach-davinci/include/mach/dm646x.h +++ b/arch/arm/mach-davinci/include/mach/dm646x.h @@ -18,13 +18,6 @@ #include #include -#define DM646X_EMAC_BASE (0x01C80000) -#define DM646X_EMAC_MDIO_BASE (DM646X_EMAC_BASE + 0x4000) -#define DM646X_EMAC_CNTRL_OFFSET (0x0000) -#define DM646X_EMAC_CNTRL_MOD_OFFSET (0x1000) -#define DM646X_EMAC_CNTRL_RAM_OFFSET (0x2000) -#define DM646X_EMAC_CNTRL_RAM_SIZE (0x2000) - #define DM646X_ASYNC_EMIF_CONTROL_BASE 0x20008000 #define DM646X_ASYNC_EMIF_CS2_SPACE_BASE 0x42000000 -- 1.6.2.4 From manjunath.hadli at ti.com Mon Nov 14 09:09:17 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Mon, 14 Nov 2011 20:39:17 +0530 Subject: [PATCH v2 5/5] davinci: delete individual platform header files and use a common header In-Reply-To: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> References: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321283357-27698-6-git-send-email-manjunath.hadli@ti.com> include davinci_common.h file in files using the platform header file for dm355, dm365, dm644x and dm646x and delete the individual platform header files. Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/board-dm355-evm.c | 2 +- arch/arm/mach-davinci/board-dm355-leopard.c | 2 +- arch/arm/mach-davinci/board-dm365-evm.c | 2 +- arch/arm/mach-davinci/board-dm644x-evm.c | 2 +- arch/arm/mach-davinci/board-dm646x-evm.c | 2 +- arch/arm/mach-davinci/board-neuros-osd2.c | 2 +- arch/arm/mach-davinci/board-sffsdr.c | 2 +- arch/arm/mach-davinci/dm355.c | 2 +- arch/arm/mach-davinci/dm365.c | 2 +- arch/arm/mach-davinci/dm644x.c | 2 +- arch/arm/mach-davinci/dm646x.c | 2 +- arch/arm/mach-davinci/include/mach/dm355.h | 16 ---------------- arch/arm/mach-davinci/include/mach/dm365.h | 18 ------------------ arch/arm/mach-davinci/include/mach/dm644x.h | 27 --------------------------- arch/arm/mach-davinci/include/mach/dm646x.h | 16 ---------------- drivers/media/video/davinci/vpif.h | 3 +-- drivers/media/video/davinci/vpif_capture.h | 1 + drivers/media/video/davinci/vpif_display.c | 2 +- 18 files changed, 14 insertions(+), 91 deletions(-) delete mode 100644 arch/arm/mach-davinci/include/mach/dm355.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm365.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm644x.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm646x.h diff --git a/arch/arm/mach-davinci/board-dm355-evm.c b/arch/arm/mach-davinci/board-dm355-evm.c index 4e0e707..ab20bd7 100644 --- a/arch/arm/mach-davinci/board-dm355-evm.c +++ b/arch/arm/mach-davinci/board-dm355-evm.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-dm355-leopard.c b/arch/arm/mach-davinci/board-dm355-leopard.c index ff2d241..f2cffa1 100644 --- a/arch/arm/mach-davinci/board-dm355-leopard.c +++ b/arch/arm/mach-davinci/board-dm355-leopard.c @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c index 1918ae7..f338b64 100644 --- a/arch/arm/mach-davinci/board-dm365-evm.c +++ b/arch/arm/mach-davinci/board-dm365-evm.c @@ -32,7 +32,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 0cf8abf..c12a02e 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index e574d7f..d7bfc63 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -36,7 +36,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-neuros-osd2.c b/arch/arm/mach-davinci/board-neuros-osd2.c index e5f231a..be73ed5 100644 --- a/arch/arm/mach-davinci/board-neuros-osd2.c +++ b/arch/arm/mach-davinci/board-neuros-osd2.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-sffsdr.c b/arch/arm/mach-davinci/board-sffsdr.c index 5dd4da9..330c9f9 100644 --- a/arch/arm/mach-davinci/board-sffsdr.c +++ b/arch/arm/mach-davinci/board-sffsdr.c @@ -35,7 +35,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index fe520d4..0929e0f 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -18,7 +18,7 @@ #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 77edee8..34aac1b 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -21,7 +21,7 @@ #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 1b4b911..c4ebee2 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -15,7 +15,7 @@ #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 0560e82..f543be3 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -16,7 +16,7 @@ #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/include/mach/dm355.h b/arch/arm/mach-davinci/include/mach/dm355.h deleted file mode 100644 index 40c298b..0000000 --- a/arch/arm/mach-davinci/include/mach/dm355.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Chip specific defines for DM355 SoC - * - * Author: Kevin Hilman, Deep Root Systems, LLC - * - * 2007 (c) Deep Root Systems, LLC. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ -#ifndef __ASM_ARCH_DM355_H -#define __ASM_ARCH_DM355_H - -#include - -#endif /* __ASM_ARCH_DM355_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm365.h b/arch/arm/mach-davinci/include/mach/dm365.h deleted file mode 100644 index 3c30934..0000000 --- a/arch/arm/mach-davinci/include/mach/dm365.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 2009 Texas Instruments Incorporated - * - * 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 version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef __ASM_ARCH_DM365_H -#define __ASM_ARCH_DM665_H - -#include - -#endif /* __ASM_ARCH_DM365_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h deleted file mode 100644 index 186fabb..0000000 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This file contains the processor specific definitions - * of the TI DM644x. - * - * Copyright (C) 2008 Texas Instruments. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __ASM_ARCH_DM644X_H -#define __ASM_ARCH_DM644X_H - -#include - -#endif /* __ASM_ARCH_DM644X_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h deleted file mode 100644 index 216e7c5..0000000 --- a/arch/arm/mach-davinci/include/mach/dm646x.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Chip specific defines for DM646x SoC - * - * Author: Kevin Hilman, Deep Root Systems, LLC - * - * 2007 (c) Deep Root Systems, LLC. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ -#ifndef __ASM_ARCH_DM646X_H -#define __ASM_ARCH_DM646X_H - -#include - -#endif /* __ASM_ARCH_DM646X_H */ diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h index 25036cb..73b00bd 100644 --- a/drivers/media/video/davinci/vpif.h +++ b/drivers/media/video/davinci/vpif.h @@ -18,8 +18,7 @@ #include #include -#include -#include +#include #include /* Maximum channel allowed */ diff --git a/drivers/media/video/davinci/vpif_capture.h b/drivers/media/video/davinci/vpif_capture.h index a693d4e..c019d26 100644 --- a/drivers/media/video/davinci/vpif_capture.h +++ b/drivers/media/video/davinci/vpif_capture.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include "vpif.h" diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 286f029..8921871 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -39,7 +39,7 @@ #include #include -#include +#include #include "vpif_display.h" #include "vpif.h" -- 1.6.2.4 From manjunath.hadli at ti.com Mon Nov 14 09:19:39 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Mon, 14 Nov 2011 15:19:39 +0000 Subject: [GIT PULL] davinci vpbe: enable DM365 v4l2 display driver In-Reply-To: <1317214968-8679-1-git-send-email-manjunath.hadli@ti.com> References: <1317214968-8679-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Mauro, A gentle reminder for the pull request. These have been acked by Hans. Cheers, -Manju On Wed, Sep 28, 2011 at 18:32:48, Hadli, Manjunath wrote: > Mauro, > Please pull : > git://linuxtv.org/mhadli/v4l-dvb-davinci_devices.git for_mauro > > The patchset adds incremental changes necessary to enable dm365 > v4l2 display driver, which includes vpbe display driver changes, osd specific changes and venc changes. The changes are incremental in nature,addind a few HD modes, and taking care of register level changes. > > The patches are tested for both SD and HD modes. > > Manjunath Hadli (3): > davinci vpbe: add dm365 VPBE display driver changes > davinci vpbe: add dm365 and dm355 specific OSD changes > davinci vpbe: add VENC block changes to enable dm365 and dm355 > > drivers/media/video/davinci/vpbe.c | 48 +++- > drivers/media/video/davinci/vpbe_osd.c | 472 ++++++++++++++++++++++++++++--- drivers/media/video/davinci/vpbe_venc.c | 205 ++++++++++++-- > include/media/davinci/vpbe.h | 16 + > include/media/davinci/vpbe_venc.h | 4 + > 5 files changed, 677 insertions(+), 68 deletions(-) > > From hverkuil at xs4all.nl Mon Nov 14 12:20:48 2011 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Mon, 14 Nov 2011 19:20:48 +0100 Subject: board-dm646x-evm.c: clock setup fix Message-ID: <1321294849-2738-1-git-send-email-hverkuil@xs4all.nl> The setup_vpif_input_channel_mode function uses the wrong register. Manju, can you review? I'm not sure whether this should be merged for v3.3 through the linux-media tree or the davinci tree. Opinions welcome. Actually, this fix should probably go in for v3.2 since it is a clear bug fix. Regards, Hans From hverkuil at xs4all.nl Mon Nov 14 12:20:49 2011 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Mon, 14 Nov 2011 19:20:49 +0100 Subject: [PATCH] board-dm646x-evm.c: wrong register used in setup_vpif_input_channel_mode In-Reply-To: <1321294849-2738-1-git-send-email-hverkuil@xs4all.nl> References: <1321294849-2738-1-git-send-email-hverkuil@xs4all.nl> Message-ID: <986dc5c6de4525aa3427ccded735d8e982080b0e.1321294701.git.hans.verkuil@cisco.com> From: Hans Verkuil The function setup_vpif_input_channel_mode() used the VSCLKDIS register instead of VIDCLKCTL. This meant that when in HD mode videoport channel 0 used a different clock from channel 1. Clearly a copy-and-paste error. Signed-off-by: Hans Verkuil --- arch/arm/mach-davinci/board-dm646x-evm.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index 337c45e..607a527 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -563,7 +563,7 @@ static int setup_vpif_input_channel_mode(int mux_mode) int val; u32 value; - if (!vpif_vsclkdis_reg || !cpld_client) + if (!vpif_vidclkctl_reg || !cpld_client) return -ENXIO; val = i2c_smbus_read_byte(cpld_client); @@ -571,7 +571,7 @@ static int setup_vpif_input_channel_mode(int mux_mode) return val; spin_lock_irqsave(&vpif_reg_lock, flags); - value = __raw_readl(vpif_vsclkdis_reg); + value = __raw_readl(vpif_vidclkctl_reg); if (mux_mode) { val &= VPIF_INPUT_TWO_CHANNEL; value |= VIDCH1CLK; @@ -579,7 +579,7 @@ static int setup_vpif_input_channel_mode(int mux_mode) val |= VPIF_INPUT_ONE_CHANNEL; value &= ~VIDCH1CLK; } - __raw_writel(value, vpif_vsclkdis_reg); + __raw_writel(value, vpif_vidclkctl_reg); spin_unlock_irqrestore(&vpif_reg_lock, flags); err = i2c_smbus_write_byte(cpld_client, val); -- 1.7.7 From nsekhar at ti.com Mon Nov 14 12:52:55 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Tue, 15 Nov 2011 00:22:55 +0530 Subject: [PATCH] ARM: restart: davinci: use new restart hook Message-ID: <1321296775-5110-1-git-send-email-nsekhar@ti.com> Rather than using davinci specifci davinci_soc_info based restart hook, use the existing arm_pm_restart pointer instead. This should finally graduate to using the restart hook provided in machine descriptor, but that needs more local header files in davinci. Tested on DM365 and AM18x EVMs. Signed-off-by: Sekhar Nori --- Applies to reset branch of rmk's tree. arch/arm/mach-davinci/common.c | 3 --- arch/arm/mach-davinci/da830.c | 1 - arch/arm/mach-davinci/da850.c | 1 - arch/arm/mach-davinci/devices-da8xx.c | 6 ++++++ arch/arm/mach-davinci/devices.c | 6 ++++++ arch/arm/mach-davinci/dm355.c | 1 - arch/arm/mach-davinci/dm365.c | 1 - arch/arm/mach-davinci/dm644x.c | 1 - arch/arm/mach-davinci/dm646x.c | 1 - arch/arm/mach-davinci/include/mach/common.h | 2 -- arch/arm/mach-davinci/include/mach/system.h | 2 -- arch/arm/mach-davinci/tnetv107x.c | 9 +++++++-- 12 files changed, 19 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-davinci/common.c b/arch/arm/mach-davinci/common.c index 865ffe5..cb9b2e4 100644 --- a/arch/arm/mach-davinci/common.c +++ b/arch/arm/mach-davinci/common.c @@ -97,9 +97,6 @@ void __init davinci_common_init(struct davinci_soc_info *soc_info) local_flush_tlb_all(); flush_cache_all(); - if (!davinci_soc_info.reset) - davinci_soc_info.reset = davinci_watchdog_reset; - /* * We want to check CPU revision early for cpu_is_xxxx() macros. * IO space mapping must be initialized before we can do that. diff --git a/arch/arm/mach-davinci/da830.c b/arch/arm/mach-davinci/da830.c index a6bf5dc..deee5c2 100644 --- a/arch/arm/mach-davinci/da830.c +++ b/arch/arm/mach-davinci/da830.c @@ -1201,7 +1201,6 @@ static struct davinci_soc_info davinci_soc_info_da830 = { .gpio_irq = IRQ_DA8XX_GPIO0, .serial_dev = &da8xx_serial_device, .emac_pdata = &da8xx_emac_pdata, - .reset_device = &da8xx_wdt_device, }; void __init da830_init(void) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index b047f87..0ed7fdb 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -1121,7 +1121,6 @@ static struct davinci_soc_info davinci_soc_info_da850 = { .emac_pdata = &da8xx_emac_pdata, .sram_dma = DA8XX_ARM_RAM_BASE, .sram_len = SZ_8K, - .reset_device = &da8xx_wdt_device, }; void __init da850_init(void) diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index 68def71..96b8bce 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -363,8 +363,14 @@ struct platform_device da8xx_wdt_device = { .resource = da8xx_watchdog_resources, }; +static void da8xx_restart(char mode, const char *cmd) +{ + davinci_watchdog_reset(&da8xx_wdt_device); +} + int __init da8xx_register_watchdog(void) { + arm_pm_restart = da8xx_restart; return platform_device_register(&da8xx_wdt_device); } diff --git a/arch/arm/mach-davinci/devices.c b/arch/arm/mach-davinci/devices.c index 806a2f0..433416a 100644 --- a/arch/arm/mach-davinci/devices.c +++ b/arch/arm/mach-davinci/devices.c @@ -291,8 +291,14 @@ struct platform_device davinci_wdt_device = { .resource = wdt_resources, }; +static void davinci_restart(char mode, const char *cmd) +{ + davinci_watchdog_reset(&davinci_wdt_device); +} + static void davinci_init_wdt(void) { + arm_pm_restart = davinci_restart; platform_device_register(&davinci_wdt_device); } diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index fe520d4..19667cf 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -853,7 +853,6 @@ static struct davinci_soc_info davinci_soc_info_dm355 = { .serial_dev = &dm355_serial_device, .sram_dma = 0x00010000, .sram_len = SZ_32K, - .reset_device = &davinci_wdt_device, }; void __init dm355_init_asp1(u32 evt_enable, struct snd_platform_data *pdata) diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 679e168..f15b435 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -1083,7 +1083,6 @@ static struct davinci_soc_info davinci_soc_info_dm365 = { .emac_pdata = &dm365_emac_pdata, .sram_dma = 0x00010000, .sram_len = SZ_32K, - .reset_device = &davinci_wdt_device, }; void __init dm365_init_asp(struct snd_platform_data *pdata) diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 3470983..0800f9c 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -767,7 +767,6 @@ static struct davinci_soc_info davinci_soc_info_dm644x = { .emac_pdata = &dm644x_emac_pdata, .sram_dma = 0x00008000, .sram_len = SZ_16K, - .reset_device = &davinci_wdt_device, }; void __init dm644x_init_asp(struct snd_platform_data *pdata) diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 0b68ed5..f8e07dc 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -855,7 +855,6 @@ static struct davinci_soc_info davinci_soc_info_dm646x = { .emac_pdata = &dm646x_emac_pdata, .sram_dma = 0x10010000, .sram_len = SZ_32K, - .reset_device = &davinci_wdt_device, }; void __init dm646x_init_mcasp0(struct snd_platform_data *pdata) diff --git a/arch/arm/mach-davinci/include/mach/common.h b/arch/arm/mach-davinci/include/mach/common.h index a57cba2..2f9d06d 100644 --- a/arch/arm/mach-davinci/include/mach/common.h +++ b/arch/arm/mach-davinci/include/mach/common.h @@ -77,8 +77,6 @@ struct davinci_soc_info { struct emac_platform_data *emac_pdata; dma_addr_t sram_dma; unsigned sram_len; - struct platform_device *reset_device; - void (*reset)(struct platform_device *); }; extern struct davinci_soc_info davinci_soc_info; diff --git a/arch/arm/mach-davinci/include/mach/system.h b/arch/arm/mach-davinci/include/mach/system.h index e65629c..6bd1dc0 100644 --- a/arch/arm/mach-davinci/include/mach/system.h +++ b/arch/arm/mach-davinci/include/mach/system.h @@ -20,8 +20,6 @@ static inline void arch_idle(void) static inline void arch_reset(char mode, const char *cmd) { - if (davinci_soc_info.reset) - davinci_soc_info.reset(davinci_soc_info.reset_device); } #endif /* __ASM_ARCH_SYSTEM_H */ diff --git a/arch/arm/mach-davinci/tnetv107x.c b/arch/arm/mach-davinci/tnetv107x.c index 409bb86..cffa147 100644 --- a/arch/arm/mach-davinci/tnetv107x.c +++ b/arch/arm/mach-davinci/tnetv107x.c @@ -730,6 +730,11 @@ static void tnetv107x_watchdog_reset(struct platform_device *pdev) __raw_writel(1, ®s->kick); } +static void tnetv107x_restart(char mode, const char *cmd) +{ + tnetv107x_watchdog_reset(&tnetv107x_wdt_device); +} + static struct davinci_soc_info tnetv107x_soc_info = { .io_desc = io_desc, .io_desc_num = ARRAY_SIZE(io_desc), @@ -752,11 +757,11 @@ static struct davinci_soc_info tnetv107x_soc_info = { .gpio_num = TNETV107X_N_GPIO, .timer_info = &timer_info, .serial_dev = &tnetv107x_serial_device, - .reset = tnetv107x_watchdog_reset, - .reset_device = &tnetv107x_wdt_device, }; void __init tnetv107x_init(void) { davinci_common_init(&tnetv107x_soc_info); + + arm_pm_restart = tnetv107x_restart; } -- 1.6.2.4 From nsekhar at ti.com Mon Nov 14 14:12:08 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Tue, 15 Nov 2011 01:42:08 +0530 Subject: [PATCH 3/4] ARM: davinci: dm646x does not have a DSP domain In-Reply-To: <1321301529-7582-2-git-send-email-nsekhar@ti.com> References: <1321301529-7582-1-git-send-email-nsekhar@ti.com> <1321301529-7582-2-git-send-email-nsekhar@ti.com> Message-ID: <1321301529-7582-3-git-send-email-nsekhar@ti.com> Fix the incorrect classification of DSP clock into a seperate DSP domain on DM646x. Per the reference guide (http://www.ti.com/lit/ug/spruep9e/spruep9e.pdf) there is only one "AlwaysON" power domain on DM6467. Signed-off-by: Sekhar Nori --- This patch has not been tested on DM6467 yet. arch/arm/mach-davinci/dm646x.c | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index b0c350a..d73f9f9 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -161,7 +161,6 @@ static struct clk dsp_clk = { .name = "dsp", .parent = &pll1_sysclk1, .lpsc = DM646X_LPSC_C64X_CPU, - .flags = PSC_DSP, .usecount = 1, /* REVISIT how to disable? */ }; -- 1.6.2.4 From nsekhar at ti.com Mon Nov 14 14:12:06 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Tue, 15 Nov 2011 01:42:06 +0530 Subject: [PATCH v4 1/4] ARM: davinci: psc: fix incorrect mask Message-ID: <1321301529-7582-1-git-send-email-nsekhar@ti.com> From: Murali Karicheri There are 5 LSB bits defined in PDSTAT and the code currently uses a mask of 1 bit to check the status. Use a proper mask per the hardware specification. While at it, use a #define for the mask to improve readability. Reviewed-by: Sergei Shtylyov Signed-off-by: Murali Karicheri Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/include/mach/psc.h | 1 + arch/arm/mach-davinci/psc.c | 2 +- 2 files changed, 2 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/psc.h b/arch/arm/mach-davinci/include/mach/psc.h index fa59c09..8bf279a 100644 --- a/arch/arm/mach-davinci/include/mach/psc.h +++ b/arch/arm/mach-davinci/include/mach/psc.h @@ -244,6 +244,7 @@ #define PSC_STATE_ENABLE 3 #define MDSTAT_STATE_MASK 0x3f +#define PDSTAT_STATE_MASK 0x1f #define MDCTL_FORCE BIT(31) #ifndef __ASSEMBLER__ diff --git a/arch/arm/mach-davinci/psc.c b/arch/arm/mach-davinci/psc.c index 1fb6bdf..a0e1770 100644 --- a/arch/arm/mach-davinci/psc.c +++ b/arch/arm/mach-davinci/psc.c @@ -80,7 +80,7 @@ void davinci_psc_config(unsigned int domain, unsigned int ctlr, __raw_writel(mdctl, psc_base + MDCTL + 4 * id); pdstat = __raw_readl(psc_base + PDSTAT); - if ((pdstat & 0x00000001) == 0) { + if ((pdstat & PDSTAT_STATE_MASK) == 0) { pdctl1 = __raw_readl(psc_base + PDCTL1); pdctl1 |= 0x1; __raw_writel(pdctl1, psc_base + PDCTL1); -- 1.6.2.4 From nsekhar at ti.com Mon Nov 14 14:12:07 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Tue, 15 Nov 2011 01:42:07 +0530 Subject: [PATCH v4 2/4] ARM: davinci: psc: fix incorrect offsets In-Reply-To: <1321301529-7582-1-git-send-email-nsekhar@ti.com> References: <1321301529-7582-1-git-send-email-nsekhar@ti.com> Message-ID: <1321301529-7582-2-git-send-email-nsekhar@ti.com> From: Murali Karicheri Seperate PDSTAT and PDCTL registers are defined for domain 0 and domain 1 where as the code always reads the domain 0 PDSTAT register and domain 1 PDCTL register. Fix this issue. While at it, introduce usage of macros for register masks to improve readability. Reviewed-by: Sergei Shtylyov Signed-off-by: Murali Karicheri Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/include/mach/psc.h | 4 +++- arch/arm/mach-davinci/psc.c | 16 ++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/psc.h b/arch/arm/mach-davinci/include/mach/psc.h index 8bf279a..8bc3fc2 100644 --- a/arch/arm/mach-davinci/include/mach/psc.h +++ b/arch/arm/mach-davinci/include/mach/psc.h @@ -233,7 +233,7 @@ #define PTCMD 0x120 #define PTSTAT 0x128 #define PDSTAT 0x200 -#define PDCTL1 0x304 +#define PDCTL 0x300 #define MDSTAT 0x800 #define MDCTL 0xA00 @@ -246,6 +246,8 @@ #define MDSTAT_STATE_MASK 0x3f #define PDSTAT_STATE_MASK 0x1f #define MDCTL_FORCE BIT(31) +#define PDCTL_NEXT BIT(1) +#define PDCTL_EPCGOOD BIT(8) #ifndef __ASSEMBLER__ diff --git a/arch/arm/mach-davinci/psc.c b/arch/arm/mach-davinci/psc.c index a0e1770..d7e210f 100644 --- a/arch/arm/mach-davinci/psc.c +++ b/arch/arm/mach-davinci/psc.c @@ -52,7 +52,7 @@ int __init davinci_psc_is_clk_active(unsigned int ctlr, unsigned int id) void davinci_psc_config(unsigned int domain, unsigned int ctlr, unsigned int id, bool enable, u32 flags) { - u32 epcpr, ptcmd, ptstat, pdstat, pdctl1, mdstat, mdctl; + u32 epcpr, ptcmd, ptstat, pdstat, pdctl, mdstat, mdctl; void __iomem *psc_base; struct davinci_soc_info *soc_info = &davinci_soc_info; u32 next_state = PSC_STATE_ENABLE; @@ -79,11 +79,11 @@ void davinci_psc_config(unsigned int domain, unsigned int ctlr, mdctl |= MDCTL_FORCE; __raw_writel(mdctl, psc_base + MDCTL + 4 * id); - pdstat = __raw_readl(psc_base + PDSTAT); + pdstat = __raw_readl(psc_base + PDSTAT + 4 * domain); if ((pdstat & PDSTAT_STATE_MASK) == 0) { - pdctl1 = __raw_readl(psc_base + PDCTL1); - pdctl1 |= 0x1; - __raw_writel(pdctl1, psc_base + PDCTL1); + pdctl = __raw_readl(psc_base + PDCTL + 4 * domain); + pdctl |= PDCTL_NEXT; + __raw_writel(pdctl, psc_base + PDCTL + 4 * domain); ptcmd = 1 << domain; __raw_writel(ptcmd, psc_base + PTCMD); @@ -92,9 +92,9 @@ void davinci_psc_config(unsigned int domain, unsigned int ctlr, epcpr = __raw_readl(psc_base + EPCPR); } while ((((epcpr >> domain) & 1) == 0)); - pdctl1 = __raw_readl(psc_base + PDCTL1); - pdctl1 |= 0x100; - __raw_writel(pdctl1, psc_base + PDCTL1); + pdctl = __raw_readl(psc_base + PDCTL + 4 * domain); + pdctl |= PDCTL_EPCGOOD; + __raw_writel(pdctl, psc_base + PDCTL + 4 * domain); } else { ptcmd = 1 << domain; __raw_writel(ptcmd, psc_base + PTCMD); -- 1.6.2.4 From nsekhar at ti.com Mon Nov 14 14:12:09 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Tue, 15 Nov 2011 01:42:09 +0530 Subject: [PATCH v4 4/4] ARM: davinci: add support for multiple power domains In-Reply-To: <1321301529-7582-3-git-send-email-nsekhar@ti.com> References: <1321301529-7582-1-git-send-email-nsekhar@ti.com> <1321301529-7582-2-git-send-email-nsekhar@ti.com> <1321301529-7582-3-git-send-email-nsekhar@ti.com> Message-ID: <1321301529-7582-4-git-send-email-nsekhar@ti.com> From: Murali Karicheri On a new SoC based on DaVinci, there are multiple power domains similar to that in C6670 (c6x). Currently the clock module assumes that there are only two power domains (0 and 1). This patch removes this restriction to allow porting on to the new SoC. Reviewed-by :Sergei Shtylyov Signed-off-by: Murali Karicheri Signed-off-by: Sekhar Nori --- This patch has not been tested on DM644x yet. arch/arm/mach-davinci/clock.c | 13 +++---------- arch/arm/mach-davinci/clock.h | 10 +++++----- arch/arm/mach-davinci/dm644x.c | 4 ++-- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c index 0086113..008772e 100644 --- a/arch/arm/mach-davinci/clock.c +++ b/arch/arm/mach-davinci/clock.c @@ -31,19 +31,12 @@ static LIST_HEAD(clocks); static DEFINE_MUTEX(clocks_mutex); static DEFINE_SPINLOCK(clockfw_lock); -static unsigned psc_domain(struct clk *clk) -{ - return (clk->flags & PSC_DSP) - ? DAVINCI_GPSC_DSPDOMAIN - : DAVINCI_GPSC_ARMDOMAIN; -} - static void __clk_enable(struct clk *clk) { if (clk->parent) __clk_enable(clk->parent); if (clk->usecount++ == 0 && (clk->flags & CLK_PSC)) - davinci_psc_config(psc_domain(clk), clk->gpsc, clk->lpsc, + davinci_psc_config(clk->domain, clk->gpsc, clk->lpsc, true, clk->flags); } @@ -53,7 +46,7 @@ static void __clk_disable(struct clk *clk) return; if (--clk->usecount == 0 && !(clk->flags & CLK_PLL) && (clk->flags & CLK_PSC)) - davinci_psc_config(psc_domain(clk), clk->gpsc, clk->lpsc, + davinci_psc_config(clk->domain, clk->gpsc, clk->lpsc, false, clk->flags); if (clk->parent) __clk_disable(clk->parent); @@ -237,7 +230,7 @@ static int __init clk_disable_unused(void) pr_debug("Clocks: disable unused %s\n", ck->name); - davinci_psc_config(psc_domain(ck), ck->gpsc, ck->lpsc, + davinci_psc_config(ck->domain, ck->gpsc, ck->lpsc, false, ck->flags); } spin_unlock_irq(&clockfw_lock); diff --git a/arch/arm/mach-davinci/clock.h b/arch/arm/mach-davinci/clock.h index a705f36..46f0f1b 100644 --- a/arch/arm/mach-davinci/clock.h +++ b/arch/arm/mach-davinci/clock.h @@ -93,6 +93,7 @@ struct clk { u8 usecount; u8 lpsc; u8 gpsc; + u8 domain; u32 flags; struct clk *parent; struct list_head children; /* list of children */ @@ -107,11 +108,10 @@ struct clk { /* Clock flags: SoC-specific flags start at BIT(16) */ #define ALWAYS_ENABLED BIT(1) #define CLK_PSC BIT(2) -#define PSC_DSP BIT(3) /* PSC uses DSP domain, not ARM */ -#define CLK_PLL BIT(4) /* PLL-derived clock */ -#define PRE_PLL BIT(5) /* source is before PLL mult/div */ -#define PSC_SWRSTDISABLE BIT(6) /* Disable state is SwRstDisable */ -#define PSC_FORCE BIT(7) /* Force module state transtition */ +#define CLK_PLL BIT(3) /* PLL-derived clock */ +#define PRE_PLL BIT(4) /* source is before PLL mult/div */ +#define PSC_SWRSTDISABLE BIT(5) /* Disable state is SwRstDisable */ +#define PSC_FORCE BIT(6) /* Force module state transtition */ #define CLK(dev, con, ck) \ { \ diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 555ff5b..f38c4bb 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -130,7 +130,7 @@ static struct clk dsp_clk = { .name = "dsp", .parent = &pll1_sysclk1, .lpsc = DAVINCI_LPSC_GEM, - .flags = PSC_DSP, + .domain = DAVINCI_GPSC_DSPDOMAIN, .usecount = 1, /* REVISIT how to disable? */ }; @@ -145,7 +145,7 @@ static struct clk vicp_clk = { .name = "vicp", .parent = &pll1_sysclk2, .lpsc = DAVINCI_LPSC_IMCOP, - .flags = PSC_DSP, + .domain = DAVINCI_GPSC_DSPDOMAIN, .usecount = 1, /* REVISIT how to disable? */ }; -- 1.6.2.4 From nsekhar at ti.com Mon Nov 14 14:24:20 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 14 Nov 2011 20:24:20 +0000 Subject: [PATCH 1/2 - v3] ARM: davinci - fix incorrect offsets and mask usage in psc code In-Reply-To: <1316813173-31270-1-git-send-email-m-karicheri2@ti.com> References: <1316813173-31270-1-git-send-email-m-karicheri2@ti.com> Message-ID: Hi Murali, I posted an updated version of this patch. In future, can you please CC LAKML in for all kernel patches. And, the subject like prefix we are using is: "ARM: davinci: " On Sat, Sep 24, 2011 at 02:56:12, Karicheri, Muralidharan wrote: > From: Murali Karicheri > > There are 5 LSB bits defined in PDSTAT and the code currently uses > a mask of 1 bit to check the status. Also there is PDSTAT and PDCTL > registers defined for domain0 and domain1 where as the code > always read the domain0 PDSTAT register and domain1 PDCTL register. > This patch fixes these issues. These two issues are now dealt in separate patches. > > Reviewed-by: Sergei Shtylyov > > Signed-off-by: Murali Karicheri > --- > change from v2-v3 > - description of patch changed as per review comment > - defined a constant for PDSTAT_STATE_MASK > This patch applies to the davinci-next branch of linux-davinci tree > > arch/arm/mach-davinci/include/mach/psc.h | 3 ++- > arch/arm/mach-davinci/psc.c | 18 +++++++++--------- > 2 files changed, 11 insertions(+), 10 deletions(-) > > diff --git a/arch/arm/mach-davinci/include/mach/psc.h b/arch/arm/mach-davinci/include/mach/psc.h > index a47e6f2..1724d95 100644 > --- a/arch/arm/mach-davinci/include/mach/psc.h > +++ b/arch/arm/mach-davinci/include/mach/psc.h > @@ -233,7 +233,7 @@ > #define PTCMD 0x120 > #define PTSTAT 0x128 > #define PDSTAT 0x200 > -#define PDCTL1 0x304 > +#define PDCTL 0x300 > #define MDSTAT 0x800 > #define MDCTL 0xA00 > > @@ -244,6 +244,7 @@ > #define PSC_STATE_ENABLE 3 > > #define MDSTAT_STATE_MASK 0x1f > +#define PDSTAT_STATE_MASK 0x1f > > #ifndef __ASSEMBLER__ > > diff --git a/arch/arm/mach-davinci/psc.c b/arch/arm/mach-davinci/psc.c > index a415804..5dbe974 100644 > --- a/arch/arm/mach-davinci/psc.c > +++ b/arch/arm/mach-davinci/psc.c > @@ -50,7 +50,7 @@ int __init davinci_psc_is_clk_active(unsigned int ctlr, unsigned int id) > void davinci_psc_config(unsigned int domain, unsigned int ctlr, > unsigned int id, u32 next_state) > { > - u32 epcpr, ptcmd, ptstat, pdstat, pdctl1, mdstat, mdctl; > + u32 epcpr, ptcmd, ptstat, pdstat, pdctl, mdstat, mdctl; > void __iomem *psc_base; > struct davinci_soc_info *soc_info = &davinci_soc_info; > > @@ -67,11 +67,11 @@ void davinci_psc_config(unsigned int domain, unsigned int ctlr, > mdctl |= next_state; > __raw_writel(mdctl, psc_base + MDCTL + 4 * id); > > - pdstat = __raw_readl(psc_base + PDSTAT); > - if ((pdstat & 0x00000001) == 0) { > - pdctl1 = __raw_readl(psc_base + PDCTL1); > - pdctl1 |= 0x1; > - __raw_writel(pdctl1, psc_base + PDCTL1); > + pdstat = __raw_readl(psc_base + PDSTAT + 4 * domain); > + if ((pdstat & PDSTAT_STATE_MASK) == 0) { > + pdctl = __raw_readl(psc_base + PDCTL + 4 * domain); > + pdctl |= 0x1; I have defined and used the macro PDCTL_NEXT here. > + __raw_writel(pdctl, psc_base + PDCTL + 4 * domain); > > ptcmd = 1 << domain; > __raw_writel(ptcmd, psc_base + PTCMD); > @@ -80,9 +80,9 @@ void davinci_psc_config(unsigned int domain, unsigned int ctlr, > epcpr = __raw_readl(psc_base + EPCPR); > } while ((((epcpr >> domain) & 1) == 0)); > > - pdctl1 = __raw_readl(psc_base + PDCTL1); > - pdctl1 |= 0x100; > - __raw_writel(pdctl1, psc_base + PDCTL1); > + pdctl = __raw_readl(psc_base + PDCTL + 4 * domain); > + pdctl |= 0x100; And used PDCTL_EPCGOOD good here. Thanks, Sekhar From nsekhar at ti.com Mon Nov 14 14:28:47 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 14 Nov 2011 20:28:47 +0000 Subject: [PATCH 2/2 - v3] ARM: davinci: enhancement to support multiple power domains In-Reply-To: <1316813173-31270-2-git-send-email-m-karicheri2@ti.com> References: <1316813173-31270-1-git-send-email-m-karicheri2@ti.com> <1316813173-31270-2-git-send-email-m-karicheri2@ti.com> Message-ID: On Sat, Sep 24, 2011 at 02:56:13, Karicheri, Muralidharan wrote: > From: Murali Karicheri > > In one of the new SoC that we are working on, there are multiple power > domains similar to that in C6670. Currently clock module assumes that > there are only two power domains (domain0 and domain1). This patch is > added to enhance the code to support more than two power domains so as > to allow porting of Linux on to the above SoC. > > Reviewed-by :Sergei Shtylyov > > Signed-off-by: Murali Karicheri > --- > diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c > index 1e0f809..c110c1f 100644 > --- a/arch/arm/mach-davinci/dm646x.c > +++ b/arch/arm/mach-davinci/dm646x.c > @@ -157,7 +157,7 @@ static struct clk dsp_clk = { > .name = "dsp", > .parent = &pll1_sysclk1, > .lpsc = DM646X_LPSC_C64X_CPU, > - .flags = PSC_DSP, > + .domain = DAVINCI_GPSC_DSPDOMAIN, > .usecount = 1, /* REVISIT how to disable? */ > }; I found that this DM6467 clock definition is not as per SoC documentation so fixed it in a separate patch. Thanks, Sekhar From sshtylyov at mvista.com Tue Nov 15 04:03:14 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Tue, 15 Nov 2011 14:03:14 +0400 Subject: [PATCH v2 4/5] davinci: create new common platform header for davinci In-Reply-To: <1321283357-27698-5-git-send-email-manjunath.hadli@ti.com> References: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> <1321283357-27698-5-git-send-email-manjunath.hadli@ti.com> Message-ID: <4EC238E2.3040600@mvista.com> Hello. On 14-11-2011 19:09, Manjunath Hadli wrote: > remove the code from individual platform header files for > dm365, dm355, dm644x and dm646x and consolidate it into a > single and common header file davinci_common.h. > Include the new header file in individual platform header > files as a pre-cursor for deleting these headers in follow > up patches. > Signed-off-by: Manjunath Hadli > --- > .../arm/mach-davinci/include/mach/davinci_common.h | 88 ++++++++++++++++++++ > arch/arm/mach-davinci/include/mach/dm355.h | 18 +---- > arch/arm/mach-davinci/include/mach/dm365.h | 20 +---- > arch/arm/mach-davinci/include/mach/dm644x.h | 15 +--- > arch/arm/mach-davinci/include/mach/dm646x.h | 20 +---- > 5 files changed, 92 insertions(+), 69 deletions(-) > create mode 100644 arch/arm/mach-davinci/include/mach/davinci_common.h > diff --git a/arch/arm/mach-davinci/include/mach/davinci_common.h b/arch/arm/mach-davinci/include/mach/davinci_common.h > new file mode 100644 > index 0000000..a859318 > --- /dev/null > +++ b/arch/arm/mach-davinci/include/mach/davinci_common.h Why not call it just davinci.h? WBR, Sergei From sshtylyov at mvista.com Tue Nov 15 04:40:16 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Tue, 15 Nov 2011 14:40:16 +0400 Subject: [PATCH v2 1/5] davinci: dm644x: remove the macros from the header to move to c file In-Reply-To: <1321283357-27698-2-git-send-email-manjunath.hadli@ti.com> References: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> <1321283357-27698-2-git-send-email-manjunath.hadli@ti.com> Message-ID: <4EC24190.9010005@mvista.com> Hello. On 14-11-2011 19:09, Manjunath Hadli wrote: > move the register base addresses and offsets used only by dm644x > platform file from platform header dm644x.h to dm644x.c as they > are used only in the c file. > Signed-off-by: Manjunath Hadli > --- > arch/arm/mach-davinci/dm644x.c | 6 ++++++ > arch/arm/mach-davinci/include/mach/dm644x.h | 7 ------- > 2 files changed, 6 insertions(+), 7 deletions(-) > diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c > index 3470983..1b4b911 100644 > --- a/arch/arm/mach-davinci/dm644x.c > +++ b/arch/arm/mach-davinci/dm644x.c > @@ -34,6 +34,12 @@ > * Device specific clocks > */ > #define DM644X_REF_FREQ 27000000 Add an empty line here please. > +#define DM644X_EMAC_BASE 0x01c80000 > +#define DM644X_EMAC_MDIO_BASE (DM644X_EMAC_BASE + 0x4000) > +#define DM644X_EMAC_CNTRL_OFFSET 0x0000 > +#define DM644X_EMAC_CNTRL_MOD_OFFSET 0x1000 > +#define DM644X_EMAC_CNTRL_RAM_OFFSET 0x2000 > +#define DM644X_EMAC_CNTRL_RAM_SIZE 0x2000 > > static struct pll_data pll1_data = { > .num = 1, WBR, Sergei From sshtylyov at mvista.com Tue Nov 15 04:54:49 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Tue, 15 Nov 2011 14:54:49 +0400 Subject: [PATCH v2 5/5] davinci: delete individual platform header files and use a common header In-Reply-To: <1321283357-27698-6-git-send-email-manjunath.hadli@ti.com> References: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> <1321283357-27698-6-git-send-email-manjunath.hadli@ti.com> Message-ID: <4EC244F9.1030604@mvista.com> Hello. On 14-11-2011 19:09, Manjunath Hadli wrote: > include davinci_common.h file in files using the platform > header file for dm355, dm365, dm644x and dm646x and delete the > individual platform header files. > Signed-off-by: Manjunath Hadli [...] > diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h > index 25036cb..73b00bd 100644 > --- a/drivers/media/video/davinci/vpif.h > +++ b/drivers/media/video/davinci/vpif.h > @@ -18,8 +18,7 @@ > > #include > #include > -#include Why are you removing this? > -#include > +#include > #include > > /* Maximum channel allowed */ > diff --git a/drivers/media/video/davinci/vpif_capture.h b/drivers/media/video/davinci/vpif_capture.h > index a693d4e..c019d26 100644 > --- a/drivers/media/video/davinci/vpif_capture.h > +++ b/drivers/media/video/davinci/vpif_capture.h > @@ -27,6 +27,7 @@ > #include > #include > #include > +#include Not clear why are you adding this when no platform header was included before. WBR, Sergei From prakash.pm at ti.com Tue Nov 15 06:01:19 2011 From: prakash.pm at ti.com (Manjunathappa, Prakash) Date: Tue, 15 Nov 2011 17:31:19 +0530 Subject: [PATCH] video:da8xx-fb: Add 24bpp LCD configuration support Message-ID: <1321358479-20390-1-git-send-email-prakash.pm@ti.com> LCD controller on am335x supports 24bpp raster configuration in addition to ones on da850. LCDC also supports 24bpp in unpacked format having ARGB:8888 32bpp format data in DDR, but it doesn't interpret Alpha component of the data. Signed-off-by: Manjunathappa, Prakash --- drivers/video/da8xx-fb.c | 57 +++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 56 insertions(+), 1 deletions(-) diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c index 55f91d9..e111971 100644 --- a/drivers/video/da8xx-fb.c +++ b/drivers/video/da8xx-fb.c @@ -82,6 +82,8 @@ #define LCD_V2_LIDD_CLK_EN BIT(1) #define LCD_V2_CORE_CLK_EN BIT(0) #define LCD_V2_LPP_B10 26 +#define LCD_V2_TFT_24BPP_MODE BIT(25) +#define LCD_V2_TFT_24BPP_UNPACK BIT(26) /* LCD Raster Timing 2 Register */ #define LCD_AC_BIAS_TRANSITIONS_PER_INT(x) ((x) << 16) @@ -151,7 +153,7 @@ struct da8xx_fb_par { unsigned int dma_end; struct clk *lcdc_clk; int irq; - unsigned short pseudo_palette[16]; + unsigned short pseudo_palette[32]; unsigned int palette_sz; unsigned int pxl_clk; int blank; @@ -458,6 +460,9 @@ static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, { u32 reg; + if ((bpp > 16) && (lcd_revision == LCD_VERSION_1)) + return -EINVAL; + /* Set the Panel Width */ /* Pixels per line = (PPL + 1)*16 */ if (lcd_revision == LCD_VERSION_1) { @@ -501,6 +506,13 @@ static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(1 << 8); if (raster_order) reg |= LCD_RASTER_ORDER; + + if (bpp == 24) + reg |= (LCD_TFT_MODE | LCD_V2_TFT_24BPP_MODE); + else if (bpp == 32) + reg |= (LCD_TFT_MODE | LCD_V2_TFT_24BPP_MODE + | LCD_V2_TFT_24BPP_UNPACK); + lcdc_write(reg, LCD_RASTER_CTRL_REG); switch (bpp) { @@ -508,6 +520,8 @@ static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, case 2: case 4: case 16: + case 24: + case 32: par->palette_sz = 16 * 2; break; @@ -537,6 +551,9 @@ static int fb_setcolreg(unsigned regno, unsigned red, unsigned green, if (info->fix.visual == FB_VISUAL_DIRECTCOLOR) return 1; + if ((info->var.bits_per_pixel > 16) && (lcd_revision == LCD_VERSION_1)) + return 1; + if (info->var.bits_per_pixel == 8) { red >>= 4; green >>= 8; @@ -566,6 +583,23 @@ static int fb_setcolreg(unsigned regno, unsigned red, unsigned green, update_hw = 1; palette[0] = 0x4000; } + } else if (((info->var.bits_per_pixel == 32) && regno < 32) || + ((info->var.bits_per_pixel == 24) && regno < 24)) { + red >>= (24 - info->var.red.length); + red <<= info->var.red.offset; + + green >>= (24 - info->var.green.length); + green <<= info->var.green.offset; + + blue >>= (24 - info->var.blue.length); + blue <<= info->var.blue.offset; + + par->pseudo_palette[regno] = red | green | blue; + + if (palette[0] != 0x4000) { + update_hw = 1; + palette[0] = 0x4000; + } } /* Update the palette in the h/w as needed. */ @@ -777,6 +811,9 @@ static int fb_check_var(struct fb_var_screeninfo *var, { int err = 0; + if ((var->bits_per_pixel > 16) && (lcd_revision == LCD_VERSION_1)) + return -EINVAL; + switch (var->bits_per_pixel) { case 1: case 8: @@ -809,6 +846,24 @@ static int fb_check_var(struct fb_var_screeninfo *var, var->transp.offset = 0; var->transp.length = 0; break; + case 24: + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + break; + case 32: + var->transp.offset = 24; + var->transp.length = 8; + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + break; default: err = -EINVAL; } -- 1.7.1 From prakash.pm at ti.com Tue Nov 15 06:02:23 2011 From: prakash.pm at ti.com (Manjunathappa, Prakash) Date: Tue, 15 Nov 2011 17:32:23 +0530 Subject: [PATCH] video:da8xx-fb: Disable and reset sequence on version2 of LCDC Message-ID: <1321358543-23124-1-git-send-email-prakash.pm@ti.com> Patch follows the disable and software reset sequence specified in version2 to LCDC functional specification. Without this flicker is observed on re-enabling the LCDC. Signed-off-by: Manjunathappa, Prakash --- drivers/video/da8xx-fb.c | 15 ++++++++++++++- 1 files changed, 14 insertions(+), 1 deletions(-) diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c index e111971..6b27751 100644 --- a/drivers/video/da8xx-fb.c +++ b/drivers/video/da8xx-fb.c @@ -118,6 +118,7 @@ /* Clock registers available only on Version 2 */ #define LCD_CLK_ENABLE_REG 0x6c #define LCD_CLK_RESET_REG 0x70 +#define LCD_CLK_MAIN_RESET BIT(3) #define LCD_NUM_BUFFERS 2 @@ -246,6 +247,10 @@ static inline void lcd_enable_raster(void) { u32 reg; + /* Bring LCDC out of reset */ + if (lcd_revision == LCD_VERSION_2) + lcdc_write(0, LCD_CLK_RESET_REG); + reg = lcdc_read(LCD_RASTER_CTRL_REG); if (!(reg & LCD_RASTER_ENABLE)) lcdc_write(reg | LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); @@ -259,6 +264,10 @@ static inline void lcd_disable_raster(void) reg = lcdc_read(LCD_RASTER_CTRL_REG); if (reg & LCD_RASTER_ENABLE) lcdc_write(reg & ~LCD_RASTER_ENABLE, LCD_RASTER_CTRL_REG); + + if (lcd_revision == LCD_VERSION_2) + /* Write 1 to reset LCDC */ + lcdc_write(LCD_CLK_MAIN_RESET, LCD_CLK_RESET_REG); } static void lcd_blit(int load_mode, struct da8xx_fb_par *par) @@ -618,8 +627,12 @@ static void lcd_reset(struct da8xx_fb_par *par) lcdc_write(0, LCD_DMA_CTRL_REG); lcdc_write(0, LCD_RASTER_CTRL_REG); - if (lcd_revision == LCD_VERSION_2) + if (lcd_revision == LCD_VERSION_2) { lcdc_write(0, LCD_INT_ENABLE_SET_REG); + /* Write 1 to reset */ + lcdc_write(LCD_CLK_MAIN_RESET, LCD_CLK_RESET_REG); + lcdc_write(0, LCD_CLK_RESET_REG); + } } static void lcd_calc_clk_divider(struct da8xx_fb_par *par) -- 1.7.1 From sshtylyov at mvista.com Tue Nov 15 06:18:45 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Tue, 15 Nov 2011 16:18:45 +0400 Subject: [PATCH v4 2/4] ARM: davinci: psc: fix incorrect offsets In-Reply-To: <1321301529-7582-2-git-send-email-nsekhar@ti.com> References: <1321301529-7582-1-git-send-email-nsekhar@ti.com> <1321301529-7582-2-git-send-email-nsekhar@ti.com> Message-ID: <4EC258A5.3030207@mvista.com> Hello. On 15-11-2011 0:12, Sekhar Nori wrote: > From: Murali Karicheri > Seperate PDSTAT and PDCTL registers are defined for > domain 0 and domain 1 where as the code always reads > the domain 0 PDSTAT register and domain 1 PDCTL register. > Fix this issue. While at it, introduce usage of macros > for register masks to improve readability. > Reviewed-by: Sergei Shtylyov > Signed-off-by: Murali Karicheri > Signed-off-by: Sekhar Nori > --- > arch/arm/mach-davinci/include/mach/psc.h | 4 +++- > arch/arm/mach-davinci/psc.c | 16 ++++++++-------- > 2 files changed, 11 insertions(+), 9 deletions(-) > diff --git a/arch/arm/mach-davinci/include/mach/psc.h b/arch/arm/mach-davinci/include/mach/psc.h > index 8bf279a..8bc3fc2 100644 > --- a/arch/arm/mach-davinci/include/mach/psc.h > +++ b/arch/arm/mach-davinci/include/mach/psc.h [...] > @@ -246,6 +246,8 @@ > #define MDSTAT_STATE_MASK 0x3f > #define PDSTAT_STATE_MASK 0x1f > #define MDCTL_FORCE BIT(31) > +#define PDCTL_NEXT BIT(1) BIT(0) actually. WBR, Sergei From manjunath.hadli at ti.com Tue Nov 15 07:55:50 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Tue, 15 Nov 2011 13:55:50 +0000 Subject: [PATCH v2 4/5] davinci: create new common platform header for davinci In-Reply-To: <4EC238E2.3040600@mvista.com> References: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> <1321283357-27698-5-git-send-email-manjunath.hadli@ti.com> <4EC238E2.3040600@mvista.com> Message-ID: Sergei, On Tue, Nov 15, 2011 at 15:33:14, Sergei Shtylyov wrote: > Hello. > > On 14-11-2011 19:09, Manjunath Hadli wrote: > > > remove the code from individual platform header files for dm365, > > dm355, dm644x and dm646x and consolidate it into a single and common > > header file davinci_common.h. > > Include the new header file in individual platform header files as a > > pre-cursor for deleting these headers in follow up patches. > > > Signed-off-by: Manjunath Hadli > > --- > > .../arm/mach-davinci/include/mach/davinci_common.h | 88 ++++++++++++++++++++ > > arch/arm/mach-davinci/include/mach/dm355.h | 18 +---- > > arch/arm/mach-davinci/include/mach/dm365.h | 20 +---- > > arch/arm/mach-davinci/include/mach/dm644x.h | 15 +--- > > arch/arm/mach-davinci/include/mach/dm646x.h | 20 +---- > > 5 files changed, 92 insertions(+), 69 deletions(-) > > create mode 100644 > > arch/arm/mach-davinci/include/mach/davinci_common.h > > > diff --git a/arch/arm/mach-davinci/include/mach/davinci_common.h > > b/arch/arm/mach-davinci/include/mach/davinci_common.h > > new file mode 100644 > > index 0000000..a859318 > > --- /dev/null > > +++ b/arch/arm/mach-davinci/include/mach/davinci_common.h > > Why not call it just davinci.h? Ok. Regards, --Manju > > WBR, Sergei > From manjunath.hadli at ti.com Tue Nov 15 08:16:38 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Tue, 15 Nov 2011 14:16:38 +0000 Subject: [PATCH v2 5/5] davinci: delete individual platform header files and use a common header In-Reply-To: <4EC244F9.1030604@mvista.com> References: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> <1321283357-27698-6-git-send-email-manjunath.hadli@ti.com> <4EC244F9.1030604@mvista.com> Message-ID: Sergei, On Tue, Nov 15, 2011 at 16:24:49, Sergei Shtylyov wrote: > Hello. > > On 14-11-2011 19:09, Manjunath Hadli wrote: > > > include davinci_common.h file in files using the platform header file > > for dm355, dm365, dm644x and dm646x and delete the individual platform > > header files. > > > Signed-off-by: Manjunath Hadli > [...] > > > diff --git a/drivers/media/video/davinci/vpif.h > > b/drivers/media/video/davinci/vpif.h > > index 25036cb..73b00bd 100644 > > --- a/drivers/media/video/davinci/vpif.h > > +++ b/drivers/media/video/davinci/vpif.h > > @@ -18,8 +18,7 @@ > > > > #include > > #include > > -#include > > Why are you removing this? It currently builds without the hardware.h. Perhaps it is not using any of the defines. > > > -#include > > +#include > > #include > > > > /* Maximum channel allowed */ > > diff --git a/drivers/media/video/davinci/vpif_capture.h > > b/drivers/media/video/davinci/vpif_capture.h > > index a693d4e..c019d26 100644 > > --- a/drivers/media/video/davinci/vpif_capture.h > > +++ b/drivers/media/video/davinci/vpif_capture.h > > @@ -27,6 +27,7 @@ > > #include > > #include > > #include > > +#include > > Not clear why are you adding this when no platform header was included before. There was an inner inclusion of i2c.h. Will correct it for i2c.h only and remove davinci_common.h > > WBR, Sergei > > Thx, -Manju From manjunath.hadli at ti.com Tue Nov 15 08:17:38 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Tue, 15 Nov 2011 14:17:38 +0000 Subject: [PATCH v2 1/5] davinci: dm644x: remove the macros from the header to move to c file In-Reply-To: <4EC24190.9010005@mvista.com> References: <1321283357-27698-1-git-send-email-manjunath.hadli@ti.com> <1321283357-27698-2-git-send-email-manjunath.hadli@ti.com> <4EC24190.9010005@mvista.com> Message-ID: Sergei, On Tue, Nov 15, 2011 at 16:10:16, Sergei Shtylyov wrote: > Hello. > > On 14-11-2011 19:09, Manjunath Hadli wrote: > > > move the register base addresses and offsets used only by dm644x > > platform file from platform header dm644x.h to dm644x.c as they are > > used only in the c file. > > > Signed-off-by: Manjunath Hadli > > --- > > arch/arm/mach-davinci/dm644x.c | 6 ++++++ > > arch/arm/mach-davinci/include/mach/dm644x.h | 7 ------- > > 2 files changed, 6 insertions(+), 7 deletions(-) > > > diff --git a/arch/arm/mach-davinci/dm644x.c > > b/arch/arm/mach-davinci/dm644x.c index 3470983..1b4b911 100644 > > --- a/arch/arm/mach-davinci/dm644x.c > > +++ b/arch/arm/mach-davinci/dm644x.c > > @@ -34,6 +34,12 @@ > > * Device specific clocks > > */ > > #define DM644X_REF_FREQ 27000000 > > Add an empty line here please. Ok. Regards, --Manju > > > +#define DM644X_EMAC_BASE 0x01c80000 > > +#define DM644X_EMAC_MDIO_BASE (DM644X_EMAC_BASE + 0x4000) > > +#define DM644X_EMAC_CNTRL_OFFSET 0x0000 > > +#define DM644X_EMAC_CNTRL_MOD_OFFSET 0x1000 > > +#define DM644X_EMAC_CNTRL_RAM_OFFSET 0x2000 > > +#define DM644X_EMAC_CNTRL_RAM_SIZE 0x2000 > > > > static struct pll_data pll1_data = { > > .num = 1, > > WBR, Sergei > > From linux at arm.linux.org.uk Tue Nov 15 10:54:08 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Tue, 15 Nov 2011 16:54:08 +0000 Subject: [PATCH] ARM: restart: davinci: use new restart hook In-Reply-To: <1321296775-5110-1-git-send-email-nsekhar@ti.com> References: <1321296775-5110-1-git-send-email-nsekhar@ti.com> Message-ID: <20111115165408.GA9581@n2100.arm.linux.org.uk> On Tue, Nov 15, 2011 at 12:22:55AM +0530, Sekhar Nori wrote: > Rather than using davinci specifci davinci_soc_info based > restart hook, use the existing arm_pm_restart pointer > instead. Thanks for looking at this. Although you're using arm_pm_restart directly, I'd much prefer to use the .restart method in the machine description of at all possible. One of the problems if initializing arm_pm_restart in various functions is that unless you're familiar with the order in which these functions are called, you don't know what's being used. With the machine description holding it, it's obvious which platform should be using what method. Any chance this patch could be reworked along those lines? Thanks. From nsekhar at ti.com Wed Nov 16 05:31:08 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 16 Nov 2011 11:31:08 +0000 Subject: [PATCH] ARM: restart: davinci: use new restart hook In-Reply-To: <20111115165408.GA9581@n2100.arm.linux.org.uk> References: <1321296775-5110-1-git-send-email-nsekhar@ti.com> <20111115165408.GA9581@n2100.arm.linux.org.uk> Message-ID: Hi Russell, On Tue, Nov 15, 2011 at 22:24:08, Russell King - ARM Linux wrote: > On Tue, Nov 15, 2011 at 12:22:55AM +0530, Sekhar Nori wrote: > > Rather than using davinci specifci davinci_soc_info based > > restart hook, use the existing arm_pm_restart pointer > > instead. > > Thanks for looking at this. > > Although you're using arm_pm_restart directly, I'd much prefer to use > the .restart method in the machine description of at all possible. > > One of the problems if initializing arm_pm_restart in various functions > is that unless you're familiar with the order in which these functions > are called, you don't know what's being used. With the machine > description holding it, it's obvious which platform should be using > what method. > > Any chance this patch could be reworked along those lines? I fully agree with this. Currently there is only one local header file clock.h in mach-davinci. I need to work on making common.h local before this can be done. That breaks the GPIO and USB driver build. So it's a little bit of work to get there. It will take me 1-2 weeks. Also, I thought abusing clock.h for this purpose is worse than using arm_pm_restart. Thanks, Sekhar From manjunath.hadli at ti.com Thu Nov 17 04:18:57 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 15:48:57 +0530 Subject: [PATCH v3 4/5] ARM: davinci: create new common platform header for davinci In-Reply-To: <1321525138-3928-1-git-send-email-manjunath.hadli@ti.com> References: <1321525138-3928-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321525138-3928-5-git-send-email-manjunath.hadli@ti.com> remove the code from individual platform header files for dm365, dm355, dm644x and dm646x and consolidate it into a single and common header file davinci_common.h. Include the new header file in individual platform header files as a pre-cursor for deleting these headers in follow up patches. Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/include/mach/davinci.h | 88 ++++++++++++++++++++++++++ arch/arm/mach-davinci/include/mach/dm355.h | 18 +----- arch/arm/mach-davinci/include/mach/dm365.h | 20 +------ arch/arm/mach-davinci/include/mach/dm644x.h | 15 +---- arch/arm/mach-davinci/include/mach/dm646x.h | 20 +------ 5 files changed, 92 insertions(+), 69 deletions(-) create mode 100644 arch/arm/mach-davinci/include/mach/davinci.h diff --git a/arch/arm/mach-davinci/include/mach/davinci.h b/arch/arm/mach-davinci/include/mach/davinci.h new file mode 100644 index 0000000..49bf2f3 --- /dev/null +++ b/arch/arm/mach-davinci/include/mach/davinci.h @@ -0,0 +1,88 @@ +/* + * This file contains the processor specific definitions + * of the TI DM644x, DM355, DM365, and DM646X. + * + * Copyright (C) 2011 Texas Instruments Incorporated + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef __DAVINCI_H +#define __DAVINCI_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* DM355 base addresses */ +#define DM355_ASYNC_EMIF_CONTROL_BASE 0x01e10000 +#define DM355_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 + +#define ASP1_TX_EVT_EN 1 +#define ASP1_RX_EVT_EN 2 + +/* DM365 base addresses */ +#define DM365_ASYNC_EMIF_CONTROL_BASE 0x01d10000 +#define DM365_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 +#define DM365_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 + +/* DM644x base addresses */ +#define DM644X_ASYNC_EMIF_CONTROL_BASE 0x01e00000 +#define DM644X_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 +#define DM644X_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 +#define DM644X_ASYNC_EMIF_DATA_CE2_BASE 0x06000000 +#define DM644X_ASYNC_EMIF_DATA_CE3_BASE 0x08000000 + +/* DM646x base addresses */ +#define DM646X_ASYNC_EMIF_CONTROL_BASE 0x20008000 +#define DM646X_ASYNC_EMIF_CS2_SPACE_BASE 0x42000000 + +/* DM355 function declarations */ +struct spi_board_info; + +void __init dm355_init(void); +void dm355_init_spi0(unsigned chipselect_mask, + struct spi_board_info *info, unsigned len); +void __init dm355_init_asp1(u32 evt_enable, struct snd_platform_data *pdata); +void dm355_set_vpfe_config(struct vpfe_config *cfg); + +/* DM365 function declarations */ +void __init dm365_init(void); +void __init dm365_init_asp(struct snd_platform_data *pdata); +void __init dm365_init_vc(struct snd_platform_data *pdata); +void __init dm365_init_ks(struct davinci_ks_platform_data *pdata); +void __init dm365_init_rtc(void); +void dm365_init_spi0(unsigned chipselect_mask, + struct spi_board_info *info, unsigned len); + +void dm365_set_vpfe_config(struct vpfe_config *cfg); + +/* DM644X function declarations */ +void __init dm644x_init(void); +void __init dm644x_init_asp(struct snd_platform_data *pdata); +void dm644x_set_vpfe_config(struct vpfe_config *cfg); + +/* DM646X function declarations */ +void __init dm646x_init(void); +void __init dm646x_init_mcasp0(struct snd_platform_data *pdata); +void __init dm646x_init_mcasp1(struct snd_platform_data *pdata); +void __init dm646x_board_setup_refclk(struct clk *clk); +int __init dm646x_init_edma(struct edma_rsv_info *rsv); + +void dm646x_video_init(void); + +void dm646x_setup_vpif(struct vpif_display_config *, + struct vpif_capture_config *); +#endif /*__DAVINCI_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm355.h b/arch/arm/mach-davinci/include/mach/dm355.h index 36dff4a..14e7766 100644 --- a/arch/arm/mach-davinci/include/mach/dm355.h +++ b/arch/arm/mach-davinci/include/mach/dm355.h @@ -11,22 +11,6 @@ #ifndef __ASM_ARCH_DM355_H #define __ASM_ARCH_DM355_H -#include -#include -#include - -#define DM355_ASYNC_EMIF_CONTROL_BASE 0x01E10000 -#define DM355_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 - -#define ASP1_TX_EVT_EN 1 -#define ASP1_RX_EVT_EN 2 - -struct spi_board_info; - -void __init dm355_init(void); -void dm355_init_spi0(unsigned chipselect_mask, - struct spi_board_info *info, unsigned len); -void __init dm355_init_asp1(u32 evt_enable, struct snd_platform_data *pdata); -void dm355_set_vpfe_config(struct vpfe_config *cfg); +#include #endif /* __ASM_ARCH_DM355_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm365.h b/arch/arm/mach-davinci/include/mach/dm365.h index 51924de..3433d72 100644 --- a/arch/arm/mach-davinci/include/mach/dm365.h +++ b/arch/arm/mach-davinci/include/mach/dm365.h @@ -13,24 +13,6 @@ #ifndef __ASM_ARCH_DM365_H #define __ASM_ARCH_DM665_H -#include -#include -#include -#include -#include -#include +#include -#define DM365_ASYNC_EMIF_CONTROL_BASE 0x01D10000 -#define DM365_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 -#define DM365_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 - -void __init dm365_init(void); -void __init dm365_init_asp(struct snd_platform_data *pdata); -void __init dm365_init_vc(struct snd_platform_data *pdata); -void __init dm365_init_ks(struct davinci_ks_platform_data *pdata); -void __init dm365_init_rtc(void); -void dm365_init_spi0(unsigned chipselect_mask, - struct spi_board_info *info, unsigned len); - -void dm365_set_vpfe_config(struct vpfe_config *cfg); #endif /* __ASM_ARCH_DM365_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h index 724377f..99e9525 100644 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ b/arch/arm/mach-davinci/include/mach/dm644x.h @@ -22,19 +22,6 @@ #ifndef __ASM_ARCH_DM644X_H #define __ASM_ARCH_DM644X_H -#include -#include -#include -#include - -#define DM644X_ASYNC_EMIF_CONTROL_BASE 0x01E00000 -#define DM644X_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 -#define DM644X_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 -#define DM644X_ASYNC_EMIF_DATA_CE2_BASE 0x06000000 -#define DM644X_ASYNC_EMIF_DATA_CE3_BASE 0x08000000 - -void __init dm644x_init(void); -void __init dm644x_init_asp(struct snd_platform_data *pdata); -void dm644x_set_vpfe_config(struct vpfe_config *cfg); +#include #endif /* __ASM_ARCH_DM644X_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h index eb95864..8bdea80 100644 --- a/arch/arm/mach-davinci/include/mach/dm646x.h +++ b/arch/arm/mach-davinci/include/mach/dm646x.h @@ -11,24 +11,6 @@ #ifndef __ASM_ARCH_DM646X_H #define __ASM_ARCH_DM646X_H -#include -#include -#include -#include -#include -#include - -#define DM646X_ASYNC_EMIF_CONTROL_BASE 0x20008000 -#define DM646X_ASYNC_EMIF_CS2_SPACE_BASE 0x42000000 - -void __init dm646x_init(void); -void __init dm646x_init_mcasp0(struct snd_platform_data *pdata); -void __init dm646x_init_mcasp1(struct snd_platform_data *pdata); -int __init dm646x_init_edma(struct edma_rsv_info *rsv); - -void dm646x_video_init(void); - -void dm646x_setup_vpif(struct vpif_display_config *, - struct vpif_capture_config *); +#include #endif /* __ASM_ARCH_DM646X_H */ -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:18:53 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 15:48:53 +0530 Subject: [PATCH v3 0/5] ARM: davinci: re-arrange definitions to have a common davinci header Message-ID: <1321525138-3928-1-git-send-email-manjunath.hadli@ti.com> Re-arrange definitions and remove unnecessary code so that we can have a common header for all davinci platforms. This will enable us to share defines and enable common routines to be used without polluting hardware.h. This patch set forms the base for a later set of patches for having a common system module base address (DAVINCI_SYSTEM_MODULE_BASE). Changes from previous version(As per Sergei's comments): 1. Renamed davinci_common.h to davinci.h. 2. Added extra line whereever appropriate. 3. removed unnecessary header inclusion. Manjunath Hadli (5): ARM: davinci: dm644x: remove the macros from the header to move to c file ARM: davinci: dm365: remove the macros from the header to move to c file ARM: davinci: dm646x: remove the macros from the header to move to c file ARM: davinci: create new common platform header for davinci ARM: davinci: delete individual platform header files and use a common header arch/arm/mach-davinci/board-dm355-evm.c | 2 +- arch/arm/mach-davinci/board-dm355-leopard.c | 2 +- arch/arm/mach-davinci/board-dm365-evm.c | 2 +- arch/arm/mach-davinci/board-dm644x-evm.c | 2 +- arch/arm/mach-davinci/board-dm646x-evm.c | 2 +- arch/arm/mach-davinci/board-neuros-osd2.c | 2 +- arch/arm/mach-davinci/board-sffsdr.c | 2 +- arch/arm/mach-davinci/dm355.c | 2 +- arch/arm/mach-davinci/dm365.c | 18 +++++- arch/arm/mach-davinci/dm644x.c | 9 +++- arch/arm/mach-davinci/dm646x.c | 9 +++- arch/arm/mach-davinci/include/mach/davinci.h | 88 ++++++++++++++++++++++++++ arch/arm/mach-davinci/include/mach/dm355.h | 32 --------- arch/arm/mach-davinci/include/mach/dm365.h | 52 --------------- arch/arm/mach-davinci/include/mach/dm644x.h | 47 -------------- arch/arm/mach-davinci/include/mach/dm646x.h | 41 ------------ drivers/media/video/davinci/vpif.h | 3 +- drivers/media/video/davinci/vpif_display.c | 2 +- 18 files changed, 131 insertions(+), 186 deletions(-) create mode 100644 arch/arm/mach-davinci/include/mach/davinci.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm355.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm365.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm644x.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm646x.h From manjunath.hadli at ti.com Thu Nov 17 04:18:58 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 15:48:58 +0530 Subject: [PATCH v3 5/5] ARM: davinci: delete individual platform header files and use a common header In-Reply-To: <1321525138-3928-1-git-send-email-manjunath.hadli@ti.com> References: <1321525138-3928-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321525138-3928-6-git-send-email-manjunath.hadli@ti.com> include davinci_common.h file in files using the platform header file for dm355, dm365, dm644x and dm646x and delete the individual platform header files. Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/board-dm355-evm.c | 2 +- arch/arm/mach-davinci/board-dm355-leopard.c | 2 +- arch/arm/mach-davinci/board-dm365-evm.c | 2 +- arch/arm/mach-davinci/board-dm644x-evm.c | 2 +- arch/arm/mach-davinci/board-dm646x-evm.c | 2 +- arch/arm/mach-davinci/board-neuros-osd2.c | 2 +- arch/arm/mach-davinci/board-sffsdr.c | 2 +- arch/arm/mach-davinci/dm355.c | 2 +- arch/arm/mach-davinci/dm365.c | 2 +- arch/arm/mach-davinci/dm644x.c | 2 +- arch/arm/mach-davinci/dm646x.c | 2 +- arch/arm/mach-davinci/include/mach/dm355.h | 16 ---------------- arch/arm/mach-davinci/include/mach/dm365.h | 18 ------------------ arch/arm/mach-davinci/include/mach/dm644x.h | 27 --------------------------- arch/arm/mach-davinci/include/mach/dm646x.h | 16 ---------------- drivers/media/video/davinci/vpif.h | 3 +-- drivers/media/video/davinci/vpif_display.c | 2 +- 17 files changed, 13 insertions(+), 91 deletions(-) delete mode 100644 arch/arm/mach-davinci/include/mach/dm355.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm365.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm644x.h delete mode 100644 arch/arm/mach-davinci/include/mach/dm646x.h diff --git a/arch/arm/mach-davinci/board-dm355-evm.c b/arch/arm/mach-davinci/board-dm355-evm.c index 4e0e707..7b98160 100644 --- a/arch/arm/mach-davinci/board-dm355-evm.c +++ b/arch/arm/mach-davinci/board-dm355-evm.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-dm355-leopard.c b/arch/arm/mach-davinci/board-dm355-leopard.c index ff2d241..b4f9558 100644 --- a/arch/arm/mach-davinci/board-dm355-leopard.c +++ b/arch/arm/mach-davinci/board-dm355-leopard.c @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c index 1918ae7..7aec2ab 100644 --- a/arch/arm/mach-davinci/board-dm365-evm.c +++ b/arch/arm/mach-davinci/board-dm365-evm.c @@ -32,7 +32,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 0cf8abf..880aa0c 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index e574d7f..383acdf 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -36,7 +36,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-neuros-osd2.c b/arch/arm/mach-davinci/board-neuros-osd2.c index e5f231a..a4608cf 100644 --- a/arch/arm/mach-davinci/board-neuros-osd2.c +++ b/arch/arm/mach-davinci/board-neuros-osd2.c @@ -30,7 +30,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/board-sffsdr.c b/arch/arm/mach-davinci/board-sffsdr.c index 5dd4da9..b993ac6 100644 --- a/arch/arm/mach-davinci/board-sffsdr.c +++ b/arch/arm/mach-davinci/board-sffsdr.c @@ -35,7 +35,7 @@ #include #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index fe520d4..2e29ed0 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -18,7 +18,7 @@ #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 77edee8..6b8ce8c 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -21,7 +21,7 @@ #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 27d1371..401f916 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -15,7 +15,7 @@ #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 0560e82..850ad3c 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -16,7 +16,7 @@ #include -#include +#include #include #include #include diff --git a/arch/arm/mach-davinci/include/mach/dm355.h b/arch/arm/mach-davinci/include/mach/dm355.h deleted file mode 100644 index 14e7766..0000000 --- a/arch/arm/mach-davinci/include/mach/dm355.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Chip specific defines for DM355 SoC - * - * Author: Kevin Hilman, Deep Root Systems, LLC - * - * 2007 (c) Deep Root Systems, LLC. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ -#ifndef __ASM_ARCH_DM355_H -#define __ASM_ARCH_DM355_H - -#include - -#endif /* __ASM_ARCH_DM355_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm365.h b/arch/arm/mach-davinci/include/mach/dm365.h deleted file mode 100644 index 3433d72..0000000 --- a/arch/arm/mach-davinci/include/mach/dm365.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (C) 2009 Texas Instruments Incorporated - * - * 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 version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ -#ifndef __ASM_ARCH_DM365_H -#define __ASM_ARCH_DM665_H - -#include - -#endif /* __ASM_ARCH_DM365_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h deleted file mode 100644 index 99e9525..0000000 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * This file contains the processor specific definitions - * of the TI DM644x. - * - * Copyright (C) 2008 Texas Instruments. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __ASM_ARCH_DM644X_H -#define __ASM_ARCH_DM644X_H - -#include - -#endif /* __ASM_ARCH_DM644X_H */ diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h deleted file mode 100644 index 8bdea80..0000000 --- a/arch/arm/mach-davinci/include/mach/dm646x.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Chip specific defines for DM646x SoC - * - * Author: Kevin Hilman, Deep Root Systems, LLC - * - * 2007 (c) Deep Root Systems, LLC. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - */ -#ifndef __ASM_ARCH_DM646X_H -#define __ASM_ARCH_DM646X_H - -#include - -#endif /* __ASM_ARCH_DM646X_H */ diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h index 25036cb..1e0fef9 100644 --- a/drivers/media/video/davinci/vpif.h +++ b/drivers/media/video/davinci/vpif.h @@ -18,8 +18,7 @@ #include #include -#include -#include +#include #include /* Maximum channel allowed */ diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 286f029..4766d41 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -39,7 +39,7 @@ #include #include -#include +#include #include "vpif_display.h" #include "vpif.h" -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:18:56 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 15:48:56 +0530 Subject: [PATCH v3 3/5] ARM: davinci: dm646x: remove the macros from the header to move to c file In-Reply-To: <1321525138-3928-1-git-send-email-manjunath.hadli@ti.com> References: <1321525138-3928-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321525138-3928-4-git-send-email-manjunath.hadli@ti.com> move the register base addresses and offsets used only by dm646x platform file from platform header dm646x.h to dm646x.c as they are used only in the c file. Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/dm646x.c | 7 +++++++ arch/arm/mach-davinci/include/mach/dm646x.h | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 0b68ed5..0560e82 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -46,6 +46,13 @@ #define DM646X_REF_FREQ 27000000 #define DM646X_AUX_FREQ 24000000 +#define DM646X_EMAC_BASE 0x01c80000 +#define DM646X_EMAC_MDIO_BASE (DM646X_EMAC_BASE + 0x4000) +#define DM646X_EMAC_CNTRL_OFFSET 0x0000 +#define DM646X_EMAC_CNTRL_MOD_OFFSET 0x1000 +#define DM646X_EMAC_CNTRL_RAM_OFFSET 0x2000 +#define DM646X_EMAC_CNTRL_RAM_SIZE 0x2000 + static struct pll_data pll1_data = { .num = 1, .phys_base = DAVINCI_PLL1_BASE, diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h index a8ee6c9..eb95864 100644 --- a/arch/arm/mach-davinci/include/mach/dm646x.h +++ b/arch/arm/mach-davinci/include/mach/dm646x.h @@ -18,13 +18,6 @@ #include #include -#define DM646X_EMAC_BASE (0x01C80000) -#define DM646X_EMAC_MDIO_BASE (DM646X_EMAC_BASE + 0x4000) -#define DM646X_EMAC_CNTRL_OFFSET (0x0000) -#define DM646X_EMAC_CNTRL_MOD_OFFSET (0x1000) -#define DM646X_EMAC_CNTRL_RAM_OFFSET (0x2000) -#define DM646X_EMAC_CNTRL_RAM_SIZE (0x2000) - #define DM646X_ASYNC_EMIF_CONTROL_BASE 0x20008000 #define DM646X_ASYNC_EMIF_CS2_SPACE_BASE 0x42000000 -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:18:54 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 15:48:54 +0530 Subject: [PATCH v3 1/5] ARM: davinci: dm644x: remove the macros from the header to move to c file In-Reply-To: <1321525138-3928-1-git-send-email-manjunath.hadli@ti.com> References: <1321525138-3928-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321525138-3928-2-git-send-email-manjunath.hadli@ti.com> move the register base addresses and offsets used only by dm644x platform file from platform header dm644x.h to dm644x.c as they are used only in the c file. Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/dm644x.c | 7 +++++++ arch/arm/mach-davinci/include/mach/dm644x.h | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 3470983..27d1371 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -35,6 +35,13 @@ */ #define DM644X_REF_FREQ 27000000 +#define DM644X_EMAC_BASE 0x01c80000 +#define DM644X_EMAC_MDIO_BASE (DM644X_EMAC_BASE + 0x4000) +#define DM644X_EMAC_CNTRL_OFFSET 0x0000 +#define DM644X_EMAC_CNTRL_MOD_OFFSET 0x1000 +#define DM644X_EMAC_CNTRL_RAM_OFFSET 0x2000 +#define DM644X_EMAC_CNTRL_RAM_SIZE 0x2000 + static struct pll_data pll1_data = { .num = 1, .phys_base = DAVINCI_PLL1_BASE, diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h index 5a1b26d..724377f 100644 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ b/arch/arm/mach-davinci/include/mach/dm644x.h @@ -27,13 +27,6 @@ #include #include -#define DM644X_EMAC_BASE (0x01C80000) -#define DM644X_EMAC_MDIO_BASE (DM644X_EMAC_BASE + 0x4000) -#define DM644X_EMAC_CNTRL_OFFSET (0x0000) -#define DM644X_EMAC_CNTRL_MOD_OFFSET (0x1000) -#define DM644X_EMAC_CNTRL_RAM_OFFSET (0x2000) -#define DM644X_EMAC_CNTRL_RAM_SIZE (0x2000) - #define DM644X_ASYNC_EMIF_CONTROL_BASE 0x01E00000 #define DM644X_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 #define DM644X_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:18:55 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 15:48:55 +0530 Subject: [PATCH v3 2/5] ARM: davinci: dm365: remove the macros from the header to move to c file In-Reply-To: <1321525138-3928-1-git-send-email-manjunath.hadli@ti.com> References: <1321525138-3928-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321525138-3928-3-git-send-email-manjunath.hadli@ti.com> move the register base addresses and offsets used only by dm365 platform file from platform header dm365.h to dm365.c as they are used only in the c file. Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/dm365.c | 16 ++++++++++++++++ arch/arm/mach-davinci/include/mach/dm365.h | 16 ---------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 679e168..77edee8 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -40,6 +40,22 @@ #define DM365_REF_FREQ 24000000 /* 24 MHz on the DM365 EVM */ +/* Base of key scan register bank */ +#define DM365_KEYSCAN_BASE 0x01c69400 + +#define DM365_RTC_BASE 0x01c69000 + +#define DAVINCI_DM365_VC_BASE 0x01d0c000 +#define DAVINCI_DMA_VC_TX 2 +#define DAVINCI_DMA_VC_RX 3 + +#define DM365_EMAC_BASE 0x01d07000 +#define DM365_EMAC_MDIO_BASE (DM365_EMAC_BASE + 0x4000) +#define DM365_EMAC_CNTRL_OFFSET 0x0000 +#define DM365_EMAC_CNTRL_MOD_OFFSET 0x3000 +#define DM365_EMAC_CNTRL_RAM_OFFSET 0x1000 +#define DM365_EMAC_CNTRL_RAM_SIZE 0x2000 + static struct pll_data pll1_data = { .num = 1, .phys_base = DAVINCI_PLL1_BASE, diff --git a/arch/arm/mach-davinci/include/mach/dm365.h b/arch/arm/mach-davinci/include/mach/dm365.h index 2563bf4..51924de 100644 --- a/arch/arm/mach-davinci/include/mach/dm365.h +++ b/arch/arm/mach-davinci/include/mach/dm365.h @@ -20,22 +20,6 @@ #include #include -#define DM365_EMAC_BASE (0x01D07000) -#define DM365_EMAC_MDIO_BASE (DM365_EMAC_BASE + 0x4000) -#define DM365_EMAC_CNTRL_OFFSET (0x0000) -#define DM365_EMAC_CNTRL_MOD_OFFSET (0x3000) -#define DM365_EMAC_CNTRL_RAM_OFFSET (0x1000) -#define DM365_EMAC_CNTRL_RAM_SIZE (0x2000) - -/* Base of key scan register bank */ -#define DM365_KEYSCAN_BASE (0x01C69400) - -#define DM365_RTC_BASE (0x01C69000) - -#define DAVINCI_DM365_VC_BASE (0x01D0C000) -#define DAVINCI_DMA_VC_TX 2 -#define DAVINCI_DMA_VC_RX 3 - #define DM365_ASYNC_EMIF_CONTROL_BASE 0x01D10000 #define DM365_ASYNC_EMIF_DATA_CE0_BASE 0x02000000 #define DM365_ASYNC_EMIF_DATA_CE1_BASE 0x04000000 -- 1.6.2.4 From sshtylyov at mvista.com Thu Nov 17 04:35:49 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Thu, 17 Nov 2011 14:35:49 +0400 Subject: [PATCH v3 4/5] ARM: davinci: create new common platform header for davinci In-Reply-To: <1321525138-3928-5-git-send-email-manjunath.hadli@ti.com> References: <1321525138-3928-1-git-send-email-manjunath.hadli@ti.com> <1321525138-3928-5-git-send-email-manjunath.hadli@ti.com> Message-ID: <4EC4E385.7030101@mvista.com> Hello. On 17-11-2011 14:18, Manjunath Hadli wrote: > remove the code from individual platform header files for > dm365, dm355, dm644x and dm646x and consolidate it into a > single and common header file davinci_common.h. > Include the new header file in individual platform header > files as a pre-cursor for deleting these headers in follow > up patches. > Signed-off-by: Manjunath Hadli Sorry, didn't notice something in the first review... > diff --git a/arch/arm/mach-davinci/include/mach/davinci.h b/arch/arm/mach-davinci/include/mach/davinci.h > new file mode 100644 > index 0000000..49bf2f3 > --- /dev/null > +++ b/arch/arm/mach-davinci/include/mach/davinci.h > @@ -0,0 +1,88 @@ > +/* > + * This file contains the processor specific definitions > + * of the TI DM644x, DM355, DM365, and DM646X. DM646x for consistency. > +/* DM644X function declarations */ DM644x for consistency. > +/* DM646X function declarations */ DM646x for consistency. WBR, Sergei From sshtylyov at mvista.com Thu Nov 17 04:38:11 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Thu, 17 Nov 2011 14:38:11 +0400 Subject: [PATCH v3 5/5] ARM: davinci: delete individual platform header files and use a common header In-Reply-To: <1321525138-3928-6-git-send-email-manjunath.hadli@ti.com> References: <1321525138-3928-1-git-send-email-manjunath.hadli@ti.com> <1321525138-3928-6-git-send-email-manjunath.hadli@ti.com> Message-ID: <4EC4E413.3020708@mvista.com> Hello. On 17-11-2011 14:18, Manjunath Hadli wrote: > include davinci_common.h file in files using the platform Just davinci.h now. > header file for dm355, dm365, dm644x and dm646x and delete the > individual platform header files. > Signed-off-by: Manjunath Hadli > diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h > index 25036cb..1e0fef9 100644 > --- a/drivers/media/video/davinci/vpif.h > +++ b/drivers/media/video/davinci/vpif.h > @@ -18,8 +18,7 @@ > > #include > #include > -#include You need to either describe this change, or do it in a sperate patch. WBR, Sergei From manjunath.hadli at ti.com Thu Nov 17 04:44:26 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 16:14:26 +0530 Subject: [RESEND RFC PATCH v4 00/15] RFC for Media Controller capture driver for DM365 Message-ID: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> These are a updated subset of patches for Media Controller implementation on DM365. A few more implementation patches which include Resizer, Previewer , AEW and AF are added to provide a broader perspective for review. Manjunath Hadli (15): davinci: vpfe: add dm3xx IPIPEIF hardware support module davinci: vpfe: add IPIPE hardware layer support davinci: vpfe: add IPIPE support for media controller driver davinci: vpfe: add support for CCDC hardware for dm365 davinci: vpfe: add ccdc driver with media controller interface davinci: vpfe: add v4l2 video driver support davinci: vpfe: v4l2 capture driver with media interface davinci: vpfe: previewer driver based on v4l2 media controller framework davinci: vpfe: resizer driver based on media framework davinci: vpfe: add DM365 autofoucus(AF) hardware interface davinci: vpfe: add autofocus driver based on media framework davinci: vpfe: add hardware interface for dm365 aew davinci: vpfe: add aew driver based on v4l2 media framework davinci: vpfe: delete vpfe_types.h davinci: vpfe: build infrastructure for dm365 drivers/media/video/davinci/Kconfig | 46 +- drivers/media/video/davinci/Makefile | 17 +- drivers/media/video/davinci/ccdc_hw_device.h | 12 +- drivers/media/video/davinci/ccdc_types.h | 43 + drivers/media/video/davinci/dm365_a3_hw.c | 387 +++ drivers/media/video/davinci/dm365_a3_hw.h | 253 ++ drivers/media/video/davinci/dm365_aew.c | 544 ++++ drivers/media/video/davinci/dm365_aew.h | 55 + drivers/media/video/davinci/dm365_af.c | 564 ++++ drivers/media/video/davinci/dm365_af.h | 59 + drivers/media/video/davinci/dm365_ccdc.c | 1456 ++++++++++ drivers/media/video/davinci/dm365_ccdc.h | 91 + drivers/media/video/davinci/dm365_ccdc_regs.h | 309 ++ drivers/media/video/davinci/dm365_def_para.c | 310 ++ drivers/media/video/davinci/dm365_def_para.h | 39 + drivers/media/video/davinci/dm365_ipipe.c | 3844 +++++++++++++++++++++++++ drivers/media/video/davinci/dm365_ipipe.h | 378 +++ drivers/media/video/davinci/dm365_ipipe_hw.c | 935 ++++++ drivers/media/video/davinci/dm365_ipipe_hw.h | 539 ++++ drivers/media/video/davinci/dm3xx_ipipeif.c | 312 ++ drivers/media/video/davinci/dm3xx_ipipeif.h | 255 ++ drivers/media/video/davinci/imp_common.h | 86 + drivers/media/video/davinci/imp_hw_if.h | 171 ++ drivers/media/video/davinci/vpfe_aew.c | 238 ++ drivers/media/video/davinci/vpfe_aew.h | 51 + drivers/media/video/davinci/vpfe_af.c | 240 ++ drivers/media/video/davinci/vpfe_af.h | 50 + drivers/media/video/davinci/vpfe_capture.c | 796 +++++ drivers/media/video/davinci/vpfe_capture.h | 99 + drivers/media/video/davinci/vpfe_ccdc.c | 817 ++++++ drivers/media/video/davinci/vpfe_ccdc.h | 86 + drivers/media/video/davinci/vpfe_previewer.c | 1064 +++++++ drivers/media/video/davinci/vpfe_previewer.h | 65 + drivers/media/video/davinci/vpfe_resizer.c | 1079 +++++++ drivers/media/video/davinci/vpfe_resizer.h | 63 + drivers/media/video/davinci/vpfe_video.c | 1726 +++++++++++ drivers/media/video/davinci/vpfe_video.h | 146 + include/linux/dm365_aew.h | 153 + include/linux/dm365_af.h | 203 ++ include/linux/dm365_ccdc.h | 611 ++++ include/linux/dm365_ipipe.h | 1029 +++++++ include/linux/dm3xx_ipipeif.h | 64 + include/linux/imp_common.h | 171 ++ include/media/davinci/ccdc_types.h | 43 - include/media/davinci/vpfe.h | 93 + include/media/davinci/vpfe_types.h | 51 - 46 files changed, 19536 insertions(+), 107 deletions(-) create mode 100644 drivers/media/video/davinci/ccdc_types.h create mode 100644 drivers/media/video/davinci/dm365_a3_hw.c create mode 100644 drivers/media/video/davinci/dm365_a3_hw.h create mode 100644 drivers/media/video/davinci/dm365_aew.c create mode 100644 drivers/media/video/davinci/dm365_aew.h create mode 100644 drivers/media/video/davinci/dm365_af.c create mode 100644 drivers/media/video/davinci/dm365_af.h create mode 100644 drivers/media/video/davinci/dm365_ccdc.c create mode 100644 drivers/media/video/davinci/dm365_ccdc.h create mode 100644 drivers/media/video/davinci/dm365_ccdc_regs.h create mode 100644 drivers/media/video/davinci/dm365_def_para.c create mode 100644 drivers/media/video/davinci/dm365_def_para.h create mode 100644 drivers/media/video/davinci/dm365_ipipe.c create mode 100644 drivers/media/video/davinci/dm365_ipipe.h create mode 100644 drivers/media/video/davinci/dm365_ipipe_hw.c create mode 100644 drivers/media/video/davinci/dm365_ipipe_hw.h create mode 100644 drivers/media/video/davinci/dm3xx_ipipeif.c create mode 100644 drivers/media/video/davinci/dm3xx_ipipeif.h create mode 100644 drivers/media/video/davinci/imp_common.h create mode 100644 drivers/media/video/davinci/imp_hw_if.h create mode 100644 drivers/media/video/davinci/vpfe_aew.c create mode 100644 drivers/media/video/davinci/vpfe_aew.h create mode 100644 drivers/media/video/davinci/vpfe_af.c create mode 100644 drivers/media/video/davinci/vpfe_af.h create mode 100644 drivers/media/video/davinci/vpfe_capture.c create mode 100644 drivers/media/video/davinci/vpfe_capture.h create mode 100644 drivers/media/video/davinci/vpfe_ccdc.c create mode 100644 drivers/media/video/davinci/vpfe_ccdc.h create mode 100644 drivers/media/video/davinci/vpfe_previewer.c create mode 100644 drivers/media/video/davinci/vpfe_previewer.h create mode 100644 drivers/media/video/davinci/vpfe_resizer.c create mode 100644 drivers/media/video/davinci/vpfe_resizer.h create mode 100644 drivers/media/video/davinci/vpfe_video.c create mode 100644 drivers/media/video/davinci/vpfe_video.h create mode 100644 include/linux/dm365_aew.h create mode 100644 include/linux/dm365_af.h create mode 100644 include/linux/dm365_ccdc.h create mode 100644 include/linux/dm365_ipipe.h create mode 100644 include/linux/dm3xx_ipipeif.h create mode 100644 include/linux/imp_common.h delete mode 100644 include/media/davinci/ccdc_types.h create mode 100644 include/media/davinci/vpfe.h delete mode 100644 include/media/davinci/vpfe_types.h From manjunath.hadli at ti.com Thu Nov 17 04:44:27 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 16:14:27 +0530 Subject: [RESEND RFC PATCH v4 01/15] davinci: vpfe: add dm3xx IPIPEIF hardware support module In-Reply-To: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> References: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321526681-22574-2-git-send-email-manjunath.hadli@ti.com> add support for dm3xx IPIPEIF hardware setup. This is the lowest software layer for the dm3x vpfe driver which directly accesses hardware. Add support for features like default pixel correction, dark frame substraction and hardware setup. Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/dm3xx_ipipeif.c | 312 +++++++++++++++++++++++++++ drivers/media/video/davinci/dm3xx_ipipeif.h | 255 ++++++++++++++++++++++ include/linux/dm3xx_ipipeif.h | 64 ++++++ 3 files changed, 631 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/dm3xx_ipipeif.c create mode 100644 drivers/media/video/davinci/dm3xx_ipipeif.h create mode 100644 include/linux/dm3xx_ipipeif.h diff --git a/drivers/media/video/davinci/dm3xx_ipipeif.c b/drivers/media/video/davinci/dm3xx_ipipeif.c new file mode 100644 index 0000000..a3a4554 --- /dev/null +++ b/drivers/media/video/davinci/dm3xx_ipipeif.c @@ -0,0 +1,312 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* ipipe module to hold common functionality across DM355 and DM365 +*/ +#include +#include +#include +#include +#include +#include "dm3xx_ipipeif.h" + +static void *__iomem ipipeif_base_addr; + +static inline u32 regr_if(u32 offset) +{ + return readl(ipipeif_base_addr + offset); +} + +static inline void regw_if(u32 val, u32 offset) +{ + writel(val, ipipeif_base_addr + offset); +} + +void ipipeif_set_enable(char en, unsigned int mode) +{ + regw_if(1, IPIPEIF_ENABLE); +} + +u32 ipipeif_get_enable(void) +{ + return regr_if(IPIPEIF_ENABLE); +} + +int ipipeif_set_address(struct ipipeif *params, unsigned int address) +{ + u32 val; + + if (params->source == 0) + return -EINVAL; + + val = (params->adofs >> 5) & IPIPEIF_ADOFS_LSB_MASK; + regw_if(val, IPIPEIF_ADOFS); + + /* lower sixteen bit */ + val = (address >> IPIPEIF_ADDRL_SHIFT) & IPIPEIF_ADDRL_MASK; + regw_if(val, IPIPEIF_ADDRL); + + /* upper next seven bit */ + val = (address >> IPIPEIF_ADDRU_SHIFT) & IPIPEIF_ADDRU_MASK; + regw_if(val, IPIPEIF_ADDRU); + + return 0; +} + +static void ipipeif_config_dpc(struct ipipeif_dpc *dpc) +{ + u32 val = 0; + + if (dpc->en) { + val = (dpc->en & 1) << IPIPEIF_DPC2_EN_SHIFT; + val |= dpc->thr & IPIPEIF_DPC2_THR_MASK; + } + + regw_if(val, IPIPEIF_DPC2); +} + +#define RD_DATA_15_2 0x7 +/* This function sets up IPIPEIF and is called from + * ipipe_hw_setup() + */ +int ipipeif_hw_setup(struct ipipeif *params, int device_type) +{ + enum v4l2_mbus_pixelcode isif_port_if; + unsigned int val; + + if (params == NULL) + return -EINVAL; + + /* Enable clock to IPIPEIF and IPIPE */ + if (device_type == DM365) + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); + + /* Combine all the fields to make CFG1 register of IPIPEIF */ + val = params->mode << ONESHOT_SHIFT; + val |= params->source << INPSRC_SHIFT; + val |= params->clock_select << CLKSEL_SHIFT; + val |= params->avg_filter << AVGFILT_SHIFT; + val |= params->decimation << DECIM_SHIFT; + + if (device_type == DM355) { + val |= params->var.if_base.ialaw << IALAW_SHIFT; + val |= params->var.if_base.pack_mode << PACK8IN_SHIFT; + val |= params->var.if_base.clk_div << CLKDIV_SHIFT; + val |= params->var.if_base.data_shift << DATASFT_SHIFT; + } else { + /* DM365 IPIPE 5.1 */ + val |= params->var.if_5_1.pack_mode << PACK8IN_SHIFT; + val |= params->var.if_5_1.source1 << INPSRC1_SHIFT; + if (params->source != IPIPEIF_SDRAM_YUV) + val |= params->var.if_5_1.data_shift << DATASFT_SHIFT; + else + val &= ~(RD_DATA_15_2 << DATASFT_SHIFT); + } + regw_if(val, IPIPEIF_CFG1); + + switch (params->source) { + case IPIPEIF_CCDC: + regw_if(params->gain, IPIPEIF_GAIN); + break; + case IPIPEIF_SDRAM_RAW: + case IPIPEIF_CCDC_DARKFM: + regw_if(params->gain, IPIPEIF_GAIN); + /* fall through */ + case IPIPEIF_SDRAM_YUV: + val |= params->var.if_5_1.data_shift << DATASFT_SHIFT; + regw_if(params->glob_hor_size, IPIPEIF_PPLN); + regw_if(params->glob_ver_size, IPIPEIF_LPFR); + regw_if(params->hnum, IPIPEIF_HNUM); + regw_if(params->vnum, IPIPEIF_VNUM); + break; + default: + /* Do nothing */ + return -EINVAL; + } + + /*check if decimation is enable or not */ + if (params->decimation) + regw_if(params->rsz, IPIPEIF_RSZ); + + if (device_type != DM365) + return 0; + + /* Setup sync alignment and initial rsz position */ + val = params->var.if_5_1.align_sync & 1; + val <<= IPIPEIF_INIRSZ_ALNSYNC_SHIFT; + val |= params->var.if_5_1.rsz_start & IPIPEIF_INIRSZ_MASK; + regw_if(val, IPIPEIF_INIRSZ); + + /* Enable DPCM decompression */ + switch (params->source) { + case IPIPEIF_SDRAM_RAW: + val = 0; + if (params->var.if_5_1.dpcm.en) { + val = params->var.if_5_1.dpcm.en & 1; + val |= (params->var.if_5_1.dpcm.type & 1) << + IPIPEIF_DPCM_BITS_SHIFT; + val |= (params->var.if_5_1.dpcm.pred & 1) << + IPIPEIF_DPCM_PRED_SHIFT; + } + regw_if(val, IPIPEIF_DPCM); + + /* set DPC */ + ipipeif_config_dpc(¶ms->var.if_5_1.dpc); + + regw_if(params->var.if_5_1.clip, IPIPEIF_OCLIP); + /* fall through for SDRAM YUV mode */ + isif_port_if = params->var.if_5_1.isif_port.if_type; + /* configure CFG2 */ + val = regr_if(IPIPEIF_CFG2); + switch (isif_port_if) { + case V4L2_MBUS_FMT_YUYV8_1X16: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + regw_if(val, IPIPEIF_CFG2); + break; + default: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + RESETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + regw_if(val, IPIPEIF_CFG2); + break; + } + break; + case IPIPEIF_SDRAM_YUV: + /* Set clock divider */ + if (params->clock_select == IPIPEIF_SDRAM_CLK) { + val = regr_if(IPIPEIF_CLKDIV); + val |= (params->var.if_5_1.clk_div.m - 1) << + IPIPEIF_CLKDIV_M_SHIFT; + val |= (params->var.if_5_1.clk_div.n - 1); + regw_if(val, IPIPEIF_CLKDIV); + } + break; + case IPIPEIF_CCDC: + case IPIPEIF_CCDC_DARKFM: + /* set DPC */ + ipipeif_config_dpc(¶ms->var.if_5_1.dpc); + + /* Set DF gain & threshold control */ + val = 0; + if (params->var.if_5_1.df_gain_en) { + val = params->var.if_5_1.df_gain_thr & + IPIPEIF_DF_GAIN_THR_MASK; + regw_if(val, IPIPEIF_DFSGTH); + val = (params->var.if_5_1.df_gain_en & 1) << + IPIPEIF_DF_GAIN_EN_SHIFT; + val |= params->var.if_5_1.df_gain & + IPIPEIF_DF_GAIN_MASK; + } + regw_if(val, IPIPEIF_DFSGVL); + isif_port_if = params->var.if_5_1.isif_port.if_type; + + /* configure CFG2 */ + val = params->var.if_5_1.isif_port.hdpol << + IPIPEIF_CFG2_HDPOL_SHIFT; + val |= params->var.if_5_1.isif_port.vdpol << + IPIPEIF_CFG2_VDPOL_SHIFT; + + switch (isif_port_if) { + case V4L2_MBUS_FMT_YUYV8_1X16: + case V4L2_MBUS_FMT_YUYV10_1X20: + RESETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + break; + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + SETBIT(val, IPIPEIF_CFG2_YUV8_SHIFT); + SETBIT(val, IPIPEIF_CFG2_YUV16_SHIFT); + val |= params->var.if_5_1.pix_order << + IPIPEIF_CFG2_YUV8P_SHIFT; + break; + default: + /* Bayer */ + regw_if(params->var.if_5_1.clip, + IPIPEIF_OCLIP); + } + regw_if(val, IPIPEIF_CFG2); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int __devinit dm3xx_ipipeif_probe(struct platform_device *pdev) +{ + static resource_size_t res_len; + struct resource *res; + int status; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + res_len = resource_size(res); + + res = request_mem_region(res->start, res_len, res->name); + if (!res) + return -EBUSY; + + ipipeif_base_addr = ioremap_nocache(res->start, res_len); + if (!ipipeif_base_addr) { + status = -EBUSY; + goto fail; + } + return 0; + +fail: + release_mem_region(res->start, res_len); + + return status; +} + +static int dm3xx_ipipeif_remove(struct platform_device *pdev) +{ + struct resource *res; + + iounmap(ipipeif_base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + release_mem_region(res->start, resource_size(res)); + return 0; +} + +static struct platform_driver dm3xx_ipipeif_driver = { + .driver = { + .name = "dm3xx_ipipeif", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(dm3xx_ipipeif_remove), + .probe = dm3xx_ipipeif_probe, +}; + +static int dm3xx_ipipeif_init(void) +{ + return platform_driver_register(&dm3xx_ipipeif_driver); +} + +static void dm3xx_ipipeif_exit(void) +{ + platform_driver_unregister(&dm3xx_ipipeif_driver); +} + +module_init(dm3xx_ipipeif_init); +module_exit(dm3xx_ipipeif_exit); + +MODULE_LICENSE("GPL2"); diff --git a/drivers/media/video/davinci/dm3xx_ipipeif.h b/drivers/media/video/davinci/dm3xx_ipipeif.h new file mode 100644 index 0000000..3995eb0 --- /dev/null +++ b/drivers/media/video/davinci/dm3xx_ipipeif.h @@ -0,0 +1,255 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _DM3XX_IPIPEIF_H +#define _DM3XX_IPIPEIF_H + +#include +#include +#include +#include +#include "vpss.h" + +/* Used to shift input image data based on the data lines connected + * to parallel port + */ +/* IPIPE base specific types */ +enum ipipeif_data_shift { + IPIPEIF_BITS15_2, + IPIPEIF_BITS14_1, + IPIPEIF_BITS13_0, + IPIPEIF_BITS12_0, + IPIPEIF_BITS11_0, + IPIPEIF_BITS10_0, + IPIPEIF_BITS9_0 +}; + +enum ipipeif_clkdiv { + IPIPEIF_DIVIDE_HALF, + IPIPEIF_DIVIDE_THIRD, + IPIPEIF_DIVIDE_FOURTH, + IPIPEIF_DIVIDE_FIFTH, + IPIPEIF_DIVIDE_SIXTH, + IPIPEIF_DIVIDE_EIGHTH, + IPIPEIF_DIVIDE_SIXTEENTH, + IPIPEIF_DIVIDE_THIRTY +}; + +enum ipipeif_clock { + IPIPEIF_PIXCEL_CLK, + IPIPEIF_SDRAM_CLK +}; + +enum ipipeif_pack_mode { + IPIPEIF_PACK_16_BIT, + IPIPEIF_PACK_8_BIT +}; + +enum ipipe_oper_mode { + IPIPEIF_CONTINUOUS, + IPIPEIF_ONE_SHOT +}; + +enum ipipeif_5_1_pack_mode { + IPIPEIF_5_1_PACK_16_BIT, + IPIPEIF_5_1_PACK_8_BIT, + IPIPEIF_5_1_PACK_8_BIT_A_LAW, + IPIPEIF_5_1_PACK_12_BIT +}; + +enum ipipeif_avg_filter { + IPIPEIF_AVG_OFF, + IPIPEIF_AVG_ON +}; + +enum ipipeif_input_source { + IPIPEIF_CCDC, + IPIPEIF_SDRAM_RAW, + IPIPEIF_CCDC_DARKFM, + IPIPEIF_SDRAM_YUV +}; + +enum ipipeif_ialaw { + IPIPEIF_ALAW_OFF, + IPIPEIF_ALAW_ON +}; + +struct ipipeif_base { + enum ipipeif_ialaw ialaw; + enum ipipeif_pack_mode pack_mode; + enum ipipeif_data_shift data_shift; + enum ipipeif_clkdiv clk_div; +}; + +enum ipipeif_input_src1 { + IPIPEIF_SRC1_PARALLEL_PORT, + IPIPEIF_SRC1_SDRAM_RAW, + IPIPEIF_SRC1_ISIF_DARKFM, + IPIPEIF_SRC1_SDRAM_YUV +}; + +enum ipipeif_dpcm_type { + IPIPEIF_DPCM_8BIT_10BIT, + IPIPEIF_DPCM_8BIT_12BIT +}; + +struct ipipeif_dpcm_decomp { + unsigned char en; + enum ipipeif_dpcm_type type; + enum ipipeif_dpcm_pred pred; +}; + +enum ipipeif_dfs_dir { + IPIPEIF_PORT_MINUS_SDRAM, + IPIPEIF_SDRAM_MINUS_PORT +}; + +struct ipipeif_5_1 { + enum ipipeif_5_1_pack_mode pack_mode; + enum ipipeif_5_1_data_shift data_shift; + enum ipipeif_input_src1 source1; + struct ipipeif_5_1_clkdiv clk_div; + /* Defect pixel correction */ + struct ipipeif_dpc dpc; + /* DPCM decompression */ + struct ipipeif_dpcm_decomp dpcm; + /* ISIF port pixel order */ + enum ipipeif_pixel_order pix_order; + /* interface parameters from isif */ + struct vpfe_hw_if_param isif_port; + /* clipped to this value */ + unsigned short clip; + /* Align HSync and VSync to rsz_start */ + unsigned char align_sync; + /* resizer start position */ + unsigned int rsz_start; + /* DF gain enable */ + unsigned char df_gain_en; + /* DF gain value */ + unsigned short df_gain; + /* DF gain threshold value */ + unsigned short df_gain_thr; +}; + +/* ipipeif structures common to DM350 and DM365 used by ipipeif API */ +struct ipipeif { + enum ipipe_oper_mode mode; + enum ipipeif_input_source source; + enum ipipeif_clock clock_select; + unsigned int glob_hor_size; + unsigned int glob_ver_size; + unsigned int hnum; + unsigned int vnum; + unsigned int adofs; + unsigned char rsz; + enum ipipeif_decimation decimation; + enum ipipeif_avg_filter avg_filter; + unsigned short gain; + /* IPIPE 5.1 */ + union var_part { + struct ipipeif_base if_base; + struct ipipeif_5_1 if_5_1; + } var; +}; + +/* IPIPEIF Register Offsets from the base address */ +#define IPIPEIF_ENABLE 0x00 +#define IPIPEIF_CFG1 0x04 +#define IPIPEIF_PPLN 0x08 +#define IPIPEIF_LPFR 0x0c +#define IPIPEIF_HNUM 0x10 +#define IPIPEIF_VNUM 0x14 +#define IPIPEIF_ADDRU 0x18 +#define IPIPEIF_ADDRL 0x1c +#define IPIPEIF_ADOFS 0x20 +#define IPIPEIF_RSZ 0x24 +#define IPIPEIF_GAIN 0x28 + +/* Below registers are available only on IPIPE 5.1 */ +#define IPIPEIF_DPCM 0x2c +#define IPIPEIF_CFG2 0x30 +#define IPIPEIF_INIRSZ 0x34 +#define IPIPEIF_OCLIP 0x38 +#define IPIPEIF_DTUDF 0x3c +#define IPIPEIF_CLKDIV 0x40 +#define IPIPEIF_DPC1 0x44 +#define IPIPEIF_DPC2 0x48 +#define IPIPEIF_DFSGVL 0x4c +#define IPIPEIF_DFSGTH 0x50 +#define IPIPEIF_RSZ3A 0x54 +#define IPIPEIF_INIRSZ3A 0x58 +#define IPIPEIF_RSZ_MIN 16 +#define IPIPEIF_RSZ_MAX 112 +#define IPIPEIF_RSZ_CONST 16 +#define SETBIT(reg, bit) (reg = ((reg) | ((0x00000001)<<(bit)))) +#define RESETBIT(reg, bit) (reg = ((reg) & (~(0x00000001<<(bit))))) + +#define IPIPEIF_ADOFS_LSB_MASK 0x1ff +#define IPIPEIF_ADOFS_LSB_SHIFT 5 +#define IPIPEIF_ADOFS_MSB_MASK 0x200 +#define IPIPEIF_ADDRU_MASK 0x7ff +#define IPIPEIF_ADDRL_SHIFT 5 +#define IPIPEIF_ADDRL_MASK 0xffff +#define IPIPEIF_ADDRU_SHIFT 21 +#define IPIPEIF_ADDRMSB_SHIFT 31 +#define IPIPEIF_ADDRMSB_LEFT_SHIFT 10 + +/* CFG1 Masks and shifts */ +#define ONESHOT_SHIFT 0 +#define DECIM_SHIFT 1 +#define INPSRC_SHIFT 2 +#define CLKDIV_SHIFT 4 +#define AVGFILT_SHIFT 7 +#define PACK8IN_SHIFT 8 +#define IALAW_SHIFT 9 +#define CLKSEL_SHIFT 10 +#define DATASFT_SHIFT 11 +#define INPSRC1_SHIFT 14 + +/* DPC2 */ +#define IPIPEIF_DPC2_EN_SHIFT 12 +#define IPIPEIF_DPC2_THR_MASK 0xfff +/* Applicable for IPIPE 5.1 */ +#define IPIPEIF_DF_GAIN_EN_SHIFT 10 +#define IPIPEIF_DF_GAIN_MASK 0x3ff +#define IPIPEIF_DF_GAIN_THR_MASK 0xfff +/* DPCM */ +#define IPIPEIF_DPCM_BITS_SHIFT 2 +#define IPIPEIF_DPCM_PRED_SHIFT 1 +/* CFG2 */ +#define IPIPEIF_CFG2_HDPOL_SHIFT 1 +#define IPIPEIF_CFG2_VDPOL_SHIFT 2 +#define IPIPEIF_CFG2_YUV8_SHIFT 6 +#define IPIPEIF_CFG2_YUV16_SHIFT 3 +#define IPIPEIF_CFG2_YUV8P_SHIFT 7 + +/* INIRSZ */ +#define IPIPEIF_INIRSZ_ALNSYNC_SHIFT 13 +#define IPIPEIF_INIRSZ_MASK 0x1fff + +/* CLKDIV */ +#define IPIPEIF_CLKDIV_M_SHIFT 8 + +int ipipeif_set_address(struct ipipeif *if_params, unsigned int address); +void ipipeif_set_enable(char en, unsigned int mode); +int ipipeif_hw_setup(struct ipipeif *if_params, int device_type); +u32 ipipeif_get_enable(void); + +#define DM355 0 +#define DM365 1 + +#endif diff --git a/include/linux/dm3xx_ipipeif.h b/include/linux/dm3xx_ipipeif.h new file mode 100644 index 0000000..a63ead5 --- /dev/null +++ b/include/linux/dm3xx_ipipeif.h @@ -0,0 +1,64 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _DM3XX_IPIPEIF_INCLUDE_H +#define _DM3XX_IPIPEIF_INCLUDE_H + +#include + +/* IPIPE 5.1 interface types */ +/* dpcm predicator for IPIPE 5.1 */ +enum ipipeif_dpcm_pred { + IPIPEIF_DPCM_SIMPLE_PRED, + IPIPEIF_DPCM_ADV_PRED +}; + +/* clockdiv for IPIPE 5.1 */ +struct ipipeif_5_1_clkdiv { + unsigned char m; + unsigned char n; +}; + +/* data shift for IPIPE 5.1 */ +enum ipipeif_5_1_data_shift { + IPIPEIF_5_1_BITS11_0, + IPIPEIF_5_1_BITS10_0, + IPIPEIF_5_1_BITS9_0, + IPIPEIF_5_1_BITS8_0, + IPIPEIF_5_1_BITS7_0, + IPIPEIF_5_1_BITS15_4, +}; + +enum ipipeif_decimation { + IPIPEIF_DECIMATION_OFF, + IPIPEIF_DECIMATION_ON +}; + +/* DPC at the if for IPIPE 5.1 */ +struct ipipeif_dpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* threshold */ + unsigned short thr; +}; + +enum ipipeif_pixel_order { + IPIPEIF_CBCR_Y, + IPIPEIF_Y_CBCR +}; + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:44:37 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 16:14:37 +0530 Subject: [RESEND RFC PATCH v4 11/15] davinci: vpfe: add autofocus driver based on media framework In-Reply-To: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> References: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321526681-22574-12-git-send-email-manjunath.hadli@ti.com> Add autofocus driver based on v4l2 media framework. The driver processess the bayer data from CCDC and generates the data which will be used for calculation of parametsrs responsible for focussing. The driver implement the v4l2 event queing mechanism to know the readiness data from the driver. Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/vpfe_af.c | 240 +++++++++++++++++++++++++++++++++ drivers/media/video/davinci/vpfe_af.h | 50 +++++++ 2 files changed, 290 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpfe_af.c create mode 100644 drivers/media/video/davinci/vpfe_af.h diff --git a/drivers/media/video/davinci/vpfe_af.c b/drivers/media/video/davinci/vpfe_af.c new file mode 100644 index 0000000..b19bc8a --- /dev/null +++ b/drivers/media/video/davinci/vpfe_af.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Nagabhushana Netagunte + */ +#include +#include +#include +#include +#include +#include +#include "vpss.h" +#include "vpfe_capture.h" + +/* + * V4L2 subdev operations + */ + +/* + * af_subscribe_event - subscribe event + * @sd: VPFE AF V4L2 subdevice + * @fh: file handle + * @sub: event subscription structure + */ +static int af_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct vpfe_af_device *af = v4l2_get_subdevdata(sd); + + if (sub->type != af->event_type) + return -EINVAL; + + return v4l2_event_subscribe(fh, sub); +} + +/* + * af_unsubscribe_event - unsubscribe event + * @sd: VPFE AF V4L2 subdevice + * @fh: file handle + * @sub: event subscription structure + */ +static int af_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct vpfe_af_device *af = v4l2_get_subdevdata(sd); + + if (sub->type != af->event_type) + return -EINVAL; + + return v4l2_event_unsubscribe(fh, sub); +} + +/* + * af_queue_event - queue event + * @sd: VPFE AF V4L2 subdevice + */ +void af_queue_event(struct v4l2_subdev *sd) +{ + struct vpfe_af_device *af = v4l2_get_subdevdata(sd); + struct video_device *vdev = &sd->devnode; + struct v4l2_event event; + + memset(&event, 0, sizeof(event)); + event.type = af->event_type; + v4l2_event_queue(vdev, &event); +} + +/* + * af_set_stream - Enable/Disable streaming on the AF module + * @sd: VPFE AF V4L2 subdevice + * @enable: Enable/disable stream + */ +static int set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_af_device *af = v4l2_get_subdevdata(sd); + + if (af->input == AF_INPUT_CCDC) + return af_set_stream(sd, enable); + + return 0; +} + +/* + * af_ioctl - AF module private ioctl's + * @sd: VPFE AF V4L2 subdevice + * @cmd: ioctl command + * @arg: ioctl argument + * + * Return 0 on success or a negative error code otherwise. + */ +static long ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + return af_ioctl(sd, cmd, arg); +} + +/* V4L2 subdev core operations */ +static const struct v4l2_subdev_core_ops af_v4l2_core_ops = { + .ioctl = ioctl, + .subscribe_event = af_subscribe_event, + .unsubscribe_event = af_unsubscribe_event, +}; + +/* V4L2 subdev video operations */ +static const struct v4l2_subdev_video_ops af_v4l2_video_ops = { + .s_stream = set_stream, +}; + +/* V4L2 subdev operations */ +static const struct v4l2_subdev_ops af_v4l2_ops = { + .core = &af_v4l2_core_ops, + .video = &af_v4l2_video_ops, +}; + +/* + * Media entity operations + */ + +/* + * af_link_setup - Setup AF connections + * @entity: AF media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int af_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_af_device *af = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev; + + vpfe_dev = to_vpfe_device(af); + + if ((flags & MEDIA_LNK_FL_ENABLED)) { + af->input = AF_INPUT_CCDC; + af_open(); + } else { + af->input = AF_INPUT_NONE; + af_release(); + } + return 0; +} + +static const struct media_entity_operations af_media_ops = { + .link_setup = af_link_setup, +}; + +/* + * vpfe_af_register_entities - AF subdev driver registration + * @af - pointer to af subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +int vpfe_af_register_entities(struct vpfe_af_device *af, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &af->subdev); + if (ret < 0) + return ret; + + return 0; +} + +/* + * vpfe_af_unregister_entities - AF subdev driver unregistration + * @af - pointer to af subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +void vpfe_af_unregister_entities(struct vpfe_af_device *af) +{ + /* cleanup entity */ + media_entity_cleanup(&af->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&af->subdev); +} + +/* + * vpfe_af_init - AF module initilaization. + * @vpfe_af - pointer to af subdevice structure. + * @pdev: platform device pointer. + */ +int vpfe_af_init(struct vpfe_af_device *vpfe_af, struct platform_device *pdev) +{ + struct v4l2_subdev *af = &vpfe_af->subdev; + struct media_pad *pads = &vpfe_af->pads[0]; + struct media_entity *me = &af->entity; + int ret; + + if (af_init(pdev)) + return -EINVAL; + + v4l2_subdev_init(af, &af_v4l2_ops); + strlcpy(af->name, "DAVINCI AF", sizeof(af->name)); + af->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(af, vpfe_af); + af->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + af->nevents = DAVINCI_AF_NEVENTS; + pads[AF_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; + vpfe_af->input = AF_INPUT_NONE; + vpfe_af->event_type = DAVINCI_EVENT_AF; + me->ops = &af_media_ops; + ret = media_entity_init(me, AF_PADS_NUM, pads, 0); + if (ret) + goto out_davanci_init; + + return 0; + +out_davanci_init: + af_cleanup(); + + return ret; +} + +/* + * vpfe_af_cleanup - AF module cleanup. + */ +void vpfe_af_cleanup(void) +{ + af_cleanup(); +} diff --git a/drivers/media/video/davinci/vpfe_af.h b/drivers/media/video/davinci/vpfe_af.h new file mode 100644 index 0000000..dea1d11 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_af.h @@ -0,0 +1,50 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef _VPFE_AF_H +#define _VPFE_AF_H + +#define AF_PAD_SINK 0 +#define AF_PADS_NUM 1 + +#define DAVINCI_AF_NEVENTS 1 + +#define AF_INPUT_NONE 0 +#define AF_INPUT_CCDC 1 + +#define DAVINCI_EVENT_AF 1 + +struct vpfe_af_device { + struct v4l2_subdev subdev; + struct media_pad pads[AF_PADS_NUM]; + unsigned int input; + unsigned long event_type; +}; + +int af_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg); +int af_set_stream(struct v4l2_subdev *sd, int enable); +int af_init(struct platform_device *pdev); +void af_cleanup(void); +int af_release(void); +int af_open(void); + +int vpfe_af_init(struct vpfe_af_device *vpfe_af, struct platform_device *pdev); +void vpfe_af_unregister_entities(struct vpfe_af_device *af); +int vpfe_af_register_entities(struct vpfe_af_device *af, + struct v4l2_device *v4l2_dev); +void af_queue_event(struct v4l2_subdev *sd); +void vpfe_af_cleanup(void); +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:44:28 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 16:14:28 +0530 Subject: [RESEND RFC PATCH v4 02/15] davinci: vpfe: add IPIPE hardware layer support In-Reply-To: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> References: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321526681-22574-3-git-send-email-manjunath.hadli@ti.com> add dm365 IPIPE hardware support. IPIPE is the hardware IP which implements the functionality required for resizer, previewer and the associated feature support. This is built along with the vpfe driver, and implements hardware setup including coeffcient programming for various hardware filters, gamma, cfa and clock enable. Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/dm365_ipipe_hw.c | 935 ++++++++++++++++++++++++++ drivers/media/video/davinci/dm365_ipipe_hw.h | 539 +++++++++++++++ 2 files changed, 1474 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/dm365_ipipe_hw.c create mode 100644 drivers/media/video/davinci/dm365_ipipe_hw.h diff --git a/drivers/media/video/davinci/dm365_ipipe_hw.c b/drivers/media/video/davinci/dm365_ipipe_hw.c new file mode 100644 index 0000000..7de9702 --- /dev/null +++ b/drivers/media/video/davinci/dm365_ipipe_hw.c @@ -0,0 +1,935 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include +#include +#include "dm365_ipipe.h" +#include "dm3xx_ipipeif.h" +#include "dm365_ipipe_hw.h" + +static void ipipe_clock_enable(void) +{ + /* enable IPIPE MMR for register write access */ + regw_ip(IPIPE_GCK_MMR_DEFAULT, IPIPE_GCK_MMR); + /* enable the clock wb,cfa,dfc,d2f,pre modules */ + regw_ip(IPIPE_GCK_PIX_DEFAULT, IPIPE_GCK_PIX); + /* enable RSZ MMR for register write access */ +} + +/* Set input channel format to either 420 Y or C format */ +void rsz_set_in_pix_format(unsigned char y_c) +{ + u32 val; + + val = regr_rsz(RSZ_SRC_FMT1); + val |= y_c & 1; + regw_rsz(val, RSZ_SRC_FMT1); +} + +static void rsz_set_common_params(struct ipipe_params *params) +{ + struct rsz_common_params *rsz_common = ¶ms->rsz_common; + u32 val; + + /* Set mode */ + regw_rsz(params->ipipe_mode, RSZ_SRC_MODE); + + /* data source selection and bypass */ + val = (rsz_common->passthrough << RSZ_BYPASS_SHIFT) | + rsz_common->source; + + regw_rsz(val, RSZ_SRC_FMT0); + val = regr_rsz(RSZ_SRC_MODE); + /* src image selection */ + val = (rsz_common->raw_flip & 1) | + (rsz_common->src_img_fmt << RSZ_SRC_IMG_FMT_SHIFT) | + ((rsz_common->y_c & 1) << RSZ_SRC_Y_C_SEL_SHIFT); + + regw_rsz(val, RSZ_SRC_FMT1); + regw_rsz(rsz_common->vps & IPIPE_RSZ_VPS_MASK, RSZ_SRC_VPS); + regw_rsz(rsz_common->hps & IPIPE_RSZ_HPS_MASK, RSZ_SRC_HPS); + regw_rsz(rsz_common->vsz & IPIPE_RSZ_VSZ_MASK, RSZ_SRC_VSZ); + regw_rsz(rsz_common->hsz & IPIPE_RSZ_HSZ_MASK, RSZ_SRC_HSZ); + regw_rsz(rsz_common->yuv_y_min, RSZ_YUV_Y_MIN); + regw_rsz(rsz_common->yuv_y_max, RSZ_YUV_Y_MAX); + regw_rsz(rsz_common->yuv_c_min, RSZ_YUV_C_MIN); + regw_rsz(rsz_common->yuv_c_max, RSZ_YUV_C_MAX); + /* chromatic position */ + regw_rsz(rsz_common->out_chr_pos, RSZ_YUV_PHS); + val = regr_rsz(RSZ_SRC_MODE); +} + +static void rsz_set_rsz_regs(unsigned int rsz_id, struct ipipe_params *params) +{ + struct ipipe_rsz_rescale_param *rsc_params; + struct ipipe_ext_mem_param *ext_mem; + struct ipipe_rsz_resize2rgb *rgb; + u32 reg_base; + u32 val; + + val = regr_rsz(RSZ_SEQ); + rsc_params = ¶ms->rsz_rsc_param[rsz_id]; + rgb = ¶ms->rsz2rgb[rsz_id]; + ext_mem = ¶ms->ext_mem_param[rsz_id]; + + if (rsz_id == RSZ_A) { + val = rsc_params->h_flip << RSZA_H_FLIP_SHIFT; + val |= rsc_params->v_flip << RSZA_V_FLIP_SHIFT; + reg_base = RSZ_EN_A; + } else { + val = rsc_params->h_flip << RSZB_H_FLIP_SHIFT; + val |= rsc_params->v_flip << RSZB_V_FLIP_SHIFT; + reg_base = RSZ_EN_B; + } + /* update flip settings */ + regw_rsz(val, RSZ_SEQ); + + regw_rsz(rsc_params->mode, reg_base + RSZ_MODE); + val = (rsc_params->cen << RSZ_CEN_SHIFT) | rsc_params->yen; + regw_rsz(val, reg_base + RSZ_420); + regw_rsz(rsc_params->i_vps & RSZ_VPS_MASK, reg_base + RSZ_I_VPS); + regw_rsz(rsc_params->i_hps & RSZ_HPS_MASK, reg_base + RSZ_I_HPS); + regw_rsz(rsc_params->o_vsz & RSZ_O_VSZ_MASK, reg_base + RSZ_O_VSZ); + regw_rsz(rsc_params->o_hsz & RSZ_O_HSZ_MASK, reg_base + RSZ_O_HSZ); + regw_rsz(rsc_params->v_phs_y & RSZ_V_PHS_MASK, reg_base + RSZ_V_PHS_Y); + regw_rsz(rsc_params->v_phs_c & RSZ_V_PHS_MASK, reg_base + RSZ_V_PHS_C); + /* keep this additional adjustment to zero for now */ + regw_rsz(rsc_params->v_dif & RSZ_V_DIF_MASK, reg_base + RSZ_V_DIF); + + val = (rsc_params->v_typ_y & 1) | ((rsc_params->v_typ_c & 1) << + RSZ_TYP_C_SHIFT); + regw_rsz(val, reg_base + RSZ_V_TYP); + + val = (rsc_params->v_lpf_int_y & RSZ_LPF_INT_MASK) | + ((rsc_params->v_lpf_int_c & RSZ_LPF_INT_MASK) << + RSZ_LPF_INT_C_SHIFT); + regw_rsz(val, reg_base + RSZ_V_LPF); + + regw_rsz(rsc_params->h_phs & RSZ_H_PHS_MASK, reg_base + RSZ_H_PHS); + regw_rsz(0, reg_base + RSZ_H_PHS_ADJ); + regw_rsz(rsc_params->h_dif & RSZ_H_DIF_MASK, reg_base + RSZ_H_DIF); + val = (rsc_params->h_typ_y & 1) | ((rsc_params->h_typ_c & 1) << + RSZ_TYP_C_SHIFT); + regw_rsz(val, reg_base + RSZ_H_TYP); + val = (rsc_params->h_lpf_int_y & RSZ_LPF_INT_MASK) | + ((rsc_params->h_lpf_int_c & RSZ_LPF_INT_MASK) << + RSZ_LPF_INT_C_SHIFT); + regw_rsz(val, reg_base + RSZ_H_LPF); + + regw_rsz(rsc_params->dscale_en & 1, reg_base + RSZ_DWN_EN); + val = rsc_params->h_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK; + val |= (rsc_params->v_dscale_ave_sz & RSZ_DWN_SCALE_AV_SZ_MASK) << + RSZ_DWN_SCALE_AV_SZ_V_SHIFT; + regw_rsz(val, reg_base + RSZ_DWN_AV); + + /* setting rgb conversion parameters */ + regw_rsz(rgb->rgb_en, reg_base + RSZ_RGB_EN); + val = (rgb->rgb_typ << RSZ_RGB_TYP_SHIFT) | + (rgb->rgb_msk0 << RSZ_RGB_MSK0_SHIFT) | + (rgb->rgb_msk1 << RSZ_RGB_MSK1_SHIFT); + regw_rsz(val, reg_base + RSZ_RGB_TYP); + regw_rsz(rgb->rgb_alpha_val & RSZ_RGB_ALPHA_MASK, + reg_base + RSZ_RGB_BLD); + + /* setting external memory parameters */ + regw_rsz(ext_mem->rsz_sdr_oft_y, reg_base + RSZ_SDR_Y_OFT); + regw_rsz(ext_mem->rsz_sdr_ptr_s_y, reg_base + RSZ_SDR_Y_PTR_S); + regw_rsz(ext_mem->rsz_sdr_ptr_e_y, reg_base + RSZ_SDR_Y_PTR_E); + regw_rsz(ext_mem->rsz_sdr_oft_c, reg_base + RSZ_SDR_C_OFT); + regw_rsz(ext_mem->rsz_sdr_ptr_s_c, reg_base + RSZ_SDR_C_PTR_S); + regw_rsz((ext_mem->rsz_sdr_ptr_e_c >> 1), reg_base + RSZ_SDR_C_PTR_E); +} + +/*set the registers of either RSZ0 or RSZ1 */ +static void ipipe_setup_resizer(struct ipipe_params *params) +{ + /* enable MMR gate to write to Resizer */ + regw_rsz(1, RSZ_GCK_MMR); + + /* Enable resizer if it is not in bypass mode */ + if (params->rsz_common.passthrough) + regw_rsz(0, RSZ_GCK_SDR); + else + regw_rsz(1, RSZ_GCK_SDR); + + rsz_set_common_params(params); + + regw_rsz(params->rsz_en[RSZ_A], RSZ_EN_A); + if (params->rsz_en[RSZ_A]) + /*setting rescale parameters */ + rsz_set_rsz_regs(RSZ_A, params); + + regw_rsz(params->rsz_en[RSZ_B], RSZ_EN_B); + if (params->rsz_en[RSZ_B]) + rsz_set_rsz_regs(RSZ_B, params); + + regr_rsz(RSZ_SRC_MODE); +} + +/* ipipe_hw_setup:It is used for Hardware Setup */ +int ipipe_hw_setup(struct ipipe_params *config) +{ + u32 data_format; + u32 val; + + if (!config) { + printk(KERN_ERR "NULL config block received\n"); + return -EINVAL; + } + + if (ipipeif_hw_setup(&config->ipipeif_param, DM365) < 0) { + printk(KERN_ERR "Unable to configure IPIPEIF"); + return -EINVAL; + } + + /* enable clock to IPIPE */ + vpss_enable_clock(VPSS_IPIPE_CLOCK, 1); + /* enable clock to MMR and modules before writting + * to ipipe registers + */ + ipipe_clock_enable(); + + if (config->rsz_common.source == IPIPEIF_DATA) { + /* we need to skip configuring IPIPE */ + regw_ip(0, IPIPE_SRC_EN); + } else { + /* enable ipipe mode to either one shot or continuous */ + val = config->ipipe_mode; + regw_ip(val, IPIPE_SRC_MODE); + data_format = config->ipipe_dpaths_fmt; + regw_ip(data_format, IPIPE_SRC_FMT); + /* set size */ + regw_ip(config->ipipe_vps & IPIPE_RSZ_VPS_MASK, IPIPE_SRC_VPS); + regw_ip(config->ipipe_hps & IPIPE_RSZ_HPS_MASK, IPIPE_SRC_HPS); + regw_ip(config->ipipe_vsz & IPIPE_RSZ_VSZ_MASK, IPIPE_SRC_VSZ); + regw_ip(config->ipipe_hsz & IPIPE_RSZ_HSZ_MASK, IPIPE_SRC_HSZ); + + if (data_format == IPIPE_RAW2YUV || + data_format == IPIPE_RAW2RAW) { + /* Combine all the fields to make COLPAT register + * of IPIPE + */ + val = config->ipipe_colpat_elep << COLPAT_EE_SHIFT; + val |= config->ipipe_colpat_elop << COLPAT_EO_SHIFT; + val |= config->ipipe_colpat_olep << COLPAT_OE_SHIFT; + val |= config->ipipe_colpat_olop << COLPAT_OO_SHIFT; + regw_ip(val, IPIPE_SRC_COL); + } + } + ipipe_setup_resizer(config); + + return 0; +} + +static void rsz_set_y_address(unsigned int address, unsigned int offset) +{ + u32 val; + + val = address & SET_LOW_ADD; + regw_rsz(val, offset + RSZ_SDR_Y_BAD_L); + regw_rsz(val, offset + RSZ_SDR_Y_SAD_L); + val = (address & SET_HIGH_ADD) >> 16; + regw_rsz(val, offset + RSZ_SDR_Y_BAD_H); + regw_rsz(val, offset + RSZ_SDR_Y_SAD_H); +} + +static void rsz_set_c_address(unsigned int address, unsigned int offset) +{ + u32 val; + + val = address & SET_LOW_ADD; + + regw_rsz(val, offset + RSZ_SDR_C_BAD_L); + regw_rsz(val, offset + RSZ_SDR_C_SAD_L); + val = (address & SET_HIGH_ADD) >> 16; + regw_rsz(val, offset + RSZ_SDR_C_BAD_H); + regw_rsz(val, offset + RSZ_SDR_C_SAD_H); +} + +/* Assume we get a valid params ptr and resize_no set to RSZ_A + * or RSZ_B. This could be called in the interrupt context and + * must be efficient + */ +void rsz_set_output_address(struct ipipe_params *params, + int resize_no, unsigned int address) +{ + unsigned int rsz_start_add; + unsigned int val; + + struct ipipe_ext_mem_param *mem_param = + ¶ms->ext_mem_param[resize_no]; + struct rsz_common_params *rsz_common = + ¶ms->rsz_common; + struct ipipe_rsz_rescale_param *rsc_param = + ¶ms->rsz_rsc_param[resize_no]; + + if (resize_no == RSZ_A) + rsz_start_add = RSZ_EN_A; + else + rsz_start_add = RSZ_EN_B; + /* y_c = 0 for y, = 1 for c */ + if (rsz_common->src_img_fmt == RSZ_IMG_420) { + if (rsz_common->y_c) { + /* C channel */ + val = address + mem_param->flip_ofst_c; + rsz_set_c_address(val, rsz_start_add); + } else { + val = address + mem_param->flip_ofst_y; + rsz_set_y_address(val, rsz_start_add); + } + } else { + if (rsc_param->cen && rsc_param->yen) { + /* 420 */ + val = address + mem_param->c_offset; + val = address + mem_param->c_offset + + mem_param->flip_ofst_c; + val += mem_param->user_y_ofst + + mem_param->user_c_ofst; + if (resize_no == RSZ_B) + val += + params->ext_mem_param[RSZ_A].user_y_ofst + + params->ext_mem_param[RSZ_A].user_c_ofst; + /* set C address */ + rsz_set_c_address(val, rsz_start_add); + } + val = address + mem_param->flip_ofst_y; + val += mem_param->user_y_ofst; + if (resize_no == RSZ_B) + val += params->ext_mem_param[RSZ_A].user_y_ofst + + params->ext_mem_param[RSZ_A].user_c_ofst; + /* set Y address */ + rsz_set_y_address(val, rsz_start_add); + } + /* resizer must be enabled */ + regw_rsz(params->rsz_en[resize_no], rsz_start_add); + +} + +void ipipe_set_lutdpc_regs(struct prev_lutdpc *dpc) +{ + u32 max_tbl_size = LUT_DPC_MAX_SIZE >> 1; + u32 lut_start_addr = DPC_TB0_START_ADDR; + u32 val; + u32 count; + + ipipe_clock_enable(); + regw_ip(dpc->en, DPC_LUT_EN); + if (dpc->en != 1) + return; + + /* if dpc is enabled */ + val = LUTDPC_TBL_256_EN; + val |= dpc->repl_white & 1; + regw_ip(val, DPC_LUT_SEL); + regw_ip(LUT_DPC_START_ADDR, DPC_LUT_ADR); + regw_ip(dpc->dpc_size, DPC_LUT_SIZ & LUT_DPC_SIZE_MASK); + + if (dpc->table == NULL) + return; + + for (count = 0; count < dpc->dpc_size; count++) { + if (count >= max_tbl_size) + lut_start_addr = DPC_TB1_START_ADDR; + val = dpc->table[count].horz_pos & LUT_DPC_H_POS_MASK; + val |= (dpc->table[count].vert_pos & LUT_DPC_V_POS_MASK) << + LUT_DPC_V_POS_SHIFT; + val |= dpc->table[count].method << LUT_DPC_CORR_METH_SHIFT; + w_ip_table(val, (lut_start_addr + + ((count % max_tbl_size) << 2))); + } +} + +static void set_dpc_thresholds(struct prev_otfdpc_2_0 *dpc_thr) +{ + regw_ip((dpc_thr->corr_thr.r & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2C_THR_R); + regw_ip((dpc_thr->corr_thr.gr & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2C_THR_GR); + regw_ip((dpc_thr->corr_thr.gb & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2C_THR_GB); + regw_ip((dpc_thr->corr_thr.b & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2C_THR_B); + regw_ip((dpc_thr->det_thr.r & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2D_THR_R); + regw_ip((dpc_thr->det_thr.gr & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2D_THR_GR); + regw_ip((dpc_thr->det_thr.gb & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2D_THR_GB); + regw_ip((dpc_thr->det_thr.b & OTFDPC_DPC2_THR_MASK), + DPC_OTF_2D_THR_B); +} + +void ipipe_set_otfdpc_regs(struct prev_otfdpc *otfdpc) +{ + struct prev_otfdpc_2_0 *dpc_2_0 = &otfdpc->alg_cfg.dpc_2_0; + struct prev_otfdpc_3_0 *dpc_3_0 = &otfdpc->alg_cfg.dpc_3_0; + u32 val; + + ipipe_clock_enable(); + + regw_ip((otfdpc->en & 1), DPC_OTF_EN); + if (otfdpc->en != 1) + return; + + /* dpc enabled */ + val = otfdpc->det_method << OTF_DET_METHOD_SHIFT; + val |= otfdpc->alg; + regw_ip(val, DPC_OTF_TYP); + if (otfdpc->det_method == IPIPE_DPC_OTF_MIN_MAX) { + /* ALG= 0, TYP = 0, DPC_OTF_2D_THR_[x]=0 + * DPC_OTF_2C_THR_[x] = Maximum thresohld + * MinMax method + */ + dpc_2_0->det_thr.r = dpc_2_0->det_thr.gb = + dpc_2_0->det_thr.gr = dpc_2_0->det_thr.b = 0; + set_dpc_thresholds(dpc_2_0); + return; + } + /* MinMax2 */ + if (otfdpc->alg == IPIPE_OTFDPC_2_0) { + set_dpc_thresholds(dpc_2_0); + return; + } + regw_ip((dpc_3_0->act_adj_shf & OTF_DPC3_0_SHF_MASK), DPC_OTF_3_SHF); + /* Detection thresholds */ + regw_ip(((dpc_3_0->det_thr & OTF_DPC3_0_THR_MASK) << + OTF_DPC3_0_THR_SHIFT), DPC_OTF_3D_THR); + regw_ip((dpc_3_0->det_slp & OTF_DPC3_0_SLP_MASK), DPC_OTF_3D_SLP); + regw_ip((dpc_3_0->det_thr_min & OTF_DPC3_0_DET_MASK), DPC_OTF_3D_MIN); + regw_ip((dpc_3_0->det_thr_max & OTF_DPC3_0_DET_MASK), DPC_OTF_3D_MAX); + /* Correction thresholds */ + regw_ip(((dpc_3_0->corr_thr & OTF_DPC3_0_THR_MASK) << + OTF_DPC3_0_THR_SHIFT), DPC_OTF_3C_THR); + regw_ip((dpc_3_0->corr_slp & OTF_DPC3_0_SLP_MASK), DPC_OTF_3C_SLP); + regw_ip((dpc_3_0->corr_thr_min & OTF_DPC3_0_CORR_MASK), DPC_OTF_3C_MIN); + regw_ip((dpc_3_0->corr_thr_max & OTF_DPC3_0_CORR_MASK), DPC_OTF_3C_MAX); +} + +/* 2D Noise filter */ +void ipipe_set_d2f_regs(unsigned int id, struct prev_nf *noise_filter) +{ + + u32 offset = D2F_1ST; + int count; + u32 val; + + /* id = 0 , NF1 & id = 1, NF 2 */ + if (id) + offset = D2F_2ND; + ipipe_clock_enable(); + regw_ip(noise_filter->en & 1, offset + D2F_EN); + if (noise_filter->en != 1) + return; + + /*noise filter enabled */ + /* Combine all the fields to make D2F_CFG register of IPIPE */ + val = ((noise_filter->spread_val & D2F_SPR_VAL_MASK) << + D2F_SPR_VAL_SHIFT) | + ((noise_filter->shft_val & D2F_SHFT_VAL_MASK) << + D2F_SHFT_VAL_SHIFT) | + (noise_filter->gr_sample_meth << + D2F_SAMPLE_METH_SHIFT) | + ((noise_filter->apply_lsc_gain & 1) << + D2F_APPLY_LSC_GAIN_SHIFT) | D2F_USE_SPR_REG_VAL; + + regw_ip(val, offset + D2F_TYP); + /* edge detection minimum */ + regw_ip(noise_filter->edge_det_min_thr & D2F_EDGE_DET_THR_MASK, + offset + D2F_EDG_MIN); + /* edge detection maximum */ + regw_ip(noise_filter->edge_det_max_thr & D2F_EDGE_DET_THR_MASK, + offset + D2F_EDG_MAX); + for (count = 0; count < IPIPE_NF_STR_TABLE_SIZE; count++) { + regw_ip((noise_filter->str[count] & D2F_STR_VAL_MASK), + offset + D2F_STR + count * 4); + + } + for (count = 0; count < IPIPE_NF_THR_TABLE_SIZE; count++) { + regw_ip(noise_filter->thr[count] & D2F_THR_VAL_MASK, + offset + D2F_THR + count * 4); + } +} + +#define IPIPE_U8Q5(decimal, integer) \ + (((decimal & 0x1f) | ((integer & 0x7) << 5))) + +/* Green Imbalance Correction */ +void ipipe_set_gic_regs(struct prev_gic *gic) +{ + u32 val; + + ipipe_clock_enable(); + regw_ip(gic->en & 1, GIC_EN); + + if (!gic->en) + return; + + /*gic enabled */ + val = gic->wt_fn_type << GIC_TYP_SHIFT; + val |= gic->thr_sel << GIC_THR_SEL_SHIFT; + val |= (gic->apply_lsc_gain & 1) << GIC_APPLY_LSC_GAIN_SHIFT; + regw_ip(val, GIC_TYP); + regw_ip(gic->gain & GIC_GAIN_MASK, GIC_GAN); + + if (gic->gic_alg != IPIPE_GIC_ALG_ADAPT_GAIN) { + /* Constant Gain. Set threshold to maximum */ + regw_ip(GIC_THR_MASK, GIC_THR); + return; + } + + if (gic->thr_sel == IPIPE_GIC_THR_REG) { + regw_ip(gic->thr & GIC_THR_MASK, GIC_THR); + regw_ip(gic->slope & GIC_SLOPE_MASK, GIC_SLP); + } else { + /* Use NF thresholds */ + val = IPIPE_U8Q5(gic->nf2_thr_gain.decimal, + gic->nf2_thr_gain.integer); + regw_ip(val, GIC_NFGAN); + } +} + +#define IPIPE_U13Q9(decimal, integer) \ + (((decimal & 0x1ff) | ((integer & 0xf) << 9))) +/* White balance */ +void ipipe_set_wb_regs(struct prev_wb *wb) +{ + u32 val; + + ipipe_clock_enable(); + /* Ofsets. S12 */ + regw_ip(wb->ofst_r & WB_OFFSET_MASK, WB2_OFT_R); + regw_ip(wb->ofst_gr & WB_OFFSET_MASK, WB2_OFT_GR); + regw_ip(wb->ofst_gb & WB_OFFSET_MASK, WB2_OFT_GB); + regw_ip(wb->ofst_b & WB_OFFSET_MASK, WB2_OFT_B); + + /* Gains. U13Q9 */ + val = IPIPE_U13Q9(wb->gain_r.decimal, wb->gain_r.integer); + regw_ip(val, WB2_WGN_R); + val = IPIPE_U13Q9(wb->gain_gr.decimal, wb->gain_gr.integer); + regw_ip(val, WB2_WGN_GR); + val = IPIPE_U13Q9(wb->gain_gb.decimal, wb->gain_gb.integer); + regw_ip(val, WB2_WGN_GB); + val = IPIPE_U13Q9(wb->gain_b.decimal, wb->gain_b.integer); + regw_ip(val, WB2_WGN_B); +} + +/* CFA */ +void ipipe_set_cfa_regs(struct prev_cfa *cfa) +{ + ipipe_clock_enable(); + regw_ip(cfa->alg, CFA_MODE); + regw_ip(cfa->hpf_thr_2dir & CFA_HPF_THR_2DIR_MASK, CFA_2DIR_HPF_THR); + regw_ip(cfa->hpf_slp_2dir & CFA_HPF_SLOPE_2DIR_MASK, CFA_2DIR_HPF_SLP); + regw_ip(cfa->hp_mix_thr_2dir & CFA_HPF_MIX_THR_2DIR_MASK, + CFA_2DIR_MIX_THR); + regw_ip(cfa->hp_mix_slope_2dir & CFA_HPF_MIX_SLP_2DIR_MASK, + CFA_2DIR_MIX_SLP); + regw_ip(cfa->dir_thr_2dir & CFA_DIR_THR_2DIR_MASK, CFA_2DIR_DIR_THR); + regw_ip(cfa->dir_slope_2dir & CFA_DIR_SLP_2DIR_MASK, CFA_2DIR_DIR_SLP); + regw_ip(cfa->nd_wt_2dir & CFA_ND_WT_2DIR_MASK, CFA_2DIR_NDWT); + regw_ip(cfa->hue_fract_daa & CFA_DAA_HUE_FRA_MASK, CFA_MONO_HUE_FRA); + regw_ip(cfa->edge_thr_daa & CFA_DAA_EDG_THR_MASK, CFA_MONO_EDG_THR); + regw_ip(cfa->thr_min_daa & CFA_DAA_THR_MIN_MASK, CFA_MONO_THR_MIN); + regw_ip(cfa->thr_slope_daa & CFA_DAA_THR_SLP_MASK, CFA_MONO_THR_SLP); + regw_ip(cfa->slope_min_daa & CFA_DAA_SLP_MIN_MASK, CFA_MONO_SLP_MIN); + regw_ip(cfa->slope_slope_daa & CFA_DAA_SLP_SLP_MASK, CFA_MONO_SLP_SLP); + regw_ip(cfa->lp_wt_daa & CFA_DAA_LP_WT_MASK, CFA_MONO_LPWT); +} + +void ipipe_set_rgb2rgb_regs(unsigned int id, struct prev_rgb2rgb *rgb) +{ + u32 offset_mask = RGB2RGB_1_OFST_MASK; + u32 offset = RGB1_MUL_BASE; + u32 integ_mask = 0xf; + u32 val; + + ipipe_clock_enable(); + + if (id) { + /* For second RGB module, gain integer is 3 bits instead + of 4, offset has 11 bits insread of 13 */ + offset = RGB2_MUL_BASE; + integ_mask = 0x7; + offset_mask = RGB2RGB_2_OFST_MASK; + } + /* Gains */ + val = (rgb->coef_rr.decimal & 0xff) | + ((rgb->coef_rr.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_RR); + val = (rgb->coef_gr.decimal & 0xff) | + ((rgb->coef_gr.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_GR); + val = (rgb->coef_br.decimal & 0xff) | + ((rgb->coef_br.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_BR); + val = (rgb->coef_rg.decimal & 0xff) | + ((rgb->coef_rg.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_RG); + val = (rgb->coef_gg.decimal & 0xff) | + ((rgb->coef_gg.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_GG); + val = (rgb->coef_bg.decimal & 0xff) | + ((rgb->coef_bg.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_BG); + val = (rgb->coef_rb.decimal & 0xff) | + ((rgb->coef_rb.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_RB); + val = (rgb->coef_gb.decimal & 0xff) | + ((rgb->coef_gb.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_GB); + val = (rgb->coef_bb.decimal & 0xff) | + ((rgb->coef_bb.integer & integ_mask) << 8); + regw_ip(val, offset + RGB_MUL_BB); + + /* Offsets */ + regw_ip(rgb->out_ofst_r & offset_mask, offset + RGB_OFT_OR); + regw_ip(rgb->out_ofst_g & offset_mask, offset + RGB_OFT_OG); + regw_ip(rgb->out_ofst_b & offset_mask, offset + RGB_OFT_OB); +} + +static void ipipe_update_gamma_tbl(struct ipipe_gamma_entry *table, + int size, u32 addr) +{ + int count; + u32 val; + + for (count = 0; count < size; count++) { + val = table[count].slope & GAMMA_MASK; + val |= (table[count].offset & GAMMA_MASK) << GAMMA_SHIFT; + w_ip_table(val, (addr + (count * 4))); + } +} + +/* Gamma correction */ +void ipipe_set_gamma_regs(struct prev_gamma *gamma) +{ + int table_size; + u32 val; + + ipipe_clock_enable(); + val = (gamma->bypass_r << GAMMA_BYPR_SHIFT) | + (gamma->bypass_b << GAMMA_BYPG_SHIFT) | + (gamma->bypass_g << GAMMA_BYPB_SHIFT) | + (gamma->tbl_sel << GAMMA_TBL_SEL_SHIFT) | + (gamma->tbl_size << GAMMA_TBL_SIZE_SHIFT); + + regw_ip(val, GMM_CFG); + + if (gamma->tbl_sel != IPIPE_GAMMA_TBL_RAM) + return; + + table_size = gamma->tbl_size; + + if (!gamma->bypass_r && gamma->table_r != NULL) { + ipipe_update_gamma_tbl(gamma->table_r, table_size, + GAMMA_R_START_ADDR); + } + if (!gamma->bypass_b && gamma->table_b != NULL) { + ipipe_update_gamma_tbl(gamma->table_b, table_size, + GAMMA_B_START_ADDR); + } + if (!gamma->bypass_g && gamma->table_g != NULL) { + ipipe_update_gamma_tbl(gamma->table_g, table_size, + GAMMA_G_START_ADDR); + } +} + +/* 3D LUT */ +void ipipe_set_3d_lut_regs(struct prev_3d_lut *lut_3d) +{ + struct ipipe_3d_lut_entry *tbl; + u32 bnk_index; + u32 tbl_index; + u32 val; + u32 i; + + ipipe_clock_enable(); + regw_ip(lut_3d->en, D3LUT_EN); + + if (!lut_3d->en) + return; + + /* lut_3d enabled */ + if (!lut_3d->table) + return; + + /* valied table */ + tbl = lut_3d->table; + for (i = 0 ; i < MAX_SIZE_3D_LUT; i++) { + /* Each entry has 0-9 (B), 10-19 (G) and + 20-29 R values */ + val = tbl[i].b & D3_LUT_ENTRY_MASK; + val |= (tbl[i].g & D3_LUT_ENTRY_MASK) << + D3_LUT_ENTRY_G_SHIFT; + val |= (tbl[i].r & D3_LUT_ENTRY_MASK) << + D3_LUT_ENTRY_R_SHIFT; + bnk_index = i % 4; + tbl_index = i >> 2; + tbl_index <<= 2; + if (bnk_index == 0) + w_ip_table(val, tbl_index + D3L_TB0_START_ADDR); + else if (bnk_index == 1) + w_ip_table(val, tbl_index + D3L_TB1_START_ADDR); + else if (bnk_index == 2) + w_ip_table(val, tbl_index + D3L_TB2_START_ADDR); + else + w_ip_table(val, tbl_index + D3L_TB3_START_ADDR); + } +} + +/* Lumina adjustments */ +void ipipe_set_lum_adj_regs(struct prev_lum_adj *lum_adj) +{ + u32 val; + + ipipe_clock_enable(); + /* combine fields of YUV_ADJ to set brightness and contrast */ + val = lum_adj->contrast << LUM_ADJ_CONTR_SHIFT | + lum_adj->brightness << LUM_ADJ_BRIGHT_SHIFT; + regw_ip(val, YUV_ADJ); +} + +#define IPIPE_S12Q8(decimal, integer) \ + (((decimal & 0xff) | ((integer & 0xf) << 8))) +/* RGB2YUV */ +void ipipe_set_rgb2ycbcr_regs(struct prev_rgb2yuv *yuv) +{ + u32 val; + + /* S10Q8 */ + ipipe_clock_enable(); + val = IPIPE_S12Q8(yuv->coef_ry.decimal, yuv->coef_ry.integer); + regw_ip(val, YUV_MUL_RY); + val = IPIPE_S12Q8(yuv->coef_gy.decimal, yuv->coef_gy.integer); + regw_ip(val, YUV_MUL_GY); + val = IPIPE_S12Q8(yuv->coef_by.decimal, yuv->coef_by.integer); + regw_ip(val, YUV_MUL_BY); + val = IPIPE_S12Q8(yuv->coef_rcb.decimal, yuv->coef_rcb.integer); + regw_ip(val, YUV_MUL_RCB); + val = IPIPE_S12Q8(yuv->coef_gcb.decimal, yuv->coef_gcb.integer); + regw_ip(val, YUV_MUL_GCB); + val = IPIPE_S12Q8(yuv->coef_bcb.decimal, yuv->coef_bcb.integer); + regw_ip(val, YUV_MUL_BCB); + val = IPIPE_S12Q8(yuv->coef_rcr.decimal, yuv->coef_rcr.integer); + regw_ip(val, YUV_MUL_RCR); + val = IPIPE_S12Q8(yuv->coef_gcr.decimal, yuv->coef_gcr.integer); + regw_ip(val, YUV_MUL_GCR); + val = IPIPE_S12Q8(yuv->coef_bcr.decimal, yuv->coef_bcr.integer); + regw_ip(val, YUV_MUL_BCR); + regw_ip(yuv->out_ofst_y & RGB2YCBCR_OFST_MASK, YUV_OFT_Y); + regw_ip(yuv->out_ofst_cb & RGB2YCBCR_OFST_MASK, YUV_OFT_CB); + regw_ip(yuv->out_ofst_cr & RGB2YCBCR_OFST_MASK, YUV_OFT_CR); +} + +/* YUV 422 conversion */ +void ipipe_set_yuv422_conv_regs(struct prev_yuv422_conv *conv) +{ + u32 val; + + ipipe_clock_enable(); + /* Combine all the fields to make YUV_PHS register of IPIPE */ + val = (conv->chrom_pos << 0) | (conv->en_chrom_lpf << 1); + regw_ip(val, YUV_PHS); +} + +/* GBCE */ +void ipipe_set_gbce_regs(struct prev_gbce *gbce) +{ + unsigned int tbl_index; + unsigned int count; + u32 mask = GBCE_Y_VAL_MASK; + u32 val; + + if (gbce->type == IPIPE_GBCE_GAIN_TBL) + mask = GBCE_GAIN_VAL_MASK; + + ipipe_clock_enable(); + regw_ip(gbce->en & 1, GBCE_EN); + + if (!gbce->en) + return; + + regw_ip(gbce->type, GBCE_TYP); + + if (!gbce->table) + return; + + /* set to 0 */ + val = 0; + + for (count = 0; count < MAX_SIZE_GBCE_LUT; count++) { + tbl_index = count >> 1; + tbl_index <<= 2; + /* Each table has 2 LUT entries, first in LS + * and second in MS positions + */ + if (count % 2) { + val |= + (gbce->table[count] & mask) << + GBCE_ENTRY_SHIFT; + w_ip_table(val, tbl_index + GBCE_TB_START_ADDR); + } else { + val = gbce->table[count] & mask; + } + } +} + +/* Edge Enhancement */ +void ipipe_set_ee_regs(struct prev_yee *ee) +{ + unsigned int tbl_index; + unsigned int count; + u32 val; + + ipipe_clock_enable(); + regw_ip(ee->en, YEE_EN); + + if (!ee->en) + return; + + val = ee->en_halo_red & 1; + val |= ee->merge_meth << YEE_HALO_RED_EN_SHIFT; + regw_ip(val, YEE_TYP); + regw_ip(ee->hpf_shft, YEE_SHF); + regw_ip(ee->hpf_coef_00 & YEE_COEF_MASK, YEE_MUL_00); + regw_ip(ee->hpf_coef_01 & YEE_COEF_MASK, YEE_MUL_01); + regw_ip(ee->hpf_coef_02 & YEE_COEF_MASK, YEE_MUL_02); + regw_ip(ee->hpf_coef_10 & YEE_COEF_MASK, YEE_MUL_10); + regw_ip(ee->hpf_coef_11 & YEE_COEF_MASK, YEE_MUL_11); + regw_ip(ee->hpf_coef_12 & YEE_COEF_MASK, YEE_MUL_12); + regw_ip(ee->hpf_coef_20 & YEE_COEF_MASK, YEE_MUL_20); + regw_ip(ee->hpf_coef_21 & YEE_COEF_MASK, YEE_MUL_21); + regw_ip(ee->hpf_coef_22 & YEE_COEF_MASK, YEE_MUL_22); + regw_ip(ee->yee_thr & YEE_THR_MASK, YEE_THR); + regw_ip(ee->es_gain & YEE_ES_GAIN_MASK, YEE_E_GAN); + regw_ip(ee->es_thr1 & YEE_ES_THR1_MASK, YEE_E_THR1); + regw_ip(ee->es_thr2 & YEE_THR_MASK, YEE_E_THR2); + regw_ip(ee->es_gain_grad & YEE_THR_MASK, YEE_G_GAN); + regw_ip(ee->es_ofst_grad & YEE_THR_MASK, YEE_G_OFT); + + if (ee->table == NULL) + return; + + for (count = 0; count < MAX_SIZE_YEE_LUT; count++) { + tbl_index = count >> 1; + tbl_index <<= 2; + /* Each table has 2 LUT entries, first in LS + * and second in MS positions + */ + if (count % 2) { + val |= (ee->table[count] & YEE_ENTRY_MASK) << + YEE_ENTRY_SHIFT; + w_ip_table(val, tbl_index + YEE_TB_START_ADDR); + } else { + val = ee->table[count] & YEE_ENTRY_MASK; + } + } +} + +/* Chromatic Artifact Correction. CAR */ +static void ipipe_set_mf(void) +{ + /* typ to dynamic switch */ + regw_ip(IPIPE_CAR_DYN_SWITCH, CAR_TYP); + /* Set SW0 to maximum */ + regw_ip(CAR_MF_THR, CAR_SW); +} + +static void ipipe_set_gain_ctrl(struct prev_car *car) +{ + regw_ip(IPIPE_CAR_CHR_GAIN_CTRL, CAR_TYP); + regw_ip(car->hpf, CAR_HPF_TYP); + regw_ip(car->hpf_shft & CAR_HPF_SHIFT_MASK, CAR_HPF_SHF); + regw_ip(car->hpf_thr, CAR_HPF_THR); + regw_ip(car->gain1.gain, CAR_GN1_GAN); + regw_ip(car->gain1.shft & CAR_GAIN1_SHFT_MASK, CAR_GN1_SHF); + regw_ip(car->gain1.gain_min & CAR_GAIN_MIN_MASK, CAR_GN1_MIN); + regw_ip(car->gain2.gain, CAR_GN2_GAN); + regw_ip(car->gain2.shft & CAR_GAIN2_SHFT_MASK, CAR_GN2_SHF); + regw_ip(car->gain2.gain_min & CAR_GAIN_MIN_MASK, CAR_GN2_MIN); +} + +void ipipe_set_car_regs(struct prev_car *car) +{ + u32 val; + + ipipe_clock_enable(); + regw_ip(car->en, CAR_EN); + + if (!car->en) + return; + + switch (car->meth) { + case IPIPE_CAR_MED_FLTR: + ipipe_set_mf(); + break; + case IPIPE_CAR_CHR_GAIN_CTRL: + ipipe_set_gain_ctrl(car); + break; + default: + /* Dynamic switch between MF and Gain Ctrl. */ + ipipe_set_mf(); + ipipe_set_gain_ctrl(car); + /* Set the threshold for switching between + * the two Here we overwrite the MF SW0 value + */ + regw_ip(IPIPE_CAR_DYN_SWITCH, CAR_TYP); + val = car->sw1; + val <<= CAR_SW1_SHIFT; + val |= car->sw0; + regw_ip(val, CAR_SW); + } +} + +/* Chromatic Gain Suppression */ +void ipipe_set_cgs_regs(struct prev_cgs *cgs) +{ + ipipe_clock_enable(); + regw_ip(cgs->en, CGS_EN); + + if (!cgs->en) + return; + + /* Set the bright side parameters */ + regw_ip(cgs->h_thr, CGS_GN1_H_THR); + regw_ip(cgs->h_slope, CGS_GN1_H_GAN); + regw_ip(cgs->h_shft & CAR_SHIFT_MASK, CGS_GN1_H_SHF); + regw_ip(cgs->h_min, CGS_GN1_H_MIN); +} + +void rsz_src_enable(int enable) +{ + regw_rsz(enable, RSZ_SRC_EN); +} + +int rsz_enable(int rsz_id, int enable) +{ + if (rsz_id == RSZ_A) { + regw_rsz(enable, RSZ_EN_A); + /* We always enable RSZ_A. RSZ_B is enable upon request from + * application. So enable RSZ_SRC_EN along with RSZ_A + */ + regw_rsz(enable, RSZ_SRC_EN); + } else if (rsz_id == RSZ_B) { + regw_rsz(enable, RSZ_EN_B); + } else { + return -EINVAL; + } + + return 0; +} diff --git a/drivers/media/video/davinci/dm365_ipipe_hw.h b/drivers/media/video/davinci/dm365_ipipe_hw.h new file mode 100644 index 0000000..a999b8a --- /dev/null +++ b/drivers/media/video/davinci/dm365_ipipe_hw.h @@ -0,0 +1,539 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef _DM365_IPIPE_HW_H +#define _DM365_IPIPE_HW_H + +#include +#include + +#define IPIPE_IOBASE_VADDR IO_ADDRESS(0x01c70800) +#define RSZ_IOBASE_VADDR IO_ADDRESS(0x01c70400) +#define IPIPE_INT_TABLE_IOBASE_VADDR IO_ADDRESS(0x01c70000) + +#define SET_LOW_ADD 0x0000ffff +#define SET_HIGH_ADD 0xffff0000 + +/* Below are the internal tables */ +#define DPC_TB0_START_ADDR 0x8000 +#define DPC_TB1_START_ADDR 0x8400 + +#define GAMMA_R_START_ADDR 0xa800 +#define GAMMA_G_START_ADDR 0xb000 +#define GAMMA_B_START_ADDR 0xb800 + +/* RAM table addresses for edge enhancement correction*/ +#define YEE_TB_START_ADDR 0x8800 + +/* RAM table address for GBC LUT */ +#define GBCE_TB_START_ADDR 0x9000 + +/* RAM table for 3D NF LUT */ +#define D3L_TB0_START_ADDR 0x9800 +#define D3L_TB1_START_ADDR 0x9c00 +#define D3L_TB2_START_ADDR 0xa000 +#define D3L_TB3_START_ADDR 0xa400 + +/* IPIPE Register Offsets from the base address */ +#define IPIPE_SRC_EN 0x0000 +#define IPIPE_SRC_MODE 0x0004 +#define IPIPE_SRC_FMT 0x0008 +#define IPIPE_SRC_COL 0x000c +#define IPIPE_SRC_VPS 0x0010 +#define IPIPE_SRC_VSZ 0x0014 +#define IPIPE_SRC_HPS 0x0018 +#define IPIPE_SRC_HSZ 0x001c + +#define IPIPE_SEL_SBU 0x0020 + +#define IPIPE_DMA_STA 0x0024 +#define IPIPE_GCK_MMR 0x0028 +#define IPIPE_GCK_PIX 0x002c +#define IPIPE_RESERVED0 0x0030 + +/* Defect Correction */ +#define DPC_LUT_EN 0x0034 +#define DPC_LUT_SEL 0x0038 +#define DPC_LUT_ADR 0x003c +#define DPC_LUT_SIZ 0x0040 +#define DPC_OTF_EN 0x0044 +#define DPC_OTF_TYP 0x0048 +#define DPC_OTF_2D_THR_R 0x004c +#define DPC_OTF_2D_THR_GR 0x0050 +#define DPC_OTF_2D_THR_GB 0x0054 +#define DPC_OTF_2D_THR_B 0x0058 +#define DPC_OTF_2C_THR_R 0x005c +#define DPC_OTF_2C_THR_GR 0x0060 +#define DPC_OTF_2C_THR_GB 0x0064 +#define DPC_OTF_2C_THR_B 0x0068 +#define DPC_OTF_3_SHF 0x006c +#define DPC_OTF_3D_THR 0x0070 +#define DPC_OTF_3D_SLP 0x0074 +#define DPC_OTF_3D_MIN 0x0078 +#define DPC_OTF_3D_MAX 0x007c +#define DPC_OTF_3C_THR 0x0080 +#define DPC_OTF_3C_SLP 0x0084 +#define DPC_OTF_3C_MIN 0x0088 +#define DPC_OTF_3C_MAX 0x008c + +/* Lense Shading Correction */ +#define LSC_VOFT 0x90 +#define LSC_VA2 0x94 +#define LSC_VA1 0x98 +#define LSC_VS 0x9c +#define LSC_HOFT 0xa0 +#define LSC_HA2 0xa4 +#define LSC_HA1 0xa8 +#define LSC_HS 0xac +#define LSC_GAIN_R 0xb0 +#define LSC_GAIN_GR 0xb4 +#define LSC_GAIN_GB 0xb8 +#define LSC_GAIN_B 0xbc +#define LSC_OFT_R 0xc0 +#define LSC_OFT_GR 0xc4 +#define LSC_OFT_GB 0xc8 +#define LSC_OFT_B 0xcc +#define LSC_SHF 0xd0 +#define LSC_MAX 0xd4 + +/* Noise Filter 1. Ofsets from start address given */ +#define D2F_1ST 0xd8 +#define D2F_EN 0x0 +#define D2F_TYP 0x4 +#define D2F_THR 0x8 +#define D2F_STR 0x28 +#define D2F_SPR 0x48 +#define D2F_EDG_MIN 0x68 +#define D2F_EDG_MAX 0x6c + +/* Noise Filter 2 */ +#define D2F_2ND 0x148 + +/* GIC */ +#define GIC_EN 0x1b8 +#define GIC_TYP 0x1bc +#define GIC_GAN 0x1c0 +#define GIC_NFGAN 0x1c4 +#define GIC_THR 0x1c8 +#define GIC_SLP 0x1cc + +/* White Balance */ +#define WB2_OFT_R 0x1d0 +#define WB2_OFT_GR 0x1d4 +#define WB2_OFT_GB 0x1d8 +#define WB2_OFT_B 0x1dc +#define WB2_WGN_R 0x1e0 +#define WB2_WGN_GR 0x1e4 +#define WB2_WGN_GB 0x1e8 +#define WB2_WGN_B 0x1ec + +/* CFA interpolation */ +#define CFA_MODE 0x1f0 +#define CFA_2DIR_HPF_THR 0x1f4 +#define CFA_2DIR_HPF_SLP 0x1f8 +#define CFA_2DIR_MIX_THR 0x1fc +#define CFA_2DIR_MIX_SLP 0x200 +#define CFA_2DIR_DIR_THR 0x204 +#define CFA_2DIR_DIR_SLP 0x208 +#define CFA_2DIR_NDWT 0x20c +#define CFA_MONO_HUE_FRA 0x210 +#define CFA_MONO_EDG_THR 0x214 +#define CFA_MONO_THR_MIN 0x218 +#define CFA_MONO_THR_SLP 0x21c +#define CFA_MONO_SLP_MIN 0x220 +#define CFA_MONO_SLP_SLP 0x224 +#define CFA_MONO_LPWT 0x228 + +/* RGB to RGB conversiona - 1st */ +#define RGB1_MUL_BASE 0x22c +/* Offsets from base */ +#define RGB_MUL_RR 0x0 +#define RGB_MUL_GR 0x4 +#define RGB_MUL_BR 0x8 +#define RGB_MUL_RG 0xc +#define RGB_MUL_GG 0x10 +#define RGB_MUL_BG 0x14 +#define RGB_MUL_RB 0x18 +#define RGB_MUL_GB 0x1c +#define RGB_MUL_BB 0x20 +#define RGB_OFT_OR 0x24 +#define RGB_OFT_OG 0x28 +#define RGB_OFT_OB 0x2c + +/* Gamma */ +#define GMM_CFG 0x25c + +/* RGB to RGB conversiona - 2nd */ +#define RGB2_MUL_BASE 0x260 + +/* 3D LUT */ +#define D3LUT_EN 0x290 + +/* RGB to YUV(YCbCr) conversion */ +#define YUV_ADJ 0x294 +#define YUV_MUL_RY 0x298 +#define YUV_MUL_GY 0x29c +#define YUV_MUL_BY 0x2a0 +#define YUV_MUL_RCB 0x2a4 +#define YUV_MUL_GCB 0x2a8 +#define YUV_MUL_BCB 0x2ac +#define YUV_MUL_RCR 0x2b0 +#define YUV_MUL_GCR 0x2b4 +#define YUV_MUL_BCR 0x2b8 +#define YUV_OFT_Y 0x2bc +#define YUV_OFT_CB 0x2c0 +#define YUV_OFT_CR 0x2c4 +#define YUV_PHS 0x2c8 + +/* Global Brightness and Contrast */ +#define GBCE_EN 0x2cc +#define GBCE_TYP 0x2d0 + +/* Edge Enhancer */ +#define YEE_EN 0x2d4 +#define YEE_TYP 0x2d8 +#define YEE_SHF 0x2dc +#define YEE_MUL_00 0x2e0 +#define YEE_MUL_01 0x2e4 +#define YEE_MUL_02 0x2e8 +#define YEE_MUL_10 0x2ec +#define YEE_MUL_11 0x2f0 +#define YEE_MUL_12 0x2f4 +#define YEE_MUL_20 0x2f8 +#define YEE_MUL_21 0x2fc +#define YEE_MUL_22 0x300 +#define YEE_THR 0x304 +#define YEE_E_GAN 0x308 +#define YEE_E_THR1 0x30c +#define YEE_E_THR2 0x310 +#define YEE_G_GAN 0x314 +#define YEE_G_OFT 0x318 + +/* Chroma Artifact Reduction */ +#define CAR_EN 0x31c +#define CAR_TYP 0x320 +#define CAR_SW 0x324 +#define CAR_HPF_TYP 0x328 +#define CAR_HPF_SHF 0x32c +#define CAR_HPF_THR 0x330 +#define CAR_GN1_GAN 0x334 +#define CAR_GN1_SHF 0x338 +#define CAR_GN1_MIN 0x33c +#define CAR_GN2_GAN 0x340 +#define CAR_GN2_SHF 0x344 +#define CAR_GN2_MIN 0x348 + +/* Chroma Gain Suppression */ +#define CGS_EN 0x34c +#define CGS_GN1_L_THR 0x350 +#define CGS_GN1_L_GAN 0x354 +#define CGS_GN1_L_SHF 0x358 +#define CGS_GN1_L_MIN 0x35c +#define CGS_GN1_H_THR 0x360 +#define CGS_GN1_H_GAN 0x364 +#define CGS_GN1_H_SHF 0x368 +#define CGS_GN1_H_MIN 0x36c +#define CGS_GN2_L_THR 0x370 +#define CGS_GN2_L_GAN 0x374 +#define CGS_GN2_L_SHF 0x378 +#define CGS_GN2_L_MIN 0x37c + +/* Resizer */ +#define RSZ_SRC_EN 0x0 +#define RSZ_SRC_MODE 0x4 +#define RSZ_SRC_FMT0 0x8 +#define RSZ_SRC_FMT1 0xc +#define RSZ_SRC_VPS 0x10 +#define RSZ_SRC_VSZ 0x14 +#define RSZ_SRC_HPS 0x18 +#define RSZ_SRC_HSZ 0x1c +#define RSZ_DMA_RZA 0x20 +#define RSZ_DMA_RZB 0x24 +#define RSZ_DMA_STA 0x28 +#define RSZ_GCK_MMR 0x2c +#define RSZ_RESERVED0 0x30 +#define RSZ_GCK_SDR 0x34 +#define RSZ_IRQ_RZA 0x38 +#define RSZ_IRQ_RZB 0x3c +#define RSZ_YUV_Y_MIN 0x40 +#define RSZ_YUV_Y_MAX 0x44 +#define RSZ_YUV_C_MIN 0x48 +#define RSZ_YUV_C_MAX 0x4c +#define RSZ_YUV_PHS 0x50 +#define RSZ_SEQ 0x54 + +/* Resizer Rescale Parameters */ +#define RSZ_EN_A 0x58 +#define RSZ_EN_B 0xe8 +/* offset of the registers to be added with base register of + either RSZ0 or RSZ1 +*/ +#define RSZ_MODE 0x4 +#define RSZ_420 0x8 +#define RSZ_I_VPS 0xc +#define RSZ_I_HPS 0x10 +#define RSZ_O_VSZ 0x14 +#define RSZ_O_HSZ 0x18 +#define RSZ_V_PHS_Y 0x1c +#define RSZ_V_PHS_C 0x20 +#define RSZ_V_DIF 0x24 +#define RSZ_V_TYP 0x28 +#define RSZ_V_LPF 0x2c +#define RSZ_H_PHS 0x30 +#define RSZ_H_PHS_ADJ 0x34 +#define RSZ_H_DIF 0x38 +#define RSZ_H_TYP 0x3c +#define RSZ_H_LPF 0x40 +#define RSZ_DWN_EN 0x44 +#define RSZ_DWN_AV 0x48 + +/* Resizer RGB Conversion Parameters */ +#define RSZ_RGB_EN 0x4c +#define RSZ_RGB_TYP 0x50 +#define RSZ_RGB_BLD 0x54 + +/* Resizer External Memory Parameters */ +#define RSZ_SDR_Y_BAD_H 0x58 +#define RSZ_SDR_Y_BAD_L 0x5c +#define RSZ_SDR_Y_SAD_H 0x60 +#define RSZ_SDR_Y_SAD_L 0x64 +#define RSZ_SDR_Y_OFT 0x68 +#define RSZ_SDR_Y_PTR_S 0x6c +#define RSZ_SDR_Y_PTR_E 0x70 +#define RSZ_SDR_C_BAD_H 0x74 +#define RSZ_SDR_C_BAD_L 0x78 +#define RSZ_SDR_C_SAD_H 0x7c +#define RSZ_SDR_C_SAD_L 0x80 +#define RSZ_SDR_C_OFT 0x84 +#define RSZ_SDR_C_PTR_S 0x88 +#define RSZ_SDR_C_PTR_E 0x8c + +/* Macro for resizer */ +#define IPIPE_RESIZER_A(i) (RSZ_IOBASE_VADDR + RSZ_EN_A + i) +#define IPIPE_RESIZER_B(i) (RSZ_IOBASE_VADDR + RSZ_EN_B + i) + +#define RSZ_YUV_Y_MIN 0x40 +#define RSZ_YUV_Y_MAX 0x44 +#define RSZ_YUV_C_MIN 0x48 +#define RSZ_YUV_C_MAX 0x4c + +#define IPIPE_GCK_MMR_DEFAULT 1 +#define IPIPE_GCK_PIX_DEFAULT 0xe +#define RSZ_GCK_MMR_DEFAULT 1 +#define RSZ_GCK_SDR_DEFAULT 1 + +/* Below defines for masks and shifts */ +#define COLPAT_EE_SHIFT 0 +#define COLPAT_EO_SHIFT 2 +#define COLPAT_OE_SHIFT 4 +#define COLPAT_OO_SHIFT 6 + +/* LUTDPC */ +#define LUTDPC_TBL_256_EN 0 +#define LUTDPC_INF_TBL_EN 1 +#define LUT_DPC_START_ADDR 0 +#define LUT_DPC_H_POS_MASK 0x1fff +#define LUT_DPC_V_POS_MASK 0x1fff +#define LUT_DPC_V_POS_SHIFT 13 +#define LUT_DPC_CORR_METH_SHIFT 26 +#define LUT_DPC_MAX_SIZE 256 +#define LUT_DPC_SIZE_MASK 0x3ff + +/* OTFDPC */ +#define OTFDPC_DPC2_THR_MASK 0xfff +#define OTF_DET_METHOD_SHIFT 1 +#define OTF_DPC3_0_SHF_MASK 3 +#define OTF_DPC3_0_THR_SHIFT 6 +#define OTF_DPC3_0_THR_MASK 0x3f +#define OTF_DPC3_0_SLP_MASK 0x3f +#define OTF_DPC3_0_DET_MASK 0xfff +#define OTF_DPC3_0_CORR_MASK 0xfff + +/* NF (D2F) */ +#define D2F_SPR_VAL_MASK 0x1f +#define D2F_SPR_VAL_SHIFT 0 +#define D2F_SHFT_VAL_MASK 3 +#define D2F_SHFT_VAL_SHIFT 5 +#define D2F_SAMPLE_METH_SHIFT 7 +#define D2F_APPLY_LSC_GAIN_SHIFT 8 +#define D2F_USE_SPR_REG_VAL 0 +#define D2F_STR_VAL_MASK 0x1f +#define D2F_THR_VAL_MASK 0x3ff +#define D2F_EDGE_DET_THR_MASK 0x7ff + +/* Green Imbalance Correction */ +#define GIC_TYP_SHIFT 0 +#define GIC_THR_SEL_SHIFT 1 +#define GIC_APPLY_LSC_GAIN_SHIFT 2 +#define GIC_GAIN_MASK 0xff +#define GIC_THR_MASK 0xfff +#define GIC_SLOPE_MASK 0xfff +#define GIC_NFGAN_INT_MASK 7 +#define GIC_NFGAN_DECI_MASK 0x1f + +/* WB */ +#define WB_OFFSET_MASK 0xfff +#define WB_GAIN_INT_MASK 0xf +#define WB_GAIN_DECI_MASK 0x1ff + +/* CFA */ +#define CFA_HPF_THR_2DIR_MASK 0x1fff +#define CFA_HPF_SLOPE_2DIR_MASK 0x3ff +#define CFA_HPF_MIX_THR_2DIR_MASK 0x1fff +#define CFA_HPF_MIX_SLP_2DIR_MASK 0x3ff +#define CFA_DIR_THR_2DIR_MASK 0x3ff +#define CFA_DIR_SLP_2DIR_MASK 0x7f +#define CFA_ND_WT_2DIR_MASK 0x3f +#define CFA_DAA_HUE_FRA_MASK 0x3f +#define CFA_DAA_EDG_THR_MASK 0xff +#define CFA_DAA_THR_MIN_MASK 0x3ff +#define CFA_DAA_THR_SLP_MASK 0x3ff +#define CFA_DAA_SLP_MIN_MASK 0x3ff +#define CFA_DAA_SLP_SLP_MASK 0x3ff +#define CFA_DAA_LP_WT_MASK 0x3f + +/* RGB2RGB */ +#define RGB2RGB_1_OFST_MASK 0x1fff +#define RGB2RGB_1_GAIN_INT_MASK 0xf +#define RGB2RGB_GAIN_DECI_MASK 0xff +#define RGB2RGB_2_OFST_MASK 0x7ff +#define RGB2RGB_2_GAIN_INT_MASK 0x7 + +/* Gamma */ +#define GAMMA_BYPR_SHIFT 0 +#define GAMMA_BYPG_SHIFT 1 +#define GAMMA_BYPB_SHIFT 2 +#define GAMMA_TBL_SEL_SHIFT 4 +#define GAMMA_TBL_SIZE_SHIFT 5 +#define GAMMA_MASK 0x3ff +#define GAMMA_SHIFT 10 + +/* 3D LUT */ +#define D3_LUT_ENTRY_MASK 0x3ff +#define D3_LUT_ENTRY_R_SHIFT 20 +#define D3_LUT_ENTRY_G_SHIFT 10 +#define D3_LUT_ENTRY_B_SHIFT 0 + +/* Lumina adj */ +#define LUM_ADJ_CONTR_SHIFT 0 +#define LUM_ADJ_BRIGHT_SHIFT 8 + +/* RGB2YCbCr */ +#define RGB2YCBCR_OFST_MASK 0x7ff +#define RGB2YCBCR_COEF_INT_MASK 0xf +#define RGB2YCBCR_COEF_DECI_MASK 0xff + +/* GBCE */ +#define GBCE_Y_VAL_MASK 0xff +#define GBCE_GAIN_VAL_MASK 0x3ff +#define GBCE_ENTRY_SHIFT 10 + +/* Edge Enhancements */ +#define YEE_HALO_RED_EN_SHIFT 1 +#define YEE_HPF_SHIFT_MASK 0xf +#define YEE_COEF_MASK 0x3ff +#define YEE_THR_MASK 0x3f +#define YEE_ES_GAIN_MASK 0xfff +#define YEE_ES_THR1_MASK 0xfff +#define YEE_ENTRY_SHIFT 9 +#define YEE_ENTRY_MASK 0x1ff + +/* CAR */ +#define CAR_MF_THR 0xff +#define CAR_SW1_SHIFT 8 +#define CAR_GAIN1_SHFT_MASK 7 +#define CAR_GAIN_MIN_MASK 0x1ff +#define CAR_GAIN2_SHFT_MASK 0xf +#define CAR_HPF_SHIFT_MASK 3 + +/* CGS */ +#define CAR_SHIFT_MASK 3 + +/* Resizer */ +#define RSZ_BYPASS_SHIFT 1 +#define RSZ_SRC_IMG_FMT_SHIFT 1 +#define RSZ_SRC_Y_C_SEL_SHIFT 2 +#define IPIPE_RSZ_VPS_MASK 0xffff +#define IPIPE_RSZ_HPS_MASK 0xffff +#define IPIPE_RSZ_VSZ_MASK 0x1fff +#define IPIPE_RSZ_HSZ_MASK 0x1fff +#define RSZ_HPS_MASK 0x1fff +#define RSZ_VPS_MASK 0x1fff +#define RSZ_O_HSZ_MASK 0x1fff +#define RSZ_O_VSZ_MASK 0x1fff +#define RSZ_V_PHS_MASK 0x3fff +#define RSZ_V_DIF_MASK 0x3fff + +#define RSZA_H_FLIP_SHIFT 0 +#define RSZA_V_FLIP_SHIFT 1 +#define RSZB_H_FLIP_SHIFT 2 +#define RSZB_V_FLIP_SHIFT 3 +#define RSZ_A 0 +#define RSZ_B 1 +#define RSZ_CEN_SHIFT 1 +#define RSZ_YEN_SHIFT 0 +#define RSZ_TYP_Y_SHIFT 0 +#define RSZ_TYP_C_SHIFT 1 +#define RSZ_LPF_INT_MASK 0x3f +#define RSZ_LPF_INT_MASK 0x3f +#define RSZ_LPF_INT_C_SHIFT 6 +#define RSZ_H_PHS_MASK 0x3fff +#define RSZ_H_DIF_MASK 0x3fff +#define RSZ_DIFF_DOWN_THR 256 +#define RSZ_DWN_SCALE_AV_SZ_V_SHIFT 3 +#define RSZ_DWN_SCALE_AV_SZ_MASK 7 +#define RSZ_RGB_MSK1_SHIFT 2 +#define RSZ_RGB_MSK0_SHIFT 1 +#define RSZ_RGB_TYP_SHIFT 0 +#define RSZ_RGB_ALPHA_MASK 0xff + +static inline u32 regr_ip(u32 offset) +{ + return readl(IPIPE_IOBASE_VADDR + offset); +} + +static inline u32 regw_ip(u32 val, u32 offset) +{ + writel(val, IPIPE_IOBASE_VADDR + offset); + + return val; +} + +static inline u32 r_ip_table(u32 offset) +{ + return readl(IPIPE_INT_TABLE_IOBASE_VADDR + offset); +} + +static inline u32 w_ip_table(u32 val, u32 offset) +{ + writel(val, IPIPE_INT_TABLE_IOBASE_VADDR + offset); + + return val; +} + +static inline u32 regr_rsz(u32 offset) +{ + return readl(RSZ_IOBASE_VADDR + offset); +} + +static inline u32 regw_rsz(u32 val, u32 offset) +{ + writel(val, RSZ_IOBASE_VADDR + offset); + + return val; +} + +#endif /* End of #ifdef _DM365_IPIPE_HW_H */ -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:44:31 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 16:14:31 +0530 Subject: [RESEND RFC PATCH v4 05/15] davinci: vpfe: add ccdc driver with media controller interface In-Reply-To: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> References: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321526681-22574-6-git-send-email-manjunath.hadli@ti.com> Add the CCDC driver for davinci Dm3XX SoCs. The driver supports CCDC as a media entity with 2 pads - 1 input and 1 output. The driver implements streaming support and subdev interface. The ccdc supports bayer and YUV formats. Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/ccdc_hw_device.h | 12 +- drivers/media/video/davinci/vpfe_ccdc.c | 817 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpfe_ccdc.h | 86 +++ 3 files changed, 906 insertions(+), 9 deletions(-) create mode 100644 drivers/media/video/davinci/vpfe_ccdc.c create mode 100644 drivers/media/video/davinci/vpfe_ccdc.h diff --git a/drivers/media/video/davinci/ccdc_hw_device.h b/drivers/media/video/davinci/ccdc_hw_device.h index 86b9b35..118c108 100644 --- a/drivers/media/video/davinci/ccdc_hw_device.h +++ b/drivers/media/video/davinci/ccdc_hw_device.h @@ -20,11 +20,10 @@ #ifndef _CCDC_HW_DEVICE_H #define _CCDC_HW_DEVICE_H -#ifdef __KERNEL__ #include #include -#include -#include +#include +#include "ccdc_types.h" /* * ccdc hw operations @@ -57,7 +56,7 @@ struct ccdc_hw_ops { */ int (*get_params) (void *params); /* Pointer to function to configure ccdc */ - int (*configure) (void); + int (*configure) (int mode); /* Pointer to function to set buffer type */ int (*set_buftype) (enum ccdc_buftype buf_type); @@ -102,9 +101,4 @@ struct ccdc_hw_device { struct ccdc_hw_ops hw_ops; }; -/* Used by CCDC module to register & unregister with vpfe capture driver */ -int vpfe_register_ccdc_device(struct ccdc_hw_device *dev); -void vpfe_unregister_ccdc_device(struct ccdc_hw_device *dev); - -#endif #endif diff --git a/drivers/media/video/davinci/vpfe_ccdc.c b/drivers/media/video/davinci/vpfe_ccdc.c new file mode 100644 index 0000000..5082f77 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_ccdc.c @@ -0,0 +1,817 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Nagabhushana Netagunte + */ +#include +#include +#include +#include +#include +#include "vpfe_capture.h" + +#include "ccdc_hw_device.h" + +#define MAX_WIDTH 4096 +#define MAX_HEIGHT 4096 + +static const unsigned int ccdc_fmts[] = { + V4L2_MBUS_FMT_YUYV8_2X8, + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_YUYV8_1X16, + V4L2_MBUS_FMT_YUYV10_1X20, + V4L2_MBUS_FMT_SBGGR10_1X10, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, +#ifdef CONFIG_ARCH_DAVINCI_DM365 + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, +#endif +}; + +/* + * CCDC helper functions + */ +/* get field id in ccdc hardware */ +enum v4l2_field ccdc_get_fid(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ccdc_device *ccdc = &vpfe_dev->vpfe_ccdc; + struct ccdc_hw_device *ccdc_dev = ccdc->ccdc_dev; + + return ccdc_dev->hw_ops.getfid(); +} + +/* Retrieve active or try pad format based on query */ +static struct v4l2_mbus_framefmt * +__ccdc_get_format(struct vpfe_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_subdev_format fmt; + + fmt.pad = pad; + fmt.which = which; + + return v4l2_subdev_get_try_format(fh, pad); + } else + return &ccdc->formats[pad]; +} + +/* configure format in ccdc hardware */ +static int vpfe_config_ccdc_format(struct vpfe_device *vpfe_dev, + unsigned int pad) +{ + struct ccdc_hw_device *ccdc_dev = vpfe_dev->vpfe_ccdc.ccdc_dev; + struct vpfe_ccdc_device *vpfe_ccdc = &vpfe_dev->vpfe_ccdc; + enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; + struct v4l2_pix_format format; + int ret = 0; + + v4l2_fill_pix_format(&format, &vpfe_dev->vpfe_ccdc.formats[pad]); + mbus_to_pix(&vpfe_dev->vpfe_ccdc.formats[pad], &format); + + if (ccdc_dev->hw_ops.set_pixel_format( + format.pixelformat) < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "couldn't set pix format in ccdc\n"); + return -EINVAL; + } + + /* call for s_crop will override these values */ + vpfe_ccdc->crop.left = 0; + vpfe_ccdc->crop.top = 0; + vpfe_ccdc->crop.width = format.width; + vpfe_ccdc->crop.height = format.height; + + /* configure the image window */ + ccdc_dev->hw_ops.set_image_window(&vpfe_ccdc->crop); + + switch (vpfe_dev->vpfe_ccdc.formats[pad].field) { + case V4L2_FIELD_INTERLACED: + /* do nothing, since it is default */ + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_INTERLEAVED); + break; + case V4L2_FIELD_NONE: + frm_fmt = CCDC_FRMFMT_PROGRESSIVE; + /* buffer type only applicable for interlaced scan */ + break; + case V4L2_FIELD_SEQ_TB: + ret = ccdc_dev->hw_ops.set_buftype( + CCDC_BUFTYPE_FLD_SEPARATED); + break; + default: + return -EINVAL; + } + + /* set the frame format */ + if (!ret) + ret = ccdc_dev->hw_ops.set_frame_format(frm_fmt); + + return ret; +} + +/* + * ccdc_try_format - Try video format on a pad + * @ccdc: VPFE CCDC device + * @fh : V4L2 subdev file handle + * @pad: Pad number + * @fmt: Format + */ +static void +ccdc_try_format(struct vpfe_ccdc_device *vpfe_ccdc, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + unsigned int width = fmt->format.width; + unsigned int height = fmt->format.height; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ccdc_fmts); i++) { + if (fmt->format.code == ccdc_fmts[i]) + break; + } + + /* If not found, use YUYV8_2x8 as default */ + if (i >= ARRAY_SIZE(ccdc_fmts)) + fmt->format.code = V4L2_MBUS_FMT_YUYV8_2X8; + + /* Clamp the size. */ + fmt->format.width = clamp_t(u32, width, 32, MAX_WIDTH); + fmt->format.height = clamp_t(u32, height, 32, MAX_HEIGHT); + + /* The data formatter truncates the number of horizontal output + * pixels to a multiple of 16. To avoid clipping data, allow + * callers to request an output size bigger than the input size + * up to the nearest multiple of 16. + */ + if (fmt->pad == CCDC_PAD_SOURCE) + fmt->format.width &= ~15; +} + +/* + * ccdc_buffer_isr - CCDC module non-progressive buffer scheduling isr + * @ccdc: CCDC device pointer + * + */ +void ccdc_buffer_isr(struct vpfe_ccdc_device *ccdc) +{ + struct vpfe_video_device *video = &ccdc->video_out; + struct ccdc_hw_device *ccdc_dev = ccdc->ccdc_dev; + enum v4l2_field field; + int fid; + + if (!video->started) + return; + + field = video->fmt.fmt.pix.field; + + /* reset sbl overblow bit */ + if (ccdc_dev->hw_ops.reset != NULL) + ccdc_dev->hw_ops.reset(); + + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + if (video->cur_frm != video->next_frm) + vpfe_process_buffer_complete(video); + return; + } + + /* interlaced or TB capture check which field we + * are in hardware + */ + fid = ccdc_dev->hw_ops.getfid(); + + /* switch the software maintained field id */ + video->field_id ^= 1; + if (fid == video->field_id) { + /* we are in-sync here,continue */ + if (fid == 0) { + /* + * One frame is just being captured. If the + * next frame is available, release the current + * frame and move on + */ + if (video->cur_frm != video->next_frm) + vpfe_process_buffer_complete(video); + /* + * based on whether the two fields are stored + * interleavely or separately in memory, + * reconfigure the CCDC memory address + */ + if (field == V4L2_FIELD_SEQ_TB) + vpfe_schedule_bottom_field(video); + + return; + } + /* + * if one field is just being captured configure + * the next frame get the next frame from the + * empty queue if no frame is available hold on + * to the current buffer + */ + spin_lock(&video->dma_queue_lock); + if (!list_empty(&video->dma_queue) && + video->cur_frm == video->next_frm) + vpfe_schedule_next_buffer(video); + spin_unlock(&video->dma_queue_lock); + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + video->field_id = fid; + } +} + +/* + * ccdc_vidint1_isr - CCDC module progressive buffer scheduling isr + * @ccdc: CCDC device pointer + * + */ +void ccdc_vidint1_isr(struct vpfe_ccdc_device *ccdc) +{ + struct vpfe_video_device *video = &ccdc->video_out; + + if (!video->started) + return; + + spin_lock(&video->dma_queue_lock); + if (video->fmt.fmt.pix.field == V4L2_FIELD_NONE && + !list_empty(&video->dma_queue) && video->cur_frm == video->next_frm) + vpfe_schedule_next_buffer(video); + + spin_unlock(&video->dma_queue_lock); +} + +/* + * VPFE video operations + */ + +static void ccdc_video_queue(struct vpfe_device *vpfe_dev, unsigned long addr) +{ + struct vpfe_ccdc_device *vpfe_ccdc = &vpfe_dev->vpfe_ccdc; + struct ccdc_hw_device *ccdc_dev = vpfe_ccdc->ccdc_dev; + + ccdc_dev->hw_ops.setfbaddr(addr); +} + +static const struct vpfe_video_operations ccdc_video_ops = { + .queue = ccdc_video_queue, +}; + + +/* + * V4L2 subdev operations + */ + +/* + * ccdc_ioctl - CCDC module private ioctl's + * @sd: VPFE CCDC V4L2 subdevice + * @cmd: ioctl command + * @arg: ioctl argument + * + * Return 0 on success or a negative error code otherwise. + */ +static long ccdc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct vpfe_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct ccdc_hw_device *ccdc_dev = ccdc->ccdc_dev; + int ret; + + switch (cmd) { + case VPFE_CMD_S_CCDC_RAW_PARAMS: + ret = ccdc_dev->hw_ops.set_params(arg); + break; + case VPFE_CMD_G_CCDC_RAW_PARAMS: + if (!ccdc_dev->hw_ops.get_params) { + ret = -EINVAL; + break; + } + ret = ccdc_dev->hw_ops.get_params(arg); + break; + + default: + ret = -ENOIOCTLCMD; + } + + return ret; +} + +/* + * ccdc_set_stream - Enable/Disable streaming on the CCDC module + * @sd: VPFE CCDC V4L2 subdevice + * @enable: Enable/disable stream + */ +static int ccdc_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct ccdc_hw_device *ccdc_dev = ccdc->ccdc_dev; + int ret; + + if (enable) { + ret = ccdc_dev->hw_ops.configure( + (ccdc->output == CCDC_OUTPUT_MEMORY) ? 0 : 1); + if (ret) + return ret; + + if ((ccdc_dev->hw_ops.enable_out_to_sdram) && + (ccdc->output == CCDC_OUTPUT_MEMORY)) + ccdc_dev->hw_ops.enable_out_to_sdram(1); + + ccdc_dev->hw_ops.enable(1); + } else { + + ccdc_dev->hw_ops.enable(0); + + if (ccdc_dev->hw_ops.enable_out_to_sdram) + ccdc_dev->hw_ops.enable_out_to_sdram(0); + } + + return 0; +} + +/* +* ccdc_set_format - set format on pad +* @sd : VPFE ccdc device +* @fh : V4L2 subdev file handle +* @fmt : pointer to v4l2 subdev format structure +* +* Return 0 on success or -EINVAL if format or pad is invalid +*/ +static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(ccdc); + struct v4l2_mbus_framefmt *format; + + format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + ccdc_try_format(ccdc, fh, fmt); + memcpy(format, &fmt->format, sizeof(*format)); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == CCDC_PAD_SOURCE) + return vpfe_config_ccdc_format(vpfe_dev, fmt->pad); + + return 0; +} + +/* + * ccdc_get_format - Retrieve the video format on a pad + * @sd : VPFE CCDC V4L2 subdevice + * @fh : V4L2 subdev file handle + * @fmt: Format + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_ccdc_device *vpfe_ccdc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __ccdc_get_format(vpfe_ccdc, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + memcpy(&fmt->format, format, sizeof(fmt->format)); + + return 0; +} + +/* + * ccdc_enum_frame_size - enum frame sizes on pads + * @sd: VPFE ccdc V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_frame_size_enum structure + */ +static int ccdc_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct v4l2_subdev_format format; + + if (fse->index != 0) + return -EINVAL; + + format.pad = fse->pad; + format.format.code = fse->code; + format.format.width = 1; + format.format.height = 1; + format.which = V4L2_SUBDEV_FORMAT_TRY; + ccdc_try_format(ccdc, fh, &format); + fse->min_width = format.format.width; + fse->min_height = format.format.height; + + if (format.format.code != fse->code) + return -EINVAL; + + format.pad = fse->pad; + format.format.code = fse->code; + format.format.width = -1; + format.format.height = -1; + format.which = V4L2_SUBDEV_FORMAT_TRY; + ccdc_try_format(ccdc, fh, &format); + fse->max_width = format.format.width; + fse->max_height = format.format.height; + + return 0; +} + +/* + * ccdc_enum_mbus_code - enum mbus codes for pads + * @sd: VPFE ccdc V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case CCDC_PAD_SINK: + case CCDC_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(ccdc_fmts)) + return -EINVAL; + + code->code = ccdc_fmts[code->index]; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * ccdc_pad_set_crop - set crop rectangle on pad + * @sd: VPFE ccdc V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Return 0 on success, -EINVAL if pad is invalid + */ +static int ccdc_pad_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct vpfe_ccdc_device *vpfe_ccdc = v4l2_get_subdevdata(sd); + struct ccdc_hw_device *ccdc_dev = vpfe_ccdc->ccdc_dev; + struct v4l2_mbus_framefmt *format; + + /* check wether its a valid pad */ + if (crop->pad != CCDC_PAD_SINK) + return -EINVAL; + + format = __ccdc_get_format(vpfe_ccdc, fh, crop->pad, crop->which); + if (format == NULL) + return -EINVAL; + + /* check wether crop rect is within limits */ + if (crop->rect.top < 0 || crop->rect.left < 0 || + (crop->rect.left + crop->rect.width > + vpfe_ccdc->formats[CCDC_PAD_SINK].width) || + (crop->rect.top + crop->rect.height > + vpfe_ccdc->formats[CCDC_PAD_SINK].height)) { + crop->rect.left = 0; + crop->rect.top = 0; + crop->rect.width = format->width; + crop->rect.height = format->height; + } + + /* adjust the width to 16 pixel boundry */ + crop->rect.width = ((crop->rect.width + 15) & ~0xf); + + vpfe_ccdc->crop = crop->rect; + + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + ccdc_dev->hw_ops.set_image_window(&vpfe_ccdc->crop); + } else { + struct v4l2_rect *rect; + + rect = v4l2_subdev_get_try_crop(fh, CCDC_PAD_SINK); + memcpy(rect, &vpfe_ccdc->crop, sizeof(*rect)); + } + + return 0; +} + +/* + * ccdc_pad_get_crop - get crop rectangle on pad + * @sd: VPFE ccdc V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + * + * Return 0 on success, -EINVAL if pad is invalid + */ +static int ccdc_pad_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct vpfe_ccdc_device *vpfe_ccdc = v4l2_get_subdevdata(sd); + + /* check wether its a valid pad */ + if (crop->pad != CCDC_PAD_SINK) + return -EINVAL; + + if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { + struct v4l2_rect *rect; + rect = v4l2_subdev_get_try_crop(fh, CCDC_PAD_SINK); + memcpy(&crop->rect, rect, sizeof(*rect)); + } else { + crop->rect = vpfe_ccdc->crop; + } + + return 0; +} + +/* + * ccdc_init_formats - Initialize formats on all pads + * @sd: VPFE ccdc V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int ccdc_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct v4l2_subdev_format format; + struct v4l2_subdev_crop crop; + + memset(&format, 0, sizeof(format)); + format.pad = CCDC_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SBGGR10_1X10; + format.format.width = MAX_WIDTH; + format.format.height = MAX_HEIGHT; + ccdc_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = CCDC_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SBGGR10_1X10; + format.format.width = MAX_WIDTH; + format.format.height = MAX_HEIGHT; + ccdc_set_format(sd, fh, &format); + + memset(&crop, 0, sizeof(crop)); + crop.pad = CCDC_PAD_SINK; + crop.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + crop.rect.width = MAX_WIDTH; + crop.rect.height = MAX_HEIGHT; + ccdc_pad_set_crop(sd, fh, &crop); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops ccdc_v4l2_core_ops = { + .ioctl = ccdc_ioctl, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_file_ops ccdc_v4l2_file_ops = { + .open = ccdc_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops ccdc_v4l2_video_ops = { + .s_stream = ccdc_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = { + .enum_mbus_code = ccdc_enum_mbus_code, + .enum_frame_size = ccdc_enum_frame_size, + .get_fmt = ccdc_get_format, + .set_fmt = ccdc_set_format, + .set_crop = ccdc_pad_set_crop, + .get_crop = ccdc_pad_get_crop, +}; + +/* v4l2 subdev operations */ +static const struct v4l2_subdev_ops ccdc_v4l2_ops = { + .core = &ccdc_v4l2_core_ops, + .file = &ccdc_v4l2_file_ops, + .video = &ccdc_v4l2_video_ops, + .pad = &ccdc_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * ccdc_link_setup - Setup CCDC connections + * @entity: CCDC media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int ccdc_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + + switch (local->index | media_entity_type(remote->entity)) { + case CCDC_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* read from decoder/sensor */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + ccdc->input = CCDC_INPUT_NONE; + break; + } + + if (ccdc->input != CCDC_INPUT_NONE) + return -EBUSY; + + ccdc->input = CCDC_INPUT_PARALLEL; + + break; + + case CCDC_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + /* write to memory */ + if (flags & MEDIA_LNK_FL_ENABLED) + ccdc->output = CCDC_OUTPUT_MEMORY; + else + ccdc->output = CCDC_OUTPUT_NONE; + break; + + case CCDC_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + if (flags & MEDIA_LNK_FL_ENABLED) + ccdc->output = CCDC_OUTPUT_PREVIEWER; + else + ccdc->output = CCDC_OUTPUT_NONE; + + break; + default: + return -EINVAL; + } + + return 0; +} +static const struct media_entity_operations ccdc_media_ops = { + .link_setup = ccdc_link_setup, +}; + +/* + * vpfe_ccdc_unregister_entities - CCDC subdevs/video + * driver unregistrations. + * @ccdc - pointer to ccdc subdevice structure. + */ +void vpfe_ccdc_unregister_entities(struct vpfe_ccdc_device *ccdc) +{ + struct ccdc_hw_device *ccdc_dev = ccdc->ccdc_dev; + struct device *dev = ccdc->subdev.v4l2_dev->dev; + + vpfe_video_unregister(&ccdc->video_out); + + if (ccdc_dev->hw_ops.close) + ccdc_dev->hw_ops.close(dev); + + /* cleanup entity */ + media_entity_cleanup(&ccdc->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&ccdc->subdev); +} + +/* + * vpfe_ccdc_register_entities - CCDC subdevs/video + * driver registrations. + * @ccdc - pointer to ccdc subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +int vpfe_ccdc_register_entities(struct vpfe_ccdc_device *ccdc, + struct v4l2_device *vdev) +{ + struct ccdc_hw_device *ccdc_dev = NULL; + struct vpfe_device *vpfe_dev = to_vpfe_device(ccdc); + struct device *dev = vdev->dev; + unsigned int flags; + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &ccdc->subdev); + if (ret < 0) + return ret; + + ccdc_dev = ccdc->ccdc_dev; + + ret = ccdc_dev->hw_ops.open(dev); + if (ret) + goto out_ccdc_open; + + ret = vpfe_video_register(&ccdc->video_out, vdev); + if (ret) { + printk(KERN_ERR "failed to register ccdc video out device\n"); + goto out_video_register; + } + + ccdc->video_out.vpfe_dev = vpfe_dev; + + flags = 0; + /* connect ccdc to video node */ + ret = media_entity_create_link(&ccdc->subdev.entity, 1, + &ccdc->video_out.video_dev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + + return 0; +out_create_link: + vpfe_video_unregister(&ccdc->video_out); +out_video_register: + if (ccdc_dev->hw_ops.close) + ccdc_dev->hw_ops.close(dev); + +out_ccdc_open: + v4l2_device_unregister_subdev(&ccdc->subdev); + + return ret; +} + +/* + * vpfe_ccdc_init - Initialize V4L2 subdev and media entity + * @ccdc: VPFE CCDC module + * + * Return 0 on success and a negative error code on failure. + */ +int vpfe_ccdc_init(struct vpfe_ccdc_device *ccdc, struct platform_device *pdev) +{ + struct v4l2_subdev *sd = &ccdc->subdev; + struct media_pad *pads = &ccdc->pads[0]; + struct media_entity *me = &sd->entity; + int ret; + + if (ccdc_init(pdev)) { + printk(KERN_ERR "vpfe_ccdc_init-not supported\n"); + return -1; + } + + /* queue ops */ + ccdc->video_out.ops = &ccdc_video_ops; + + v4l2_subdev_init(sd, &ccdc_v4l2_ops); + strlcpy(sd->name, "DAVINCI CCDC", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, ccdc); + sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + sd->nevents = DAVINCI_CCDC_NEVENTS; + pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; + pads[CCDC_PAD_SOURCE].flags = MEDIA_PAD_FL_OUTPUT; + + ccdc->input = CCDC_INPUT_NONE; + ccdc->output = CCDC_OUTPUT_NONE; + + me->ops = &ccdc_media_ops; + + ret = media_entity_init(me, CCDC_PADS_NUM, pads, 0); + if (ret) + goto out_davanci_init; + ccdc->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = vpfe_video_init(&ccdc->video_out, "CCDC"); + if (ret) { + printk(KERN_ERR "failed to init ccdc-out video device\n"); + goto out_davanci_init; + } + + ccdc->ccdc_dev = get_ccdc_dev(); + + return 0; + +out_davanci_init: + ccdc_remove(pdev); + return ret; +} + +/* + * vpfe_ccdc_cleanup - CCDC module cleanup. + * @dev: Device pointer specific to the VPFE. + */ +void vpfe_ccdc_cleanup(struct platform_device *pdev) +{ + ccdc_remove(pdev); +} diff --git a/drivers/media/video/davinci/vpfe_ccdc.h b/drivers/media/video/davinci/vpfe_ccdc.h new file mode 100644 index 0000000..19af898 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_ccdc.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _VPFE_CCDC_H +#define _VPFE_CCDC_H + +#define VPFE_CMD_S_CCDC_RAW_PARAMS _IOW('V', 1, \ + struct ccdc_config_params_raw) +#define VPFE_CMD_G_CCDC_RAW_PARAMS _IOR('V', 2, \ + struct ccdc_config_params_raw) + +#ifdef CONFIG_ARCH_DAVINCI_DM365 +#include "dm365_ccdc.h" +#endif + +#ifdef CONFIG_ARCH_DAVINCI_DM355 +#include "dm355_ccdc.h" +#endif + +#ifdef CONFIG_ARCH_DAVINCI_DM644x +#include "dm644x_ccdc.h" +#endif + +#define CCDC_PAD_SINK 0 +#define CCDC_PAD_SOURCE 1 + +#define CCDC_PADS_NUM 2 + +#define DAVINCI_CCDC_NEVENTS 0 + +enum ccdc_input_entity { + CCDC_INPUT_NONE, + CCDC_INPUT_PARALLEL, +}; + +#define CCDC_OUTPUT_NONE (0) +#define CCDC_OUTPUT_MEMORY (1 << 0) +#define CCDC_OUTPUT_RESIZER (1 << 1) +#define CCDC_OUTPUT_PREVIEWER (1 << 2) + +#define CCDC_NOT_CHAINED 0 +#define CCDC_CHAINED 1 + +struct vpfe_ccdc_device { + struct v4l2_subdev subdev; + struct media_pad pads[CCDC_PADS_NUM]; + struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM]; + enum ccdc_input_entity input; + unsigned int output; + + struct ccdc_hw_device *ccdc_dev; + struct v4l2_rect crop; + + /* independent video device */ + struct vpfe_video_device video_out; +}; + +enum v4l2_field ccdc_get_fid(struct vpfe_device *vpfe_dev); +void ccdc_remove(struct platform_device *pdev); +int ccdc_init(struct platform_device *pdev); +struct ccdc_hw_device *get_ccdc_dev(void); + +void vpfe_ccdc_unregister_entities(struct vpfe_ccdc_device *ccdc); +int vpfe_ccdc_register_entities(struct vpfe_ccdc_device *ccdc, + struct v4l2_device *v4l2_dev); +int vpfe_ccdc_init(struct vpfe_ccdc_device *vpfe_ccdc, + struct platform_device *pdev); +void vpfe_ccdc_cleanup(struct platform_device *pdev); +void ccdc_vidint1_isr(struct vpfe_ccdc_device *ccdc); +void ccdc_buffer_isr(struct vpfe_ccdc_device *ccdc); + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:44:33 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 16:14:33 +0530 Subject: [RESEND RFC PATCH v4 07/15] davinci: vpfe: v4l2 capture driver with media interface In-Reply-To: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> References: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321526681-22574-8-git-send-email-manjunath.hadli@ti.com> Add the vpfe capture driver which implements media controller interface. The driver suports all the setup functionality for all all units nnamely- ccdc, previewer, resizer, h3a, aew. The driver supports both dm365 and Dm355. The driver does isr registration, v4l2 device registration, media registration and platform driver registrations. It calls the appropriate subdevs from here to cerate the appropriate subdevices and media entities. Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/vpfe_capture.c | 796 ++++++++++++++++++++++++++++ drivers/media/video/davinci/vpfe_capture.h | 99 ++++ include/media/davinci/vpfe.h | 93 ++++ 3 files changed, 988 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpfe_capture.c create mode 100644 drivers/media/video/davinci/vpfe_capture.h create mode 100644 include/media/davinci/vpfe.h diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c new file mode 100644 index 0000000..2d3ce73 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -0,0 +1,796 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors:- + * Manjunath Hadli + * Nagabhushana Netagunte + * + * Driver name : VPFE Capture driver + * VPFE Capture driver allows applications to capture and stream video + * frames on DaVinci SoCs (DM6446, DM355 etc) from a YUV source such as + * TVP5146 or Raw Bayer RGB image data from an image sensor + * such as Microns' MT9T001, MT9T031 etc. + * + * These SoCs have, in common, a Video Processing Subsystem (VPSS) that + * consists of a Video Processing Front End (VPFE) for capturing + * video/raw image data and Video Processing Back End (VPBE) for displaying + * YUV data through an in-built analog encoder or Digital LCD port. This + * driver is for capture through VPFE. A typical EVM using these SoCs have + * following high level configuration. + * + * decoder(TVP5146/ YUV/ + * MT9T001) --> Raw Bayer RGB ---> MUX -> VPFE (CCDC/ISIF) + * data input | | + * V | + * SDRAM | + * V + * Image Processor + * | + * V + * SDRAM + * The data flow happens from a decoder connected to the VPFE over a + * YUV embedded (BT.656/BT.1120) or separate sync or raw bayer rgb interface + * and to the input of VPFE through an optional MUX (if more inputs are + * to be interfaced on the EVM). The input data is first passed through + * CCDC (CCD Controller, a.k.a Image Sensor Interface, ISIF). The CCDC + * does very little or no processing on YUV data and does pre-process Raw + * Bayer RGB data through modules such as Defect Pixel Correction (DFC) + * Color Space Conversion (CSC), data gain/offset etc. After this, data + * can be written to SDRAM or can be connected to the image processing + * block such as IPIPE (on DM355/DM365 only). + * + * Features supported + * - MMAP IO + * - USERPTR IO + * - Capture using TVP5146 over BT.656 + * - Support for interfacing decoders using sub device model + * - Work with DM365 or DM355 or DM6446 CCDC to do Raw Bayer + * RGB/YUV data capture to SDRAM. + * - Chaining of Image Processor + * - SINGLE-SHOT mode + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vpfe_capture.h" + +static int debug; +static int interface; +static u32 cont_bufoffset; +static u32 cont_bufsize; +static u32 en_serializer; + +module_param(interface, bool, S_IRUGO); +module_param(debug, bool, 0644); +module_param(cont_bufoffset, uint, S_IRUGO); +module_param(cont_bufsize, uint, S_IRUGO); +module_param(en_serializer, uint, S_IRUGO); + +/** + * VPFE capture can be used for capturing video such as from TVP5146 or TVP7002 + * and for capture raw bayer data from camera sensors such as mt9p031. At this + * point there is problem in co-existence of mt9p031 and tvp5146 due to i2c + * address collision. So set the variable below from bootargs to do either video + * capture or camera capture. + * interface = 0 - video capture (from TVP514x or such), + * interface = 1 - Camera capture (from mt9p031 or such) + * Re-visit this when we fix the co-existence issue + */ +MODULE_PARM_DESC(interface, "interface 0-1 (default:0)"); +MODULE_PARM_DESC(debug, "Debug level 0-1"); +MODULE_PARM_DESC(cont_bufoffset, "Capture buffer offset (default 0)"); +MODULE_PARM_DESC(cont_bufsize, "Capture buffer size (default 0)"); +MODULE_PARM_DESC(en_serializer, "enable IPIPE serializer (default:0)"); + +MODULE_DESCRIPTION("VPFE Video for Linux Capture Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); + +/* map mbus_fmt to pixelformat */ +void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix) +{ + switch (mbus->code) { + case V4L2_MBUS_FMT_UYVY8_2X8: + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->bytesperline = pix->width * 2; + break; + case V4L2_MBUS_FMT_YUYV8_2X8: + pix->pixelformat = V4L2_PIX_FMT_YUYV; + pix->bytesperline = pix->width * 2; + break; + case V4L2_MBUS_FMT_YUYV10_1X20: + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->bytesperline = pix->width * 2; + break; + case V4L2_MBUS_FMT_SBGGR10_1X10: + pix->pixelformat = V4L2_PIX_FMT_SBGGR16; + pix->bytesperline = pix->width * 2; + break; + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + pix->pixelformat = V4L2_PIX_FMT_SGRBG10DPCM8; + pix->bytesperline = pix->width; + break; + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + pix->pixelformat = V4L2_PIX_FMT_SGRBG10ALAW8; + pix->bytesperline = pix->width; + break; + case V4L2_MBUS_FMT_NV12_1X20: + pix->pixelformat = V4L2_PIX_FMT_NV12; + pix->bytesperline = pix->width; + break; + case V4L2_MBUS_FMT_Y8_1X8: + pix->pixelformat = V4L2_PIX_FMT_GREY; + pix->bytesperline = pix->width; + break; + case V4L2_MBUS_FMT_UV8_1X8: + pix->pixelformat = V4L2_PIX_FMT_UV8; + pix->bytesperline = pix->width; + break; + default: + printk(KERN_ERR "invalid mbus code\n"); + } + + /* pitch should be 32 bytes aligned */ + pix->bytesperline = ALIGN(pix->bytesperline, 32); + + if (pix->pixelformat == V4L2_PIX_FMT_NV12) + pix->sizeimage = pix->bytesperline * pix->height + + ((pix->bytesperline * pix->height) >> 1); + else + pix->sizeimage = pix->bytesperline * pix->height; +} + +/* ISR for VINT0*/ +static irqreturn_t vpfe_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_isr\n"); + ccdc_buffer_isr(&vpfe_dev->vpfe_ccdc); + prv_buffer_isr(&vpfe_dev->vpfe_previewer); + rsz_buffer_isr(&vpfe_dev->vpfe_resizer); + + return IRQ_HANDLED; +} + +/* vpfe_vdint1_isr - isr handler for VINT1 interrupt */ +static irqreturn_t vpfe_vdint1_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_vdint1_isr\n"); + ccdc_vidint1_isr(&vpfe_dev->vpfe_ccdc); + + return IRQ_HANDLED; +} + +/* ISR for ipipe dma completion */ +static irqreturn_t vpfe_imp_dma_isr(int irq, void *dev_id) +{ + struct vpfe_device *vpfe_dev = dev_id; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_imp_dma_isr\n"); + prv_dma_isr(&vpfe_dev->vpfe_previewer); + rsz_dma_isr(&vpfe_dev->vpfe_resizer); + + return IRQ_HANDLED; +} + +/* set user setting of serializer in ipipe */ +static void vpfe_initialize(struct vpfe_device *vpfe_dev) +{ + /* inform user choice on serializer to ipipe */ + enable_serializer(vpfe_dev->ipipe, en_serializer); +} + +/** + * vpfe_disable_clock() - Disable clocks for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Disables clocks defined in vpfe configuration. The function + * assumes that at least one clock is to be defined which is + * true as of now. + */ +static void vpfe_disable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int i; + + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + clk_disable(vpfe_dev->clks[i]); + clk_put(vpfe_dev->clks[i]); + } + + kzfree(vpfe_dev->clks); + v4l2_info(vpfe_dev->pdev->driver, "vpfe capture clocks disabled\n"); +} + +/** + * vpfe_enable_clock() - Enable clocks for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Enables clocks defined in vpfe configuration. The function + * assumes that at least one clock is to be defined which is + * true as of now. + */ +static int vpfe_enable_clock(struct vpfe_device *vpfe_dev) +{ + struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; + int ret = -EFAULT; + int i; + + if (!vpfe_cfg->num_clocks) + return 0; + + vpfe_dev->clks = kzalloc(vpfe_cfg->num_clocks * + sizeof(struct clock *), GFP_KERNEL); + + if (vpfe_dev->clks == NULL) { + v4l2_err(vpfe_dev->pdev->driver, "Memory allocation failed\n"); + return -ENOMEM; + } + + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + if (vpfe_cfg->clocks[i] == NULL) { + v4l2_err(vpfe_dev->pdev->driver, + "clock %s is not defined in vpfe config\n", + vpfe_cfg->clocks[i]); + goto out; + } + + vpfe_dev->clks[i] = clk_get(vpfe_dev->pdev, + vpfe_cfg->clocks[i]); + if (vpfe_dev->clks[i] == NULL) { + v4l2_err(vpfe_dev->pdev->driver, + "Failed to get clock %s\n", + vpfe_cfg->clocks[i]); + goto out; + } + + if (clk_enable(vpfe_dev->clks[i])) { + v4l2_err(vpfe_dev->pdev->driver, + "vpfe clock %s not enabled\n", + vpfe_cfg->clocks[i]); + goto out; + } + + v4l2_info(vpfe_dev->pdev->driver, "vpss clock %s enabled", + vpfe_cfg->clocks[i]); + } + + return 0; +out: + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + if (vpfe_dev->clks[i]) + clk_put(vpfe_dev->clks[i]); + } + + v4l2_err(vpfe_dev->pdev->driver, "failed to enable clocks\n"); + + kzfree(vpfe_dev->clks); + return ret; +} + +/** + * vpfe_detach_irq() - Detach IRQs for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Detach all IRQs defined in vpfe configuration. + */ +static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) +{ + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); + free_irq(vpfe_dev->imp_dma_irq, vpfe_dev); +} + +/** + * vpfe_attach_irq() - Attach IRQs for vpfe capture driver + * @vpfe_dev - ptr to vpfe capture device + * + * Attach all IRQs defined in vpfe configuration. + */ +static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) +{ + int ret = 0; + + ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED, + "vpfe_capture0", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting VINT0 interrupt\n"); + return ret; + } + + ret = request_irq(vpfe_dev->ccdc_irq1, vpfe_vdint1_isr, IRQF_DISABLED, + "vpfe_capture1", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, + "Error: requesting VINT1 interrupt\n"); + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + return ret; + } + + ret = request_irq(vpfe_dev->imp_dma_irq, vpfe_imp_dma_isr, + IRQF_DISABLED, "Imp_Sdram_Irq", vpfe_dev); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Error: requesting IMP" + " IRQ interrupt\n"); + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); + free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); + return ret; + } + + return 0; +} + +/** + * register_i2c_devices() - register all i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * + * register all i2c v4l2 subdevs + */ +static int register_i2c_devices(struct vpfe_device *vpfe_dev) +{ + struct vpfe_ext_subdev_info *sdinfo; + struct vpfe_config *vpfe_cfg; + struct i2c_adapter *i2c_adap; + unsigned int num_subdevs; + int ret; + int i; + int k; + + vpfe_cfg = vpfe_dev->cfg; + + i2c_adap = i2c_get_adapter(1); + num_subdevs = vpfe_cfg->num_subdevs; + + vpfe_dev->sd = kzalloc(sizeof(struct v4l2_subdev *) *num_subdevs, + GFP_KERNEL); + + if (vpfe_dev->sd == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, + "unable to allocate memory for subdevice pointers\n"); + return -ENOMEM; + } + + for (i = 0, k = 0; i < num_subdevs; i++) { + sdinfo = &vpfe_cfg->sub_devs[i]; + /** + * register subdevices based on interface setting. Currently + * tvp5146 and mt9p031 cannot co-exists due to i2c address + * conflicts. So only one of them is registered. Re-visit this + * once we have support for i2c switch handling in i2c driver + * framework + */ + + if (interface == sdinfo->is_camera) { + /* setup input path */ + if (vpfe_cfg->setup_input && + vpfe_cfg->setup_input(sdinfo->grp_id) < 0) { + ret = -EFAULT; + v4l2_info(&vpfe_dev->v4l2_dev, "could" + " not setup input for %s\n", + sdinfo->module_name); + goto probe_sd_out; + } + /* Load up the subdevice */ + vpfe_dev->sd[k] = + v4l2_i2c_new_subdev_board(&vpfe_dev->v4l2_dev, + i2c_adap, &sdinfo->board_info, + NULL, 1); + if (vpfe_dev->sd[k]) { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s registered\n", + sdinfo->module_name); + + vpfe_dev->sd[k]->grp_id = sdinfo->grp_id; + k++; + + sdinfo->registered = 1; + } + } else { + v4l2_info(&vpfe_dev->v4l2_dev, + "v4l2 sub device %s is not registered\n", + sdinfo->module_name); + } + } + + vpfe_dev->num_ext_subdevs = k; + + return 0; + +probe_sd_out: + kzfree(vpfe_dev->sd); + + return ret; +} + +/** + * vpfe_register_entities() - register all v4l2 subdevs and media entities + * @vpfe_dev - ptr to vpfe capture device + * + * register all v4l2 subdevs, media entities, and creates links + * between entities + */ +static int vpfe_register_entities(struct vpfe_device *vpfe_dev) +{ + unsigned int flags = 0; + int ret; + int i; + + /* register i2c devices first */ + ret = register_i2c_devices(vpfe_dev); + if (ret) + return ret; + + /* register rest of the sub-devs */ + ret = vpfe_ccdc_register_entities(&vpfe_dev->vpfe_ccdc, + &vpfe_dev->v4l2_dev); + if (ret) + return ret; + + ret = vpfe_previewer_register_entities(&vpfe_dev->vpfe_previewer, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_ccdc_register; + + ret = vpfe_resizer_register_entities(&vpfe_dev->vpfe_resizer, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_previewer_register; + + ret = vpfe_aew_register_entities(&vpfe_dev->vpfe_aew, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_resizer_register; + + ret = vpfe_af_register_entities(&vpfe_dev->vpfe_af, + &vpfe_dev->v4l2_dev); + if (ret) + goto out_aew_register; + + /* create links now, starting with external(i2c) entities */ + for (i = 0; i < vpfe_dev->num_ext_subdevs; i++) { + /* if entity has no pads (ex: amplifier), + cant establish link */ + if (vpfe_dev->sd[i]->entity.num_pads) { + ret = media_entity_create_link(&vpfe_dev->sd[i]->entity, + 0, &vpfe_dev->vpfe_ccdc.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + } + } + + ret = media_entity_create_link(&vpfe_dev->vpfe_ccdc.subdev.entity, + 1, &vpfe_dev->vpfe_aew.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ccdc.subdev.entity, + 1, &vpfe_dev->vpfe_af.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_ccdc.subdev.entity, 1, + &vpfe_dev->vpfe_previewer.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + ret = media_entity_create_link(&vpfe_dev->vpfe_previewer.subdev.entity, + 1, &vpfe_dev->vpfe_resizer.subdev.entity, + 0, flags); + if (ret < 0) + goto out_resizer_register; + + return 0; + +out_aew_register: + vpfe_aew_unregister_entities(&vpfe_dev->vpfe_aew); +out_resizer_register: + vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer); +out_previewer_register: + vpfe_previewer_unregister_entities(&vpfe_dev->vpfe_previewer); +out_ccdc_register: + vpfe_ccdc_unregister_entities(&vpfe_dev->vpfe_ccdc); + return ret; +} + +/** + * vpfe_unregister_entities() - unregister all v4l2 subdevs and media entities + * @vpfe_dev - ptr to vpfe capture device + * + * unregister all v4l2 subdevs and media entities + */ +static void vpfe_unregister_entities(struct vpfe_device *vpfe_dev) +{ + vpfe_ccdc_unregister_entities(&vpfe_dev->vpfe_ccdc); + vpfe_previewer_unregister_entities(&vpfe_dev->vpfe_previewer); + vpfe_resizer_unregister_entities(&vpfe_dev->vpfe_resizer); + vpfe_aew_unregister_entities(&vpfe_dev->vpfe_aew); + vpfe_af_unregister_entities(&vpfe_dev->vpfe_af); +} + +/** + * vpfe_cleanup_modules() - cleanup all non-i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * @pdev - pointer to platform device + * + * cleanup all v4l2 subdevs + */ +static void vpfe_cleanup_modules(struct vpfe_device *vpfe_dev, + struct platform_device *pdev) +{ + vpfe_ccdc_cleanup(pdev); + vpfe_previewer_cleanup(pdev, vpfe_dev->ipipe); + vpfe_aew_cleanup(); + vpfe_af_cleanup(); +} + +/** + * vpfe_initialize_modules() - initialize all non-i2c v4l2 subdevs + * @vpfe_dev - ptr to vpfe capture device + * @pdev - pointer to platform device + * + * intialize all v4l2 subdevs and media entities + */ +static int vpfe_initialize_modules(struct vpfe_device *vpfe_dev, + struct platform_device *pdev) +{ + int ret; + + ret = vpfe_ccdc_init(&vpfe_dev->vpfe_ccdc, pdev); + if (ret) + return ret; + + ret = vpfe_previewer_init(&vpfe_dev->vpfe_previewer, pdev); + if (ret) + goto out_ccdc_init; + + ret = vpfe_resizer_init(&vpfe_dev->vpfe_resizer, pdev); + if (ret) + goto out_previewer_init; + + ret = vpfe_aew_init(&vpfe_dev->vpfe_aew, pdev); + if (ret) + goto out_previewer_init; + + ret = vpfe_af_init(&vpfe_dev->vpfe_af, pdev); + if (ret) + goto out_aew_init; + + return 0; + +out_aew_init: + vpfe_aew_cleanup(); +out_previewer_init: + vpfe_previewer_cleanup(pdev, vpfe_dev->ipipe); +out_ccdc_init: + vpfe_ccdc_cleanup(pdev); + + return ret; +} + +/** + * vpfe_probe : vpfe probe function + * @pdev: platform device pointer + * + * This function creates device entries by register itself to the V4L2 driver + * and initializes fields of each device objects + */ +static __devinit int vpfe_probe(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev; + struct resource *res1; + unsigned long phys_end_kernel; + int ret = -ENOMEM; + int err; + size_t size; + + vpfe_dev = kzalloc(sizeof(*vpfe_dev), GFP_KERNEL); + if (!vpfe_dev) { + v4l2_err(pdev->dev.driver, + "Failed to allocate memory for vpfe_dev\n"); + return ret; + } + + if (pdev->dev.platform_data == NULL) { + v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + vpfe_dev->cfg = pdev->dev.platform_data; + + if (vpfe_dev->cfg->card_name == NULL || + vpfe_dev->cfg->sub_devs == NULL) { + v4l2_err(pdev->dev.driver, "null ptr in vpfe_cfg\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + + /* Get VINT0 irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT0\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->ccdc_irq0 = res1->start; + + /* Get VINT1 irq resource */ + res1 = platform_get_resource(pdev, + IORESOURCE_IRQ, 1); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for VINT1\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->ccdc_irq1 = res1->start; + + /* Get DMA irq resource */ + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 2); + if (!res1) { + v4l2_err(pdev->dev.driver, + "Unable to get interrupt for DMA\n"); + ret = -ENOENT; + goto probe_free_dev_mem; + } + vpfe_dev->imp_dma_irq = res1->start; + + vpfe_dev->pdev = &pdev->dev; + + /* enable vpss clocks */ + ret = vpfe_enable_clock(vpfe_dev); + if (ret) + goto probe_free_dev_mem; + + if (vpfe_initialize_modules(vpfe_dev, pdev)) + goto probe_disable_clock; + + vpfe_initialize(vpfe_dev); + + vpfe_dev->media_dev.dev = vpfe_dev->pdev; + strcpy((char *)&vpfe_dev->media_dev.model, "davinci-media"); + ret = media_device_register(&vpfe_dev->media_dev); + if (ret) { + v4l2_err(pdev->dev.driver, + "Unable to register media device.\n"); + goto probe_out_entities_cleanup; + } + + vpfe_dev->v4l2_dev.mdev = &vpfe_dev->media_dev; + + ret = v4l2_device_register(&pdev->dev, &vpfe_dev->v4l2_dev); + if (ret) { + v4l2_err(pdev->dev.driver, "Unable to register v4l2 device.\n"); + goto probe_out_media_unregister; + } + v4l2_info(&vpfe_dev->v4l2_dev, "v4l2 device registered\n"); + + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpfe_dev); + + /* register subdevs/entities */ + if (vpfe_register_entities(vpfe_dev)) + goto probe_out_v4l2_unregister; + + ret = vpfe_attach_irq(vpfe_dev); + if (ret) + goto probe_out_entities_unregister; + + if (cont_bufsize) { + /* attempt to determine the end of Linux kernel memory */ + phys_end_kernel = virt_to_phys((void *)PAGE_OFFSET) + + (num_physpages << PAGE_SHIFT); + size = cont_bufsize; + phys_end_kernel += cont_bufoffset; + err = dma_declare_coherent_memory(&pdev->dev, phys_end_kernel, + phys_end_kernel, size, + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE); + if (!err) { + dev_err(&pdev->dev, "Unable to declare MMAP memory.\n"); + ret = -ENOENT; + goto probe_detach_irq; + } + vpfe_dev->video_limit = size; + } + + return 0; + +probe_detach_irq: + vpfe_detach_irq(vpfe_dev); +probe_out_entities_unregister: + vpfe_unregister_entities(vpfe_dev); + kzfree(vpfe_dev->sd); +probe_out_v4l2_unregister: + v4l2_device_unregister(&vpfe_dev->v4l2_dev); +probe_out_media_unregister: + media_device_unregister(&vpfe_dev->media_dev); +probe_out_entities_cleanup: + vpfe_cleanup_modules(vpfe_dev, pdev); +probe_disable_clock: + vpfe_disable_clock(vpfe_dev); +probe_free_dev_mem: + kzfree(vpfe_dev); + + return ret; +} + +/* + * vpfe_remove : This function un-registers device from V4L2 driver + */ +static int vpfe_remove(struct platform_device *pdev) +{ + struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev); + + v4l2_info(pdev->dev.driver, "vpfe_remove\n"); + + kzfree(vpfe_dev->sd); + vpfe_detach_irq(vpfe_dev); + vpfe_unregister_entities(vpfe_dev); + vpfe_cleanup_modules(vpfe_dev, pdev); + v4l2_device_unregister(&vpfe_dev->v4l2_dev); + media_device_unregister(&vpfe_dev->media_dev); + vpfe_disable_clock(vpfe_dev); + kzfree(vpfe_dev); + + return 0; +} + +static struct platform_driver vpfe_driver = { + .driver = { + .name = CAPTURE_DRV_NAME, + .owner = THIS_MODULE, + }, + .probe = vpfe_probe, + .remove = __devexit_p(vpfe_remove), +}; + +/** + * vpfe_init : This function registers device driver + */ +static __init int vpfe_init(void) +{ + /* Register driver to the kernel */ + return platform_driver_register(&vpfe_driver); +} + +/** + * vpfe_cleanup : This function un-registers device driver + */ +static void vpfe_cleanup(void) +{ + platform_driver_unregister(&vpfe_driver); +} + +module_init(vpfe_init); +module_exit(vpfe_cleanup); diff --git a/drivers/media/video/davinci/vpfe_capture.h b/drivers/media/video/davinci/vpfe_capture.h new file mode 100644 index 0000000..7219baf --- /dev/null +++ b/drivers/media/video/davinci/vpfe_capture.h @@ -0,0 +1,99 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _VPFE_CAPTURE_H +#define _VPFE_CAPTURE_H + +/* Header files */ +#include +#include +#include +#include +#include +#include +#include +#include "imp_hw_if.h" +#include "imp_common.h" + +#include "vpfe_video.h" +#include "vpfe_ccdc.h" +#include "vpfe_resizer.h" +#include "vpfe_previewer.h" +#include "vpfe_aew.h" +#include "vpfe_af.h" + +/* Macros */ +#define VPFE_MAJOR_RELEASE 0 +#define VPFE_MINOR_RELEASE 0 +#define VPFE_BUILD 1 +#define VPFE_CAPTURE_VERSION_CODE ((VPFE_MAJOR_RELEASE << 16) | \ + (VPFE_MINOR_RELEASE << 8) | \ + VPFE_BUILD) + +#define to_vpfe_device(ptr_module) \ + container_of(ptr_module, struct vpfe_device, vpfe_##ptr_module) +#define to_device(ptr_module) \ + (to_vpfe_device(ptr_module)->dev) + +struct vpfe_device { + /* external registered sub devices */ + struct v4l2_subdev **sd; + /* number of registered external subdevs */ + unsigned int num_ext_subdevs; + /* vpfe cfg */ + struct vpfe_config *cfg; + /* clock ptrs for vpfe capture */ + struct clk **clks; + /* V4l2 device */ + struct v4l2_device v4l2_dev; + /* parent device */ + struct device *pdev; + /* IRQ number for DMA transfer completion at the image processor */ + unsigned int imp_dma_irq; + /* CCDC IRQs used when CCDC/ISIF output to SDRAM */ + unsigned int ccdc_irq0; + unsigned int ccdc_irq1; + /* maximum video memory that is available*/ + unsigned int video_limit; + /* media device */ + struct media_device media_dev; + /* ccdc subdevice */ + struct vpfe_ccdc_device vpfe_ccdc; + /* resizer subdevice */ + struct vpfe_resizer_device vpfe_resizer; + /* previewer subdevice */ + struct vpfe_previewer_device vpfe_previewer; + /* aew subdevice */ + struct vpfe_aew_device vpfe_aew; + /* af subdevice */ + struct vpfe_af_device vpfe_af; + void *ipipe; +}; + +/* File handle structure */ +struct vpfe_fh { + struct vpfe_video_device *video; + /* Indicates whether this file handle is doing IO */ + u8 io_allowed; + /* Used to keep track priority of this instance */ + enum v4l2_priority prio; +}; + +void mbus_to_pix(const struct v4l2_mbus_framefmt *mbus, + struct v4l2_pix_format *pix); + +#endif /* _DAVINCI_VPFE_H */ diff --git a/include/media/davinci/vpfe.h b/include/media/davinci/vpfe.h new file mode 100644 index 0000000..b55d318 --- /dev/null +++ b/include/media/davinci/vpfe.h @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _VPFE_H +#define _VPFE_H + +#ifdef __KERNEL__ +#include +#include +#include + +#define CAPTURE_DRV_NAME "vpfe-capture" + +struct vpfe_route { + __u32 input; + __u32 output; +}; + +enum vpfe_subdev_id { + VPFE_SUBDEV_TVP5146 = 1, + VPFE_SUBDEV_MT9T031 = 2, + VPFE_SUBDEV_TVP7002 = 3, + VPFE_SUBDEV_MT9P031 = 4, +}; + +enum vpfe_pin_pol { + VPFE_PINPOL_POSITIVE, + VPFE_PINPOL_NEGATIVE +}; + +/* interface description */ +struct vpfe_hw_if_param { + enum v4l2_mbus_pixelcode if_type; + enum vpfe_pin_pol hdpol; + enum vpfe_pin_pol vdpol; +}; + +struct vpfe_ext_subdev_info { + /* v4l2 subdev */ + struct v4l2_subdev *subdev; + /* Sub device module name */ + char module_name[32]; + /* Sub device group id */ + int grp_id; + /* Number of inputs supported */ + int num_inputs; + /* inputs available at the sub device */ + struct v4l2_input *inputs; + /* Sub dev routing information for each input */ + struct vpfe_route *routes; + /* ccdc bus/interface configuration */ + struct vpfe_hw_if_param ccdc_if_params; + /* i2c subdevice board info */ + struct i2c_board_info board_info; + /* Is this a camera sub device ? */ + unsigned is_camera:1; + /* check if sub dev supports routing */ + unsigned can_route:1; + /* registered ? */ + unsigned registered:1; +}; + +struct vpfe_config { + /* Number of sub devices connected to vpfe */ + int num_subdevs; + /* information about each subdev */ + struct vpfe_ext_subdev_info *sub_devs; + /* evm card info */ + char *card_name; + /* setup function for the input path */ + int (*setup_input)(enum vpfe_subdev_id id); + /* number of clocks */ + int num_clocks; + /* clocks used for vpfe capture */ + char *clocks[]; +}; +#endif +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:44:35 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 16:14:35 +0530 Subject: [RESEND RFC PATCH v4 09/15] davinci: vpfe: resizer driver based on media framework In-Reply-To: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> References: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321526681-22574-10-git-send-email-manjunath.hadli@ti.com> Add the video resizer driver with the v4l2 media controller framework which takes care of resizing the video frames with both up-scaling downscaling facility. The formats that is supported is YUV422. The driver supports both continuous mode where it works in tandem with the CCDC and previewer and single shot mode where it can be used in isolation or with previewer. The driver underneath uses the dm365 IPIPE module for programming the hardware. The driver supports resizer as a subdevice and a media entity, and the enumerable pads are 2(1 input and 1 output). The driver supports streaming and all the pad and link related operations. Specific filter functionality including filter types are set through private ioctls. Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/vpfe_resizer.c | 1079 ++++++++++++++++++++++++++++ drivers/media/video/davinci/vpfe_resizer.h | 63 ++ 2 files changed, 1142 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpfe_resizer.c create mode 100644 drivers/media/video/davinci/vpfe_resizer.h diff --git a/drivers/media/video/davinci/vpfe_resizer.c b/drivers/media/video/davinci/vpfe_resizer.c new file mode 100644 index 0000000..f391b1a --- /dev/null +++ b/drivers/media/video/davinci/vpfe_resizer.c @@ -0,0 +1,1079 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Nagabhushana Netagunte + */ + +#include +#include +#include +#include +#include +#include +#include "vpfe_capture.h" +#include "imp_hw_if.h" +#include "vpss.h" + +#define MIN_IN_WIDTH 32 +#define MIN_IN_HEIGHT 32 +#define MAX_IN_WIDTH 4095 +#define MAX_IN_HEIGHT 4095 +#define MIN_OUT_WIDTH 16 +#define MIN_OUT_HEIGHT 2 + +/* resizer pixel formats */ +static const unsigned int resz_input_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, +#ifdef CONFIG_ARCH_DAVINCI_DM365 + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, +#endif +}; + +static const unsigned int resz_output_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, +#ifdef CONFIG_ARCH_DAVINCI_DM365 + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_UV8_1X8, + V4L2_MBUS_FMT_NV12_1X20, +#endif +}; + +/* + * imp_set_resizer_config - set resizer config + * @resizer: vpfe resizer device pointer + * @channel: image processor logical channel + * @chan_config: resizer channel configuration + */ +static int imp_set_resizer_config(struct vpfe_resizer_device *resizer, + struct imp_logical_channel *channel, + struct rsz_channel_config *chan_config) +{ + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct device *dev = resizer->subdev.v4l2_dev->dev; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + int ret; + int len; + + dev_dbg(dev, "imp_set_resizer_config. len = %d\n", chan_config->len); + + if (channel->config_state == STATE_NOT_CONFIGURED) { + channel->config = + imp_hw_if->alloc_config_block(dev, vpfe_dev->ipipe); + + if (!channel->config) { + dev_err(dev, "memory allocation failed\n"); + return -EFAULT; + } + /* allocate buffer for holding user configuration */ + channel->user_config = imp_hw_if->alloc_user_config_block(dev, + vpfe_dev->ipipe, IMP_RESIZER, &len); + + if (!channel->user_config) { + dev_err(dev, "memory allocation failed\n"); + if (!chan_config->chain) + kfree(channel->config); + return -EFAULT; + } + channel->user_config_size = len; + dev_dbg(dev, "imp_set_resizer_config, len = %d.\n", len); + } + + if (!chan_config->config) { + /* put defaults for user configuration */ + imp_hw_if->set_user_config_defaults(dev, vpfe_dev->ipipe, + IMP_RESIZER, channel->user_config); + + dev_dbg(dev, "imp_set_resizer_config, default\n"); + } else { + memcpy(channel->user_config, chan_config->config, + channel->user_config_size); + dev_dbg(dev, "imp_set_resizer_config, user setting\n"); + } + + /* Update the user configuration in the hw config block or + if chained, copy it to the shared block and allow previewer + to configure it */ + ret = imp_hw_if->set_resizer_config(dev, vpfe_dev->ipipe, + chan_config->chain, + channel->user_config, + channel->config); + + if (ret < 0) + dev_err(dev, "set resizer config failed\n"); + + channel->chained = chan_config->chain; + channel->config_state = STATE_CONFIGURED; + + return ret; +} + +/* + * imp_get_resizer_config - set resizer config + * @resizer: vpfe resizer device pointer + * @channel: image processor logical channel + * @chan_config: resizer channel configuration + */ +static int imp_get_resize_config(struct vpfe_resizer_device *resizer, + struct imp_logical_channel *channel, + struct rsz_channel_config *chan_config) +{ + struct device *dev = resizer->subdev.v4l2_dev->dev; + + dev_dbg(dev, "imp_get_resize_config:\n"); + + if (channel->config_state != STATE_CONFIGURED) { + dev_err(dev, "channel not configured\n"); + return -EINVAL; + } + + if (!chan_config->config) { + dev_err(dev, " channel configuration NULL ptr\n"); + return -EINVAL; + } + + if (copy_to_user((void *)chan_config->config, + (void *)channel->user_config, + channel->user_config_size)) { + dev_err(dev, "Error in copy to user\n"); + return -EFAULT; + } + + return 0; +} + +/* + * set_resizer_channel_cont_mode - Set continous channel mode + * @resizer: vpfe resizer device pointer + */ +static void set_resizer_channel_cont_mode(struct vpfe_resizer_device *resizer) +{ + struct imp_logical_channel *channel = &resizer->channel; + + channel->config_state = STATE_NOT_CONFIGURED; + channel->user_config = NULL; +} + +/* + * set_resizer_channel_ss_mode - Set single-shot channel mode + * @resizer: vpfe resizer device pointer + */ +static int set_resizer_channel_ss_mode(struct vpfe_resizer_device *resizer) +{ + struct imp_logical_channel *channel = &resizer->channel; + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + + channel->config_state = STATE_NOT_CONFIGURED; + channel->user_config = NULL; + return imp_hw_if->set_oper_mode(vpfe_dev->ipipe, IMP_MODE_SINGLE_SHOT); +} + +/* + * reset_resizer_channel_mode - Reset channel mode + * @resizer: vpfe resizer device pointer + */ +static void reset_resizer_channel_mode(struct vpfe_resizer_device *resizer) +{ + struct imp_logical_channel *channel = &resizer->channel; + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + + if (resizer->input == RESIZER_INPUT_MEMORY) + imp_hw_if->reset_oper_mode(vpfe_dev->ipipe); + + if (channel->config_state == STATE_CONFIGURED) { + kfree(channel->user_config); + channel->user_config = NULL; + } +} + +/* + * VPFE video operations + */ +/* + * rsz_video_in_queue - RESIZER video in queue + * @vpfe_dev: vpfe device pointer + * @addr: buffer address + */ +static void rsz_video_in_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct imp_logical_channel *chan = &resizer->channel; + + if (resizer->input == RESIZER_INPUT_MEMORY) + imp_hw_if->update_inbuf_address(chan->config, addr); + else + imp_hw_if->update_inbuf_address(NULL, addr); +} + +/* + * rsz_video_out1_queue - RESIZER-A video out queue + * @vpfe_dev: vpfe device pointer + * @addr: buffer address + */ +static void rsz_video_out1_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct vpfe_pipeline *pipe = &resizer->video_out.pipe; + struct imp_logical_channel *chan = &resizer->channel; + + if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) + imp_hw_if->update_outbuf1_address(vpfe_dev->ipipe, + chan->config, addr); + else + imp_hw_if->update_outbuf1_address(vpfe_dev->ipipe, NULL, addr); + +} + +/* + * rsz_video_out2_queue - RESIZER-B video out queue + * @vpfe_dev: vpfe device pointer + * @addr: buffer address + */ +static void rsz_video_out2_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_resizer_device *resizer = &vpfe_dev->vpfe_resizer; + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct vpfe_pipeline *pipe = &resizer->video_out.pipe; + struct imp_logical_channel *chan = &resizer->channel; + + if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) + imp_hw_if->update_outbuf2_address(vpfe_dev->ipipe, + chan->config, addr); + else + imp_hw_if->update_outbuf2_address(vpfe_dev->ipipe, NULL, addr); +} + +static const struct vpfe_video_operations video_in_ops = { + .queue = rsz_video_in_queue, +}; + +static const struct vpfe_video_operations video_out1_ops = { + .queue = rsz_video_out1_queue, +}; + +static const struct vpfe_video_operations video_out2_ops = { + .queue = rsz_video_out2_queue, +}; + +/* + * rsz_ss_buffer_isr - RESIZER module single-shot buffer scheduling isr + * @resizer: RESIZER device pointer + * + */ +static void rsz_ss_buffer_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_video_device *video_out = &resizer->video_out; + struct vpfe_video_device *video_in = &resizer->video_in; + struct vpfe_video_device *video_out2 = &resizer->video_out2; + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct imp_logical_channel *chan = &resizer->channel; + struct vpfe_pipeline *pipe = &video_out->pipe; + u32 val; + + if (resizer->output == RESIZER_OUPUT_MEMORY) { + val = vpss_dma_complete_interrupt(); + if (val != 0 && val != 2) + return; + } + + if (resizer->input == RESIZER_INPUT_MEMORY) { + spin_lock(&video_in->dma_queue_lock); + vpfe_process_buffer_complete(video_in); + video_in->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_schedule_next_buffer(video_in); + spin_unlock(&video_in->dma_queue_lock); + } + + if (resizer->output == RESIZER_OUPUT_MEMORY) { + spin_lock(&video_out->dma_queue_lock); + vpfe_process_buffer_complete(video_out); + video_out->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_schedule_next_buffer(video_out); + spin_unlock(&video_out->dma_queue_lock); + } + + /* If resizer B is enabled */ + if (pipe->output_num > 1 && resizer->output2 == RESIZER_OUPUT_MEMORY) { + spin_lock(&video_out->dma_queue_lock); + vpfe_process_buffer_complete(video_out2); + video_out2->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_schedule_next_buffer(video_out2); + spin_unlock(&video_out2->dma_queue_lock); + } + + /* start HW if buffers are queued */ + if (is_pipe_ready(pipe) && resizer->output == RESIZER_OUPUT_MEMORY) + imp_hw_if->enable(vpfe_dev->ipipe, 1, chan->config); +} + +/* + * rsz_buffer_isr - RESIZER module buffer scheduling isr + * @resizer: RESIZER device pointer + * + */ +void rsz_buffer_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_video_device *video_out2 = &resizer->video_out2; + struct vpfe_video_device *video_out = &resizer->video_out; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_pipeline *pipe = &resizer->video_out.pipe; + enum v4l2_field field; + int fid; + + if (!video_out->started) + return; + + if (resizer->input != RESIZER_INPUT_PREVIEWER) + return; + + field = video_out->fmt.fmt.pix.field; + + if (field == V4L2_FIELD_NONE) { + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + + /* handle progressive frame capture */ + if (video_out->cur_frm != video_out->next_frm) { + vpfe_process_buffer_complete(video_out); + if (pipe->output_num > 1) + vpfe_process_buffer_complete(video_out2); + } + + video_out->skip_frame_count--; + if (!video_out->skip_frame_count) { + video_out->skip_frame_count = + video_out->skip_frame_count_init; + if (imp_hw_if->enable_resize) + imp_hw_if->enable_resize(1); + } else { + if (imp_hw_if->enable_resize) + imp_hw_if->enable_resize(0); + } + return; + } + + /* handle interlaced frame capture */ + fid = ccdc_get_fid(vpfe_dev); + + /* switch the software maintained field id */ + video_out->field_id ^= 1; + if (fid == video_out->field_id) { + /* + * we are in-sync here,continue. + * One frame is just being captured. If the + * next frame is available, release the current + * frame and move on + */ + if (fid == 0 && video_out->cur_frm != video_out->next_frm) { + vpfe_process_buffer_complete(video_out); + if (pipe->output_num > 1) + vpfe_process_buffer_complete(video_out2); + } + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + video_out->field_id = fid; + } +} + +/* + * rsz_dma_isr - RESIZER module dma isr + * @resizer: RESIZER device pointer + * + */ +void rsz_dma_isr(struct vpfe_resizer_device *resizer) +{ + struct vpfe_video_device *video_out = &resizer->video_out; + struct vpfe_video_device *video_out2 = &resizer->video_out2; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct vpfe_pipeline *pipe = &video_out->pipe; + enum v4l2_field field; + int schedule_capture = 0; + int fid; + + if (!video_out->started) + return; + + if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) { + rsz_ss_buffer_isr(resizer); + return; + } + + field = video_out->fmt.fmt.pix.field; + + if (field == V4L2_FIELD_NONE) { + if (!list_empty(&video_out->dma_queue) && + video_out->cur_frm == video_out->next_frm) + schedule_capture = 1; + } else { + fid = ccdc_get_fid(vpfe_dev); + if (fid == video_out->field_id) { + /* we are in-sync here,continue */ + if (fid == 1 && !list_empty(&video_out->dma_queue) && + video_out->cur_frm == video_out->next_frm) + schedule_capture = 1; + } + } + if (!schedule_capture) + return; + + spin_lock(&video_out->dma_queue_lock); + vpfe_schedule_next_buffer(video_out); + spin_unlock(&video_out->dma_queue_lock); + if (pipe->output_num > 1) { + spin_lock(&video_out2->dma_queue_lock); + vpfe_schedule_next_buffer(video_out2); + spin_unlock(&video_out2->dma_queue_lock); + } +} + +/* + * V4L2 subdev operations + */ + +/* + * resizer_ioctl - RESIZER module private ioctl's + * @sd: VPFE RESZIER V4L2 subdevice + * @cmd: ioctl command + * @arg: ioctl argument + * + * Return 0 on success or a negative error code otherwise. + */ +static long resizer_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct imp_logical_channel *rsz_conf_chan = &resizer->channel; + struct device *dev = resizer->subdev.v4l2_dev->dev; + struct rsz_channel_config *user_config; + int ret = -EINVAL; + + switch (cmd) { + case RSZ_S_CONFIG: + user_config = (struct rsz_channel_config *)arg; + + dev_dbg(dev, "RSZ_S_CONFIG:\n"); + ret = imp_set_resizer_config(resizer, rsz_conf_chan, + user_config); + break; + case RSZ_G_CONFIG: + user_config = (struct rsz_channel_config *)arg; + dev_dbg(dev, "RSZ_G_CONFIG:%d:%d\n", user_config->chain, + user_config->len); + if (!user_config->config) { + dev_err(dev, "error in PREV_GET_CONFIG\n"); + goto ERROR; + } + ret = imp_get_resize_config(resizer, rsz_conf_chan, + user_config); + break; + default: + printk(KERN_ERR "invalid command\n"); + } + +ERROR: + return ret; +} + +/* + * resizer_set_stream - Enable/Disable streaming on the RESIZER module + * @sd: VPFE RESIZER V4L2 subdevice + * @enable: Enable/disable stream + */ +static int resizer_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct vpfe_pipeline *pipe = &resizer->video_out.pipe; + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct imp_logical_channel *chan = &resizer->channel; + struct device *dev = resizer->subdev.v4l2_dev->dev; + + void *config = (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) ? + chan->config : NULL; + + if (resizer->output != RESIZER_OUPUT_MEMORY) + return 0; + + switch (enable) { + case 1: + if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT && + imp_hw_if->serialize(vpfe_dev->ipipe)) { + if (imp_hw_if->hw_setup(dev, vpfe_dev->ipipe, + config) < 0) + return -EINVAL; + } else if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) { + imp_hw_if->lock_chain(vpfe_dev->ipipe); + if (imp_hw_if->hw_setup(dev, vpfe_dev->ipipe, NULL)) + return -EINVAL; + } + imp_hw_if->enable(vpfe_dev->ipipe, 1, config); + break; + case 0: + imp_hw_if->enable(vpfe_dev->ipipe, 0, config); + if ((pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS)) + imp_hw_if->unlock_chain(vpfe_dev->ipipe); + break; + } + + return 0; +} + +/* + * __resizer_get_format - helper function for getting resizer format + * @res : pointer to resizer private structure + * @pad : pad number + * @fh : V4L2 subdev file handle + * @which : wanted subdev format + * Retun wanted mbus frame format + */ +static struct v4l2_mbus_framefmt * +__resizer_get_format(struct vpfe_resizer_device *res, struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + return &res->formats[pad]; +} + +/* + * resizer_try_format - Handle try format by pad subdev method + * @res : VPFE resizer device + * @fh : V4L2 subdev file handle + * @pad : pad num + * @fmt : pointer to v4l2 format structure + * @which : wanted subdev format + */ +static void resizer_try_format(struct vpfe_resizer_device *res, + struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + struct imp_hw_interface *imp_hw_if; + unsigned int max_out_height; + unsigned int max_out_width; + unsigned int i; + + imp_hw_if = res->imp_hw_if; + + if (pad == RESIZER_PAD_SINK) { + for (i = 0; i < ARRAY_SIZE(resz_input_fmts); i++) { + if (fmt->code == resz_input_fmts[i]) + break; + } + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resz_input_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_IN_WIDTH, + MAX_IN_WIDTH); + fmt->height = clamp_t(u32, fmt->height, MIN_IN_HEIGHT, + MAX_IN_HEIGHT); + } else if (pad == RESIZER_PAD_SOURCE) { + max_out_width = imp_hw_if->get_max_output_width(0); + max_out_height = imp_hw_if->get_max_output_height(0); + + for (i = 0; i < ARRAY_SIZE(resz_output_fmts); i++) { + if (fmt->code == resz_output_fmts[i]) + break; + } + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resz_output_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH, + max_out_width); + fmt->width &= ~15; + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT, + max_out_height); + } else if (pad == RESIZER_PAD_SOURCE2) { + max_out_width = imp_hw_if->get_max_output_width(1); + max_out_height = imp_hw_if->get_max_output_height(1); + + for (i = 0; i < ARRAY_SIZE(resz_output_fmts); i++) { + if (fmt->code == resz_output_fmts[i]) + break; + } + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(resz_output_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_WIDTH, + max_out_width); + fmt->width &= ~15; + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_HEIGHT, + max_out_height); + } +} + +/* +* resizer_set_format - set format on pad +* @sd : VPFE resizer device +* @fh : V4L2 subdev file handle +* @fmt : pointer to v4l2 subdev format structure +*/ +static int resizer_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + struct imp_hw_interface *imp_hw_if = resizer->imp_hw_if; + struct v4l2_mbus_framefmt *format; + + format = __resizer_get_format(resizer, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + resizer_try_format(resizer, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == RESIZER_PAD_SINK && + resizer->input == RESIZER_INPUT_PREVIEWER) { + /* + *This is continous mode, input format is already set in + *PREVIEWER sink set format, no need to do here. + */ + resizer->formats[fmt->pad] = fmt->format; + } else if (fmt->pad == RESIZER_PAD_SOURCE && + resizer->output == RESIZER_OUPUT_MEMORY) { + + imp_hw_if->set_out_mbus_format(vpfe_dev->ipipe, &fmt->format); + resizer->formats[fmt->pad] = fmt->format; + + } else if (fmt->pad == RESIZER_PAD_SINK && + resizer->input == RESIZER_INPUT_MEMORY) { + /* + * single shot mode + */ + + imp_hw_if->set_in_mbus_format(vpfe_dev->ipipe, &fmt->format); + resizer->formats[fmt->pad] = fmt->format; + } else if (fmt->pad == RESIZER_PAD_SOURCE2 && + resizer->output2 == RESIZER_OUPUT_MEMORY) { + imp_hw_if->set_out2_mbus_format(vpfe_dev->ipipe, &fmt->format); + resizer->formats[fmt->pad] = fmt->format; + + } else { + return -EINVAL; + } + + return 0; +} + +/* + * resizer_get_format - Retrieve the video format on a pad + * @sd : VPFE RESIZER V4L2 subdevice + * @fh : V4L2 subdev file handle + * @fmt: Format + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int resizer_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_resizer_device *res = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + format = __resizer_get_format(res, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + fmt->format = *format; + + return 0; +} + +/* + * resizer_enum_frame_size - enum frame sizes on pads + * @sd: VPFE resizer V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_frame_size_enum structure + */ +static int resizer_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_resizer_device *res = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + resizer_try_format(res, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * resizer_enum_mbus_code - enum mbus codes for pads + * @sd: VPFE resizer V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int resizer_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->pad == RESIZER_PAD_SINK) { + if (code->index >= ARRAY_SIZE(resz_input_fmts)) + return -EINVAL; + + code->code = resz_input_fmts[code->index]; + } else if (code->pad == RESIZER_PAD_SOURCE) { + if (code->index >= ARRAY_SIZE(resz_output_fmts)) + return -EINVAL; + + code->code = resz_output_fmts[code->index]; + } + + return 0; +} + +/* + * resizer_init_formats - Initialize formats on all pads + * @sd: VPFE resizer V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int resizer_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct vpfe_resizer_device *res = v4l2_get_subdevdata(sd); + struct imp_hw_interface *imp_hw_if = res->imp_hw_if; + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_YUYV8_2X8; + format.format.width = MAX_IN_WIDTH; + format.format.height = MAX_IN_HEIGHT; + resizer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = RESIZER_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = imp_hw_if->get_max_output_width(0); + format.format.height = imp_hw_if->get_max_output_height(0); + resizer_set_format(sd, fh, &format); + + return 0; +} + +/* V4L2 subdev core operations */ +static const struct v4l2_subdev_core_ops resizer_v4l2_core_ops = { + .ioctl = resizer_ioctl, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_file_ops resizer_v4l2_file_ops = { + .open = resizer_init_formats, +}; + +/* V4L2 subdev video operations */ +static const struct v4l2_subdev_video_ops resizer_v4l2_video_ops = { + .s_stream = resizer_set_stream, +}; + +/* V4L2 subdev pad operations */ +static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { + .enum_mbus_code = resizer_enum_mbus_code, + .enum_frame_size = resizer_enum_frame_size, + .get_fmt = resizer_get_format, + .set_fmt = resizer_set_format, +}; + +/* V4L2 subdev operations */ +static const struct v4l2_subdev_ops resizer_v4l2_ops = { + .core = &resizer_v4l2_core_ops, + .file = &resizer_v4l2_file_ops, + .video = &resizer_v4l2_video_ops, + .pad = &resizer_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * resizer_link_setup - Setup RESIZER connections + * @entity: RESIZER media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int resizer_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_resizer_device *resizer = v4l2_get_subdevdata(sd); + + switch (local->index | media_entity_type(remote->entity)) { + case RESIZER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* Read from previewer - continous mode */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + reset_resizer_channel_mode(resizer); + resizer->input = RESIZER_INPUT_NONE; + break; + } + + if (resizer->input != RESIZER_INPUT_NONE) + return -EBUSY; + + resizer->input = RESIZER_INPUT_PREVIEWER; + + set_resizer_channel_cont_mode(resizer); + break; + case RESIZER_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* Read from memory - single shot mode*/ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + reset_resizer_channel_mode(resizer); + resizer->input = RESIZER_INPUT_NONE; + break; + } + + if (set_resizer_channel_ss_mode(resizer)) + return -EINVAL; + + resizer->input = RESIZER_INPUT_MEMORY; + break; + case RESIZER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + /* Write to memory */ + if (flags & MEDIA_LNK_FL_ENABLED) + resizer->output = RESIZER_OUPUT_MEMORY; + else + resizer->output = RESIZER_OUTPUT_NONE; + + break; + case RESIZER_PAD_SOURCE2 | MEDIA_ENT_T_DEVNODE: + /* Write to memory */ + if (flags & MEDIA_LNK_FL_ENABLED) + resizer->output2 = RESIZER_OUPUT_MEMORY; + else + resizer->output2 = RESIZER_OUTPUT_NONE; + + break; + default: + return -EINVAL; + } + + return 0; +} +static const struct media_entity_operations resizer_media_ops = { + .link_setup = resizer_link_setup, +}; + +/* + * vpfe_resizer_unregister_entities - RESIZER subdevs/video + * driver unregistrations. + * @vpfe_rsz - pointer to resizer subdevice structure. + */ +void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz) +{ + /* unregister video devices */ + vpfe_video_unregister(&vpfe_rsz->video_in); + vpfe_video_unregister(&vpfe_rsz->video_out); + vpfe_video_unregister(&vpfe_rsz->video_out2); + + /* cleanup entity */ + media_entity_cleanup(&vpfe_rsz->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&vpfe_rsz->subdev); +} + +/* + * vpfe_resizer_register_entities - RESIZER subdevs/video + * driver registrations. + * @resizer - pointer to resizer subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +int vpfe_resizer_register_entities(struct vpfe_resizer_device *resizer, + struct v4l2_device *vdev) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(resizer); + unsigned int flags; + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &resizer->subdev); + if (ret < 0) { + printk(KERN_ERR "failed to register resizer as v4l2-subdev\n"); + return ret; + } + + ret = vpfe_video_register(&resizer->video_in, vdev); + if (ret) { + printk(KERN_ERR "failed to register RSZ video-in device\n"); + goto out_video_in_register; + } + + resizer->video_in.vpfe_dev = vpfe_dev; + + ret = vpfe_video_register(&resizer->video_out, vdev); + if (ret) { + printk(KERN_ERR "failed to register RSZ video-out device\n"); + goto out_video_out_register; + } + + resizer->video_out.vpfe_dev = vpfe_dev; + + ret = vpfe_video_register(&resizer->video_out2, vdev); + if (ret) { + printk(KERN_ERR "failed to register RSZ video-out2 device\n"); + goto out_video_out2_register; + } + + resizer->video_out2.vpfe_dev = vpfe_dev; + + flags = 0; + ret = media_entity_create_link(&resizer->video_in.video_dev.entity, 0, + &resizer->subdev.entity, 0, flags); + if (ret < 0) + goto out_create_link; + + ret = media_entity_create_link(&resizer->subdev.entity, 1, + &resizer->video_out.video_dev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + + ret = media_entity_create_link(&resizer->subdev.entity, 2, + &resizer->video_out2.video_dev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + + return 0; + +out_create_link: + vpfe_video_unregister(&resizer->video_out2); +out_video_out2_register: + vpfe_video_unregister(&resizer->video_out); +out_video_out_register: + vpfe_video_unregister(&resizer->video_in); +out_video_in_register: + media_entity_cleanup(&resizer->subdev.entity); + v4l2_device_unregister_subdev(&resizer->subdev); + return ret; +} + +/* + * vpfe_resizer_init - RESIZER module initilaization. + * @vpfe_rsz - pointer to resizer subdevice structure. + * @pdev: platform device pointer. + */ +int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev) +{ + struct imp_logical_channel *channel = &vpfe_rsz->channel; + struct v4l2_subdev *resizer = &vpfe_rsz->subdev; + struct media_pad *pads = &vpfe_rsz->pads[0]; + struct media_entity *me = &resizer->entity; + int ret; + + vpfe_rsz->imp_hw_if = imp_get_hw_if(); + if (!vpfe_rsz->imp_hw_if) + return -EINVAL; + + vpfe_rsz->video_in.ops = &video_in_ops; + vpfe_rsz->video_out.ops = &video_out1_ops; + vpfe_rsz->video_out2.ops = &video_out2_ops; + + v4l2_subdev_init(resizer, &resizer_v4l2_ops); + strlcpy(resizer->name, "DAVINCI RESIZER", sizeof(resizer->name)); + resizer->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(resizer, vpfe_rsz); + resizer->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[RESIZER_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; + pads[RESIZER_PAD_SOURCE].flags = MEDIA_PAD_FL_OUTPUT; + pads[RESIZER_PAD_SOURCE2].flags = MEDIA_PAD_FL_OUTPUT; + + vpfe_rsz->input = RESIZER_INPUT_NONE; + vpfe_rsz->output = RESIZER_OUTPUT_NONE; + vpfe_rsz->output2 = RESIZER_OUTPUT_NONE; + + channel->type = IMP_RESIZER; + channel->config_state = STATE_NOT_CONFIGURED; + + me->ops = &resizer_media_ops; + + ret = media_entity_init(me, RESIZER_PADS_NUM, pads, 0); + if (ret) + return ret; + + vpfe_rsz->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + ret = vpfe_video_init(&vpfe_rsz->video_in, "RSZ"); + if (ret) { + printk(KERN_ERR "failed to init RSZ video-in device\n"); + return ret; + } + + vpfe_rsz->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = vpfe_video_init(&vpfe_rsz->video_out, "RSZ"); + if (ret) { + printk(KERN_ERR "failed to init RSZ video-out device\n"); + return ret; + } + + vpfe_rsz->video_out2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = vpfe_video_init(&vpfe_rsz->video_out2, "RSZB"); + if (ret) { + printk(KERN_ERR "failed to init RSZ video-out2 device\n"); + return ret; + } + + return 0; +} diff --git a/drivers/media/video/davinci/vpfe_resizer.h b/drivers/media/video/davinci/vpfe_resizer.h new file mode 100644 index 0000000..35d60e0 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_resizer.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _VPFE_RSZ_H +#define _VPFE_RSZ_H + +#define RESIZER_PAD_SINK 0 +#define RESIZER_PAD_SOURCE 1 +#define RESIZER_PAD_SOURCE2 2 + +#define RESIZER_PADS_NUM 3 + +enum resizer_input_entity { + RESIZER_INPUT_NONE, + RESIZER_INPUT_MEMORY, + RESIZER_INPUT_PREVIEWER, +}; + +#define RESIZER_OUTPUT_NONE 0 +#define RESIZER_OUPUT_MEMORY (1 << 0) + +struct vpfe_resizer_device { + struct v4l2_subdev subdev; + struct media_pad pads[RESIZER_PADS_NUM]; + struct v4l2_mbus_framefmt formats[RESIZER_PADS_NUM]; + enum resizer_input_entity input; + unsigned int output; + unsigned int output2; + + /* pointer to ipipe function pointers */ + struct imp_hw_interface *imp_hw_if; + struct rsz_channel_config chan_config; + struct imp_logical_channel channel; + + struct vpfe_video_device video_in; + struct vpfe_video_device video_out; + struct vpfe_video_device video_out2; +}; + +void vpfe_resizer_unregister_entities(struct vpfe_resizer_device *vpfe_rsz); +int vpfe_resizer_register_entities(struct vpfe_resizer_device *vpfe_rsz, + struct v4l2_device *v4l2_dev); +int vpfe_resizer_init(struct vpfe_resizer_device *vpfe_rsz, + struct platform_device *pdev); +void rsz_buffer_isr(struct vpfe_resizer_device *resizer); +void vpfe_resizer_cleanup(struct platform_device *pdev); +void rsz_dma_isr(struct vpfe_resizer_device *resizer); + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:44:34 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 16:14:34 +0530 Subject: [RESEND RFC PATCH v4 08/15] davinci: vpfe: previewer driver based on v4l2 media controller framework In-Reply-To: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> References: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321526681-22574-9-git-send-email-manjunath.hadli@ti.com> Add the video previewer driver with the v4l2 media controller framework which takes care of converting the video frames from bayer to YUV or RGB. the driver supports both continuous mode where it works in tandem with the CCDC and single shot mode where it can be used in isolation. The driver underneath uses the dm365 IPIPE module for programming the hardware. The driver supports previewer as a subdevice and a media entity, and the enumerable pads are 2(1 input and 1 output). The driver supports streaing and all the pad and link related operations. Specific functions like defect pixel correction, LUT are supported through private ioctls. Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/vpfe_previewer.c | 1064 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpfe_previewer.h | 65 ++ 2 files changed, 1129 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpfe_previewer.c create mode 100644 drivers/media/video/davinci/vpfe_previewer.h diff --git a/drivers/media/video/davinci/vpfe_previewer.c b/drivers/media/video/davinci/vpfe_previewer.c new file mode 100644 index 0000000..d40234f --- /dev/null +++ b/drivers/media/video/davinci/vpfe_previewer.c @@ -0,0 +1,1064 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Nagabhushana Netagunte + */ + +#include +#include +#include +#include +#include +#include +#include "vpfe_capture.h" +#include "imp_hw_if.h" +#include "vpss.h" + +#define MIN_OUT_WIDTH 32 +#define MIN_OUT_HEIGHT 32 + +static int serializer_initialized; +static struct imp_serializer imp_serializer_info; + +/* previewer input format descriptions */ +static const unsigned int prev_input_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_SBGGR10_1X10, + V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8, +#ifdef CONFIG_ARCH_DAVINCI_DM365 + V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, +#endif +}; + +/* previewer ouput format descriptions */ +static const unsigned int prev_output_fmts[] = { + V4L2_MBUS_FMT_UYVY8_2X8, +}; + +/* + * imp_set_preview_config - set previewer config + * @previewer: vpfe previewer device pointer + * @channel: image processor logical channel + * @chan_config: previewer channel configuration + */ +static int imp_set_preview_config(struct vpfe_previewer_device *previewer, + struct imp_logical_channel *channel, + struct prev_channel_config *chan_config) +{ + struct imp_hw_interface *imp_hw_if = previewer->imp_hw_if; + struct vpfe_device *vpfe_dev = to_vpfe_device(previewer); + struct device *dev = previewer->subdev.v4l2_dev->dev; + int len = 0; + int ret; + + if (channel->config_state == STATE_NOT_CONFIGURED) { + channel->config = imp_hw_if->alloc_config_block(dev, + vpfe_dev->ipipe); + /* allocate buffer for holding user configuration */ + channel->user_config = imp_hw_if->alloc_user_config_block(dev, + vpfe_dev->ipipe, IMP_PREVIEWER, + &len); + if (!channel->user_config) { + dev_err(dev, + "memory allocate failed for user config\n"); + return -EFAULT; + } + channel->user_config_size = len; + } + + if (!chan_config->config) { + dev_dbg(dev, "imp_set_preview_config.. default.\n"); + /* put defaults for user configuration */ + imp_hw_if->set_user_config_defaults(dev, vpfe_dev->ipipe, + IMP_PREVIEWER, channel->user_config); + } else { + dev_dbg(dev, "imp_set_preview_config.. user config\n"); + memcpy(channel->user_config, chan_config->config, + channel->user_config_size); + } + + /* Update the user configuration in the hw config block */ + ret = imp_hw_if->set_preview_config(dev, vpfe_dev->ipipe, + channel->user_config, channel->config); + + if (ret < 0) + dev_err(dev, "set preview config failed\n"); + + channel->config_state = STATE_CONFIGURED; + + return ret; +} + +/* + * imp_get_preview_config - get current previewer config + * @previewer: vpfe previewer device pointer + * @channel: image processor logical channel + * @chan_config: previewer channel configuration + */ +static int imp_get_preview_config(struct vpfe_previewer_device *previewer, + struct imp_logical_channel *channel, + struct prev_channel_config *chan_config) +{ + struct device *dev = previewer->subdev.v4l2_dev->dev; + + if (channel->config_state != STATE_CONFIGURED) { + dev_err(dev, "channel not configured\n"); + return -EINVAL; + } + + if (!chan_config->config) { + dev_err(dev, "channel configuration NULL ptr\n"); + return -EINVAL; + } + + if (copy_to_user((void *)chan_config->config, + (void *)channel->user_config, + channel->user_config_size)) { + dev_err(dev, "Error in copy to user\n"); + return -EFAULT; + } + + return 0; +} + +/* + * imp_get_module_interface - get module interface + * @previewer: vpfe previewer device pointer + * @module_id: module id + */ +static struct prev_module_if +*imp_get_module_interface(struct vpfe_previewer_device *previewer, + unsigned short module_id) +{ + struct imp_hw_interface *imp_hw_if = previewer->imp_hw_if; + struct device *dev = previewer->subdev.v4l2_dev->dev; + struct prev_module_if *module_if; + unsigned int index = 0; + + while (1) { + module_if = imp_hw_if->prev_enum_modules(dev, index); + if (!module_if) + break; + if (module_if->module_id == module_id) + break; + index++; + } + + return module_if; +} + +/* + * imp_init_serializer - intilaize serializer + */ +static int imp_init_serializer(void) +{ + if (serializer_initialized) + return 0; + + memset((void *)&imp_serializer_info, (char)0, + sizeof(struct imp_serializer)); + init_completion(&imp_serializer_info.sem_isr); + imp_serializer_info.sem_isr.done = 0; + imp_serializer_info.array_count = 0; + mutex_init(&imp_serializer_info.array_sem); + serializer_initialized = 1; + + return 0; +} + +/* + * set_channel_prv_cont_mode - Set continous channel mode + * @previewer: vpfe previewer device pointer + */ +static int set_channel_prv_cont_mode(struct vpfe_previewer_device *previewer) +{ + struct imp_logical_channel *channel = &previewer->channel; + struct imp_hw_interface *imp_hw_if = previewer->imp_hw_if; + struct vpfe_device *vpfe_dev = to_vpfe_device(previewer); + + channel->type = IMP_PREVIEWER; + channel->config_state = STATE_NOT_CONFIGURED; + + return imp_hw_if->set_oper_mode(vpfe_dev->ipipe, IMP_MODE_CONTINUOUS); +} + +/* + * set_channel_prv_ss_mode - Set single-shot channel mode + * @previewer: vpfe previewer device pointer + */ +static int set_channel_prv_ss_mode(struct vpfe_previewer_device *previewer) +{ + struct imp_logical_channel *channel = &previewer->channel; + struct imp_hw_interface *imp_hw_if = previewer->imp_hw_if; + struct vpfe_device *vpfe_dev = to_vpfe_device(previewer); + + channel->type = IMP_PREVIEWER; + channel->config_state = STATE_NOT_CONFIGURED; + + return imp_hw_if->set_oper_mode(vpfe_dev->ipipe, IMP_MODE_SINGLE_SHOT); +} + +/* + * reset_channel_prv_mode - Reset channel mode + * @previewer: vpfe previewer device pointer + */ +static void reset_channel_prv_mode(struct vpfe_previewer_device *previewer) +{ + struct imp_logical_channel *channel = &previewer->channel; + struct imp_hw_interface *imp_hw_if = previewer->imp_hw_if; + struct vpfe_device *vpfe_dev = to_vpfe_device(previewer); + + imp_hw_if->reset_oper_mode(vpfe_dev->ipipe); + + if (channel->config_state == STATE_CONFIGURED) { + kfree(channel->user_config); + memset(channel, 0, sizeof(struct imp_logical_channel)); + } +} + +/* + * prv_video_in_queue - PREVIEWER video in queue + * @vpfe_dev: vpfe device pointer + * @addr: buffer address + */ +static void prv_video_in_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_previewer_device *previewer = &vpfe_dev->vpfe_previewer; + struct imp_hw_interface *imp_hw_if = previewer->imp_hw_if; + struct imp_logical_channel *chan = &previewer->channel; + + if (previewer->input == PREVIEWER_INPUT_MEMORY) + imp_hw_if->update_inbuf_address(chan->config, addr); + else + imp_hw_if->update_inbuf_address(NULL, addr); +} + +/* + * prv_video_out_queue - PREVIEWER video out queue + * @vpfe_dev: vpfe device pointer + * @addr: buffer address + */ +static void prv_video_out_queue(struct vpfe_device *vpfe_dev, + unsigned long addr) +{ + struct vpfe_previewer_device *previewer = &vpfe_dev->vpfe_previewer; + struct imp_hw_interface *imp_hw_if = previewer->imp_hw_if; + struct imp_logical_channel *chan = &previewer->channel; + + if (previewer->input == PREVIEWER_INPUT_MEMORY) + imp_hw_if->update_outbuf1_address(vpfe_dev->ipipe, + chan->config, addr); + else + imp_hw_if->update_outbuf1_address(vpfe_dev->ipipe, NULL, addr); +} + +static const struct vpfe_video_operations video_in_ops = { + .queue = prv_video_in_queue, +}; + +static const struct vpfe_video_operations video_out_ops = { + .queue = prv_video_out_queue, +}; + +/* + * prv_ss_buffer_isr - PREVIEWER module single-shot buffer scheduling isr + * @previewer: PREVIEWER device pointer + * + */ +static void prv_ss_buffer_isr(struct vpfe_previewer_device *previewer) +{ + struct vpfe_video_device *video_out = &previewer->video_out; + struct vpfe_video_device *video_in = &previewer->video_in; + struct imp_hw_interface *imp_hw_if = previewer->imp_hw_if; + struct vpfe_device *vpfe_dev = to_vpfe_device(previewer); + struct imp_logical_channel *chan = &previewer->channel; + struct vpfe_pipeline *pipe = &video_out->pipe; + u32 val; + + if (!video_in->started) + return; + + if (previewer->output == PREVIEWER_OUTPUT_MEMORY) { + val = vpss_dma_complete_interrupt(); + if (val != 0 && val != 2) + return; + } + + if (previewer->input == PREVIEWER_INPUT_MEMORY) { + spin_lock(&video_in->dma_queue_lock); + vpfe_process_buffer_complete(video_in); + video_in->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_schedule_next_buffer(video_in); + spin_unlock(&video_in->dma_queue_lock); + } + + if (previewer->output == PREVIEWER_OUTPUT_MEMORY) { + spin_lock(&video_out->dma_queue_lock); + vpfe_process_buffer_complete(video_out); + video_out->state = VPFE_VIDEO_BUFFER_NOT_QUEUED; + vpfe_schedule_next_buffer(video_out); + spin_unlock(&video_out->dma_queue_lock); + + /* start HW if buffers are queued */ + if (is_pipe_ready(pipe)) + imp_hw_if->enable(vpfe_dev->ipipe, 1, chan->config); + } +} + +/* + * prv_buffer_isr - PREVIEWER module buffer scheduling isr + * @previewer: PREVIEWER device pointer + * + */ +void prv_buffer_isr(struct vpfe_previewer_device *previewer) +{ + struct vpfe_video_device *video_out = &previewer->video_out; + struct vpfe_device *vpfe_dev = to_vpfe_device(previewer); + enum v4l2_field field; + int fid; + + if (!video_out->started) + return; + + if (previewer->input != PREVIEWER_INPUT_CCDC) + return; + + field = video_out->fmt.fmt.pix.field; + + if (field == V4L2_FIELD_NONE) { + /* handle progressive frame capture */ + if (video_out->cur_frm != video_out->next_frm) + vpfe_process_buffer_complete(video_out); + return; + } + /* handle interlaced frame capture */ + fid = ccdc_get_fid(vpfe_dev); + + /* switch the software maintained field id */ + video_out->field_id ^= 1; + if (fid == video_out->field_id) { + /* + * we are in-sync here,continue. + * One frame is just being captured. If the + * next frame is available, release the current + * frame and move on + */ + if (fid == 0 && video_out->cur_frm != video_out->next_frm) + vpfe_process_buffer_complete(video_out); + } else if (fid == 0) { + /* + * out of sync. Recover from any hardware out-of-sync. + * May loose one frame + */ + video_out->field_id = fid; + } +} + +/* + * prv_dma_isr - PREVIEWER module dma isr + * @previewer: PREVIEWER device pointer + * + */ +void prv_dma_isr(struct vpfe_previewer_device *previewer) +{ + struct vpfe_video_device *video_out = &previewer->video_out; + struct vpfe_device *vpfe_dev = to_vpfe_device(previewer); + enum v4l2_field field; + int schedule_capture = 0; + int fid; + + if (previewer->input == PREVIEWER_INPUT_MEMORY) { + prv_ss_buffer_isr(previewer); + return; + } + + if (!video_out->started) + return; + + field = video_out->fmt.fmt.pix.field; + + if (field == V4L2_FIELD_NONE) { + if (!list_empty(&video_out->dma_queue) && + video_out->cur_frm == video_out->next_frm) + schedule_capture = 1; + } else { + fid = ccdc_get_fid(vpfe_dev); + if (fid == video_out->field_id) { + /* we are in-sync here,continue */ + if (fid == 1 && !list_empty(&video_out->dma_queue) && + video_out->cur_frm == video_out->next_frm) + schedule_capture = 1; + } + } + if (!schedule_capture) + return; + spin_lock(&video_out->dma_queue_lock); + + vpfe_schedule_next_buffer(video_out); + spin_unlock(&video_out->dma_queue_lock); + + return; +} + +/* + * previewer_ioctl - PREVIEWER module private ioctl's + * @sd: VPFE PREVIEWER V4L2 subdevice + * @cmd: ioctl command + * @arg: ioctl argument + * + * Return 0 on success or a negative error code otherwise. + */ +static long previewer_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct vpfe_previewer_device *previewer = v4l2_get_subdevdata(sd); + struct imp_hw_interface *imp_hw_if = previewer->imp_hw_if; + struct vpfe_device *vpfe_dev = to_vpfe_device(previewer); + struct imp_logical_channel *chan = &previewer->channel; + struct device *dev = previewer->subdev.v4l2_dev->dev; + struct prev_channel_config *user_config; + struct prev_module_param *module_param; + struct prev_cap *cap; + + struct prev_module_if *module_if = NULL; + int ret = 0; + + if (!(void *)arg) + return -EFAULT; + + switch (cmd) { + case PREV_ENUM_CAP: + dev_dbg(dev, "PREV_ENUM_CAP:\n"); + cap = (struct prev_cap *)arg; + module_if = imp_hw_if->prev_enum_modules(dev, cap->index); + if (!module_if) { + dev_dbg(dev, "PREV_ENUM_CAP - Last module\n"); + return -EINVAL; + } + strcpy(cap->version, module_if->version); + cap->module_id = module_if->module_id; + cap->control = module_if->control; + cap->path = module_if->path; + strcpy(cap->module_name, module_if->module_name); + break; + case PREV_S_PARAM: + dev_dbg(dev, "PREV_S_PARAM:\n"); + module_param = (struct prev_module_param *)arg; + if (chan->config_state != STATE_CONFIGURED) { + dev_err(dev, "Channel not configured\n"); + return -EINVAL; + } + module_if = imp_get_module_interface(previewer, + module_param->module_id); + if (!module_if) { + dev_err(dev, "Invalid module id\n"); + return -EINVAL; + } + if (strcmp(module_if->version, module_param->version)) { + dev_err(dev, "Invalid module version\n"); + return -EINVAL; + } + /* we have a valid */ + ret = module_if->set(dev, vpfe_dev->ipipe, module_param->param, + module_param->len); + if (ret < 0) { + dev_err(dev, "error in PREV_S_PARAM\n"); + return -EINVAL; + } + break; + case PREV_G_PARAM: + dev_dbg(dev, "PREV_G_PARAM:\n"); + module_param = (struct prev_module_param *)arg; + if (!module_param) + return -EINVAL; + + module_if = imp_get_module_interface(previewer, + module_param->module_id); + if (!module_if) { + dev_err(dev, "Invalid module id\n"); + return -EINVAL; + } + if (strcmp(module_if->version, module_param->version)) { + dev_err(dev, "Invalid module version\n"); + return -EINVAL; + } + + ret = module_if->get(dev, vpfe_dev->ipipe, module_param->param, + module_param->len); + if (ret < 0) { + dev_err(dev, "error in PREV_G_PARAM\n"); + return ret; + } + break; + case PREV_S_CONFIG: + dev_dbg(dev, "PREV_S_CONFIG:\n"); + ret = imp_set_preview_config(previewer, chan, + (struct prev_channel_config *)arg); + break; + case PREV_G_CONFIG: + dev_dbg(dev, "PREV_G_CONFIG:\n"); + user_config = (struct prev_channel_config *)arg; + if (!user_config->config) { + dev_err(dev, "error in PREV_GET_CONFIG\n"); + return -EINVAL; + } + ret = imp_get_preview_config(previewer, chan, user_config); + break; + default: + ret = -ENOIOCTLCMD; + } + + return ret; +} + +/* + * previewer_set_stream - Enable/Disable streaming on the PREVIEWER module + * @sd: VPFE PREVIEWER V4L2 subdevice + * @enable: Enable/disable stream + */ +static int previewer_set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_previewer_device *previewer = v4l2_get_subdevdata(sd); + struct imp_hw_interface *imp_hw_if = previewer->imp_hw_if; + struct vpfe_device *vpfe_dev = to_vpfe_device(previewer); + struct imp_logical_channel *chan = &previewer->channel; + struct device *dev = previewer->subdev.v4l2_dev->dev; + + /* in case of single shot, send config to ipipe */ + void *config = (previewer->input == PREVIEWER_INPUT_MEMORY) ? + chan->config : NULL; + + if (previewer->output != PREVIEWER_OUTPUT_MEMORY) + return 0; + + switch (enable) { + case 1: + if (previewer->input == PREVIEWER_INPUT_MEMORY && + imp_hw_if->serialize(vpfe_dev->ipipe)) { + if (imp_hw_if->hw_setup(dev, vpfe_dev->ipipe, + config) < 0) + return -EINVAL; + } else if (previewer->input == PREVIEWER_INPUT_CCDC) { + imp_hw_if->lock_chain(vpfe_dev->ipipe); + if (imp_hw_if->hw_setup(dev, vpfe_dev->ipipe, NULL)) + return -EINVAL; + } + imp_hw_if->enable(vpfe_dev->ipipe, 1, config); + break; + case 0: + imp_hw_if->enable(vpfe_dev->ipipe, 0, config); + if (previewer->input == PREVIEWER_INPUT_CCDC) + imp_hw_if->unlock_chain(vpfe_dev->ipipe); + break; + } + + return 0; +} + +/* + * Retrieve active or try format based on the request + */ +static struct v4l2_mbus_framefmt * +__previewer_get_format(struct vpfe_previewer_device *prev, + struct v4l2_subdev_fh *fh, + unsigned int pad, enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_format(fh, pad); + return &prev->formats[pad]; +} + +/* + * preview_try_format - Handle try format by pad subdev method + * @prev: VPFE preview device + * @fh : V4L2 subdev file handle + * @pad: pad num + * @fmt: pointer to v4l2 format structure + */ +static void preview_try_format(struct vpfe_previewer_device *prev, + struct v4l2_subdev_fh *fh, unsigned int pad, + struct v4l2_mbus_framefmt *fmt, + enum v4l2_subdev_format_whence which) +{ + struct imp_hw_interface *imp_hw_if; + unsigned int max_out_height; + unsigned int max_out_width; + unsigned int i; + + imp_hw_if = prev->imp_hw_if; + max_out_width = imp_hw_if->get_max_output_width(0); + max_out_height = imp_hw_if->get_max_output_height(0); + + if (pad == PREVIEWER_PAD_SINK) { + for (i = 0; i < ARRAY_SIZE(prev_input_fmts); i++) { + if (fmt->code == prev_input_fmts[i]) + break; + } + + /* If not found, use SBGGR10 as default */ + if (i >= ARRAY_SIZE(prev_input_fmts)) + fmt->code = V4L2_MBUS_FMT_SBGGR10_1X10; + } else if (pad == PREVIEWER_PAD_SOURCE) { + for (i = 0; i < ARRAY_SIZE(prev_output_fmts); i++) { + if (fmt->code == prev_output_fmts[i]) + break; + } + + /* If not found, use UYVY as default */ + if (i >= ARRAY_SIZE(prev_output_fmts)) + fmt->code = V4L2_MBUS_FMT_UYVY8_2X8; + } + + fmt->width = clamp_t(u32, fmt->width, MIN_OUT_HEIGHT, max_out_width); + fmt->height = clamp_t(u32, fmt->height, MIN_OUT_WIDTH, max_out_height); +} + +/* + * previewer_set_format - Set the video format on a pad + * @sd : VPFE PREVIEWER V4L2 subdevice + * @fh : V4L2 subdev file handle + * @fmt: Format + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int previewer_set_format(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_previewer_device *previewer = v4l2_get_subdevdata(sd); + struct imp_hw_interface *imp_hw_if = previewer->imp_hw_if; + struct vpfe_device *vpfe_dev = to_vpfe_device(previewer); + struct v4l2_mbus_framefmt *format; + + format = __previewer_get_format(previewer, fh, fmt->pad, fmt->which); + if (format == NULL) + return -EINVAL; + + preview_try_format(previewer, fh, fmt->pad, &fmt->format, fmt->which); + *format = fmt->format; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) + return 0; + + if (fmt->pad == PREVIEWER_PAD_SINK && + (previewer->input == PREVIEWER_INPUT_CCDC || + previewer->input == PREVIEWER_INPUT_MEMORY)) { + /* + *In continous mode,set IPEPE input format here. + */ + imp_hw_if->set_in_mbus_format(vpfe_dev->ipipe, &fmt->format); + previewer->formats[fmt->pad] = fmt->format; + + } else if (fmt->pad == PREVIEWER_PAD_SOURCE && + previewer->output == PREVIEWER_OUTPUT_RESIZER) { + previewer->formats[fmt->pad] = fmt->format; + } else if (fmt->pad == PREVIEWER_PAD_SOURCE && + previewer->output == PREVIEWER_OUTPUT_MEMORY) { + imp_hw_if->set_out_mbus_format(vpfe_dev->ipipe, &fmt->format); + previewer->formats[fmt->pad] = fmt->format; + } else { + return -EINVAL; + } + + return 0; +} + +/* + * previewer_get_format - Retrieve the video format on a pad + * @sd : VPFE PREVIEWER V4L2 subdevice + * @fh : V4L2 subdev file handle + * @fmt: Format + * + * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond + * to the format type. + */ +static int previewer_get_format(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct vpfe_previewer_device *previewer = v4l2_get_subdevdata(sd); + + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + fmt->format = previewer->formats[fmt->pad]; + else + fmt->format = *(v4l2_subdev_get_try_format(fh, fmt->pad)); + + return 0; +} + +/* + * previewer_enum_frame_size - enum frame sizes on pads + * @sd: VPFE previewer V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_frame_size_enum structure + */ +static int previewer_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct vpfe_previewer_device *prev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt format; + + if (fse->index != 0) + return -EINVAL; + + format.code = fse->code; + format.width = 1; + format.height = 1; + preview_try_format(prev, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->min_width = format.width; + fse->min_height = format.height; + + if (format.code != fse->code) + return -EINVAL; + + format.code = fse->code; + format.width = -1; + format.height = -1; + preview_try_format(prev, fh, fse->pad, &format, + V4L2_SUBDEV_FORMAT_TRY); + fse->max_width = format.width; + fse->max_height = format.height; + + return 0; +} + +/* + * previewer_enum_mbus_code - enum mbus codes for pads + * @sd: VPFE previewer V4L2 subdevice + * @fh: V4L2 subdev file handle + * @code: pointer to v4l2_subdev_mbus_code_enum structure + */ +static int previewer_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + switch (code->pad) { + case PREVIEWER_PAD_SINK: + if (code->index >= ARRAY_SIZE(prev_input_fmts)) + return -EINVAL; + + code->code = prev_input_fmts[code->index]; + break; + case PREVIEWER_PAD_SOURCE: + if (code->index >= ARRAY_SIZE(prev_output_fmts)) + return -EINVAL; + + code->code = prev_output_fmts[code->index]; + break; + default: + return -EINVAL; + } + + return 0; +} + +/* + * previewer_init_formats - Initialize formats on all pads + * @sd: VPFE previewer V4L2 subdevice + * @fh: V4L2 subdev file handle + * + * Initialize all pad formats with default values. If fh is not NULL, try + * formats are initialized on the file handle. Otherwise active formats are + * initialized on the device. + */ +static int previewer_init_formats(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh) +{ + struct vpfe_previewer_device *prv = v4l2_get_subdevdata(sd); + struct imp_hw_interface *imp_hw_if = prv->imp_hw_if; + struct v4l2_subdev_format format; + + memset(&format, 0, sizeof(format)); + format.pad = PREVIEWER_PAD_SINK; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_SBGGR10_1X10; + format.format.width = imp_hw_if->get_max_output_width(0); + format.format.height = imp_hw_if->get_max_output_height(0); + previewer_set_format(sd, fh, &format); + + memset(&format, 0, sizeof(format)); + format.pad = PREVIEWER_PAD_SOURCE; + format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; + format.format.code = V4L2_MBUS_FMT_UYVY8_2X8; + format.format.width = imp_hw_if->get_max_output_width(0); + format.format.height = imp_hw_if->get_max_output_height(0); + previewer_set_format(sd, fh, &format); + + return 0; +} + +/* subdev core operations */ +static const struct v4l2_subdev_core_ops previewer_v4l2_core_ops = { + .ioctl = previewer_ioctl, +}; + +/* subdev file operations */ +static const struct v4l2_subdev_file_ops previewer_v4l2_file_ops = { + .open = previewer_init_formats, +}; + +/* subdev video operations */ +static const struct v4l2_subdev_video_ops previewer_v4l2_video_ops = { + .s_stream = previewer_set_stream, +}; + +/* subdev pad operations */ +static const struct v4l2_subdev_pad_ops previewer_v4l2_pad_ops = { + .enum_mbus_code = previewer_enum_mbus_code, + .enum_frame_size = previewer_enum_frame_size, + .get_fmt = previewer_get_format, + .set_fmt = previewer_set_format, +}; + +/* v4l2 subdev operation */ +static const struct v4l2_subdev_ops previewer_v4l2_ops = { + .core = &previewer_v4l2_core_ops, + .file = &previewer_v4l2_file_ops, + .video = &previewer_v4l2_video_ops, + .pad = &previewer_v4l2_pad_ops, +}; + +/* + * Media entity operations + */ + +/* + * previewer_link_setup - Setup PREVIEWER connections + * @entity: PREVIEWER media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int previewer_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_previewer_device *previewer = v4l2_get_subdevdata(sd); + + switch (local->index | media_entity_type(remote->entity)) { + case PREVIEWER_PAD_SINK | MEDIA_ENT_T_V4L2_SUBDEV: + /* Read from ccdc - continous mode */ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + previewer->input = PREVIEWER_INPUT_NONE; + reset_channel_prv_mode(previewer); + break; + } + if (previewer->input != PREVIEWER_INPUT_NONE) + return -EBUSY; + + if (set_channel_prv_cont_mode(previewer)) + return -EINVAL; + previewer->input = PREVIEWER_INPUT_CCDC; + break; + case PREVIEWER_PAD_SINK | MEDIA_ENT_T_DEVNODE: + /* Read from memory - single shot mode*/ + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + previewer->input = PREVIEWER_INPUT_NONE; + reset_channel_prv_mode(previewer); + break; + } + if (set_channel_prv_ss_mode(previewer)) + return -EINVAL; + + previewer->input = PREVIEWER_INPUT_MEMORY; + break; + case PREVIEWER_PAD_SOURCE | MEDIA_ENT_T_V4L2_SUBDEV: + /* out to RESIZER */ + if (flags & MEDIA_LNK_FL_ENABLED) + previewer->output = PREVIEWER_OUTPUT_RESIZER; + else + previewer->output = PREVIEWER_OUTPUT_NONE; + + break; + case PREVIEWER_PAD_SOURCE | MEDIA_ENT_T_DEVNODE: + /* Write to memory */ + if (flags & MEDIA_LNK_FL_ENABLED) + previewer->output = PREVIEWER_OUTPUT_MEMORY; + else + previewer->output = PREVIEWER_OUTPUT_NONE; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations previewer_media_ops = { + .link_setup = previewer_link_setup, +}; + +/* + * vpfe_previewer_unregister_entities - PREVIEWER subdevs/video + * driver unregistrations. + * @vpfe_prev - pointer to previewer subdevice structure. + */ +void vpfe_previewer_unregister_entities(struct vpfe_previewer_device *vpfe_prev) +{ + /* unregister video devices */ + vpfe_video_unregister(&vpfe_prev->video_in); + vpfe_video_unregister(&vpfe_prev->video_out); + + /* cleanup entity */ + media_entity_cleanup(&vpfe_prev->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&vpfe_prev->subdev); +} + +/* + * vpfe_previewer_register_entities - PREVIEWER subdevs/video + * driver registrations. + * @previewer - pointer to previewer subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +int vpfe_previewer_register_entities(struct vpfe_previewer_device *previewer, + struct v4l2_device *vdev) +{ + struct vpfe_device *vpfe_dev = to_vpfe_device(previewer); + unsigned int flags; + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &previewer->subdev); + if (ret) { + printk(KERN_ERR "failed to register previewer as v4l2 subdevice\n"); + return ret; + } + + ret = vpfe_video_register(&previewer->video_in, vdev); + if (ret) { + printk(KERN_ERR "failed to register previewer video-in device\n"); + goto out_video_in_register; + } + + previewer->video_in.vpfe_dev = vpfe_dev; + + ret = vpfe_video_register(&previewer->video_out, vdev); + if (ret) { + printk(KERN_ERR "failed to register previewer video-out device\n"); + goto out_video_out_register; + } + + previewer->video_out.vpfe_dev = vpfe_dev; + + flags = 0; + ret = media_entity_create_link(&previewer->video_in.video_dev.entity, 0, + &previewer->subdev.entity, 0, flags); + if (ret < 0) + goto out_create_link; + + ret = media_entity_create_link(&previewer->subdev.entity, 1, + &previewer->video_out.video_dev.entity, + 0, flags); + if (ret < 0) + goto out_create_link; + + return 0; + +out_create_link: + vpfe_video_unregister(&previewer->video_out); +out_video_out_register: + vpfe_video_unregister(&previewer->video_in); +out_video_in_register: + media_entity_cleanup(&previewer->subdev.entity); + v4l2_device_unregister_subdev(&previewer->subdev); + return ret; +} + +/* + * vpfe_previewer_init - PREVIEWER module initilaization. + * @vpfe_prev - pointer to previewer subdevice structure. + * @pdev: platform device pointer. + */ +int vpfe_previewer_init(struct vpfe_previewer_device *previewer, + struct platform_device *pdev) +{ + struct imp_logical_channel *channel = &previewer->channel; + struct vpfe_device *vpfe_dev = to_vpfe_device(previewer); + struct media_pad *pads = &previewer->pads[0]; + struct v4l2_subdev *sd = &previewer->subdev; + struct media_entity *me = &sd->entity; + int ret = 0; + + if (ipipe_init(&vpfe_dev->ipipe)) + return -EINVAL; + + previewer->imp_hw_if = imp_get_hw_if(); + if (!previewer->imp_hw_if) + return -EINVAL; + + previewer->video_in.ops = &video_in_ops; + previewer->video_out.ops = &video_out_ops; + + v4l2_subdev_init(sd, &previewer_v4l2_ops); + strlcpy(sd->name, "DAVINCI PREVIEWER", sizeof(sd->name)); + sd->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(sd, previewer); + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + pads[PREVIEWER_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; + pads[PREVIEWER_PAD_SOURCE].flags = MEDIA_PAD_FL_OUTPUT; + + previewer->input = PREVIEWER_INPUT_NONE; + previewer->output = PREVIEWER_OUTPUT_NONE; + + channel->type = IMP_PREVIEWER; + channel->config_state = STATE_NOT_CONFIGURED; + + me->ops = &previewer_media_ops; + + ret = media_entity_init(me, PREVIEWER_PADS_NUM, pads, 0); + if (ret) + return ret; + + previewer->video_in.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + ret = vpfe_video_init(&previewer->video_in, "PRV"); + if (ret) { + printk(KERN_ERR "failed to init PRV video-in device\n"); + goto out_ipipe_init; + } + previewer->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = vpfe_video_init(&previewer->video_out, "PRV"); + if (ret) { + printk(KERN_ERR "failed to init PRV video-out device\n"); + goto out_ipipe_init; + } + imp_init_serializer(); + + return 0; + +out_ipipe_init: + ipipe_cleanup(vpfe_dev->ipipe); + return ret; +} + +/* + * vpfe_previewer_cleanup - PREVIEWER module cleanup. + * @dev: Device pointer specific to the VPFE. + */ +void vpfe_previewer_cleanup(struct platform_device *pdev, void *ipipe) +{ + ipipe_cleanup(ipipe); +} diff --git a/drivers/media/video/davinci/vpfe_previewer.h b/drivers/media/video/davinci/vpfe_previewer.h new file mode 100644 index 0000000..855f321 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_previewer.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _VPFE_PREV_H +#define _VPFE_PREV_H + +#define PREVIEWER_PAD_SINK 0 +#define PREVIEWER_PAD_SOURCE 1 + +#define PREVIEWER_PADS_NUM 2 + +#define PREVIEWER_OUTPUT_NONE 0 +#define PREVIEWER_OUTPUT_MEMORY (1 << 0) +#define PREVIEWER_OUTPUT_RESIZER (1 << 1) + +enum previewer_input_entity { + PREVIEWER_INPUT_NONE, + PREVIEWER_INPUT_MEMORY, + PREVIEWER_INPUT_CCDC, +}; + + +struct vpfe_previewer_device { + struct v4l2_subdev subdev; + struct media_pad pads[PREVIEWER_PADS_NUM]; + struct v4l2_mbus_framefmt formats[PREVIEWER_PADS_NUM]; + enum previewer_input_entity input; + unsigned int output; + + /* pointer to ipipe function pointers */ + struct imp_hw_interface *imp_hw_if; + struct prev_channel_config prv_config; + struct imp_logical_channel channel; + + struct vpfe_video_device video_in; + struct vpfe_video_device video_out; +}; + +int vpfe_previewer_register_entities(struct vpfe_previewer_device *vpfe_prev, + struct v4l2_device *v4l2_dev); +int vpfe_previewer_init(struct vpfe_previewer_device *vpfe_prev, + struct platform_device *pdev); +void vpfe_previewer_unregister_entities + (struct vpfe_previewer_device *vpfe_prev); +void prv_buffer_isr(struct vpfe_previewer_device *previewer); +void vpfe_previewer_cleanup(struct platform_device *pdev, void *ipipe); +void prv_dma_isr(struct vpfe_previewer_device *previewer); +void enable_serializer(void *ipipe, int val); +void ipipe_cleanup(void *ipipe); +int ipipe_init(void **ipipe); +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:44:30 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 16:14:30 +0530 Subject: [RESEND RFC PATCH v4 04/15] davinci: vpfe: add support for CCDC hardware for dm365 In-Reply-To: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> References: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321526681-22574-5-git-send-email-manjunath.hadli@ti.com> add support for ccdc on dm365 SoC. ccdc is responsible for capturing video data- both raw bayer through sync seperate signals and through BT656/1120 interfaces. This driver implements the hardware functionality. Mainly- setting of hardware, validation of parameters, and isr configuration. Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/ccdc_types.h | 43 + drivers/media/video/davinci/dm365_ccdc.c | 1456 +++++++++++++++++++++++++ drivers/media/video/davinci/dm365_ccdc.h | 91 ++ drivers/media/video/davinci/dm365_ccdc_regs.h | 309 ++++++ include/linux/dm365_ccdc.h | 611 +++++++++++ include/media/davinci/ccdc_types.h | 43 - 6 files changed, 2510 insertions(+), 43 deletions(-) create mode 100644 drivers/media/video/davinci/ccdc_types.h create mode 100644 drivers/media/video/davinci/dm365_ccdc.c create mode 100644 drivers/media/video/davinci/dm365_ccdc.h create mode 100644 drivers/media/video/davinci/dm365_ccdc_regs.h create mode 100644 include/linux/dm365_ccdc.h delete mode 100644 include/media/davinci/ccdc_types.h diff --git a/drivers/media/video/davinci/ccdc_types.h b/drivers/media/video/davinci/ccdc_types.h new file mode 100644 index 0000000..5773874 --- /dev/null +++ b/drivers/media/video/davinci/ccdc_types.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + **************************************************************************/ +#ifndef _CCDC_TYPES_H +#define _CCDC_TYPES_H +enum ccdc_pixfmt { + CCDC_PIXFMT_RAW, + CCDC_PIXFMT_YCBCR_16BIT, + CCDC_PIXFMT_YCBCR_8BIT +}; + +enum ccdc_frmfmt { + CCDC_FRMFMT_PROGRESSIVE, + CCDC_FRMFMT_INTERLACED +}; + +/* PIXEL ORDER IN MEMORY from LSB to MSB */ +/* only applicable for 8-bit input mode */ +enum ccdc_pixorder { + CCDC_PIXORDER_YCBYCR, + CCDC_PIXORDER_CBYCRY, +}; + +enum ccdc_buftype { + CCDC_BUFTYPE_FLD_INTERLEAVED, + CCDC_BUFTYPE_FLD_SEPARATED +}; +#endif diff --git a/drivers/media/video/davinci/dm365_ccdc.c b/drivers/media/video/davinci/dm365_ccdc.c new file mode 100644 index 0000000..c3f3b19 --- /dev/null +++ b/drivers/media/video/davinci/dm365_ccdc.c @@ -0,0 +1,1456 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include +#include +#include +#include "dm365_ccdc.h" +#include "vpss.h" +#include "dm365_ccdc_regs.h" +#include "ccdc_hw_device.h" + +static struct device *dev; + +/* Defauts for module configuation paramaters */ +static struct ccdc_config_params_raw ccdc_config_defaults = { + .linearize = { + .corr_shft = CCDC_NO_SHIFT, + .scale_fact = {1, 0}, + }, + .gain_offset = { + .gain = { + .r_ye = {1, 0}, + .gr_cy = {1, 0}, + .gb_g = {1, 0}, + .b_mg = {1, 0}, + }, + }, + .culling = { + .hcpat_odd = 0xff, + .hcpat_even = 0xff, + .vcpat = 0xff + }, +}; + +/* ISIF operation configuration */ +struct ccdc_oper_config { + enum v4l2_mbus_pixelcode if_type; + struct ccdc_ycbcr_config ycbcr; + struct ccdc_params_raw bayer; + enum ccdc_data_pack data_pack; + void *__iomem base_addr; + void *__iomem linear_tbl0_addr; + void *__iomem linear_tbl1_addr; +}; + +static struct ccdc_oper_config ccdc_cfg = { + .ycbcr = { + .v4l2_pix_fmt = V4L2_PIX_FMT_UYVY, + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .win = CCDC_WIN_NTSC, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED, + }, + .bayer = { + .v4l2_pix_fmt = V4L2_PIX_FMT_SGRBG10ALAW8, + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .gain = { + .r_ye = {1, 0}, + .gr_cy = {1, 0}, + .gb_g = {1, 0}, + .b_mg = {1, 0}, + }, + .cfa_pat = CCDC_CFA_PAT_MOSAIC, + .data_msb = CCDC_BIT_MSB_11, + .config_params = { + .data_size = CCDC_12_BITS, + .data_shift = CCDC_NO_SHIFT, + .col_pat_field0 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED, + }, + .col_pat_field1 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED, + }, + }, + }, + .data_pack = CCDC_DATA_PACK8, +}; + +/* Raw Bayer formats */ +static u32 ccdc_raw_bayer_pix_formats[] = { V4L2_PIX_FMT_SGRBG10ALAW8, + V4L2_PIX_FMT_SGRBG10DPCM8, + V4L2_PIX_FMT_SBGGR16 }; + +/* Raw YUV formats */ +static u32 ccdc_raw_yuv_pix_formats[] = { V4L2_PIX_FMT_UYVY, + V4L2_PIX_FMT_YUYV }; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return readl(ccdc_cfg.base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + writel(val, ccdc_cfg.base_addr + offset); +} + +static inline u32 ccdc_merge(u32 mask, u32 val, u32 offset) +{ + u32 new_val = (regr(offset) & ~mask) | (val & mask); + + regw(new_val, offset); + + return new_val; +} + +static inline void regw_lin_tbl(u32 val, u32 offset, int i) +{ + if (!i) + writel(val, ccdc_cfg.linear_tbl0_addr + offset); + else + writel(val, ccdc_cfg.linear_tbl1_addr + offset); +} + +static void ccdc_disable_all_modules(void) +{ + /* disable BC */ + regw(0, CLAMPCFG); + /* disable vdfc */ + regw(0, DFCCTL); + /* disable CSC */ + regw(0, CSCCTL); + /* disable linearization */ + regw(0, LINCFG0); + /* disable other modules here as they are supported */ +} + +static void ccdc_enable(int en) +{ + if (!en) { + /* Before disable isif, disable all ISIF modules */ + ccdc_disable_all_modules(); + /** + * wait for next VD. Assume lowest scan rate is 12 Hz. So + * 100 msec delay is good enough + */ + } + msleep(100); + ccdc_merge(CCDC_SYNCEN_VDHDEN_MASK, en, SYNCEN); +} + +static void ccdc_enable_output_to_sdram(int en) +{ + ccdc_merge(CCDC_SYNCEN_WEN_MASK, en << CCDC_SYNCEN_WEN_SHIFT, SYNCEN); +} + +static void ccdc_config_culling(struct ccdc_cul *cul) +{ + u32 val; + + /* Horizontal pattern */ + val = cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT; + val |= cul->hcpat_odd; + regw(val, CULH); + + /* vertical pattern */ + regw(cul->vcpat, CULV); + + /* LPF */ + ccdc_merge((CCDC_LPF_MASK << CCDC_LPF_SHIFT), + (cul->en_lpf << CCDC_LPF_SHIFT), MODESET); +} + +static void ccdc_config_gain_offset(void) +{ + struct ccdc_gain_offsets_adj *gain_off_ptr = + &ccdc_cfg.bayer.config_params.gain_offset; + u32 val; + + val = ((gain_off_ptr->gain_sdram_en & 1) << GAIN_SDRAM_EN_SHIFT) | + ((gain_off_ptr->gain_ipipe_en & 1) << GAIN_IPIPE_EN_SHIFT) | + ((gain_off_ptr->gain_h3a_en & 1) << GAIN_H3A_EN_SHIFT) | + ((gain_off_ptr->offset_sdram_en & 1) << OFST_SDRAM_EN_SHIFT) | + ((gain_off_ptr->offset_ipipe_en & 1) << OFST_IPIPE_EN_SHIFT) | + ((gain_off_ptr->offset_h3a_en & 1) << OFST_H3A_EN_SHIFT); + + ccdc_merge(GAIN_OFFSET_EN_MASK, val, CGAMMAWD); + + val = (gain_off_ptr->gain.r_ye.integer & GAIN_INTEGER_MASK) + << GAIN_INTEGER_SHIFT; + val |= ccdc_cfg.bayer.config_params.gain_offset.gain.r_ye.decimal & + GAIN_DECIMAL_MASK; + regw(val, CRGAIN); + + val = (gain_off_ptr->gain.gr_cy.integer & GAIN_INTEGER_MASK) << + GAIN_INTEGER_SHIFT; + val |= gain_off_ptr->gain.gr_cy.decimal & GAIN_DECIMAL_MASK; + + regw(val, CGRGAIN); + + val = (gain_off_ptr->gain.gb_g.integer & GAIN_INTEGER_MASK) << + GAIN_INTEGER_SHIFT; + val |= gain_off_ptr->gain.gb_g.decimal & GAIN_DECIMAL_MASK; + regw(val, CGBGAIN); + + val = (gain_off_ptr->gain.b_mg.integer & GAIN_INTEGER_MASK) << + GAIN_INTEGER_SHIFT; + val |= gain_off_ptr->gain.b_mg.decimal & GAIN_DECIMAL_MASK; + regw(val, CBGAIN); + + regw((gain_off_ptr->offset & OFFSET_MASK), COFSTA); +} + +static void ccdc_restore_defaults(void) +{ + enum vpss_ccdc_source_sel source = VPSS_CCDCIN; + int i; + + memcpy(&ccdc_cfg.bayer.config_params, &ccdc_config_defaults, + sizeof(struct ccdc_config_params_raw)); + + dev_dbg(dev, "\nstarting ccdc_restore_defaults..."); + /* Enable clock to ISIF, IPIPEIF and BL */ + vpss_enable_clock(VPSS_CCDC_CLOCK, 1); + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); + vpss_enable_clock(VPSS_BL_CLOCK, 1); + + /* set all registers to default value */ + for (i = 0; i <= 0x1f8; i += 4) + regw(0, i); + + /* no culling support */ + regw(0xffff, CULH); + regw(0xff, CULV); + + /* Set default offset and gain */ + ccdc_config_gain_offset(); + + vpss_select_ccdc_source(source); + + dev_dbg(dev, "\nEnd of ccdc_restore_defaults..."); +} + +static int ccdc_open(struct device *device) +{ + dev = device; + ccdc_restore_defaults(); + + return 0; +} + +/* This function will configure the window size to be capture in CCDC reg */ +static void ccdc_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, int ppc, int mode) +{ + int horz_nr_pixels; + int vert_nr_lines; + int horz_start; + int vert_start; + int mid_img; + + dev_dbg(dev, "\nStarting ccdc_setwin..."); + /** + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = (image_win->width << (ppc - 1)) - 1; + + /* Writing the horizontal info into the registers */ + regw(horz_start & START_PX_HOR_MASK, SPH); + regw(horz_nr_pixels & NUM_PX_HOR_MASK, LNH); + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + } else { + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* configure VDINT0 and VDINT1 */ + mid_img = vert_start + (image_win->height / 2); + regw(mid_img, VDINT1); + } + + if (!mode) + regw(0, VDINT0); + else + regw(vert_nr_lines, VDINT0); + regw(vert_start & START_VER_ONE_MASK, SLV0); + regw(vert_start & START_VER_TWO_MASK, SLV1); + regw(vert_nr_lines & NUM_LINES_VER, LNV); +} + +static void ccdc_config_bclamp(struct ccdc_black_clamp *bc) +{ + u32 val; + + /** + * DC Offset is always added to image data irrespective of bc enable + * status + */ + val = bc->dc_offset & CCDC_BC_DCOFFSET_MASK; + regw(val, CLDCOFST); + + if (!bc->en) + return; + + val = (bc->bc_mode_color & CCDC_BC_MODE_COLOR_MASK) << + CCDC_BC_MODE_COLOR_SHIFT; + + /* Enable BC and horizontal clamp caculation paramaters */ + val = val | 1 | ((bc->horz.mode & CCDC_HORZ_BC_MODE_MASK) << + CCDC_HORZ_BC_MODE_SHIFT); + + regw(val, CLAMPCFG); + + if (bc->horz.mode != CCDC_HORZ_BC_DISABLE) { + /** + * Window count for calculation + * Base window selection + * pixel limit + * Horizontal size of window + * vertical size of the window + * Horizontal start position of the window + * Vertical start position of the window + */ + val = (bc->horz.win_count_calc & CCDC_HORZ_BC_WIN_COUNT_MASK) | + ((bc->horz.base_win_sel_calc & 1) << + CCDC_HORZ_BC_WIN_SEL_SHIFT) | + ((bc->horz.clamp_pix_limit & 1) << + CCDC_HORZ_BC_PIX_LIMIT_SHIFT) | + ((bc->horz.win_h_sz_calc & + CCDC_HORZ_BC_WIN_H_SIZE_MASK) << + CCDC_HORZ_BC_WIN_H_SIZE_SHIFT) | + ((bc->horz.win_v_sz_calc & + CCDC_HORZ_BC_WIN_V_SIZE_MASK) << + CCDC_HORZ_BC_WIN_V_SIZE_SHIFT); + + regw(val, CLHWIN0); + + val = bc->horz.win_start_h_calc & CCDC_HORZ_BC_WIN_START_H_MASK; + regw(val, CLHWIN1); + + val = bc->horz.win_start_v_calc & CCDC_HORZ_BC_WIN_START_V_MASK; + regw(val, CLHWIN2); + } + + /* vertical clamp caculation paramaters */ + + /* OB H Valid */ + val = bc->vert.ob_h_sz_calc & CCDC_VERT_BC_OB_H_SZ_MASK; + + /* Reset clamp value sel for previous line */ + val |= (bc->vert.reset_val_sel & CCDC_VERT_BC_RST_VAL_SEL_MASK) << + CCDC_VERT_BC_RST_VAL_SEL_SHIFT; + + /* Line average coefficient */ + val |= bc->vert.line_ave_coef << CCDC_VERT_BC_LINE_AVE_COEF_SHIFT; + regw(val, CLVWIN0); + + /* Configured reset value */ + if (bc->vert.reset_val_sel == CCDC_VERT_BC_USE_CONFIG_CLAMP_VAL) { + val = bc->vert.reset_clamp_val & CCDC_VERT_BC_RST_VAL_MASK; + regw(val, CLVRV); + } + + /* Optical Black horizontal start position */ + val = bc->vert.ob_start_h & CCDC_VERT_BC_OB_START_HORZ_MASK; + regw(val, CLVWIN1); + + /* Optical Black vertical start position */ + val = bc->vert.ob_start_v & CCDC_VERT_BC_OB_START_VERT_MASK; + regw(val, CLVWIN2); + + val = bc->vert.ob_v_sz_calc & CCDC_VERT_BC_OB_VERT_SZ_MASK; + regw(val, CLVWIN3); + + /* Vertical start position for BC subtraction */ + val = bc->vert_start_sub & CCDC_BC_VERT_START_SUB_V_MASK; + regw(val, CLSV); +} + +static void ccdc_config_linearization(struct ccdc_linearize *linearize) +{ + u32 val; + u32 i; + + if (!linearize->en) { + regw(0, LINCFG0); + return; + } + + /* shift value for correction */ + val = (linearize->corr_shft & CCDC_LIN_CORRSFT_MASK) << + CCDC_LIN_CORRSFT_SHIFT; + /* enable */ + val |= 1; + regw(val, LINCFG0); + + /* Scale factor */ + val = (linearize->scale_fact.integer & 1) << + CCDC_LIN_SCALE_FACT_INTEG_SHIFT; + val |= linearize->scale_fact.decimal & CCDC_LIN_SCALE_FACT_DECIMAL_MASK; + regw(val, LINCFG1); + + for (i = 0; i < CCDC_LINEAR_TAB_SIZE; i++) { + val = linearize->table[i] & CCDC_LIN_ENTRY_MASK; + if (i%2) + regw_lin_tbl(val, ((i >> 1) << 2), 1); + else + regw_lin_tbl(val, ((i >> 1) << 2), 0); + } +} + +static void ccdc_config_dfc(struct ccdc_dfc *vdfc) +{ +#define DFC_WRITE_WAIT_COUNT 1000 + u32 count = DFC_WRITE_WAIT_COUNT; + u32 val; + int i; + + if (!vdfc->en) + return; + + /* Correction mode */ + val = (vdfc->corr_mode & CCDC_VDFC_CORR_MOD_MASK) << + CCDC_VDFC_CORR_MOD_SHIFT; + + /* Correct whole line or partial */ + if (vdfc->corr_whole_line) + val |= 1 << CCDC_VDFC_CORR_WHOLE_LN_SHIFT; + + /* level shift value */ + val |= (vdfc->def_level_shift & CCDC_VDFC_LEVEL_SHFT_MASK) << + CCDC_VDFC_LEVEL_SHFT_SHIFT; + + regw(val, DFCCTL); + + /* Defect saturation level */ + val = vdfc->def_sat_level & CCDC_VDFC_SAT_LEVEL_MASK; + regw(val, VDFSATLV); + + regw(vdfc->table[0].pos_vert & CCDC_VDFC_POS_MASK, DFCMEM0); + regw(vdfc->table[0].pos_horz & CCDC_VDFC_POS_MASK, DFCMEM1); + if (vdfc->corr_mode == CCDC_VDFC_NORMAL || + vdfc->corr_mode == CCDC_VDFC_HORZ_INTERPOL_IF_SAT) { + regw(vdfc->table[0].level_at_pos, DFCMEM2); + regw(vdfc->table[0].level_up_pixels, DFCMEM3); + regw(vdfc->table[0].level_low_pixels, DFCMEM4); + } + + val = regr(DFCMEMCTL); + /* set DFCMARST and set DFCMWR */ + val |= 1 << CCDC_DFCMEMCTL_DFCMARST_SHIFT; + val |= 1; + regw(val, DFCMEMCTL); + + while (count && (regr(DFCMEMCTL) & 0x01)) + count--; + + val = regr(DFCMEMCTL); + if (!count) { + dev_dbg(dev, "defect table write timeout !!!\n"); + return; + } + + for (i = 1; i < vdfc->num_vdefects; i++) { + regw(vdfc->table[i].pos_vert & CCDC_VDFC_POS_MASK, DFCMEM0); + regw(vdfc->table[i].pos_horz & CCDC_VDFC_POS_MASK, DFCMEM1); + if (vdfc->corr_mode == CCDC_VDFC_NORMAL || + vdfc->corr_mode == CCDC_VDFC_HORZ_INTERPOL_IF_SAT) { + regw(vdfc->table[i].level_at_pos, DFCMEM2); + regw(vdfc->table[i].level_up_pixels, DFCMEM3); + regw(vdfc->table[i].level_low_pixels, DFCMEM4); + } + val = regr(DFCMEMCTL); + /* clear DFCMARST and set DFCMWR */ + val &= ~(1 << CCDC_DFCMEMCTL_DFCMARST_SHIFT); + val |= 1; + regw(val, DFCMEMCTL); + + count = DFC_WRITE_WAIT_COUNT; + while (count && (regr(DFCMEMCTL) & 0x01)) + count--; + + val = regr(DFCMEMCTL); + if (!count) { + dev_err(dev, "defect table write timeout !!!\n"); + return; + } + } + if (vdfc->num_vdefects < CCDC_VDFC_TABLE_SIZE) { + /* Extra cycle needed */ + regw(0, DFCMEM0); + regw(0x1FFF, DFCMEM1); + val = 1; + regw(val, DFCMEMCTL); + } + + /* enable VDFC */ + ccdc_merge((1 << CCDC_VDFC_EN_SHIFT), (1 << CCDC_VDFC_EN_SHIFT), + DFCCTL); + + ccdc_merge((1 << CCDC_VDFC_EN_SHIFT), (0 << CCDC_VDFC_EN_SHIFT), + DFCCTL); + + regw(0x6, DFCMEMCTL); + for (i = 0 ; i < vdfc->num_vdefects; i++) { + count = DFC_WRITE_WAIT_COUNT; + while (count && (regr(DFCMEMCTL) & 0x2)) + count--; + + val = regr(DFCMEMCTL); + if (!count) { + dev_err(dev, "defect table write timeout !!!\n"); + return; + } + + val = regr(DFCMEM0) | regr(DFCMEM1) | regr(DFCMEM2) | + regr(DFCMEM3) | regr(DFCMEM4); + regw(0x2, DFCMEMCTL); + } +} + +static void ccdc_config_csc(struct ccdc_df_csc *df_csc) +{ + u32 val1; + u32 val2; + u32 i; + + if (!df_csc->csc.en) { + regw(0, CSCCTL); + return; + } + + /* initialize all bits to 0 */ + val1 = 0; + + for (i = 0; i < CCDC_CSC_NUM_COEFF; i++) { + if ((i % 2) == 0) { + /* CSCM - LSB */ + val1 = ((df_csc->csc.coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) << + CCDC_CSC_COEF_INTEG_SHIFT) | + ((df_csc->csc.coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK)); + } else { + + /* CSCM - MSB */ + val2 = ((df_csc->csc.coeff[i].integer & + CCDC_CSC_COEF_INTEG_MASK) << + CCDC_CSC_COEF_INTEG_SHIFT) | + ((df_csc->csc.coeff[i].decimal & + CCDC_CSC_COEF_DECIMAL_MASK)); + val2 <<= CCDC_CSCM_MSB_SHIFT; + val2 |= val1; + regw(val2, (CSCM0 + ((i-1) << 1))); + } + } + + /* program the active area */ + regw(df_csc->start_pix & CCDC_DF_CSC_SPH_MASK, FMTSPH); + /** + * one extra pixel as required for CSC. Actually number of + * pixel - 1 should be configured in this register. So we + * need to subtract 1 before writing to FMTSPH, but we will + * not do this since csc requires one extra pixel + */ + regw((df_csc->num_pixels) & CCDC_DF_CSC_SPH_MASK, FMTLNH); + regw(df_csc->start_line & CCDC_DF_CSC_SPH_MASK, FMTSLV); + /** + * one extra line as required for CSC. See reason documented for + * num_pixels + */ + regw((df_csc->num_lines) & CCDC_DF_CSC_SPH_MASK, FMTLNV); + + /* Enable CSC */ + regw(1, CSCCTL); +} + +static int ccdc_config_raw(int mode) +{ + struct ccdc_params_raw *params = &ccdc_cfg.bayer; + struct ccdc_config_params_raw *module_params = + &ccdc_cfg.bayer.config_params; + struct vpss_pg_frame_size frame_size; + struct vpss_sync_pol sync; + u32 val; + + dev_dbg(dev, "\nStarting ccdc_config_raw..\n"); + + /* In case of user has set BT656IF earlier, it should be reset + when configuring for raw input. + */ + regw(0, REC656IF); + + /* Configure CCDCFG register */ + + /** + * Set CCD Not to swap input since input is RAW data + * Set FID detection function to Latch at V-Sync + * Set WENLOG - ccdc valid area + * Set TRGSEL + * Set EXTRG + * Packed to 8 or 16 bits + */ + + val = CCDC_YCINSWP_RAW | CCDC_CCDCFG_FIDMD_LATCH_VSYNC | + CCDC_CCDCFG_WENLOG_AND | CCDC_CCDCFG_TRGSEL_WEN | + CCDC_CCDCFG_EXTRG_DISABLE | (ccdc_cfg.data_pack & + CCDC_DATA_PACK_MASK); + + dev_dbg(dev, "Writing 0x%x to ...CCDCFG\n", val); + regw(val, CCDCFG); + + /** + * Configure the vertical sync polarity(MODESET.VDPOL) + * Configure the horizontal sync polarity (MODESET.HDPOL) + * Configure frame id polarity (MODESET.FLDPOL) + * Configure data polarity + * Configure External WEN Selection + * Configure frame format(progressive or interlace) + * Configure pixel format (Input mode) + * Configure the data shift + */ + + val = CCDC_VDHDOUT_INPUT | ((params->vd_pol & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT) | ((params->hd_pol & CCDC_HD_POL_MASK) << + CCDC_HD_POL_SHIFT) | ((params->fid_pol & CCDC_FID_POL_MASK) << + CCDC_FID_POL_SHIFT) | ((CCDC_DATAPOL_NORMAL & + CCDC_DATAPOL_MASK) << CCDC_DATAPOL_SHIFT) | ((CCDC_EXWEN_DISABLE & + CCDC_EXWEN_MASK) << CCDC_EXWEN_SHIFT) | ((params->frm_fmt & + CCDC_FRM_FMT_MASK) << CCDC_FRM_FMT_SHIFT) | ((params->pix_fmt & + CCDC_INPUT_MASK) << CCDC_INPUT_SHIFT) | ((params->config_params. + data_shift & CCDC_DATASFT_MASK) << CCDC_DATASFT_SHIFT); + + regw(val, MODESET); + dev_dbg(dev, "Writing 0x%x to MODESET...\n", val); + + /** + * Configure GAMMAWD register + * CFA pattern setting + */ + val = (params->cfa_pat & CCDC_GAMMAWD_CFA_MASK) << + CCDC_GAMMAWD_CFA_SHIFT; + + /* Gamma msb */ + if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10ALAW8) + val = val | CCDC_ALAW_ENABLE; + + val = val | ((params->data_msb & CCDC_ALAW_GAMA_WD_MASK) << + CCDC_ALAW_GAMA_WD_SHIFT); + + regw(val, CGAMMAWD); + + /* Configure DPCM compression settings */ + if (params->v4l2_pix_fmt == V4L2_PIX_FMT_SGRBG10DPCM8) { + val = 1 << CCDC_DPCM_EN_SHIFT; + val |= (module_params->pred & + CCDC_DPCM_PREDICTOR_MASK) << CCDC_DPCM_PREDICTOR_SHIFT; + } + + regw(val, MISC); + /* Configure Gain & Offset */ + + ccdc_config_gain_offset(); + + /* Configure Color pattern */ + val = params->config_params.col_pat_field0.olop | + (params->config_params.col_pat_field0.olep << 2) | + (params->config_params.col_pat_field0.elop << 4) | + (params->config_params.col_pat_field0.elep << 6) | + (params->config_params.col_pat_field1.olop << 8) | + (params->config_params.col_pat_field1.olep << 10) | + (params->config_params.col_pat_field1.elop << 12) | + (params->config_params.col_pat_field1.elep << 14); + regw(val, CCOLP); + dev_dbg(dev, "Writing %x to CCOLP ...\n", val); + + /* Configure HSIZE register */ + val = (params->horz_flip_en & CCDC_HSIZE_FLIP_MASK) << + CCDC_HSIZE_FLIP_SHIFT; + + /* calculate line offset in 32 bytes based on pack value */ + if (ccdc_cfg.data_pack == CCDC_PACK_8BIT) + val |= ((params->win.width + 31) >> 5) & CCDC_LINEOFST_MASK; + else if (ccdc_cfg.data_pack == CCDC_PACK_12BIT) + val |= ((((params->win.width + (params->win.width >> 2)) + + 31) >> 5) & CCDC_LINEOFST_MASK); + else + val |= (((params->win.width * 2) + 31) >> 5) & + CCDC_LINEOFST_MASK; + regw(val, HSIZE); + + /* Configure SDOFST register */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_en) { + /* For interlace inverse mode */ + regw(0x4B6D, SDOFST); + dev_dbg(dev, "Writing 0x4B6D to SDOFST...\n"); + } else { + /* For interlace non inverse mode */ + regw(0x0B6D, SDOFST); + dev_dbg(dev, "Writing 0x0B6D to SDOFST...\n"); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + if (params->image_invert_en) { + /* For progessive inverse mode */ + regw(0x4000, SDOFST); + dev_dbg(dev, "Writing 0x4000 to SDOFST...\n"); + } else { + /* For progessive non inverse mode */ + regw(0x0000, SDOFST); + dev_dbg(dev, "Writing 0x0000 to SDOFST...\n"); + } + } + + /* Configure video window */ + ccdc_setwin(¶ms->win, params->frm_fmt, 1, mode); + + /* Configure Black Clamp */ + ccdc_config_bclamp(&module_params->bclamp); + + /* Configure Vertical Defection Pixel Correction */ + ccdc_config_dfc(&module_params->dfc); + + if (!module_params->df_csc.df_or_csc) + /* Configure Color Space Conversion */ + ccdc_config_csc(&module_params->df_csc); + + ccdc_config_linearization(&module_params->linearize); + + /* Configure Culling */ + ccdc_config_culling(&module_params->culling); + + /* Configure Horizontal and vertical offsets(DFC,LSC,Gain) */ + val = module_params->horz_offset & CCDC_DATA_H_OFFSET_MASK; + regw(val, DATAHOFST); + + val = module_params->vert_offset & CCDC_DATA_V_OFFSET_MASK; + regw(val, DATAVOFST); + + /* Setup test pattern if enabled */ + if (params->config_params.test_pat_gen) { + /* Use the HD/VD pol settings from user */ + sync.ccdpg_hdpol = params->hd_pol & CCDC_HD_POL_MASK; + sync.ccdpg_vdpol = params->vd_pol & CCDC_VD_POL_MASK; + + vpss_set_sync_pol(sync); + + frame_size.hlpfr = ccdc_cfg.bayer.win.width; + frame_size.pplen = ccdc_cfg.bayer.win.height; + vpss_set_pg_frame_size(frame_size); + vpss_select_ccdc_source(VPSS_PGLPBK); + } + + dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n"); + + return 0; +} + +static int ccdc_validate_df_csc_params(struct ccdc_df_csc *df_csc) +{ + struct ccdc_color_space_conv *csc; + int err = -EINVAL; + int csc_df_en; + int i; + + if (!df_csc->df_or_csc) { + /* csc configuration */ + csc = &df_csc->csc; + if (csc->en) { + csc_df_en = 1; + for (i = 0; i < CCDC_CSC_NUM_COEFF; i++) { + if (csc->coeff[i].integer > + CCDC_CSC_COEF_INTEG_MASK || + csc->coeff[i].decimal > + CCDC_CSC_COEF_DECIMAL_MASK) { + dev_dbg(dev, + "invalid csc coefficients\n"); + return err; + } + } + } + } + + if (df_csc->start_pix > CCDC_DF_CSC_SPH_MASK) { + dev_dbg(dev, "invalid df_csc start pix value\n"); + return err; + } + if (df_csc->num_pixels > CCDC_DF_NUMPIX) { + dev_dbg(dev, "invalid df_csc num pixels value\n"); + return err; + } + if (df_csc->start_line > CCDC_DF_CSC_LNH_MASK) { + dev_dbg(dev, "invalid df_csc start_line value\n"); + return err; + } + if (df_csc->num_lines > CCDC_DF_NUMLINES) { + dev_dbg(dev, "invalid df_csc num_lines value\n"); + return err; + } + + return 0; +} + +static int ccdc_validate_dfc_params(struct ccdc_dfc *dfc) +{ + int err = -EINVAL; + int i; + + if (!dfc->en) + return 0; + + if (dfc->corr_whole_line > 1) { + dev_dbg(dev, "invalid corr_whole_line value\n"); + return err; + } + + if (dfc->def_level_shift > 4) { + dev_dbg(dev, "invalid def_level_shift value\n"); + return err; + } + + if (dfc->def_sat_level > 4095) { + dev_dbg(dev, "invalid def_sat_level value\n"); + return err; + } + + if (!dfc->num_vdefects || dfc->num_vdefects > 8) { + dev_dbg(dev, "invalid num_vdefects value\n"); + return err; + } + + for (i = 0; i < CCDC_VDFC_TABLE_SIZE; i++) { + if (dfc->table[i].pos_vert > 0x1fff) { + dev_dbg(dev, "invalid pos_vert value\n"); + return err; + } + + if (dfc->table[i].pos_horz > 0x1fff) { + dev_dbg(dev, "invalid pos_horz value\n"); + return err; + } + } + + return 0; +} + +static int ccdc_validate_bclamp_params(struct ccdc_black_clamp *bclamp) +{ + int err = -EINVAL; + + if (bclamp->dc_offset > 0x1fff) { + dev_dbg(dev, "invalid bclamp dc_offset value\n"); + return err; + } + + if (!bclamp->en) + return 0; + + if (bclamp->horz.clamp_pix_limit > 1) { + dev_dbg(dev, + "invalid bclamp horz clamp_pix_limit value\n"); + return err; + } + + if (bclamp->horz.win_count_calc < 1 || + bclamp->horz.win_count_calc > 32) { + dev_dbg(dev, "invalid bclamp horz win_count_calc value\n"); + return err; + } + + if (bclamp->horz.win_start_h_calc > 0x1fff) { + dev_dbg(dev, "invalid bclamp win_start_v_calc value\n"); + return err; + } + + if (bclamp->horz.win_start_v_calc > 0x1fff) { + dev_dbg(dev, "invalid bclamp win_start_v_calc value\n"); + return err; + } + + if (bclamp->vert.reset_clamp_val > 0xfff) { + dev_dbg(dev, "invalid bclamp reset_clamp_val value\n"); + return err; + } + + if (bclamp->vert.ob_v_sz_calc > 0x1fff) { + dev_dbg(dev, "invalid bclamp ob_v_sz_calc value\n"); + return err; + } + + if (bclamp->vert.ob_start_h > 0x1fff) { + dev_dbg(dev, "invalid bclamp ob_start_h value\n"); + return err; + } + + if (bclamp->vert.ob_start_v > 0x1fff) { + dev_dbg(dev, "invalid bclamp ob_start_h value\n"); + return err; + } + + return 0; +} + +static int ccdc_validate_gain_ofst_params(struct ccdc_gain_offsets_adj + *gain_offset) +{ + int err = -EINVAL; + + if (gain_offset->gain_sdram_en || gain_offset->gain_ipipe_en || + gain_offset->gain_h3a_en) { + if (gain_offset->gain.r_ye.integer > 7 || + gain_offset->gain.r_ye.decimal > 0x1ff) { + dev_dbg(dev, "invalid gain r_ye\n"); + return err; + } + if ((gain_offset->gain.gr_cy.integer > 7) || + (gain_offset->gain.gr_cy.decimal > 0x1ff)) { + dev_dbg(dev, "invalid gain gr_cy\n"); + return err; + } + if (gain_offset->gain.gb_g.integer > 7 || + gain_offset->gain.gb_g.decimal > 0x1ff) { + dev_dbg(dev, "invalid gain gb_g\n"); + return err; + } + if (gain_offset->gain.b_mg.integer > 7 || + gain_offset->gain.b_mg.decimal > 0x1ff) { + dev_dbg(dev, "invalid gain b_mg\n"); + return err; + } + } + if ((gain_offset->offset_sdram_en || gain_offset->offset_ipipe_en || + gain_offset->offset_h3a_en) && gain_offset->offset > 0xfff) { + dev_dbg(dev, "invalid gain b_mg\n"); + return err; + } + + return 0; +} + +static int +validate_ccdc_config_params_raw(struct ccdc_config_params_raw *params) +{ + int err; + + err = ccdc_validate_df_csc_params(¶ms->df_csc); + if (err) + goto exit; + err = ccdc_validate_dfc_params(¶ms->dfc); + if (err) + goto exit; + err = ccdc_validate_bclamp_params(¶ms->bclamp); + if (err) + goto exit; + err = ccdc_validate_gain_ofst_params(¶ms->gain_offset); +exit: + return err; +} + +static int ccdc_set_buftype(enum ccdc_buftype buf_type) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) + ccdc_cfg.bayer.buf_type = buf_type; + else + ccdc_cfg.ycbcr.buf_type = buf_type; + + return 0; +} + +static enum ccdc_buftype ccdc_get_buftype(void) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) + return ccdc_cfg.bayer.buf_type; + + return ccdc_cfg.ycbcr.buf_type; +} + +static int ccdc_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) { + if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { + *pix = ccdc_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(ccdc_raw_yuv_pix_formats)) { + *pix = ccdc_raw_yuv_pix_formats[i]; + ret = 0; + } + } + + return ret; +} + +static int ccdc_set_pixel_format(unsigned int pixfmt) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) { + if (pixfmt == V4L2_PIX_FMT_SBGGR16) + ccdc_cfg.data_pack = CCDC_PACK_16BIT; + else if ((pixfmt == V4L2_PIX_FMT_SGRBG10DPCM8) || + (pixfmt == V4L2_PIX_FMT_SGRBG10ALAW8)) + ccdc_cfg.data_pack = CCDC_PACK_8BIT; + else + return -EINVAL; + + ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + ccdc_cfg.bayer.v4l2_pix_fmt = pixfmt; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + + ccdc_cfg.data_pack = CCDC_PACK_8BIT; + ccdc_cfg.ycbcr.v4l2_pix_fmt = pixfmt; + } + + return 0; +} + +static u32 ccdc_get_pixel_format(void) +{ + u32 pixfmt; + + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) + pixfmt = ccdc_cfg.bayer.v4l2_pix_fmt; + else + pixfmt = ccdc_cfg.ycbcr.v4l2_pix_fmt; + + return pixfmt; +} + +static int ccdc_set_image_window(struct v4l2_rect *win) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) { + ccdc_cfg.bayer.win.top = win->top; + ccdc_cfg.bayer.win.left = win->left; + ccdc_cfg.bayer.win.width = win->width; + ccdc_cfg.bayer.win.height = win->height; + } else { + ccdc_cfg.ycbcr.win.top = win->top; + ccdc_cfg.ycbcr.win.left = win->left; + ccdc_cfg.ycbcr.win.width = win->width; + ccdc_cfg.ycbcr.win.height = win->height; + } + + return 0; +} + +static void ccdc_get_image_window(struct v4l2_rect *win) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) + *win = ccdc_cfg.bayer.win; + else + *win = ccdc_cfg.ycbcr.win; +} + +static unsigned int ccdc_get_line_length(void) +{ + unsigned int len; + + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) { + if (ccdc_cfg.data_pack == CCDC_PACK_8BIT) + len = ((ccdc_cfg.bayer.win.width)); + else if (ccdc_cfg.data_pack == CCDC_PACK_12BIT) + len = (ccdc_cfg.bayer.win.width * 2) + + (ccdc_cfg.bayer.win.width >> 2); + else + len = ccdc_cfg.bayer.win.width * 2; + } else { + len = ccdc_cfg.ycbcr.win.width * 2; + } + + return ALIGN(len, 32); +} + +static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) + ccdc_cfg.bayer.frm_fmt = frm_fmt; + else + ccdc_cfg.ycbcr.frm_fmt = frm_fmt; + + return 0; +} + +static enum ccdc_frmfmt ccdc_get_frame_format(void) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) + return ccdc_cfg.bayer.frm_fmt; + else + return ccdc_cfg.ycbcr.frm_fmt; +} + +static int ccdc_getfid(void) +{ + return (regr(MODESET) >> 15) & 0x1; +} + +/* misc operations */ +static void ccdc_setfbaddr(unsigned long addr) +{ + regw((addr >> 21) & 0x07ff, CADU); + regw((addr >> 5) & 0x0ffff, CADL); +} + +static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + ccdc_cfg.if_type = params->if_type; + + switch (params->if_type) { + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + case V4L2_MBUS_FMT_Y8_1X8: + ccdc_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT; + break; + case V4L2_MBUS_FMT_YUYV10_1X20: + case V4L2_MBUS_FMT_YUYV8_1X16: + ccdc_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_16BIT; + break; + case V4L2_MBUS_FMT_SBGGR10_1X10: + ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + break; + default: + dev_dbg(dev, "Invalid interface type\n"); + return -EINVAL; + } + + return 0; +} + +/* Parameter operations */ +static int ccdc_get_params(void *params) +{ + /* only raw module parameters can be set through the IOCTL */ + if (ccdc_cfg.if_type != V4L2_MBUS_FMT_SBGGR10_1X10) + return -EINVAL; + + memcpy(params, &ccdc_cfg.bayer.config_params, + sizeof(ccdc_cfg.bayer.config_params)); + + return 0; +} + +/* Parameter operations */ +static int ccdc_set_params(void *params) +{ + struct ccdc_config_params_raw ccdc_raw_params; + int ret = -EINVAL; + + /* only raw module parameters can be set through the IOCTL */ + if (ccdc_cfg.if_type != V4L2_MBUS_FMT_SBGGR10_1X10) + return ret; + + memcpy(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); + + if (!validate_ccdc_config_params_raw(&ccdc_raw_params)) { + memcpy(&ccdc_cfg.bayer.config_params, &ccdc_raw_params, + sizeof(ccdc_raw_params)); + ret = 0; + } + + return ret; +} + +/* This function will configure CCDC for YCbCr parameters. */ +static int ccdc_config_ycbcr(int mode) +{ + struct ccdc_ycbcr_config *params = &ccdc_cfg.ycbcr; + struct vpss_pg_frame_size frame_size; + struct vpss_sync_pol sync; + u32 modeset; + u32 ccdcfg; + + /** + * first reset the CCDC + * all registers have default values after reset + * This is important since we assume default values to be set in + * a lot of registers that we didn't touch + */ + dev_dbg(dev, "\nStarting ccdc_config_ycbcr..."); + + /* start with all bits zero */ + modeset = 0; + ccdcfg = 0; + + /* configure pixel format or input mode */ + modeset = modeset | ((params->pix_fmt & CCDC_INPUT_MASK) << + CCDC_INPUT_SHIFT) | ((params->frm_fmt & CCDC_FRM_FMT_MASK) << + CCDC_FRM_FMT_SHIFT) | (((params->fid_pol & + CCDC_FID_POL_MASK) << CCDC_FID_POL_SHIFT)) | + (((params->hd_pol & CCDC_HD_POL_MASK) << CCDC_HD_POL_SHIFT)) | + (((params->vd_pol & CCDC_VD_POL_MASK) << CCDC_VD_POL_SHIFT)); + + /* pack the data to 8-bit CCDCCFG */ + switch (ccdc_cfg.if_type) { + case V4L2_MBUS_FMT_YUYV8_2X8: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { + dev_dbg(dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + modeset |= ((VPFE_PINPOL_NEGATIVE & CCDC_VD_POL_MASK) << + CCDC_VD_POL_SHIFT); + regw(3, REC656IF); + ccdcfg = ccdcfg | CCDC_DATA_PACK8 | CCDC_YCINSWP_YCBCR; + break; + case V4L2_MBUS_FMT_YUYV10_2X10: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { + dev_dbg(dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* setup BT.656, embedded sync */ + regw(3, REC656IF); + /* enable 10 bit mode in ccdcfg */ + ccdcfg = ccdcfg | CCDC_DATA_PACK8 | CCDC_YCINSWP_YCBCR | + CCDC_BW656_ENABLE; + break; + case V4L2_MBUS_FMT_YUYV10_1X20: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { + dev_dbg(dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + regw(3, REC656IF); + break; + + case V4L2_MBUS_FMT_Y8_1X8: + ccdcfg |= CCDC_DATA_PACK8; + ccdcfg |= CCDC_YCINSWP_YCBCR; + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { + dev_dbg(dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + case V4L2_MBUS_FMT_YUYV8_1X16: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { + dev_dbg(dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + default: + /* should never come here */ + dev_dbg(dev, "Invalid interface type\n"); + return -EINVAL; + } + + regw(modeset, MODESET); + + /* Set up pix order */ + ccdcfg |= (params->pix_order & CCDC_PIX_ORDER_MASK) << + CCDC_PIX_ORDER_SHIFT; + + regw(ccdcfg, CCDCFG); + + /* configure video window */ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_YUYV10_1X20 || + ccdc_cfg.if_type == V4L2_MBUS_FMT_YUYV8_1X16) + ccdc_setwin(¶ms->win, params->frm_fmt, 1, mode); + else + ccdc_setwin(¶ms->win, params->frm_fmt, 2, mode); + + /** + * configure the horizontal line offset + * this is done by rounding up width to a multiple of 16 pixels + * and multiply by two to account for y:cb:cr 4:2:2 data + */ + regw(((((params->win.width * 2) + 31) & 0xffffffe0) >> 5), HSIZE); + + /* configure the memory line offset */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED && + params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) { + /* two fields are interleaved in memory */ + regw(0x00000249, SDOFST); + } + + /* Setup test pattern if enabled */ + if (ccdc_cfg.bayer.config_params.test_pat_gen) { + sync.ccdpg_hdpol = (params->hd_pol & CCDC_HD_POL_MASK); + sync.ccdpg_vdpol = (params->vd_pol & CCDC_VD_POL_MASK); + vpss_set_sync_pol(sync); + vpss_set_pg_frame_size(frame_size); + } + + return 0; +} + +static int ccdc_configure(int mode) +{ + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) + return ccdc_config_raw(mode); + else + ccdc_config_ycbcr(mode); + + return 0; +} + +static int ccdc_close(struct device *device) +{ + /* copy defaults to module params */ + memcpy(&ccdc_cfg.bayer.config_params, &ccdc_config_defaults, + sizeof(struct ccdc_config_params_raw)); + + return 0; +} + +static struct ccdc_hw_device ccdc_hw_dev = { + .name = "dm365_isif", + .owner = THIS_MODULE, + .hw_ops = { + .open = ccdc_open, + .close = ccdc_close, + .enable = ccdc_enable, + .enable_out_to_sdram = ccdc_enable_output_to_sdram, + .set_hw_if_params = ccdc_set_hw_if_params, + .set_params = ccdc_set_params, + .get_params = ccdc_get_params, + .configure = ccdc_configure, + .set_buftype = ccdc_set_buftype, + .get_buftype = ccdc_get_buftype, + .enum_pix = ccdc_enum_pix, + .set_pixel_format = ccdc_set_pixel_format, + .get_pixel_format = ccdc_get_pixel_format, + .set_frame_format = ccdc_set_frame_format, + .get_frame_format = ccdc_get_frame_format, + .set_image_window = ccdc_set_image_window, + .get_image_window = ccdc_get_image_window, + .get_line_length = ccdc_get_line_length, + .setfbaddr = ccdc_setfbaddr, + .getfid = ccdc_getfid, + }, +}; + +struct ccdc_hw_device *get_ccdc_dev(void) +{ + return &ccdc_hw_dev; +} + +int ccdc_init(struct platform_device *pdev) +{ + static resource_size_t res_len; + struct resource *res; + void *__iomem addr; + int status; + int i; + + i = 0; + /* Get the ISIF base address, linearization table0 and table1 addr. */ + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) { + status = -ENOENT; + goto fail_nobase_res; + } + res_len = res->end - res->start + 1; + res = request_mem_region(res->start, res_len, res->name); + if (!res) { + status = -EBUSY; + goto fail_nobase_res; + } + addr = ioremap_nocache(res->start, res_len); + if (!addr) { + status = -EBUSY; + goto fail_base_iomap; + } + switch (i) { + case 0: + /* ISIF base address */ + ccdc_cfg.base_addr = addr; + break; + case 1: + /* ISIF linear tbl0 address */ + ccdc_cfg.linear_tbl0_addr = addr; + break; + default: + /* ISIF linear tbl0 address */ + ccdc_cfg.linear_tbl1_addr = addr; + break; + } + i++; + } + + davinci_cfg_reg(DM365_VIN_CAM_WEN); + davinci_cfg_reg(DM365_VIN_CAM_VD); + davinci_cfg_reg(DM365_VIN_CAM_HD); + davinci_cfg_reg(DM365_VIN_YIN4_7_EN); + davinci_cfg_reg(DM365_VIN_YIN0_3_EN); + + return 0; +fail_base_iomap: + release_mem_region(res->start, res_len); + i--; +fail_nobase_res: + if (ccdc_cfg.base_addr) + iounmap(ccdc_cfg.base_addr); + if (ccdc_cfg.linear_tbl0_addr) + iounmap(ccdc_cfg.linear_tbl0_addr); + + while (i >= 0) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + release_mem_region(res->start, res_len); + i--; + } + + return status; +} + +void ccdc_remove(struct platform_device *pdev) +{ + struct resource *res; + int i; + + iounmap(ccdc_cfg.base_addr); + iounmap(ccdc_cfg.linear_tbl0_addr); + iounmap(ccdc_cfg.linear_tbl1_addr); + + i = 0; + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (res) + release_mem_region(res->start, + res->end - res->start + 1); + i++; + } +} diff --git a/drivers/media/video/davinci/dm365_ccdc.h b/drivers/media/video/davinci/dm365_ccdc.h new file mode 100644 index 0000000..bf8ea86 --- /dev/null +++ b/drivers/media/video/davinci/dm365_ccdc.h @@ -0,0 +1,91 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +* ccdc header file for DM365 ISIF +*/ +#ifndef _DM365_CCDC_H +#define _DM365_CCDC_H + +#include +#include +#include "ccdc_types.h" + +struct ccdc_ycbcr_config { + /* v4l2 pixel format */ + unsigned long v4l2_pix_fmt; + /* ccdc pixel format */ + enum ccdc_pixfmt pix_fmt; + /* ccdc frame format */ + enum ccdc_frmfmt frm_fmt; + /* CCDC crop window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* ccdc pix order. Only used for ycbcr capture */ + enum ccdc_pixorder pix_order; + /* ccdc buffer type. Only used for ycbcr capture */ + enum ccdc_buftype buf_type; +}; + +struct ccdc_params_raw { + /* v4l2 pixel format */ + unsigned long v4l2_pix_fmt; + /* ccdc pixel format */ + enum ccdc_pixfmt pix_fmt; + /* ccdc frame format */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* buffer type. Applicable for interlaced mode */ + enum ccdc_buftype buf_type; + /* Gain values */ + struct ccdc_gain gain; + /* cfa pattern */ + enum ccdc_cfa_pattern cfa_pat; + /* Data MSB position */ + enum ccdc_data_msb data_msb; + /* Enable horizontal flip */ + unsigned char horz_flip_en; + /* Enable image invert vertically */ + unsigned char image_invert_en; + + /*all the userland defined stuff*/ + struct ccdc_config_params_raw config_params; +}; + +enum ccdc_data_pack { + CCDC_PACK_16BIT, + CCDC_PACK_12BIT, + CCDC_PACK_8BIT +}; + +struct ccdc_hw_device *get_ccdc_dev(void); + +#define CCDC_WIN_NTSC {0, 0, 720, 480} +#define CCDC_WIN_VGA {0, 0, 640, 480} +#define ISP5_CCDCMUX 0x20 + +#endif diff --git a/drivers/media/video/davinci/dm365_ccdc_regs.h b/drivers/media/video/davinci/dm365_ccdc_regs.h new file mode 100644 index 0000000..9e734d5 --- /dev/null +++ b/drivers/media/video/davinci/dm365_ccdc_regs.h @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _DM365_CCDC_REGS_H +#define _DM365_CCDC_REGS_H + +/* ISIF registers relative offsets */ +#define SYNCEN 0x00 +#define MODESET 0x04 +#define HDW 0x08 +#define VDW 0x0c +#define PPLN 0x10 +#define LPFR 0x14 +#define SPH 0x18 +#define LNH 0x1c +#define SLV0 0x20 +#define SLV1 0x24 +#define LNV 0x28 +#define CULH 0x2c +#define CULV 0x30 +#define HSIZE 0x34 +#define SDOFST 0x38 +#define CADU 0x3c +#define CADL 0x40 +#define LINCFG0 0x44 +#define LINCFG1 0x48 +#define CCOLP 0x4c +#define CRGAIN 0x50 +#define CGRGAIN 0x54 +#define CGBGAIN 0x58 +#define CBGAIN 0x5c +#define COFSTA 0x60 +#define FLSHCFG0 0x64 +#define FLSHCFG1 0x68 +#define FLSHCFG2 0x6c +#define VDINT0 0x70 +#define VDINT1 0x74 +#define VDINT2 0x78 +#define MISC 0x7c +#define CGAMMAWD 0x80 +#define REC656IF 0x84 +#define CCDCFG 0x88 +/***************************************************** +* Defect Correction registers +*****************************************************/ +#define DFCCTL 0x8c +#define VDFSATLV 0x90 +#define DFCMEMCTL 0x94 +#define DFCMEM0 0x98 +#define DFCMEM1 0x9c +#define DFCMEM2 0xa0 +#define DFCMEM3 0xa4 +#define DFCMEM4 0xa8 +/**************************************************** +* Black Clamp registers +****************************************************/ +#define CLAMPCFG 0xac +#define CLDCOFST 0xb0 +#define CLSV 0xb4 +#define CLHWIN0 0xb8 +#define CLHWIN1 0xbc +#define CLHWIN2 0xc0 +#define CLVRV 0xc4 +#define CLVWIN0 0xc8 +#define CLVWIN1 0xcc +#define CLVWIN2 0xd0 +#define CLVWIN3 0xd4 +/**************************************************** +* Lense Shading Correction +****************************************************/ +#define DATAHOFST 0xd8 +#define DATAVOFST 0xdc +#define LSCHVAL 0xe0 +#define LSCVVAL 0xe4 +#define TWODLSCCFG 0xe8 +#define TWODLSCOFST 0xec +#define TWODLSCINI 0xf0 +#define TWODLSCGRBU 0xf4 +#define TWODLSCGRBL 0xf8 +#define TWODLSCGROF 0xfc +#define TWODLSCORBU 0x100 +#define TWODLSCORBL 0x104 +#define TWODLSCOROF 0x108 +#define TWODLSCIRQEN 0x10c +#define TWODLSCIRQST 0x110 +/**************************************************** +* Data formatter +****************************************************/ +#define FMTCFG 0x114 +#define FMTPLEN 0x118 +#define FMTSPH 0x11c +#define FMTLNH 0x120 +#define FMTSLV 0x124 +#define FMTLNV 0x128 +#define FMTRLEN 0x12c +#define FMTHCNT 0x130 +#define FMTAPTR_BASE 0x134 +/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */ +#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4)) +#define FMTPGMVF0 0x174 +#define FMTPGMVF1 0x178 +#define FMTPGMAPU0 0x17c +#define FMTPGMAPU1 0x180 +#define FMTPGMAPS0 0x184 +#define FMTPGMAPS1 0x188 +#define FMTPGMAPS2 0x18c +#define FMTPGMAPS3 0x190 +#define FMTPGMAPS4 0x194 +#define FMTPGMAPS5 0x198 +#define FMTPGMAPS6 0x19c +#define FMTPGMAPS7 0x1a0 +/************************************************ +* Color Space Converter +************************************************/ +#define CSCCTL 0x1a4 +#define CSCM0 0x1a8 +#define CSCM1 0x1ac +#define CSCM2 0x1b0 +#define CSCM3 0x1b4 +#define CSCM4 0x1b8 +#define CSCM5 0x1bc +#define CSCM6 0x1c0 +#define CSCM7 0x1c4 +#define OBWIN0 0x1c8 +#define OBWIN1 0x1cc +#define OBWIN2 0x1d0 +#define OBWIN3 0x1d4 +#define OBVAL0 0x1d8 +#define OBVAL1 0x1dc +#define OBVAL2 0x1e0 +#define OBVAL3 0x1e4 +#define OBVAL4 0x1e8 +#define OBVAL5 0x1ec +#define OBVAL6 0x1f0 +#define OBVAL7 0x1f4 +#define CLKCTL 0x1f8 + +#define CCDC_LINEAR_LUT0_ADDR 0x1c7c000 +#define CCDC_LINEAR_LUT1_ADDR 0x1c7c400 + +/* Masks & Shifts below */ +#define START_PX_HOR_MASK 0x7fff +#define NUM_PX_HOR_MASK 0x7fff +#define START_VER_ONE_MASK 0x7fff +#define START_VER_TWO_MASK 0x7fff +#define NUM_LINES_VER 0x7fff + +/* gain - offset masks */ +#define GAIN_INTEGER_MASK 0x7 +#define GAIN_INTEGER_SHIFT 0x9 +#define GAIN_DECIMAL_MASK 0x1ff +#define OFFSET_MASK 0xfff +#define GAIN_SDRAM_EN_SHIFT 12 +#define GAIN_IPIPE_EN_SHIFT 13 +#define GAIN_H3A_EN_SHIFT 14 +#define OFST_SDRAM_EN_SHIFT 8 +#define OFST_IPIPE_EN_SHIFT 9 +#define OFST_H3A_EN_SHIFT 10 +#define GAIN_OFFSET_EN_MASK 0x7700 + +/* Culling */ +#define CULL_PAT_EVEN_LINE_SHIFT 8 + +/* CCDCFG register */ +#define CCDC_YCINSWP_RAW (0x00 << 4) +#define CCDC_YCINSWP_YCBCR (0x01 << 4) +#define CCDC_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6) +#define CCDC_CCDCFG_WENLOG_AND (0x00 << 8) +#define CCDC_CCDCFG_TRGSEL_WEN (0x00 << 9) +#define CCDC_CCDCFG_EXTRG_DISABLE (0x00 << 10) +#define CCDC_LATCH_ON_VSYNC_DISABLE (0x01 << 15) +#define CCDC_LATCH_ON_VSYNC_ENABLE (0x00 << 15) +#define CCDC_DATA_PACK_MASK 0x03 +#define CCDC_DATA_PACK16 0x0 +#define CCDC_DATA_PACK12 0x1 +#define CCDC_DATA_PACK8 0x2 +#define CCDC_PIX_ORDER_SHIFT 11 +#define CCDC_PIX_ORDER_MASK 0x01 +#define CCDC_BW656_ENABLE (0x01 << 5) + +/* MODESET registers */ +#define CCDC_VDHDOUT_INPUT (0x00 << 0) +#define CCDC_INPUT_MASK 0x03 +#define CCDC_INPUT_SHIFT 12 +#define CCDC_RAW_INPUT_MODE 0x00 +#define CCDC_FID_POL_MASK 0x01 +#define CCDC_FID_POL_SHIFT 4 +#define CCDC_HD_POL_MASK 0x01 +#define CCDC_HD_POL_SHIFT 3 +#define CCDC_VD_POL_MASK 0x01 +#define CCDC_VD_POL_SHIFT 2 +#define CCDC_DATAPOL_NORMAL 0x00 +#define CCDC_DATAPOL_MASK 0x01 +#define CCDC_DATAPOL_SHIFT 6 +#define CCDC_EXWEN_DISABLE 0x00 +#define CCDC_EXWEN_MASK 0x01 +#define CCDC_EXWEN_SHIFT 5 +#define CCDC_FRM_FMT_MASK 0x01 +#define CCDC_FRM_FMT_SHIFT 7 +#define CCDC_DATASFT_MASK 0x07 +#define CCDC_DATASFT_SHIFT 8 +#define CCDC_LPF_SHIFT 14 +#define CCDC_LPF_MASK 0x1 + +/* GAMMAWD registers */ +#define CCDC_ALAW_GAMA_WD_MASK 0xf +#define CCDC_ALAW_GAMA_WD_SHIFT 1 +#define CCDC_ALAW_ENABLE 0x01 +#define CCDC_GAMMAWD_CFA_MASK 0x01 +#define CCDC_GAMMAWD_CFA_SHIFT 5 + +/* HSIZE registers */ +#define CCDC_HSIZE_FLIP_MASK 0x01 +#define CCDC_HSIZE_FLIP_SHIFT 12 +#define CCDC_LINEOFST_MASK 0xfff + +/* MISC registers */ +#define CCDC_DPCM_EN_SHIFT 12 +#define CCDC_DPCM_EN_MASK 1 +#define CCDC_DPCM_PREDICTOR_SHIFT 13 +#define CCDC_DPCM_PREDICTOR_MASK 1 + +/* Black clamp related */ +#define CCDC_BC_DCOFFSET_MASK 0x1fff +#define CCDC_BC_MODE_COLOR_MASK 1 +#define CCDC_BC_MODE_COLOR_SHIFT 4 +#define CCDC_HORZ_BC_MODE_MASK 3 +#define CCDC_HORZ_BC_MODE_SHIFT 1 +#define CCDC_HORZ_BC_WIN_COUNT_MASK 0x1f +#define CCDC_HORZ_BC_WIN_SEL_SHIFT 5 +#define CCDC_HORZ_BC_PIX_LIMIT_SHIFT 6 +#define CCDC_HORZ_BC_WIN_H_SIZE_MASK 3 +#define CCDC_HORZ_BC_WIN_H_SIZE_SHIFT 8 +#define CCDC_HORZ_BC_WIN_V_SIZE_MASK 3 +#define CCDC_HORZ_BC_WIN_V_SIZE_SHIFT 12 +#define CCDC_HORZ_BC_WIN_START_H_MASK 0x1fff +#define CCDC_HORZ_BC_WIN_START_V_MASK 0x1fff +#define CCDC_VERT_BC_OB_H_SZ_MASK 7 +#define CCDC_VERT_BC_RST_VAL_SEL_MASK 3 +#define CCDC_VERT_BC_RST_VAL_SEL_SHIFT 4 +#define CCDC_VERT_BC_LINE_AVE_COEF_SHIFT 8 +#define CCDC_VERT_BC_OB_START_HORZ_MASK 0x1fff +#define CCDC_VERT_BC_OB_START_VERT_MASK 0x1fff +#define CCDC_VERT_BC_OB_VERT_SZ_MASK 0x1fff +#define CCDC_VERT_BC_RST_VAL_MASK 0xfff +#define CCDC_BC_VERT_START_SUB_V_MASK 0x1fff + +/* VDFC registers */ +#define CCDC_VDFC_EN_SHIFT 4 +#define CCDC_VDFC_CORR_MOD_MASK 3 +#define CCDC_VDFC_CORR_MOD_SHIFT 5 +#define CCDC_VDFC_CORR_WHOLE_LN_SHIFT 7 +#define CCDC_VDFC_LEVEL_SHFT_MASK 7 +#define CCDC_VDFC_LEVEL_SHFT_SHIFT 8 +#define CCDC_VDFC_SAT_LEVEL_MASK 0xfff +#define CCDC_VDFC_POS_MASK 0x1fff +#define CCDC_DFCMEMCTL_DFCMARST_SHIFT 2 + +/* CSC registers */ +#define CCDC_CSC_COEF_INTEG_MASK 7 +#define CCDC_CSC_COEF_DECIMAL_MASK 0x1f +#define CCDC_CSC_COEF_INTEG_SHIFT 5 +#define CCDC_CSCM_MSB_SHIFT 8 +#define CCDC_DF_CSC_SPH_MASK 0x1fff +#define CCDC_DF_CSC_LNH_MASK 0x1fff +#define CCDC_DF_CSC_SLV_MASK 0x1fff +#define CCDC_DF_CSC_LNV_MASK 0x1fff +#define CCDC_DF_NUMLINES 0x7fff +#define CCDC_DF_NUMPIX 0x1fff + +/* Offsets for LSC/DFC/Gain */ +#define CCDC_DATA_H_OFFSET_MASK 0x1fff +#define CCDC_DATA_V_OFFSET_MASK 0x1fff + +/* Linearization */ +#define CCDC_LIN_CORRSFT_MASK 7 +#define CCDC_LIN_CORRSFT_SHIFT 4 +#define CCDC_LIN_SCALE_FACT_INTEG_SHIFT 10 +#define CCDC_LIN_SCALE_FACT_DECIMAL_MASK 0x3ff +#define CCDC_LIN_ENTRY_MASK 0x3ff + +#define CCDC_DF_FMTRLEN_MASK 0x1fff +#define CCDC_DF_FMTHCNT_MASK 0x1fff + +/* Pattern registers */ +#define CCDC_PG_EN (1 << 3) +#define CCDC_SEL_PG_SRC (3 << 4) +#define CCDC_PG_VD_POL_SHIFT 0 +#define CCDC_PG_HD_POL_SHIFT 1 + +/*masks and shifts*/ +#define CCDC_SYNCEN_VDHDEN_MASK (1 << 0) +#define CCDC_SYNCEN_WEN_MASK (1 << 1) +#define CCDC_SYNCEN_WEN_SHIFT 1 + +#endif diff --git a/include/linux/dm365_ccdc.h b/include/linux/dm365_ccdc.h new file mode 100644 index 0000000..99765f6 --- /dev/null +++ b/include/linux/dm365_ccdc.h @@ -0,0 +1,611 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _DM365_CCDC_INCLUDE_H +#define _DM365_CCDC_INCLUDE_H + +#include "types.h" + +#define VPFE_CMD_S_CCDC_RAW_PARAMS _IOW('V', 1, \ + struct ccdc_config_params_raw) +#define VPFE_CMD_G_CCDC_RAW_PARAMS _IOR('V', 2, \ + struct ccdc_config_params_raw) +/** + * ccdc float type S8Q8/U8Q8 + */ +struct ccdc_float_8 { + /* 8 bit integer part */ + __u8 integer; + /* 8 bit decimal part */ + __u8 decimal; +}; + +/** + * brief ccdc float type U16Q16/S16Q16 + */ +struct ccdc_float_16 { + /* 16 bit integer part */ + __u16 integer; + /* 16 bit decimal part */ + __u16 decimal; +}; + +/************************************************************************ + * Vertical Defect Correction parameters + ***********************************************************************/ + +/** + * vertical defect correction methods + */ +enum ccdc_vdfc_corr_mode { + /* Defect level subtraction. Just fed through if saturating */ + CCDC_VDFC_NORMAL, + /** + * Defect level subtraction. Horizontal interpolation ((i-2)+(i+2))/2 + * if data saturating + */ + CCDC_VDFC_HORZ_INTERPOL_IF_SAT, + /* Horizontal interpolation (((i-2)+(i+2))/2) */ + CCDC_VDFC_HORZ_INTERPOL +}; + +/** + * Max Size of the Vertical Defect Correction table + */ +#define CCDC_VDFC_TABLE_SIZE 8 + +/** + * Values used for shifting up the vdfc defect level + */ +enum ccdc_vdfc_shift { + /* No Shift */ + CCDC_VDFC_NO_SHIFT, + /* Shift by 1 bit */ + CCDC_VDFC_SHIFT_1, + /* Shift by 2 bit */ + CCDC_VDFC_SHIFT_2, + /* Shift by 3 bit */ + CCDC_VDFC_SHIFT_3, + /* Shift by 4 bit */ + CCDC_VDFC_SHIFT_4 +}; + +/** + * Defect Correction (DFC) table entry + */ +struct ccdc_vdfc_entry { + /* vertical position of defect */ + unsigned short pos_vert; + /* horizontal position of defect */ + unsigned short pos_horz; + /** + * Defect level of Vertical line defect position. This is subtracted + * from the data at the defect position + */ + unsigned char level_at_pos; + /** + * Defect level of the pixels upper than the vertical line defect. + * This is subtracted from the data + */ + unsigned char level_up_pixels; + /** + * Defect level of the pixels lower than the vertical line defect. + * This is subtracted from the data + */ + unsigned char level_low_pixels; +}; + +/** + * Structure for Defect Correction (DFC) parameter + */ +struct ccdc_dfc { + /* enable vertical defect correction */ + unsigned char en; + /* Correction methods */ + enum ccdc_vdfc_corr_mode corr_mode; + /** + * 0 - whole line corrected, 1 - not + * pixels upper than the defect + */ + unsigned char corr_whole_line; + /** + * defect level shift value. level_at_pos, level_upper_pos, + * and level_lower_pos can be shifted up by this value + */ + enum ccdc_vdfc_shift def_level_shift; + /* defect saturation level */ + unsigned short def_sat_level; + /* number of vertical defects. Max is CCDC_VDFC_TABLE_SIZE */ + short num_vdefects; + /* VDFC table ptr */ + struct ccdc_vdfc_entry table[CCDC_VDFC_TABLE_SIZE]; +}; + +/************************************************************************ +* Digital/Black clamp or DC Subtract parameters +************************************************************************/ +/** + * Horizontal Black Clamp modes + */ +enum ccdc_horz_bc_mode { + /** + * Horizontal clamp disabled. Only vertical clamp + * value is subtracted + */ + CCDC_HORZ_BC_DISABLE, + /** + * Horizontal clamp value is calculated and subtracted + * from image data along with vertical clamp value + */ + CCDC_HORZ_BC_CLAMP_CALC_ENABLED, + /** + * Horizontal clamp value calculated from previous image + * is subtracted from image data along with vertical clamp + * value. How the horizontal clamp value for the first image + * is calculated in this case ??? + */ + CCDC_HORZ_BC_CLAMP_NOT_UPDATED +}; + +/** + * Base window selection for Horizontal Black Clamp calculations + */ +enum ccdc_horz_bc_base_win_sel { + /* Select Most left window for bc calculation */ + CCDC_SEL_MOST_LEFT_WIN, + + /* Select Most right window for bc calculation */ + CCDC_SEL_MOST_RIGHT_WIN, +}; + +/* Size of window in horizontal direction for horizontal bc */ +enum ccdc_horz_bc_sz_h { + CCDC_HORZ_BC_SZ_H_2PIXELS, + CCDC_HORZ_BC_SZ_H_4PIXELS, + CCDC_HORZ_BC_SZ_H_8PIXELS, + CCDC_HORZ_BC_SZ_H_16PIXELS +}; + +/* Size of window in vertcal direction for vertical bc */ +enum ccdc_horz_bc_sz_v { + CCDC_HORZ_BC_SZ_H_32PIXELS, + CCDC_HORZ_BC_SZ_H_64PIXELS, + CCDC_HORZ_BC_SZ_H_128PIXELS, + CCDC_HORZ_BC_SZ_H_256PIXELS +}; + +/** + * Structure for Horizontal Black Clamp config params + */ +struct ccdc_horz_bclamp { + /* horizontal clamp mode */ + enum ccdc_horz_bc_mode mode; + /** + * pixel value limit enable. + * 0 - limit disabled + * 1 - pixel value limited to 1023 + */ + unsigned char clamp_pix_limit; + /** + * Select most left or right window for clamp val + * calculation + */ + enum ccdc_horz_bc_base_win_sel base_win_sel_calc; + /* Window count per color for calculation. range 1-32 */ + unsigned char win_count_calc; + /* Window start position - horizontal for calculation. 0 - 8191 */ + unsigned short win_start_h_calc; + /* Window start position - vertical for calculation 0 - 8191 */ + unsigned short win_start_v_calc; + /* Width of the sample window in pixels for calculation */ + enum ccdc_horz_bc_sz_h win_h_sz_calc; + /* Height of the sample window in pixels for calculation */ + enum ccdc_horz_bc_sz_v win_v_sz_calc; +}; + +/** + * Black Clamp vertical reset values + */ +enum ccdc_vert_bc_reset_val_sel { + /* Reset value used is the clamp value calculated */ + CCDC_VERT_BC_USE_HORZ_CLAMP_VAL, + /* Reset value used is reset_clamp_val configured */ + CCDC_VERT_BC_USE_CONFIG_CLAMP_VAL, + /* No update, previous image value is used */ + CCDC_VERT_BC_NO_UPDATE +}; + +enum ccdc_vert_bc_sz_h { + CCDC_VERT_BC_SZ_H_2PIXELS, + CCDC_VERT_BC_SZ_H_4PIXELS, + CCDC_VERT_BC_SZ_H_8PIXELS, + CCDC_VERT_BC_SZ_H_16PIXELS, + CCDC_VERT_BC_SZ_H_32PIXELS, + CCDC_VERT_BC_SZ_H_64PIXELS +}; + +/** + * Structure for Vetical Black Clamp configuration params + */ +struct ccdc_vert_bclamp { + /* Reset value selection for vertical clamp calculation */ + enum ccdc_vert_bc_reset_val_sel reset_val_sel; + /* U12 value if reset_sel = CCDC_BC_VERT_USE_CONFIG_CLAMP_VAL */ + unsigned short reset_clamp_val; + /** + * U8Q8. Line average coefficient used in vertical clamp + * calculation + */ + unsigned char line_ave_coef; + /* Width in pixels of the optical black region used for calculation. */ + enum ccdc_vert_bc_sz_h ob_h_sz_calc; + /* Height of the optical black region for calculation */ + unsigned short ob_v_sz_calc; + /* Optical black region start position - horizontal. 0 - 8191 */ + unsigned short ob_start_h; + /* Optical black region start position - vertical 0 - 8191 */ + unsigned short ob_start_v; +}; + +/** + * Structure for Black Clamp configuration params + */ +struct ccdc_black_clamp { + /** + * this offset value is added irrespective of the clamp + * enable status. S13 + */ + unsigned short dc_offset; + /** + * Enable black/digital clamp value to be subtracted + * from the image data + */ + unsigned char en; + /** + * black clamp mode. same/separate clamp for 4 colors + * 0 - disable - same clamp value for all colors + * 1 - clamp value calculated separately for all colors + */ + unsigned char bc_mode_color; + /* Vrtical start position for bc subtraction */ + unsigned short vert_start_sub; + /* Black clamp for horizontal direction */ + struct ccdc_horz_bclamp horz; + /* Black clamp for vertical direction */ + struct ccdc_vert_bclamp vert; +}; + +/************************************************************************* +** Color Space Convertion (CSC) +*************************************************************************/ +/** + * Number of Coefficient values used for CSC + */ +#define CCDC_CSC_NUM_COEFF 16 + +/************************************************************************* +** Color Space Conversion parameters +*************************************************************************/ +/** + * Structure used for CSC config params + */ +struct ccdc_color_space_conv { + /* Enable color space conversion */ + unsigned char en; + /** + * csc coeffient table. S8Q5, M00 at index 0, M01 at index 1, and + * so forth + */ + struct ccdc_float_8 coeff[CCDC_CSC_NUM_COEFF]; +}; + +enum ccdc_data_size { + /* 8 bits */ + CCDC_8_BITS, + /* 9 bits */ + CCDC_9_BITS, + /* 10 bits */ + CCDC_10_BITS, + /* 11 bits */ + CCDC_11_BITS, + /* 12 bits */ + CCDC_12_BITS, + /* 13 bits */ + CCDC_13_BITS, + /* 14 bits */ + CCDC_14_BITS, + /* 15 bits */ + CCDC_15_BITS, + /* 16 bits */ + CCDC_16_BITS +}; + +enum ccdc_datasft { + /* No Shift */ + CCDC_NO_SHIFT, + /* 1 bit Shift */ + CCDC_1BIT_SHIFT, + /* 2 bit Shift */ + CCDC_2BIT_SHIFT, + /* 3 bit Shift */ + CCDC_3BIT_SHIFT, + /* 4 bit Shift */ + CCDC_4BIT_SHIFT, + /* 5 bit Shift */ + CCDC_5BIT_SHIFT, + /* 6 bit Shift */ + CCDC_6BIT_SHIFT +}; + +enum ccdc_data_msb { + /* MSB b15 */ + CCDC_BIT_MSB_15, + /* MSB b14 */ + CCDC_BIT_MSB_14, + /* MSB b13 */ + CCDC_BIT_MSB_13, + /* MSB b12 */ + CCDC_BIT_MSB_12, + /* MSB b11 */ + CCDC_BIT_MSB_11, + /* MSB b10 */ + CCDC_BIT_MSB_10, + /* MSB b9 */ + CCDC_BIT_MSB_9, + /* MSB b8 */ + CCDC_BIT_MSB_8, + /* MSB b7 */ + CCDC_BIT_MSB_7 +}; + +/************************************************************************* +** Gain parameters +*************************************************************************/ +/** + * Structure for Gain parameters + */ +struct ccdc_gain { + /* Gain for Red or ye */ + struct ccdc_float_16 r_ye; + /* Gain for Gr or cy */ + struct ccdc_float_16 gr_cy; + /* Gain for Gb or g */ + struct ccdc_float_16 gb_g; + /* Gain for Blue or mg */ + struct ccdc_float_16 b_mg; +}; + +/** + * Predicator types for DPCM compression + */ +enum ccdc_dpcm_predictor { + /* Choose Predictor1 for DPCM compression */ + CCDC_DPCM_PRED1, + /* Choose Predictor2 for DPCM compression */ + CCDC_DPCM_PRED2 +}; + +#define CCDC_LINEAR_TAB_SIZE 192 +/************************************************************************* +** Linearization parameters +*************************************************************************/ +/** + * Structure for Sensor data linearization + */ +struct ccdc_linearize { + /* Enable or Disable linearization of data */ + unsigned char en; + /* Shift value applied */ + enum ccdc_datasft corr_shft; + /* scale factor applied U11Q10 */ + struct ccdc_float_16 scale_fact; + /* Size of the linear table */ + unsigned short table[CCDC_LINEAR_TAB_SIZE]; +}; + +enum ccdc_cfa_pattern { + CCDC_CFA_PAT_MOSAIC, + CCDC_CFA_PAT_STRIPE +}; + +enum ccdc_colpats { + CCDC_RED, + CCDC_GREEN_RED, + CCDC_GREEN_BLUE, + CCDC_BLUE +}; + +struct ccdc_col_pat { + enum ccdc_colpats olop; + enum ccdc_colpats olep; + enum ccdc_colpats elop; + enum ccdc_colpats elep; +}; + +/************************************************************************* +** CCDC Raw configuration parameters +*************************************************************************/ +enum ccdc_fmt_mode { + CCDC_SPLIT, + CCDC_COMBINE +}; + +enum ccdc_lnum { + CCDC_1LINE, + CCDC_2LINES, + CCDC_3LINES, + CCDC_4LINES +}; + +enum ccdc_line { + CCDC_1STLINE, + CCDC_2NDLINE, + CCDC_3RDLINE, + CCDC_4THLINE +}; + +struct ccdc_fmtplen { + /** + * number of program entries for SET0, range 1 - 16 + * when fmtmode is CCDC_SPLIT, 1 - 8 when fmtmode is + * CCDC_COMBINE + */ + unsigned short plen0; + /** + * number of program entries for SET1, range 1 - 16 + * when fmtmode is CCDC_SPLIT, 1 - 8 when fmtmode is + * CCDC_COMBINE + */ + unsigned short plen1; + /** + * number of program entries for SET2, range 1 - 16 + * when fmtmode is CCDC_SPLIT, 1 - 8 when fmtmode is + * CCDC_COMBINE + */ + unsigned short plen2; + /** + * number of program entries for SET3, range 1 - 16 + * when fmtmode is CCDC_SPLIT, 1 - 8 when fmtmode is + * CCDC_COMBINE + */ + unsigned short plen3; +}; + +struct ccdc_fmt_cfg { + /* Split or combine or line alternate */ + enum ccdc_fmt_mode fmtmode; + /* enable or disable line alternating mode */ + unsigned char ln_alter_en; + /* Split/combine line number */ + enum ccdc_lnum lnum; + /* Address increment Range 1 - 16 */ + unsigned int addrinc; +}; + +struct ccdc_fmt_addr_ptr { + /* Initial address */ + unsigned int init_addr; + /* output line number */ + enum ccdc_line out_line; +}; + +struct ccdc_fmtpgm_ap { + /* program address pointer */ + unsigned char pgm_aptr; + /* program address increment or decrement */ + unsigned char pgmupdt; +}; + +struct ccdc_data_formatter { + /* Enable/Disable data formatter */ + unsigned char en; + /* data formatter configuration */ + struct ccdc_fmt_cfg cfg; + /* Formatter program entries length */ + struct ccdc_fmtplen plen; + /* first pixel in a line fed to formatter */ + unsigned short fmtrlen; + /* HD interval for output line. Only valid when split line */ + unsigned short fmthcnt; + /* formatter address pointers */ + struct ccdc_fmt_addr_ptr fmtaddr_ptr[16]; + /* program enable/disable */ + unsigned char pgm_en[32]; + /* program address pointers */ + struct ccdc_fmtpgm_ap fmtpgm_ap[32]; +}; + +struct ccdc_df_csc { + /* Color Space Conversion confguration, 0 - csc, 1 - df */ + unsigned int df_or_csc; + /* csc configuration valid if df_or_csc is 0 */ + struct ccdc_color_space_conv csc; + /* data formatter configuration valid if df_or_csc is 1 */ + struct ccdc_data_formatter df; + /* start pixel in a line at the input */ + unsigned int start_pix; + /* number of pixels in input line */ + unsigned int num_pixels; + /* start line at the input */ + unsigned int start_line; + /* number of lines at the input */ + unsigned int num_lines; +}; + +struct ccdc_gain_offsets_adj { + /* Gain adjustment per color */ + struct ccdc_gain gain; + /* Offset adjustment */ + unsigned short offset; + /* Enable or Disable Gain adjustment for SDRAM data */ + unsigned char gain_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + unsigned char gain_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + unsigned char gain_h3a_en; + /* Enable or Disable Gain adjustment for SDRAM data */ + unsigned char offset_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + unsigned char offset_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + unsigned char offset_h3a_en; +}; + +struct ccdc_cul { + /* Horizontal Cull pattern for odd lines */ + unsigned char hcpat_odd; + /* Horizontal Cull pattern for even lines */ + unsigned char hcpat_even; + /* Vertical Cull pattern */ + unsigned char vcpat; + /* Enable or disable lpf. Apply when cull is enabled */ + unsigned char en_lpf; +}; + +/* all the stuff in this struct will be provided by userland */ +struct ccdc_config_params_raw { + /* Linearization parameters for image sensor data input */ + struct ccdc_linearize linearize; + /* Data formatter or CSC */ + struct ccdc_df_csc df_csc; + /* Defect Pixel Correction (DFC) confguration */ + struct ccdc_dfc dfc; + /* Black/Digital Clamp configuration */ + struct ccdc_black_clamp bclamp; + /* Gain, offset adjustments */ + struct ccdc_gain_offsets_adj gain_offset; + /* Culling */ + struct ccdc_cul culling; + /* Predictor for DPCM compression */ + enum ccdc_dpcm_predictor pred; + /* horizontal offset for Gain/LSC/DFC */ + unsigned short horz_offset; + /* vertical offset for Gain/LSC/DFC */ + unsigned short vert_offset; + /* color pattern for field 0 */ + struct ccdc_col_pat col_pat_field0; + /* color pattern for field 1 */ + struct ccdc_col_pat col_pat_field1; + /* data size from 8 to 16 bits */ + enum ccdc_data_size data_size; + /* Data shift applied before storing to SDRAM */ + enum ccdc_datasft data_shift; + /* enable input test pattern generation */ + unsigned char test_pat_gen; +}; + +#endif diff --git a/include/media/davinci/ccdc_types.h b/include/media/davinci/ccdc_types.h deleted file mode 100644 index 5773874..0000000 --- a/include/media/davinci/ccdc_types.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2008-2009 Texas Instruments Inc - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - **************************************************************************/ -#ifndef _CCDC_TYPES_H -#define _CCDC_TYPES_H -enum ccdc_pixfmt { - CCDC_PIXFMT_RAW, - CCDC_PIXFMT_YCBCR_16BIT, - CCDC_PIXFMT_YCBCR_8BIT -}; - -enum ccdc_frmfmt { - CCDC_FRMFMT_PROGRESSIVE, - CCDC_FRMFMT_INTERLACED -}; - -/* PIXEL ORDER IN MEMORY from LSB to MSB */ -/* only applicable for 8-bit input mode */ -enum ccdc_pixorder { - CCDC_PIXORDER_YCBYCR, - CCDC_PIXORDER_CBYCRY, -}; - -enum ccdc_buftype { - CCDC_BUFTYPE_FLD_INTERLEAVED, - CCDC_BUFTYPE_FLD_SEPARATED -}; -#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:44:39 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 16:14:39 +0530 Subject: [RESEND RFC PATCH v4 13/15] davinci: vpfe: add aew driver based on v4l2 media framework In-Reply-To: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> References: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321526681-22574-14-git-send-email-manjunath.hadli@ti.com> Add auto exposure and white balance driver based on v4l2 media framework. The driver processess the bayer data from CCDC and generates the data which will be used for calculation of parameters responsible for auto exposure and white balancing. The driver implement the v4l2 event queing mechanism to know the readiness data from the driver. Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/vpfe_aew.c | 238 ++++++++++++++++++++++++++++++++ drivers/media/video/davinci/vpfe_aew.h | 51 +++++++ 2 files changed, 289 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpfe_aew.c create mode 100644 drivers/media/video/davinci/vpfe_aew.h diff --git a/drivers/media/video/davinci/vpfe_aew.c b/drivers/media/video/davinci/vpfe_aew.c new file mode 100644 index 0000000..4c2f075 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_aew.c @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors: + * Manjunath Hadli + * Nagabhushana Netagunte + */ +#include +#include +#include +#include +#include +#include +#include "vpss.h" +#include "vpfe_capture.h" + +/* + * V4L2 subdev operations + */ + +/* + * aew_subscribe_event - subscribe event + * @sd: VPFE AEW V4L2 subdevice + * @fh: file handle + * @sub: event subscription structure + */ +static int aew_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct vpfe_aew_device *aew = v4l2_get_subdevdata(sd); + + if (sub->type != aew->event_type) + return -EINVAL; + return v4l2_event_subscribe(fh, sub); +} + +/* + * aew_unsubscribe_event - unsubscribe event + * @sd: VPFE AEW V4L2 subdevice + * @fh: file handle + * @sub: event subscription structure + */ +static int aew_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) +{ + struct vpfe_aew_device *aew = v4l2_get_subdevdata(sd); + + if (sub->type != aew->event_type) + return -EINVAL; + return v4l2_event_unsubscribe(fh, sub); +} + +/* + * aew_queue_event - queue event + * @sd: VPFE AEW V4L2 subdevice + */ +void aew_queue_event(struct v4l2_subdev *sd) +{ + struct vpfe_aew_device *aew = v4l2_get_subdevdata(sd); + struct video_device *vdev = &sd->devnode; + struct v4l2_event event; + + memset(&event, 0, sizeof(event)); + event.type = aew->event_type; + v4l2_event_queue(vdev, &event); +} + +/* + * aew_set_stream - Enable/Disable streaming on the AEW module + * @sd: VPFE AEW V4L2 subdevice + * @enable: Enable/disable stream + */ +static int set_stream(struct v4l2_subdev *sd, int enable) +{ + struct vpfe_aew_device *aew = v4l2_get_subdevdata(sd); + + if (aew->input == AEW_INPUT_CCDC) + return aew_set_stream(sd, enable); + + return 0; +} + +/* + * aew_ioctl - AEW module private ioctl's + * @sd: VPFE AEW V4L2 subdevice + * @cmd: ioctl command + * @arg: ioctl argument + * + * Return 0 on success or a negative error code otherwise. + */ +static long ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + return aew_ioctl(sd, cmd, arg); +} + +/* V4L2 subdev core operations */ +static const struct v4l2_subdev_core_ops aew_v4l2_core_ops = { + .ioctl = ioctl, + .subscribe_event = aew_subscribe_event, + .unsubscribe_event = aew_unsubscribe_event, +}; + +/* V4L2 subdev video operations */ +static const struct v4l2_subdev_video_ops aew_v4l2_video_ops = { + .s_stream = set_stream, +}; + +/* V4L2 subdev operations */ +static const struct v4l2_subdev_ops aew_v4l2_ops = { + .core = &aew_v4l2_core_ops, + .video = &aew_v4l2_video_ops, +}; + +/* + * Media entity operations + */ + +/* + * aew_link_setup - Setup AEW connections + * @entity: AEW media entity + * @local: Pad at the local end of the link + * @remote: Pad at the remote end of the link + * @flags: Link flags + * + * return -EINVAL or zero on success + */ +static int aew_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct vpfe_aew_device *aew = v4l2_get_subdevdata(sd); + struct vpfe_device *vpfe_dev; + + vpfe_dev = to_vpfe_device(aew); + if ((flags & MEDIA_LNK_FL_ENABLED)) { + aew->input = AEW_INPUT_CCDC; + aew_open(); + return 0; + } + aew->input = AEW_INPUT_NONE; + aew_release(); + return 0; +} + +static const struct media_entity_operations aew_media_ops = { + .link_setup = aew_link_setup, +}; + +/* + * vpfe_aew_register_entities - AEW subdev driver registration + * @aew - pointer to aew subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +int vpfe_aew_register_entities(struct vpfe_aew_device *aew, + struct v4l2_device *vdev) +{ + int ret; + + /* Register the subdev */ + ret = v4l2_device_register_subdev(vdev, &aew->subdev); + if (ret < 0) + return ret; + + return 0; +} + +/* + * vpfe_aew_unregister_entities - AEW subdev driver unregistration + * @aew - pointer to aew subdevice structure. + * @vdev: pointer to v4l2 device structure. + */ +void vpfe_aew_unregister_entities(struct vpfe_aew_device *aew) +{ + /* cleanup entity */ + media_entity_cleanup(&aew->subdev.entity); + /* unregister subdev */ + v4l2_device_unregister_subdev(&aew->subdev); +} + +/* + * vpfe_aew_init - AEW module initilaization. + * @vpfe_aew - pointer to aew subdevice structure. + * @pdev: platform device pointer. + */ +int vpfe_aew_init(struct vpfe_aew_device *vpfe_aew, + struct platform_device *pdev) +{ + struct v4l2_subdev *aew = &vpfe_aew->subdev; + struct media_pad *pads = &vpfe_aew->pads[0]; + struct media_entity *me = &aew->entity; + int ret; + + if (aew_init(pdev)) + return -EINVAL; + + v4l2_subdev_init(aew, &aew_v4l2_ops); + strlcpy(aew->name, "DAVINCI AEW", sizeof(aew->name)); + aew->grp_id = 1 << 16; /* group ID for davinci subdevs */ + v4l2_set_subdevdata(aew, vpfe_aew); + aew->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; + aew->nevents = DAVINCI_AEW_NEVENTS; + pads[AEW_PAD_SINK].flags = MEDIA_PAD_FL_INPUT; + vpfe_aew->input = AEW_INPUT_NONE; + vpfe_aew->event_type = DAVINCI_EVENT_AEWB; + me->ops = &aew_media_ops; + ret = media_entity_init(me, AEW_PADS_NUM, pads, 0); + if (ret) + goto out_davanci_init; + + return 0; + +out_davanci_init: + aew_cleanup(); + + return ret; +} + +/* + * vpfe_aew_cleanup - AEW module cleanup. + */ +void vpfe_aew_cleanup(void) +{ + aew_cleanup(); +} diff --git a/drivers/media/video/davinci/vpfe_aew.h b/drivers/media/video/davinci/vpfe_aew.h new file mode 100644 index 0000000..d4ad908 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_aew.h @@ -0,0 +1,51 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef _VPFE_AEW_H +#define _VPFE_AEW_H + +#define AEW_PAD_SINK 0 +#define AEW_PADS_NUM 1 + +#define DAVINCI_AEW_NEVENTS 1 + +#define AEW_INPUT_NONE 0 +#define AEW_INPUT_CCDC 1 + +#define DAVINCI_EVENT_AEWB 1 + +struct vpfe_aew_device { + struct v4l2_subdev subdev; + struct media_pad pads[AEW_PADS_NUM]; + unsigned int input; + unsigned long event_type; +}; + +int aew_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg); +int aew_set_stream(struct v4l2_subdev *sd, int enable); +int aew_init(struct platform_device *pdev); +void aew_cleanup(void); +int aew_release(void); +int aew_open(void); + +void vpfe_aew_unregister_entities(struct vpfe_aew_device *aew); +int vpfe_aew_register_entities(struct vpfe_aew_device *aew, + struct v4l2_device *v4l2_dev); +int vpfe_aew_init(struct vpfe_aew_device *vpfe_aew, + struct platform_device *pdev); +void aew_queue_event(struct v4l2_subdev *sd); +void vpfe_aew_cleanup(void); +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:44:32 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 16:14:32 +0530 Subject: [RESEND RFC PATCH v4 06/15] davinci: vpfe: add v4l2 video driver support In-Reply-To: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> References: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321526681-22574-7-git-send-email-manjunath.hadli@ti.com> add a generic video driver functionality to be used by all the vpfe drivers for davinci SoCs. The functionality includes all the standard v4l2 interfaces including streaming. The video node interface can be used both as an input and output node for both continuous and single shot modes.Also supports dv_presets to include HD modes, wth support for both user pointer IO and mmap. Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/vpfe_video.c | 1726 ++++++++++++++++++++++++++++++ drivers/media/video/davinci/vpfe_video.h | 146 +++ 2 files changed, 1872 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpfe_video.c create mode 100644 drivers/media/video/davinci/vpfe_video.h diff --git a/drivers/media/video/davinci/vpfe_video.c b/drivers/media/video/davinci/vpfe_video.c new file mode 100644 index 0000000..5a0a403 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_video.c @@ -0,0 +1,1726 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Contributors:- + * Manjunath Hadli + * Nagabhushana Netagunte + */ + +#include +#include +#include +#include +#include +#include +#include +#include "vpfe_capture.h" +#include "ccdc_hw_device.h" + +/* minimum number of buffers needed in cont-mode */ +#define CONT_MIN_NUM_BUFFERS 3 + +static int debug; + +/* get v4l2 subdev pointer to external subdev which is active */ +static struct media_entity *vpfe_get_input_entity + (struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct media_pad *remote; + + remote = media_entity_remote_source(&vpfe_dev->vpfe_ccdc.pads[0]); + if (remote == NULL) { + printk(KERN_ERR "invalid media connection to ccdc\n"); + return NULL; + } + + return remote->entity; +} + +/* updates external subdev(sensor/decoder) which is active */ +static int vpfe_update_current_ext_subdev(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_config *vpfe_cfg; + struct v4l2_subdev *subdev; + struct media_pad *remote; + int i; + + remote = media_entity_remote_source(&vpfe_dev->vpfe_ccdc.pads[0]); + if (remote == NULL) { + printk(KERN_ERR "invalid media connection to ccdc\n"); + return -EINVAL; + } + + subdev = media_entity_to_v4l2_subdev(remote->entity); + + vpfe_cfg = vpfe_dev->pdev->platform_data; + + for (i = 0; i < vpfe_cfg->num_subdevs; i++) { + if (!strcmp(vpfe_cfg->sub_devs[i].module_name, subdev->name)) { + video->current_ext_subdev = &vpfe_cfg->sub_devs[i]; + break; + } + } + + /* if user not linked decoder/sensor to ccdc */ + if (i == vpfe_cfg->num_subdevs) { + printk(KERN_ERR "invalid media chain connection to ccdc\n"); + return -EINVAL; + } + + /* find the v4l2 subdev pointer */ + for (i = 0; i < vpfe_dev->num_ext_subdevs; i++) { + if (!strcmp(video->current_ext_subdev->module_name, + vpfe_dev->sd[i]->name)) + video->current_ext_subdev->subdev = vpfe_dev->sd[i]; + } + + return 0; +} + +/* get the subdev which is connected to the output video node */ +static struct v4l2_subdev * +vpfe_video_remote_subdev(struct vpfe_video_device *video, u32 *pad) +{ + struct media_pad *remote; + + remote = media_entity_remote_source(&video->pad); + + if (remote == NULL || remote->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + return NULL; + + if (pad) + *pad = remote->index; + + return media_entity_to_v4l2_subdev(remote->entity); +} + +/* get the format set at ouput pad of the adjacent subdev */ +static int +__vpfe_video_get_format(struct vpfe_video_device *video, + struct v4l2_format *format) +{ + struct v4l2_subdev_format fmt; + struct v4l2_subdev *subdev; + struct media_pad *remote; + u32 pad; + int ret; + + subdev = vpfe_video_remote_subdev(video, &pad); + if (subdev == NULL) + return -EINVAL; + + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + remote = media_entity_remote_source(&video->pad); + fmt.pad = remote->index; + + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt); + if (ret == -ENOIOCTLCMD) + return -EINVAL; + + format->type = video->type; + /* convert mbus_format to v4l2_format */ + v4l2_fill_pix_format(&format->fmt.pix, &fmt.format); + mbus_to_pix(&fmt.format, &format->fmt.pix); + + return 0; +} + +/* make a note of pipeline details */ +static void vpfe_prepare_pipeline(struct vpfe_video_device *video) +{ + struct media_entity *entity = &video->video_dev.entity; + struct media_device *mdev = entity->parent; + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_video_device *far_end = NULL; + struct media_entity_graph graph; + + pipe->input_num = 0; + pipe->output_num = 0; + + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + pipe->inputs[pipe->input_num++] = video; + else + pipe->outputs[pipe->output_num++] = video; + + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + if (entity == &video->video_dev.entity) + continue; + + if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) + continue; + + far_end = to_vpfe_video(media_entity_to_video_device(entity)); + + if (far_end->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + pipe->inputs[pipe->input_num++] = far_end; + else + pipe->outputs[pipe->output_num++] = far_end; + } + + mutex_unlock(&mdev->graph_mutex); +} + +/* update pipe state selected by user */ +static int vpfe_update_pipe_state(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int ret; + + vpfe_prepare_pipeline(video); + + /* Find out if there is any input video + if yes, it is single shot. + */ + if (pipe->input_num == 0) { + pipe->state = VPFE_PIPELINE_STREAM_CONTINUOUS; + ret = vpfe_update_current_ext_subdev(video); + if (ret) { + printk(KERN_ERR "invalid external subdev\n"); + return ret; + } + } else { + pipe->state = VPFE_PIPELINE_STREAM_SINGLESHOT; + } + + video->initialized = 1; + video->skip_frame_count = 1; + video->skip_frame_count_init = 1; + + return 0; +} + +/* checks wether pipeline is ready for enabling */ +int is_pipe_ready(struct vpfe_pipeline *pipe) +{ + int i; + + for (i = 0; i < pipe->input_num; i++) + if (!pipe->inputs[i]->started || + pipe->inputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED) + return 0; + + for (i = 0; i < pipe->output_num; i++) + if (!pipe->outputs[i]->started || + pipe->outputs[i]->state != VPFE_VIDEO_BUFFER_QUEUED) + return 0; + + return 1; +} + +/** + * Validate a pipeline by checking both ends of all links for format + * discrepancies. + * + * Return 0 if all formats match, or -EPIPE if at least one link is found with + * different formats on its two ends. + */ +static int vpfe_video_validate_pipeline(struct vpfe_pipeline *pipe) +{ + struct v4l2_subdev_format fmt_source; + struct v4l2_subdev_format fmt_sink; + struct v4l2_subdev *subdev; + struct media_pad *pad; + int ret; + + /* Should not matter if it is output[0] or 1 as + the general ideas is to traverse backwards and + the fact that the out video node always has the + format of the connected pad. + */ + subdev = vpfe_video_remote_subdev(pipe->outputs[0], NULL); + if (subdev == NULL) + return -EPIPE; + + while (1) { + /* Retrieve the sink format */ + pad = &subdev->entity.pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_INPUT)) + break; + + fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt_sink.pad = pad->index; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, + &fmt_sink); + + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + /* Retrieve the source format */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + pad->entity->type != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + subdev = media_entity_to_v4l2_subdev(pad->entity); + + fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt_source.pad = pad->index; + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + /* Check if the two ends match */ + if (fmt_source.format.code != fmt_sink.format.code || + fmt_source.format.width != fmt_sink.format.width || + fmt_source.format.height != fmt_sink.format.height) + return -EPIPE; + } + + return 0; +} + +/** + * vpfe_pipeline_enable - Enable streaming on a pipeline + * @vpfe_dev: vpfe device + * @pipe: vpfe pipeline + * + * Walk the entities chain starting at the pipeline output video node and start + * all modules in the chain in the given mode. + * + * Return 0 if successfull, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int vpfe_pipeline_enable(struct vpfe_pipeline *pipe) +{ + struct media_entity_graph graph; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_device *mdev; + int ret = 0; + + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + entity = vpfe_get_input_entity(pipe->outputs[0]); + else + entity = &pipe->inputs[0]->video_dev.entity; + + mdev = entity->parent; + + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + continue; + + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + } + mutex_unlock(&mdev->graph_mutex); + + return ret; +} + +/** + * vpfe_pipeline_disable - Disable streaming on a pipeline + * @vpfe_dev: vpfe device + * @pipe: VPFE pipeline + * + * Walk the entities chain starting at the pipeline output video node and stop + * all modules in the chain. + * + * Return 0 if all modules have been properly stopped, or -ETIMEDOUT if a module + * can't be stopped. + */ +static int vpfe_pipeline_disable(struct vpfe_pipeline *pipe) +{ + struct media_entity_graph graph; + struct media_entity *entity; + struct v4l2_subdev *subdev; + struct media_device *mdev; + int ret = 0; + + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + entity = vpfe_get_input_entity(pipe->outputs[0]); + else + entity = &pipe->inputs[0]->video_dev.entity; + + mdev = entity->parent; + + mutex_lock(&mdev->graph_mutex); + media_entity_graph_walk_start(&graph, entity); + + while ((entity = media_entity_graph_walk_next(&graph))) { + + if (media_entity_type(entity) == MEDIA_ENT_T_DEVNODE) + continue; + + subdev = media_entity_to_v4l2_subdev(entity); + + ret = v4l2_subdev_call(subdev, video, s_stream, 0); + if (ret < 0 && ret != -ENOIOCTLCMD) + break; + } + + mutex_unlock(&mdev->graph_mutex); + + return (ret == 0) ? ret : -ETIMEDOUT ; +} + +/** + * vpfe_pipeline_set_stream - Enable/disable streaming on a pipeline + * @vpfe_dev: VPFE device + * @pipe: VPFE pipeline + * @state: Stream state (stopped or active) + * + * Set the pipeline to the given stream state. + * + * Return 0 if successfull, or the return value of the failed video::s_stream + * operation otherwise. + */ +static int vpfe_pipeline_set_stream(struct vpfe_pipeline *pipe, + enum vpfe_pipeline_stream_state state) +{ + if (state == VPFE_PIPELINE_STREAM_STOPPED) + return vpfe_pipeline_disable(pipe); + + return vpfe_pipeline_enable(pipe); +} + +static int all_videos_stopped(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int i; + + for (i = 0; i < pipe->input_num; i++) + if (pipe->inputs[i]->started) + return 0; + + for (i = 0; i < pipe->output_num; i++) + if (pipe->outputs[i]->started) + return 0; + + return 1; +} + +/* + * vpfe_open - open video device + * @file: file pointer + * + * initialize media pipeline state, allocate memory for file hadle + * + * Return 0 if successfull, or the return -ENODEV otherwise. + */ +static int vpfe_open(struct file *file) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_fh *fh; + + /* Allocate memory for the file handle object */ + fh = kzalloc(sizeof(struct vpfe_fh), GFP_KERNEL); + + if (fh == NULL) + return -ENOMEM; + /* store pointer to fh in private_data member of file */ + file->private_data = fh; + fh->video = video; + mutex_lock(&video->lock); + /* If decoder is not initialized. initialize it */ + if (!video->initialized && vpfe_update_pipe_state(video)) { + mutex_unlock(&video->lock); + return -ENODEV; + } + /* Increment device usrs counter */ + video->usrs++; + /* Set io_allowed member to false */ + fh->io_allowed = 0; + /* Initialize priority of this instance to default priority */ + fh->prio = V4L2_PRIORITY_UNSET; + v4l2_prio_open(&video->prio, &fh->prio); + mutex_unlock(&video->lock); + + return 0; +} + +/* get the next buffer available from dma queue */ +unsigned long vpfe_get_next_buffer(struct vpfe_video_device *video) +{ + /* mark next buffer as active */ + video->next_frm = list_entry(video->dma_queue.next, + struct videobuf_buffer, queue); + + /* in single shot mode both curr_frm + and next_frm point to same buffer */ + video->cur_frm = video->next_frm; + list_del(&video->next_frm->queue); + video->next_frm->state = VIDEOBUF_ACTIVE; + + return videobuf_to_dma_contig(video->next_frm); +} + +/* schedule the next buffer which is available on dma queue */ +void vpfe_schedule_next_buffer(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + if (list_empty(&video->dma_queue)) + return; + + video->next_frm = list_entry(video->dma_queue.next, + struct videobuf_buffer, queue); + + if (VPFE_PIPELINE_STREAM_SINGLESHOT == video->pipe.state) + video->cur_frm = video->next_frm; + + list_del(&video->next_frm->queue); + video->next_frm->state = VIDEOBUF_ACTIVE; + addr = videobuf_to_dma_contig(video->next_frm); + + video->ops->queue(vpfe_dev, addr); + + video->state = VPFE_VIDEO_BUFFER_QUEUED; +} + +/* schedule the buffer for capturing bottom field */ +void vpfe_schedule_bottom_field(struct vpfe_video_device *video) +{ + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + + addr = videobuf_to_dma_contig(video->cur_frm); + addr += video->field_off; + + video->ops->queue(vpfe_dev, addr); +} + +/* make buffer available for dequeue */ +void vpfe_process_buffer_complete(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + struct timespec timespec; + s64 nsec; + + ktime_get_ts(×pec); + nsec = timespec_to_ns(×pec); + + video->cur_frm->ts = ns_to_timeval(nsec); + video->cur_frm->state = VIDEOBUF_DONE; + video->cur_frm->size = video->fmt.fmt.pix.sizeimage; + wake_up_interruptible(&video->cur_frm->done); + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + video->cur_frm = video->next_frm; +} + +/* vpfe_stop_capture: stop streaming */ +static void vpfe_stop_capture(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + + video->started = 0; + + if (video->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + return; + + if (all_videos_stopped(video)) + vpfe_pipeline_set_stream(pipe, + VPFE_PIPELINE_STREAM_STOPPED); +} + +/* + * vpfe_release - release video device + * @file: file pointer + * + * deletes buffer queue, frees the buffers and the vpfe file handle + * + * Return 0 + */ +static int vpfe_release(struct file *file) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_release\n"); + + /* Get the device lock */ + mutex_lock(&video->lock); + /* if this instance is doing IO */ + if (fh->io_allowed) { + if (video->started) { + vpfe_stop_capture(video); + /* mark pipe state as stopped in vpfe_release(), + as app might call streamon() after streamoff() + in which case driver has to start streaming. + */ + video->pipe.state = VPFE_PIPELINE_STREAM_STOPPED; + videobuf_streamoff(&video->buffer_queue); + } + video->io_usrs = 0; + } + + /* Decrement device usrs counter */ + video->usrs--; + /* Close the priority */ + v4l2_prio_close(&video->prio, fh->prio); + + /* If this is the last file handle */ + if (!video->usrs) + video->initialized = 0; + + mutex_unlock(&video->lock); + file->private_data = NULL; + /* Free memory allocated to file handle object */ + kzfree(fh); + + return 0; +} + +/* + * vpfe_mmap : It is used to map kernel space buffers + * into user spaces + */ +static int vpfe_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_mmap\n"); + + return videobuf_mmap_mapper(&video->buffer_queue, vma); +} + +/* + * vpfe_poll: It is used for select/poll system call + */ +static unsigned int vpfe_poll(struct file *file, poll_table *wait) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_poll\n"); + + if (video->started) + return videobuf_poll_stream(file, + &video->buffer_queue, wait); + + return 0; +} + +/* vpfe capture driver file operations */ +static const struct v4l2_file_operations vpfe_fops = { + .owner = THIS_MODULE, + .open = vpfe_open, + .release = vpfe_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vpfe_mmap, + .poll = vpfe_poll +}; + +/* + * vpfe_querycap - query capabilities of video device + * @file: file pointer + * @priv: void pointer + * @cap: pointer to v4l2_capability structure + * + * fills v4l2 capabilities structure + * + * Return 0 + */ +static int vpfe_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querycap\n"); + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + else + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + + cap->version = VPFE_CAPTURE_VERSION_CODE; + strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver)); + strlcpy(cap->bus_info, "VPFE", sizeof(cap->bus_info)); + strlcpy(cap->card, vpfe_dev->cfg->card_name, sizeof(cap->card)); + + return 0; +} + +/* + * vpfe_g_fmt - get the format which is active on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * fills v4l2 format structure with active format + * + * Return 0 + */ +static int vpfe_g_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_fmt\n"); + /* Fill in the information about format */ + *fmt = video->fmt; + + return 0; +} + +/* + * vpfe_enum_fmt - enum formats supported on media chain + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_fmtdesc structure + * + * fills v4l2_fmtdesc structure with output format set on adjacent subdev, + * only one format is enumearted as subdevs are already configured + * + * Return 0 if successfull, error code otherwise + */ +static int vpfe_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev_format sd_fmt; + struct v4l2_mbus_framefmt mbus; + struct v4l2_subdev *subdev; + struct v4l2_format format; + struct media_pad *remote; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_fmt\n"); + + /* since already subdev pad format is set, + only one pixel format is available */ + if (fmt->index > 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid index\n"); + return -EINVAL; + } + + /* get the remote pad */ + remote = media_entity_remote_source(&video->pad); + if (remote == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, "invalid remote pad \ + for video node\n"); + return -EINVAL; + } + + /* get the remote subdev */ + subdev = vpfe_video_remote_subdev(video, NULL); + if (subdev == NULL) { + v4l2_err(&vpfe_dev->v4l2_dev, "invalid remote subdev \ + for video node\n"); + return -EINVAL; + } + + sd_fmt.pad = remote->index; + sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + /* get output format of remote subdev */ + ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &sd_fmt); + if (ret) { + v4l2_err(&vpfe_dev->v4l2_dev, "invalid remote subdev \ + for video node\n"); + return ret; + } + /* convert to pix format */ + mbus.code = sd_fmt.format.code; + mbus_to_pix(&mbus, &format.fmt.pix); + + /* copy the result */ + fmt->pixelformat = format.fmt.pix.pixelformat; + + return 0; +} + +/* + * vpfe_s_fmt - set the format on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * validate and set the format on video device + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_format format; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_fmt\n"); + + /* If streaming is started, return error */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + + /* get adjacent subdev's output pad format */ + ret = __vpfe_video_get_format(video, &format); + if (ret) + return ret; + + *fmt = format; + + video->fmt = *fmt; + + return 0; +} + +/* + * vpfe_try_fmt - try the format on video device + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_format structure + * + * validate the format, update with correct format + * based on output format set on adjacent subdev + * + * Return 0 on success, error code otherwise + */ +static int vpfe_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_format format; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_try_fmt\n"); + + /* get adjacent subdev's output pad format */ + ret = __vpfe_video_get_format(video, &format); + if (ret) + return ret; + + *fmt = format; + + return 0; +} + +/* + * vpfe_enum_input - enum inputs supported on media chain + * @file: file pointer + * @priv: void pointer + * @fmt: pointer to v4l2_fmtdesc structure + * + * fills v4l2_input structure with input available on media chain, + * only one input is enumearted as media chain is setup by this time + * + * Return 0 if successfull, -EINVAL is media chain is invalid + */ +static int vpfe_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_ext_subdev_info *sdinfo = video->current_ext_subdev; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_input\n"); + + /* enumerate from the subdev user has choosen through mc */ + if (inp->index < sdinfo->num_inputs) { + memcpy(inp, &sdinfo->inputs[inp->index], + sizeof(struct v4l2_input)); + return 0; + } + + return -EINVAL; +} + +/* + * vpfe_g_input - get index of the input which is active + * @file: file pointer + * @priv: void pointer + * @index: pointer to unsigned int + * + * set index with input index which is active + * + * Return 0 + */ +static int vpfe_g_input(struct file *file, void *priv, unsigned int *index) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_input\n"); + + *index = video->current_input; + + return 0; +} + +/* + * vpfe_s_input - set input which is pointed by input index + * @file: file pointer + * @priv: void pointer + * @index: pointer to unsigned int + * + * set input on external subdev + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_input(struct file *file, void *priv, unsigned int index) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct ccdc_hw_device *ccdc_dev = vpfe_dev->vpfe_ccdc.ccdc_dev; + struct imp_hw_interface *imp_hw_if = vpfe_dev->vpfe_previewer.imp_hw_if; + struct vpfe_ext_subdev_info *sdinfo; + struct vpfe_route *route; + struct v4l2_input *inps; + u32 output; + u32 input; + int ret; + int i; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_input\n"); + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + /* + * If streaming is started return device busy + * error + */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "Streaming is on\n"); + ret = -EBUSY; + goto unlock_out; + } + + sdinfo = video->current_ext_subdev; + + if (!sdinfo->registered) { + ret = -EINVAL; + goto unlock_out; + } + + if (vpfe_dev->cfg->setup_input && + vpfe_dev->cfg->setup_input(sdinfo->grp_id) < 0) { + ret = -EFAULT; + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "couldn't setup input for %s\n", + sdinfo->module_name); + goto unlock_out; + } + + route = &sdinfo->routes[index]; + if (route && sdinfo->can_route) { + input = route->input; + output = route->output; + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + sdinfo->grp_id, video, + s_routing, input, output, 0); + + if (ret) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "s_input:error in setting input in decoder\n"); + ret = -EINVAL; + goto unlock_out; + } + } + + /* set standards set by subdev in video device */ + for (i = 0; i < sdinfo->num_inputs; i++) { + inps = &sdinfo->inputs[i]; + video->video_dev.tvnorms |= inps->std; + } + + /* set the bus/interface parameter for the sub device in ccdc */ + ret = ccdc_dev->hw_ops.set_hw_if_params(&sdinfo->ccdc_if_params); + if (ret) + goto unlock_out; + + /* update the if parameters to imp hw interface */ + if (imp_hw_if && imp_hw_if->set_hw_if_param) + ret = imp_hw_if->set_hw_if_param(vpfe_dev->ipipe, + &sdinfo->ccdc_if_params); + if (ret) + goto unlock_out; + + video->current_input = index; + +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_querystd - query std which is being input on external subdev + * @file: file pointer + * @priv: void pointer + * @std_id: pointer to v4l2_std_id structure + * + * call external subdev through v4l2_device_call_until_err to + * get the std that is being active. + * + * Return 0 on success, error code otherwise + */ +static int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querystd\n"); + + ret = mutex_lock_interruptible(&video->lock); + sdinfo = video->current_ext_subdev; + if (ret) + return ret; + + /* Call querystd function of decoder device */ + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + video, querystd, std_id); + mutex_unlock(&video->lock); + + return ret; +} + +/* + * vpfe_s_std - set std on external subdev + * @file: file pointer + * @priv: void pointer + * @std_id: pointer to v4l2_std_id structure + * + * set std pointed by std_id on external subdev by calling it using + * v4l2_device_call_until_err + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_std(struct file *file, void *priv, v4l2_std_id *std_id) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sdinfo; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_std\n"); + + /* Call decoder driver function to set the standard */ + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + sdinfo = video->current_ext_subdev; + /* If streaming is started, return device busy error */ + if (video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "streaming is started\n"); + ret = -EBUSY; + goto unlock_out; + } + + ret = v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, s_std, *std_id); + if (ret < 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Failed to set standard\n"); + goto unlock_out; + } + +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_enum_preset - enumerate dv_preset which are supported by + * to external subdev + * + * @file: file pointer + * @priv: void pointer + * @preset: pointer to v4l2_dv_enum_preset structure + * + * enum dv_preset's which are supported by external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int vpfe_enum_preset(struct file *file, void *fh, + struct v4l2_dv_enum_preset *preset) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_enum_preset\n"); + + return v4l2_subdev_call(subdev, video, enum_dv_presets, preset); +} + +/* + * vpfe_query_preset - query the dv_preset which is being input + * to external subdev + * + * @file: file pointer + * @priv: void pointer + * @preset: pointer to v4l2_preset structure + * + * get dv_preset which is being input on external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int vpfe_query_preset(struct file *file, void *fh, + struct v4l2_dv_preset *preset) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_query_preset\n"); + + return v4l2_subdev_call(subdev, video, query_dv_preset, preset); +} + +/* + * vpfe_s_preset - set dv_preset on external subdev + * @file: file pointer + * @priv: void pointer + * @preset: pointer to v4l2_preset structure + * + * set dv_preset pointed by preset on external subdev through + * v4l2_device_call_until_err, this configures amplifier also + * + * Return 0 on success, error code otherwise + */ +static int vpfe_s_preset(struct file *file, void *fh, + struct v4l2_dv_preset *preset) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_preset\n"); + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, + video->current_ext_subdev->grp_id, + video, s_dv_preset, preset); +} + +/* + * vpfe_g_preset - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @preset: pointer to v4l2_preset structure + * + * get dv_preset which is set on external subdev through + * v4l2_subdev_call + * + * Return 0 on success, error code otherwise + */ +static int vpfe_g_preset(struct file *file, void *fh, + struct v4l2_dv_preset *preset) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct v4l2_subdev *subdev = video->current_ext_subdev->subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_preset\n"); + + return v4l2_subdev_call(subdev, video, query_dv_preset, preset); +} + +/* + * Videobuf operations + */ +static int vpfe_videobuf_setup(struct videobuf_queue *vq, + unsigned int *count, + unsigned int *size) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_buffer_setup\n"); + + *size = video->fmt.fmt.pix.sizeimage; + + if (vpfe_dev->video_limit) { + while (*size * *count > vpfe_dev->video_limit) + (*count)--; + } + + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) { + if (*count < CONT_MIN_NUM_BUFFERS) + *count = CONT_MIN_NUM_BUFFERS; + } + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, + "count=%d, size=%d\n", *count, *size); + + return 0; +} + +static int vpfe_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + unsigned long addr; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_videobuf_prepare\n"); + + if (VIDEOBUF_NEEDS_INIT != vb->state) + return 0; + + /* Initialize buffer */ + vb->width = video->fmt.fmt.pix.width; + vb->height = video->fmt.fmt.pix.height; + vb->size = video->fmt.fmt.pix.sizeimage; + vb->field = field; + + ret = videobuf_iolock(vq, vb, NULL); + if (ret < 0) + return ret; + + addr = videobuf_to_dma_contig(vb); + /* Make sure user addresses are aligned to 32 bytes */ + if (!ALIGN(addr, 32)) + return -EINVAL; + + vb->state = VIDEOBUF_PREPARED; + + return 0; +} + +static void vpfe_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and device object */ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + unsigned long flags; + unsigned long empty; + unsigned long addr; + + spin_lock_irqsave(&video->dma_queue_lock, flags); + empty = list_empty(&video->dma_queue); + + /* add the buffer to the DMA queue */ + list_add_tail(&vb->queue, &video->dma_queue); + spin_unlock_irqrestore(&video->dma_queue_lock, flags); + + /* Change state of the buffer */ + vb->state = VIDEOBUF_QUEUED; + + /* this case happens in case of single shot */ + if (empty && video->started && pipe->state == + VPFE_PIPELINE_STREAM_SINGLESHOT && + video->state == VPFE_VIDEO_BUFFER_NOT_QUEUED) { + spin_lock(&video->dma_queue_lock); + addr = vpfe_get_next_buffer(video); + video->ops->queue(vpfe_dev, addr); + + video->state = VPFE_VIDEO_BUFFER_QUEUED; + spin_unlock(&video->dma_queue_lock); + + /* enable h/w each time in single shot */ + if (is_pipe_ready(pipe)) + vpfe_pipeline_set_stream(pipe, + VPFE_PIPELINE_STREAM_SINGLESHOT); + } +} + +static void vpfe_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct vpfe_fh *fh = vq->priv_data; + struct vpfe_video_device *video = fh->video; + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_videobuf_release\n"); + + if (video->memory == V4L2_MEMORY_MMAP) + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops vpfe_videobuf_qops = { + .buf_setup = vpfe_videobuf_setup, + .buf_prepare = vpfe_videobuf_prepare, + .buf_queue = vpfe_videobuf_queue, + .buf_release = vpfe_videobuf_release, +}; + +/* + * vpfe_reqbufs. supported REQBUF only once opening + * the device. + */ +static int vpfe_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + int ret; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_reqbufs\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != req_buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + if (video->io_usrs != 0) { + v4l2_err(&vpfe_dev->v4l2_dev, "Only one IO user allowed\n"); + ret = -EBUSY; + goto unlock_out; + } + + video->memory = req_buf->memory; + videobuf_queue_dma_contig_init(&video->buffer_queue, + &vpfe_videobuf_qops, vpfe_dev->pdev, + &video->irqlock, req_buf->type, + video->fmt.fmt.pix.field, + sizeof(struct videobuf_buffer), + fh, NULL); + + fh->io_allowed = 1; + video->io_usrs = 1; + INIT_LIST_HEAD(&video->dma_queue); + ret = videobuf_reqbufs(&video->buffer_queue, req_buf); + +unlock_out: + mutex_unlock(&video->lock); + return ret; +} + +/* + * vpfe_querybuf. query buffers for exchange + */ +static int vpfe_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_querybuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (video->memory != V4L2_MEMORY_MMAP) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid memory\n"); + return -EINVAL; + } + + /* Call videobuf_querybuf to get information */ + return videobuf_querybuf(&video->buffer_queue, buf); +} + +/* + * vpfe_qbuf. queue buffers for capture or processing + */ +static int vpfe_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_qbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != p->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* + * If this file handle is not allowed to do IO, + * return error + */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + return videobuf_qbuf(&video->buffer_queue, p); +} + +/* + * vpfe_dqbuf. deque buffer which is done with processing + */ +static int vpfe_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_dqbuf\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf->type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + return videobuf_dqbuf(&video->buffer_queue, + buf, file->f_flags & O_NONBLOCK); +} + +/* vpfe_start_capture: start streaming on all the subdevs */ +static int vpfe_start_capture(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + int ret = 0; + + video->started = 1; + + if (is_pipe_ready(pipe)) + ret = vpfe_pipeline_set_stream(pipe, pipe->state); + + return ret; +} + +/* + * vpfe_streamon - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @buf_type: enum v4l2_buf_type + * + * queue buffer onto hardware for capture/processing and + * start all the subdevs which are in media chain + * + * Return 0 on success, error code otherwise + */ +static int vpfe_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_fh *fh = file->private_data; + struct vpfe_ext_subdev_info *sdinfo; + unsigned long addr; + int ret = -EINVAL; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamon\n"); + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type && + V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) { + v4l2_err(&vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return ret; + } + + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + sdinfo = video->current_ext_subdev; + /* If buffer queue is empty, return error */ + if (list_empty(&video->buffer_queue.stream)) { + v4l2_err(&vpfe_dev->v4l2_dev, "buffer queue is empty\n"); + return -EIO; + } + /* Validate the pipeline */ + if (V4L2_BUF_TYPE_VIDEO_CAPTURE == buf_type) { + ret = vpfe_video_validate_pipeline(pipe); + if (ret < 0) + return ret; + } + /* Call videobuf_streamon to start streaming * in videobuf */ + ret = videobuf_streamon(&video->buffer_queue); + if (ret) + return ret; + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + goto streamoff; + + /* Get the next frame from the buffer queue */ + video->next_frm = list_entry(video->dma_queue.next, + struct videobuf_buffer, queue); + video->cur_frm = video->next_frm; + /* Remove buffer from the buffer queue */ + list_del(&video->cur_frm->queue); + /* Mark state of the current frame to active */ + video->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + video->field_id = 0; + addr = videobuf_to_dma_contig(video->cur_frm); + video->ops->queue(vpfe_dev, addr); + video->state = VPFE_VIDEO_BUFFER_QUEUED; + ret = vpfe_start_capture(video); + if (ret) + goto unlock_out; + + mutex_unlock(&video->lock); + return ret; +unlock_out: + mutex_unlock(&video->lock); +streamoff: + ret = videobuf_streamoff(&video->buffer_queue); + return ret; +} + +/* + * vpfe_streamoff - get dv_preset which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @buf_type: enum v4l2_buf_type + * + * stop all the subdevs which are in media chain + * + * Return 0 on success, error code otherwise + */ +static int vpfe_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_fh *fh = file->private_data; + int ret = 0; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_streamoff\n"); + + if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + buf_type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed) { + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "fh->io_allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!video->started) { + v4l2_err(&vpfe_dev->v4l2_dev, "device is not started\n"); + return -EINVAL; + } + + ret = mutex_lock_interruptible(&video->lock); + if (ret) + return ret; + + vpfe_stop_capture(video); + + ret = videobuf_streamoff(&video->buffer_queue); + mutex_unlock(&video->lock); + + return ret; +} + +/* + * vpfe_queryctrl - query for v4l2 controls which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @ctrl: pointer to v4l2_control structure + * + * get the v4l2 controls active on external subdev through + * v4l2_device_call_until_err + * + * Return return value returned by v4l2_device_call_until_err + */ +static int vpfe_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sub_dev = video->current_ext_subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_queryctrl\n"); + + /* pass it to sub device */ + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sub_dev->grp_id, + core, queryctrl, qc); +} + +/* + * vpfe_g_ctrl - get the v4l2 controls which is set on external subdev + * @file: file pointer + * @priv: void pointer + * @ctrl: pointer to v4l2_control structure + * + * get the v4l2 controls set on external subdev through + * v4l2_device_call_until_err + * + * Return return value returned by v4l2_device_call_until_err + */ +static int vpfe_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sub_dev = video->current_ext_subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_g_ctrl\n"); + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sub_dev->grp_id, + core, g_ctrl, ctrl); +} + +/* + * vpfe_s_ctrl - set the v4l2 controls on external subdev + * @file: file pointer + * @priv: void pointer + * @ctrl: pointer to v4l2_control structure + * + * call external subdev through v4l2_device_call_until_err to + * set v4l2 controls + * + * Return return value returned by v4l2_device_call_until_err + */ +static int vpfe_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct vpfe_video_device *video = video_drvdata(file); + struct vpfe_device *vpfe_dev = video->vpfe_dev; + struct vpfe_ext_subdev_info *sub_dev = video->current_ext_subdev; + + v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_s_ctrl\n"); + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sub_dev->grp_id, + core, s_ctrl, ctrl); +} + +/* vpfe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { + .vidioc_querycap = vpfe_querycap, + .vidioc_g_fmt_vid_cap = vpfe_g_fmt, + .vidioc_s_fmt_vid_cap = vpfe_s_fmt, + .vidioc_try_fmt_vid_cap = vpfe_try_fmt, + .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt, + .vidioc_g_fmt_vid_out = vpfe_g_fmt, + .vidioc_s_fmt_vid_out = vpfe_s_fmt, + .vidioc_try_fmt_vid_out = vpfe_try_fmt, + .vidioc_enum_fmt_vid_out = vpfe_enum_fmt, + .vidioc_enum_input = vpfe_enum_input, + .vidioc_g_input = vpfe_g_input, + .vidioc_s_input = vpfe_s_input, + .vidioc_querystd = vpfe_querystd, + .vidioc_s_std = vpfe_s_std, + .vidioc_enum_dv_presets = vpfe_enum_preset, + .vidioc_query_dv_preset = vpfe_query_preset, + .vidioc_s_dv_preset = vpfe_s_preset, + .vidioc_g_dv_preset = vpfe_g_preset, + .vidioc_reqbufs = vpfe_reqbufs, + .vidioc_querybuf = vpfe_querybuf, + .vidioc_qbuf = vpfe_qbuf, + .vidioc_dqbuf = vpfe_dqbuf, + .vidioc_streamon = vpfe_streamon, + .vidioc_streamoff = vpfe_streamoff, + .vidioc_queryctrl = vpfe_queryctrl, + .vidioc_g_ctrl = vpfe_g_ctrl, + .vidioc_s_ctrl = vpfe_s_ctrl, +}; + +/** + * VPFE video core operations + */ +static const struct vpfe_video_operations vpfe_video_dummy_ops = { +}; + +/* VPFE video init function */ +int vpfe_video_init(struct vpfe_video_device *video, const char *name) +{ + const char *direction; + int ret; + + switch (video->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + direction = "output"; + video->pad.flags = MEDIA_PAD_FL_INPUT; + video->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + direction = "input"; + video->pad.flags = MEDIA_PAD_FL_OUTPUT; + video->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + break; + default: + return -EINVAL; + } + + /* Initialize field of video device */ + video->video_dev.release = video_device_release; + video->video_dev.fops = &vpfe_fops; + video->video_dev.ioctl_ops = &vpfe_ioctl_ops; + video->video_dev.minor = -1; + video->video_dev.tvnorms = 0; + video->video_dev.current_norm = V4L2_STD_NTSC; + + snprintf(video->video_dev.name, sizeof(video->video_dev.name), + "DAVINCI VIDEO %s %s", name, direction); + + /* Initialize prio member of device object */ + v4l2_prio_init(&video->prio); + + spin_lock_init(&video->irqlock); + spin_lock_init(&video->dma_queue_lock); + mutex_init(&video->lock); + + ret = media_entity_init(&video->video_dev.entity, + 1, &video->pad, 0); + if (ret < 0) + return ret; + + video_set_drvdata(&video->video_dev, video); + + return 0; +} + +/* vpfe video device register function */ +int vpfe_video_register(struct vpfe_video_device *video, + struct v4l2_device *vdev) +{ + int ret; + + video->video_dev.v4l2_dev = vdev; + + ret = video_register_device(&video->video_dev, VFL_TYPE_GRABBER, -1); + if (ret < 0) + printk(KERN_ERR "%s: could not register video device (%d)\n", + __func__, ret); + + return ret; +} + +/* vpfe video device unregister function */ +void vpfe_video_unregister(struct vpfe_video_device *video) +{ + if (video_is_registered(&video->video_dev)) { + media_entity_cleanup(&video->video_dev.entity); + video_unregister_device(&video->video_dev); + } +} diff --git a/drivers/media/video/davinci/vpfe_video.h b/drivers/media/video/davinci/vpfe_video.h new file mode 100644 index 0000000..2bc10cc --- /dev/null +++ b/drivers/media/video/davinci/vpfe_video.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* All video device related structures will go here */ +#ifndef _VPFE_VIDEO_H +#define _VPFE_VIDEO_H + +#include +#include + +struct vpfe_device; + +/* + * struct vpfe_video_operations - VPFE video operations + * @queue: Resume streaming when a buffer is queued. Called on VIDIOC_QBUF + * if there was no buffer previously queued. + */ +struct vpfe_video_operations { + void(*queue)(struct vpfe_device *vpfe_dev, unsigned long addr); +}; + +enum vpfe_pipeline_stream_state { + VPFE_PIPELINE_STREAM_STOPPED, + VPFE_PIPELINE_STREAM_CONTINUOUS, + VPFE_PIPELINE_STREAM_SINGLESHOT +}; + +enum vpfe_video_state { + /* indicates that buffer is not queued */ + VPFE_VIDEO_BUFFER_NOT_QUEUED = 0, + /* indicates that buffer is queued */ + VPFE_VIDEO_BUFFER_QUEUED = 1, +}; + +struct vpfe_pipeline { + /* media pipeline */ + struct media_pipeline *pipe; + /* state of the pipeline, continous, + single-shot or stopped */ + enum vpfe_pipeline_stream_state state; + /* number of active input video entities */ + unsigned int input_num; + /* number of active output video entities */ + unsigned int output_num; + /* input video nodes in case of single-shot mode */ + struct vpfe_video_device *inputs[10]; + /* capturing video nodes */ + struct vpfe_video_device *outputs[10]; +}; + +#define to_vpfe_pipeline(__e) \ + container_of((__e)->pipe, struct vpfe_pipeline, pipe) + +#define to_vpfe_video(vdev) \ + container_of(vdev, struct vpfe_video_device, video_dev) + +struct vpfe_video_device { + /* vpfe device */ + struct vpfe_device *vpfe_dev; + /* video dev */ + struct video_device video_dev; + /* media pad of video entity */ + struct media_pad pad; + /* video operations supported by video device */ + const struct vpfe_video_operations *ops; + /* type of the video buffers used by user */ + enum v4l2_buf_type type; + /* Indicates id of the field which is being captured */ + u32 field_id; + /* pipiline for which video device is part of */ + struct vpfe_pipeline pipe; + /* Indicates whether streaming started */ + u8 started; + /* Indicates state of the stream */ + unsigned int state; + /* current input at the sub device */ + int current_input; + /* + * This field keeps track of type of buffer exchange mechanism + * user has selected + */ + enum v4l2_memory memory; + /* Used to keep track of state of the priority */ + struct v4l2_prio_state prio; + /* number of open instances of the channel */ + u32 usrs; + /* flag to indicate whether decoder is initialized */ + u8 initialized; + /* skip frame count */ + u8 skip_frame_count; + /* skip frame count init value */ + u8 skip_frame_count_init; + /* time per frame for skipping */ + struct v4l2_fract timeperframe; + /* ptr to currently selected sub device */ + struct vpfe_ext_subdev_info *current_ext_subdev; + /* Pointer pointing to current v4l2_buffer */ + struct videobuf_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct videobuf_buffer *next_frm; + /* Used to store pixel format */ + struct v4l2_format fmt; + /* Buffer queue used in video-buf */ + struct videobuf_queue buffer_queue; + /* Queue of filled frames */ + struct list_head dma_queue; + /* Used in video-buf */ + spinlock_t irqlock; + /* IRQ lock for DMA queue */ + spinlock_t dma_queue_lock; + /* lock used to access this structure */ + struct mutex lock; + /* number of users performing IO */ + u32 io_usrs; + /* + * offset where second field starts from the starting of the + * buffer for field seperated YCbCr formats + */ + u32 field_off; +}; + +int is_pipe_ready(struct vpfe_pipeline *pipe); +void vpfe_video_unregister(struct vpfe_video_device *video); +int vpfe_video_register(struct vpfe_video_device *video, + struct v4l2_device *vdev); +int vpfe_video_init(struct vpfe_video_device *video, const char *name); + +void vpfe_process_buffer_complete(struct vpfe_video_device *video); +void vpfe_schedule_bottom_field(struct vpfe_video_device *video); +void vpfe_schedule_next_buffer(struct vpfe_video_device *video); +unsigned long vpfe_get_next_buffer(struct vpfe_video_device *video); +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:44:40 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 16:14:40 +0530 Subject: [RESEND RFC PATCH v4 14/15] davinci: vpfe: delete vpfe_types.h In-Reply-To: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> References: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321526681-22574-15-git-send-email-manjunath.hadli@ti.com> delete vpfe_types.h as it is no longer used. Signed-off-by: Manjunath Hadli --- include/media/davinci/vpfe_types.h | 51 ------------------------------------ 1 files changed, 0 insertions(+), 51 deletions(-) delete mode 100644 include/media/davinci/vpfe_types.h diff --git a/include/media/davinci/vpfe_types.h b/include/media/davinci/vpfe_types.h deleted file mode 100644 index 76fb74b..0000000 --- a/include/media/davinci/vpfe_types.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2008-2009 Texas Instruments Inc - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef _VPFE_TYPES_H -#define _VPFE_TYPES_H - -#ifdef __KERNEL__ - -enum vpfe_pin_pol { - VPFE_PINPOL_POSITIVE, - VPFE_PINPOL_NEGATIVE -}; - -enum vpfe_hw_if_type { - /* BT656 - 8 bit */ - VPFE_BT656, - /* BT1120 - 16 bit */ - VPFE_BT1120, - /* Raw Bayer */ - VPFE_RAW_BAYER, - /* YCbCr - 8 bit with external sync */ - VPFE_YCBCR_SYNC_8, - /* YCbCr - 16 bit with external sync */ - VPFE_YCBCR_SYNC_16, - /* BT656 - 10 bit */ - VPFE_BT656_10BIT -}; - -/* interface description */ -struct vpfe_hw_if_param { - enum vpfe_hw_if_type if_type; - enum vpfe_pin_pol hdpol; - enum vpfe_pin_pol vdpol; -}; - -#endif -#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:44:38 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 16:14:38 +0530 Subject: [RESEND RFC PATCH v4 12/15] davinci: vpfe: add hardware interface for dm365 aew In-Reply-To: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> References: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321526681-22574-13-git-send-email-manjunath.hadli@ti.com> add the hardware functionality for enablling the dm365 auto exposure and white balance unit. The module supports hardware setup, isr management, and parameter validation. Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/dm365_a3_hw.c | 387 ++++++++++++++++++++ drivers/media/video/davinci/dm365_a3_hw.h | 253 +++++++++++++ drivers/media/video/davinci/dm365_aew.c | 544 +++++++++++++++++++++++++++++ drivers/media/video/davinci/dm365_aew.h | 55 +++ include/linux/dm365_aew.h | 153 ++++++++ 5 files changed, 1392 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/dm365_a3_hw.c create mode 100644 drivers/media/video/davinci/dm365_a3_hw.h create mode 100644 drivers/media/video/davinci/dm365_aew.c create mode 100644 drivers/media/video/davinci/dm365_aew.h create mode 100644 include/linux/dm365_aew.h diff --git a/drivers/media/video/davinci/dm365_a3_hw.c b/drivers/media/video/davinci/dm365_a3_hw.c new file mode 100644 index 0000000..b929c22 --- /dev/null +++ b/drivers/media/video/davinci/dm365_a3_hw.c @@ -0,0 +1,387 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include "dm365_af.h" +#include "dm365_aew.h" +#include "dm365_a3_hw.h" + +/* 3A hardware module configuration */ +struct a3_config { + /* lock for write protection */ + struct mutex lock; + /* base address */ + void __iomem *base; +}; + +/* H3A module configuration */ +static struct a3_config a3_cfg; + +/* register access routines */ +static inline u32 regr(unsigned int offset) +{ + return __raw_readl(a3_cfg.base + offset); +} + +static inline u32 regw(u32 val, unsigned int offset) +{ + __raw_writel(val, (a3_cfg.base + offset)); + + return val; +} + +/* Function to set register */ +int af_register_setup(struct device *dev, struct af_device *af_dev) +{ + unsigned int address; + unsigned int utemp; + int index; + + /* Lock resource since this register is written by + * both the AF and AEW drivers + */ + mutex_lock(&a3_cfg.lock); + /* Configure Hardware Registers */ + /* Set PCR Register */ + utemp = regr(AFPCR); /* Read PCR Register */ + + /*Set Accumulator Mode */ + utemp &= ~FVMODE; + if (af_dev->config->mode == ACCUMULATOR_PEAK) + utemp |= FVMODE; + /* Set A-law */ + utemp &= ~AF_ALAW_EN; + if (af_dev->config->alaw_enable == H3A_AF_ENABLE) + utemp |= AF_ALAW_EN; + + /* Set HFV only or HFV and VFV */ + utemp &= ~AF_VF_EN; + if (af_dev->config->fv_sel == AF_HFV_AND_VFV) + utemp |= AF_VF_EN; + /* Set RGB Position */ + utemp &= ~RGBPOS; + utemp |= (af_dev->config->rgb_pos) << AF_RGBPOS_SHIFT; + + utemp &= ~AF_MED_EN; + /*HMF Configurations */ + if (af_dev->config->hmf_config.enable == H3A_AF_ENABLE) { + /* Enable HMF */ + utemp |= AF_MED_EN; + /* Set Median Threshold */ + utemp &= ~MED_TH; + utemp |= (af_dev->config->hmf_config.threshold << + AF_MED_TH_SHIFT) & MED_TH; + } + /* Set PCR Register */ + regw(utemp , AFPCR); + mutex_unlock(&a3_cfg.lock); + + /* Configure AFPAX1 */ + /*Paxel parameter configuration */ + /*Set Width in AFPAX1 Register */ + utemp = SET_VAL(af_dev->config->paxel_config.width) << AF_PAXW_SHIFT; + + /* Set height in AFPAX1 */ + utemp &= ~PAXH; + utemp |= SET_VAL(af_dev->config->paxel_config.height); + regw(utemp , AFPAX1); + /* Configure AFPAX2 Register */ + /* Set Column Increment in AFPAX2 Register */ + utemp = 0; + utemp &= ~AFINCH; + utemp |= SET_VAL(af_dev->config->paxel_config.column_incr) << + AF_COLUMN_INCR_SHIFT; + + /* Set Line Increment in AFPAX2 Register */ + utemp &= ~AFINCV; + utemp |= SET_VAL(af_dev->config->paxel_config.line_incr) << + AF_LINE_INCR_SHIFT; + + /* Set Vertical Count */ + utemp &= ~PAXVC; + utemp |= (af_dev->config->paxel_config.vt_cnt - 1) << AF_VT_COUNT_SHIFT; + /* Set Horizontal Count */ + utemp &= ~PAXHC; + utemp |= af_dev->config->paxel_config.hz_cnt - 1; + regw(utemp, AFPAX2); + + /* Configure PAXSTART Register */ + /*Configure Horizontal Start */ + utemp = 0; + utemp &= ~PAXSH; + utemp |= af_dev->config->paxel_config.hz_start << AF_HZ_START_SHIFT; + /* Configure Vertical Start */ + utemp &= ~PAXSV; + utemp |= af_dev->config->paxel_config.vt_start; + regw(utemp , AFPAXSTART); + + /*SetIIRSH Register */ + regw(af_dev->config->iir_config.hz_start_pos, AFIIRSH); + + /* Set IIR Filter0 Coefficients */ + address = AFCOEF010; + for (index = 0; index < AF_NUMBER_OF_HFV_COEF; index += 2) { + utemp = af_dev->config->iir_config.coeff_set0[index] & + COEF_MASK0; + if (index < AF_NUMBER_OF_HFV_COEF - 1) { + utemp |= (af_dev->config->iir_config. + coeff_set0[index + 1] << AF_COEF_SHIFT) & COEF_MASK1; + } + regw(utemp, address); + dev_dbg(dev, "COEF0 %x\n", regr(address)); + address = address + AF_OFFSET; + } + + /* Set IIR Filter1 Coefficients */ + address = AFCOEF110; + for (index = 0; index < AF_NUMBER_OF_HFV_COEF; index += 2) { + utemp = af_dev->config->iir_config.coeff_set1[index] & + COEF_MASK0; + if (index < AF_NUMBER_OF_HFV_COEF-1) { + utemp |= (af_dev->config->iir_config. + coeff_set1[index + 1] << AF_COEF_SHIFT) & COEF_MASK1; + } + regw(utemp, address); + dev_dbg(dev, "COEF0 %x\n", regr(address)); + address = address + AF_OFFSET; + } + + /* HFV thresholds for FIR 1 & 2 */ + utemp = af_dev->config->fir_config.hfv_thr1 & HFV_THR0_MASK; + utemp |= (af_dev->config->fir_config.hfv_thr2 << HFV_THR2_SHIFT) & + HFV_THR2_MASK; + regw(utemp, AF_HFV_THR); + + /* VFV coefficients and thresholds */ + utemp = af_dev->config->fir_config.coeff_1[0] & VFV_COEF_MASK0; + utemp |= (af_dev->config->fir_config.coeff_1[1] << 8) & VFV_COEF_MASK1; + utemp |= (af_dev->config->fir_config.coeff_1[2] << 16) & VFV_COEF_MASK2; + utemp |= (af_dev->config->fir_config.coeff_1[3] << 24) & VFV_COEF_MASK3; + regw(utemp, AF_VFV_CFG1); + + utemp = af_dev->config->fir_config.coeff_1[4] & VFV_COEF_MASK0; + utemp |= (af_dev->config->fir_config.vfv_thr1 << VFV_THR_SHIFT) & + VFV_THR_MASK; + regw(utemp, AF_VFV_CFG2); + + /* VFV coefficients and thresholds */ + utemp = af_dev->config->fir_config.coeff_2[0] & VFV_COEF_MASK0; + utemp |= (af_dev->config->fir_config.coeff_2[1] << 8) & VFV_COEF_MASK1; + utemp |= (af_dev->config->fir_config.coeff_2[2] << 16) & VFV_COEF_MASK2; + utemp |= (af_dev->config->fir_config.coeff_2[3] << 24) & VFV_COEF_MASK3; + regw(utemp, AF_VFV_CFG3); + + utemp = af_dev->config->fir_config.coeff_2[4] & VFV_COEF_MASK0; + utemp |= (af_dev->config->fir_config.vfv_thr2 << VFV_THR_SHIFT) & + VFV_THR_MASK; + regw(utemp, AF_VFV_CFG4); + /* Set AFBUFST to Current buffer Physical Address */ + regw((unsigned int)(virt_to_phys(af_dev->buff_curr)), AFBUFST); + + return 0; +} +EXPORT_SYMBOL(af_register_setup); + +inline u32 af_get_hw_state(void) +{ + return (regr(AFPCR) & AF_BUSYAF) >> AF_BUSYAF_SHIFT; +} +EXPORT_SYMBOL(af_get_hw_state); + +inline u32 aew_get_hw_state(void) +{ + return (regr(AEWPCR) & AEW_BUSYAEWB) >> AEW_BUSYAEW_SHIFT; +} +EXPORT_SYMBOL(aew_get_hw_state); + +inline u32 af_get_enable(void) +{ + return regr(AFPCR) & AF_EN; +} +EXPORT_SYMBOL(af_get_enable); + +inline u32 aew_get_enable(void) +{ + return (regr(AEWPCR) & AEW_EN) >> AEW_EN_SHIFT; +} +EXPORT_SYMBOL(aew_get_enable); + +/* Function to Enable/Disable AF Engine */ +inline void af_engine_setup(struct device *dev, int enable) +{ + unsigned int pcr; + + mutex_lock(&a3_cfg.lock); + pcr = regr(AFPCR); + dev_dbg(dev, "Engine Setup value before PCR : %x\n", pcr); + + /* Set AF_EN bit in PCR Register */ + if (enable) + pcr |= AF_EN; + else + pcr &= ~AF_EN; + + regw(pcr, AFPCR); + mutex_unlock(&a3_cfg.lock); + + dev_dbg(dev, "Engine Setup value after PCR : %x\n", pcr); +} +EXPORT_SYMBOL(af_engine_setup); + +/* Function to set address */ +inline void af_set_address(struct device *dev, unsigned long address) +{ + regw((address & ~0x3F), AFBUFST); +} +EXPORT_SYMBOL(af_set_address); + +/* Function to set hardware configuration registers */ +int aew_register_setup(struct device *dev, struct aew_device *aew_dev) +{ + unsigned utemp; + + mutex_lock(&a3_cfg.lock); + /* Set up the registers */ + utemp = regr(AEWPCR); + + /* Enable A Law */ + if (aew_dev->config->alaw_enable == H3A_AEW_ENABLE) + utemp |= AEW_ALAW_EN; + else + utemp &= ~AEW_ALAW_EN; + + utemp &= ~AEW_MED_EN; + /*HMF Configurations */ + if (aew_dev->config->hmf_config.enable == H3A_AEW_ENABLE) { + /* Enable HMF */ + utemp |= AEW_MED_EN; + /* Set Median Threshold */ + utemp &= ~MED_TH; + utemp |= (aew_dev->config->hmf_config.threshold << + AF_MED_TH_SHIFT) & MED_TH; + } + + /*Configure Saturation limit */ + utemp &= ~AVE2LMT; + utemp |= aew_dev->config->saturation_limit << AEW_AVE2LMT_SHIFT; + regw(utemp, AEWPCR); + mutex_unlock(&a3_cfg.lock); + + /*Window parameter configuration */ + /* Configure Window Width in AEWWIN1 register */ + utemp = SET_VAL(aew_dev->config->window_config.height) << + AEW_WINH_SHIFT; + + /* Configure Window height in AEWWIN1 register */ + utemp |= SET_VAL(aew_dev->config->window_config.width) << + AEW_WINW_SHIFT; + + /* Configure Window vertical count in AEWWIN2 register */ + utemp |= (aew_dev->config->window_config.vt_cnt - 1) << + AEW_VT_COUNT_SHIFT; + + /* Configure Window horizontal count in AEWWIN1 register */ + utemp |= (aew_dev->config->window_config).hz_cnt - 1; + + /* Configure Window vertical start in AEWWIN1 register */ + regw(utemp, AEWWIN1); + + /*Window Start parameter configuration */ + utemp = aew_dev->config->window_config.vt_start << AEW_VT_START_SHIFT; + + /* Configure Window horizontal start in AEWWIN2 register */ + utemp &= ~WINSH; + utemp |= (aew_dev->config->window_config).hz_start; + regw(utemp, AEWINSTART); + + /*Window Line Increment configuration */ + /*Configure vertical line increment in AEWSUBWIN */ + utemp = SET_VAL(aew_dev->config->window_config. + vt_line_incr) << AEW_LINE_INCR_SHIFT; + + /* Configuring Horizontal Line increment in AEWSUBWIN */ + utemp &= ~AEWINCH; + utemp |= SET_VAL(aew_dev->config->window_config.hz_line_incr); + + regw(utemp, AEWSUBWIN); + + /* Black Window Configuration */ + /* Configure vertical start and height in AEWWINBLK */ + utemp = (aew_dev->config->blackwindow_config).vt_start << + AEW_BLKWIN_VT_START_SHIFT; + + /* Configure height in Black window */ + utemp &= ~BLKWINH; + utemp |= SET_VAL(aew_dev->config->blackwindow_config.height); + regw(utemp, AEWINBLK); + + /* AE/AWB engine configuration */ + utemp = aew_dev->config->sum_shift & AEW_SUMSHFT_MASK; + utemp |= (aew_dev->config->out_format << AEFMT_SHFT) & AEFMT_MASK; + regw(utemp, AEW_CFG); + + /* Set AFBUFST to Current buffer Physical Address */ + regw((unsigned int)(virt_to_phys(aew_dev->buff_curr)), AEWBUFST); + + return 0; +} +EXPORT_SYMBOL(aew_register_setup); + +/* Function to enable/ disable AEW Engine */ +inline void aew_engine_setup(struct device *dev, int value) +{ + unsigned int pcr; + + dev_dbg(dev, "AEW_REG(PCR) Before Setting %x\n", regr(AEWPCR)); + mutex_lock(&a3_cfg.lock); + /* Read Pcr Register */ + pcr = regr(AEWPCR); + pcr &= ~AEW_EN; + pcr |= value << AEW_EN_SHIFT; + /*Set AF_EN bit in PCR Register */ + regw(pcr, AEWPCR); + mutex_unlock(&a3_cfg.lock); + dev_dbg(dev, "After Setting %d : PCR VALUE %x\n", value, regr(AEWPCR)); + +} +EXPORT_SYMBOL(aew_engine_setup); + +/* Function used to set adddress */ +inline void aew_set_address(struct device *dev, unsigned long address) +{ + regw((address & ~0x3F), AEWBUFST); +} +EXPORT_SYMBOL(aew_set_address); + +static int dm365_afew_hw_init(void) +{ + mutex_init(&a3_cfg.lock); + a3_cfg.base = ioremap(DM365_A3_HW_ADDR, DM365_A3_HW_ADDR_SIZE); + if (!a3_cfg.base) { + printk(KERN_ERR "Unable to ioremap 3A registers\n"); + return -EINVAL; + } + regw(0, LINE_START); + + return 0; +} + +static void dm365_afew_hw_exit(void) +{ + iounmap(a3_cfg.base); +} +subsys_initcall(dm365_afew_hw_init); +module_exit(dm365_afew_hw_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/davinci/dm365_a3_hw.h b/drivers/media/video/davinci/dm365_a3_hw.h new file mode 100644 index 0000000..eb5a1d4 --- /dev/null +++ b/drivers/media/video/davinci/dm365_a3_hw.h @@ -0,0 +1,253 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef DM365_A3_HW_H +#define DM365_A3_HW_H + +#include +#include +#include +#include "dm365_aew.h" +#include "dm365_af.h" + +/* AF/AE/AWB Base address and range */ +#define DM365_A3_HW_ADDR 0x1c71400 +#define DM365_A3_HW_ADDR_SIZE 128 +/* AF Register Offsets */ + +/* Peripheral Revision and Class Information */ +#define AFPID 0x0 +/* Peripheral Control Register */ +#define AFPCR 0x4 +/* Setup for the Paxel Configuration */ +#define AFPAX1 0x8 +/* Setup for the Paxel Configuration */ +#define AFPAX2 0xc +/* Start Position for AF Engine Paxels */ +#define AFPAXSTART 0x10 +/* Start Position for IIRSH */ +#define AFIIRSH 0x14 +/* SDRAM/DDRAM Start address */ +#define AFBUFST 0x18 +/* IIR filter coefficient data for SET 0 */ +#define AFCOEF010 0x1c +/* IIR filter coefficient data for SET 0 */ +#define AFCOEF032 0x20 +/* IIR filter coefficient data for SET 0 */ +#define AFCOEF054 0x24 +/* IIR filter coefficient data for SET 0 */ +#define AFCOEF076 0x28 +/* IIR filter coefficient data for SET 0 */ +#define AFCOEF098 0x2c +/* IIR filter coefficient data for SET 0 */ +#define AFCOEF0010 0x30 +/* IIR filter coefficient data for SET 1 */ +#define AFCOEF110 0x34 +/* IIR filter coefficient data for SET 1 */ +#define AFCOEF132 0x38 +/* IIR filter coefficient data for SET 1 */ +#define AFCOEF154 0x3c +/* IIR filter coefficient data for SET 1 */ +#define AFCOEF176 0x40 +/* IIR filter coefficient data for SET 1 */ +#define AFCOEF198 0x44 +/* IIR filter coefficient data for SET 1 */ +#define AFCOEF1010 0x48 + +/* Vertical Focus vlaue configuration 1 */ +#define AF_VFV_CFG1 0x68 +/* Vertical Focus vlaue configuration 2 */ +#define AF_VFV_CFG2 0x6c +/* Vertical Focus vlaue configuration 3 */ +#define AF_VFV_CFG3 0x70 +/* Vertical Focus vlaue configuration 4 */ +#define AF_VFV_CFG4 0x74 +/* Horizontal Focus vlaue Threshold */ +#define AF_HFV_THR 0x78 +/* COEFFICIENT BASE ADDRESS */ + +#define AF_OFFSET 0x00000004 + +/* AEW Register offsets */ +#define AEWPID AFPID +/* Peripheral Control Register */ +#define AEWPCR AFPCR +/* Configuration for AE/AWB Windows */ +#define AEWWIN1 0x4c +/* Start position for AE/AWB Windows */ +#define AEWINSTART 0x50 +/* Start position and height for black linr of AE/AWB Windows */ +#define AEWINBLK 0x54 +/* Configuration for subsampled data in AE/AWB windows */ +#define AEWSUBWIN 0x58 +/* SDRAM/DDRAM Start address for AEW Engine */ +#define AEWBUFST 0x5c +/* Line start */ +#define LINE_START 0x64 + +/* AEW Engine configuration */ +#define AEW_CFG 0x60 + +/* PID fields */ +#define PID_MINOR (0x3f << 0) +#define PID_MAJOR (7 << 8) +#define PID_RTL (0x1f << 11) +#define PID_FUNC (0xfff << 16) +#define PID_SCHEME (3 << 30) + +/* PCR FIELDS */ + +/*Saturation Limit */ +#define AVE2LMT (0x3ff << 22) +#define AF_VF_EN (1 << 20) +#define AEW_MED_EN (1 << 19) +/* Busy bit for AEW */ +#define AEW_BUSYAEWB (1 << 18) +/* Alaw Enable/Disable Bit */ +#define AEW_ALAW_EN (1 << 17) +/* AEW Engine Enable/Disable bit */ +#define AEW_EN (1 << 16) +/* Busy Bit for AF */ +#define AF_BUSYAF (1 << 15) +#define FVMODE (1 << 14) +#define RGBPOS (7 << 11) +#define MED_TH (0xff << 3) +#define AF_MED_EN (1 << 2) +#define AF_ALAW_EN (1 << 1) +#define AF_EN (1 << 0) + +/* + * AFPAX1 fields + */ +#define PAXW (0xff << 16) +#define PAXH 0xff + +/* + * AFPAX2 fields + */ +#define AFINCH (0xf << 17) +#define AFINCV (0xf << 13) +#define PAXVC (0x7f << 6) +#define PAXHC 0x3f + +/* + * AFPAXSTART fields + */ +#define PAXSH (0xfff << 16) +#define PAXSV 0xfff + +/* + * IIR COEFFICIENT MASKS + */ +#define COEF_MASK0 0xfff +#define COEF_MASK1 (0xfff << 16) + +/* + * VFV_CFGX COEFFICIENT MASKS + */ +#define VFV_COEF_MASK0 0xff +#define VFV_COEF_MASK1 (0xff << 8) +#define VFV_COEF_MASK2 (0xff << 16) +#define VFV_COEF_MASK3 (0xff << 24) + +/* HFV THR MASKS */ +#define HFV_THR0_MASK 0xffff +#define HFV_THR2_SHIFT 16 +#define HFV_THR2_MASK (0xffff << HFV_THR2_SHIFT) + +/* VFV THR MASKS */ +#define VFV_THR_SHIFT 16 +#define VFV_THR_MASK (0xffff << VFV_THR_SHIFT) + +/* BIT SHIFTS */ +#define AF_BUSYAF_SHIFT 15 +#define AEW_EN_SHIFT 16 +#define AEW_BUSYAEW_SHIFT 18 +#define AF_RGBPOS_SHIFT 11 +#define AF_MED_TH_SHIFT 3 +#define AF_PAXW_SHIFT 16 +#define AF_LINE_INCR_SHIFT 13 +#define AF_COLUMN_INCR_SHIFT 17 +#define AF_VT_COUNT_SHIFT 6 +#define AF_HZ_START_SHIFT 16 +#define AF_COEF_SHIFT 16 + +/* AEWWIN1 fields */ +/* Window Height */ +#define WINH (0x7f << 24) +/* Window Width */ +#define WINW (0x7f << 13) +/* Window vertical Count */ +#define WINVC (0x7f << 6) +/* Window Horizontal Count */ +#define WINHC 0x3f + +/* AEWWINSTART fields */ +/* Window Vertical Start */ +#define WINSV (0xfff << 16) +/* Window Horizontal start */ +#define WINSH 0xfff + +/* AEWWINBLK fields + * Black Window Vertical Start + */ +#define BLKWINSV (0xfff << 16) +/* Black Window height */ +#define BLKWINH 0x7f + +/* AEWSUBWIN fields + * Vertical Lime Increment + */ +#define AEWINCV (0xf << 8) +/* Horizontal Line Increment */ +#define AEWINCH 0xf + +/* BIT POSITIONS */ +#define AEW_AVE2LMT_SHIFT 22 +#define AEW_WINH_SHIFT 24 +#define AEW_WINW_SHIFT 13 +#define AEW_VT_COUNT_SHIFT 6 +#define AEW_VT_START_SHIFT 16 +#define AEW_LINE_INCR_SHIFT 8 + +#define AEW_EN_SHIFT 16 +#define AEW_BUSYAEWB_SHIFT 18 +#define AEW_BLKWIN_VT_START_SHIFT 16 + +#define AEFMT_SHFT 8 +#define AEFMT_MASK (3 << AEFMT_SHFT) +#define AEW_SUMSHFT_MASK 0xf + +#define SET_VAL(x) (((x) / 2) - 1) +#define NOT_EVEN 1 +#define CHECK_EVEN(x) ((x) % 2) + +/* Function declaration for af */ +int af_register_setup(struct device *, struct af_device *); +void af_set_address(struct device *, unsigned long); +void af_engine_setup(struct device *, int); +u32 af_get_hw_state(void); +u32 af_get_enable(void); + +/* Function Declaration for aew */ +int aew_register_setup(struct device *, struct aew_device *); +void aew_set_address(struct device *, unsigned long); +void aew_engine_setup(struct device *, int); +u32 aew_get_hw_state(void); +u32 aew_get_enable(void); + +#endif /*end of #ifdef __DAVINCI_A3_HW_H */ diff --git a/drivers/media/video/davinci/dm365_aew.c b/drivers/media/video/davinci/dm365_aew.c new file mode 100644 index 0000000..8c193c8 --- /dev/null +++ b/drivers/media/video/davinci/dm365_aew.c @@ -0,0 +1,544 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include "dm365_a3_hw.h" +#include "vpss.h" +#include "vpfe_aew.h" + +#define DRIVERNAME "DM365AEW" + +/* Global structure */ +static struct aew_device *aew_dev_configptr; +static struct device *aewdev; + +int aew_validate_parameters(void) +{ + + /* Check horizontal Count */ + if (aew_dev_configptr->config->window_config.hz_cnt < + AEW_WINDOW_HORIZONTAL_COUNT_MIN || + aew_dev_configptr->config->window_config.hz_cnt > + AEW_WINDOW_HORIZONTAL_COUNT_MAX) { + dev_err(aewdev, "Horizontal Count is incorrect\n"); + return -EINVAL; + } + /* Check Vertical Count */ + if (aew_dev_configptr->config->window_config.vt_cnt < + AEW_WINDOW_VERTICAL_COUNT_MIN || + aew_dev_configptr->config->window_config.vt_cnt > + AEW_WINDOW_VERTICAL_COUNT_MAX) { + dev_err(aewdev, "Vertical Count is incorrect\n"); + return -EINVAL; + } + /* Check line increment */ + if (NOT_EVEN == CHECK_EVEN(aew_dev_configptr->config->window_config. + hz_line_incr) || aew_dev_configptr->config->window_config. + hz_line_incr < AEW_HZ_LINEINCR_MIN || + aew_dev_configptr->config->window_config.hz_line_incr > + AEW_HZ_LINEINCR_MAX) { + dev_err(aewdev, "Invalid Parameters\n"); + dev_err(aewdev, "Horizontal Line Increment is incorrect\n"); + return -EINVAL; + } + /* Check line increment */ + if (NOT_EVEN == CHECK_EVEN(aew_dev_configptr->config->window_config. + vt_line_incr) || aew_dev_configptr->config->window_config. + vt_line_incr < AEW_VT_LINEINCR_MIN || + aew_dev_configptr->config->window_config.vt_line_incr > + AEW_VT_LINEINCR_MAX) { + dev_err(aewdev, "Invalid Parameters\n"); + dev_err(aewdev, "Vertical Line Increment is incorrect\n"); + return -EINVAL; + } + /* Check width */ + if (NOT_EVEN == CHECK_EVEN(aew_dev_configptr->config->window_config. + width) || aew_dev_configptr->config->window_config.width < + AEW_WIDTH_MIN || + aew_dev_configptr->config->window_config.width > + AEW_WIDTH_MAX) { + dev_err(aewdev, "Width is incorrect\n"); + return -EINVAL; + } + /* Check Height */ + if (NOT_EVEN == CHECK_EVEN(aew_dev_configptr->config->window_config. + height) || aew_dev_configptr->config->window_config.height < + AEW_HEIGHT_MIN || + aew_dev_configptr->config->window_config.height > + AEW_HEIGHT_MAX) { + dev_err(aewdev, "height incorrect\n"); + return -EINVAL; + } + /* Check Horizontal Start */ + if (aew_dev_configptr->config->window_config.hz_start < + AEW_HZSTART_MIN || + aew_dev_configptr->config->window_config.hz_start > + AEW_HZSTART_MAX) { + dev_err(aewdev, "horizontal start is incorrect\n"); + return -EINVAL; + } + if (aew_dev_configptr->config->window_config.vt_start > + AEW_VTSTART_MAX) { + dev_err(aewdev, "Vertical start is incorrect\n"); + return -EINVAL; + } + if (aew_dev_configptr->config->alaw_enable > H3A_AEW_ENABLE || + aew_dev_configptr->config->alaw_enable < H3A_AEW_DISABLE) { + dev_err(aewdev, "A Law setting is incorrect\n"); + return -EINVAL; + } + if (aew_dev_configptr->config->saturation_limit > AEW_AVELMT_MAX) { + dev_err(aewdev, "Saturation Limit is incorrect\n"); + return -EINVAL; + } + /* Check Black Window Height */ + if (NOT_EVEN == CHECK_EVEN(aew_dev_configptr->config-> + blackwindow_config.height) || + aew_dev_configptr->config->blackwindow_config.height < + AEW_BLKWINHEIGHT_MIN || + aew_dev_configptr->config->blackwindow_config.height > + AEW_BLKWINHEIGHT_MAX) { + dev_err(aewdev, "Black Window height incorrect\n"); + return -EINVAL; + } + /* Check Black Window Height */ + if (NOT_EVEN == CHECK_EVEN(aew_dev_configptr->config-> + blackwindow_config.height) || + aew_dev_configptr->config->blackwindow_config.vt_start < + AEW_BLKWINVTSTART_MIN || + aew_dev_configptr->config->blackwindow_config.vt_start > + AEW_BLKWINVTSTART_MAX) { + dev_err(aewdev, "Black Window vertical Start is incorrect\n"); + return -EINVAL; + } + + if (aew_dev_configptr->config->out_format < AEW_OUT_SUM_OF_SQUARES || + aew_dev_configptr->config->out_format > AEW_OUT_SUM_ONLY) { + dev_err(aewdev, "Invalid out_format\n"); + return -EINVAL; + } + + if (aew_dev_configptr->config->sum_shift > AEW_SUMSHIFT_MAX) { + dev_err(aewdev, "sum_shift param is invalid, max = %d\n", + AEW_SUMSHIFT_MAX); + return -EINVAL; + } + + return 0; +} + +/* inline function to free reserver pages */ +inline void aew_free_pages(unsigned long addr, unsigned long bufsize) +{ + unsigned long tempaddr; + unsigned long size; + + tempaddr = addr; + if (!addr) + return; + + size = PAGE_SIZE << (get_order(bufsize)); + while (size > 0) { + ClearPageReserved(virt_to_page(addr)); + addr += PAGE_SIZE; + size -= PAGE_SIZE; + } + free_pages(tempaddr, get_order(bufsize)); +} + +/* Function to perform hardware Configuration */ +int aew_hardware_setup(void) +{ + unsigned int busyaew; + unsigned long size; + unsigned long adr; + /* Size for buffer in bytes */ + int buff_size; + int result; + + /* Get the value of PCR register */ + busyaew = aew_get_hw_state(); + + /* If H3A Engine is busy then return */ + if (busyaew == 1) { + dev_err(aewdev, "Error : AEW Engine is busy\n"); + return -EBUSY; + } + + result = aew_validate_parameters(); + dev_dbg(aewdev, "Result = %d\n", result); + if (result < 0) { + dev_err(aewdev, "Error : Parameters are incorrect\n"); + return result; + } + + /* Deallocate the previously allocated buffers */ + if (aew_dev_configptr->buff_old) + aew_free_pages((unsigned long)aew_dev_configptr->buff_old, + aew_dev_configptr->size_window); + + if (aew_dev_configptr->buff_curr) + aew_free_pages((unsigned long)aew_dev_configptr-> + buff_curr, aew_dev_configptr->size_window); + + if (aew_dev_configptr->buff_app) + aew_free_pages((unsigned long)aew_dev_configptr-> + buff_app, aew_dev_configptr->size_window); + + /* + * Allocat the buffers as per the new buffer size + * Allocate memory for old buffer + */ + if (aew_dev_configptr->config->out_format == AEW_OUT_SUM_ONLY) + buff_size = (aew_dev_configptr->config->window_config.hz_cnt) * + (aew_dev_configptr->config->window_config.vt_cnt) * + AEW_WINDOW_SIZE_SUM_ONLY; + else + buff_size = (aew_dev_configptr->config->window_config.hz_cnt) * + (aew_dev_configptr->config->window_config.vt_cnt) * + AEW_WINDOW_SIZE; + + aew_dev_configptr->buff_old = (void *)__get_free_pages(GFP_KERNEL | + GFP_DMA, get_order(buff_size)); + + if (aew_dev_configptr->buff_old == NULL) + return -ENOMEM; + + /* Make pges reserved so that they will be swapped out */ + adr = (unsigned long)aew_dev_configptr->buff_old; + size = PAGE_SIZE << (get_order(buff_size)); + while (size > 0) { + /* + * make sure the frame buffers + * are never swapped out of memory + */ + SetPageReserved(virt_to_page(adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + /* Allocate memory for current buffer */ + aew_dev_configptr->buff_curr = (void *)__get_free_pages(GFP_KERNEL | + GFP_DMA, get_order(buff_size)); + + if (aew_dev_configptr->buff_curr == NULL) { + /*Free all buffer that are allocated */ + if (aew_dev_configptr->buff_old) + aew_free_pages((unsigned long)aew_dev_configptr-> + buff_old, buff_size); + return -ENOMEM; + } + + /* Make pges reserved so that they will be swapped out */ + adr = (unsigned long)aew_dev_configptr->buff_curr; + size = PAGE_SIZE << (get_order(buff_size)); + while (size > 0) { + /* + * make sure the frame buffers + * are never swapped out of memory + */ + SetPageReserved(virt_to_page(adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + /* Allocate memory for application buffer */ + aew_dev_configptr->buff_app = (void *)__get_free_pages(GFP_KERNEL | + GFP_DMA, get_order(buff_size)); + + if (aew_dev_configptr->buff_app == NULL) { + /* Free all buffer that were allocated previously */ + if (aew_dev_configptr->buff_old) + aew_free_pages((unsigned long)aew_dev_configptr-> + buff_old, buff_size); + if (aew_dev_configptr->buff_curr) + aew_free_pages((unsigned long)aew_dev_configptr-> + buff_curr, buff_size); + return -ENOMEM; + } + + /* Make pages reserved so that they will be swapped out */ + adr = (unsigned long)aew_dev_configptr->buff_app; + size = PAGE_SIZE << (get_order(buff_size)); + while (size > 0) { + /* + * make sure the frame buffers + * are never swapped out of memory + */ + SetPageReserved(virt_to_page(adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + /* Set the registers */ + aew_register_setup(aewdev, aew_dev_configptr); + aew_dev_configptr->size_window = buff_size; + aew_dev_configptr->aew_config = H3A_AEW_CONFIG; + + return 0; +} + +int aew_open(void) +{ + /* Return if Device is in use (Single Channel Support is provided) */ + if (aew_dev_configptr->in_use == AEW_IN_USE) + return -EBUSY; + + /* Set the aew_dev_configptr structure */ + aew_dev_configptr->config = NULL; + + /* Allocate memory for configuration structure of this channel */ + aew_dev_configptr->config = (struct aew_configuration *) + kmalloc(sizeof(struct aew_configuration), GFP_KERNEL); + + if (aew_dev_configptr->config == NULL) { + dev_err(aewdev, "Error : Kmalloc fail\n"); + return -ENOMEM; + } + + /* Device is in use */ + aew_dev_configptr->in_use = AEW_IN_USE; + /* No Hardware Set up done */ + aew_dev_configptr->aew_config = H3A_AEW_CONFIG_NOT_DONE; + /* No statistics are available */ + aew_dev_configptr->buffer_filled = 0; + /* Set Window Size to 0 */ + aew_dev_configptr->size_window = 0; + + return 0; +} + +int aew_release(void) +{ + aew_engine_setup(aewdev, 0); + /* The Application has closed device so device is not in use */ + aew_dev_configptr->in_use = AEW_NOT_IN_USE; + + /* Release memory for configuration structure of this channel */ + kfree(aew_dev_configptr->config); + + /* Free Old Buffer */ + if (aew_dev_configptr->buff_old) + aew_free_pages((unsigned long)aew_dev_configptr->buff_old, + aew_dev_configptr->size_window); + + /* Free Current Buffer */ + if (aew_dev_configptr->buff_curr) + aew_free_pages((unsigned long)aew_dev_configptr-> + buff_curr, aew_dev_configptr->size_window); + + /* Free Application Buffer */ + if (aew_dev_configptr->buff_app) + aew_free_pages((unsigned long)aew_dev_configptr->buff_app, + aew_dev_configptr->size_window); + + aew_dev_configptr->buff_old = NULL; + aew_dev_configptr->buff_curr = NULL; + aew_dev_configptr->config = NULL; + aew_dev_configptr->buff_app = NULL; + + return 0; +} + +/* + * This function will process IOCTL commands sent by the application and + * control the devices IO operations. + */ +int aew_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + /* Stores Previous Configurations */ + struct aew_configuration aewconfig = *(aew_dev_configptr->config); + struct aew_statdata *stat_data = (struct aew_statdata *)arg; + void *buffer_temp; + int result; + + /* Switch according to IOCTL command */ + switch (cmd) { + /* + * This ioctl is used to perform hardware set up + * and will set all the registers for AF engine + */ + case AEW_S_PARAM: + /* Copy config structure passed by user */ + memcpy(aew_dev_configptr->config, + (struct aew_configuration *)arg, + sizeof(struct aew_configuration)); + + /* Call aew_hardware_setup to perform register configuration */ + result = aew_hardware_setup(); + if (!result) { + /* + * Hardware Set up is successful + * Return the no of bytes required for buffer + */ + result = aew_dev_configptr->size_window; + } else { + /* Change Configuration Structure to original */ + *(aew_dev_configptr->config) = aewconfig; + dev_err(aewdev, "Error : AEW_S_PARAM failed\n"); + } + break; + + /* This ioctl is used to return parameters in user space */ + case AEW_G_PARAM: + if (aew_dev_configptr->aew_config == H3A_AEW_CONFIG) { + memcpy((struct aew_configuration *)arg, + aew_dev_configptr->config, + sizeof(struct aew_configuration)); + result = aew_dev_configptr->size_window; + } else { + dev_err(aewdev, + "Error : AEW Hardware is not configured.\n"); + result = -EINVAL; + } + break; + case AEW_GET_STAT: + /* Implement the read functionality */ + if (aew_dev_configptr->buffer_filled != 1) + return -EINVAL; + + if (stat_data->buf_length < aew_dev_configptr->size_window) + return -EINVAL; + + /* Disable the interrupts and then swap the buffers */ + disable_irq(6); + + /* New Statistics are availaible */ + aew_dev_configptr->buffer_filled = 0; + + /* Swap application buffer and old buffer */ + buffer_temp = aew_dev_configptr->buff_old; + aew_dev_configptr->buff_old = aew_dev_configptr->buff_app; + aew_dev_configptr->buff_app = buffer_temp; + + /* Interrupts are enabled */ + enable_irq(6); + + /* + * Copy the entire statistics located in application + * buffer to user space + */ + memcpy(stat_data->buffer, aew_dev_configptr->buff_app, + aew_dev_configptr->size_window); + + result = aew_dev_configptr->size_window; + break; + default: + dev_err(aewdev, "Error: It should not come here!!\n"); + result = -ENOTTY; + break; + } + return result; +} + +/* This function will handle interrupt generated by H3A Engine. */ +static irqreturn_t aew_isr(int irq, void *dev_id) +{ + struct v4l2_subdev *sd = dev_id; + /* EN AF Bit */ + unsigned int enaew; + /* Temporary Buffer for Swapping */ + void *buffer_temp; + + /* Get the value of PCR register */ + enaew = aew_get_enable(); + + /* If AEW engine is not enabled, interrupt is not for AEW */ + if (!enaew || !aew_dev_configptr) + return IRQ_RETVAL(IRQ_NONE); + + /* + * Interrupt is generated by AEW, so Service the Interrupt + * Swap current buffer and old buffer + */ + buffer_temp = aew_dev_configptr->buff_curr; + aew_dev_configptr->buff_curr = aew_dev_configptr->buff_old; + aew_dev_configptr->buff_old = buffer_temp; + + /* Set the AEWBUFSTAT REgister to current buffer Address */ + aew_set_address(aewdev, + (unsigned long)(virt_to_phys(aew_dev_configptr->buff_curr))); + /* + * Set buffer filled flag to indicate statistics are available + */ + aew_dev_configptr->buffer_filled = 1; + /* queue the event with v4l2 */ + aew_queue_event(sd); + + return IRQ_RETVAL(IRQ_HANDLED); +} + +int aew_set_stream(struct v4l2_subdev *sd, int enable) +{ + int result; + + if (!enable) { + /* stop capture */ + free_irq(6, sd); + /* Disable AEW Engine */ + aew_engine_setup(aewdev, 0); + return 0; + } + /* start capture */ + /* Enable AEW Engine if Hardware set up is done */ + if (aew_dev_configptr->aew_config == H3A_AEW_CONFIG_NOT_DONE) { + dev_err(aewdev, "Error : AEW Hardware is not configured.\n"); + return -EINVAL; + } + result = request_irq(6, aew_isr, IRQF_SHARED, "dm365_h3a_aew", + (void *)sd); + if (result != 0) + return result; + /* Enable AF Engine */ + aew_engine_setup(aewdev, 1); + + return 0; +} + +int aew_init(struct platform_device *pdev) +{ + aew_dev_configptr = kmalloc(sizeof(struct aew_device), GFP_KERNEL); + if (!aew_dev_configptr) { + printk(KERN_ERR "aew_init: Error : kmalloc fail\n"); + return -ENOMEM; + } + /* Initialize device structure */ + memset(aew_dev_configptr, 0, sizeof(struct aew_device)); + aew_dev_configptr->in_use = AEW_NOT_IN_USE; + aew_dev_configptr->buffer_filled = 0; + aewdev = &pdev->dev; + + return 0; +} + +void aew_cleanup(void) +{ + /* in use */ + if (aew_dev_configptr->in_use == AEW_IN_USE) { + printk(KERN_ERR "aew_cleanup: Error : dm365_aew in use"); + return; + } + /* Free device structure */ + kfree(aew_dev_configptr); + aew_dev_configptr = NULL; +} diff --git a/drivers/media/video/davinci/dm365_aew.h b/drivers/media/video/davinci/dm365_aew.h new file mode 100644 index 0000000..47bfda5 --- /dev/null +++ b/drivers/media/video/davinci/dm365_aew.h @@ -0,0 +1,55 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef DM365_AEW_DRIVER_H +#define DM365_AEW_DRIVER_H + +#include +#include +#include +#include +#include + +/* Contains information about device structure of AEW*/ +struct aew_device { + /* Driver usage flag */ + enum aew_in_use_flag in_use; + /* Device configuration */ + struct aew_configuration *config; + /* Contains latest statistics */ + void *buff_old; + /* Buffer in which HW will fill the statistics or HW is already + * filling + */ + void *buff_curr; + /* statistics Buffer which will be passed */ + void *buff_app; + /* to user on read call. Flag indicates statistics are available */ + int buffer_filled; + /* Window size in bytes */ + unsigned int size_window; + /* Wait queue for the driver */ + wait_queue_head_t aew_wait_queue; + /* Mutex for driver */ + struct mutex read_blocked; + /* Flag indicates Engine is configured */ + enum aew_config_flag aew_config; +}; + +int aew_validate_parameters(void); +int aew_hardware_setup(void); + +#endif /*End of DM365_AEW_H */ diff --git a/include/linux/dm365_aew.h b/include/linux/dm365_aew.h new file mode 100644 index 0000000..3882d79 --- /dev/null +++ b/include/linux/dm365_aew.h @@ -0,0 +1,153 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef _DM365_AEW_INCLUDE_H +#define _DM365_AEW_INCLUDE_H + +/* Driver Range Constants */ +#define AEW_WINDOW_VERTICAL_COUNT_MIN 1 +#define AEW_WINDOW_VERTICAL_COUNT_MAX 128 +#define AEW_WINDOW_HORIZONTAL_COUNT_MIN 2 +#define AEW_WINDOW_HORIZONTAL_COUNT_MAX 36 + +#define AEW_WIDTH_MIN 8 +#define AEW_WIDTH_MAX 256 + +#define AEW_AVELMT_MAX 1023 + +#define AEW_HZ_LINEINCR_MIN 2 +#define AEW_HZ_LINEINCR_MAX 32 + +#define AEW_VT_LINEINCR_MIN 2 +#define AEW_VT_LINEINCR_MAX 32 + +#define AEW_HEIGHT_MIN 2 +#define AEW_HEIGHT_MAX 256 + +#define AEW_HZSTART_MIN 0 +#define AEW_HZSTART_MAX 4095 + +#define AEW_VTSTART_MIN 0 +#define AEW_VTSTART_MAX 4095 + +#define AEW_BLKWINHEIGHT_MIN 2 +#define AEW_BLKWINHEIGHT_MAX 256 +#define AEW_BLKWINVTSTART_MIN 0 +#define AEW_BLKWINVTSTART_MAX 4095 + +#define AEW_SUMSHIFT_MAX 15 + +/* Statistics data size per window */ +#define AEW_WINDOW_SIZE 32 +#define AEW_WINDOW_SIZE_SUM_ONLY 16 + +/* List of ioctls */ +#define AEW_MAGIC_NO 'e' +#define AEW_S_PARAM _IOWR(AEW_MAGIC_NO, 1, struct aew_configuration) +#define AEW_G_PARAM _IOWR(AEW_MAGIC_NO, 2, struct aew_configuration) +#define AEW_GET_STAT _IOWR(AEW_MAGIC_NO, 5, struct aew_statdata) + +/* Enum for device usage */ +enum aew_in_use_flag { + /* Device is not in use */ + AEW_NOT_IN_USE, + /* Device in use */ + AEW_IN_USE +}; + +/* Enum for Enable/Disable specific feature */ +enum aew_enable_flag { + H3A_AEW_DISABLE, + H3A_AEW_ENABLE +}; + +enum aew_config_flag { + H3A_AEW_CONFIG_NOT_DONE, + H3A_AEW_CONFIG +}; + + +/* Contains the information regarding Window Structure in AEW Engine */ +struct aew_window { + /* Width of the window */ + unsigned int width; + /* Height of the window */ + unsigned int height; + /* Horizontal Start of the window */ + unsigned int hz_start; + /* Vertical Start of the window */ + unsigned int vt_start; + /* Horizontal Count */ + unsigned int hz_cnt; + /* Vertical Count */ + unsigned int vt_cnt; + /* Horizontal Line Increment */ + unsigned int hz_line_incr; + /* Vertical Line Increment */ + unsigned int vt_line_incr; +}; + +/* Contains the information regarding the AEW Black Window Structure */ +struct aew_black_window { + /* Height of the Black Window */ + unsigned int height; + /* Vertical Start of the black Window */ + unsigned int vt_start; +}; + +/* Contains the information regarding the Horizontal Median Filter */ +struct aew_hmf { + /* Status of Horizontal Median Filter */ + enum aew_enable_flag enable; + /* Threshhold Value for Horizontal Median Filter. Make sure + * to keep this same as AF threshold since we have a common + * threshold for both + */ + unsigned int threshold; +}; + +/* AE/AWB output format */ +enum aew_output_format { + AEW_OUT_SUM_OF_SQUARES, + AEW_OUT_MIN_MAX, + AEW_OUT_SUM_ONLY +}; + +/* Contains configuration required for setup of AEW engine */ +struct aew_configuration { + /* A-law status */ + enum aew_enable_flag alaw_enable; + /* AE/AWB output format */ + enum aew_output_format out_format; + /* AW/AWB right shift value for sum of pixels */ + char sum_shift; + /* Saturation Limit */ + int saturation_limit; + /* HMF configurations */ + struct aew_hmf hmf_config; + /* Window for AEW Engine */ + struct aew_window window_config; + /* Black Window */ + struct aew_black_window blackwindow_config; +}; + +struct aew_statdata { + void *buffer; + int buf_length; +}; + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:44:41 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 16:14:41 +0530 Subject: [RESEND RFC PATCH v4 15/15] davinci: vpfe: build infrastructure for dm365 In-Reply-To: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> References: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321526681-22574-16-git-send-email-manjunath.hadli@ti.com> add build infrastructure for dm365 specific modules such as IPIPE, AEW, AF. Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/Kconfig | 46 ++++++++++++++++++++++++++++++++- drivers/media/video/davinci/Makefile | 17 +++++++++++- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig index 6b19540..6f6da53 100644 --- a/drivers/media/video/davinci/Kconfig +++ b/drivers/media/video/davinci/Kconfig @@ -11,6 +11,48 @@ config DISPLAY_DAVINCI_DM646X_EVM To compile this driver as a module, choose M here: the module will be called vpif_display. +config VIDEO_DM365_3A_HW + tristate "DM365 Auto Focus, Auto Exposure/ White Balance HW module" + depends on ARCH_DAVINCI_DM365 + help + DM365 Auto Focus, Auto Exposure and Auto White Balancing HW module + + This module has functions which configure AEW/AF hardware, high level + AF module and AEW module use these functionalities. It collects metrics + about the image or video data + +config VIDEO_DM365_AF + tristate "DM365 Auto Focus Driver" + depends on ARCH_DAVINCI_DM365 + select VIDEO_DM365_3A_HW + help + DM365 Auto Focus hardware module. + + Auto Focus driver is used to support control loop for Auto Focus. + It collects metrics about the image or video data. This provides + hooks to AF subdevice driver. + +config VIDEO_DM365_AEW + tristate "DM365 Auto exposure /White Balance Driver" + depends on ARCH_DAVINCI_DM365 + select VIDEO_DM365_3A_HW + help + DM365 Auto Exposure and Auto White Balance hardware module. + + This is used to support the control loops for Auto Exposure + and Auto White Balance. It collects metrics about the image + or video data + +config DM365_IPIPE + depends on ARCH_DAVINCI && ARCH_DAVINCI_DM365 + tristate "DM365 IPIPE" + help + dm365 IPIPE hardware module. + + This is the hardware module that implements imp_hw_interface + for DM365. This hardware module provides previewer and resizer + functionality for image processing. + config CAPTURE_DAVINCI_DM646X_EVM tristate "DM646x EVM Video Capture" depends on VIDEO_DEV && MACH_DAVINCI_DM6467_EVM @@ -51,7 +93,7 @@ config VIDEO_VPFE_CAPTURE config VIDEO_DM6446_CCDC tristate "DM6446 CCDC HW module" - depends on VIDEO_VPFE_CAPTURE + depends on VIDEO_VPFE_CAPTURE && ARCH_DAVINCI_DM644x select VIDEO_VPSS_SYSTEM default y help @@ -80,7 +122,7 @@ config VIDEO_DM355_CCDC module will be called vpfe. config VIDEO_ISIF - tristate "ISIF HW module" + tristate "DM365 ISIF HW module" depends on ARCH_DAVINCI_DM365 && VIDEO_VPFE_CAPTURE select VIDEO_VPSS_SYSTEM default y diff --git a/drivers/media/video/davinci/Makefile b/drivers/media/video/davinci/Makefile index a379557..8544040 100644 --- a/drivers/media/video/davinci/Makefile +++ b/drivers/media/video/davinci/Makefile @@ -12,7 +12,20 @@ obj-$(CONFIG_CAPTURE_DAVINCI_DM646X_EVM) += vpif_capture.o # Capture: DM6446 and DM355 obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o -obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o +obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o vpfe_ccdc.o \ + vpfe_resizer.o vpfe_previewer.o \ + vpfe_aew.o vpfe_af.o vpfe_video.o obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o -obj-$(CONFIG_VIDEO_ISIF) += isif.o +obj-$(CONFIG_VIDEO_ISIF) += dm365_ccdc.o + +dm365_a3_hw_driver-objs := dm365_a3_hw.o +obj-$(CONFIG_VIDEO_DM365_3A_HW) += dm365_a3_hw_driver.o +dm365_af_driver-objs := dm365_af.o +obj-$(CONFIG_VIDEO_DM365_AF) += dm365_af_driver.o +dm365_aew_driver-objs := dm365_aew.o +obj-$(CONFIG_VIDEO_DM365_AEW) += dm365_aew_driver.o + +dm365_imp-objs := dm365_ipipe.o dm365_def_para.o \ + dm365_ipipe_hw.o dm3xx_ipipeif.o +obj-$(CONFIG_DM365_IPIPE) += dm365_imp.o -- 1.6.2.4 From manjunath.hadli at ti.com Thu Nov 17 04:44:29 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 17 Nov 2011 16:14:29 +0530 Subject: [RESEND RFC PATCH v4 03/15] davinci: vpfe: add IPIPE support for media controller driver In-Reply-To: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> References: <1321526681-22574-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1321526681-22574-4-git-send-email-manjunath.hadli@ti.com> Add the IPIPE interfacing layer to the vpfe driver. This patch adds dm365 specific implementation of the genric imp_hw_interface interface for programming the IPIPE block, mainly setting the resizer and previewer configuration parameters. This is built as an independent module. Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/dm365_def_para.c | 310 +++ drivers/media/video/davinci/dm365_def_para.h | 39 + drivers/media/video/davinci/dm365_ipipe.c | 3844 ++++++++++++++++++++++++++ drivers/media/video/davinci/dm365_ipipe.h | 378 +++ drivers/media/video/davinci/imp_common.h | 86 + drivers/media/video/davinci/imp_hw_if.h | 171 ++ include/linux/dm365_ipipe.h | 1029 +++++++ include/linux/imp_common.h | 171 ++ 8 files changed, 6028 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/dm365_def_para.c create mode 100644 drivers/media/video/davinci/dm365_def_para.h create mode 100644 drivers/media/video/davinci/dm365_ipipe.c create mode 100644 drivers/media/video/davinci/dm365_ipipe.h create mode 100644 drivers/media/video/davinci/imp_common.h create mode 100644 drivers/media/video/davinci/imp_hw_if.h create mode 100644 include/linux/dm365_ipipe.h create mode 100644 include/linux/imp_common.h diff --git a/drivers/media/video/davinci/dm365_def_para.c b/drivers/media/video/davinci/dm365_def_para.c new file mode 100644 index 0000000..3ba9785 --- /dev/null +++ b/drivers/media/video/davinci/dm365_def_para.c @@ -0,0 +1,310 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include "dm365_ipipe.h" + +/* Defaults for lutdpc */ +struct prev_lutdpc dm365_lutdpc_defaults; + +/* Defaults for otfdpc */ +struct prev_lutdpc dm365_otfdpc_defaults; + +/* Defaults for 2D - nf */ +struct prev_nf dm365_nf_defaults; + +/* defaults for GIC */ +struct prev_gic dm365_gic_defaults; + +/* Defaults for white balance */ +struct prev_wb dm365_wb_defaults = { + .gain_r = {2, 0x00}, + .gain_gr = {2, 0x00}, + .gain_gb = {2, 0x00}, + .gain_b = {2, 0x00} +}; + +/* Defaults for CFA */ +struct prev_cfa dm365_cfa_defaults = { + .alg = IPIPE_CFA_ALG_2DIRAC, +}; + +/* Defaults for rgb2rgb */ +struct prev_rgb2rgb dm365_rgb2rgb_defaults = { + .coef_rr = {1, 0}, /* 256 */ + .coef_gr = {0, 0}, + .coef_br = {0, 0}, + .coef_rg = {0, 0}, + .coef_gg = {1, 0}, /* 256 */ + .coef_bg = {0, 0}, + .coef_rb = {0, 0}, + .coef_gb = {0, 0}, + .coef_bb = {1, 0}, /* 256 */ +}; + +/* Defaults for gamma correction */ +struct prev_gamma dm365_gamma_defaults = { + .tbl_sel = IPIPE_GAMMA_TBL_ROM +}; + +/* Defaults for 3d lut */ +struct prev_3d_lut dm365_3d_lut_defaults; + +/* Defaults for lumina adjustments */ +struct prev_lum_adj dm365_lum_adj_defaults = { + .contrast = 16 +}; + +/* Defaults for rgb2yuv conversion */ +struct prev_rgb2yuv dm365_rgb2yuv_defaults = { + .coef_ry = {0, 0x4d}, + .coef_gy = {0, 0x96}, + .coef_by = {0, 0x1d}, + .coef_rcb = {0xf, 0xd5}, + .coef_gcb = {0xf, 0xab}, + .coef_bcb = {0, 0x80}, + .coef_rcr = {0, 0x80}, + .coef_gcr = {0xf, 0x95}, + .coef_bcr = {0xf, 0xeb}, + .out_ofst_cb = 0x80, + .out_ofst_cr = 0x80 +}; + +/* Defaults for GBCE */ +struct prev_gbce dm365_gbce_defaults; + +/* Defaults for yuv 422 conversion */ +struct prev_yuv422_conv dm365_yuv422_conv_defaults = { + .chrom_pos = IPIPE_YUV422_CHR_POS_COSITE +}; + +/* Defaults for Edge Ehnancements */ +struct prev_yee dm365_yee_defaults; + +/* Defaults for CAR conversion */ +struct prev_car dm365_car_defaults; + +/* Defaults for CGS */ +struct prev_cgs dm365_cgs_defaults; + +#define WIDTH_I 640 +#define HEIGHT_I 480 +#define WIDTH_O 640 +#define HEIGHT_O 480 + +/* default ipipeif settings */ +struct ipipeif_5_1 ipipeif_5_1_defaults = { + .pack_mode = IPIPEIF_5_1_PACK_16_BIT, + .data_shift = IPIPEIF_BITS11_0, + .source1 = IPIPEIF_SRC1_PARALLEL_PORT, + .clk_div = { + .m = 1, /* clock = sdram clock * (m/n) */ + .n = 6 + }, + .dpcm = { + .type = IPIPEIF_DPCM_8BIT_12BIT, + .pred = IPIPEIF_DPCM_SIMPLE_PRED + }, + .pix_order = IPIPEIF_CBCR_Y, + .isif_port = { + .if_type = V4L2_MBUS_FMT_SBGGR10_1X10, + .hdpol = VPFE_PINPOL_POSITIVE, + .vdpol = VPFE_PINPOL_POSITIVE + }, + .clip = 4095, +}; + +struct ipipe_params dm365_ipipe_defs = { + .ipipeif_param = { + .mode = IPIPEIF_ONE_SHOT, + .source = IPIPEIF_SDRAM_RAW, + .clock_select = IPIPEIF_SDRAM_CLK, + .glob_hor_size = WIDTH_I + 8, + .glob_ver_size = HEIGHT_I + 10, + .hnum = WIDTH_I, + .vnum = HEIGHT_I, + .adofs = WIDTH_I * 2, + .rsz = 16, /* resize ratio 16/rsz */ + .decimation = IPIPEIF_DECIMATION_OFF, + .avg_filter = IPIPEIF_AVG_OFF, + .gain = 0x200, /* U10Q9 */ + }, + .ipipe_mode = IPIPEIF_ONE_SHOT, + .ipipe_dpaths_fmt = IPIPE_RAW2YUV, + .ipipe_colpat_olop = IPIPE_GREEN_BLUE, + .ipipe_colpat_olep = IPIPE_BLUE, + .ipipe_colpat_elop = IPIPE_RED, + .ipipe_colpat_elep = IPIPE_GREEN_RED, + .ipipe_vsz = HEIGHT_I - 1, + .ipipe_hsz = WIDTH_I - 1, + .rsz_common = { + .vsz = HEIGHT_I - 1, + .hsz = WIDTH_I - 1, + .src_img_fmt = RSZ_IMG_422, + .raw_flip = 1, /* flip preserve Raw format */ + .source = IPIPE_DATA, + .passthrough = IPIPE_BYPASS_OFF, + .yuv_y_max = 255, + .yuv_c_max = 255, + .rsz_seq_crv = DISABLE, + .out_chr_pos = IPIPE_YUV422_CHR_POS_COSITE + }, + .rsz_rsc_param = { + { + .mode = IPIPEIF_ONE_SHOT, + .h_flip = DISABLE, + .v_flip = DISABLE, + .cen = DISABLE, + .yen = DISABLE, + .o_vsz = HEIGHT_O - 1, + .o_hsz = WIDTH_O - 1, + .v_dif = 256, + .v_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dif = 256, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + }, + { + .mode = IPIPEIF_ONE_SHOT, + .h_flip = DISABLE, + .v_flip = DISABLE, + .cen = DISABLE, + .yen = DISABLE, + .o_vsz = HEIGHT_O - 1, + .o_hsz = WIDTH_O - 1, + .v_dif = 256, + .v_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dif = 256, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + }, + }, + .rsz2rgb = { + { + .rgb_en = DISABLE + }, + { + .rgb_en = DISABLE + } + }, + .ext_mem_param = { + { + .rsz_sdr_oft_y = WIDTH_O << 1, + .rsz_sdr_ptr_e_y = HEIGHT_O, + .rsz_sdr_oft_c = WIDTH_O, + .rsz_sdr_ptr_e_c = HEIGHT_O >> 1, + }, + { + .rsz_sdr_oft_y = WIDTH_O << 1, + .rsz_sdr_ptr_e_y = HEIGHT_O, + .rsz_sdr_oft_c = WIDTH_O, + .rsz_sdr_ptr_e_c = HEIGHT_O, + }, + }, + .rsz_en[0] = ENABLE, + .rsz_en[1] = DISABLE +}; + +struct prev_single_shot_config dm365_prev_ss_config_defs = { + .bypass = IPIPE_BYPASS_OFF, + .input = { + .ppln = WIDTH_I + 8, + .lpfr = HEIGHT_I + 10, + .pred = IPIPEIF_DPCM_SIMPLE_PRED, + .clk_div = {1, 6}, + .data_shift = IPIPEIF_BITS11_0, + .rsz = 16, /* resize ratio 16/rsz */ + .avg_filter_en = IPIPEIF_AVG_OFF, + .dpc = {0, 0}, + .gain = 512, + .clip = 4095, + .colp_olop = IPIPE_GREEN_BLUE, + .colp_olep = IPIPE_BLUE, + .colp_elop = IPIPE_RED, + .colp_elep = IPIPE_GREEN_RED + }, +}; + +struct prev_continuous_config dm365_prev_cont_config_defs = { + .bypass = IPIPE_BYPASS_OFF, + .input = { + .rsz = 16, + .avg_filter_en = IPIPEIF_AVG_OFF, + .gain = 512, + .clip = 4095, + .colp_olop = IPIPE_GREEN_BLUE, + .colp_olep = IPIPE_BLUE, + .colp_elop = IPIPE_RED, + .colp_elep = IPIPE_GREEN_RED + }, +}; + +struct rsz_single_shot_config dm365_rsz_ss_config_defs = { + .input = { + .ppln = WIDTH_I + 8, + .lpfr = HEIGHT_I + 10, + .clk_div = {1, 6}, + .rsz = 16, /* resize ratio 16/rsz */ + .avg_filter_en = IPIPEIF_AVG_OFF, + }, + .output1 = { + .v_typ_y = RSZ_INTP_CUBIC, + .v_typ_c = RSZ_INTP_CUBIC, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + }, + .output2 = { + .v_typ_y = RSZ_INTP_CUBIC, + .v_typ_c = RSZ_INTP_CUBIC, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + }, + .yuv_y_max = 255, + .yuv_c_max = 255, + .out_chr_pos = IPIPE_YUV422_CHR_POS_COSITE, +}; + +struct rsz_continuous_config dm365_rsz_cont_config_defs = { + .output1 = { + .v_typ_y = RSZ_INTP_CUBIC, + .v_typ_c = RSZ_INTP_CUBIC, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + }, + .output2 = { + .v_typ_y = RSZ_INTP_CUBIC, + .v_typ_c = RSZ_INTP_CUBIC, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + }, + .yuv_y_max = 255, + .yuv_c_max = 255, + .out_chr_pos = IPIPE_YUV422_CHR_POS_COSITE, +}; diff --git a/drivers/media/video/davinci/dm365_def_para.h b/drivers/media/video/davinci/dm365_def_para.h new file mode 100644 index 0000000..872b3cb --- /dev/null +++ b/drivers/media/video/davinci/dm365_def_para.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2011 Texas Instruments Inc + * + * 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 version 2. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "dm365_ipipe.h" +extern struct prev_lutdpc dm365_lutdpc_defaults; +extern struct prev_otfdpc dm365_otfdpc_defaults; +extern struct prev_nf dm365_nf_defaults; +extern struct prev_gic dm365_gic_defaults; +extern struct prev_wb dm365_wb_defaults; +extern struct prev_cfa dm365_cfa_defaults; +extern struct prev_rgb2rgb dm365_rgb2rgb_defaults; +extern struct prev_gamma dm365_gamma_defaults; +extern struct prev_3d_lut dm365_3d_lut_defaults; +extern struct prev_lum_adj dm365_lum_adj_defaults; +extern struct prev_rgb2yuv dm365_rgb2yuv_defaults; +extern struct prev_yuv422_conv dm365_yuv422_conv_defaults; +extern struct prev_gbce dm365_gbce_defaults; +extern struct prev_yee dm365_yee_defaults; +extern struct prev_car dm365_car_defaults; +extern struct prev_cgs dm365_cgs_defaults; +extern struct ipipe_params dm365_ipipe_defs; +extern struct prev_single_shot_config dm365_prev_ss_config_defs; +extern struct prev_continuous_config dm365_prev_cont_config_defs; +extern struct rsz_single_shot_config dm365_rsz_ss_config_defs; +extern struct rsz_continuous_config dm365_rsz_cont_config_defs; +extern struct ipipeif_5_1 ipipeif_5_1_defaults; diff --git a/drivers/media/video/davinci/dm365_ipipe.c b/drivers/media/video/davinci/dm365_ipipe.c new file mode 100644 index 0000000..00ef550 --- /dev/null +++ b/drivers/media/video/davinci/dm365_ipipe.c @@ -0,0 +1,3844 @@ +/* +* Copyright (C) 2011 Texas Instruments Inc +* +* 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 version 2. +* +* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dm365_ipipe.h" +#include "imp_hw_if.h" +#include "dm365_ipipe_hw.h" +#include "dm365_def_para.h" + +static int ipipe_enum_pix(void *ipipe, u32 *pix, int i) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + if (i >= ARRAY_SIZE(oper_state->ipipe_raw_yuv_pix_formats)) + return -EINVAL; + + *pix = oper_state->ipipe_raw_yuv_pix_formats[i]; + return 0; +} + +/* IPIPE hardware limits */ +#define IPIPE_MAX_OUTPUT_WIDTH_A 2176 +#define IPIPE_MAX_OUTPUT_WIDTH_B 640 + +static int ipipe_get_max_output_width(int rsz) +{ + if (rsz == RSZ_A) + return IPIPE_MAX_OUTPUT_WIDTH_A; + return IPIPE_MAX_OUTPUT_WIDTH_B; +} + +/* Based on max resolution supported. QXGA */ +#define IPIPE_MAX_OUTPUT_HEIGHT_A 1536 +/* Based on max resolution supported. VGA */ +#define IPIPE_MAX_OUTPUT_HEIGHT_B 480 + +static int ipipe_get_max_output_height(int rsz) +{ + if (rsz == RSZ_A) + return IPIPE_MAX_OUTPUT_HEIGHT_A; + return IPIPE_MAX_OUTPUT_HEIGHT_B; +} + +static int ipipe_serialize(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return oper_state->en_serializer; +} + +static int ipipe_set_ipipe_if_address(void *config, unsigned int address) +{ + struct ipipeif *if_params; + + if (!config) + return -EINVAL; + + if_params = &((struct ipipe_params *)config)->ipipeif_param; + + return ipipeif_set_address(if_params, address); +} + +static void ipipe_lock_chain(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + mutex_lock(&oper_state->lock); + oper_state->resource_in_use = 1; + mutex_unlock(&oper_state->lock); +} + +static void ipipe_unlock_chain(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + mutex_lock(&oper_state->lock); + oper_state->resource_in_use = 0; + oper_state->prev_config_state = STATE_NOT_CONFIGURED; + oper_state->rsz_config_state = STATE_NOT_CONFIGURED; + oper_state->rsz_chained = 0; + mutex_unlock(&oper_state->lock); +} +static int ipipe_process_pix_fmts(enum v4l2_mbus_pixelcode in_pix_fmt, + enum v4l2_mbus_pixelcode out_pix_fmt, + struct ipipe_params *param) +{ + enum v4l2_mbus_pixelcode temp_pix_fmt; + + switch (in_pix_fmt) { + case V4L2_MBUS_FMT_SBGGR8_1X8: + temp_pix_fmt = V4L2_MBUS_FMT_SBGGR10_1X10; + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_8_BIT; + break; + case V4L2_MBUS_FMT_SGRBG10_ALAW8_1X8: + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_8_BIT_A_LAW; + temp_pix_fmt = V4L2_MBUS_FMT_SBGGR10_1X10; + break; + case V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8: + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_8_BIT; + param->ipipeif_param.var.if_5_1.dpcm.en = 1; + temp_pix_fmt = V4L2_MBUS_FMT_SBGGR10_1X10; + break; + case V4L2_MBUS_FMT_SBGGR10_1X10: + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_16_BIT; + temp_pix_fmt = V4L2_MBUS_FMT_SBGGR10_1X10; + break; + case V4L2_MBUS_FMT_SBGGR12_1X12: + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_12_BIT; + temp_pix_fmt = V4L2_MBUS_FMT_SBGGR10_1X10; + break; + case V4L2_MBUS_FMT_Y8_1X8: + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_8_BIT; + temp_pix_fmt = V4L2_MBUS_FMT_UYVY8_2X8; + break; + case V4L2_MBUS_FMT_UV8_1X8: + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_8_BIT; + temp_pix_fmt = V4L2_MBUS_FMT_UYVY8_2X8; + break; + default: + temp_pix_fmt = V4L2_MBUS_FMT_UYVY8_2X8; + } + + if (temp_pix_fmt == V4L2_MBUS_FMT_SBGGR10_1X10) { + if (out_pix_fmt == V4L2_MBUS_FMT_SBGGR10_1X10) + param->ipipe_dpaths_fmt = IPIPE_RAW2RAW; + else if (out_pix_fmt == V4L2_MBUS_FMT_UYVY8_2X8 || + out_pix_fmt == V4L2_MBUS_FMT_NV12_1X20) + param->ipipe_dpaths_fmt = IPIPE_RAW2YUV; + else + return -EINVAL; + } else if (temp_pix_fmt == V4L2_MBUS_FMT_UYVY8_2X8) { + switch (out_pix_fmt) { + case V4L2_MBUS_FMT_UYVY8_2X8: + case V4L2_MBUS_FMT_NV12_1X20: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_UV8_1X8: + param->ipipe_dpaths_fmt = IPIPE_YUV2YUV; + break; + default: + return -EINVAL; + } + } + return 0; +} + +/* + * calculate_resize_ratios() + * calculates resize ratio for resizer A or B. This is called after setting + * the input size or output size + */ +static void calculate_resize_ratios(struct ipipe_params *param, int index) +{ + param->rsz_rsc_param[index].h_dif = ((param->ipipe_hsz + 1) * 256) / + (param->rsz_rsc_param[index].o_hsz + 1); + param->rsz_rsc_param[index].v_dif = ((param->ipipe_vsz + 1) * 256) / + (param->rsz_rsc_param[index].o_vsz + 1); +} + +static int ipipe_do_hw_setup(struct device *dev, void *ipipe, void *config) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct ipipe_params *param = (struct ipipe_params *)config; + int ret; + + dev_dbg(dev, "ipipe_do_hw_setup\n"); + ret = mutex_lock_interruptible(&oper_state->lock); + if (ret) + return ret; + + if (!config && oper_state->oper_mode == IMP_MODE_CONTINUOUS) { + /* continuous mode */ + param = oper_state->shared_config_param; + if (param->rsz_en[RSZ_A]) + calculate_resize_ratios(param, RSZ_A); + if (param->rsz_en[RSZ_B]) + calculate_resize_ratios(param, RSZ_B); + ret = ipipe_hw_setup(param); + } + mutex_unlock(&oper_state->lock); + + return ret; +} + +static unsigned int ipipe_rsz_chain_state(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return oper_state->rsz_chained; +} + +static void ipipe_update_outbuf1_address(void *ipipe, void *config, + unsigned int address) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + if (!config && oper_state->oper_mode == IMP_MODE_CONTINUOUS) + return rsz_set_output_address(oper_state->shared_config_param, + 0, address); + + rsz_set_output_address((struct ipipe_params *)config, 0, address); +} + +static void ipipe_update_outbuf2_address(void *ipipe, void *config, + unsigned int address) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + if (!config && oper_state->oper_mode == IMP_MODE_CONTINUOUS) + return rsz_set_output_address(oper_state->shared_config_param, + 1, address); + + rsz_set_output_address((struct ipipe_params *)config, 1, address); +} + +static void ipipe_enable(void *ipipe, unsigned char en, void *config) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct ipipe_params *param = (struct ipipe_params *)config; + unsigned char ret; + + if (en) + en = !!en; + + if (oper_state->oper_mode == IMP_MODE_CONTINUOUS) + param = oper_state->shared_config_param; + + if (oper_state->oper_mode == IMP_MODE_SINGLE_SHOT && en) { + /* for single-shot mode, need to wait for h/w to + reset many register bits */ + + if (param->rsz_common.source == IPIPE_DATA) { + do { + ret = regr_ip(IPIPE_SRC_EN); + } while (ret); + } + + do { + ret = regr_rsz(RSZ_SRC_EN); + } while (ret); + + if (param->rsz_en[RSZ_A]) { + do { + ret = regr_rsz(RSZ_A); + } while (ret); + } + + if (en && param->rsz_en[RSZ_B]) { + do { + ret = regr_rsz(RSZ_B); + } while (ret); + } + + do { + ret = ipipeif_get_enable(); + } while (ret & 0x1); + } + + if (param->rsz_common.source == IPIPE_DATA) + regw_ip(en, IPIPE_SRC_EN); + + if (param->rsz_en[RSZ_A]) + rsz_enable(RSZ_A, en); + + if (param->rsz_en[RSZ_B]) + rsz_enable(RSZ_B, en); + + if (oper_state->oper_mode == IMP_MODE_SINGLE_SHOT) + ipipeif_set_enable(en, oper_state->oper_mode); +} + +static int validate_lutdpc_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + int i; + + if (oper_state->lutdpc.en > 1 || + oper_state->lutdpc.repl_white > 1 || + oper_state->lutdpc.dpc_size > LUT_DPC_MAX_SIZE) + return -EINVAL; + + if (oper_state->lutdpc.en && !oper_state->lutdpc.table) + return -EINVAL; + + for (i = 0; i < oper_state->lutdpc.dpc_size; i++) { + if (oper_state->lutdpc.table[i].horz_pos > LUT_DPC_H_POS_MASK || + oper_state->lutdpc.table[i].vert_pos > LUT_DPC_V_POS_MASK) + return -EINVAL; + } + return 0; +} + +static int set_lutdpc_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct ipipe_lutdpc_entry *temp_lutdpc; + struct prev_lutdpc dpc_param; + + if (!param) { + /* Copy defaults for dfc */ + temp_lutdpc = oper_state->lutdpc.table; + memcpy((void *)&oper_state->lutdpc, + (void *)&dm365_lutdpc_defaults, + sizeof(struct prev_lutdpc)); + oper_state->lutdpc.table = temp_lutdpc; + goto success; + } + + if (len != sizeof(struct prev_lutdpc)) { + dev_err(dev, "set_lutdpc_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + + if (copy_from_user(&dpc_param, (struct prev_lutdpc *)param, + sizeof(struct prev_lutdpc))) { + dev_err(dev, "set_lutdpc_params: Error in copy to kernel\n"); + return -EFAULT; + } + + if (!dpc_param.table) { + dev_err(dev, "Invalid user dpc table ptr\n"); + return -EINVAL; + } + + oper_state->lutdpc.en = dpc_param.en; + oper_state->lutdpc.repl_white = dpc_param.repl_white; + oper_state->lutdpc.dpc_size = dpc_param.dpc_size; + + if (copy_from_user(oper_state->lutdpc.table, (struct ipipe_dpc_entry *) + dpc_param.table, (oper_state->lutdpc.dpc_size * + sizeof(struct ipipe_lutdpc_entry)))) { + dev_err(dev, "set_lutdpc_params: Error in copying " + "dfc table to kernel\n"); + return -EFAULT; + } + + if (validate_lutdpc_params(dev, oper_state) < 0) + return -EINVAL; + +success: + ipipe_set_lutdpc_regs(&oper_state->lutdpc); + + return 0; +} + +static int get_lutdpc_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_lutdpc *lut_param = (struct prev_lutdpc *)param; + struct prev_lutdpc user_lutdpc; + + if (!lut_param) { + dev_err(dev, "get_lutdpc_params: invalid user ptr"); + return -EINVAL; + } + + if (len != sizeof(struct prev_lutdpc)) { + dev_err(dev, + "get_lutdpc_params: param struct length mismatch\n"); + return -EINVAL; + } + + if (copy_from_user(&user_lutdpc, lut_param, + sizeof(struct prev_lutdpc))) { + dev_err(dev, "get_lutdpc_params: Error in copy to kernel\n"); + return -EFAULT; + } + + user_lutdpc.en = oper_state->lutdpc.en; + user_lutdpc.repl_white = oper_state->lutdpc.repl_white; + user_lutdpc.dpc_size = oper_state->lutdpc.dpc_size; + if (!user_lutdpc.table) { + dev_err(dev, "get_lutdpc_params:" " Invalid table ptr"); + return -EINVAL; + } + if (copy_to_user(user_lutdpc.table, + oper_state->lutdpc.table, + (oper_state->lutdpc.dpc_size * + sizeof(struct ipipe_lutdpc_entry)))) { + dev_err(dev, + "get_lutdpc_params:Table Error in" " copy to user\n"); + return -EFAULT; + } + + if (copy_to_user(lut_param, &user_lutdpc, sizeof(struct prev_lutdpc))) { + dev_err(dev, "get_lutdpc_params: Error in copy" " to user\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_otfdpc_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + struct prev_otfdpc *dpc_param = + (struct prev_otfdpc *)&oper_state->otfdpc; + struct prev_otfdpc_2_0 *dpc_2_0; + struct prev_otfdpc_3_0 *dpc_3_0; + + if (dpc_param->en > 1) + return -EINVAL; + if (dpc_param->alg == IPIPE_OTFDPC_2_0) { + dpc_2_0 = &dpc_param->alg_cfg.dpc_2_0; + if (dpc_2_0->det_thr.r > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.gr > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.gb > OTFDPC_DPC2_THR_MASK || + dpc_2_0->det_thr.b > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.r > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.gr > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.gb > OTFDPC_DPC2_THR_MASK || + dpc_2_0->corr_thr.b > OTFDPC_DPC2_THR_MASK) + return -EINVAL; + return 0; + } + + dpc_3_0 = &dpc_param->alg_cfg.dpc_3_0; + if (dpc_3_0->act_adj_shf > OTF_DPC3_0_SHF_MASK || + dpc_3_0->det_thr > OTF_DPC3_0_DET_MASK || + dpc_3_0->det_slp > OTF_DPC3_0_SLP_MASK || + dpc_3_0->det_thr_min > OTF_DPC3_0_DET_MASK || + dpc_3_0->det_thr_max > OTF_DPC3_0_DET_MASK || + dpc_3_0->corr_thr > OTF_DPC3_0_CORR_MASK || + dpc_3_0->corr_slp > OTF_DPC3_0_SLP_MASK || + dpc_3_0->corr_thr_min > OTF_DPC3_0_CORR_MASK || + dpc_3_0->corr_thr_max > OTF_DPC3_0_CORR_MASK) + return -EINVAL; + + return 0; +} + +static int set_otfdpc_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_otfdpc *dpc_param = (struct prev_otfdpc *)param; + + if (!param) { + /* Copy defaults for dpc2.0 defaults */ + memcpy((void *)&oper_state->otfdpc, + (void *)&dm365_otfdpc_defaults, + sizeof(struct ipipe_otfdpc_2_0)); + } else { + if (len != sizeof(struct prev_otfdpc)) { + dev_err(dev, "set_otfdpc_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->otfdpc, dpc_param, + sizeof(struct prev_otfdpc))) { + dev_err(dev, "set_otfdpc_params: Error in " + "copy to kernel\n"); + return -EFAULT; + } + + if (validate_otfdpc_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_otfdpc_regs(&oper_state->otfdpc); + + return 0; +} + +static int get_otfdpc_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_otfdpc *dpc_param = (struct prev_otfdpc *)param; + + if (!dpc_param) { + dev_err(dev, "get_otfdpc_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_otfdpc)) { + dev_err(dev, + "get_otfdpc_params: param struct length mismatch\n"); + return -EINVAL; + } + if (copy_to_user(dpc_param, &oper_state->otfdpc, + sizeof(struct prev_otfdpc))) { + dev_err(dev, "get_otfdpc_params: Error in copy dpc " + "table to user\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_nf_params(struct device *dev, + struct ipipe_oper_state *oper_state, + unsigned int id) +{ + struct prev_nf *nf_param = &oper_state->nf1; + int i; + + if (id) + nf_param = &oper_state->nf2; + if (nf_param->en > 1 || + nf_param->shft_val > D2F_SHFT_VAL_MASK || + nf_param->spread_val > D2F_SPR_VAL_MASK || + nf_param->apply_lsc_gain > 1 || + nf_param->edge_det_min_thr > D2F_EDGE_DET_THR_MASK || + nf_param->edge_det_max_thr > D2F_EDGE_DET_THR_MASK) + return -EINVAL; + + for (i = 0; i < IPIPE_NF_THR_TABLE_SIZE; i++) + if (nf_param->thr[i] > D2F_THR_VAL_MASK) + return -EINVAL; + for (i = 0; i < IPIPE_NF_STR_TABLE_SIZE; i++) + if (nf_param->str[i] > D2F_STR_VAL_MASK) + return -EINVAL; + return 0; +} + +static int set_nf_params(struct device *dev, + struct ipipe_oper_state *oper_state, + unsigned int id, void *param, int len) +{ + struct prev_nf *nf_param = (struct prev_nf *)param; + struct prev_nf *nf = &oper_state->nf1; + + if (id) + nf = &oper_state->nf2; + + if (!nf_param) { + /* Copy defaults for nf */ + memcpy((void *)nf, (void *)&dm365_nf_defaults, + sizeof(struct prev_nf)); + memset((void *)nf->thr, 0, IPIPE_NF_THR_TABLE_SIZE); + memset((void *)nf->str, 0, IPIPE_NF_THR_TABLE_SIZE); + } else { + if (len != sizeof(struct prev_nf)) { + dev_err(dev, "set_nf_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + if (copy_from_user(nf, nf_param, sizeof(struct prev_nf))) { + dev_err(dev, + "set_nf_params: Error in copy to kernel\n"); + return -EFAULT; + } + if (validate_nf_params(dev, oper_state, id) < 0) + return -EINVAL; + } + /* Now set the values in the hw */ + ipipe_set_d2f_regs(id, nf); + + return 0; +} + +static int set_nf1_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return set_nf_params(dev, oper_state, 0, param, len); +} + +static int set_nf2_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return set_nf_params(dev, oper_state, 1, param, len); +} + +static int get_nf_params(struct device *dev, + struct ipipe_oper_state *oper_state, + unsigned int id, void *param, int len) +{ + struct prev_nf *nf_param = (struct prev_nf *)param; + struct prev_nf *nf = &oper_state->nf1; + + if (!nf_param) { + dev_err(dev, "get_nf_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_nf)) { + dev_err(dev, "get_nf_params: param struct length mismatch\n"); + return -EINVAL; + } + if (id) + nf = &oper_state->nf2; + if (copy_to_user((struct prev_nf *)nf_param, nf, + sizeof(struct prev_nf))) { + dev_err(dev, "get_nf_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int get_nf1_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return get_nf_params(dev, oper_state, 0, param, len); +} + +static int get_nf2_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return get_nf_params(dev, oper_state, 1, param, len); +} + +static int validate_gic_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + if (oper_state->gic.en > 1 || + oper_state->gic.gain > GIC_GAIN_MASK || + oper_state->gic.thr > GIC_THR_MASK || + oper_state->gic.slope > GIC_SLOPE_MASK || + oper_state->gic.apply_lsc_gain > 1 || + oper_state->gic.nf2_thr_gain.integer > GIC_NFGAN_INT_MASK || + oper_state->gic.nf2_thr_gain.decimal > GIC_NFGAN_DECI_MASK) + return -EINVAL; + return 0; +} + +static int set_gic_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_gic *gic_param = (struct prev_gic *)param; + + if (!gic_param) { + /* Copy defaults for nf */ + memcpy((void *)&oper_state->gic, (void *)&dm365_gic_defaults, + sizeof(struct prev_gic)); + } else { + if (len != sizeof(struct prev_gic)) { + dev_err(dev, "set_gic_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->gic, gic_param, + sizeof(struct prev_gic))) { + dev_err(dev, + "set_gic_params: Error in copy to kernel\n"); + return -EFAULT; + } + if (validate_gic_params(dev, oper_state) < 0) + return -EINVAL; + } + /* Now set the values in the hw */ + ipipe_set_gic_regs(&oper_state->gic); + + return 0; +} + +static int get_gic_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_gic *gic_param = (struct prev_gic *)param; + + if (!gic_param) { + dev_err(dev, "get_gic_params: invalid user ptr"); + return -EINVAL; + } + + if (len != sizeof(struct prev_gic)) { + dev_err(dev, "get_gic_params: param struct length mismatch\n"); + return -EINVAL; + } + + if (copy_to_user((struct prev_gic *)gic_param, &oper_state->gic, + sizeof(struct prev_gic))) { + dev_err(dev, "get_gic_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_wb_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + if (oper_state->wb.ofst_r > WB_OFFSET_MASK || + oper_state->wb.ofst_gr > WB_OFFSET_MASK || + oper_state->wb.ofst_gb > WB_OFFSET_MASK || + oper_state->wb.ofst_b > WB_OFFSET_MASK || + oper_state->wb.gain_r.integer > WB_GAIN_INT_MASK || + oper_state->wb.gain_r.decimal > WB_GAIN_DECI_MASK || + oper_state->wb.gain_gr.integer > WB_GAIN_INT_MASK || + oper_state->wb.gain_gr.decimal > WB_GAIN_DECI_MASK || + oper_state->wb.gain_gb.integer > WB_GAIN_INT_MASK || + oper_state->wb.gain_gb.decimal > WB_GAIN_DECI_MASK || + oper_state->wb.gain_b.integer > WB_GAIN_INT_MASK || + oper_state->wb.gain_b.decimal > WB_GAIN_DECI_MASK) + return -EINVAL; + return 0; +} + +static int set_wb_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_wb *wb_param = (struct prev_wb *)param; + + dev_dbg(dev, "set_wb_params"); + if (!wb_param) { + /* Copy defaults for wb */ + memcpy((void *)&oper_state->wb, (void *)&dm365_wb_defaults, + sizeof(struct prev_wb)); + } else { + if (len != sizeof(struct prev_wb)) { + dev_err(dev, "set_wb_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->wb, wb_param, + sizeof(struct prev_wb))) { + dev_err(dev, + "set_wb_params: Error in copy to kernel\n"); + return -EFAULT; + } + if (validate_wb_params(dev, oper_state) < 0) + return -EINVAL; + } + /* Now set the values in the hw */ + ipipe_set_wb_regs(&oper_state->wb); + + return 0; +} + +static int get_wb_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_wb *wb_param = (struct prev_wb *)param; + + if (!wb_param) { + dev_err(dev, "get_wb_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_wb)) { + dev_err(dev, "get_wb_params: param struct length mismatch\n"); + return -EINVAL; + } + if (copy_to_user((struct prev_wb *)wb_param, &oper_state->wb, + sizeof(struct prev_wb))) { + dev_err(dev, "get_wb_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_cfa_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + if (oper_state->cfa.hpf_thr_2dir > CFA_HPF_THR_2DIR_MASK || + oper_state->cfa.hpf_slp_2dir > CFA_HPF_SLOPE_2DIR_MASK || + oper_state->cfa.hp_mix_thr_2dir > CFA_HPF_MIX_THR_2DIR_MASK || + oper_state->cfa.hp_mix_slope_2dir > CFA_HPF_MIX_SLP_2DIR_MASK || + oper_state->cfa.dir_thr_2dir > CFA_DIR_THR_2DIR_MASK || + oper_state->cfa.dir_slope_2dir > CFA_DIR_SLP_2DIR_MASK || + oper_state->cfa.nd_wt_2dir > CFA_ND_WT_2DIR_MASK || + oper_state->cfa.hue_fract_daa > CFA_DAA_HUE_FRA_MASK || + oper_state->cfa.edge_thr_daa > CFA_DAA_EDG_THR_MASK || + oper_state->cfa.thr_min_daa > CFA_DAA_THR_MIN_MASK || + oper_state->cfa.thr_slope_daa > CFA_DAA_THR_SLP_MASK || + oper_state->cfa.slope_min_daa > CFA_DAA_SLP_MIN_MASK || + oper_state->cfa.slope_slope_daa > CFA_DAA_SLP_SLP_MASK || + oper_state->cfa.lp_wt_daa > CFA_DAA_LP_WT_MASK) + return -EINVAL; + return 0; +} + +static int set_cfa_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_cfa *cfa_param = (struct prev_cfa *)param; + + dev_dbg(dev, "set_cfa_params"); + if (!cfa_param) { + /* Copy defaults for wb */ + memcpy((void *)&oper_state->cfa, (void *)&dm365_cfa_defaults, + sizeof(struct prev_cfa)); + } else { + if (len != sizeof(struct prev_cfa)) { + dev_err(dev, "set_cfa_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->cfa, cfa_param, + sizeof(struct prev_cfa))) { + dev_err(dev, + "set_cfa_params: Error in copy to kernel\n"); + return -EFAULT; + } + if (validate_cfa_params(dev, oper_state) < 0) + return -EINVAL; + } + /* Now set the values in the hw */ + ipipe_set_cfa_regs(&oper_state->cfa); + + return 0; +} + +static int get_cfa_params(struct device *dev, void *ipipe, void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_cfa *cfa_param = (struct prev_cfa *)param; + + dev_dbg(dev, "get_cfa_params\n"); + if (!cfa_param) { + dev_err(dev, "get_cfa_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_cfa)) { + dev_err(dev, "get_cfa_params: param struct length mismatch\n"); + return -EINVAL; + } + if (copy_to_user((struct prev_cfa *)cfa_param, &oper_state->cfa, + sizeof(struct prev_cfa))) { + dev_err(dev, "get_cfa_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_rgb2rgb_params(struct device *dev, + struct ipipe_oper_state *oper_state, + unsigned int id) +{ + struct prev_rgb2rgb *rgb2rgb = &oper_state->rgb2rgb_1; + u32 gain_int_upper = RGB2RGB_1_GAIN_INT_MASK; + u32 offset_upper = RGB2RGB_1_OFST_MASK; + + if (id) { + rgb2rgb = &oper_state->rgb2rgb_2; + offset_upper = RGB2RGB_2_OFST_MASK; + gain_int_upper = RGB2RGB_2_GAIN_INT_MASK; + } + if (rgb2rgb->coef_rr.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rr.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gr.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gr.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_br.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_br.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_rg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_bg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_bg.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_rb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_gb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->coef_bb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_bb.integer > gain_int_upper) + return -EINVAL; + + if (rgb2rgb->out_ofst_r > offset_upper || + rgb2rgb->out_ofst_g > offset_upper || + rgb2rgb->out_ofst_b > offset_upper) + return -EINVAL; + return 0; +} + +static int set_rgb2rgb_params(struct device *dev, + struct ipipe_oper_state *oper_state, + unsigned int id, + void *param, int len) +{ + struct prev_rgb2rgb *rgb2rgb_param = (struct prev_rgb2rgb *)param; + struct prev_rgb2rgb *rgb2rgb = &oper_state->rgb2rgb_1; + + if (id) + rgb2rgb = &oper_state->rgb2rgb_2; + if (!rgb2rgb_param) { + /* Copy defaults for rgb2rgb conversion */ + memcpy((void *)rgb2rgb, (void *)&dm365_rgb2rgb_defaults, + sizeof(struct prev_rgb2rgb)); + } else { + + if (len != sizeof(struct prev_rgb2rgb)) { + dev_err(dev, "set_rgb2rgb_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + + if (copy_from_user(rgb2rgb, rgb2rgb_param, + sizeof(struct prev_rgb2rgb))) { + dev_err(dev, "set_rgb2rgb_params: Error in " + "copy to kernel\n"); + return -EFAULT; + } + if (validate_rgb2rgb_params(dev, oper_state, id) < 0) + return -EINVAL; + } + ipipe_set_rgb2rgb_regs(id, rgb2rgb); + + return 0; +} + +static int set_rgb2rgb_1_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return set_rgb2rgb_params(dev, oper_state, 0, param, len); +} + +static int set_rgb2rgb_2_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return set_rgb2rgb_params(dev, oper_state, 1, param, len); +} + +static int get_rgb2rgb_params(struct device *dev, + struct ipipe_oper_state *oper_state, + unsigned int id, + void *param, int len) +{ + struct prev_rgb2rgb *rgb2rgb_param = (struct prev_rgb2rgb *)param; + struct prev_rgb2rgb *rgb2rgb = &oper_state->rgb2rgb_1; + + if (!rgb2rgb_param) { + dev_err(dev, "get_rgb2rgb_params: invalid user ptr"); + return -EINVAL; + } + + if (len != sizeof(struct prev_rgb2rgb)) { + dev_err(dev, + "get_rgb2rgb_params: param struct length mismatch\n"); + return -EINVAL; + } + + if (id) + rgb2rgb = &oper_state->rgb2rgb_2; + if (copy_to_user((struct prev_rgb2rgb *)rgb2rgb_param, rgb2rgb, + sizeof(struct prev_rgb2rgb))) { + dev_err(dev, "get_rgb2rgb_params: Error in copy to user\n"); + return -EFAULT; + } + + return 0; +} + +static int get_rgb2rgb_1_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return get_rgb2rgb_params(dev, oper_state, 0, param, len); +} + +static int get_rgb2rgb_2_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return get_rgb2rgb_params(dev, oper_state, 1, param, len); +} + +static int validate_gamma_entry(struct ipipe_gamma_entry *table, int size) +{ + int i; + + if (!table) + return -EINVAL; + + for (i = 0; i < size; i++) { + if (table[i].slope > GAMMA_MASK || + table[i].offset > GAMMA_MASK) + return -EINVAL; + } + return 0; +} + +static int validate_gamma_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + int table_size; + int err; + + if (oper_state->gamma.bypass_r > 1 || + oper_state->gamma.bypass_b > 1 || + oper_state->gamma.bypass_g > 1) + return -EINVAL; + + if (oper_state->gamma.tbl_sel != IPIPE_GAMMA_TBL_RAM) + return 0; + + table_size = oper_state->gamma.tbl_size; + if (!oper_state->gamma.bypass_r) { + err = validate_gamma_entry(oper_state->gamma.table_r, + table_size); + if (err) { + dev_err(dev, "GAMMA R - table entry invalid\n"); + return err; + } + } + if (!oper_state->gamma.bypass_b) { + err = validate_gamma_entry(oper_state->gamma.table_b, + table_size); + if (err) { + dev_err(dev, "GAMMA B - table entry invalid\n"); + return err; + } + } + if (!oper_state->gamma.bypass_g) { + err = validate_gamma_entry(oper_state->gamma.table_g, + table_size); + if (err) { + dev_err(dev, "GAMMA G - table entry invalid\n"); + return err; + } + } + return 0; +} + +static int set_gamma_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_gamma *gamma_param = (struct prev_gamma *)param; + struct prev_gamma user_gamma; + int table_size; + + if (!gamma_param) { + /* Copy defaults for gamma */ + oper_state->gamma.bypass_r = dm365_gamma_defaults.bypass_r; + oper_state->gamma.bypass_g = dm365_gamma_defaults.bypass_g; + oper_state->gamma.bypass_b = dm365_gamma_defaults.bypass_b; + oper_state->gamma.tbl_sel = dm365_gamma_defaults.tbl_sel; + oper_state->gamma.tbl_size = dm365_gamma_defaults.tbl_size; + /* By default, we bypass the gamma correction. + * So no values by default for tables + */ + goto success; + } + + if (len != sizeof(struct prev_gamma)) { + dev_err(dev, "set_gamma_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_gamma, gamma_param, + sizeof(struct prev_gamma))) { + dev_err(dev, "set_gamma_params: Error in copy to kernel\n"); + return -EFAULT; + } + + if (validate_gamma_params(dev, oper_state) < 0) + return -EINVAL; + + oper_state->gamma.bypass_r = user_gamma.bypass_r; + oper_state->gamma.bypass_b = user_gamma.bypass_b; + oper_state->gamma.bypass_g = user_gamma.bypass_g; + oper_state->gamma.tbl_sel = user_gamma.tbl_sel; + oper_state->gamma.tbl_size = user_gamma.tbl_size; + + if (user_gamma.tbl_sel != IPIPE_GAMMA_TBL_RAM) + goto success; + + table_size = user_gamma.tbl_size; + + if (!user_gamma.bypass_r) { + if (!user_gamma.table_r) { + dev_err(dev, "set_gamma_params: Invalid" + " table ptr for R\n"); + return -EINVAL; + } + if (copy_from_user(oper_state->gamma.table_r, + user_gamma.table_r, (table_size * + sizeof(struct ipipe_gamma_entry)))) { + dev_err(dev, "set_gamma_params: R-Error" + " in copy to kernel\n"); + return -EFAULT; + } + } + + if (!user_gamma.bypass_b) { + if (!user_gamma.table_b) { + dev_err(dev, "set_gamma_params: Invalid" + " table ptr for B\n"); + return -EINVAL; + } + if (copy_from_user(oper_state->gamma.table_b, + user_gamma.table_b, (table_size * + sizeof(struct ipipe_gamma_entry)))) { + dev_err(dev, "set_gamma_params: B-Error" + " in copy to kernel\n"); + return -EFAULT; + } + } + + if (!user_gamma.bypass_g) { + if (!user_gamma.table_g) { + dev_err(dev, "set_gamma_params: Invalid" + " table ptr for G\n"); + return -EINVAL; + } + if (copy_from_user(oper_state->gamma.table_g, + user_gamma.table_g, (table_size * + sizeof(struct ipipe_gamma_entry)))) { + dev_err(dev, "set_gamma_params: G-Error " + "in copy to kernel\n"); + return -EFAULT; + } + } + +success: + ipipe_set_gamma_regs(&oper_state->gamma); + + return 0; +} + +static int get_gamma_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_gamma *gamma_param = (struct prev_gamma *)param; + struct prev_gamma user_gamma; + int table_size; + + if (!gamma_param) { + dev_err(dev, "get_gamma_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_gamma)) { + dev_err(dev, + "get_gamma_params: param struct length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_gamma, gamma_param, + sizeof(struct prev_gamma))) { + dev_err(dev, "get_gamma_params: Error in copy to kernel\n"); + return -EFAULT; + } + + user_gamma.bypass_r = oper_state->gamma.bypass_r; + user_gamma.bypass_g = oper_state->gamma.bypass_g; + user_gamma.bypass_b = oper_state->gamma.bypass_b; + user_gamma.tbl_sel = oper_state->gamma.tbl_sel; + user_gamma.tbl_size = oper_state->gamma.tbl_size; + + if (oper_state->gamma.tbl_sel != IPIPE_GAMMA_TBL_RAM) + goto copy_user_gama; + + table_size = oper_state->gamma.tbl_size; + + if (!oper_state->gamma.bypass_r && !user_gamma.table_r) { + dev_err(dev, + "get_gamma_params: table ptr empty for R\n"); + return -EINVAL; + } + if (copy_to_user(user_gamma.table_r, oper_state->gamma.table_r, + (table_size * sizeof(struct ipipe_gamma_entry)))) { + dev_err(dev, "set_gamma_params: R-Table Error " + "in copy to user\n"); + return -EFAULT; + } + + if (!oper_state->gamma.bypass_b && !user_gamma.table_b) { + dev_err(dev, "get_gamma_params: table ptr empty for B\n"); + return -EINVAL; + } + if (copy_to_user(user_gamma.table_b, oper_state->gamma.table_b, + (table_size * sizeof(struct ipipe_gamma_entry)))) { + dev_err(dev, + "set_gamma_params: B-Table Error in copy to user\n"); + return -EFAULT; + } + + if (!oper_state->gamma.bypass_g && !user_gamma.table_g) { + dev_err(dev, "get_gamma_params: table ptr empty for G\n"); + return -EINVAL; + } + if (copy_from_user(oper_state->gamma.table_g, user_gamma.table_g, + (table_size * sizeof(struct ipipe_gamma_entry)))) { + dev_err(dev, "set_gamma_params: G-Table copy error\n"); + return -EFAULT; + } + +copy_user_gama: + if (copy_to_user(gamma_param, &user_gamma, sizeof(struct prev_gamma))) { + dev_err(dev, "get_dfc_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_3d_lut_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + int i; + + if (!oper_state->lut_3d.en) + return 0; + + for (i = 0; i < MAX_SIZE_3D_LUT; i++) { + if (oper_state->lut_3d.table[i].r > D3_LUT_ENTRY_MASK || + oper_state->lut_3d.table[i].g > D3_LUT_ENTRY_MASK || + oper_state->lut_3d.table[i].b > D3_LUT_ENTRY_MASK) + return -EINVAL; + } + return 0; +} + +static int set_3d_lut_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_3d_lut *lut_param = (struct prev_3d_lut *)param; + struct prev_3d_lut user_3d_lut; + + if (!lut_param) { + /* Copy defaults for gamma */ + oper_state->lut_3d.en = dm365_3d_lut_defaults.en; + /* By default, 3D lut is disabled + */ + } else { + if (len != sizeof(struct prev_3d_lut)) { + dev_err(dev, "set_3d_lut_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_3d_lut, lut_param, + sizeof(struct prev_3d_lut))) { + dev_err(dev, "set_3d_lut_params: Error in copy to" + " kernel\n"); + return -EFAULT; + } + oper_state->lut_3d.en = user_3d_lut.en; + if (!user_3d_lut.table) { + dev_err(dev, "set_3d_lut_params: Invalid table ptr"); + return -EINVAL; + } + if (copy_from_user(oper_state->lut_3d.table, user_3d_lut.table, + (MAX_SIZE_3D_LUT * + sizeof(struct ipipe_3d_lut_entry)))) { + dev_err(dev, "set_3d_lut_params:Error" + " in copy to kernel\n"); + return -EFAULT; + } + + if (validate_3d_lut_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_3d_lut_regs(&oper_state->lut_3d); + + return 0; +} +static int get_3d_lut_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_3d_lut *lut_param = (struct prev_3d_lut *)param; + struct prev_3d_lut user_3d_lut; + + if (!lut_param) { + dev_err(dev, "get_3d_lut_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_3d_lut)) { + dev_err(dev, + "get_3d_lut_params: param struct length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_3d_lut, lut_param, + sizeof(struct prev_3d_lut))) { + dev_err(dev, "get_3d_lut_params: Error in copy to kernel\n"); + return -EFAULT; + } + + user_3d_lut.en = oper_state->lut_3d.en; + if (!user_3d_lut.table) { + dev_err(dev, "get_3d_lut_params:" " Invalid table ptr"); + return -EINVAL; + } + if (copy_to_user(user_3d_lut.table, oper_state->lut_3d.table, + (MAX_SIZE_3D_LUT * + sizeof(struct ipipe_3d_lut_entry)))) { + dev_err(dev, + "get_3d_lut_params:Table Error in" " copy to user\n"); + return -EFAULT; + } + + if (copy_to_user(lut_param, &user_3d_lut, + sizeof(struct prev_3d_lut))) { + dev_err(dev, "get_3d_lut_params: Error in copy" " to user\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_lum_adj_params(struct device *dev) +{ + /* nothing to validate */ + return 0; +} + +static int set_lum_adj_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_lum_adj *lum_adj_param = (struct prev_lum_adj *)param; + + if (!lum_adj_param) { + /* Copy defaults for Luminance adjustments */ + memcpy((void *)&oper_state->lum_adj, + (void *)&dm365_lum_adj_defaults, + sizeof(struct prev_lum_adj)); + } else { + if (len != sizeof(struct prev_lum_adj)) { + dev_err(dev, "set_lum_adj_params: param struct length" + " mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->lum_adj, lum_adj_param, + sizeof(struct prev_lum_adj))) { + dev_err(dev, "set_lum_adj_params: Error in copy" + " from user\n"); + return -EFAULT; + } + if (validate_lum_adj_params(dev) < 0) + return -EINVAL; + } + + ipipe_set_lum_adj_regs(&oper_state->lum_adj); + + return 0; +} + +static int get_lum_adj_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_lum_adj *lum_adj_param = (struct prev_lum_adj *)param; + + if (!lum_adj_param) { + dev_err(dev, "get_lum_adj_params: invalid user ptr"); + return -EINVAL; + } + + if (len != sizeof(struct prev_lum_adj)) { + dev_err(dev, + "get_lum_adj_params: param struct length mismatch\n"); + return -EINVAL; + } + + if (copy_to_user(lum_adj_param, &oper_state->lum_adj, + sizeof(struct prev_lum_adj))) { + dev_err(dev, "get_lum_adj_params: Error in copy to" " user\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_rgb2yuv_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + if (oper_state->rgb2yuv.coef_ry.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_ry.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_gy.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_gy.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_by.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_by.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_rcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_rcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_gcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_gcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_bcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_bcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_rcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_rcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_gcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_gcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.coef_bcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + oper_state->rgb2yuv.coef_bcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -EINVAL; + + if (oper_state->rgb2yuv.out_ofst_y > RGB2YCBCR_OFST_MASK || + oper_state->rgb2yuv.out_ofst_cb > RGB2YCBCR_OFST_MASK || + oper_state->rgb2yuv.out_ofst_cr > RGB2YCBCR_OFST_MASK) + return -EINVAL; + return 0; +} + +static int set_rgb2yuv_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_rgb2yuv *rgb2yuv_param = (struct prev_rgb2yuv *)param; + + if (!rgb2yuv_param) { + /* Copy defaults for rgb2yuv conversion */ + memcpy((void *)&oper_state->rgb2yuv, + (void *)&dm365_rgb2yuv_defaults, + sizeof(struct prev_rgb2yuv)); + } else { + if (len != sizeof(struct prev_rgb2yuv)) { + dev_err(dev, "set_rgb2yuv_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->rgb2yuv, + rgb2yuv_param, sizeof(struct prev_rgb2yuv))) { + dev_err(dev, "set_rgb2yuv_params: Error in copy from" + " user\n"); + return -EFAULT; + } + if (validate_rgb2yuv_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_rgb2ycbcr_regs(&oper_state->rgb2yuv); + + return 0; +} + +static int get_rgb2yuv_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_rgb2yuv *rgb2yuv_param = (struct prev_rgb2yuv *)param; + + if (!rgb2yuv_param) { + dev_err(dev, "get_rgb2yuv_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_rgb2yuv)) { + dev_err(dev, + "get_rgb2yuv_params: param struct length mismatch\n"); + return -EINVAL; + } + if (copy_to_user((struct prev_rgb2yuv *)rgb2yuv_param, + &oper_state->rgb2yuv, + sizeof(struct prev_rgb2yuv))) { + dev_err(dev, "get_rgb2yuv_params: Error in copy from" + " kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_gbce_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + u32 max = GBCE_Y_VAL_MASK; + int i; + + if (!oper_state->gbce.en) + return 0; + + if (oper_state->gbce.type == IPIPE_GBCE_GAIN_TBL) + max = GBCE_GAIN_VAL_MASK; + for (i = 0; i < MAX_SIZE_GBCE_LUT; i++) + if (oper_state->gbce.table[i] > max) + return -EINVAL; + return 0; +} +static int set_gbce_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_gbce *gbce_param = (struct prev_gbce *)param; + struct prev_gbce user_gbce; + + if (!gbce_param) { + /* Copy defaults for gamma */ + oper_state->gbce.en = dm365_gbce_defaults.en; + /* By default, GBCE is disabled + */ + } else { + if (len != sizeof(struct prev_gbce)) { + dev_err(dev, "set_gbce_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_gbce, gbce_param, + sizeof(struct prev_gbce))) { + dev_err(dev, "set_gbce_params: Error in copy to" + " kernel\n"); + return -EFAULT; + } + oper_state->gbce.en = user_gbce.en; + oper_state->gbce.type = user_gbce.type; + if (!user_gbce.table) { + dev_err(dev, "set_gbce_params:" " Invalid table ptr"); + return -EINVAL; + } + + if (copy_from_user(oper_state->gbce.table, + user_gbce.table, (MAX_SIZE_GBCE_LUT * + sizeof(unsigned short)))) { + dev_err(dev, "set_gbce_params:Error" + " in copy to kernel\n"); + return -EFAULT; + } + if (validate_gbce_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_gbce_regs(&oper_state->gbce); + + return 0; +} +static int get_gbce_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_gbce *gbce_param = (struct prev_gbce *)param; + struct prev_gbce user_gbce; + + if (!gbce_param) { + dev_err(dev, "get_gbce_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_gbce)) { + dev_err(dev, + "get_gbce_params: param struct length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_gbce, gbce_param, sizeof(struct prev_gbce))) { + dev_err(dev, "get_gbce_params: Error in copy to" " kernel\n"); + return -EFAULT; + } + + user_gbce.en = oper_state->gbce.en; + user_gbce.type = oper_state->gbce.type; + if (!user_gbce.table) { + dev_err(dev, "get_gbce_params:" " Invalid table ptr"); + return -EINVAL; + } + if (copy_to_user(user_gbce.table, oper_state->gbce.table, + (MAX_SIZE_GBCE_LUT * sizeof(unsigned short)))) { + dev_err(dev, + "get_gbce_params:Table Error in" " copy to user\n"); + return -EFAULT; + } + + if (copy_to_user(gbce_param, &user_gbce, sizeof(struct prev_gbce))) { + dev_err(dev, "get_gbce_params: Error in copy" " to user\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_yuv422_conv_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + if (oper_state->yuv422_conv.en_chrom_lpf > 1) + return -EINVAL; + return 0; +} + +static int set_yuv422_conv_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_yuv422_conv *yuv422_conv_param = + (struct prev_yuv422_conv *)param; + + if (!yuv422_conv_param) { + /* Copy defaults for yuv 422 conversion */ + memcpy((void *)&oper_state->yuv422_conv, + (void *)&dm365_yuv422_conv_defaults, + sizeof(struct prev_yuv422_conv)); + } else { + if (len != sizeof(struct prev_yuv422_conv)) { + dev_err(dev, "set_yuv422_conv_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->yuv422_conv, yuv422_conv_param, + sizeof(struct prev_yuv422_conv))) { + dev_err(dev, "set_yuv422_conv_params: Error in copy" + " from user\n"); + return -EFAULT; + } + if (validate_yuv422_conv_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_yuv422_conv_regs(&oper_state->yuv422_conv); + + return 0; +} +static int get_yuv422_conv_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_yuv422_conv *yuv422_conv_param = + (struct prev_yuv422_conv *)param; + + if (!yuv422_conv_param) { + dev_err(dev, "get_yuv422_conv_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_yuv422_conv)) { + dev_err(dev, "get_yuv422_conv_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_to_user(yuv422_conv_param, &oper_state->yuv422_conv, + sizeof(struct prev_yuv422_conv))) { + dev_err(dev, + "get_yuv422_conv_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_yee_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + int i; + + if (oper_state->yee.en > 1 || + oper_state->yee.en_halo_red > 1 || + oper_state->yee.hpf_shft > YEE_HPF_SHIFT_MASK) + return -EINVAL; + + if (oper_state->yee.hpf_coef_00 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_01 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_02 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_10 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_11 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_12 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_20 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_21 > YEE_COEF_MASK || + oper_state->yee.hpf_coef_22 > YEE_COEF_MASK) + return -EINVAL; + + if (oper_state->yee.yee_thr > YEE_THR_MASK || + oper_state->yee.es_gain > YEE_ES_GAIN_MASK || + oper_state->yee.es_thr1 > YEE_ES_THR1_MASK || + oper_state->yee.es_thr2 > YEE_THR_MASK || + oper_state->yee.es_gain_grad > YEE_THR_MASK || + oper_state->yee.es_ofst_grad > YEE_THR_MASK) + return -EINVAL; + + for (i = 0; i < MAX_SIZE_YEE_LUT ; i++) + if (oper_state->yee.table[i] > YEE_ENTRY_MASK) + return -EINVAL; + return 0; +} +static int set_yee_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_yee *yee_param = (struct prev_yee *)param; + struct prev_yee user_yee; + short *temp_table; + + if (!yee_param) { + temp_table = oper_state->yee.table; + /* Copy defaults for ns */ + memcpy((void *)&oper_state->yee, (void *)&dm365_yee_defaults, + sizeof(struct prev_yee)); + oper_state->yee.table = temp_table; + } else { + if (len != sizeof(struct prev_yee)) { + dev_err(dev, "set_yee_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_yee, yee_param, + sizeof(struct prev_yee))) { + dev_err(dev, + "set_yee_params: Error in copy from user\n"); + return -EFAULT; + } + if (!user_yee.table) { + dev_err(dev, "set_yee_params: yee table ptr null\n"); + return -EINVAL; + } + if (copy_from_user(oper_state->yee.table, user_yee.table, + (MAX_SIZE_YEE_LUT * sizeof(short)))) { + dev_err(dev, + "set_yee_params: Error in copy from user\n"); + return -EFAULT; + } + temp_table = oper_state->yee.table; + memcpy(&oper_state->yee, &user_yee, sizeof(struct prev_yee)); + oper_state->yee.table = temp_table; + if (validate_yee_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_ee_regs(&oper_state->yee); + + return 0; +} +static int get_yee_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_yee *yee_param = (struct prev_yee *)param; + struct prev_yee user_yee; + short *temp_table; + + if (!yee_param) { + dev_err(dev, "get_yee_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_yee)) { + dev_err(dev, "get_yee_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&user_yee, yee_param, sizeof(struct prev_yee))) { + dev_err(dev, "get_yee_params: Error in copy to kernel\n"); + return -EFAULT; + } + if (!user_yee.table) { + dev_err(dev, "get_yee_params: yee table ptr null\n"); + return -EINVAL; + } + if (copy_to_user(user_yee.table, oper_state->yee.table, + (MAX_SIZE_YEE_LUT * sizeof(short)))) { + dev_err(dev, "get_yee_params: Error in copy from kernel\n"); + return -EFAULT; + } + temp_table = user_yee.table; + memcpy(&user_yee, &oper_state->yee, sizeof(struct prev_yee)); + user_yee.table = temp_table; + + if (copy_to_user(yee_param, &user_yee, sizeof(struct prev_yee))) { + dev_err(dev, "get_yee_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_car_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + if (oper_state->car.en > 1 || + oper_state->car.hpf_shft > CAR_HPF_SHIFT_MASK || + oper_state->car.gain1.shft > CAR_GAIN1_SHFT_MASK || + oper_state->car.gain1.gain_min > CAR_GAIN_MIN_MASK || + oper_state->car.gain2.shft > CAR_GAIN2_SHFT_MASK || + oper_state->car.gain2.gain_min > CAR_GAIN_MIN_MASK) + return -EINVAL; + return 0; +} + +static int set_car_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_car *car_param = (struct prev_car *)param; + + if (!car_param) { + /* Copy defaults for ns */ + memcpy((void *)&oper_state->car, (void *)&dm365_car_defaults, + sizeof(struct prev_car)); + } else { + if (len != sizeof(struct prev_car)) { + dev_err(dev, "set_car_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->car, car_param, + sizeof(struct prev_car))) { + dev_err(dev, + "set_car_params: Error in copy from user\n"); + return -EFAULT; + } + if (validate_car_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_car_regs(&oper_state->car); + + return 0; +} +static int get_car_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_car *car_param = (struct prev_car *)param; + + if (!car_param) { + dev_err(dev, "get_car_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_car)) { + dev_err(dev, "get_car_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_to_user(car_param, &oper_state->car, + sizeof(struct prev_car))) { + dev_err(dev, "get_car_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static int validate_cgs_params(struct device *dev, + struct ipipe_oper_state *oper_state) +{ + if (oper_state->cgs.en > 1 || + oper_state->cgs.h_shft > CAR_SHIFT_MASK) + return -EINVAL; + return 0; +} + +static int set_cgs_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_cgs *cgs_param = (struct prev_cgs *)param; + + if (!cgs_param) { + /* Copy defaults for ns */ + memcpy((void *)&oper_state->cgs, (void *)&dm365_cgs_defaults, + sizeof(struct prev_cgs)); + } else { + if (len != sizeof(struct prev_cgs)) { + dev_err(dev, "set_cgs_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_from_user(&oper_state->cgs, cgs_param, + sizeof(struct prev_cgs))) { + dev_err(dev, + "set_cgs_params: Error in copy from user\n"); + return -EFAULT; + } + if (validate_cgs_params(dev, oper_state) < 0) + return -EINVAL; + } + + ipipe_set_cgs_regs(&oper_state->cgs); + + return 0; +} + +static int get_cgs_params(struct device *dev, void *ipipe, + void *param, int len) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + struct prev_cgs *cgs_param = (struct prev_cgs *)param; + + if (!cgs_param) { + dev_err(dev, "get_cgs_params: invalid user ptr"); + return -EINVAL; + } + if (len != sizeof(struct prev_cgs)) { + dev_err(dev, "get_cgs_params: param struct" + " length mismatch\n"); + return -EINVAL; + } + if (copy_to_user(cgs_param, &oper_state->cgs, + sizeof(struct prev_cgs))) { + dev_err(dev, "get_cgs_params: Error in copy from kernel\n"); + return -EFAULT; + } + + return 0; +} + +static struct prev_module_if prev_modules[PREV_MAX_MODULES] = { + { + .version = "5.1", + .module_id = PREV_LUTDPC, + .module_name = "LUT Defect Correction", + .control = 0, + .path = IMP_RAW2RAW | IMP_RAW2YUV, + .set = set_lutdpc_params, + .get = get_lutdpc_params + }, + { + .version = "5.1", + .module_id = PREV_OTFDPC, + .module_name = "OTF Defect Pixel Correction", + .control = 0, + .path = IMP_RAW2RAW | IMP_RAW2YUV, + .set = set_otfdpc_params, + .get = get_otfdpc_params + }, + { + .version = "5.1", + .module_id = PREV_NF1, + .module_name = "2-D Noise filter - 1", + .control = 0, + .path = IMP_RAW2RAW | IMP_RAW2YUV, + .set = set_nf1_params, + .get = get_nf1_params + }, + { + .version = "5.1", + .module_id = PREV_NF2, + .module_name = "2-D Noise filter - 2", + .control = 0, + .path = IMP_RAW2RAW | IMP_RAW2YUV, + .set = set_nf2_params, + .get = get_nf2_params + }, + { + .version = "5.1", + .module_id = PREV_GIC, + .module_name = "Green Imbalance Correction", + .control = 0, + .path = IMP_RAW2RAW | IMP_RAW2YUV, + .set = set_gic_params, + .get = get_gic_params + }, + { + .version = "5.1", + .module_id = PREV_WB, + .module_name = "White balance", + .control = 1, + .path = IMP_RAW2RAW | IMP_RAW2YUV, + .set = set_wb_params, + .get = get_wb_params + }, + { + .version = "5.1", + .module_id = PREV_CFA, + .module_name = "CFA Interpolation", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_cfa_params, + .get = get_cfa_params + }, + { + .version = "5.1", + .module_id = PREV_RGB2RGB_1, + .module_name = "RGB-RGB Conversion - 1", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_rgb2rgb_1_params, + .get = get_rgb2rgb_1_params + }, + { + .version = "5.1", + .module_id = PREV_GAMMA, + .module_name = "Gamma Correction", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_gamma_params, + .get = get_gamma_params + }, + { + .version = "5.1", + .module_id = PREV_RGB2RGB_2, + .module_name = "RGB-RGB Conversion - 2", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_rgb2rgb_2_params, + .get = get_rgb2rgb_2_params + }, + { + .version = "5.1", + .module_id = PREV_3D_LUT, + .module_name = "3D LUT", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_3d_lut_params, + .get = get_3d_lut_params + }, + { + .version = "5.1", + .module_id = PREV_RGB2YUV, + .module_name = "RGB-YCbCr conversion", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_rgb2yuv_params, + .get = get_rgb2yuv_params + }, + { + .version = "5.1", + .module_id = PREV_GBCE, + .module_name = "Global Brightness,Contrast Control", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_gbce_params, + .get = get_gbce_params + }, + { + .version = "5.1", + .module_id = PREV_YUV422_CONV, + .module_name = "YUV 422 conversion", + .control = 0, + .path = IMP_RAW2YUV, + .set = set_yuv422_conv_params, + .get = get_yuv422_conv_params + }, + { + .version = "5.1", + .module_id = PREV_LUM_ADJ, + .module_name = "Luminance Adjustment", + .control = 1, + .path = IMP_RAW2YUV, + .set = set_lum_adj_params, + .get = get_lum_adj_params + }, + { + .version = "5.1", + .module_id = PREV_YEE, + .module_name = "Edge Enhancer", + .control = 1, + .path = IMP_RAW2YUV | IMP_YUV2YUV, + .set = set_yee_params, + .get = get_yee_params + }, + { + .version = "5.1", + .module_id = PREV_CAR, + .module_name = "Chromatic Artifact Reduction", + .control = 1, + .path = IMP_RAW2YUV | IMP_YUV2YUV, + .set = set_car_params, + .get = get_car_params + }, + { + .version = "5.1", + .module_id = PREV_CGS, + .module_name = "Chromatic Gain Suppression", + .control = 1, + .path = IMP_RAW2YUV | IMP_YUV2YUV, + .set = set_cgs_params, + .get = get_cgs_params + } +}; + +static struct prev_module_if *prev_enum_preview_cap(struct device *dev, + unsigned int index) +{ + dev_dbg(dev, "prev_enum_preview_cap: index = %d\n", index); + + if (index < 0 || index >= PREV_MAX_MODULES) + return NULL; + + return &prev_modules[index]; +} + +static int ipipe_set_oper_mode(void *ipipe, unsigned int mode) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + if (oper_state->oper_mode != IMP_MODE_NOT_CONFIGURED) { + printk(KERN_ERR "IPIPE is already active!\n"); + return -EINVAL; + } + oper_state->oper_mode = mode; + + return 0; +} + +static void ipipe_reset_oper_mode(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + oper_state->oper_mode = IMP_MODE_NOT_CONFIGURED; + oper_state->prev_config_state = STATE_NOT_CONFIGURED; + oper_state->rsz_config_state = STATE_NOT_CONFIGURED; + oper_state->rsz_chained = 0; + oper_state->rsz_output_enabled[RSZ_A] = DISABLE; + oper_state->rsz_output_enabled[RSZ_B] = DISABLE; +} + +static unsigned int prev_get_oper_mode(void *ipipe) +{ + struct ipipe_oper_state *oper_state = (struct ipipe_oper_state *)ipipe; + + return oper_state->oper_mode; +} + +static unsigned int ipipe_get_ope