From bengardiner at nanometrics.ca Wed Jun 1 10:03:22 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 1 Jun 2011 11:03:22 -0400 Subject: [PATCH v3 0/3] davinci: ioremap SRAM instead of iotables In-Reply-To: <20110601051753.GA5153@game.jcrosoft.org> References: <20110601051753.GA5153@game.jcrosoft.org> Message-ID: The davinci platforms are mapping their io regions using iotables. This patch series converts them to mapping using ioremap. This version of the series is based on-top-of '[RFC PATCH v4] Consolidate SRAM support' from Russell King and Jean-Christophe PLAGNIOL-VILLARD. V3 has a fix to the commit message of 1/3 as requested by Jean-Christophe PLAGNIOL- VILLARD and also his Acks added to the series. The davinci sram init is first changed to ioremap the regions specified by each of the soc_infos; then the iotables are each removed; then the SRAM_VIRT definition is removed. Finally, the da850's sram region is changed from the ARM local RAM region to the Shared RAM region. This change is needed to support mcasp ping-pong buffers on da850. Suspend was tested with rtcwake and was found to work. Ben Gardiner (2): [v3] davinci: sram: ioremap the davinci_soc_info specified sram regions [v3] davinci: da850-dm646x: remove the SRAM_VIRT iotable entry Subhasish Ghosh (1): [v3] davinci: da850: changed SRAM allocator to shared ram. arch/arm/mach-davinci/da850.c | 10 ++-------- arch/arm/mach-davinci/dm355.c | 6 ------ arch/arm/mach-davinci/dm365.c | 6 ------ arch/arm/mach-davinci/dm644x.c | 6 ------ arch/arm/mach-davinci/dm646x.c | 6 ------ arch/arm/mach-davinci/include/mach/common.h | 2 -- arch/arm/mach-davinci/include/mach/da8xx.h | 1 + arch/arm/mach-davinci/sram.c | 10 ++++++++-- 8 files changed, 11 insertions(+), 36 deletions(-) -- 1.7.4.1 From bengardiner at nanometrics.ca Wed Jun 1 10:03:23 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 1 Jun 2011 11:03:23 -0400 Subject: [PATCH 1/3] [v3] davinci: sram: ioremap the davinci_soc_info specified sram regions In-Reply-To: References: <20110601051753.GA5153@game.jcrosoft.org> Message-ID: <0787ab1fe2a1db4f18feb4b45f6ba44f90e9da59.1306940448.git.bengardiner@nanometrics.ca> The current davinci init sets up SRAM in iotables. There has been an observed failure to boot a da850 with 128K specified in the iotable. Make the davinci sram allocator -- now based on RMK's consolidated SRAM support -- do an ioremap of the region specified by the entries in davinci_soc_info before registering with gen_pool_add_virt(). This commit breaks runtime of davinci boards since the regions that the sram init is now trying to ioremap have been iomapped by their iotable entries. The iotable entries will be removed in the patches to come. Signed-off-by: Ben Gardiner CC: Sekhar Nori Acked-by: Jean-Christophe PLAGNIOL-VILLARD --- Changes since v2: * Added Jean-Christophe PLAGNIOL-VILLARD's Ack Changes since v1: * return -ENOMEM if ioremap fails (Sekhar Nori) --- arch/arm/mach-davinci/sram.c | 10 ++++++++-- 1 files changed, 8 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/sram.c b/arch/arm/mach-davinci/sram.c index 2c53db2..68b05d5 100644 --- a/arch/arm/mach-davinci/sram.c +++ b/arch/arm/mach-davinci/sram.c @@ -8,6 +8,7 @@ * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ +#include #include #include @@ -25,6 +26,7 @@ EXPORT_SYMBOL_GPL(davinci_gen_pool); */ static int __init sram_init(void) { + void *addr; unsigned len = davinci_soc_info.sram_len; if (!len) @@ -36,8 +38,12 @@ static int __init sram_init(void) if (!davinci_gen_pool) return -ENOMEM; - WARN_ON(gen_pool_add_virt(davinci_gen_pool, SRAM_VIRT, - davinci_soc_info.sram_phys, len, -1)); + addr = ioremap(davinci_soc_info.sram_phys, len); + if (!addr) + return -ENOMEM; + if(WARN_ON(gen_pool_add_virt(davinci_gen_pool, addr, + davinci_soc_info.sram_phys, len, -1))) + iounmap(addr); return 0; } -- 1.7.4.1 From bengardiner at nanometrics.ca Wed Jun 1 10:03:25 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 1 Jun 2011 11:03:25 -0400 Subject: [PATCH 3/3] [v3] davinci: da850: changed SRAM allocator to shared ram. In-Reply-To: References: <20110601051753.GA5153@game.jcrosoft.org> Message-ID: <9abf8b57b0b664ffc262af63bd0495e9d7c09230.1306940448.git.bengardiner@nanometrics.ca> From: Subhasish Ghosh This patch modifies the sram allocator to allocate memory from the DA8XX shared RAM. Signed-off-by: Subhasish Ghosh [rebased onto consolidated SRAM patches] Signed-off-by: Ben Gardiner Reviewed-by: Sergei Shtylyov Acked-by: Jean-Christophe PLAGNIOL-VILLARD --- Changes since v2: * Added Jean-Christophe PLAGNIOL-VILLARD's Ack Changes since v1: * re-sorted the new DA8XX_SHARED_RAM_BASE definition in da8xx.h to between DA8XX_AEMIF_CTL_BASE and DA8XX_DDR2_CTL_BASE (Sergei Shtylyov). --- arch/arm/mach-davinci/da850.c | 4 ++-- arch/arm/mach-davinci/include/mach/da8xx.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 904ede9..4781230 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -1093,8 +1093,8 @@ static struct davinci_soc_info davinci_soc_info_da850 = { .gpio_irq = IRQ_DA8XX_GPIO0, .serial_dev = &da8xx_serial_device, .emac_pdata = &da8xx_emac_pdata, - .sram_phys = DA8XX_ARM_RAM_BASE, - .sram_len = SZ_8K, + .sram_phys = DA8XX_SHARED_RAM_BASE, + .sram_len = SZ_128K, .reset_device = &da8xx_wdt_device, }; diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index ad64da7..b67499f 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -67,6 +67,7 @@ extern unsigned int da850_max_speed; #define DA8XX_AEMIF_CS2_BASE 0x60000000 #define DA8XX_AEMIF_CS3_BASE 0x62000000 #define DA8XX_AEMIF_CTL_BASE 0x68000000 +#define DA8XX_SHARED_RAM_BASE 0x80000000 #define DA8XX_ARM_RAM_BASE 0xffff0000 void __init da830_init(void); -- 1.7.4.1 From bengardiner at nanometrics.ca Wed Jun 1 10:03:24 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 1 Jun 2011 11:03:24 -0400 Subject: [PATCH 2/3] [v3] davinci: da850-dm646x: remove the SRAM_VIRT iotable entry In-Reply-To: References: <20110601051753.GA5153@game.jcrosoft.org> Message-ID: The sram regions defined for da850-dm646x in their iotable entries are also defined in their davinci_soc_info's. Remove this duplicate information which is now uneccessary since sram init will ioremap the regions defined by their davinci_soc_info's. Since this removal completely removes all uses of SRAM_VIRT, also remove the SRAM_VIRT definition. Signed-off-by: Ben Gardiner Reviewed-by: Sergei Shtylyov Acked-by: Jean-Christophe PLAGNIOL-VILLARD --- Changes since v2: * Added Jean-Christophe PLAGNIOL-VILLARD's Ack Changes since v1: * squashed patches 3-8 in v1 series into this patch (Sergei Shtylyov) --- arch/arm/mach-davinci/da850.c | 6 ------ arch/arm/mach-davinci/dm355.c | 6 ------ arch/arm/mach-davinci/dm365.c | 6 ------ arch/arm/mach-davinci/dm644x.c | 6 ------ arch/arm/mach-davinci/dm646x.c | 6 ------ arch/arm/mach-davinci/include/mach/common.h | 2 -- 6 files changed, 0 insertions(+), 32 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 5c2bf3b..904ede9 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -725,12 +725,6 @@ static struct map_desc da850_io_desc[] = { .length = DA8XX_CP_INTC_SIZE, .type = MT_DEVICE }, - { - .virtual = SRAM_VIRT, - .pfn = __phys_to_pfn(DA8XX_ARM_RAM_BASE), - .length = SZ_8K, - .type = MT_DEVICE - }, }; static u32 da850_psc_bases[] = { DA8XX_PSC0_BASE, DA8XX_PSC1_BASE }; diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 9bda687..94f44d3 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -757,12 +757,6 @@ static struct map_desc dm355_io_desc[] = { .length = IO_SIZE, .type = MT_DEVICE }, - { - .virtual = SRAM_VIRT, - .pfn = __phys_to_pfn(0x00010000), - .length = SZ_32K, - .type = MT_MEMORY_NONCACHED, - }, }; /* Contents of JTAG ID register used to identify exact cpu type */ diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index d306034..58f5b0a 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -970,12 +970,6 @@ static struct map_desc dm365_io_desc[] = { .length = IO_SIZE, .type = MT_DEVICE }, - { - .virtual = SRAM_VIRT, - .pfn = __phys_to_pfn(0x00010000), - .length = SZ_32K, - .type = MT_MEMORY_NONCACHED, - }, }; static struct resource dm365_ks_resources[] = { diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 3949ed7..11e6481 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -663,12 +663,6 @@ static struct map_desc dm644x_io_desc[] = { .length = IO_SIZE, .type = MT_DEVICE }, - { - .virtual = SRAM_VIRT, - .pfn = __phys_to_pfn(0x00008000), - .length = SZ_16K, - .type = MT_MEMORY_NONCACHED, - }, }; /* Contents of JTAG ID register used to identify exact cpu type */ diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index a4365f7..57d697e 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -747,12 +747,6 @@ static struct map_desc dm646x_io_desc[] = { .length = IO_SIZE, .type = MT_DEVICE }, - { - .virtual = SRAM_VIRT, - .pfn = __phys_to_pfn(0x00010000), - .length = SZ_32K, - .type = MT_MEMORY_NONCACHED, - }, }; /* Contents of JTAG ID register used to identify exact cpu type */ diff --git a/arch/arm/mach-davinci/include/mach/common.h b/arch/arm/mach-davinci/include/mach/common.h index 665d049..16b5ec5 100644 --- a/arch/arm/mach-davinci/include/mach/common.h +++ b/arch/arm/mach-davinci/include/mach/common.h @@ -86,8 +86,6 @@ extern struct davinci_soc_info davinci_soc_info; extern void davinci_common_init(struct davinci_soc_info *soc_info); extern void davinci_init_ide(void); -/* standard place to map on-chip SRAMs; they *may* support DMA */ -#define SRAM_VIRT 0xfffe0000 #define SRAM_SIZE SZ_128K #endif /* __ARCH_ARM_MACH_DAVINCI_COMMON_H */ -- 1.7.4.1 From nsekhar at ti.com Wed Jun 1 11:56:35 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 1 Jun 2011 22:26:35 +0530 Subject: [PATCH v3 0/3] davinci: ioremap SRAM instead of iotables In-Reply-To: References: <20110601051753.GA5153@game.jcrosoft.org> Message-ID: Hi Ben, No need to send out a new revision just to add an ack. The maintainer would take care of collecting the acks when committing the patch. Thanks, Sekhar From nsekhar at ti.com Wed Jun 1 12:15:00 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 1 Jun 2011 22:45:00 +0530 Subject: [PATCH V2] davinci: da850: move input frequency to board specific files In-Reply-To: <1306488053-27978-1-git-send-email-christian.riesch@omicron.at> References: <1306312622-4901-1-git-send-email-christian.riesch@omicron.at> <1306488053-27978-1-git-send-email-christian.riesch@omicron.at> Message-ID: On Fri, May 27, 2011 at 14:50:53, Christian Riesch wrote: > From: Bob Dunlop > > Currently the input frequency of the SoC is hardcoded in the SoC specific > da850.c file to 24 MHz. Since the SoC accepts input frequencies in a wide > range from 12 to 50 MHz, boards with different oscillator/crystal > frequencies may be built. The same problem was fixed for dm6467/T EVMs with a different approach. This solution is much better than what was done for dm6467/T. So I am working on converting the dm6467/T code to this approach and should be able to post the updated patchset by tomorrow. Thanks, Sekhar From nsekhar at ti.com Thu Jun 2 12:11:15 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Thu, 2 Jun 2011 22:41:15 +0530 Subject: [PATCH 1/3] davinci: dm646x.h: include header files under mach/ after those in linux/ Message-ID: <1307034677-27236-1-git-send-email-nsekhar@ti.com> The general header file inclusion order followed in the kernel is to include files under linux/ first and then under mach/. The dm646x.h file has the opposite of this. Fix it. Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/include/mach/dm646x.h | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h index 7a27f3f..5e95b02 100644 --- a/arch/arm/mach-davinci/include/mach/dm646x.h +++ b/arch/arm/mach-davinci/include/mach/dm646x.h @@ -11,13 +11,14 @@ #ifndef __ASM_ARCH_DM646X_H #define __ASM_ARCH_DM646X_H -#include -#include #include #include #include #include +#include +#include + #define DM646X_EMAC_BASE (0x01C80000) #define DM646X_EMAC_MDIO_BASE (DM646X_EMAC_BASE + 0x4000) #define DM646X_EMAC_CNTRL_OFFSET (0x0000) -- 1.7.3.2 From nsekhar at ti.com Thu Jun 2 12:11:16 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Thu, 2 Jun 2011 22:41:16 +0530 Subject: [PATCH 2/3] davinci: dm6467/T EVM: pass reference clock rate to dm646x_init() In-Reply-To: <1307034677-27236-1-git-send-email-nsekhar@ti.com> References: <1307034677-27236-1-git-send-email-nsekhar@ti.com> Message-ID: <1307034677-27236-2-git-send-email-nsekhar@ti.com> From: Bob Dunlop The DM6467 and DM6467T EVMs use different reference clock frequencies. This difference is currently supported by having the SoC code call a public board routine which sets up the reference clock frequency. This does not scale as more boards are added. Instead, pass the reference clock frequency as a parameter to dm646x_init(). Boards which do not need the default reference clock changed can pass NULL to this function. Signed-off-by: Bob Dunlop Signed-off-by: Sekhar Nori --- I have marked Bob as the author of this patch since it is essentially his idea. Hope he doesnt mind that :) arch/arm/mach-davinci/board-dm646x-evm.c | 23 +++++++++++------------ arch/arm/mach-davinci/dm646x.c | 6 ++++-- arch/arm/mach-davinci/include/mach/common.h | 10 ++++++++++ arch/arm/mach-davinci/include/mach/dm646x.h | 4 ++-- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index f6ac9ba..37c49a9 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -719,9 +719,19 @@ static void __init cdce_clk_init(void) } } +#define DM646X_EVM_REF_FREQ 27000000 +#define DM6467T_EVM_REF_FREQ 33000000 + +static struct davinci_board_info dm646x_evm; + static void __init davinci_map_io(void) { - dm646x_init(); + if (machine_is_davinci_dm6467tevm()) + dm646x_evm.ref_clk_rate = DM6467T_EVM_REF_FREQ; + else + dm646x_evm.ref_clk_rate = DM646X_EVM_REF_FREQ; + + dm646x_init(&dm646x_evm); cdce_clk_init(); } @@ -785,17 +795,6 @@ static __init void evm_init(void) soc_info->emac_pdata->phy_id = DM646X_EVM_PHY_ID; } -#define DM646X_EVM_REF_FREQ 27000000 -#define DM6467T_EVM_REF_FREQ 33000000 - -void __init dm646x_board_setup_refclk(struct clk *clk) -{ - if (machine_is_davinci_dm6467tevm()) - clk->rate = DM6467T_EVM_REF_FREQ; - else - clk->rate = DM646X_EVM_REF_FREQ; -} - MACHINE_START(DAVINCI_DM6467_EVM, "DaVinci DM646x EVM") .boot_params = (0x80000100), .map_io = davinci_map_io, diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 1e0f809..871af17 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -899,9 +899,11 @@ int __init dm646x_init_edma(struct edma_rsv_info *rsv) return platform_device_register(&dm646x_edma_device); } -void __init dm646x_init(void) +void __init dm646x_init(struct davinci_board_info *board) { - dm646x_board_setup_refclk(&ref_clk); + if (board && board->ref_clk_rate) + ref_clk.rate = board->ref_clk_rate; + davinci_common_init(&davinci_soc_info_dm646x); } diff --git a/arch/arm/mach-davinci/include/mach/common.h b/arch/arm/mach-davinci/include/mach/common.h index a57cba2..fbe650b 100644 --- a/arch/arm/mach-davinci/include/mach/common.h +++ b/arch/arm/mach-davinci/include/mach/common.h @@ -83,6 +83,16 @@ struct davinci_soc_info { extern struct davinci_soc_info davinci_soc_info; +/* + * DaVinci board info. + * + * This structure is used to pass board information to + * early SoC specific initialization code. + */ +struct davinci_board_info { + unsigned long ref_clk_rate; +}; + extern void davinci_common_init(struct davinci_soc_info *soc_info); extern void davinci_init_ide(void); diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h index 5e95b02..b92db82 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 #include @@ -29,10 +30,9 @@ #define DM646X_ASYNC_EMIF_CONTROL_BASE 0x20008000 #define DM646X_ASYNC_EMIF_CS2_SPACE_BASE 0x42000000 -void __init dm646x_init(void); +void __init dm646x_init(struct davinci_board_info *board); 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); -- 1.7.3.2 From nsekhar at ti.com Thu Jun 2 12:11:17 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Thu, 2 Jun 2011 22:41:17 +0530 Subject: [PATCH v3 3/3] davinci: da850: move input frequency to board specific files In-Reply-To: <1307034677-27236-2-git-send-email-nsekhar@ti.com> References: <1307034677-27236-1-git-send-email-nsekhar@ti.com> <1307034677-27236-2-git-send-email-nsekhar@ti.com> Message-ID: <1307034677-27236-3-git-send-email-nsekhar@ti.com> From: Bob Dunlop Currently the input frequency of the SoC is hardcoded in the SoC specific da850.c file to 24 MHz. Since the SoC accepts input frequencies in a wide range from 12 to 50 MHz, boards with different oscillator/crystal frequencies may be built. This patch allows setting a different input frequency in the board specific files to support boards with oscillator/crystal frequencies other than 24 MHz. Signed-off-by: Bob Dunlop Signed-off-by: Christian Riesch Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/board-da850-evm.c | 2 +- arch/arm/mach-davinci/board-mityomapl138.c | 2 +- arch/arm/mach-davinci/board-omapl138-hawk.c | 2 +- arch/arm/mach-davinci/da850.c | 5 ++++- arch/arm/mach-davinci/include/mach/da8xx.h | 3 ++- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..231ff87 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1252,7 +1252,7 @@ console_initcall(da850_evm_console_init); static void __init da850_evm_map_io(void) { - da850_init(); + da850_init(NULL); } MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM") diff --git a/arch/arm/mach-davinci/board-mityomapl138.c b/arch/arm/mach-davinci/board-mityomapl138.c index 606a6f2..362770c 100644 --- a/arch/arm/mach-davinci/board-mityomapl138.c +++ b/arch/arm/mach-davinci/board-mityomapl138.c @@ -561,7 +561,7 @@ console_initcall(mityomapl138_console_init); static void __init mityomapl138_map_io(void) { - da850_init(); + da850_init(NULL); } MACHINE_START(MITYOMAPL138, "MityDSP-L138/MityARM-1808") diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index 67c38d0..c43a6c3 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -334,7 +334,7 @@ console_initcall(omapl138_hawk_console_init); static void __init omapl138_hawk_map_io(void) { - da850_init(); + da850_init(NULL); } MACHINE_START(OMAPL138_HAWKBOARD, "AM18x/OMAP-L138 Hawkboard") diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 133aac4..7f94d77 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -1104,10 +1104,13 @@ static struct davinci_soc_info davinci_soc_info_da850 = { .reset_device = &da8xx_wdt_device, }; -void __init da850_init(void) +void __init da850_init(struct davinci_board_info *board) { unsigned int v; + if (board && board->ref_clk_rate) + ref_clk.rate = board->ref_clk_rate; + davinci_common_init(&davinci_soc_info_da850); da8xx_syscfg0_base = ioremap(DA8XX_SYSCFG0_BASE, SZ_4K); diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index ad64da7..90227ce 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -70,7 +71,7 @@ extern unsigned int da850_max_speed; #define DA8XX_ARM_RAM_BASE 0xffff0000 void __init da830_init(void); -void __init da850_init(void); +void __init da850_init(struct davinci_board_info *info); int da830_register_edma(struct edma_rsv_info *rsv); int da850_register_edma(struct edma_rsv_info *rsv[2]); -- 1.7.3.2 From nsekhar at ti.com Thu Jun 2 12:21:58 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 2 Jun 2011 22:51:58 +0530 Subject: [PATCH 1/1] davinci: dm646x: move vpif related code to driver core header from platform In-Reply-To: <1305899929-2509-1-git-send-email-manjunath.hadli@ti.com> References: <1305899929-2509-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Hi Mauro, On Fri, May 20, 2011 at 19:28:49, Hadli, Manjunath wrote: > move vpif related code for capture and display drivers > from dm646x platform header file to vpif.h as these definitions > are related to driver code more than the platform or board. > > Signed-off-by: Manjunath Hadli Will you be taking this patch through your tree? If not, with your ack, I can queue it for inclusion through the ARM tree. Thanks, Sekhar > --- > 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.h | 73 +++++++++++++++++++++++++++ > 5 files changed, 77 insertions(+), 53 deletions(-) > create mode 100644 include/media/davinci/vpif.h > > diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h > index 7a27f3f..245a1c0 100644 > --- a/arch/arm/mach-davinci/include/mach/dm646x.h > +++ b/arch/arm/mach-davinci/include/mach/dm646x.h > @@ -17,6 +17,7 @@ > #include > #include > #include > +#include > > #define DM646X_EMAC_BASE (0x01C80000) > #define DM646X_EMAC_MDIO_BASE (DM646X_EMAC_BASE + 0x4000) > @@ -36,58 +37,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..e76dded 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 7a4196d..fa50b6b 100644 > --- a/drivers/media/video/davinci/vpif_capture.h > +++ b/drivers/media/video/davinci/vpif_capture.h > @@ -28,7 +28,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 b53aaa8..b531a01 100644 > --- a/drivers/media/video/davinci/vpif_display.h > +++ b/drivers/media/video/davinci/vpif_display.h > @@ -23,6 +23,7 @@ > #include > #include > #include > +#include > > #include "vpif.h" > > diff --git a/include/media/davinci/vpif.h b/include/media/davinci/vpif.h > new file mode 100644 > index 0000000..e4a4dc1 > --- /dev/null > +++ b/include/media/davinci/vpif.h > @@ -0,0 +1,73 @@ > +/* > + * 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_INC_H > +#define _VPIF_INC_H > + > +#include > + > +#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_INC_H */ > -- > 1.6.2.4 > > _______________________________________________ > 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 nsekhar at ti.com Fri Jun 3 06:32:11 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 3 Jun 2011 17:02:11 +0530 Subject: [PATCH 2/3] davinci: dm6467/T EVM: pass reference clock rate to dm646x_init() In-Reply-To: References: <1307034677-27236-1-git-send-email-nsekhar@ti.com> <1307034677-27236-2-git-send-email-nsekhar@ti.com> Message-ID: Hi Olof, On Thu, Jun 02, 2011 at 23:31:05, Olof Johansson wrote: > Do you foresee needing more members of the davinci_board_info struct > in the near future? If so, this approach is OK. If this is the only > anticipated use today, just passing in the refclk rate as an integer > argument would be just as clean, with less infrastructure. There was some discussion on passing PLL configuration information too. So yes, the structure is likely extended in future. Also, most boards end up using the reference clock used by the EVM. In that case, with this approach, most boards just pass NULL instead of replicating the reference clock frequency in all board files. Thanks, Sekhar From aj4manu at gmail.com Sat Jun 4 13:03:14 2011 From: aj4manu at gmail.com (Ajay Cyril) Date: Sat, 4 Jun 2011 23:33:14 +0530 Subject: DM365 IPNC - DVSDK Message-ID: Hi I am running the IPNC software on a DVEVM board with the DM365.(both purchased and registered). I now want to achieve USB streaming video within the IPNC framework. So my questions are 1. Are the UVC drivers already present in the LSP or do i need to add them and rebuild the kernel? If i have to add,how do i go about it? 2. I also need to include a application to view the USB video in the application layer right? 3.Can i flash the linux image independent of the IPNC software on top? Can someone help me on this? Thank you -------------- next part -------------- An HTML attachment was scrubbed... URL: From nsekhar at ti.com Sun Jun 5 06:09:28 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Sun, 5 Jun 2011 16:39:28 +0530 Subject: [PATCH 1/3] davinci: dm646x.h: include header files under mach/ after those in linux/ Message-ID: <94ab47e0d35e30c2e503b5fed887db7097f8744f.1307270971.git.nsekhar@ti.com> The general header file inclusion order followed in the kernel is to include files under linux/ first and then under mach/. The dm646x.h file has the opposite of this. Fix it. Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/include/mach/dm646x.h | 5 +++-- 1 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h index 7a27f3f..5e95b02 100644 --- a/arch/arm/mach-davinci/include/mach/dm646x.h +++ b/arch/arm/mach-davinci/include/mach/dm646x.h @@ -11,13 +11,14 @@ #ifndef __ASM_ARCH_DM646X_H #define __ASM_ARCH_DM646X_H -#include -#include #include #include #include #include +#include +#include + #define DM646X_EMAC_BASE (0x01C80000) #define DM646X_EMAC_MDIO_BASE (DM646X_EMAC_BASE + 0x4000) #define DM646X_EMAC_CNTRL_OFFSET (0x0000) -- 1.7.3.2 From nsekhar at ti.com Sun Jun 5 06:09:29 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Sun, 5 Jun 2011 16:39:29 +0530 Subject: [PATCH v2 2/3] davinci: dm6467/T EVM: pass reference clock rate to dm646x_init() In-Reply-To: <94ab47e0d35e30c2e503b5fed887db7097f8744f.1307270971.git.nsekhar@ti.com> References: <94ab47e0d35e30c2e503b5fed887db7097f8744f.1307270971.git.nsekhar@ti.com> Message-ID: <0ac24746aa643655b37c72b9829aa4717a965bd0.1307270971.git.nsekhar@ti.com> From: Bob Dunlop The DM6467 and DM6467T EVMs use different reference clock frequencies. This difference is currently supported by having the SoC code call a public board routine which sets up the reference clock frequency. This does not scale as more boards are added. Instead, pass the reference clock frequency as a parameter to dm646x_init(). Boards which do not need the default reference clock changed can pass NULL to this function. Changelog: v2: Made board information structure a local variable. Signed-off-by: Bob Dunlop Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/board-dm646x-evm.c | 23 +++++++++++------------ arch/arm/mach-davinci/dm646x.c | 6 ++++-- arch/arm/mach-davinci/include/mach/common.h | 10 ++++++++++ arch/arm/mach-davinci/include/mach/dm646x.h | 4 ++-- 4 files changed, 27 insertions(+), 16 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index f6ac9ba..c41398b 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -719,9 +719,19 @@ static void __init cdce_clk_init(void) } } +#define DM646X_EVM_REF_FREQ 27000000 +#define DM6467T_EVM_REF_FREQ 33000000 + static void __init davinci_map_io(void) { - dm646x_init(); + struct davinci_board_info info; + + if (machine_is_davinci_dm6467tevm()) + info.ref_clk_rate = DM6467T_EVM_REF_FREQ; + else + info.ref_clk_rate = DM646X_EVM_REF_FREQ; + + dm646x_init(&info); cdce_clk_init(); } @@ -785,17 +795,6 @@ static __init void evm_init(void) soc_info->emac_pdata->phy_id = DM646X_EVM_PHY_ID; } -#define DM646X_EVM_REF_FREQ 27000000 -#define DM6467T_EVM_REF_FREQ 33000000 - -void __init dm646x_board_setup_refclk(struct clk *clk) -{ - if (machine_is_davinci_dm6467tevm()) - clk->rate = DM6467T_EVM_REF_FREQ; - else - clk->rate = DM646X_EVM_REF_FREQ; -} - MACHINE_START(DAVINCI_DM6467_EVM, "DaVinci DM646x EVM") .boot_params = (0x80000100), .map_io = davinci_map_io, diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 1e0f809..871af17 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -899,9 +899,11 @@ int __init dm646x_init_edma(struct edma_rsv_info *rsv) return platform_device_register(&dm646x_edma_device); } -void __init dm646x_init(void) +void __init dm646x_init(struct davinci_board_info *board) { - dm646x_board_setup_refclk(&ref_clk); + if (board && board->ref_clk_rate) + ref_clk.rate = board->ref_clk_rate; + davinci_common_init(&davinci_soc_info_dm646x); } diff --git a/arch/arm/mach-davinci/include/mach/common.h b/arch/arm/mach-davinci/include/mach/common.h index a57cba2..fbe650b 100644 --- a/arch/arm/mach-davinci/include/mach/common.h +++ b/arch/arm/mach-davinci/include/mach/common.h @@ -83,6 +83,16 @@ struct davinci_soc_info { extern struct davinci_soc_info davinci_soc_info; +/* + * DaVinci board info. + * + * This structure is used to pass board information to + * early SoC specific initialization code. + */ +struct davinci_board_info { + unsigned long ref_clk_rate; +}; + extern void davinci_common_init(struct davinci_soc_info *soc_info); extern void davinci_init_ide(void); diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h index 5e95b02..b92db82 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 #include @@ -29,10 +30,9 @@ #define DM646X_ASYNC_EMIF_CONTROL_BASE 0x20008000 #define DM646X_ASYNC_EMIF_CS2_SPACE_BASE 0x42000000 -void __init dm646x_init(void); +void __init dm646x_init(struct davinci_board_info *board); 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); -- 1.7.3.2 From nsekhar at ti.com Sun Jun 5 06:09:30 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Sun, 5 Jun 2011 16:39:30 +0530 Subject: [PATCH v3 3/3] davinci: da850: move input frequency to board specific files In-Reply-To: <94ab47e0d35e30c2e503b5fed887db7097f8744f.1307270971.git.nsekhar@ti.com> References: <94ab47e0d35e30c2e503b5fed887db7097f8744f.1307270971.git.nsekhar@ti.com> Message-ID: <09a658dc764fb77c8080325cea17a73507cbe166.1307270971.git.nsekhar@ti.com> From: Bob Dunlop Currently the input frequency of the SoC is hardcoded in the SoC specific da850.c file to 24 MHz. Since the SoC accepts input frequencies in a wide range from 12 to 50 MHz, boards with different oscillator/crystal frequencies may be built. This patch allows setting a different input frequency in the board specific files to support boards with oscillator/crystal frequencies other than 24 MHz. Changelog: v3: Move board information structure to common.h as it is required for other SoCs as well. v2: Pass reference clock information as part of a structure instead of passing it as a number. Signed-off-by: Bob Dunlop Signed-off-by: Christian Riesch Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/board-da850-evm.c | 2 +- arch/arm/mach-davinci/board-mityomapl138.c | 2 +- arch/arm/mach-davinci/board-omapl138-hawk.c | 2 +- arch/arm/mach-davinci/da850.c | 5 ++++- arch/arm/mach-davinci/include/mach/da8xx.h | 3 ++- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..231ff87 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1252,7 +1252,7 @@ console_initcall(da850_evm_console_init); static void __init da850_evm_map_io(void) { - da850_init(); + da850_init(NULL); } MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM") diff --git a/arch/arm/mach-davinci/board-mityomapl138.c b/arch/arm/mach-davinci/board-mityomapl138.c index 606a6f2..362770c 100644 --- a/arch/arm/mach-davinci/board-mityomapl138.c +++ b/arch/arm/mach-davinci/board-mityomapl138.c @@ -561,7 +561,7 @@ console_initcall(mityomapl138_console_init); static void __init mityomapl138_map_io(void) { - da850_init(); + da850_init(NULL); } MACHINE_START(MITYOMAPL138, "MityDSP-L138/MityARM-1808") diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index 67c38d0..c43a6c3 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -334,7 +334,7 @@ console_initcall(omapl138_hawk_console_init); static void __init omapl138_hawk_map_io(void) { - da850_init(); + da850_init(NULL); } MACHINE_START(OMAPL138_HAWKBOARD, "AM18x/OMAP-L138 Hawkboard") diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 133aac4..7f94d77 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -1104,10 +1104,13 @@ static struct davinci_soc_info davinci_soc_info_da850 = { .reset_device = &da8xx_wdt_device, }; -void __init da850_init(void) +void __init da850_init(struct davinci_board_info *board) { unsigned int v; + if (board && board->ref_clk_rate) + ref_clk.rate = board->ref_clk_rate; + davinci_common_init(&davinci_soc_info_da850); da8xx_syscfg0_base = ioremap(DA8XX_SYSCFG0_BASE, SZ_4K); diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index ad64da7..90227ce 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -70,7 +71,7 @@ extern unsigned int da850_max_speed; #define DA8XX_ARM_RAM_BASE 0xffff0000 void __init da830_init(void); -void __init da850_init(void); +void __init da850_init(struct davinci_board_info *info); int da830_register_edma(struct edma_rsv_info *rsv); int da850_register_edma(struct edma_rsv_info *rsv[2]); -- 1.7.3.2 From nsekhar at ti.com Sun Jun 5 06:24:32 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Sun, 5 Jun 2011 16:54:32 +0530 Subject: [PATCH 2/3] davinci: dm6467/T EVM: pass reference clock rate to dm646x_init() In-Reply-To: References: <1307034677-27236-1-git-send-email-nsekhar@ti.com> <1307034677-27236-2-git-send-email-nsekhar@ti.com> Message-ID: On Fri, Jun 03, 2011 at 22:29:26, Olof Johansson wrote: > Only change I would like to request in that case (sorry, not > commenting on the patch directly here), is to mark dm646x_evm > __initdata (or even make it a local variable in the function), so for > multiboard kernels you won't need to keep the static struct around for > hardware you're not running on. > Fixed that and sent a new version. Thanks for the review. Regards, Sekhar From nsekhar at ti.com Sun Jun 5 07:08:42 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Sun, 5 Jun 2011 17:38:42 +0530 Subject: [PATCH] davinci: make PCM platform devices static Message-ID: Make the PCM device structures used in devices.c and devices-da8xx.c static as they are used only in the respective files. This was found when trying to build a single image for DaVinci and DA8x devices using runtime P2V support. Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/devices-da8xx.c | 2 +- arch/arm/mach-davinci/devices.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index 87aa19e..8c3cdc1 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -496,7 +496,7 @@ static struct platform_device da850_mcasp_device = { .resource = da850_mcasp_resources, }; -struct platform_device davinci_pcm_device = { +static struct platform_device davinci_pcm_device = { .name = "davinci-pcm-audio", .id = -1, }; diff --git a/arch/arm/mach-davinci/devices.c b/arch/arm/mach-davinci/devices.c index 8f4f736..806a2f0 100644 --- a/arch/arm/mach-davinci/devices.c +++ b/arch/arm/mach-davinci/devices.c @@ -298,7 +298,7 @@ static void davinci_init_wdt(void) /*-------------------------------------------------------------------------*/ -struct platform_device davinci_pcm_device = { +static struct platform_device davinci_pcm_device = { .name = "davinci-pcm-audio", .id = -1, }; -- 1.7.3.2 From sshtylyov at mvista.com Sun Jun 5 08:00:04 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Sun, 05 Jun 2011 17:00:04 +0400 Subject: [PATCH 2/3] davinci: dm6467/T EVM: pass reference clock rate to dm646x_init() In-Reply-To: <1307034677-27236-2-git-send-email-nsekhar@ti.com> References: <1307034677-27236-1-git-send-email-nsekhar@ti.com> <1307034677-27236-2-git-send-email-nsekhar@ti.com> Message-ID: <4DEB7DD4.5050706@mvista.com> Hello. On 02-06-2011 21:11, Sekhar Nori wrote: > From: Bob Dunlop > The DM6467 and DM6467T EVMs use different reference clock > frequencies. This difference is currently supported by having > the SoC code call a public board routine which sets up the reference > clock frequency. This does not scale as more boards are added. > Instead, pass the reference clock frequency as a parameter > to dm646x_init(). Boards which do not need the default reference > clock changed can pass NULL to this function. > Signed-off-by: Bob Dunlop > Signed-off-by: Sekhar Nori > --- > I have marked Bob as the author of this patch since it is essentially > his idea. Hope he doesnt mind that :) I don't think you should ascribe *your* patch to Bob. There's Suggested-by: line if you want to credit Bob. WBR, Sergei From nsekhar at ti.com Mon Jun 6 06:31:06 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 6 Jun 2011 17:01:06 +0530 Subject: [PATCH 2/3] davinci: dm6467/T EVM: pass reference clock rate to dm646x_init() In-Reply-To: <4DEB7DD4.5050706@mvista.com> References: <1307034677-27236-1-git-send-email-nsekhar@ti.com> <1307034677-27236-2-git-send-email-nsekhar@ti.com> <4DEB7DD4.5050706@mvista.com> Message-ID: On Sun, Jun 05, 2011 at 18:30:04, Sergei Shtylyov wrote: > Hello. > > On 02-06-2011 21:11, Sekhar Nori wrote: > > > From: Bob Dunlop > > > The DM6467 and DM6467T EVMs use different reference clock > > frequencies. This difference is currently supported by having > > the SoC code call a public board routine which sets up the reference > > clock frequency. This does not scale as more boards are added. > > > Instead, pass the reference clock frequency as a parameter > > to dm646x_init(). Boards which do not need the default reference > > clock changed can pass NULL to this function. > > > Signed-off-by: Bob Dunlop > > Signed-off-by: Sekhar Nori > > --- > > I have marked Bob as the author of this patch since it is essentially > > his idea. Hope he doesnt mind that :) > > I don't think you should ascribe *your* patch to Bob. There's > Suggested-by: line if you want to credit Bob. Well, in this case I actually took a patch authored by Bob, split it into two and moved around the code to other files. But there is also code written by me included in this patch. Bob, do you have an opinion on this? If not, I will go with what Sergei is suggesting. Thanks, Sekhar From bob.dunlop at xyzzy.org.uk Mon Jun 6 07:25:52 2011 From: bob.dunlop at xyzzy.org.uk (Bob Dunlop) Date: Mon, 6 Jun 2011 13:25:52 +0100 Subject: [PATCH 2/3] davinci: dm6467/T EVM: pass reference clock rate to dm646x_init() In-Reply-To: References: <1307034677-27236-1-git-send-email-nsekhar@ti.com> <1307034677-27236-2-git-send-email-nsekhar@ti.com> <4DEB7DD4.5050706@mvista.com> Message-ID: <20110606122552.GA2729@xyzzy.org.uk> Hi, On Mon, Jun 06 at 05:01, Nori, Sekhar wrote: > On Sun, Jun 05, 2011 at 18:30:04, Sergei Shtylyov wrote: > > I don't think you should ascribe *your* patch to Bob. There's > > Suggested-by: line if you want to credit Bob. > > Bob, do you have an opinion on this? If not, I will go with what > Sergei is suggesting. I have no problem one way or the other, we all know who we are. Anyway I'd like to propose a simpler patch that covers both boards in one. Fewer files touched than the original da850 patch. Add the ref_clk_rate parameter to the davinci_soc_info structure and perform the clock adjustment in davinci_common_init(). The code assumes that if which to tweak the ref_clk it will be the first one listed in cpu_clocks[] which is the case for all boards sofar. The only downside is you need to export the davinci_soc_info_dm646x struct in order to tweak it. I don't have a dm646x to test this with. Signed-off-by: Bob Dunlop diff -Naur linux-2.6-arm-orig/arch/arm/mach-davinci//board-dm646x-evm.c linux-2.6-arm/arch/arm/mach-davinci//board-dm646x-evm.c --- linux-2.6-arm-orig/arch/arm/mach-davinci//board-dm646x-evm.c 2011-02-09 08:37:02.000000000 +0000 +++ linux-2.6-arm/arch/arm/mach-davinci//board-dm646x-evm.c 2011-06-06 12:30:12.000000000 +0100 @@ -719,8 +719,16 @@ } } +#define DM646X_EVM_REF_FREQ 27000000 +#define DM6467T_EVM_REF_FREQ 33000000 + static void __init davinci_map_io(void) { + if (machine_is_davinci_dm6467tevm()) + davinci_soc_info_dm646x.ref_clk_rate = DM6467T_EVM_REF_FREQ; + else + davinci_soc_info_dm646x.ref_clk_rate = DM646X_EVM_REF_FREQ; + dm646x_init(); cdce_clk_init(); } @@ -785,17 +793,6 @@ soc_info->emac_pdata->phy_id = DM646X_EVM_PHY_ID; } -#define DM646X_EVM_REF_FREQ 27000000 -#define DM6467T_EVM_REF_FREQ 33000000 - -void __init dm646x_board_setup_refclk(struct clk *clk) -{ - if (machine_is_davinci_dm6467tevm()) - clk->rate = DM6467T_EVM_REF_FREQ; - else - clk->rate = DM646X_EVM_REF_FREQ; -} - MACHINE_START(DAVINCI_DM6467_EVM, "DaVinci DM646x EVM") .boot_params = (0x80000100), .map_io = davinci_map_io, diff -Naur linux-2.6-arm-orig/arch/arm/mach-davinci//common.c linux-2.6-arm/arch/arm/mach-davinci//common.c --- linux-2.6-arm-orig/arch/arm/mach-davinci//common.c 2011-02-09 08:37:02.000000000 +0000 +++ linux-2.6-arm/arch/arm/mach-davinci//common.c 2011-06-06 12:00:14.000000000 +0100 @@ -106,6 +106,10 @@ goto err; if (davinci_soc_info.cpu_clks) { + if(davinci_soc_info.ref_clk_rate) + davinci_soc_info.cpu_clks->clk->rate + = davinci_soc_info.ref_clk_rate; + ret = davinci_clk_init(davinci_soc_info.cpu_clks); if (ret != 0) diff -Naur linux-2.6-arm-orig/arch/arm/mach-davinci//dm646x.c linux-2.6-arm/arch/arm/mach-davinci//dm646x.c --- linux-2.6-arm-orig/arch/arm/mach-davinci//dm646x.c 2011-02-09 08:37:02.000000000 +0000 +++ linux-2.6-arm/arch/arm/mach-davinci//dm646x.c 2011-06-06 12:24:25.000000000 +0100 @@ -825,7 +825,7 @@ }, }; -static struct davinci_soc_info davinci_soc_info_dm646x = { +struct davinci_soc_info davinci_soc_info_dm646x = { .io_desc = dm646x_io_desc, .io_desc_num = ARRAY_SIZE(dm646x_io_desc), .jtag_id_reg = 0x01c40028, @@ -901,7 +901,6 @@ void __init dm646x_init(void) { - dm646x_board_setup_refclk(&ref_clk); davinci_common_init(&davinci_soc_info_dm646x); } diff -Naur linux-2.6-arm-orig/arch/arm/mach-davinci//include/mach/common.h linux-2.6-arm/arch/arm/mach-davinci//include/mach/common.h --- linux-2.6-arm-orig/arch/arm/mach-davinci//include/mach/common.h 2011-02-09 08:37:02.000000000 +0000 +++ linux-2.6-arm/arch/arm/mach-davinci//include/mach/common.h 2011-06-06 11:52:56.000000000 +0100 @@ -79,6 +79,7 @@ unsigned sram_len; struct platform_device *reset_device; void (*reset)(struct platform_device *); + unsigned long ref_clk_rate; }; extern struct davinci_soc_info davinci_soc_info; diff -Naur linux-2.6-arm-orig/arch/arm/mach-davinci//include/mach/dm646x.h linux-2.6-arm/arch/arm/mach-davinci//include/mach/dm646x.h --- linux-2.6-arm-orig/arch/arm/mach-davinci//include/mach/dm646x.h 2011-02-09 08:37:02.000000000 +0000 +++ linux-2.6-arm/arch/arm/mach-davinci//include/mach/dm646x.h 2011-06-06 12:23:02.000000000 +0100 @@ -31,7 +31,6 @@ 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); @@ -92,3 +91,5 @@ struct vpif_capture_config *); +extern struct davinci_soc_info davinci_soc_info_dm646x; + #endif /* __ASM_ARCH_DM646X_H */ -- Bob Dunlop From nsekhar at ti.com Mon Jun 6 08:02:21 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 6 Jun 2011 18:32:21 +0530 Subject: [PATCH 2/3] davinci: dm6467/T EVM: pass reference clock rate to dm646x_init() In-Reply-To: <20110606122552.GA2729@xyzzy.org.uk> References: <1307034677-27236-1-git-send-email-nsekhar@ti.com> <1307034677-27236-2-git-send-email-nsekhar@ti.com> <4DEB7DD4.5050706@mvista.com> <20110606122552.GA2729@xyzzy.org.uk> Message-ID: Hi Bob, On Mon, Jun 06, 2011 at 17:55:52, Bob Dunlop wrote: > Hi, > > On Mon, Jun 06 at 05:01, Nori, Sekhar wrote: > > On Sun, Jun 05, 2011 at 18:30:04, Sergei Shtylyov wrote: > > > I don't think you should ascribe *your* patch to Bob. There's > > > Suggested-by: line if you want to credit Bob. > > > > Bob, do you have an opinion on this? If not, I will go with what > > Sergei is suggesting. > > I have no problem one way or the other, we all know who we are. Okay, I will do what Sergei is suggesting then. > > Anyway I'd like to propose a simpler patch that covers both boards in one. > Fewer files touched than the original da850 patch. > > Add the ref_clk_rate parameter to the davinci_soc_info structure and > perform the clock adjustment in davinci_common_init(). The code assumes Lets not extend davinci_soc_info any further. We need to be looking at ways of reducing its usage (for example by making GPIO code use platform device). Also, refclk is a board information not soc information so placing it there makes it misplaced. > that if which to tweak the ref_clk it will be the first one listed in > cpu_clocks[] which is the case for all boards sofar. > > The only downside is you need to export the davinci_soc_info_dm646x struct > in order to tweak it. That's not very elegant as well. SoC information structure for a given SoC should be private to the SoC specific file. Making it public will open it up for abuse. Thanks, Sekhar From khilman at ti.com Mon Jun 6 17:44:59 2011 From: khilman at ti.com (Kevin Hilman) Date: Mon, 06 Jun 2011 15:44:59 -0700 Subject: [PATCH V2] davinci: da850: move input frequency to board specific files In-Reply-To: <1306488053-27978-1-git-send-email-christian.riesch@omicron.at> (Christian Riesch's message of "Fri, 27 May 2011 11:20:53 +0200") References: <1306312622-4901-1-git-send-email-christian.riesch@omicron.at> <1306488053-27978-1-git-send-email-christian.riesch@omicron.at> Message-ID: <874o42wpec.fsf@ti.com> Christian Riesch writes: > From: Bob Dunlop > > Currently the input frequency of the SoC is hardcoded in the SoC specific > da850.c file to 24 MHz. Since the SoC accepts input frequencies in a wide > range from 12 to 50 MHz, boards with different oscillator/crystal > frequencies may be built. > > This patch allows setting a different input frequency in the board > specific files to support boards with oscillator/crystal frequencies other > than 24 MHz. > > Signed-off-by: Bob Dunlop > Signed-off-by: Christian Riesch Why not allow board code to just do a clk_set_rate()? Currently the ref_clk struct clk does not have a .set_rate method implemented, but that should be easy enough to add. Then the default ref_clk.rate would stay the 24MHz, but any boards that want to override that simply use clk_get(), clk_set_rate(), clk_put() Kevin > --- > > Hi, > in private email Bob Dunlop suggested to pass a pointer to a small > structure instead of the frequency value to allow future expansions, > e.g., for the OPP list. Therefore I submit his patch as V2. > Christian > > arch/arm/mach-davinci/board-da850-evm.c | 2 +- > arch/arm/mach-davinci/board-mityomapl138.c | 2 +- > arch/arm/mach-davinci/board-omapl138-hawk.c | 2 +- > arch/arm/mach-davinci/da850.c | 5 ++++- > arch/arm/mach-davinci/include/mach/da8xx.h | 6 +++++- > 5 files changed, 12 insertions(+), 5 deletions(-) > > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index a7b41bf..231ff87 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -1252,7 +1252,7 @@ console_initcall(da850_evm_console_init); > > static void __init da850_evm_map_io(void) > { > - da850_init(); > + da850_init(NULL); > } > > MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM") > diff --git a/arch/arm/mach-davinci/board-mityomapl138.c b/arch/arm/mach-davinci/board-mityomapl138.c > index 606a6f2..362770c 100644 > --- a/arch/arm/mach-davinci/board-mityomapl138.c > +++ b/arch/arm/mach-davinci/board-mityomapl138.c > @@ -561,7 +561,7 @@ console_initcall(mityomapl138_console_init); > > static void __init mityomapl138_map_io(void) > { > - da850_init(); > + da850_init(NULL); > } > > MACHINE_START(MITYOMAPL138, "MityDSP-L138/MityARM-1808") > diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c > index 67c38d0..c43a6c3 100644 > --- a/arch/arm/mach-davinci/board-omapl138-hawk.c > +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c > @@ -334,7 +334,7 @@ console_initcall(omapl138_hawk_console_init); > > static void __init omapl138_hawk_map_io(void) > { > - da850_init(); > + da850_init(NULL); > } > > MACHINE_START(OMAPL138_HAWKBOARD, "AM18x/OMAP-L138 Hawkboard") > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 133aac4..ebd0603 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -1104,10 +1104,13 @@ static struct davinci_soc_info davinci_soc_info_da850 = { > .reset_device = &da8xx_wdt_device, > }; > > -void __init da850_init(void) > +void __init da850_init(struct da850_init_board_info *board) > { > unsigned int v; > > + if (board && board->ref_clk_rate) > + ref_clk.rate = board->ref_clk_rate; > + > davinci_common_init(&davinci_soc_info_da850); > > da8xx_syscfg0_base = ioremap(DA8XX_SYSCFG0_BASE, SZ_4K); > diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h > index ad64da7..66efc5d 100644 > --- a/arch/arm/mach-davinci/include/mach/da8xx.h > +++ b/arch/arm/mach-davinci/include/mach/da8xx.h > @@ -69,8 +69,12 @@ extern unsigned int da850_max_speed; > #define DA8XX_AEMIF_CTL_BASE 0x68000000 > #define DA8XX_ARM_RAM_BASE 0xffff0000 > > +struct da850_init_board_info { > + unsigned long ref_clk_rate; > +}; > + > void __init da830_init(void); > -void __init da850_init(void); > +void __init da850_init(struct da850_init_board_info *board); > > int da830_register_edma(struct edma_rsv_info *rsv); > int da850_register_edma(struct edma_rsv_info *rsv[2]); From bob.dunlop at xyzzy.org.uk Tue Jun 7 02:41:45 2011 From: bob.dunlop at xyzzy.org.uk (Bob Dunlop) Date: Tue, 7 Jun 2011 08:41:45 +0100 Subject: [PATCH 2/3] davinci: dm6467/T EVM: pass reference clock rate to dm646x_init() In-Reply-To: References: <1307034677-27236-1-git-send-email-nsekhar@ti.com> <1307034677-27236-2-git-send-email-nsekhar@ti.com> <4DEB7DD4.5050706@mvista.com> <20110606122552.GA2729@xyzzy.org.uk> Message-ID: <20110607074145.GA6593@xyzzy.org.uk> On Mon, Jun 06 at 06:32, Nori, Sekhar wrote: ... > > Add the ref_clk_rate parameter to the davinci_soc_info structure and > > perform the clock adjustment in davinci_common_init(). The code assumes > > Lets not extend davinci_soc_info any further. We need to be looking > at ways of reducing its usage (for example by making GPIO code use > platform device). > > Also, refclk is a board information not soc information so placing > it there makes it misplaced. Okay let's drop this idea. I must admit I was concerned about opening up the structure availability. -- Bob Dunlop From nsekhar at ti.com Tue Jun 7 05:39:33 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 7 Jun 2011 16:09:33 +0530 Subject: [PATCH V2] davinci: da850: move input frequency to board specific files In-Reply-To: <874o42wpec.fsf@ti.com> References: <1306312622-4901-1-git-send-email-christian.riesch@omicron.at> <1306488053-27978-1-git-send-email-christian.riesch@omicron.at> <874o42wpec.fsf@ti.com> Message-ID: Hi Kevin, On Tue, Jun 07, 2011 at 04:14:59, Hilman, Kevin wrote: > Christian Riesch writes: > > > From: Bob Dunlop > > > > Currently the input frequency of the SoC is hardcoded in the SoC specific > > da850.c file to 24 MHz. Since the SoC accepts input frequencies in a wide > > range from 12 to 50 MHz, boards with different oscillator/crystal > > frequencies may be built. > > > > This patch allows setting a different input frequency in the board > > specific files to support boards with oscillator/crystal frequencies other > > than 24 MHz. > > > > Signed-off-by: Bob Dunlop > > Signed-off-by: Christian Riesch > > Why not allow board code to just do a clk_set_rate()? > > Currently the ref_clk struct clk does not have a .set_rate method > implemented, but that should be easy enough to add. > > Then the default ref_clk.rate would stay the 24MHz, but any boards that > want to override that simply use clk_get(), clk_set_rate(), clk_put() That's certainly much more elegant, but this would mean the whole clock tree is traversed again on each boot. I am doing some measurements to see if there is any big difference in boot-time if this is done. Will get back with results. Thanks, Sekhar From khilman at ti.com Tue Jun 7 11:23:59 2011 From: khilman at ti.com (Kevin Hilman) Date: Tue, 07 Jun 2011 09:23:59 -0700 Subject: [PATCH V2] davinci: da850: move input frequency to board specific files In-Reply-To: (Sekhar Nori's message of "Tue, 7 Jun 2011 16:09:33 +0530") References: <1306312622-4901-1-git-send-email-christian.riesch@omicron.at> <1306488053-27978-1-git-send-email-christian.riesch@omicron.at> <874o42wpec.fsf@ti.com> Message-ID: <877h8xr4o0.fsf@ti.com> "Nori, Sekhar" writes: > Hi Kevin, > > On Tue, Jun 07, 2011 at 04:14:59, Hilman, Kevin wrote: >> Christian Riesch writes: >> >> > From: Bob Dunlop >> > >> > Currently the input frequency of the SoC is hardcoded in the SoC specific >> > da850.c file to 24 MHz. Since the SoC accepts input frequencies in a wide >> > range from 12 to 50 MHz, boards with different oscillator/crystal >> > frequencies may be built. >> > >> > This patch allows setting a different input frequency in the board >> > specific files to support boards with oscillator/crystal frequencies other >> > than 24 MHz. >> > >> > Signed-off-by: Bob Dunlop >> > Signed-off-by: Christian Riesch >> >> Why not allow board code to just do a clk_set_rate()? >> >> Currently the ref_clk struct clk does not have a .set_rate method >> implemented, but that should be easy enough to add. >> >> Then the default ref_clk.rate would stay the 24MHz, but any boards that >> want to override that simply use clk_get(), clk_set_rate(), clk_put() > > That's certainly much more elegant, but this would mean the whole > clock tree is traversed again on each boot. > > I am doing some measurements to see if there is any big difference > in boot-time if this is done. Will get back with results. I don't expect this to be a big boot-time impact. However, some of the clock.c assumptions might need to be updated as it currently is written from the perspective that the PLL clocks are the "root" clocks. Setting (and propagating) clock rates is what the clock framework is for, so adding a new interface to set a custom clock rate just doesn't seem right. I understand that the reference oscillator might be considered a special case, but if this can be done with the clock framework, it is much preferred. Kevin From Paul_Stuart at seektech.com Tue Jun 7 17:27:46 2011 From: Paul_Stuart at seektech.com (Paul Stuart) Date: Tue, 7 Jun 2011 15:27:46 -0700 Subject: DM365 GStreamer Resizer Config Setup happens...where? In-Reply-To: References: Message-ID: <7F1B6BBBDF05C649BBBA3C853F488A611AF21A10A0@Hawking.deepsea.com> Hi, In the case of a simple GStreamer Loop-Throught recipe like: gst-launch v4l2src input-src=composite !'video/x-raw-yuv,format=(fourcc)NV12,width=640,height=480' ! tidisplaysink2 video-standard=vga display-output=lcd Does anyone know how and where the resizer is configured to give me the 640x480 output? It just seems to automagically work. There is a resizer element in the TI plugins, but as you can see above, I haven't linked it into the pipeline. I've been digging through the source for tidisplaysink2 and don't see anything there, so I'm assuming some magic must happen in the v4l2src element, but have no evidence to back that up. Is v4l2 calling the Capture Setup in the DMAI? Any insight would be appreciated. Thanks! Paul -------------- next part -------------- An HTML attachment was scrubbed... URL: From nsekhar at ti.com Wed Jun 8 04:51:29 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 8 Jun 2011 15:21:29 +0530 Subject: [GIT PULL] DaVinci fixes for v3.0-rc Message-ID: Hi Russell, Can you please pull these two changes for v3.0-rc release? Thomas's patch fixes a regression because of generic irq chip support for DaVinci introduced in v3.0-rc1. My patch is not a regression fix but fixes an issue that has been existing for a while. Thanks, Sekhar The following changes since commit 59c5f46fbe01a00eedf54a23789634438bb80603: Linus Torvalds (1): Linux 3.0-rc2 are available in the git repository at: git://gitorious.org/linux-davinci/linux-davinci.git davinci-next Sekhar Nori (1): davinci: make PCM platform devices static Thomas Gleixner (1): arm: davinci: Fix fallout from generic irq chip conversion arch/arm/mach-davinci/devices-da8xx.c | 2 +- arch/arm/mach-davinci/devices.c | 2 +- arch/arm/mach-davinci/gpio.c | 7 ++++--- 3 files changed, 6 insertions(+), 5 deletions(-) From lamiaposta71 at gmail.com Wed Jun 8 06:56:38 2011 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Wed, 8 Jun 2011 13:56:38 +0200 Subject: DM365 GStreamer Resizer Config Setup happens...where? In-Reply-To: <7F1B6BBBDF05C649BBBA3C853F488A611AF21A10A0@Hawking.deepsea.com> References: <7F1B6BBBDF05C649BBBA3C853F488A611AF21A10A0@Hawking.deepsea.com> Message-ID: > gst-launch v4l2src input-src=composite > !?video/x-raw-yuv,format=(fourcc)NV12,width=640,height=480? ! tidisplaysink2 > video-standard=vga display-output=lcd > > > > Does anyone know how and where the resizer is configured to give me the > 640x480 output? change 320x240 in 640x480 and you'll have it. see TIVidResize gst-launch v4l2src --gst-debug=v4l2:5 num-buffers=1 always-copy=FALSE ! 'video/x-raw-yuv,width=720,height=480,format=(fourcc)UYVY,framerate=(fraction)25/1' ! TIVidResize contiguousInputFrame=TRUE ! 'video/x-raw-yuv,width=320,height=240,format=(fourcc)UYVY' ! TIImgenc1 resolution=320x240 iColorSpace=UYVY oColorSpace=YUV420P qValue=75 engineName=codecServer codecName=jpegenc ! filesink location=file13.jpg From nsekhar at ti.com Wed Jun 8 07:08:25 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 8 Jun 2011 17:38:25 +0530 Subject: [PATCH V2] davinci: da850: move input frequency to board specific files In-Reply-To: <877h8xr4o0.fsf@ti.com> References: <1306312622-4901-1-git-send-email-christian.riesch@omicron.at> <1306488053-27978-1-git-send-email-christian.riesch@omicron.at> <874o42wpec.fsf@ti.com> <877h8xr4o0.fsf@ti.com> Message-ID: On Tue, Jun 07, 2011 at 21:53:59, Hilman, Kevin wrote: > I don't expect this to be a big boot-time impact. You are right. Using PRINTK_TIME on DM365 I saw no noticeable boot-time change. When I profiled the code using do_gettimeofday(), I saw it was taking ~95 usecs to complete the propagation. I was mainly worried about all the recursion and reading of PLL and sysclk registers. Seems its not so bad. > However, some of the clock.c assumptions might need to be updated as it > currently is written from the perspective that the PLL clocks are the > "root" clocks. Hmm, just calling clk_set_rate() on refclk propagated the rate nicely across the tree. It seems DaVinci clock code is not in such a bad shape :) Or did I miss the concern? > Setting (and propagating) clock rates is what the clock framework is > for, so adding a new interface to set a custom clock rate just doesn't > seem right. I understand that the reference oscillator might be > considered a special case, but if this can be done with the clock > framework, it is much preferred. Okay. Will modify the DM6467/T EVM code to use this method instead. Thanks, Sekhar From bksingh at ti.com Wed Jun 8 07:38:16 2011 From: bksingh at ti.com (Singh, Brijesh) Date: Wed, 8 Jun 2011 07:38:16 -0500 Subject: DM365 GStreamer Resizer Config Setup happens...where? In-Reply-To: <7F1B6BBBDF05C649BBBA3C853F488A611AF21A10A0@Hawking.deepsea.com> References: <7F1B6BBBDF05C649BBBA3C853F488A611AF21A10A0@Hawking.deepsea.com> Message-ID: <70ABD7B1D427A94DA659B4C84B0F5D4A017E332338@dlee06.ent.ti.com> The resizing is done in v4l2src. The v4l2src has logic to configure IPIPE on-fly resizing mode. Look at the v4l2src, you can find the implementation details in dm36x_ipipe file. V4l2src source does not uses any of the DMAI API. Thanks Brijesh ________________________________ From: davinci-linux-open-source-bounces at linux.davincidsp.com [mailto:davinci-linux-open-source-bounces at linux.davincidsp.com] On Behalf Of Paul Stuart Sent: Tuesday, June 07, 2011 5:28 PM To: davinci-linux-open-source Subject: DM365 GStreamer Resizer Config Setup happens...where? Hi, In the case of a simple GStreamer Loop-Throught recipe like: gst-launch v4l2src input-src=composite !'video/x-raw-yuv,format=(fourcc)NV12,width=640,height=480' ! tidisplaysink2 video-standard=vga display-output=lcd Does anyone know how and where the resizer is configured to give me the 640x480 output? It just seems to automagically work. There is a resizer element in the TI plugins, but as you can see above, I haven't linked it into the pipeline. I've been digging through the source for tidisplaysink2 and don't see anything there, so I'm assuming some magic must happen in the v4l2src element, but have no evidence to back that up. Is v4l2 calling the Capture Setup in the DMAI? Any insight would be appreciated. Thanks! Paul -------------- next part -------------- An HTML attachment was scrubbed... URL: From sudhakar.raj at ti.com Wed Jun 8 08:02:35 2011 From: sudhakar.raj at ti.com (Rajashekhara, Sudhakar) Date: Wed, 8 Jun 2011 18:32:35 +0530 Subject: [PATCH] davinci: da850 EVM: read mac address from SPI flash Message-ID: <1307538155-27197-1-git-send-email-sudhakar.raj@ti.com> DA850/OMAP-L138 EMAC driver uses random mac address instead of a fixed one because the mac address is not stuffed into EMAC platform data. This patch provides a function which reads the mac address stored in SPI flash (registered as MTD device) and populates the EMAC platform data. The function which reads the mac address is registered as a callback which gets called upon addition of MTD device. NOTE: In case the MAC address stored in SPI flash is erased, follow the instructions at [1] to restore it. [1] http://processors.wiki.ti.com/index.php/GSG:_OMAP-L138_DVEVM_Additional_Procedures#Restoring_MAC_address_on_SPI_Flash Signed-off-by: Rajashekhara, Sudhakar --- arch/arm/mach-davinci/board-da850-evm.c | 20 ++++++++++++++++++++ 1 files changed, 20 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..3ff64b8 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -115,6 +115,23 @@ static struct spi_board_info da850evm_spi_info[] = { }, }; +static void da850_evm_m25p80_notify_add(struct mtd_info *mtd) +{ + char *mac_addr = davinci_soc_info.emac_pdata->mac_addr; + size_t retlen; + + if (!strcmp(mtd->name, "MAC-Address")) { + mtd->read(mtd, 0, ETH_ALEN, &retlen, mac_addr); + if (retlen == ETH_ALEN) + pr_info("Read MAC addr from SPI Flash: %pM\n", + mac_addr); + } +} + +static struct mtd_notifier da850evm_spi_notifier = { + .add = da850_evm_m25p80_notify_add, +}; + static struct mtd_partition da850_evm_norflash_partition[] = { { .name = "bootloaders + env", @@ -1237,6 +1254,9 @@ static __init void da850_evm_init(void) if (ret) pr_warning("da850_evm_init: spi 1 registration failed: %d\n", ret); + + if (mtd_has_partitions()) + register_mtd_user(&da850evm_spi_notifier); } #ifdef CONFIG_SERIAL_8250_CONSOLE -- 1.7.1 From Laurent.RUPIN at teamcast.com Thu Jun 9 04:56:50 2011 From: Laurent.RUPIN at teamcast.com (Laurent RUPIN) Date: Thu, 9 Jun 2011 11:56:50 +0200 Subject: Can not boot linux kernel 2.6.23 on TMS320DM355 DVEVM References: Message-ID: Hi, I can not boot linux kernel 2.6.33rc4 on OMAP-L138. I have the same problem. The execution is stopped at this asm line : add pc, r5, r0 ) @ call relocation code Have you found the solution? BR, Laurent -------------- next part -------------- An HTML attachment was scrubbed... URL: From christian.riesch at omicron.at Thu Jun 9 05:05:12 2011 From: christian.riesch at omicron.at (Christian Riesch) Date: Thu, 9 Jun 2011 12:05:12 +0200 Subject: [PATCH V2] davinci: da850: move input frequency to board specific files In-Reply-To: <874o42wpec.fsf@ti.com> References: <1306312622-4901-1-git-send-email-christian.riesch@omicron.at> <1306488053-27978-1-git-send-email-christian.riesch@omicron.at> <874o42wpec.fsf@ti.com> Message-ID: Hi Kevin, On Tue, Jun 7, 2011 at 12:44 AM, Kevin Hilman wrote: > Christian Riesch writes: > >> From: Bob Dunlop >> >> Currently the input frequency of the SoC is hardcoded in the SoC specific >> da850.c file to 24 MHz. Since the SoC accepts input frequencies in a wide >> range from 12 to 50 MHz, boards with different oscillator/crystal >> frequencies may be built. >> >> This patch allows setting a different input frequency in the board >> specific files to support boards with oscillator/crystal frequencies other >> than 24 MHz. >> >> Signed-off-by: Bob Dunlop >> Signed-off-by: Christian Riesch > > Why not allow board code to just do a clk_set_rate()? I'm fine with this method (In fact it was the first thing that I tried, I added a .set_rate method to ref_clk, it worked well for me). However I wonder whether first initializing the clock with the wrong value (24 MHz) and later correcting it via clk_set_rate() would break something. In the meantime, the data in the clock tree do not reflect the actual frequencies that are present on the SoC. Christian From sundaram at ti.com Thu Jun 9 07:39:28 2011 From: sundaram at ti.com (Raju, Sundaram) Date: Thu, 9 Jun 2011 18:09:28 +0530 Subject: [RFC] dmaengine: add new api for preparing simple slave transfer Message-ID: SDMA and EDMA are TI SoC specific DMA controllers. Their drivers have been maintained in the respective SoC folders till now. arch/arm/plat-omap/dma.c arch/arm/mach-davinci/dma.c I have gone through the existing offload engine (DMA) drivers in drivers/dma which do slave transfers. I would like to move SDMA and EDMA also to dmaengine framework. I believe that even though the dmaengine framework addresses and supports most of the required use cases of a client driver to a DMA controller, some extensions are required in it to make it still more generic. Current framework contains two APIs to prepare for slave transfers: struct dma_async_tx_descriptor *(*device_prep_slave_sg)( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_data_direction direction, unsigned long flags); struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_data_direction direction); and one control API. int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg); A simple single buffer transfer (i.e. non sg transfer) can be done only as a trivial case of the device_prep_slave_sg API. The client driver is expected to prepare a sg list and provide to the dmaengine API for that single buffer also. In a slave transfer, the client has to define all the buffer related attributes and the peripheral related attributes. The above 2 APIs in the dmaengine framework expect all the peripheral/slave related attributes to be filled in the dma_slave_config data structure. struct dma_slave_config { enum dma_data_direction direction; dma_addr_t src_addr; dma_addr_t dst_addr; enum dma_slave_buswidth src_addr_width; enum dma_slave_buswidth dst_addr_width; u32 src_maxburst; u32 dst_maxburst; }; This data structure is passed to the offload engine via the dma_chan data structure in its private pointer. Now coming to the buffer related attributes, sg list is a nice way to represent a disjoint buffer; all the offload engines in drivers/dma create a descriptor for each of the contiguous chunk in the sg list buffer and pass it to the controller. But many a times a client may want to transfer from a single buffer to the peripheral and most of the DMA controllers have the capability to transfer data in chunks/frames directly at a stretch. All the attributes of a buffer, which are required for the transfer can be programmed in single descriptor and submitted to the DMA controller. So I think the slave transfer API must also have a provision to pass the buffer configuration. The buffer attributes have to be passed directly as an argument in the prepare API, unlike dma_slave_config as they will be different for each buffer that is going to be transferred. It is a stretch and impractical to use a highly segmented buffer (like the one described below) in a sglist. This is because sg list itself is a representation of a disjoint buffer collection in terms of smaller buffers. Now then each of these smaller buffers can have different buffer configurations (like described below) and we are not going to go down that road now. Hence it makes sense to pass these buffer attributes for only a single buffer transfer and not a sg list. This can be done by OPTION #1 1. Adding a pointer of the dma_buffer_config data structure in the device_prep_slave_sg API 2. Ensuring that it will be ignored if a sg list passed. Only when a single buffer is passed (in the sg list) then this buffer configuration will be used. 3. Any client that wants to do a sg transfer can simply ignore this buffer configuration and pass NULL. The main disadvantage of this option is that all the existing drivers need to be updated since the API signature is changed. Now it might even be better to have a separate API for non sg transfers. This is OPTION #2 Advantages of this option are 1. No change required in the existing drivers that use device_prep_slave_sg API. 2. If any offload engine wants to prepare a simple buffer transfer differently and not as a trivial case of a sg list, this will be useful. I know this can be done using 2 different implementations inside the device_prep_slave_sg itself, but I think it's cleaner to have different APIs. I have provided the generic buffer configuration I can think of, here and also a new API definition, if it makes sense to have one. This buffer configuration might not be completely generic, and hence I ask all of you to please provide comments to improve it. Generic buffer description: A generic buffer can be split into number of frames which contain number of chunks inside them. The frames need not be contiguous, nor do the chunks inside a frame. ------------------------------------------------------------------- | Chunk 0 |ICG| Chunk 1 |ICG| ... |ICG| Chunk n | Frame 0 ------------------------------------------------------------------- | Inter Frame Gap | ------------------------------------------------------------------- | Chunk 0 |ICG| Chunk 1 |ICG| ... |ICG| Chunk n | Frame 1 ------------------------------------------------------------------- | Inter Frame Gap | ------------------------------------------------------------------- | ........ | ------------------------------------------------------------------- | Inter Frame Gap | ------------------------------------------------------------------- | Chunk 0 |ICG| Chunk 1 |ICG| ... |ICG| Chunk n | Frame m ------------------------------------------------------------------- Note: ICG = Inter Chunk Gap. struct dma_buffer_config { u32 chunk_size; /* number of bytes in a chunk */ u32 frame_size; /* number of chunks in a frame */ /* u32 n_frames; number of frames in the buffer */ u32 inter_chunk_gap; /* number of bytes between end of a chunk and the start of the next chunk */ u32 inter_frame_gap; /* number of bytes between end of a frame and the start of the next frame */ bool sync_rate; /* 0 - a sync event is required from the peripheral to transfer a chunk 1 - a sync event is required from the peripheral to transfer a frame */ }; The patch to add a new API for single buffer transfers alone: diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h @@ -491,6 +492,10 @@ struct dma_device { struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_data_direction direction, unsigned long flags); + struct dma_async_tx_descriptor *(*device_prep_slave)( + struct dma_chan *chan, dma_addr_t buf_addr, + unsigned int buf_len, struct dma_buffer_config *buf_prop, + enum dma_data_direction direction, unsigned long flags); struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_data_direction direction); The number of frames (n_frames) can always be determined using all other values in the dma_buffer_config along with the buffer length (buf_len) passed in the API. n_frames = buf_len / (chunk_sixe * frame_size); Regards, Sundaram From linux at arm.linux.org.uk Thu Jun 9 07:47:23 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Thu, 9 Jun 2011 13:47:23 +0100 Subject: [RFC] dmaengine: add new api for preparing simple slave transfer In-Reply-To: References: Message-ID: <20110609124723.GA24636@n2100.arm.linux.org.uk> Can you please re-post with sensible wrapping at or before column 72. I'm not manually reformatting your entire message just so I can find the relevant bits to reply to. Thanks. From sundaram at ti.com Thu Jun 9 11:01:56 2011 From: sundaram at ti.com (Raju, Sundaram) Date: Thu, 9 Jun 2011 21:31:56 +0530 Subject: [RFC] dmaengine: add new api for preparing simple slave transfer In-Reply-To: <20110609124723.GA24636@n2100.arm.linux.org.uk> References: <20110609124723.GA24636@n2100.arm.linux.org.uk> Message-ID: Here it is, with proper line wrapping; SDMA and EDMA are TI SoC specific DMA controllers. Their drivers have been maintained in the respective SoC folders till now. arch/arm/plat-omap/dma.c arch/arm/mach-davinci/dma.c I have gone through the existing offload engine (DMA) drivers in drivers/dma which do slave transfers. I would like to move SDMA and EDMA also to dmaengine framework. I believe that even though the dmaengine framework addresses and supports most of the required use cases of a client driver to a DMA controller, some extensions are required in it to make it still more generic. Current framework contains two APIs to prepare for slave transfers: struct dma_async_tx_descriptor *(*device_prep_slave_sg)( struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_data_direction direction, unsigned long flags); struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_data_direction direction); and one control API. int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd, unsigned long arg); A simple single buffer transfer (i.e. non sg transfer) can be done only as a trivial case of the device_prep_slave_sg API. The client driver is expected to prepare a sg list and provide to the dmaengine API for that single buffer also. In a slave transfer, the client has to define all the buffer related attributes and the peripheral related attributes. The above 2 APIs in the dmaengine framework expect all the peripheral/slave related attributes to be filled in the dma_slave_config data structure. struct dma_slave_config { enum dma_data_direction direction; dma_addr_t src_addr; dma_addr_t dst_addr; enum dma_slave_buswidth src_addr_width; enum dma_slave_buswidth dst_addr_width; u32 src_maxburst; u32 dst_maxburst; }; This data structure is passed to the offload engine via the dma_chan data structure in its private pointer. Now coming to the buffer related attributes, sg list is a nice way to represent a disjoint buffer; all the offload engines in drivers/dma create a descriptor for each of the contiguous chunk in the sg list buffer and pass it to the controller. But many a times a client may want to transfer from a single buffer to the peripheral and most of the DMA controllers have the capability to transfer data in chunks/frames directly at a stretch. All the attributes of a buffer, which are required for the transfer can be programmed in single descriptor and submitted to the DMA controller. So I think the slave transfer API must also have a provision to pass the buffer configuration. The buffer attributes have to be passed directly as an argument in the prepare API, unlike dma_slave_config as they will be different for each buffer that is going to be transferred. It is a stretch and impractical to use a highly segmented buffer (like the one described below) in a sglist. This is because sg list itself is a representation of a disjoint buffer collection in terms of smaller buffers. Now then each of these smaller buffers can have different buffer configurations (like described below) and we are not going to go down that road now. Hence it makes sense to pass these buffer attributes for only a single buffer transfer and not a sg list. This can be done by OPTION #1 1. Adding a pointer of the dma_buffer_config data structure in the device_prep_slave_sg API. 2. Ensuring that it will be ignored if a sg list passed. Only when a single buffer is passed (in the sg list) then this buffer configuration will be used. 3. Any client that wants to do a sg transfer can simply ignore this buffer configuration and pass NULL. The main disadvantage of this option is that all the existing drivers need to be updated since the API signature is changed. It might even be better to have a separate API for non sg transfers. This is OPTION #2 Advantages of this option are 1. No change required in the existing drivers that use device_prep_slave_sg API. 2. If any offload engine wants to prepare a simple buffer transfer differently and not as a trivial case of a sg list, this will be useful. I know this can be done using 2 different implementations inside the device_prep_slave_sg itself, but I think it's cleaner to have different APIs. I have provided the generic buffer configuration I can think of, here and also a new API definition, if it makes sense to have one. This buffer configuration might not be completely generic, and hence I ask all of you to please provide comments to improve it. Generic buffer description: A generic buffer can be split into number of frames which contain number of chunks inside them. The frames need not be contiguous, nor do the chunks inside a frame. ------------------------------------------------------------------- | Chunk 0 |ICG| Chunk 1 |ICG| ... |ICG| Chunk n | Frame 0 ------------------------------------------------------------------- | Inter Frame Gap | ------------------------------------------------------------------- | Chunk 0 |ICG| Chunk 1 |ICG| ... |ICG| Chunk n | Frame 1 ------------------------------------------------------------------- | Inter Frame Gap | ------------------------------------------------------------------- | ........ | ------------------------------------------------------------------- | Inter Frame Gap | ------------------------------------------------------------------- | Chunk 0 |ICG| Chunk 1 |ICG| ... |ICG| Chunk n | Frame m ------------------------------------------------------------------- Note: ICG = Inter Chunk Gap. struct dma_buffer_config { u32 chunk_size; /* number of bytes in a chunk */ u32 frame_size; /* number of chunks in a frame */ /* u32 n_frames; number of frames in the buffer */ u32 inter_chunk_gap; /* number of bytes between end of a chunk and the start of the next chunk */ u32 inter_frame_gap; /* number of bytes between end of a frame and the start of the next frame */ bool sync_rate; /* 0 - a sync event is required from the peripheral to transfer a chunk 1 - a sync event is required from the peripheral to transfer a frame */ }; The patch to add a new API for single buffer transfers alone: diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h @@ -491,6 +492,10 @@ struct dma_device { struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_data_direction direction, unsigned long flags); + struct dma_async_tx_descriptor *(*device_prep_slave)( + struct dma_chan *chan, dma_addr_t buf_addr, + unsigned int buf_len, void *buf_prop, + enum dma_data_direction direction, unsigned long flags); struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)( struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, size_t period_len, enum dma_data_direction direction); The number of frames (n_frames) can always be determined using all other values in the dma_buffer_config along with the buffer length (buf_len) passed in the API. n_frames = buf_len / (chunk_sixe * frame_size); Regards, Sundaram -----Original Message----- From: Russell King - ARM Linux [mailto:linux at arm.linux.org.uk] Sent: Thursday, June 09, 2011 6:17 PM To: Raju, Sundaram Cc: linux-arm-kernel at lists.infradead.org; linux-kernel at vger.kernel.org; davinci-linux-open-source at linux.davincidsp.com; linux-omap at vger.kernel.org Subject: Re: [RFC] dmaengine: add new api for preparing simple slave transfer Can you please re-post with sensible wrapping at or before column 72. I'm not manually reformatting your entire message just so I can find the relevant bits to reply to. Thanks. From linux at arm.linux.org.uk Thu Jun 9 11:32:06 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Thu, 9 Jun 2011 17:32:06 +0100 Subject: [RFC] dmaengine: add new api for preparing simple slave transfer In-Reply-To: References: <20110609124723.GA24636@n2100.arm.linux.org.uk> Message-ID: <20110609163206.GB24636@n2100.arm.linux.org.uk> On Thu, Jun 09, 2011 at 09:31:56PM +0530, Raju, Sundaram wrote: > Here it is, with proper line wrapping; Thanks. This is much easier to reply to. > I believe that even though the dmaengine framework addresses and > supports most of the required use cases of a client driver to a DMA > controller, some extensions are required in it to make it still more > generic. > > Current framework contains two APIs to prepare for slave transfers: > > struct dma_async_tx_descriptor *(*device_prep_slave_sg)( > struct dma_chan *chan, struct scatterlist *sgl, > unsigned int sg_len, enum dma_data_direction direction, > unsigned long flags); > > struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)( > struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, > size_t period_len, enum dma_data_direction direction); > > and one control API. > int (*device_control)(struct dma_chan *chan, enum dma_ctrl_cmd cmd, > unsigned long arg); > > A simple single buffer transfer (i.e. non sg transfer) can be done only > as a trivial case of the device_prep_slave_sg API. The client driver is > expected to prepare a sg list and provide to the dmaengine API for that > single buffer also. We can avoid preparing a sg list in every driver which wants this by having dmaengine.h provide a helper for this case: static inline dma_async_tx_descriptor *dmaengine_prep_slave_single( struct dma_chan *chan, void *buf, size_t len, enum dma_data_direction dir, unsigned long flags) { struct scatterlist sg; sg_init_one(&sg, buf, len); return chan->device->device_prep_slave_sg(chan, &sg, 1, dir, flags); } I think also providing dmaengine_prep_slave_sg() and dmaengine_prep_dma_cyclic() as wrappers to hide the chan->device->blah bit would also be beneficial (and helps keep that detail out of the users of DMA engine.) > In a slave transfer, the client has to define all the buffer related > attributes and the peripheral related attributes. I'd argue that it is incorrect to have drivers specify the buffer related attributes - that makes the API unnecessarily complicated to use, requiring two calls (one to configure the channel, and one to prepare the transfer) each time it needs to be used. Not only that but the memory side of the transfer should be no business of the driver - the driver itself should only specify the attributes for the device being driven. The attributes for the memory side of the transfer should be a property of the DMA engine itself. I would like to see in the long term the dma_slave_config structure lose its 'direction' argument, and the rest of the parameters used to define the device side parameters only. This will allow the channel to be configured once when its requested, and then the prepare call can configure the direction as required. > Now coming to the buffer related attributes, sg list is a nice way to > represent a disjoint buffer; all the offload engines in drivers/dma > create a descriptor for each of the contiguous chunk in the sg list > buffer and pass it to the controller. The sg list is the standard Linux way to describe scattered buffers. > But many a times a client may want to transfer from a single buffer to > the peripheral and most of the DMA controllers have the capability to > transfer data in chunks/frames directly at a stretch. So far, I've only seen DMA controllers which operate on a linked list of source, destination, length, attributes, and next entry pointer. > All the attributes of a buffer, which are required for the transfer > can be programmed in single descriptor and submitted to the > DMA controller. I'm not sure that this is useful - in order to make use of the data, the data would have to be copied in between the descriptors - and doesn't that rather negate the point of DMA if you have to memcpy() the data around? Isn't it far more efficient to have DMA place the data exactly where it's required in memory right from the start without any memcpy() operations? Can you explain where and how you would use something like this: > ------------------------------------------------------------------- > | Chunk 0 |ICG| Chunk 1 |ICG| ... |ICG| Chunk n | Frame 0 > ------------------------------------------------------------------- > | Inter Frame Gap | > ------------------------------------------------------------------- > | Chunk 0 |ICG| Chunk 1 |ICG| ... |ICG| Chunk n | Frame 1 > ------------------------------------------------------------------- > | Inter Frame Gap | > ------------------------------------------------------------------- > | ........ | > ------------------------------------------------------------------- > | Inter Frame Gap | > ------------------------------------------------------------------- > | Chunk 0 |ICG| Chunk 1 |ICG| ... |ICG| Chunk n | Frame m > ------------------------------------------------------------------- Can you also explain what a chunk contains, what a frame contains, how they're different and how they're processed? Where does the data to be transferred to the device live? Also, bear in mind that in Linux, we try hard to avoid allocating buffers larger than one page at a time - as soon as we get to multiple contiguous page buffers, allocations can start failing because of memory fragmentation. This is why we much prefer scatterlists over single contiguous buffers for DMA. From nsekhar at ti.com Thu Jun 9 11:48:11 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 9 Jun 2011 22:18:11 +0530 Subject: [PATCH V2] davinci: da850: move input frequency to board specific files In-Reply-To: References: <1306312622-4901-1-git-send-email-christian.riesch@omicron.at> <1306488053-27978-1-git-send-email-christian.riesch@omicron.at> <874o42wpec.fsf@ti.com> Message-ID: Hi Christian, On Thu, Jun 09, 2011 at 15:35:12, Christian Riesch wrote: > Hi Kevin, > > On Tue, Jun 7, 2011 at 12:44 AM, Kevin Hilman wrote: > > Christian Riesch writes: > > > >> From: Bob Dunlop > >> > >> Currently the input frequency of the SoC is hardcoded in the SoC specific > >> da850.c file to 24 MHz. Since the SoC accepts input frequencies in a wide > >> range from 12 to 50 MHz, boards with different oscillator/crystal > >> frequencies may be built. > >> > >> This patch allows setting a different input frequency in the board > >> specific files to support boards with oscillator/crystal frequencies other > >> than 24 MHz. > >> > >> Signed-off-by: Bob Dunlop > >> Signed-off-by: Christian Riesch > > > > Why not allow board code to just do a clk_set_rate()? > > I'm fine with this method (In fact it was the first thing that I > tried, I added a .set_rate method to ref_clk, it worked well for me). > However I wonder whether first initializing the clock with the wrong > value (24 MHz) and later correcting it via clk_set_rate() would break > something. In the meantime, the data in the clock tree do not reflect > the actual frequencies that are present on the SoC. Doing this right after _init() should be safe. The board should not assume clocks to be setup before this call is made. Thanks, Sekhar From sundaram at ti.com Fri Jun 10 05:21:41 2011 From: sundaram at ti.com (Raju, Sundaram) Date: Fri, 10 Jun 2011 15:51:41 +0530 Subject: [RFC] dmaengine: add new api for preparing simple slave transfer In-Reply-To: <1307686397.10976.116.camel@vkoul-udesk3> References: <20110609124723.GA24636@n2100.arm.linux.org.uk> <20110609163206.GB24636@n2100.arm.linux.org.uk> <1307686397.10976.116.camel@vkoul-udesk3> Message-ID: I think I should have tried to explain my case with a specific example. I tried to generalize it and it has confused and misled every one. I have tried again now. :) > > > A simple single buffer transfer (i.e. non sg transfer) can be done only > > > as a trivial case of the device_prep_slave_sg API. The client driver is > > > expected to prepare a sg list and provide to the dmaengine API for that > > > single buffer also. > > > > We can avoid preparing a sg list in every driver which wants this by > > having dmaengine.h provide a helper for this case: > > > > static inline dma_async_tx_descriptor *dmaengine_prep_slave_single( > > struct dma_chan *chan, void *buf, size_t len, > > enum dma_data_direction dir, unsigned long flags) > > { > > struct scatterlist sg; > > > > sg_init_one(&sg, buf, len); > > > > return chan->device->device_prep_slave_sg(chan, &sg, 1, dir, flags); > > } > That sounds good... Yes, this should be sufficient if the only aim is to avoid preparing a sg list in every driver which wants to transfer a single buffer. But my main aim was to add more buffer related details to the API. I have tried to explain a use case below, where this will be useful. > > > > I think also providing dmaengine_prep_slave_sg() and > > dmaengine_prep_dma_cyclic() as wrappers to hide the chan-> >device->blah > > bit would also be beneficial (and helps keep that detail out of the > > users of DMA engine.) > > Yes, this would really be helpful if someone is just looking at the client drivers and are not familiar with the dmaengine's internal data structures. > > > In a slave transfer, the client has to define all the buffer related > > > attributes and the peripheral related attributes. > > > > I'd argue that it is incorrect to have drivers specify the buffer > > related attributes - that makes the API unnecessarily complicated > > to use, requiring two calls (one to configure the channel, and one > > to prepare the transfer) each time it needs to be used. I am not able to understand why 2 calls will be required? Client configures the slave using dma_slave_config only once. If the direction flag is removed then, this configuration doesn?t have to be modified at all. > So I think the slave transfer API must also have a provision to pass > the buffer configuration. The buffer attributes have to be passed > directly as an argument in the prepare API, unlike dma_slave_config > as they will be different for each buffer that is going to be > transferred. The API is called only once in the current framework to prepare the descriptor. After adding any extra arguments in the prepare API, client will still have to call it only once. > > > > Not only that but the memory side of the transfer should be no business > > of the driver - the driver itself should only specify the attributes > > for the device being driven. The attributes for the memory side of the > > transfer should be a property of the DMA engine itself. > > Yes Russell, you are correct. Attributes of the memory side should be a property of DMA engine itself. What is meant here is that, client has told the DMAC how to write to/ read from the slave peripheral by defining all the slave properties in the dma_slave_config. It is assumed that the buffer side has to be read/written to byte after byte continuously by the DMAC. I wanted to say client must have provisions to pass the details on how it wants the DMAC to read/ write to the buffer, if the capability is available in the DMAC. DMACs have the capability to auto increment the source/destination address accordingly after transferring a byte of data and they automatically read/write to the next byte location. In some DMACs you can program an offset also. They read x bytes, skip y bytes and read the next x bytes. This detail will be passed to the client driver by the application. Then it is for the slave driver to communicate this to the DMA engine, right? > > I would like to see in the long term the dma_slave_config structure > > lose its 'direction' argument, and the rest of the parameters used to > > define the device side parameters only. > I am not sure why direction flag is required and can be done away with. > Both sg and cyclic API have a direction parameter and that should be > used. A channel can be used in any direction client wishes to. I also agree. The direction flag in the dma_slave_config is redundant. As Russell had already mentioned in another thread, this redundancy forces DMAC drivers to check if the direction flag in the dma_slave_config is same as that passed to the prepare API. Also client drivers have to keep modifying the dma_slave_config every time before they make a direction change in transfer. > > > > > But many a times a client may want to transfer from a single buffer to > > > the peripheral and most of the DMA controllers have the capability to > > > transfer data in chunks/frames directly at a stretch. > > > > So far, I've only seen DMA controllers which operate on a linked list of > > source, destination, length, attributes, and next entry pointer. > > > > > All the attributes of a buffer, which are required for the transfer > > > can be programmed in single descriptor and submitted to the > > > DMA controller. > > > > I'm not sure that this is useful - in order to make use of the data, the > > data would have to be copied in between the descriptors - and doesn't that > > rather negate the point of DMA if you have to memcpy() the data around? > > > > Isn't it far more efficient to have DMA place the data exactly where it's > > required in memory right from the start without any memcpy() operations? > > > > Can you explain where and how you would use something like this: > > Consider a simple video use case of de-interlacing a video buffer. Odd lines have to be transferred first, and then the even lines are transferred separately. This can be done by programming the inter frame gap as the line size of the video buffer in the DMAC. This will require you to have only 2 descriptors, one for the odd lines and another for the even lines. This results in only 2 descriptors being written to DMAC registers. But if we have to construct a sglist for this, we end up having to write the descriptors to DMAC as many times as the number of lines in the video buffer. To simply put it in other words, current DMA engine supports 1D transfers alone. Though sg list may be 2D, but again it the DMACs are programmed for multiple 1D transfers again and again in a sg case. If the DMAC supports multi dimensional transfer, that feature cannot be utilized by using the current DMA engine APIs. > > ------------------------------------------------------------------- > > | Chunk 0 |ICG| Chunk 1 |ICG| ... |ICG| Chunk n | Frame 0 > > ------------------------------------------------------------------- > > | Inter Frame Gap | > > ------------------------------------------------------------------- > > | Chunk 0 |ICG| Chunk 1 |ICG| ... |ICG| Chunk n | Frame 1 > > ------------------------------------------------------------------- > > | Inter Frame Gap | > > ------------------------------------------------------------------- > > | ........ | > > ------------------------------------------------------------------- > > | Inter Frame Gap | > > ------------------------------------------------------------------- > > | Chunk 0 |ICG| Chunk 1 |ICG| ... |ICG| Chunk n | Frame m > > ------------------------------------------------------------------- > > Can you also explain what a chunk contains, what a frame contains, > how they're different and how they're processed? Where does the > data to be transferred to the device live? > > > > > Note: ICG = Inter Chunk Gap. > > > > struct dma_buffer_config { > > u32 chunk_size; /* number of bytes in a chunk */ > > u32 frame_size; /* number of chunks in a frame */ > > /* u32 n_frames; number of frames in the buffer */ > > u32 inter_chunk_gap; /* number of bytes between end of a chunk > > and the start of the next chunk */ > > u32 inter_frame_gap; /* number of bytes between end of a frame > > and the start of the next frame */ > > bool sync_rate; /* 0 - a sync event is required from the > > peripheral to transfer a chunk > > 1 - a sync event is required from the > > peripheral to transfer a frame */ > > }; > > I have tried to represent a 3 dimensional buffer here. Each chunk is a 1D buffer with 'x' bytes. Each frame is a 2D buffer with 'n' chunks. The overall buffer is 3D with 'm' frames. Actually we can deduce the chunk_size from the dma_slave_config itself. It is either the src_addr_width or dst_addr_width based on the direction. Because at a stretch DMAC cannot transfer more than the slave register width. All these values in the dma_buffer_config proposed above can be programmed in the DMAC through a single descriptor if the DMAC is 3D transfer capable. If the client has to do this type of transfer using sg list then it will end up having a sglist of m*n entries and that many descriptors have to be programmed on the DMAC > > Also, bear in mind that in Linux, we try hard to avoid allocating > buffers larger than one page at a time - as soon as we get to multiple > contiguous page buffers, allocations can start failing because of > memory fragmentation. This is why we much prefer scatterlists over > single contiguous buffers for DMA. Regards, Sundaram From linux at arm.linux.org.uk Fri Jun 10 05:43:18 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 10 Jun 2011 11:43:18 +0100 Subject: [RFC] dmaengine: add new api for preparing simple slave transfer In-Reply-To: References: <20110609124723.GA24636@n2100.arm.linux.org.uk> <20110609163206.GB24636@n2100.arm.linux.org.uk> <1307686397.10976.116.camel@vkoul-udesk3> Message-ID: <20110610104318.GC24636@n2100.arm.linux.org.uk> On Fri, Jun 10, 2011 at 03:51:41PM +0530, Raju, Sundaram wrote: > Consider a simple video use case of de-interlacing a video buffer. > Odd lines have to be transferred first, and then the even lines are > transferred separately. This can be done by programming the > inter frame gap as the line size of the video buffer in the DMAC. > This will require you to have only 2 descriptors, one for the > odd lines and another for the even lines. This results in only > 2 descriptors being written to DMAC registers. How would this be handled with DMACs which can't 'skip' bytes in the buffer? You would have to generate a list of LLIs separately describing each 'line' of video and chain them together. How do you handle the situation where a driver uses your new proposed API, but it doesn't support that in hardware. > Actually we can deduce the chunk_size from the > dma_slave_config itself. It is either the src_addr_width or > dst_addr_width based on the direction. Because at a stretch > DMAC cannot transfer more than the slave register width. I think you're misinterpreting those fields. (dst|src)_addr_width tells the DMA controller the width of each transaction - whether to issue a byte, half-word, word or double-word read or write to the peripheral. It doesn't say how many of those to issue, it just says what the peripheral access size is to be. In other words, they describe the width of the FIFO register. From sundaram at ti.com Fri Jun 10 06:13:53 2011 From: sundaram at ti.com (Raju, Sundaram) Date: Fri, 10 Jun 2011 16:43:53 +0530 Subject: [RFC] dmaengine: add new api for preparing simple slave transfer In-Reply-To: <1307686140.10976.111.camel@vkoul-udesk3> References: <20110609124723.GA24636@n2100.arm.linux.org.uk> <1307686140.10976.111.camel@vkoul-udesk3> Message-ID: Vinod, > -----Original Message----- > From: Koul, Vinod [mailto:vinod.koul at intel.com] > Sent: Friday, June 10, 2011 11:39 AM > To: Raju, Sundaram; Dan > Cc: Russell King - ARM Linux; davinci-linux-open- > source at linux.davincidsp.com; linux-omap at vger.kernel.org; linux- > kernel at vger.kernel.org; linux-arm-kernel at lists.infradead.org > Subject: RE: [RFC] dmaengine: add new api for preparing simple slave transfer > > > > > The above 2 APIs in the dmaengine framework expect all the > > peripheral/slave related attributes to be filled in the > > dma_slave_config data structure. > > struct dma_slave_config { > > enum dma_data_direction direction; > > dma_addr_t src_addr; > > dma_addr_t dst_addr; > > enum dma_slave_buswidth src_addr_width; > > enum dma_slave_buswidth dst_addr_width; > > u32 src_maxburst; > > u32 dst_maxburst; > > }; > > > > This data structure is passed to the offload engine via the dma_chan > > data structure in its private pointer. > No, this is passed thru control API you described above. Please read > Documentation/dmaengine.txt Yes, Vinod its my mistake. I wanted to say control API only, but just mixed it up with how the dma_slave_config is attached to each dma_chan so that the offload drivers can use it while preparing the descriptors. > > > Now coming to the buffer related attributes, sg list is a nice way to > > represent a disjoint buffer; all the offload engines in drivers/dma > > create a descriptor for each of the contiguous chunk in the sg list > > buffer and pass it to the controller. > > > > But many a times a client may want to transfer from a single buffer to > > the peripheral and most of the DMA controllers have the capability to > > transfer data in chunks/frames directly at a stretch. > > All the attributes of a buffer, which are required for the transfer > > can be programmed in single descriptor and submitted to the > > DMA controller. > > > > So I think the slave transfer API must also have a provision to pass > > the buffer configuration. The buffer attributes have to be passed > > directly as an argument in the prepare API, unlike dma_slave_config > > as they will be different for each buffer that is going to be > > transferred. > Can you articulate what attributes you are talking about. The > dma_slave_config parameters don't represent buffer attributes. They > represent the dma attributes on how you want to transfer. These things > like bus widths, burst lengths are usually constant for the slave > transfers, not sure why they should change per buffer? > I have tried to explain the attributes in the previous mail I posted in this thread. Yes, buffer attributes should not be represented in the dma_slave_config. It is for slave related data. That is why had mentioned that buffer configuration should be a separate structure and passed in the prepare API. See quoted below: > struct dma_buffer_config { > u32 chunk_size; /* number of bytes in a chunk */ > u32 frame_size; /* number of chunks in a frame */ > /* u32 n_frames; number of frames in the buffer */ > u32 inter_chunk_gap; /* number of bytes between end of a chunk > and the start of the next chunk */ > u32 inter_frame_gap; /* number of bytes between end of a frame > and the start of the next frame */ > bool sync_rate; /* 0 - a sync event is required from the > peripheral to transfer a chunk > 1 - a sync event is required from the > peripheral to transfer a frame */ > }; > > The patch to add a new API for single buffer transfers alone: > diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h > @@ -491,6 +492,10 @@ struct dma_device { > struct dma_chan *chan, struct scatterlist *sgl, > unsigned int sg_len, enum dma_data_direction direction, > unsigned long flags); > + struct dma_async_tx_descriptor *(*device_prep_slave)( > + struct dma_chan *chan, dma_addr_t buf_addr, > + unsigned int buf_len, void *buf_prop, > + enum dma_data_direction direction, unsigned long flags); > struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)( > struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, > size_t period_len, enum dma_data_direction direction); > Regards, Sundaram From sundaram at ti.com Fri Jun 10 06:48:46 2011 From: sundaram at ti.com (Raju, Sundaram) Date: Fri, 10 Jun 2011 17:18:46 +0530 Subject: [RFC] dmaengine: add new api for preparing simple slave transfer In-Reply-To: <20110610104318.GC24636@n2100.arm.linux.org.uk> References: <20110609124723.GA24636@n2100.arm.linux.org.uk> <20110609163206.GB24636@n2100.arm.linux.org.uk> <1307686397.10976.116.camel@vkoul-udesk3> <20110610104318.GC24636@n2100.arm.linux.org.uk> Message-ID: Russell, > -----Original Message----- > From: Russell King - ARM Linux [mailto:linux at arm.linux.org.uk] > Sent: Friday, June 10, 2011 4:13 PM > To: Raju, Sundaram > Cc: Koul, Vinod; Dan; davinci-linux-open-source at linux.davincidsp.com; linux- > omap at vger.kernel.org; linux-kernel at vger.kernel.org; linux-arm- > kernel at lists.infradead.org > Subject: Re: [RFC] dmaengine: add new api for preparing simple slave transfer > > On Fri, Jun 10, 2011 at 03:51:41PM +0530, Raju, Sundaram wrote: > > Consider a simple video use case of de-interlacing a video buffer. > > Odd lines have to be transferred first, and then the even lines are > > transferred separately. This can be done by programming the > > inter frame gap as the line size of the video buffer in the DMAC. > > This will require you to have only 2 descriptors, one for the > > odd lines and another for the even lines. This results in only > > 2 descriptors being written to DMAC registers. > > How would this be handled with DMACs which can't 'skip' bytes in the > buffer? You would have to generate a list of LLIs separately > describing each 'line' of video and chain them together. Correct. > > How do you handle the situation where a driver uses your new proposed > API, but it doesn't support that in hardware. > It should be handled the same way how a sg buffer is handled, if LLI chaining feature is not available in the hardware. It has to be submitted as a queue of separate descriptors to the DMAC, where each descriptor will be processed after the completion of the previous descriptor. > > Actually we can deduce the chunk_size from the > > dma_slave_config itself. It is either the src_addr_width or > > dst_addr_width based on the direction. Because at a stretch > > DMAC cannot transfer more than the slave register width. > > I think you're misinterpreting those fields. (dst|src)_addr_width tells > the DMA controller the width of each transaction - whether to issue a > byte, half-word, word or double-word read or write to the peripheral. > It doesn't say how many of those to issue, it just says what the > peripheral access size is to be. > > In other words, they describe the width of the FIFO register. Yes correct, I was just giving an example for considering or understanding a buffer in 3D and how each dimension should be. chunk_size = (src|dst)_addr_width, for a special case, i.e, if DMAC is programmed to transfer the entire 1D buffer per sync event received from the peripheral. In slave transfer, the peripheral is going to give a sync event to DMAC when the FIFO register is full|empty. Now DMACs capable of 3D transfer, do transfer of the whole 1D buffer per sync received or even whole 2D buffer per sync received (based on the sync rate programmed in the DMAC). So the DMAC has to be programmed for a 1D size (i.e. chunk size) equal to that of the width of the FIFO register if the sync rate programmed in DMAC is "per chunk". Regards, Sundaram From linux at arm.linux.org.uk Fri Jun 10 08:33:38 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Fri, 10 Jun 2011 14:33:38 +0100 Subject: [RFC] dmaengine: add new api for preparing simple slave transfer In-Reply-To: References: <20110609124723.GA24636@n2100.arm.linux.org.uk> <20110609163206.GB24636@n2100.arm.linux.org.uk> <1307686397.10976.116.camel@vkoul-udesk3> <20110610104318.GC24636@n2100.arm.linux.org.uk> Message-ID: <20110610133338.GD24636@n2100.arm.linux.org.uk> On Fri, Jun 10, 2011 at 05:18:46PM +0530, Raju, Sundaram wrote: > Russell, > > > How do you handle the situation where a driver uses your new proposed > > API, but it doesn't support that in hardware. > > It should be handled the same way how a sg buffer is handled, > if LLI chaining feature is not available in the hardware. No, if the LLI chaining feature is available in hardware and your special 'skip' feature is not, then you have to generate a set of LLIs for the hardware to walk through to 'emulate' the skip feature. If you have no LLI support, then you need to program each transfer manually into the DMA controller. > > > Actually we can deduce the chunk_size from the > > > dma_slave_config itself. It is either the src_addr_width or > > > dst_addr_width based on the direction. Because at a stretch > > > DMAC cannot transfer more than the slave register width. > > > > I think you're misinterpreting those fields. (dst|src)_addr_width tells > > the DMA controller the width of each transaction - whether to issue a > > byte, half-word, word or double-word read or write to the peripheral. > > It doesn't say how many of those to issue, it just says what the > > peripheral access size is to be. > > > > In other words, they describe the width of the FIFO register. > > Yes correct, I was just giving an example for considering or > understanding a buffer in 3D and how each dimension should be. > > chunk_size = (src|dst)_addr_width, for a special case, > i.e, if DMAC is programmed to transfer the entire 1D buffer > per sync event received from the peripheral. Please, don't generate special cases. Design proper APIs from the outset rather than abusing what's already there. So no, don't abuse the address width stuff. In any case, the address width stuff must still be used to describe the peripherals FIFO register. > In slave transfer, the peripheral is going to give a sync event to > DMAC when the FIFO register is full|empty. 'sync event' - peripherals give 'request' signals to the DMAC asking them to transfer some more data only, and the DMAC gives an acknowledge signal back so that the peripheral knows that the DMAC is giving them data. In the generic case, they typically have no further signalling. When the DMAC sees an active request, it will attempt to transfer up to the minimum of (burst size, number of transfers remaining) to the peripheral in one go. > Now DMACs capable of 3D transfer, do transfer of the whole 1D > buffer per sync received or even whole 2D buffer per sync received > (based on the sync rate programmed in the DMAC). Ok, what I'm envisioning is that your term "chunk size" means "register width", and you view that as one dimension. We already describe this. A frame is a collection of chunks. That's already described - the number of chunks in a buffer is the buffer size divided by the chunk size. Buffers must be a multiple of the chunk size. Then we have a collection of frames. These can be non-contiguous. That's effectively described by our scatterlist. > So the DMAC has to be programmed for a 1D size (i.e. chunk size) > equal to that of the width of the FIFO register if the sync rate > programmed in DMAC is "per chunk". This appears to be a circular definition, and makes little sense to me. The overall conclusion which I'm coming to is that we already support what you're asking for, but the problem is that we're using different (and I'd argue standard) terminology to describe what we have. The only issue which I see that we don't cover is the case where you want to describe a single buffer which is organised as N bytes to be transferred, M following bytes to be skipped, N bytes to be transferred, M bytes to be skipped. I doubt there are many controllers which can be programmed with both 'N' and 'M' parameters directly. Let me finish with a summary of how memory-to-peripheral transfers are expected to operate: 1. The DMA controller reads data from system RAM using whatever parameters are most suitable for it to use up to the current buffer size. 2. When the DMA request goes active, it transfers data that it read previously to the device in dest_addr_width chunks of data. It transfers the minimum of the remaining data or the burst size. 3. The peripheral, receiving data and filling its FIFO, drops the DMA request. 4. The DMA controller then reads more data from system RAM in the same way in (1). 5. If at any point the DMA controller reaches the end of the buffer, and as a link to the next buffer, it immediately moves to the next buffer and starts fetching data from that new buffer. From Paul_Stuart at seektech.com Fri Jun 10 12:03:57 2011 From: Paul_Stuart at seektech.com (Paul Stuart) Date: Fri, 10 Jun 2011 10:03:57 -0700 Subject: DM365 GStreamer Resizer memory allocation woes In-Reply-To: <70ABD7B1D427A94DA659B4C84B0F5D4A017E332338@dlee06.ent.ti.com> References: <7F1B6BBBDF05C649BBBA3C853F488A611AF21A10A0@Hawking.deepsea.com> <70ABD7B1D427A94DA659B4C84B0F5D4A017E332338@dlee06.ent.ti.com> Message-ID: <7F1B6BBBDF05C649BBBA3C853F488A611AF22048A6@Hawking.deepsea.com> Hi, Enabling dual resizer in my gstreamer pipeline to encode and display at two different resolutions simultaneously. I've enabled the second resizer in v4l2_dm365_ipipe.c, but now there is the problem of allocating space for the Resizer B's output that directly follows Resizer A's. As far as I can tell, video capture resolution and memory buffer size are hopelessly entangled with each other. Is there a straight forward way in the gstreamer v4l2src plugin to allocate more mapped memory for the second resizer output? I've been digging around in gstV4l2bufferpool.c, but that has yet to bear fruit. Any insights would be appreciated. Thanks, Paul -------------- next part -------------- An HTML attachment was scrubbed... URL: From vico.xu at gmail.com Mon Jun 13 22:07:39 2011 From: vico.xu at gmail.com (Xu) Date: Tue, 14 Jun 2011 11:07:39 +0800 Subject: DM365EVM I2S Master Mode Message-ID: <1308020859.2562.6.camel@digitalsys> Hi, I need change dm365 as I2S master and codec as slave. When I modify davinci-evm.c : #define AUDIO_FORMAT (SND_SOC_DAIFMT_I2S | \ SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_NB_NF) The EVM not work. My kernel is 2.26.32.17 (DVSDK4.02). Thanks. Xu From sundaram at ti.com Tue Jun 14 00:38:51 2011 From: sundaram at ti.com (Raju, Sundaram) Date: Tue, 14 Jun 2011 11:08:51 +0530 Subject: [RFC] dmaengine: add new api for preparing simple slave transfer In-Reply-To: References: <20110609124723.GA24636@n2100.arm.linux.org.uk> <20110609163206.GB24636@n2100.arm.linux.org.uk> <1307686397.10976.116.camel@vkoul-udesk3> <20110610104318.GC24636@n2100.arm.linux.org.uk> <20110610133338.GD24636@n2100.arm.linux.org.uk> Message-ID: Linus, Thanks for the pointers. > -----Original Message----- > From: Linus Walleij [mailto:linus.walleij at linaro.org] > Sent: Monday, June 13, 2011 7:43 PM > To: Raju, Sundaram > Cc: Russell King - ARM Linux; Koul, Vinod; Dan; davinci-linux-open- > source at linux.davincidsp.com; linux-omap at vger.kernel.org; linux- > kernel at vger.kernel.org; linux-arm-kernel at lists.infradead.org > Subject: Re: [RFC] dmaengine: add new api for preparing simple slave transfer > > On Fri, Jun 10, 2011 at 3:33 PM, Russell King - ARM Linux > wrote: > > On Fri, Jun 10, 2011 at 05:18:46PM +0530, Raju, Sundaram wrote: > >> Now DMACs capable of 3D transfer, do transfer of the whole 1D > >> buffer per sync received or even whole 2D buffer per sync received > >> (based on the sync rate programmed in the DMAC). > > > > The only issue which I see that we don't cover is the case where you want > > to describe a single buffer which is organised as N bytes to be transferred, > > M following bytes to be skipped, N bytes to be transferred, M bytes to be > > skipped. ?I doubt there are many controllers which can be programmed with > > both 'N' and 'M' parameters directly. > > Sundaram is this how your controller works? > I mean the hardware can skip over sequences like this? > > When we added the config interface to DMAengine I originally included > a "custom config" call, but Dan wanted me to keep it out until we > had some specific usecase for it. FSLDMA recently started > to use it. > > Notice how dmaengine_slave_config() is implemented: > > static inline int dmaengine_slave_config(struct dma_chan *chan, > struct dma_slave_config *config) > { > return dmaengine_device_control(chan, DMA_SLAVE_CONFIG, > (unsigned long)config); > } > > So what is passed to the driver is just an unsigned long. > > This is actually modeled to be ioctl()-like so you can pass in a > custom config to the same callback on the device driver, > just use some other enumerator than DMA_SLAVE_CONFIG, > say like FSLDMA already does with FSLDMA_EXTERNAL_START. > > Just put some enumerator in enum dma_ctrl_cmd in > dmaengine.h such as SDMA_TEXAS_STRIDE_CONFIG and call > like this: > > /* However that config struct needs to look, basically */ > static struct sdma_ti_stride_cgf = { > take = M, > skip = N, > }; > > ret = chan->device->device_control(chan, SDMA_TEXAS_STRIDE_CONFIG, > &sdma_ti_stride_cfg); > > Or something like this. Yes, the hardware can skip over sequences like that. I also thought about your suggestion, at first before submitting the RFC. But I dint pursue this because, this ioctl() call has to be made before every single prepare call. I may have to end up using this if we decide not to change the API signature for prepare APIs. I actually intend to use this for all DMAC related ioctl(). Configuring the DMAC and programming various modes etc specific to the DMAC. I suppose this is the only way to do it. Let me know if there is any other way to do it. Thanks, Sundaram From sundaram at ti.com Tue Jun 14 01:42:11 2011 From: sundaram at ti.com (Raju, Sundaram) Date: Tue, 14 Jun 2011 12:12:11 +0530 Subject: [RFC] dmaengine: add new api for preparing simple slave transfer In-Reply-To: <1307724439.10976.188.camel@vkoul-udesk3> References: <20110609124723.GA24636@n2100.arm.linux.org.uk> <1307686140.10976.111.camel@vkoul-udesk3> <1307724439.10976.188.camel@vkoul-udesk3> Message-ID: Russell, Thanks for all the quick pointers and the summary of how memory-to-peripheral transfers are expected to operate. > Ok, what I'm envisioning is that your term "chunk size" means "register > width", and you view that as one dimension. We already describe this. > > A frame is a collection of chunks. That's already described - the number > of chunks in a buffer is the buffer size divided by the chunk size. > Buffers must be a multiple of the chunk size. > > Then we have a collection of frames. These can be non-contiguous. > That's effectively described by our scatterlist. > > > The overall conclusion which I'm coming to is that we already support > what you're asking for, but the problem is that we're using different > (and I'd argue standard) terminology to describe what we have. > > The only issue which I see that we don't cover is the case where you want > to describe a single buffer which is organised as N bytes to be transferred, > M following bytes to be skipped, N bytes to be transferred, M bytes to be > skipped. I doubt there are many controllers which can be programmed with > both 'N' and 'M' parameters directly. > Yes this is what I wanted to communicate and discuss in the list. Thanks for describing it in the standard terminology, and pointing me in the right direction. Also please note that if the gap between the buffers in a scatterlist is uniform and that can again be skipped by the DMAC automatically by programming that gap size, then that feature also is not available in the current framework. I understand it can be done with the existing scatterlist, but just writing a value in a DMAC register is much better if available, than preparing a scatterlist and passing to the API. > > Please, don't generate special cases. Design proper APIs from the > outset rather than abusing what's already there. So no, don't abuse > the address width stuff. > > In any case, the address width stuff must still be used to describe the > peripherals FIFO register. > I did not intend to abuse the existing address width. It might look like that because of how I described it here. I agree that the dma_slave_config is for peripheral related properties to be stored. I was pointing out that the chunk size variable in the dma_buffer_config I proposed will be in most cases equal to FIFO register width, to describe what I actually meant by chunk size. > IIUC, you have a buffer with gaps in between (given by above params). > Is your DMA h/w capable of parsing this buffer and directly do a > transfer based on above parameters (without any work for SW), or you are > doing this in your dma driver and then programming a list of buffers? > In former case (although i haven't seen such dma controller yet), can > you share the datasheet? It would make very little sense to change the > current API for your extra special case. This special case needs to be > handled differently rather than making rule out of it!! > Yes, Vinod. This is directly handled in the DMAC h/w. This capability is present in 2 TI DMACs. EDMA (Enhanced DMA) in all TI DaVinci SoCs and the SDMA (System DMA) in all TI OMAP SoCs. The drivers of these controllers are present in the respective DaVinci tree and OMAP tree under the SoC folders. > SDMA and EDMA are TI SoC specific DMA controllers. Their drivers have > been maintained in the respective SoC folders till now. > arch/arm/plat-omap/dma.c > arch/arm/mach-davinci/dma.c The Manual of the EDMA controller in TI DaVinci SoCs is available at http://www.ti.com/litv/pdf/sprue23d Section 2.2 in the page 23 explains how transfers are made based on the gaps programmed. It also explains how the 3D buffer is internally split in EDMA based on the gaps programmed. The complete Reference manual of TI OMAP SoCs is available at http://www.ti.com/litv/pdf/spruf98s Chapter 9 in this document describes the SDMA controller. Section 9.4.3 in page 981 explains the various address modes, how the address is incremented by the DMAC and about the gaps in between buffers and frames and how the DMAC handles them. Linus, > > Is it really so bad? It is a custom configuration after all. Even if > there were many DMACs out there supporting it, we'd probably > model it like this, just pass something like DMA_STRIDE_CONFIG > instead of something named after a specific slave controller. > > This way DMACs that didn't support striding could NACK a > transfer for device drivers requesting it and then it could figure > out what to do. > > If we can get some indication as to whether more DMACs can > do this kind of stuff, we'd maybe like to introduce > DMA_STRIDE_CONFIG already now. > I wanted to discuss this feature in the list and get this feature added to the current dmaengine framework. If the current APIs can handle this feature, then its very good and I will follow that only. If what you suggest is the right way to go then I am okay. IMHO the framework should always handle the complex case and individual implementations should implement a subset of the capability and hence I suggest the changes I posted to the list. Regards, Sundaram From lamiaposta71 at gmail.com Tue Jun 14 05:28:57 2011 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Tue, 14 Jun 2011 12:28:57 +0200 Subject: DM365 GStreamer Resizer Config Setup happens...where? In-Reply-To: <70ABD7B1D427A94DA659B4C84B0F5D4A017E332338@dlee06.ent.ti.com> References: <7F1B6BBBDF05C649BBBA3C853F488A611AF21A10A0@Hawking.deepsea.com> <70ABD7B1D427A94DA659B4C84B0F5D4A017E332338@dlee06.ent.ti.com> Message-ID: Hi Sing, On Wed, Jun 8, 2011 at 2:38 PM, Singh, Brijesh wrote: > The resizing is done in v4l2src. The v4l2src has logic to configure IPIPE > on-fly resizing mode. Look at the v4l2src, you can find the implementation > details in dm36x_ipipe file. I have found what you mean. But I can't enable it for tvp5151 input. I have written to E2E "Linux forum" asking more help. http://e2e.ti.com/support/embedded/f/354/t/116428.aspx Regards, Raffaele From alan at lxorguk.ukuu.org.uk Tue Jun 14 09:36:30 2011 From: alan at lxorguk.ukuu.org.uk (Alan Cox) Date: Tue, 14 Jun 2011 15:36:30 +0100 Subject: [linux-pm] pm loss development In-Reply-To: <20110614140642.GA14509@gvim.org> References: <1305220265-9020-1-git-send-email-lamiaposta71@gmail.com> <201105180107.58078.rjw@sisk.pl> <20110518031203.GA3640@gvim.org> <201105182143.13237.rjw@sisk.pl> <20110602222111.GA15414@ucw.cz> <20110614140642.GA14509@gvim.org> Message-ID: <20110614153630.023eeeef@lxorguk.ukuu.org.uk> > I know nothing about journalling file systems or how well they limit the > critical sections of time where the file system is exposed to corruption > from sudden power failure. Its an interesting question though. A properly written journalling file system has no critical sections. The only things it relies upon are - store ordering in the drive working properly - a single disk block write being atomic the former is well specified even for ATA devices, the latter is a pretty safe property of rotating media, although in theory you have a finite chance of getting a bad sector. For flash it's a lot lot more complicated but for a flash device claiming to be ATA compliant you ought to get ATA behaviour. All that said there is still (as ever) a tiny chance your system may malfunction. It's all down to probabilities and if your laptop explodes you need a backup (trust me, I've tested this case). Alan From nsekhar at ti.com Tue Jun 14 10:33:20 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 14 Jun 2011 21:03:20 +0530 Subject: [PATCH V2] davinci: da850: move input frequency to board specific files In-Reply-To: References: <1306312622-4901-1-git-send-email-christian.riesch@omicron.at> <1306488053-27978-1-git-send-email-christian.riesch@omicron.at> <874o42wpec.fsf@ti.com> <877h8xr4o0.fsf@ti.com> Message-ID: On Wed, Jun 08, 2011 at 17:38:25, Nori, Sekhar wrote: > On Tue, Jun 07, 2011 at 21:53:59, Hilman, Kevin wrote: > > > I don't expect this to be a big boot-time impact. > > You are right. Using PRINTK_TIME on DM365 I saw no noticeable boot-time > change. When I profiled the code using do_gettimeofday(), I saw it > was taking ~95 usecs to complete the propagation. I was mainly worried > about all the recursion and reading of PLL and sysclk registers. Seems > its not so bad. > > > However, some of the clock.c assumptions might need to be updated as it > > currently is written from the perspective that the PLL clocks are the > > "root" clocks. > > Hmm, just calling clk_set_rate() on refclk propagated the rate > nicely across the tree. It seems DaVinci clock code is not in > such a bad shape :) Or did I miss the concern? > > > Setting (and propagating) clock rates is what the clock framework is > > for, so adding a new interface to set a custom clock rate just doesn't > > seem right. I understand that the reference oscillator might be > > considered a special case, but if this can be done with the clock > > framework, it is much preferred. > > Okay. Will modify the DM6467/T EVM code to use this method > instead. So, here is the patch. I suspect reference clock information should come from devicetree data when available. I hope it is OK to take this approach till that time? Thanks, Sekhar -----8<------------ From: Sekhar Nori Date: Thu, 2 Jun 2011 14:10:50 +0530 Subject: [PATCH 1/1] davinci: dm6467/T EVM: fix setting up of reference clock rate The DM6467 and DM6467T EVMs use different reference clock frequencies. This difference is currently supported by having the SoC code call a public board routine which sets up the reference clock frequency. This does not scale as more boards are added. Instead, use the clk_set_rate() API to setup the reference clock frequency to a different value from the board file. Suggested-by: Kevin Hilman Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/board-dm646x-evm.c | 17 ++++-------- arch/arm/mach-davinci/clock.c | 38 +++++++++++++++++++++++++++ arch/arm/mach-davinci/clock.h | 2 + arch/arm/mach-davinci/dm646x.c | 4 ++- arch/arm/mach-davinci/include/mach/dm646x.h | 2 - 5 files changed, 49 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm646x-evm.c b/arch/arm/mach-davinci/board-dm646x-evm.c index f6ac9ba..9db9838 100644 --- a/arch/arm/mach-davinci/board-dm646x-evm.c +++ b/arch/arm/mach-davinci/board-dm646x-evm.c @@ -719,9 +719,15 @@ static void __init cdce_clk_init(void) } } +#define DM6467T_EVM_REF_FREQ 33000000 + static void __init davinci_map_io(void) { dm646x_init(); + + if (machine_is_davinci_dm6467tevm()) + davinci_set_refclk_rate(DM6467T_EVM_REF_FREQ); + cdce_clk_init(); } @@ -785,17 +791,6 @@ static __init void evm_init(void) soc_info->emac_pdata->phy_id = DM646X_EVM_PHY_ID; } -#define DM646X_EVM_REF_FREQ 27000000 -#define DM6467T_EVM_REF_FREQ 33000000 - -void __init dm646x_board_setup_refclk(struct clk *clk) -{ - if (machine_is_davinci_dm6467tevm()) - clk->rate = DM6467T_EVM_REF_FREQ; - else - clk->rate = DM646X_EVM_REF_FREQ; -} - MACHINE_START(DAVINCI_DM6467_EVM, "DaVinci DM646x EVM") .boot_params = (0x80000100), .map_io = davinci_map_io, diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c index e4e3af1..ae65319 100644 --- a/arch/arm/mach-davinci/clock.c +++ b/arch/arm/mach-davinci/clock.c @@ -368,6 +368,12 @@ static unsigned long clk_leafclk_recalc(struct clk *clk) return clk->parent->rate; } +int davinci_simple_set_rate(struct clk *clk, unsigned long rate) +{ + clk->rate = rate; + return 0; +} + static unsigned long clk_pllclk_recalc(struct clk *clk) { u32 ctrl, mult = 1, prediv = 1, postdiv = 1; @@ -506,6 +512,38 @@ int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv, } EXPORT_SYMBOL(davinci_set_pllrate); +/** + * davinci_set_refclk_rate() - Set the reference clock rate + * @rate: The new rate. + * + * Sets the reference clock rate to a given value. This will most likely + * result in the entire clock tree getting updated. + * + * This is used to support boards which use a reference clock different + * than that used by default in .c file. The reference clock rate + * should be updated early in the boot process; ideally soon after the + * clock tree has been initialized once with the default reference clock + * rate (davinci_common_init()). + * + * Returns 0 on success, error otherwise. + */ +int davinci_set_refclk_rate(unsigned long rate) +{ + struct clk *refclk; + + refclk = clk_get(NULL, "ref"); + if (IS_ERR(refclk)) { + pr_err("%s: failed to get reference clock.\n", __func__); + return PTR_ERR(refclk); + } + + clk_set_rate(refclk, rate); + + clk_put(refclk); + + return 0; +} + int __init davinci_clk_init(struct clk_lookup *clocks) { struct clk_lookup *c; diff --git a/arch/arm/mach-davinci/clock.h b/arch/arm/mach-davinci/clock.h index 0dd2203..50b2482 100644 --- a/arch/arm/mach-davinci/clock.h +++ b/arch/arm/mach-davinci/clock.h @@ -123,6 +123,8 @@ int davinci_clk_init(struct clk_lookup *clocks); int davinci_set_pllrate(struct pll_data *pll, unsigned int prediv, unsigned int mult, unsigned int postdiv); int davinci_set_sysclk_rate(struct clk *clk, unsigned long rate); +int davinci_set_refclk_rate(unsigned long rate); +int davinci_simple_set_rate(struct clk *clk, unsigned long rate); extern struct platform_device davinci_wdt_device; extern void davinci_watchdog_reset(struct platform_device *); diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 1e0f809..46739c9 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -42,6 +42,7 @@ /* * Device specific clocks */ +#define DM646X_REF_FREQ 27000000 #define DM646X_AUX_FREQ 24000000 static struct pll_data pll1_data = { @@ -56,6 +57,8 @@ static struct pll_data pll2_data = { static struct clk ref_clk = { .name = "ref_clk", + .rate = DM646X_REF_FREQ, + .set_rate = davinci_simple_set_rate, }; static struct clk aux_clkin = { @@ -901,7 +904,6 @@ int __init dm646x_init_edma(struct edma_rsv_info *rsv) void __init dm646x_init(void) { - dm646x_board_setup_refclk(&ref_clk); davinci_common_init(&davinci_soc_info_dm646x); } diff --git a/arch/arm/mach-davinci/include/mach/dm646x.h b/arch/arm/mach-davinci/include/mach/dm646x.h index 7a27f3f..2a00fe5 100644 --- a/arch/arm/mach-davinci/include/mach/dm646x.h +++ b/arch/arm/mach-davinci/include/mach/dm646x.h @@ -15,7 +15,6 @@ #include #include #include -#include #include #define DM646X_EMAC_BASE (0x01C80000) @@ -31,7 +30,6 @@ 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); -- 1.7.3.2 From christian.riesch at omicron.at Tue Jun 14 12:39:17 2011 From: christian.riesch at omicron.at (Christian Riesch) Date: Tue, 14 Jun 2011 19:39:17 +0200 Subject: [PATCH] davinci: da850: add a .set_rate method to ref_clk In-Reply-To: References: Message-ID: <1308073157-22993-1-git-send-email-christian.riesch@omicron.at> This patch allows setting the input clock frequency of the SoC from the board specific using the davinci_set_refclk_rate function. Suggested-by: Kevin Hilman Cc: Sekhar Nori Signed-off-by: Christian Riesch --- This patch applies on top of Sekhar's patch. Best regards, Christian arch/arm/mach-davinci/da850.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 133aac4..27fe3ac 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -58,6 +58,7 @@ static struct pll_data pll0_data = { static struct clk ref_clk = { .name = "ref_clk", .rate = DA850_REF_FREQ, + .set_rate = davinci_simple_set_rate, }; static struct clk pll0_clk = { -- 1.7.0.4 From hotforest at gmail.com Wed Jun 15 20:23:20 2011 From: hotforest at gmail.com (htbegin) Date: Thu, 16 Jun 2011 09:23:20 +0800 Subject: how to fix "hotplug/265[CPU#0]: BUG in check_flags at kernel/lockdep.c:2371" ? Message-ID: Hi, all. I used the mv_pro_5.0.0 in dvsdk 2.0 for dm6446. When I enabled the following debug-related config flags: CONFIG_DEBUG_RT_MUTEXES / CONFIG_DEBUG_PREEMPT / CONFIG_LOCKDEP, the bug comes out. It always appeared when a syscall is made during the spawn of init process. Will the debug influence the stability of the linux system seriously, and How to fix it. Thanks. The following lines are excerpt from dmesg: ======================================================================= VFS: Mounted root (nfs filesystem). ***************************************************************************** * * * REMINDER, the following debugging options are turned on in your .config: * * * * CONFIG_DEBUG_RT_MUTEXES * * CONFIG_DEBUG_PREEMPT * * CONFIG_DEBUG_SLAB * * CONFIG_LOCKDEP * * * * they may increase runtime overhead and latencies. * * * ***************************************************************************** Freeing init memory: 172K stopped custom tracer. default.hotplug/265[CPU#0]: BUG in check_flags at kernel/lockdep.c:2371 [] (dump_stack+0x0/0x24) from [] (__WARN_ON+0x58/0x74) [] (__WARN_ON+0x0/0x74) from [] (check_flags+0xa8/0x23c) r8 = 00000000 r7 = 00000000 r6 = 00000000 r5 = C6354000 r4 = 60000013 [] (check_flags+0x0/0x23c) from [] (lock_acquire+0x58/0x98) r4 = 60000013 [] (lock_acquire+0x0/0x98) from [] (compat_down_write+0x44/0x58) [] (compat_down_write+0x0/0x58) from [] (sys_brk+0x30/0xfc) r5 = 000000FC r4 = C6354000 [] (sys_brk+0x0/0xfc) from [] (__sys_trace_return+0x0/0x28) r8 = C0034008 r7 = 0000002D r6 = 00001000 r5 = 000000FC r4 = 000B9000 --------------------------- | preempt count: 00000001 ] | 1-level deep critical section nesting: ---------------------------------------- .. [] .... __spin_lock_irqsave+0x30/0x64 .....[] .. ( <= __WARN_ON+0x28/0x74) irq event stamp: 5927 hardirqs last enabled at (5926): [] __irq_usr+0x84/0xa0 hardirqs last disabled at (5927): [] __spin_lock_irq+0x28/0x60 softirqs last enabled at (5925): [] __do_softirq+0x58/0x6c softirqs last disabled at (5902): [] irq_exit+0x60/0x74 INIT: version 2.86 booting Starting the hotplug events dispatcher: udevd. Synthesizing the initial hotplug events...done. Waiting for /dev to be fully populated...done. ======================================================================= -------------- next part -------------- An HTML attachment was scrubbed... URL: From khilman at ti.com Thu Jun 16 10:06:28 2011 From: khilman at ti.com (Kevin Hilman) Date: Thu, 16 Jun 2011 08:06:28 -0700 Subject: [PATCH V2] davinci: da850: move input frequency to board specific files In-Reply-To: (Sekhar Nori's message of "Tue, 14 Jun 2011 21:03:20 +0530") References: <1306312622-4901-1-git-send-email-christian.riesch@omicron.at> <1306488053-27978-1-git-send-email-christian.riesch@omicron.at> <874o42wpec.fsf@ti.com> <877h8xr4o0.fsf@ti.com> Message-ID: <87oc1xstmz.fsf@ti.com> "Nori, Sekhar" writes: > On Wed, Jun 08, 2011 at 17:38:25, Nori, Sekhar wrote: >> On Tue, Jun 07, 2011 at 21:53:59, Hilman, Kevin wrote: >> >> > I don't expect this to be a big boot-time impact. >> >> You are right. Using PRINTK_TIME on DM365 I saw no noticeable boot-time >> change. When I profiled the code using do_gettimeofday(), I saw it >> was taking ~95 usecs to complete the propagation. I was mainly worried >> about all the recursion and reading of PLL and sysclk registers. Seems >> its not so bad. >> >> > However, some of the clock.c assumptions might need to be updated as it >> > currently is written from the perspective that the PLL clocks are the >> > "root" clocks. >> >> Hmm, just calling clk_set_rate() on refclk propagated the rate >> nicely across the tree. It seems DaVinci clock code is not in >> such a bad shape :) Or did I miss the concern? >> >> > Setting (and propagating) clock rates is what the clock framework is >> > for, so adding a new interface to set a custom clock rate just doesn't >> > seem right. I understand that the reference oscillator might be >> > considered a special case, but if this can be done with the clock >> > framework, it is much preferred. >> >> Okay. Will modify the DM6467/T EVM code to use this method >> instead. > > So, here is the patch. I suspect reference clock information > should come from devicetree data when available. I hope it is > OK to take this approach till that time? > > Thanks, > Sekhar > > -----8<------------ > From: Sekhar Nori > Date: Thu, 2 Jun 2011 14:10:50 +0530 > Subject: [PATCH 1/1] davinci: dm6467/T EVM: fix setting up of reference clock rate > > The DM6467 and DM6467T EVMs use different reference clock > frequencies. This difference is currently supported by having > the SoC code call a public board routine which sets up the reference > clock frequency. This does not scale as more boards are added. > > Instead, use the clk_set_rate() API to setup the reference clock > frequency to a different value from the board file. > > Suggested-by: Kevin Hilman > Signed-off-by: Sekhar Nori Acked-by: Kevin Hilman From linlm at sict.ac.cn Thu Jun 16 21:00:40 2011 From: linlm at sict.ac.cn (=?GBK?B?wdbBosP3?=) Date: Fri, 17 Jun 2011 10:00:40 +0800 (CST) Subject: How to use GPMC to communicate with FPGA Message-ID: <4172af.94f6.1309b54217b.Coremail.linlm@sict.ac.cn> Hi,everyone I am writing a linux driver for omap3530 gpmc to communicate with a FPGA. I configure the GPMC to Synchronous,Data/Address non-multiplexed,16bit Mode. At first I write a simple code to check the read and write functions. Here is what I did: #define MY_CS 4 #define SLV_NUM 1 static void __iomem *gpmc_base; static void __iomem *gpmc_cs4_base=0; unsigned long cs4_mem_base; static u32 gpmc_read_reg(int idx) { return __raw_readl(gpmc_base + idx); } static void gpmc_write_reg(int idx, u32 val) { __raw_writel(val, gpmc_base + idx); } u32 read_from_mcb(int idx) { return __raw_readl(gpmc_cs4_base+idx*4); } void write_to_mcb(int idx, u32 val) { __raw_writel(val, gpmc_cs4_base+ idx*4); } int my_gpmc_init() { gpmc_write_reg(GPMC_SYSCONFIG, 0x18); gpmc_write_reg(GPMC_CONFIG, 0x12); //sychronous,16bit,NOR,no multiplexed,GPMC_CLK==GPMC_FCLK gpmc_cs_write_reg(MY_CS, GPMC_CS_CONFIG1,0x28001000); if (gpmc_cs_request(MY_CS, SZ_16M, &cs4_mem_base) < 0) { printk("<1>Failed to request GPMC mem for cs4\n"); return -1; } printk("<1>cs4_mem_base:%x\n",cs4_mem_base); gpmc_cs4_base=ioremap(cs4_mem_base,SZ_16M); return 0; } void mcb_init(void) { u32 i; int offset=0; //off mcb write_to_mcb(1, 0); //mcb configure write_to_mcb(0, 1);//comm.m_s = 1; write_to_mcb(3, SLV_NUM);//comm.slv_num = 1; write_to_mcb(4, 5);//comm.t_cyclic = 5; //500us for(i = 0; i < SLV_NUM; i++) { offset = 112 + i*50; write_to_mcb(offset, (i+3)<<8);//addr.addr_s = (i+3)<<8; write_to_mcb(offset+1, DA_M1);//addr.addr_m = DA_M1; } //on mcb write_to_mcb(1, 1); printk("<1>read back checking......\n"); printk("<1>on:%lu(1)\nm_s:%lu(1)\nslv_num:%lu(1)\nt_cyclic:%lu(5)\n",read_from_mcb(1),read_from_mcb(0),read_from_mcb(3),read_from_mcb(4)); } int init_module(void) { int ret; u32 l; unsigned long rate; l = OMAP34XX_GPMC_BASE; gpmc_base = ioremap(l, SZ_4K); my_gpmc_init(); if(gpmc_cs4_base!=0) { printk("<1>gpmc_cs4_base:%x",gpmc_cs4_base); mcb_init(); } return 1; } void cleanup_module(void) { printk("<1>cleanup module ......\n"); } As you see,in function mcb_init(void),I write data to the FPGA and read them back for checking. The expected results should be "1,1,1,5" but they turn out to be all zero! Have I done anything wrong? I use "gpmc_cs_request(MY_CS, SZ_16M, &cs4_mem_base) "to get the phsical address of FPGA, then ioremap it by using "gpmc_cs4_base=ioremap(cs4_mem_base,SZ_16M)".As told by TI employee ioremap is not necessary. http://e2e.ti.com/support/dsp/omap_applications_processors/f/447/t/62630.aspx But when I directly use "cs4_mem_base" as base address,I got the segmentation fault! By the way,gpmc synchronous mode is only for devices with limited 2K memory range. then how to use gpmc_cs_request() correctly? Is there anyone can help? thanks very much! 11-06-16 -------------------------------------------------------------------------------- Levin From manjunath.hadli at ti.com Fri Jun 17 02:01:30 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 17 Jun 2011 12:31:30 +0530 Subject: [RESEND PATCH v19 0/6] davinci vpbe: dm6446 v4l2 driver Message-ID: <1308294096-25743-1-git-send-email-manjunath.hadli@ti.com> fixed a wrong file inclusion in one of the patches Manjunath Hadli (6): davinci vpbe: V4L2 display driver for DM644X SoC davinci vpbe: VPBE display driver davinci vpbe: OSD(On Screen Display) block davinci vpbe: VENC( Video Encoder) implementation davinci vpbe: Build infrastructure for VPBE driver davinci vpbe: Readme text for Dm6446 vpbe Documentation/video4linux/README.davinci-vpbe | 93 ++ drivers/media/video/davinci/Kconfig | 23 + drivers/media/video/davinci/Makefile | 2 + drivers/media/video/davinci/vpbe.c | 864 ++++++++++++ drivers/media/video/davinci/vpbe_display.c | 1860 +++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd.c | 1231 ++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 364 +++++ drivers/media/video/davinci/vpbe_venc.c | 566 ++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 177 +++ include/media/davinci/vpbe.h | 184 +++ include/media/davinci/vpbe_display.h | 147 ++ include/media/davinci/vpbe_osd.h | 394 ++++++ include/media/davinci/vpbe_types.h | 91 ++ include/media/davinci/vpbe_venc.h | 45 + 14 files changed, 6041 insertions(+), 0 deletions(-) create mode 100644 Documentation/video4linux/README.davinci-vpbe create mode 100644 drivers/media/video/davinci/vpbe.c create mode 100644 drivers/media/video/davinci/vpbe_display.c create mode 100644 drivers/media/video/davinci/vpbe_osd.c create mode 100644 drivers/media/video/davinci/vpbe_osd_regs.h create mode 100644 drivers/media/video/davinci/vpbe_venc.c create mode 100644 drivers/media/video/davinci/vpbe_venc_regs.h create mode 100644 include/media/davinci/vpbe.h create mode 100644 include/media/davinci/vpbe_display.h create mode 100644 include/media/davinci/vpbe_osd.h create mode 100644 include/media/davinci/vpbe_types.h create mode 100644 include/media/davinci/vpbe_venc.h From manjunath.hadli at ti.com Fri Jun 17 02:01:36 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 17 Jun 2011 12:31:36 +0530 Subject: [RESEND PATCH v19 6/6] davinci vpbe: Readme text for Dm6446 vpbe In-Reply-To: <1308294096-25743-1-git-send-email-manjunath.hadli@ti.com> References: <1308294096-25743-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1308294096-25743-7-git-send-email-manjunath.hadli@ti.com> Please refer to this file for detailed documentation of davinci vpbe v4l2 driver. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- Documentation/video4linux/README.davinci-vpbe | 93 +++++++++++++++++++++++++ 1 files changed, 93 insertions(+), 0 deletions(-) create mode 100644 Documentation/video4linux/README.davinci-vpbe diff --git a/Documentation/video4linux/README.davinci-vpbe b/Documentation/video4linux/README.davinci-vpbe new file mode 100644 index 0000000..7a460b0 --- /dev/null +++ b/Documentation/video4linux/README.davinci-vpbe @@ -0,0 +1,93 @@ + + VPBE V4L2 driver design + ====================================================================== + + File partitioning + ----------------- + V4L2 display device driver + drivers/media/video/davinci/vpbe_display.c + drivers/media/video/davinci/vpbe_display.h + + VPBE display controller + drivers/media/video/davinci/vpbe.c + drivers/media/video/davinci/vpbe.h + + VPBE venc sub device driver + drivers/media/video/davinci/vpbe_venc.c + drivers/media/video/davinci/vpbe_venc.h + drivers/media/video/davinci/vpbe_venc_regs.h + + VPBE osd driver + drivers/media/video/davinci/vpbe_osd.c + drivers/media/video/davinci/vpbe_osd.h + drivers/media/video/davinci/vpbe_osd_regs.h + + Functional partitioning + ----------------------- + + Consists of the following (in the same order as the list under file + partitioning):- + + 1. V4L2 display driver + Implements creation of video2 and video3 device nodes and + provides v4l2 device interface to manage VID0 and VID1 layers. + + 2. Display controller + Loads up VENC, OSD and external encoders such as ths8200. It provides + a set of API calls to V4L2 drivers to set the output/standards + in the VENC or external sub devices. It also provides + a device object to access the services from OSD subdevice + using sub device ops. The connection of external encoders to VENC LCD + controller port is done at init time based on default output and standard + selection or at run time when application change the output through + V4L2 IOCTLs. + + When connected to an external encoder, vpbe controller is also responsible + for setting up the interface between VENC and external encoders based on + board specific settings (specified in board-xxx-evm.c). This allows + interfacing external encoders such as ths8200. The setup_if_config() + is implemented for this as well as configure_venc() (part of the next patch) + API to set timings in VENC for a specific display resolution. As of this + patch series, the interconnection and enabling and setting of the external + encoders is not present, and would be a part of the next patch series. + + 3. VENC subdevice module + Responsible for setting outputs provided through internal DACs and also + setting timings at LCD controller port when external encoders are connected + at the port or LCD panel timings required. When external encoder/LCD panel + is connected, the timings for a specific standard/preset is retrieved from + the board specific table and the values are used to set the timings in + venc using non-standard timing mode. + + Support LCD Panel displays using the VENC. For example to support a Logic + PD display, it requires setting up the LCD controller port with a set of + timings for the resolution supported and setting the dot clock. So we could + add the available outputs as a board specific entry (i.e add the "LogicPD" + output name to board-xxx-evm.c). A table of timings for various LCDs + supported can be maintained in the board specific setup file to support + various LCD displays.As of this patch a basic driver is present, and this + support for external encoders and displays forms a part of the next + patch series. + + 4. OSD module + OSD module implements all OSD layer management and hardware specific + features. The VPBE module interacts with the OSD for enabling and + disabling appropriate features of the OSD. + + Current status:- + + A fully functional working version of the V4L2 driver is available. This + driver has been tested with NTSC and PAL standards and buffer streaming. + + Following are TBDs. + + vpbe display controller + - Add support for external encoders. + - add support for selecting external encoder as default at probe time. + + vpbe venc sub device + - add timings for supporting ths8200 + - add support for LogicPD LCD. + + FB drivers + - Add support for fbdev drivers.- Ready and part of subsequent patches. -- 1.6.2.4 From manjunath.hadli at ti.com Fri Jun 17 02:01:35 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 17 Jun 2011 12:31:35 +0530 Subject: [RESEND PATCH v19 5/6] davinci vpbe: Build infrastructure for VPBE driver In-Reply-To: <1308294096-25743-1-git-send-email-manjunath.hadli@ti.com> References: <1308294096-25743-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1308294096-25743-6-git-send-email-manjunath.hadli@ti.com> This patch adds the build infra-structure for Davinci VPBE dislay driver. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- drivers/media/video/davinci/Kconfig | 23 +++++++++++++++++++++++ drivers/media/video/davinci/Makefile | 2 ++ 2 files changed, 25 insertions(+), 0 deletions(-) diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig index 6b19540..60a456e 100644 --- a/drivers/media/video/davinci/Kconfig +++ b/drivers/media/video/davinci/Kconfig @@ -91,3 +91,26 @@ config VIDEO_ISIF To compile this driver as a module, choose M here: the module will be called vpfe. + +config VIDEO_DM644X_VPBE + tristate "DM644X VPBE HW module" + depends on ARCH_DAVINCI_DM644x + select VIDEO_VPSS_SYSTEM + select VIDEOBUF_DMA_CONTIG + help + Enables VPBE modules used for display on a DM644x + SoC. + + To compile this driver as a module, choose M here: the + module will be called vpbe. + + +config VIDEO_VPBE_DISPLAY + tristate "VPBE V4L2 Display driver" + depends on ARCH_DAVINCI_DM644x + select VIDEO_DM644X_VPBE + help + Enables VPBE V4L2 Display driver on a DM644x device + + To compile this driver as a module, choose M here: the + module will be called vpbe_display. diff --git a/drivers/media/video/davinci/Makefile b/drivers/media/video/davinci/Makefile index a379557..ae7dafb 100644 --- a/drivers/media/video/davinci/Makefile +++ b/drivers/media/video/davinci/Makefile @@ -16,3 +16,5 @@ obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.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_DM644X_VPBE) += vpbe.o vpbe_osd.o vpbe_venc.o +obj-$(CONFIG_VIDEO_VPBE_DISPLAY) += vpbe_display.o -- 1.6.2.4 From manjunath.hadli at ti.com Fri Jun 17 02:01:32 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 17 Jun 2011 12:31:32 +0530 Subject: [RESEND PATCH v19 2/6] davinci vpbe: VPBE display driver In-Reply-To: <1308294096-25743-1-git-send-email-manjunath.hadli@ti.com> References: <1308294096-25743-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1308294096-25743-3-git-send-email-manjunath.hadli@ti.com> This patch implements the core functionality of the display driver, mainly controlling the VENC and other encoders, and acting as the one point interface for the main V4L2 driver. This implements the core of each of the V4L2 IOCTLs. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- drivers/media/video/davinci/vpbe.c | 864 ++++++++++++++++++++++++++++++++++++ include/media/davinci/vpbe.h | 184 ++++++++ 2 files changed, 1048 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpbe.c create mode 100644 include/media/davinci/vpbe.h diff --git a/drivers/media/video/davinci/vpbe.c b/drivers/media/video/davinci/vpbe.c new file mode 100644 index 0000000..d773d30 --- /dev/null +++ b/drivers/media/video/davinci/vpbe.c @@ -0,0 +1,864 @@ +/* + * Copyright (C) 2010 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 +#include + +#include +#include +#include +#include +#include + +#define VPBE_DEFAULT_OUTPUT "Composite" +#define VPBE_DEFAULT_MODE "ntsc" + +static char *def_output = VPBE_DEFAULT_OUTPUT; +static char *def_mode = VPBE_DEFAULT_MODE; +static int debug; + +module_param(def_output, charp, S_IRUGO); +module_param(def_mode, charp, S_IRUGO); +module_param(debug, int, 0644); + +MODULE_PARM_DESC(def_output, "vpbe output name (default:Composite)"); +MODULE_PARM_DESC(def_mode, "vpbe output mode name (default:ntsc"); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +MODULE_DESCRIPTION("TI DMXXX VPBE Display controller"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); + +/** + * vpbe_current_encoder_info - Get config info for current encoder + * @vpbe_dev - vpbe device ptr + * + * Return ptr to current encoder config info + */ +static struct encoder_config_info* +vpbe_current_encoder_info(struct vpbe_device *vpbe_dev) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + int index = vpbe_dev->current_sd_index; + + return ((index == 0) ? &cfg->venc : + &cfg->ext_encoders[index-1]); +} + +/** + * vpbe_find_encoder_sd_index - Given a name find encoder sd index + * + * @vpbe_config - ptr to vpbe cfg + * @output_index - index used by application + * + * Return sd index of the encoder + */ +static int vpbe_find_encoder_sd_index(struct vpbe_config *cfg, + int index) +{ + char *encoder_name = cfg->outputs[index].subdev_name; + int i; + + /* Venc is always first */ + if (!strcmp(encoder_name, cfg->venc.module_name)) + return 0; + + for (i = 0; i < cfg->num_ext_encoders; i++) { + if (!strcmp(encoder_name, + cfg->ext_encoders[i].module_name)) + return i+1; + } + + return -EINVAL; +} + +/** + * vpbe_g_cropcap - Get crop capabilities of the display + * @vpbe_dev - vpbe device ptr + * @cropcap - cropcap is a ptr to struct v4l2_cropcap + * + * Update the crop capabilities in crop cap for current + * mode + */ +static int vpbe_g_cropcap(struct vpbe_device *vpbe_dev, + struct v4l2_cropcap *cropcap) +{ + if (NULL == cropcap) + return -EINVAL; + cropcap->bounds.left = 0; + cropcap->bounds.top = 0; + cropcap->bounds.width = vpbe_dev->current_timings.xres; + cropcap->bounds.height = vpbe_dev->current_timings.yres; + cropcap->defrect = cropcap->bounds; + + return 0; +} + +/** + * vpbe_enum_outputs - enumerate outputs + * @vpbe_dev - vpbe device ptr + * @output - ptr to v4l2_output structure + * + * Enumerates the outputs available at the vpbe display + * returns the status, -EINVAL if end of output list + */ +static int vpbe_enum_outputs(struct vpbe_device *vpbe_dev, + struct v4l2_output *output) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + int temp_index = output->index; + + if (temp_index >= cfg->num_outputs) + return -EINVAL; + + *output = cfg->outputs[temp_index].output; + output->index = temp_index; + + return 0; +} + +static int vpbe_get_mode_info(struct vpbe_device *vpbe_dev, char *mode) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index; + int i; + + if (NULL == mode) + return -EINVAL; + + for (i = 0; i < cfg->outputs[curr_output].num_modes; i++) { + var = cfg->outputs[curr_output].modes[i]; + if (!strcmp(mode, var.name)) { + vpbe_dev->current_timings = var; + return 0; + } + } + + return -EINVAL; +} + +static int vpbe_get_current_mode_info(struct vpbe_device *vpbe_dev, + struct vpbe_enc_mode_info *mode_info) +{ + if (NULL == mode_info) + return -EINVAL; + + *mode_info = vpbe_dev->current_timings; + + return 0; +} + +static int vpbe_get_dv_preset_info(struct vpbe_device *vpbe_dev, + unsigned int dv_preset) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index; + int i; + + for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) { + var = cfg->outputs[curr_output].modes[i]; + if ((var.timings_type & VPBE_ENC_DV_PRESET) && + (var.timings.dv_preset == dv_preset)) { + vpbe_dev->current_timings = var; + return 0; + } + } + + return -EINVAL; +} + +/* Get std by std id */ +static int vpbe_get_std_info(struct vpbe_device *vpbe_dev, + v4l2_std_id std_id) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index; + int i; + + for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) { + var = cfg->outputs[curr_output].modes[i]; + if ((var.timings_type & VPBE_ENC_STD) && + (var.timings.std_id & std_id)) { + vpbe_dev->current_timings = var; + return 0; + } + } + + return -EINVAL; +} + +static int vpbe_get_std_info_by_name(struct vpbe_device *vpbe_dev, + char *std_name) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index; + int i; + + for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) { + var = cfg->outputs[curr_output].modes[i]; + if (!strcmp(var.name, std_name)) { + vpbe_dev->current_timings = var; + return 0; + } + } + + return -EINVAL; +} + +/** + * vpbe_set_output - Set output + * @vpbe_dev - vpbe device ptr + * @index - index of output + * + * Set vpbe output to the output specified by the index + */ +static int vpbe_set_output(struct vpbe_device *vpbe_dev, int index) +{ + struct encoder_config_info *curr_enc_info = + vpbe_current_encoder_info(vpbe_dev); + struct vpbe_config *cfg = vpbe_dev->cfg; + int enc_out_index; + int sd_index; + int ret = 0; + + if (index >= cfg->num_outputs) + return -EINVAL; + + mutex_lock(&vpbe_dev->lock); + + sd_index = vpbe_dev->current_sd_index; + enc_out_index = cfg->outputs[index].output.index; + /* + * Currently we switch the encoder based on output selected + * by the application. If media controller is implemented later + * there is will be an API added to setup_link between venc + * and external encoder. So in that case below comparison always + * match and encoder will not be switched. But if application + * chose not to use media controller, then this provides current + * way of switching encoder at the venc output. + */ + if (strcmp(curr_enc_info->module_name, + cfg->outputs[index].subdev_name)) { + /* Need to switch the encoder at the output */ + sd_index = vpbe_find_encoder_sd_index(cfg, index); + if (sd_index < 0) { + ret = -EINVAL; + goto out; + } + + if (ret) + goto out; + } + + /* Set output at the encoder */ + ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video, + s_routing, 0, enc_out_index, 0); + if (ret) + goto out; + + /* + * It is assumed that venc or extenal encoder will set a default + * mode in the sub device. For external encoder or LCD pannel output, + * we also need to set up the lcd port for the required mode. So setup + * the lcd port for the default mode that is configured in the board + * arch/arm/mach-davinci/board-dm355-evm.setup file for the external + * encoder. + */ + ret = vpbe_get_mode_info(vpbe_dev, + cfg->outputs[index].default_mode); + if (!ret) { + struct osd_state *osd_device = vpbe_dev->osd_device; + + osd_device->ops.set_left_margin(osd_device, + vpbe_dev->current_timings.left_margin); + osd_device->ops.set_top_margin(osd_device, + vpbe_dev->current_timings.upper_margin); + vpbe_dev->current_sd_index = sd_index; + vpbe_dev->current_out_index = index; + } +out: + mutex_unlock(&vpbe_dev->lock); + return ret; +} + +static int vpbe_set_default_output(struct vpbe_device *vpbe_dev) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + int ret = 0; + int i; + + for (i = 0; i < cfg->num_outputs; i++) { + if (!strcmp(def_output, + cfg->outputs[i].output.name)) { + ret = vpbe_set_output(vpbe_dev, i); + if (!ret) + vpbe_dev->current_out_index = i; + return ret; + } + } + return ret; +} + +/** + * vpbe_get_output - Get output + * @vpbe_dev - vpbe device ptr + * + * return current vpbe output to the the index + */ +static unsigned int vpbe_get_output(struct vpbe_device *vpbe_dev) +{ + return vpbe_dev->current_out_index; +} + +/** + * vpbe_s_dv_preset - Set the given preset timings in the encoder + * + * Sets the preset if supported by the current encoder. Return the status. + * 0 - success & -EINVAL on error + */ +static int vpbe_s_dv_preset(struct vpbe_device *vpbe_dev, + struct v4l2_dv_preset *dv_preset) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + int out_index = vpbe_dev->current_out_index; + int sd_index = vpbe_dev->current_sd_index; + int ret; + + + if (!(cfg->outputs[out_index].output.capabilities & + V4L2_OUT_CAP_PRESETS)) + return -EINVAL; + + ret = vpbe_get_dv_preset_info(vpbe_dev, dv_preset->preset); + + if (ret) + return ret; + + mutex_lock(&vpbe_dev->lock); + + + ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video, + s_dv_preset, dv_preset); + /* set the lcd controller output for the given mode */ + if (!ret) { + struct osd_state *osd_device = vpbe_dev->osd_device; + + osd_device->ops.set_left_margin(osd_device, + vpbe_dev->current_timings.left_margin); + osd_device->ops.set_top_margin(osd_device, + vpbe_dev->current_timings.upper_margin); + } + mutex_unlock(&vpbe_dev->lock); + + return ret; +} + +/** + * vpbe_g_dv_preset - Get the preset in the current encoder + * + * Get the preset in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int vpbe_g_dv_preset(struct vpbe_device *vpbe_dev, + struct v4l2_dv_preset *dv_preset) +{ + if (vpbe_dev->current_timings.timings_type & + VPBE_ENC_DV_PRESET) { + dv_preset->preset = vpbe_dev->current_timings.timings.dv_preset; + return 0; + } + + return -EINVAL; +} + +/** + * vpbe_enum_dv_presets - Enumerate the dv presets in the current encoder + * + * Get the preset in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int vpbe_enum_dv_presets(struct vpbe_device *vpbe_dev, + struct v4l2_dv_enum_preset *preset_info) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + int out_index = vpbe_dev->current_out_index; + struct vpbe_output *output = &cfg->outputs[out_index]; + int j = 0; + int i; + + if (!(output->output.capabilities & V4L2_OUT_CAP_PRESETS)) + return -EINVAL; + + for (i = 0; i < output->num_modes; i++) { + if (output->modes[i].timings_type == VPBE_ENC_DV_PRESET) { + if (j == preset_info->index) + break; + j++; + } + } + + if (i == output->num_modes) + return -EINVAL; + + return v4l_fill_dv_preset_info(output->modes[i].timings.dv_preset, + preset_info); +} + +/** + * vpbe_s_std - Set the given standard in the encoder + * + * Sets the standard if supported by the current encoder. Return the status. + * 0 - success & -EINVAL on error + */ +static int vpbe_s_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id) +{ + struct vpbe_config *cfg = vpbe_dev->cfg; + int out_index = vpbe_dev->current_out_index; + int sd_index = vpbe_dev->current_sd_index; + int ret; + + if (!(cfg->outputs[out_index].output.capabilities & + V4L2_OUT_CAP_STD)) + return -EINVAL; + + ret = vpbe_get_std_info(vpbe_dev, *std_id); + if (ret) + return ret; + + mutex_lock(&vpbe_dev->lock); + + ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], video, + s_std_output, *std_id); + /* set the lcd controller output for the given mode */ + if (!ret) { + struct osd_state *osd_device = vpbe_dev->osd_device; + + osd_device->ops.set_left_margin(osd_device, + vpbe_dev->current_timings.left_margin); + osd_device->ops.set_top_margin(osd_device, + vpbe_dev->current_timings.upper_margin); + } + mutex_unlock(&vpbe_dev->lock); + + return ret; +} + +/** + * vpbe_g_std - Get the standard in the current encoder + * + * Get the standard in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int vpbe_g_std(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id) +{ + struct vpbe_enc_mode_info cur_timings = vpbe_dev->current_timings; + + if (cur_timings.timings_type & VPBE_ENC_STD) { + *std_id = cur_timings.timings.std_id; + return 0; + } + + return -EINVAL; +} + +/** + * vpbe_set_mode - Set mode in the current encoder using mode info + * + * Use the mode string to decide what timings to set in the encoder + * This is typically useful when fbset command is used to change the current + * timings by specifying a string to indicate the timings. + */ +static int vpbe_set_mode(struct vpbe_device *vpbe_dev, + struct vpbe_enc_mode_info *mode_info) +{ + struct vpbe_enc_mode_info *preset_mode = NULL; + struct vpbe_config *cfg = vpbe_dev->cfg; + struct v4l2_dv_preset dv_preset; + struct osd_state *osd_device; + int out_index = vpbe_dev->current_out_index; + int ret = 0; + int i; + + if ((NULL == mode_info) || (NULL == mode_info->name)) + return -EINVAL; + + for (i = 0; i < cfg->outputs[out_index].num_modes; i++) { + if (!strcmp(mode_info->name, + cfg->outputs[out_index].modes[i].name)) { + preset_mode = &cfg->outputs[out_index].modes[i]; + /* + * it may be one of the 3 timings type. Check and + * invoke right API + */ + if (preset_mode->timings_type & VPBE_ENC_STD) + return vpbe_s_std(vpbe_dev, + &preset_mode->timings.std_id); + if (preset_mode->timings_type & VPBE_ENC_DV_PRESET) { + dv_preset.preset = + preset_mode->timings.dv_preset; + return vpbe_s_dv_preset(vpbe_dev, &dv_preset); + } + } + } + + /* Only custom timing should reach here */ + if (preset_mode == NULL) + return -EINVAL; + + mutex_lock(&vpbe_dev->lock); + + osd_device = vpbe_dev->osd_device; + vpbe_dev->current_timings = *preset_mode; + osd_device->ops.set_left_margin(osd_device, + vpbe_dev->current_timings.left_margin); + osd_device->ops.set_top_margin(osd_device, + vpbe_dev->current_timings.upper_margin); + + mutex_unlock(&vpbe_dev->lock); + + return ret; +} + +static int vpbe_set_default_mode(struct vpbe_device *vpbe_dev) +{ + int ret; + + ret = vpbe_get_std_info_by_name(vpbe_dev, def_mode); + if (ret) + return ret; + + /* set the default mode in the encoder */ + return vpbe_set_mode(vpbe_dev, &vpbe_dev->current_timings); +} + +static int platform_device_get(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct vpbe_device *vpbe_dev = data; + + if (strcmp("vpbe-osd", pdev->name) == 0) + vpbe_dev->osd_device = platform_get_drvdata(pdev); + + return 0; +} + +/** + * vpbe_initialize() - Initialize the vpbe display controller + * @vpbe_dev - vpbe device ptr + * + * Master frame buffer device drivers calls this to initialize vpbe + * display controller. This will then registers v4l2 device and the sub + * devices and sets a current encoder sub device for display. v4l2 display + * device driver is the master and frame buffer display device driver is + * the slave. Frame buffer display driver checks the initialized during + * probe and exit if not initialized. Returns status. + */ +static int vpbe_initialize(struct device *dev, struct vpbe_device *vpbe_dev) +{ + struct encoder_config_info *enc_info; + struct v4l2_subdev **enc_subdev; + struct osd_state *osd_device; + struct i2c_adapter *i2c_adap; + int output_index; + int num_encoders; + int ret = 0; + int err; + int i; + + /* + * v4l2 abd FBDev frame buffer devices will get the vpbe_dev pointer + * from the platform device by iteration of platform drivers and + * matching with device name + */ + if (NULL == vpbe_dev || NULL == dev) { + printk(KERN_ERR "Null device pointers.\n"); + return -ENODEV; + } + + if (vpbe_dev->initialized) + return 0; + + mutex_lock(&vpbe_dev->lock); + + if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) { + /* We have dac clock available for platform */ + vpbe_dev->dac_clk = clk_get(vpbe_dev->pdev, "vpss_dac"); + if (IS_ERR(vpbe_dev->dac_clk)) { + ret = PTR_ERR(vpbe_dev->dac_clk); + goto vpbe_unlock; + } + if (clk_enable(vpbe_dev->dac_clk)) { + ret = -ENODEV; + goto vpbe_unlock; + } + } + + /* first enable vpss clocks */ + vpss_enable_clock(VPSS_VPBE_CLOCK, 1); + + /* First register a v4l2 device */ + ret = v4l2_device_register(dev, &vpbe_dev->v4l2_dev); + if (ret) { + v4l2_err(dev->driver, + "Unable to register v4l2 device.\n"); + goto vpbe_fail_clock; + } + v4l2_info(&vpbe_dev->v4l2_dev, "vpbe v4l2 device registered\n"); + + err = bus_for_each_dev(&platform_bus_type, NULL, vpbe_dev, + platform_device_get); + if (err < 0) + return err; + + vpbe_dev->venc = venc_sub_dev_init(&vpbe_dev->v4l2_dev, + vpbe_dev->cfg->venc.module_name); + /* register venc sub device */ + if (vpbe_dev->venc == NULL) { + v4l2_err(&vpbe_dev->v4l2_dev, + "vpbe unable to init venc sub device\n"); + ret = -ENODEV; + goto vpbe_fail_v4l2_device; + } + /* initialize osd device */ + osd_device = vpbe_dev->osd_device; + + if (NULL != osd_device->ops.initialize) { + err = osd_device->ops.initialize(osd_device); + if (err) { + v4l2_err(&vpbe_dev->v4l2_dev, + "unable to initialize the OSD device"); + err = -ENOMEM; + goto vpbe_fail_v4l2_device; + } + } + + /* + * Register any external encoders that are configured. At index 0 we + * store venc sd index. + */ + num_encoders = vpbe_dev->cfg->num_ext_encoders + 1; + vpbe_dev->encoders = kmalloc( + sizeof(struct v4l2_subdev *)*num_encoders, + GFP_KERNEL); + if (NULL == vpbe_dev->encoders) { + v4l2_err(&vpbe_dev->v4l2_dev, + "unable to allocate memory for encoders sub devices"); + ret = -ENOMEM; + goto vpbe_fail_v4l2_device; + } + + i2c_adap = i2c_get_adapter(vpbe_dev->cfg->i2c_adapter_id); + for (i = 0; i < (vpbe_dev->cfg->num_ext_encoders + 1); i++) { + if (i == 0) { + /* venc is at index 0 */ + enc_subdev = &vpbe_dev->encoders[i]; + *enc_subdev = vpbe_dev->venc; + continue; + } + enc_info = &vpbe_dev->cfg->ext_encoders[i]; + if (enc_info->is_i2c) { + enc_subdev = &vpbe_dev->encoders[i]; + *enc_subdev = v4l2_i2c_new_subdev_board( + &vpbe_dev->v4l2_dev, i2c_adap, + &enc_info->board_info, NULL); + if (*enc_subdev) + v4l2_info(&vpbe_dev->v4l2_dev, + "v4l2 sub device %s registered\n", + enc_info->module_name); + else { + v4l2_err(&vpbe_dev->v4l2_dev, "encoder %s" + " failed to register", + enc_info->module_name); + ret = -ENODEV; + goto vpbe_fail_sd_register; + } + } else + v4l2_warn(&vpbe_dev->v4l2_dev, "non-i2c encoders" + " currently not supported"); + } + + /* set the current encoder and output to that of venc by default */ + vpbe_dev->current_sd_index = 0; + vpbe_dev->current_out_index = 0; + output_index = 0; + + mutex_unlock(&vpbe_dev->lock); + + printk(KERN_NOTICE "Setting default output to %s\n", def_output); + ret = vpbe_set_default_output(vpbe_dev); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default output %s", + def_output); + return ret; + } + + printk(KERN_NOTICE "Setting default mode to %s\n", def_mode); + ret = vpbe_set_default_mode(vpbe_dev); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, "Failed to set default mode %s", + def_mode); + return ret; + } + vpbe_dev->initialized = 1; + /* TBD handling of bootargs for default output and mode */ + return 0; + +vpbe_fail_sd_register: + kfree(vpbe_dev->encoders); +vpbe_fail_v4l2_device: + v4l2_device_unregister(&vpbe_dev->v4l2_dev); +vpbe_fail_clock: + if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) + clk_put(vpbe_dev->dac_clk); +vpbe_unlock: + mutex_unlock(&vpbe_dev->lock); + return ret; +} + +/** + * vpbe_deinitialize() - de-initialize the vpbe display controller + * @dev - Master and slave device ptr + * + * vpbe_master and slave frame buffer devices calls this to de-initialize + * the display controller. It is called when master and slave device + * driver modules are removed and no longer requires the display controller. + */ +static void vpbe_deinitialize(struct device *dev, struct vpbe_device *vpbe_dev) +{ + v4l2_device_unregister(&vpbe_dev->v4l2_dev); + if (strcmp(vpbe_dev->cfg->module_name, "dm644x-vpbe-display") != 0) + clk_put(vpbe_dev->dac_clk); + + kfree(vpbe_dev->encoders); + vpbe_dev->initialized = 0; + /* disable vpss clocks */ + vpss_enable_clock(VPSS_VPBE_CLOCK, 0); +} + +static struct vpbe_device_ops vpbe_dev_ops = { + .g_cropcap = vpbe_g_cropcap, + .enum_outputs = vpbe_enum_outputs, + .set_output = vpbe_set_output, + .get_output = vpbe_get_output, + .s_dv_preset = vpbe_s_dv_preset, + .g_dv_preset = vpbe_g_dv_preset, + .enum_dv_presets = vpbe_enum_dv_presets, + .s_std = vpbe_s_std, + .g_std = vpbe_g_std, + .initialize = vpbe_initialize, + .deinitialize = vpbe_deinitialize, + .get_mode_info = vpbe_get_current_mode_info, + .set_mode = vpbe_set_mode, +}; + +static __devinit int vpbe_probe(struct platform_device *pdev) +{ + struct vpbe_device *vpbe_dev; + struct vpbe_config *cfg; + int ret = -EINVAL; + + if (pdev->dev.platform_data == NULL) { + v4l2_err(pdev->dev.driver, "No platform data\n"); + return -ENODEV; + } + cfg = pdev->dev.platform_data; + + if (!cfg->module_name[0] || + !cfg->osd.module_name[0] || + !cfg->venc.module_name[0]) { + v4l2_err(pdev->dev.driver, "vpbe display module names not" + " defined\n"); + return ret; + } + + vpbe_dev = kzalloc(sizeof(*vpbe_dev), GFP_KERNEL); + if (vpbe_dev == NULL) { + v4l2_err(pdev->dev.driver, "Unable to allocate memory" + " for vpbe_device\n"); + return -ENOMEM; + } + vpbe_dev->cfg = cfg; + vpbe_dev->ops = vpbe_dev_ops; + vpbe_dev->pdev = &pdev->dev; + + if (cfg->outputs->num_modes > 0) + vpbe_dev->current_timings = vpbe_dev->cfg->outputs[0].modes[0]; + else + return -ENODEV; + + /* set the driver data in platform device */ + platform_set_drvdata(pdev, vpbe_dev); + mutex_init(&vpbe_dev->lock); + + return 0; +} + +static int vpbe_remove(struct platform_device *device) +{ + struct vpbe_device *vpbe_dev = platform_get_drvdata(device); + + kfree(vpbe_dev); + + return 0; +} + +static struct platform_driver vpbe_driver = { + .driver = { + .name = "vpbe_controller", + .owner = THIS_MODULE, + }, + .probe = vpbe_probe, + .remove = vpbe_remove, +}; + +/** + * vpbe_init: initialize the vpbe driver + * + * This function registers device and driver to the kernel + */ +static __init int vpbe_init(void) +{ + return platform_driver_register(&vpbe_driver); +} + +/** + * vpbe_cleanup : cleanup function for vpbe driver + * + * This will un-registers the device and driver to the kernel + */ +static void vpbe_cleanup(void) +{ + platform_driver_unregister(&vpbe_driver); +} + +/* Function for module initialization and cleanup */ +module_init(vpbe_init); +module_exit(vpbe_cleanup); diff --git a/include/media/davinci/vpbe.h b/include/media/davinci/vpbe.h new file mode 100644 index 0000000..8b11fb0 --- /dev/null +++ b/include/media/davinci/vpbe.h @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2010 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 _VPBE_H +#define _VPBE_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +/* OSD configuration info */ +struct osd_config_info { + char module_name[32]; +}; + +struct vpbe_output { + struct v4l2_output output; + /* + * If output capabilities include dv_preset, list supported presets + * below + */ + char *subdev_name; + /* + * defualt_mode identifies the default timings set at the venc or + * external encoder. + */ + char *default_mode; + /* + * Fields below are used for supporting multiple modes. For example, + * LCD panel might support different modes and they are listed here. + * Similarly for supporting external encoders, lcd controller port + * requires a set of non-standard timing values to be listed here for + * each supported mode since venc is used in non-standard timing mode + * for interfacing with external encoder similar to configuring lcd + * panel timings + */ + unsigned int num_modes; + struct vpbe_enc_mode_info *modes; + /* + * Bus configuration goes here for external encoders. Some encoders + * may require multiple interface types for each of the output. For + * example, SD modes would use YCC8 where as HD mode would use YCC16. + * Not sure if this is needed on a per mode basis instead of per + * output basis. If per mode is needed, we may have to move this to + * mode_info structure + */ +}; + +/* encoder configuration info */ +struct encoder_config_info { + char module_name[32]; + /* Is this an i2c device ? */ + unsigned int is_i2c:1; + /* i2c subdevice board info */ + struct i2c_board_info board_info; +}; + +/* structure for defining vpbe display subsystem components */ +struct vpbe_config { + char module_name[32]; + /* i2c bus adapter no */ + int i2c_adapter_id; + struct osd_config_info osd; + struct encoder_config_info venc; + /* external encoder information goes here */ + int num_ext_encoders; + struct encoder_config_info *ext_encoders; + int num_outputs; + /* Order is venc outputs followed by LCD and then external encoders */ + struct vpbe_output *outputs; +}; + +struct vpbe_device; + +struct vpbe_device_ops { + /* crop cap for the display */ + int (*g_cropcap)(struct vpbe_device *vpbe_dev, + struct v4l2_cropcap *cropcap); + + /* Enumerate the outputs */ + int (*enum_outputs)(struct vpbe_device *vpbe_dev, + struct v4l2_output *output); + + /* Set output to the given index */ + int (*set_output)(struct vpbe_device *vpbe_dev, + int index); + + /* Get current output */ + unsigned int (*get_output)(struct vpbe_device *vpbe_dev); + + /* Set DV preset at current output */ + int (*s_dv_preset)(struct vpbe_device *vpbe_dev, + struct v4l2_dv_preset *dv_preset); + + /* Get DV presets supported at the output */ + int (*g_dv_preset)(struct vpbe_device *vpbe_dev, + struct v4l2_dv_preset *dv_preset); + + /* Enumerate the DV Presets supported at the output */ + int (*enum_dv_presets)(struct vpbe_device *vpbe_dev, + struct v4l2_dv_enum_preset *preset_info); + + /* Set std at the output */ + int (*s_std)(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id); + + /* Get the current std at the output */ + int (*g_std)(struct vpbe_device *vpbe_dev, v4l2_std_id *std_id); + + /* initialize the device */ + int (*initialize)(struct device *dev, struct vpbe_device *vpbe_dev); + + /* De-initialize the device */ + void (*deinitialize)(struct device *dev, struct vpbe_device *vpbe_dev); + + /* Get the current mode info */ + int (*get_mode_info)(struct vpbe_device *vpbe_dev, + struct vpbe_enc_mode_info*); + + /* + * Set the current mode in the encoder. Alternate way of setting + * standard or DV preset or custom timings in the encoder + */ + int (*set_mode)(struct vpbe_device *vpbe_dev, + struct vpbe_enc_mode_info*); + /* Power management operations */ + int (*suspend)(struct vpbe_device *vpbe_dev); + int (*resume)(struct vpbe_device *vpbe_dev); +}; + +/* struct for vpbe device */ +struct vpbe_device { + /* V4l2 device */ + struct v4l2_device v4l2_dev; + /* vpbe dispay controller cfg */ + struct vpbe_config *cfg; + /* parent device */ + struct device *pdev; + /* external encoder v4l2 sub devices */ + struct v4l2_subdev **encoders; + /* current encoder index */ + int current_sd_index; + struct mutex lock; + /* device initialized */ + int initialized; + /* vpbe dac clock */ + struct clk *dac_clk; + /* osd_device pointer */ + struct osd_state *osd_device; + /* + * fields below are accessed by users of vpbe_device. Not the + * ones above + */ + + /* current output */ + int current_out_index; + /* lock used by caller to do atomic operation on vpbe device */ + /* current timings set in the controller */ + struct vpbe_enc_mode_info current_timings; + /* venc sub device */ + struct v4l2_subdev *venc; + /* device operations below */ + struct vpbe_device_ops ops; +}; + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Fri Jun 17 02:01:34 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 17 Jun 2011 12:31:34 +0530 Subject: [RESEND PATCH v19 4/6] davinci vpbe: VENC( Video Encoder) implementation In-Reply-To: <1308294096-25743-1-git-send-email-manjunath.hadli@ti.com> References: <1308294096-25743-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1308294096-25743-5-git-send-email-manjunath.hadli@ti.com> This patch adds the VENC or the Video encoder, which is responsible for the blending of all source planes and timing generation for Video modes like NTSC, PAL and other digital outputs. the VENC implementation currently supports COMPOSITE and COMPONENT outputs and NTSC and PAL resolutions through the analog DACs. The venc block is implemented as a subdevice, allowing for additional external and internal encoders of other kind to plug-in. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- drivers/media/video/davinci/vpbe_venc.c | 566 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 177 ++++++++ include/media/davinci/vpbe_venc.h | 45 ++ 3 files changed, 788 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpbe_venc.c create mode 100644 drivers/media/video/davinci/vpbe_venc_regs.h create mode 100644 include/media/davinci/vpbe_venc.h diff --git a/drivers/media/video/davinci/vpbe_venc.c b/drivers/media/video/davinci/vpbe_venc.c new file mode 100644 index 0000000..03a3e5c --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc.c @@ -0,0 +1,566 @@ +/* + * Copyright (C) 2010 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 +#include +#include + +#include + +#include +#include +#include +#include + +#include "vpbe_venc_regs.h" + +#define MODULE_NAME VPBE_VENC_SUBDEV_NAME + +static int debug = 2; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-2"); + +struct venc_state { + struct v4l2_subdev sd; + struct venc_callback *callback; + struct venc_platform_data *pdata; + struct device *pdev; + u32 output; + v4l2_std_id std; + spinlock_t lock; + void __iomem *venc_base; + void __iomem *vdaccfg_reg; +}; + +static inline struct venc_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct venc_state, sd); +} + +static inline u32 venc_read(struct v4l2_subdev *sd, u32 offset) +{ + struct venc_state *venc = to_state(sd); + + return readl(venc->venc_base + offset); +} + +static inline u32 venc_write(struct v4l2_subdev *sd, u32 offset, u32 val) +{ + struct venc_state *venc = to_state(sd); + + writel(val, (venc->venc_base + offset)); + + return val; +} + +static inline u32 venc_modify(struct v4l2_subdev *sd, u32 offset, + u32 val, u32 mask) +{ + u32 new_val = (venc_read(sd, offset) & ~mask) | (val & mask); + + venc_write(sd, offset, new_val); + + return new_val; +} + +static inline u32 vdaccfg_write(struct v4l2_subdev *sd, u32 val) +{ + struct venc_state *venc = to_state(sd); + + writel(val, venc->vdaccfg_reg); + + val = readl(venc->vdaccfg_reg); + + return val; +} + +/* This function sets the dac of the VPBE for various outputs + */ +static int venc_set_dac(struct v4l2_subdev *sd, u32 out_index) +{ + switch (out_index) { + case 0: + v4l2_dbg(debug, 1, sd, "Setting output to Composite\n"); + venc_write(sd, VENC_DACSEL, 0); + break; + case 1: + v4l2_dbg(debug, 1, sd, "Setting output to S-Video\n"); + venc_write(sd, VENC_DACSEL, 0x210); + break; + case 2: + venc_write(sd, VENC_DACSEL, 0x543); + break; + default: + return -EINVAL; + } + + return 0; +} + +static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable) +{ + v4l2_dbg(debug, 2, sd, "venc_enabledigitaloutput\n"); + + if (benable) { + venc_write(sd, VENC_VMOD, 0); + venc_write(sd, VENC_CVBS, 0); + venc_write(sd, VENC_LCDOUT, 0); + venc_write(sd, VENC_HSPLS, 0); + venc_write(sd, VENC_HSTART, 0); + venc_write(sd, VENC_HVALID, 0); + venc_write(sd, VENC_HINT, 0); + venc_write(sd, VENC_VSPLS, 0); + venc_write(sd, VENC_VSTART, 0); + venc_write(sd, VENC_VVALID, 0); + venc_write(sd, VENC_VINT, 0); + venc_write(sd, VENC_YCCCTL, 0); + venc_write(sd, VENC_DACSEL, 0); + + } else { + venc_write(sd, VENC_VMOD, 0); + /* disable VCLK output pin enable */ + venc_write(sd, VENC_VIDCTL, 0x141); + + /* Disable output sync pins */ + venc_write(sd, VENC_SYNCCTL, 0); + + /* Disable DCLOCK */ + venc_write(sd, VENC_DCLKCTL, 0); + venc_write(sd, VENC_DRGBX1, 0x0000057C); + + /* Disable LCD output control (accepting default polarity) */ + venc_write(sd, VENC_LCDOUT, 0); + venc_write(sd, VENC_CMPNT, 0x100); + venc_write(sd, VENC_HSPLS, 0); + venc_write(sd, VENC_HINT, 0); + venc_write(sd, VENC_HSTART, 0); + venc_write(sd, VENC_HVALID, 0); + + venc_write(sd, VENC_VSPLS, 0); + venc_write(sd, VENC_VINT, 0); + venc_write(sd, VENC_VSTART, 0); + venc_write(sd, VENC_VVALID, 0); + + venc_write(sd, VENC_HSDLY, 0); + venc_write(sd, VENC_VSDLY, 0); + + venc_write(sd, VENC_YCCCTL, 0); + venc_write(sd, VENC_VSTARTA, 0); + + /* Set OSD clock and OSD Sync Adavance registers */ + venc_write(sd, VENC_OSDCLK0, 1); + venc_write(sd, VENC_OSDCLK1, 2); + } +} + +/* + * setting NTSC mode + */ +static int venc_set_ntsc(struct v4l2_subdev *sd) +{ + struct venc_state *venc = to_state(sd); + struct venc_platform_data *pdata = venc->pdata; + + v4l2_dbg(debug, 2, sd, "venc_set_ntsc\n"); + + /* Setup clock at VPSS & VENC for SD */ + vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1); + if (pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_525_60) < 0) + return -EINVAL; + + venc_enabledigitaloutput(sd, 0); + + /* to set VENC CLK DIV to 1 - final clock is 54 MHz */ + venc_modify(sd, VENC_VIDCTL, 0, 1 << 1); + /* Set REC656 Mode */ + venc_write(sd, VENC_YCCCTL, 0x1); + venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAFRQ); + venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAUPS); + + venc_write(sd, VENC_VMOD, 0); + venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT), + VENC_VMOD_VIE); + venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_VMD), VENC_VMOD_VMD); + venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_TVTYP_SHIFT), + VENC_VMOD_TVTYP); + venc_write(sd, VENC_DACTST, 0x0); + venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); + + return 0; +} + +/* + * setting PAL mode + */ +static int venc_set_pal(struct v4l2_subdev *sd) +{ + struct venc_state *venc = to_state(sd); + + v4l2_dbg(debug, 2, sd, "venc_set_pal\n"); + + /* Setup clock at VPSS & VENC for SD */ + vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1); + if (venc->pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_625_50) < 0) + return -EINVAL; + + venc_enabledigitaloutput(sd, 0); + + /* to set VENC CLK DIV to 1 - final clock is 54 MHz */ + venc_modify(sd, VENC_VIDCTL, 0, 1 << 1); + /* Set REC656 Mode */ + venc_write(sd, VENC_YCCCTL, 0x1); + + venc_modify(sd, VENC_SYNCCTL, 1 << VENC_SYNCCTL_OVD_SHIFT, + VENC_SYNCCTL_OVD); + venc_write(sd, VENC_VMOD, 0); + venc_modify(sd, VENC_VMOD, + (1 << VENC_VMOD_VIE_SHIFT), + VENC_VMOD_VIE); + venc_modify(sd, VENC_VMOD, + (0 << VENC_VMOD_VMD), VENC_VMOD_VMD); + venc_modify(sd, VENC_VMOD, + (1 << VENC_VMOD_TVTYP_SHIFT), + VENC_VMOD_TVTYP); + venc_write(sd, VENC_DACTST, 0x0); + venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); + + return 0; +} + +/* + * venc_set_480p59_94 + * + * This function configures the video encoder to EDTV(525p) component setting. + */ +static int venc_set_480p59_94(struct v4l2_subdev *sd) +{ + struct venc_state *venc = to_state(sd); + struct venc_platform_data *pdata = venc->pdata; + + v4l2_dbg(debug, 2, sd, "venc_set_480p59_94\n"); + + /* Setup clock at VPSS & VENC for SD */ + if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_480P59_94) < 0) + return -EINVAL; + + venc_enabledigitaloutput(sd, 0); + + venc_write(sd, VENC_OSDCLK0, 0); + venc_write(sd, VENC_OSDCLK1, 1); + venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, + VENC_VDPRO_DAFRQ); + venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, + VENC_VDPRO_DAUPS); + venc_write(sd, VENC_VMOD, 0); + venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT), + VENC_VMOD_VIE); + venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD); + venc_modify(sd, VENC_VMOD, (HDTV_525P << VENC_VMOD_TVTYP_SHIFT), + VENC_VMOD_TVTYP); + venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 << + VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD); + + venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); + + return 0; +} + +/* + * venc_set_625p + * + * This function configures the video encoder to HDTV(625p) component setting + */ +static int venc_set_576p50(struct v4l2_subdev *sd) +{ + struct venc_state *venc = to_state(sd); + struct venc_platform_data *pdata = venc->pdata; + + v4l2_dbg(debug, 2, sd, "venc_set_576p50\n"); + + /* Setup clock at VPSS & VENC for SD */ + if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_576P50) < 0) + return -EINVAL; + + venc_enabledigitaloutput(sd, 0); + + venc_write(sd, VENC_OSDCLK0, 0); + venc_write(sd, VENC_OSDCLK1, 1); + + venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, + VENC_VDPRO_DAFRQ); + venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, + VENC_VDPRO_DAUPS); + + venc_write(sd, VENC_VMOD, 0); + venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT), + VENC_VMOD_VIE); + venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD); + venc_modify(sd, VENC_VMOD, (HDTV_625P << VENC_VMOD_TVTYP_SHIFT), + VENC_VMOD_TVTYP); + + venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 << + VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD); + venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); + + return 0; +} + +static int venc_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + v4l2_dbg(debug, 1, sd, "venc_s_std_output\n"); + + if (norm & V4L2_STD_525_60) + return venc_set_ntsc(sd); + else if (norm & V4L2_STD_625_50) + return venc_set_pal(sd); + + return -EINVAL; +} + +static int venc_s_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *dv_preset) +{ + v4l2_dbg(debug, 1, sd, "venc_s_dv_preset\n"); + + if (dv_preset->preset == V4L2_DV_576P50) + return venc_set_576p50(sd); + else if (dv_preset->preset == V4L2_DV_480P59_94) + return venc_set_480p59_94(sd); + + return -EINVAL; +} + +static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct venc_state *venc = to_state(sd); + int ret; + + v4l2_dbg(debug, 1, sd, "venc_s_routing\n"); + + ret = venc_set_dac(sd, output); + if (!ret) + venc->output = output; + + return ret; +} + +static long venc_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, + void *arg) +{ + u32 val; + + switch (cmd) { + case VENC_GET_FLD: + val = venc_read(sd, VENC_VSTAT); + *((int *)arg) = ((val & VENC_VSTAT_FIDST) == + VENC_VSTAT_FIDST); + break; + default: + v4l2_err(sd, "Wrong IOCTL cmd\n"); + break; + } + + return 0; +} + +static const struct v4l2_subdev_core_ops venc_core_ops = { + .ioctl = venc_ioctl, +}; + +static const struct v4l2_subdev_video_ops venc_video_ops = { + .s_routing = venc_s_routing, + .s_std_output = venc_s_std_output, + .s_dv_preset = venc_s_dv_preset, +}; + +static const struct v4l2_subdev_ops venc_ops = { + .core = &venc_core_ops, + .video = &venc_video_ops, +}; + +static int venc_initialize(struct v4l2_subdev *sd) +{ + struct venc_state *venc = to_state(sd); + int ret; + + /* Set default to output to composite and std to NTSC */ + venc->output = 0; + venc->std = V4L2_STD_525_60; + + ret = venc_s_routing(sd, 0, venc->output, 0); + if (ret < 0) { + v4l2_err(sd, "Error setting output during init\n"); + return -EINVAL; + } + + ret = venc_s_std_output(sd, venc->std); + if (ret < 0) { + v4l2_err(sd, "Error setting std during init\n"); + return -EINVAL; + } + + return ret; +} + +static int venc_device_get(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct venc_state **venc = data; + + if (strcmp(MODULE_NAME, pdev->name) == 0) + *venc = platform_get_drvdata(pdev); + + return 0; +} + +struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev, + const char *venc_name) +{ + struct venc_state *venc; + int err; + + err = bus_for_each_dev(&platform_bus_type, NULL, &venc, + venc_device_get); + if (venc == NULL) + return NULL; + + v4l2_subdev_init(&venc->sd, &venc_ops); + + strcpy(venc->sd.name, venc_name); + if (v4l2_device_register_subdev(v4l2_dev, &venc->sd) < 0) { + v4l2_err(v4l2_dev, + "vpbe unable to register venc sub device\n"); + return NULL; + } + if (venc_initialize(&venc->sd)) { + v4l2_err(v4l2_dev, + "vpbe venc initialization failed\n"); + return NULL; + } + + return &venc->sd; +} +EXPORT_SYMBOL(venc_sub_dev_init); + +static int venc_probe(struct platform_device *pdev) +{ + struct venc_state *venc; + struct resource *res; + int ret; + + venc = kzalloc(sizeof(struct venc_state), GFP_KERNEL); + if (venc == NULL) + return -ENOMEM; + + venc->pdev = &pdev->dev; + venc->pdata = pdev->dev.platform_data; + if (NULL == venc->pdata) { + dev_err(venc->pdev, "Unable to get platform data for" + " VENC sub device"); + ret = -ENOENT; + goto free_mem; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(venc->pdev, + "Unable to get VENC register address map\n"); + ret = -ENODEV; + goto free_mem; + } + + if (!request_mem_region(res->start, resource_size(res), "venc")) { + dev_err(venc->pdev, "Unable to reserve VENC MMIO region\n"); + ret = -ENODEV; + goto free_mem; + } + + venc->venc_base = ioremap_nocache(res->start, resource_size(res)); + if (!venc->venc_base) { + dev_err(venc->pdev, "Unable to map VENC IO space\n"); + ret = -ENODEV; + goto release_venc_mem_region; + } + + spin_lock_init(&venc->lock); + platform_set_drvdata(pdev, venc); + dev_notice(venc->pdev, "VENC sub device probe success\n"); + return 0; + +release_venc_mem_region: + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); +free_mem: + kfree(venc); + return ret; +} + +static int venc_remove(struct platform_device *pdev) +{ + struct venc_state *venc = platform_get_drvdata(pdev); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap((void *)venc->venc_base); + release_mem_region(res->start, resource_size(res)); + kfree(venc); + + return 0; +} + +static struct platform_driver venc_driver = { + .probe = venc_probe, + .remove = venc_remove, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + }, +}; + +static int venc_init(void) +{ + if (platform_driver_register(&venc_driver)) { + printk(KERN_ERR "Unable to register venc driver\n"); + return -ENODEV; + } + return 0; +} + +static void venc_exit(void) +{ + platform_driver_unregister(&venc_driver); + return; +} + +module_init(venc_init); +module_exit(venc_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("VPBE VENC Driver"); +MODULE_AUTHOR("Texas Instruments"); diff --git a/drivers/media/video/davinci/vpbe_venc_regs.h b/drivers/media/video/davinci/vpbe_venc_regs.h new file mode 100644 index 0000000..947cb15 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc_regs.h @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2006-2010 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 _VPBE_VENC_REGS_H +#define _VPBE_VENC_REGS_H + +/* VPBE Video Encoder / Digital LCD Subsystem Registers (VENC) */ +#define VENC_VMOD 0x00 +#define VENC_VIDCTL 0x04 +#define VENC_VDPRO 0x08 +#define VENC_SYNCCTL 0x0C +#define VENC_HSPLS 0x10 +#define VENC_VSPLS 0x14 +#define VENC_HINT 0x18 +#define VENC_HSTART 0x1C +#define VENC_HVALID 0x20 +#define VENC_VINT 0x24 +#define VENC_VSTART 0x28 +#define VENC_VVALID 0x2C +#define VENC_HSDLY 0x30 +#define VENC_VSDLY 0x34 +#define VENC_YCCCTL 0x38 +#define VENC_RGBCTL 0x3C +#define VENC_RGBCLP 0x40 +#define VENC_LINECTL 0x44 +#define VENC_CULLLINE 0x48 +#define VENC_LCDOUT 0x4C +#define VENC_BRTS 0x50 +#define VENC_BRTW 0x54 +#define VENC_ACCTL 0x58 +#define VENC_PWMP 0x5C +#define VENC_PWMW 0x60 +#define VENC_DCLKCTL 0x64 +#define VENC_DCLKPTN0 0x68 +#define VENC_DCLKPTN1 0x6C +#define VENC_DCLKPTN2 0x70 +#define VENC_DCLKPTN3 0x74 +#define VENC_DCLKPTN0A 0x78 +#define VENC_DCLKPTN1A 0x7C +#define VENC_DCLKPTN2A 0x80 +#define VENC_DCLKPTN3A 0x84 +#define VENC_DCLKHS 0x88 +#define VENC_DCLKHSA 0x8C +#define VENC_DCLKHR 0x90 +#define VENC_DCLKVS 0x94 +#define VENC_DCLKVR 0x98 +#define VENC_CAPCTL 0x9C +#define VENC_CAPDO 0xA0 +#define VENC_CAPDE 0xA4 +#define VENC_ATR0 0xA8 +#define VENC_ATR1 0xAC +#define VENC_ATR2 0xB0 +#define VENC_VSTAT 0xB8 +#define VENC_RAMADR 0xBC +#define VENC_RAMPORT 0xC0 +#define VENC_DACTST 0xC4 +#define VENC_YCOLVL 0xC8 +#define VENC_SCPROG 0xCC +#define VENC_CVBS 0xDC +#define VENC_CMPNT 0xE0 +#define VENC_ETMG0 0xE4 +#define VENC_ETMG1 0xE8 +#define VENC_ETMG2 0xEC +#define VENC_ETMG3 0xF0 +#define VENC_DACSEL 0xF4 +#define VENC_ARGBX0 0x100 +#define VENC_ARGBX1 0x104 +#define VENC_ARGBX2 0x108 +#define VENC_ARGBX3 0x10C +#define VENC_ARGBX4 0x110 +#define VENC_DRGBX0 0x114 +#define VENC_DRGBX1 0x118 +#define VENC_DRGBX2 0x11C +#define VENC_DRGBX3 0x120 +#define VENC_DRGBX4 0x124 +#define VENC_VSTARTA 0x128 +#define VENC_OSDCLK0 0x12C +#define VENC_OSDCLK1 0x130 +#define VENC_HVLDCL0 0x134 +#define VENC_HVLDCL1 0x138 +#define VENC_OSDHADV 0x13C +#define VENC_CLKCTL 0x140 +#define VENC_GAMCTL 0x144 +#define VENC_XHINTVL 0x174 + +/* bit definitions */ +#define VPBE_PCR_VENC_DIV (1 << 1) +#define VPBE_PCR_CLK_OFF (1 << 0) + +#define VENC_VMOD_VDMD_SHIFT 12 +#define VENC_VMOD_VDMD_YCBCR16 0 +#define VENC_VMOD_VDMD_YCBCR8 1 +#define VENC_VMOD_VDMD_RGB666 2 +#define VENC_VMOD_VDMD_RGB8 3 +#define VENC_VMOD_VDMD_EPSON 4 +#define VENC_VMOD_VDMD_CASIO 5 +#define VENC_VMOD_VDMD_UDISPQVGA 6 +#define VENC_VMOD_VDMD_STNLCD 7 +#define VENC_VMOD_VIE_SHIFT 1 +#define VENC_VMOD_VDMD (7 << 12) +#define VENC_VMOD_ITLCL (1 << 11) +#define VENC_VMOD_ITLC (1 << 10) +#define VENC_VMOD_NSIT (1 << 9) +#define VENC_VMOD_HDMD (1 << 8) +#define VENC_VMOD_TVTYP_SHIFT 6 +#define VENC_VMOD_TVTYP (3 << 6) +#define VENC_VMOD_SLAVE (1 << 5) +#define VENC_VMOD_VMD (1 << 4) +#define VENC_VMOD_BLNK (1 << 3) +#define VENC_VMOD_VIE (1 << 1) +#define VENC_VMOD_VENC (1 << 0) + +/* VMOD TVTYP options for HDMD=0 */ +#define SDTV_NTSC 0 +#define SDTV_PAL 1 +/* VMOD TVTYP options for HDMD=1 */ +#define HDTV_525P 0 +#define HDTV_625P 1 +#define HDTV_1080I 2 +#define HDTV_720P 3 + +#define VENC_VIDCTL_VCLKP (1 << 14) +#define VENC_VIDCTL_VCLKE_SHIFT 13 +#define VENC_VIDCTL_VCLKE (1 << 13) +#define VENC_VIDCTL_VCLKZ_SHIFT 12 +#define VENC_VIDCTL_VCLKZ (1 << 12) +#define VENC_VIDCTL_SYDIR_SHIFT 8 +#define VENC_VIDCTL_SYDIR (1 << 8) +#define VENC_VIDCTL_DOMD_SHIFT 4 +#define VENC_VIDCTL_DOMD (3 << 4) +#define VENC_VIDCTL_YCDIR_SHIFT 0 +#define VENC_VIDCTL_YCDIR (1 << 0) + +#define VENC_VDPRO_ATYCC_SHIFT 5 +#define VENC_VDPRO_ATYCC (1 << 5) +#define VENC_VDPRO_ATCOM_SHIFT 4 +#define VENC_VDPRO_ATCOM (1 << 4) +#define VENC_VDPRO_DAFRQ (1 << 3) +#define VENC_VDPRO_DAUPS (1 << 2) +#define VENC_VDPRO_CUPS (1 << 1) +#define VENC_VDPRO_YUPS (1 << 0) + +#define VENC_SYNCCTL_VPL_SHIFT 3 +#define VENC_SYNCCTL_VPL (1 << 3) +#define VENC_SYNCCTL_HPL_SHIFT 2 +#define VENC_SYNCCTL_HPL (1 << 2) +#define VENC_SYNCCTL_SYEV_SHIFT 1 +#define VENC_SYNCCTL_SYEV (1 << 1) +#define VENC_SYNCCTL_SYEH_SHIFT 0 +#define VENC_SYNCCTL_SYEH (1 << 0) +#define VENC_SYNCCTL_OVD_SHIFT 14 +#define VENC_SYNCCTL_OVD (1 << 14) + +#define VENC_DCLKCTL_DCKEC_SHIFT 11 +#define VENC_DCLKCTL_DCKEC (1 << 11) +#define VENC_DCLKCTL_DCKPW_SHIFT 0 +#define VENC_DCLKCTL_DCKPW (0x3f << 0) + +#define VENC_VSTAT_FIDST (1 << 4) + +#define VENC_CMPNT_MRGB_SHIFT 14 +#define VENC_CMPNT_MRGB (1 << 14) + +#endif /* _VPBE_VENC_REGS_H */ diff --git a/include/media/davinci/vpbe_venc.h b/include/media/davinci/vpbe_venc.h new file mode 100644 index 0000000..426c205 --- /dev/null +++ b/include/media/davinci/vpbe_venc.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 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 _VPBE_VENC_H +#define _VPBE_VENC_H + +#include +#include + +#define VPBE_VENC_SUBDEV_NAME "vpbe-venc" + +/* venc events */ +#define VENC_END_OF_FRAME BIT(0) +#define VENC_FIRST_FIELD BIT(1) +#define VENC_SECOND_FIELD BIT(2) + +struct venc_platform_data { + enum vpbe_version venc_type; + int (*setup_clock)(enum vpbe_enc_timings_type type, + unsigned int mode); + /* Number of LCD outputs supported */ + int num_lcd_outputs; +}; + +enum venc_ioctls { + VENC_GET_FLD = 1, +}; + +/* exported functions */ +struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev, + const char *venc_name); +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Fri Jun 17 02:01:33 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 17 Jun 2011 12:31:33 +0530 Subject: [RESEND PATCH v19 3/6] davinci vpbe: OSD(On Screen Display) block In-Reply-To: <1308294096-25743-1-git-send-email-manjunath.hadli@ti.com> References: <1308294096-25743-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1308294096-25743-4-git-send-email-manjunath.hadli@ti.com> This patch implements the functionality of the OSD block of the VPBE. The OSD in total supports 4 planes or Video sources - 2 mainly RGB and 2 Video. The patch implements general handling of all the planes, with specific emphasis on the Video plane capabilities as the Video planes are supported through the V4L2 driver. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- drivers/media/video/davinci/vpbe_osd.c | 1231 +++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 364 ++++++++ include/media/davinci/vpbe_osd.h | 394 +++++++++ 3 files changed, 1989 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpbe_osd.c create mode 100644 drivers/media/video/davinci/vpbe_osd_regs.h create mode 100644 include/media/davinci/vpbe_osd.h diff --git a/drivers/media/video/davinci/vpbe_osd.c b/drivers/media/video/davinci/vpbe_osd.c new file mode 100644 index 0000000..5352884 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd.c @@ -0,0 +1,1231 @@ +/* + * Copyright (C) 2007-2010 Texas Instruments Inc + * Copyright (C) 2007 MontaVista Software, Inc. + * + * Andy Lowe (alowe at mvista.com), MontaVista Software + * - Initial version + * Murali Karicheri (mkaricheri at gmail.com), Texas Instruments Ltd. + * - ported to sub device interface + * + * 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 +#include + +#include +#include "vpbe_osd_regs.h" + +#define MODULE_NAME VPBE_OSD_SUBDEV_NAME + +/* register access routines */ +static inline u32 osd_read(struct osd_state *sd, u32 offset) +{ + struct osd_state *osd = sd; + + return readl(osd->osd_base + offset); +} + +static inline u32 osd_write(struct osd_state *sd, u32 val, u32 offset) +{ + struct osd_state *osd = sd; + + writel(val, osd->osd_base + offset); + + return val; +} + +static inline u32 osd_set(struct osd_state *sd, u32 mask, u32 offset) +{ + struct osd_state *osd = sd; + + u32 addr = osd->osd_base + offset; + u32 val = readl(addr) | mask; + + writel(val, addr); + + return val; +} + +static inline u32 osd_clear(struct osd_state *sd, u32 mask, u32 offset) +{ + struct osd_state *osd = sd; + + u32 addr = osd->osd_base + offset; + u32 val = readl(addr) & ~mask; + + writel(val, addr); + + return val; +} + +static inline u32 osd_modify(struct osd_state *sd, u32 mask, u32 val, + u32 offset) +{ + struct osd_state *osd = sd; + + u32 addr = osd->osd_base + offset; + u32 new_val = (readl(addr) & ~mask) | (val & mask); + + writel(new_val, addr); + + return new_val; +} + +/* define some macros for layer and pixfmt classification */ +#define is_osd_win(layer) (((layer) == WIN_OSD0) || ((layer) == WIN_OSD1)) +#define is_vid_win(layer) (((layer) == WIN_VID0) || ((layer) == WIN_VID1)) +#define is_rgb_pixfmt(pixfmt) \ + (((pixfmt) == PIXFMT_RGB565) || ((pixfmt) == PIXFMT_RGB888)) +#define is_yc_pixfmt(pixfmt) \ + (((pixfmt) == PIXFMT_YCbCrI) || ((pixfmt) == PIXFMT_YCrCbI) || \ + ((pixfmt) == PIXFMT_NV12)) +#define MAX_WIN_SIZE OSD_VIDWIN0XP_V0X +#define MAX_LINE_LENGTH (OSD_VIDWIN0OFST_V0LO << 5) + +/** + * _osd_dm6446_vid0_pingpong() - field inversion fix for DM6446 + * @sd - ptr to struct osd_state + * @field_inversion - inversion flag + * @fb_base_phys - frame buffer address + * @lconfig - ptr to layer config + * + * This routine implements a workaround for the field signal inversion silicon + * erratum described in Advisory 1.3.8 for the DM6446. The fb_base_phys and + * lconfig parameters apply to the vid0 window. This routine should be called + * whenever the vid0 layer configuration or start address is modified, or when + * the OSD field inversion setting is modified. + * Returns: 1 if the ping-pong buffers need to be toggled in the vsync isr, or + * 0 otherwise + */ +static int _osd_dm6446_vid0_pingpong(struct osd_state *sd, + int field_inversion, + unsigned long fb_base_phys, + const struct osd_layer_config *lconfig) +{ + struct osd_platform_data *pdata; + + pdata = (struct osd_platform_data *)sd->dev->platform_data; + if (pdata->field_inv_wa_enable) { + + if (!field_inversion || !lconfig->interlaced) { + osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR); + osd_write(sd, fb_base_phys & ~0x1F, OSD_PPVWIN0ADR); + osd_modify(sd, OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, 0, + OSD_MISCCTL); + return 0; + } else { + unsigned miscctl = OSD_MISCCTL_PPRV; + + osd_write(sd, + (fb_base_phys & ~0x1F) - lconfig->line_length, + OSD_VIDWIN0ADR); + osd_write(sd, + (fb_base_phys & ~0x1F) + lconfig->line_length, + OSD_PPVWIN0ADR); + osd_modify(sd, + OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, miscctl, + OSD_MISCCTL); + + return 1; + } + } + + return 0; +} + +static void _osd_set_field_inversion(struct osd_state *sd, int enable) +{ + unsigned fsinv = 0; + + if (enable) + fsinv = OSD_MODE_FSINV; + + osd_modify(sd, OSD_MODE_FSINV, fsinv, OSD_MODE); +} + +static void _osd_set_blink_attribute(struct osd_state *sd, int enable, + enum osd_blink_interval blink) +{ + u32 osdatrmd = 0; + + if (enable) { + osdatrmd |= OSD_OSDATRMD_BLNK; + osdatrmd |= blink << OSD_OSDATRMD_BLNKINT_SHIFT; + } + /* caller must ensure that OSD1 is configured in attribute mode */ + osd_modify(sd, OSD_OSDATRMD_BLNKINT | OSD_OSDATRMD_BLNK, osdatrmd, + OSD_OSDATRMD); +} + +static void _osd_set_rom_clut(struct osd_state *sd, + enum osd_rom_clut rom_clut) +{ + if (rom_clut == ROM_CLUT0) + osd_clear(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL); + else + osd_set(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL); +} + +static void _osd_set_palette_map(struct osd_state *sd, + enum osd_win_layer osdwin, + unsigned char pixel_value, + unsigned char clut_index, + enum osd_pix_format pixfmt) +{ + static const int map_2bpp[] = { 0, 5, 10, 15 }; + static const int map_1bpp[] = { 0, 15 }; + int bmp_offset; + int bmp_shift; + int bmp_mask; + int bmp_reg; + + switch (pixfmt) { + case PIXFMT_1BPP: + bmp_reg = map_1bpp[pixel_value & 0x1]; + break; + case PIXFMT_2BPP: + bmp_reg = map_2bpp[pixel_value & 0x3]; + break; + case PIXFMT_4BPP: + bmp_reg = pixel_value & 0xf; + break; + default: + return; + } + + switch (osdwin) { + case OSDWIN_OSD0: + bmp_offset = OSD_W0BMP01 + (bmp_reg >> 1) * sizeof(u32); + break; + case OSDWIN_OSD1: + bmp_offset = OSD_W1BMP01 + (bmp_reg >> 1) * sizeof(u32); + break; + default: + return; + } + + if (bmp_reg & 1) { + bmp_shift = 8; + bmp_mask = 0xff << 8; + } else { + bmp_shift = 0; + bmp_mask = 0xff; + } + + osd_modify(sd, bmp_mask, clut_index << bmp_shift, bmp_offset); +} + +static void _osd_set_rec601_attenuation(struct osd_state *sd, + enum osd_win_layer osdwin, int enable) +{ + switch (osdwin) { + case OSDWIN_OSD0: + osd_modify(sd, OSD_OSDWIN0MD_ATN0E, + enable ? OSD_OSDWIN0MD_ATN0E : 0, + OSD_OSDWIN0MD); + break; + case OSDWIN_OSD1: + osd_modify(sd, OSD_OSDWIN1MD_ATN1E, + enable ? OSD_OSDWIN1MD_ATN1E : 0, + OSD_OSDWIN1MD); + break; + } +} + +static void _osd_set_blending_factor(struct osd_state *sd, + enum osd_win_layer osdwin, + enum osd_blending_factor blend) +{ + switch (osdwin) { + case OSDWIN_OSD0: + osd_modify(sd, OSD_OSDWIN0MD_BLND0, + blend << OSD_OSDWIN0MD_BLND0_SHIFT, OSD_OSDWIN0MD); + break; + case OSDWIN_OSD1: + osd_modify(sd, OSD_OSDWIN1MD_BLND1, + blend << OSD_OSDWIN1MD_BLND1_SHIFT, OSD_OSDWIN1MD); + break; + } +} + +static void _osd_enable_color_key(struct osd_state *sd, + enum osd_win_layer osdwin, + unsigned colorkey, + enum osd_pix_format pixfmt) +{ + switch (pixfmt) { + case PIXFMT_RGB565: + osd_write(sd, colorkey & OSD_TRANSPVAL_RGBTRANS, + OSD_TRANSPVAL); + break; + default: + break; + } + + switch (osdwin) { + case OSDWIN_OSD0: + osd_set(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD); + break; + case OSDWIN_OSD1: + osd_set(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD); + break; + } +} + +static void _osd_disable_color_key(struct osd_state *sd, + enum osd_win_layer osdwin) +{ + switch (osdwin) { + case OSDWIN_OSD0: + osd_clear(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD); + break; + case OSDWIN_OSD1: + osd_clear(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD); + break; + } +} + +static void _osd_set_osd_clut(struct osd_state *sd, + enum osd_win_layer osdwin, + enum osd_clut clut) +{ + u32 winmd = 0; + + switch (osdwin) { + case OSDWIN_OSD0: + if (clut == RAM_CLUT) + winmd |= OSD_OSDWIN0MD_CLUTS0; + osd_modify(sd, OSD_OSDWIN0MD_CLUTS0, winmd, OSD_OSDWIN0MD); + break; + case OSDWIN_OSD1: + if (clut == RAM_CLUT) + winmd |= OSD_OSDWIN1MD_CLUTS1; + osd_modify(sd, OSD_OSDWIN1MD_CLUTS1, winmd, OSD_OSDWIN1MD); + break; + } +} + +static void _osd_set_zoom(struct osd_state *sd, enum osd_layer layer, + enum osd_zoom_factor h_zoom, + enum osd_zoom_factor v_zoom) +{ + u32 winmd = 0; + + switch (layer) { + case WIN_OSD0: + winmd |= (h_zoom << OSD_OSDWIN0MD_OHZ0_SHIFT); + winmd |= (v_zoom << OSD_OSDWIN0MD_OVZ0_SHIFT); + osd_modify(sd, OSD_OSDWIN0MD_OHZ0 | OSD_OSDWIN0MD_OVZ0, winmd, + OSD_OSDWIN0MD); + break; + case WIN_VID0: + winmd |= (h_zoom << OSD_VIDWINMD_VHZ0_SHIFT); + winmd |= (v_zoom << OSD_VIDWINMD_VVZ0_SHIFT); + osd_modify(sd, OSD_VIDWINMD_VHZ0 | OSD_VIDWINMD_VVZ0, winmd, + OSD_VIDWINMD); + break; + case WIN_OSD1: + winmd |= (h_zoom << OSD_OSDWIN1MD_OHZ1_SHIFT); + winmd |= (v_zoom << OSD_OSDWIN1MD_OVZ1_SHIFT); + osd_modify(sd, OSD_OSDWIN1MD_OHZ1 | OSD_OSDWIN1MD_OVZ1, winmd, + OSD_OSDWIN1MD); + break; + case WIN_VID1: + winmd |= (h_zoom << OSD_VIDWINMD_VHZ1_SHIFT); + winmd |= (v_zoom << OSD_VIDWINMD_VVZ1_SHIFT); + osd_modify(sd, OSD_VIDWINMD_VHZ1 | OSD_VIDWINMD_VVZ1, winmd, + OSD_VIDWINMD); + break; + } +} + +static void _osd_disable_layer(struct osd_state *sd, enum osd_layer layer) +{ + switch (layer) { + case WIN_OSD0: + osd_clear(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD); + break; + case WIN_VID0: + osd_clear(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD); + break; + case WIN_OSD1: + /* disable attribute mode as well as disabling the window */ + osd_clear(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1, + OSD_OSDWIN1MD); + break; + case WIN_VID1: + osd_clear(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD); + break; + } +} + +static void osd_disable_layer(struct osd_state *sd, enum osd_layer layer) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + if (!win->is_enabled) { + spin_unlock_irqrestore(&osd->lock, flags); + return; + } + win->is_enabled = 0; + + _osd_disable_layer(sd, layer); + + spin_unlock_irqrestore(&osd->lock, flags); +} + +static void _osd_enable_attribute_mode(struct osd_state *sd) +{ + /* enable attribute mode for OSD1 */ + osd_set(sd, OSD_OSDWIN1MD_OASW, OSD_OSDWIN1MD); +} + +static void _osd_enable_layer(struct osd_state *sd, enum osd_layer layer) +{ + switch (layer) { + case WIN_OSD0: + osd_set(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD); + break; + case WIN_VID0: + osd_set(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD); + break; + case WIN_OSD1: + /* enable OSD1 and disable attribute mode */ + osd_modify(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1, + OSD_OSDWIN1MD_OACT1, OSD_OSDWIN1MD); + break; + case WIN_VID1: + osd_set(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD); + break; + } +} + +static int osd_enable_layer(struct osd_state *sd, enum osd_layer layer, + int otherwin) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + struct osd_layer_config *cfg = &win->lconfig; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + /* + * use otherwin flag to know this is the other vid window + * in YUV420 mode, if is, skip this check + */ + if (!otherwin && (!win->is_allocated || + !win->fb_base_phys || + !cfg->line_length || + !cfg->xsize || + !cfg->ysize)) { + spin_unlock_irqrestore(&osd->lock, flags); + return -1; + } + + if (win->is_enabled) { + spin_unlock_irqrestore(&osd->lock, flags); + return 0; + } + win->is_enabled = 1; + + if (cfg->pixfmt != PIXFMT_OSD_ATTR) + _osd_enable_layer(sd, layer); + else { + _osd_enable_attribute_mode(sd); + _osd_set_blink_attribute(sd, osd->is_blinking, osd->blink); + } + + spin_unlock_irqrestore(&osd->lock, flags); + + return 0; +} + +static void _osd_start_layer(struct osd_state *sd, enum osd_layer layer, + unsigned long fb_base_phys, + unsigned long cbcr_ofst) +{ + switch (layer) { + case WIN_OSD0: + osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN0ADR); + break; + case WIN_VID0: + osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR); + break; + case WIN_OSD1: + osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN1ADR); + break; + case WIN_VID1: + osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN1ADR); + break; + } +} + +static void osd_start_layer(struct osd_state *sd, enum osd_layer layer, + unsigned long fb_base_phys, + unsigned long cbcr_ofst) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + struct osd_layer_config *cfg = &win->lconfig; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + win->fb_base_phys = fb_base_phys & ~0x1F; + _osd_start_layer(sd, layer, fb_base_phys, cbcr_ofst); + + if (layer == WIN_VID0) { + osd->pingpong = + _osd_dm6446_vid0_pingpong(sd, osd->field_inversion, + win->fb_base_phys, + cfg); + } + + spin_unlock_irqrestore(&osd->lock, flags); +} + +static void osd_get_layer_config(struct osd_state *sd, enum osd_layer layer, + struct osd_layer_config *lconfig) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + *lconfig = win->lconfig; + + spin_unlock_irqrestore(&osd->lock, flags); +} + +/** + * try_layer_config() - Try a specific configuration for the layer + * @sd - ptr to struct osd_state + * @layer - layer to configure + * @lconfig - layer configuration to try + * + * If the requested lconfig is completely rejected and the value of lconfig on + * exit is the current lconfig, then try_layer_config() returns 1. Otherwise, + * try_layer_config() returns 0. A return value of 0 does not necessarily mean + * that the value of lconfig on exit is identical to the value of lconfig on + * entry, but merely that it represents a change from the current lconfig. + */ +static int try_layer_config(struct osd_state *sd, enum osd_layer layer, + struct osd_layer_config *lconfig) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + int bad_config; + + /* verify that the pixel format is compatible with the layer */ + switch (lconfig->pixfmt) { + case PIXFMT_1BPP: + case PIXFMT_2BPP: + case PIXFMT_4BPP: + case PIXFMT_8BPP: + case PIXFMT_RGB565: + bad_config = !is_osd_win(layer); + break; + case PIXFMT_YCbCrI: + case PIXFMT_YCrCbI: + bad_config = !is_vid_win(layer); + break; + case PIXFMT_RGB888: + bad_config = !is_vid_win(layer); + break; + case PIXFMT_NV12: + bad_config = 1; + break; + case PIXFMT_OSD_ATTR: + bad_config = (layer != WIN_OSD1); + break; + default: + bad_config = 1; + break; + } + if (bad_config) { + /* + * The requested pixel format is incompatible with the layer, + * so keep the current layer configuration. + */ + *lconfig = win->lconfig; + return bad_config; + } + + /* DM6446: */ + /* only one OSD window at a time can use RGB pixel formats */ + if (is_osd_win(layer) && is_rgb_pixfmt(lconfig->pixfmt)) { + enum osd_pix_format pixfmt; + if (layer == WIN_OSD0) + pixfmt = osd->win[WIN_OSD1].lconfig.pixfmt; + else + pixfmt = osd->win[WIN_OSD0].lconfig.pixfmt; + + if (is_rgb_pixfmt(pixfmt)) { + /* + * The other OSD window is already configured for an + * RGB, so keep the current layer configuration. + */ + *lconfig = win->lconfig; + return 1; + } + } + + /* DM6446: only one video window at a time can use RGB888 */ + if (is_vid_win(layer) && lconfig->pixfmt == PIXFMT_RGB888) { + enum osd_pix_format pixfmt; + + if (layer == WIN_VID0) + pixfmt = osd->win[WIN_VID1].lconfig.pixfmt; + else + pixfmt = osd->win[WIN_VID0].lconfig.pixfmt; + + if (pixfmt == PIXFMT_RGB888) { + /* + * The other video window is already configured for + * RGB888, so keep the current layer configuration. + */ + *lconfig = win->lconfig; + return 1; + } + } + + /* window dimensions must be non-zero */ + if (!lconfig->line_length || !lconfig->xsize || !lconfig->ysize) { + *lconfig = win->lconfig; + return 1; + } + + /* round line_length up to a multiple of 32 */ + lconfig->line_length = ((lconfig->line_length + 31) / 32) * 32; + lconfig->line_length = + min(lconfig->line_length, (unsigned)MAX_LINE_LENGTH); + lconfig->xsize = min(lconfig->xsize, (unsigned)MAX_WIN_SIZE); + lconfig->ysize = min(lconfig->ysize, (unsigned)MAX_WIN_SIZE); + lconfig->xpos = min(lconfig->xpos, (unsigned)MAX_WIN_SIZE); + lconfig->ypos = min(lconfig->ypos, (unsigned)MAX_WIN_SIZE); + lconfig->interlaced = (lconfig->interlaced != 0); + if (lconfig->interlaced) { + /* ysize and ypos must be even for interlaced displays */ + lconfig->ysize &= ~1; + lconfig->ypos &= ~1; + } + + return 0; +} + +static void _osd_disable_vid_rgb888(struct osd_state *sd) +{ + /* + * The DM6446 supports RGB888 pixel format in a single video window. + * This routine disables RGB888 pixel format for both video windows. + * The caller must ensure that neither video window is currently + * configured for RGB888 pixel format. + */ + osd_clear(sd, OSD_MISCCTL_RGBEN, OSD_MISCCTL); +} + +static void _osd_enable_vid_rgb888(struct osd_state *sd, + enum osd_layer layer) +{ + /* + * The DM6446 supports RGB888 pixel format in a single video window. + * This routine enables RGB888 pixel format for the specified video + * window. The caller must ensure that the other video window is not + * currently configured for RGB888 pixel format, as this routine will + * disable RGB888 pixel format for the other window. + */ + if (layer == WIN_VID0) { + osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN, + OSD_MISCCTL_RGBEN, OSD_MISCCTL); + } else if (layer == WIN_VID1) { + osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN, + OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN, + OSD_MISCCTL); + } +} + +static void _osd_set_cbcr_order(struct osd_state *sd, + enum osd_pix_format pixfmt) +{ + /* + * The caller must ensure that all windows using YC pixfmt use the same + * Cb/Cr order. + */ + if (pixfmt == PIXFMT_YCbCrI) + osd_clear(sd, OSD_MODE_CS, OSD_MODE); + else if (pixfmt == PIXFMT_YCrCbI) + osd_set(sd, OSD_MODE_CS, OSD_MODE); +} + +static void _osd_set_layer_config(struct osd_state *sd, enum osd_layer layer, + const struct osd_layer_config *lconfig) +{ + u32 winmd = 0, winmd_mask = 0, bmw = 0; + + _osd_set_cbcr_order(sd, lconfig->pixfmt); + + switch (layer) { + case WIN_OSD0: + winmd_mask |= OSD_OSDWIN0MD_RGB0E; + if (lconfig->pixfmt == PIXFMT_RGB565) + winmd |= OSD_OSDWIN0MD_RGB0E; + + winmd_mask |= OSD_OSDWIN0MD_BMW0 | OSD_OSDWIN0MD_OFF0; + + switch (lconfig->pixfmt) { + case PIXFMT_1BPP: + bmw = 0; + break; + case PIXFMT_2BPP: + bmw = 1; + break; + case PIXFMT_4BPP: + bmw = 2; + break; + case PIXFMT_8BPP: + bmw = 3; + break; + default: + break; + } + winmd |= (bmw << OSD_OSDWIN0MD_BMW0_SHIFT); + + if (lconfig->interlaced) + winmd |= OSD_OSDWIN0MD_OFF0; + + osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN0MD); + osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN0OFST); + osd_write(sd, lconfig->xpos, OSD_OSDWIN0XP); + osd_write(sd, lconfig->xsize, OSD_OSDWIN0XL); + if (lconfig->interlaced) { + osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN0YP); + osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN0YL); + } else { + osd_write(sd, lconfig->ypos, OSD_OSDWIN0YP); + osd_write(sd, lconfig->ysize, OSD_OSDWIN0YL); + } + break; + case WIN_VID0: + winmd_mask |= OSD_VIDWINMD_VFF0; + if (lconfig->interlaced) + winmd |= OSD_VIDWINMD_VFF0; + + osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD); + osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN0OFST); + osd_write(sd, lconfig->xpos, OSD_VIDWIN0XP); + osd_write(sd, lconfig->xsize, OSD_VIDWIN0XL); + /* + * For YUV420P format the register contents are + * duplicated in both VID registers + */ + if (lconfig->interlaced) { + osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN0YP); + osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN0YL); + } else { + osd_write(sd, lconfig->ypos, OSD_VIDWIN0YP); + osd_write(sd, lconfig->ysize, OSD_VIDWIN0YL); + } + break; + case WIN_OSD1: + /* + * The caller must ensure that OSD1 is disabled prior to + * switching from a normal mode to attribute mode or from + * attribute mode to a normal mode. + */ + if (lconfig->pixfmt == PIXFMT_OSD_ATTR) { + winmd_mask |= + OSD_OSDWIN1MD_ATN1E | OSD_OSDWIN1MD_RGB1E | + OSD_OSDWIN1MD_CLUTS1 | + OSD_OSDWIN1MD_BLND1 | OSD_OSDWIN1MD_TE1; + } else { + winmd_mask |= OSD_OSDWIN1MD_RGB1E; + if (lconfig->pixfmt == PIXFMT_RGB565) + winmd |= OSD_OSDWIN1MD_RGB1E; + + winmd_mask |= OSD_OSDWIN1MD_BMW1; + switch (lconfig->pixfmt) { + case PIXFMT_1BPP: + bmw = 0; + break; + case PIXFMT_2BPP: + bmw = 1; + break; + case PIXFMT_4BPP: + bmw = 2; + break; + case PIXFMT_8BPP: + bmw = 3; + break; + default: + break; + } + winmd |= (bmw << OSD_OSDWIN1MD_BMW1_SHIFT); + } + + winmd_mask |= OSD_OSDWIN1MD_OFF1; + if (lconfig->interlaced) + winmd |= OSD_OSDWIN1MD_OFF1; + + osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN1MD); + osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN1OFST); + osd_write(sd, lconfig->xpos, OSD_OSDWIN1XP); + osd_write(sd, lconfig->xsize, OSD_OSDWIN1XL); + if (lconfig->interlaced) { + osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN1YP); + osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN1YL); + } else { + osd_write(sd, lconfig->ypos, OSD_OSDWIN1YP); + osd_write(sd, lconfig->ysize, OSD_OSDWIN1YL); + } + break; + case WIN_VID1: + winmd_mask |= OSD_VIDWINMD_VFF1; + if (lconfig->interlaced) + winmd |= OSD_VIDWINMD_VFF1; + + osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD); + osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN1OFST); + osd_write(sd, lconfig->xpos, OSD_VIDWIN1XP); + osd_write(sd, lconfig->xsize, OSD_VIDWIN1XL); + /* + * For YUV420P format the register contents are + * duplicated in both VID registers + */ + osd_modify(sd, OSD_MISCCTL_S420D, ~OSD_MISCCTL_S420D, + OSD_MISCCTL); + + if (lconfig->interlaced) { + osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN1YP); + osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN1YL); + } else { + osd_write(sd, lconfig->ypos, OSD_VIDWIN1YP); + osd_write(sd, lconfig->ysize, OSD_VIDWIN1YL); + } + break; + } +} + +static int osd_set_layer_config(struct osd_state *sd, enum osd_layer layer, + struct osd_layer_config *lconfig) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + struct osd_layer_config *cfg = &win->lconfig; + unsigned long flags; + int reject_config; + + spin_lock_irqsave(&osd->lock, flags); + + reject_config = try_layer_config(sd, layer, lconfig); + if (reject_config) { + spin_unlock_irqrestore(&osd->lock, flags); + return reject_config; + } + + /* update the current Cb/Cr order */ + if (is_yc_pixfmt(lconfig->pixfmt)) + osd->yc_pixfmt = lconfig->pixfmt; + + /* + * If we are switching OSD1 from normal mode to attribute mode or from + * attribute mode to normal mode, then we must disable the window. + */ + if (layer == WIN_OSD1) { + if (((lconfig->pixfmt == PIXFMT_OSD_ATTR) && + (cfg->pixfmt != PIXFMT_OSD_ATTR)) || + ((lconfig->pixfmt != PIXFMT_OSD_ATTR) && + (cfg->pixfmt == PIXFMT_OSD_ATTR))) { + win->is_enabled = 0; + _osd_disable_layer(sd, layer); + } + } + + _osd_set_layer_config(sd, layer, lconfig); + + if (layer == WIN_OSD1) { + struct osd_osdwin_state *osdwin_state = + &osd->osdwin[OSDWIN_OSD1]; + + if ((lconfig->pixfmt != PIXFMT_OSD_ATTR) && + (cfg->pixfmt == PIXFMT_OSD_ATTR)) { + /* + * We just switched OSD1 from attribute mode to normal + * mode, so we must initialize the CLUT select, the + * blend factor, transparency colorkey enable, and + * attenuation enable (DM6446 only) bits in the + * OSDWIN1MD register. + */ + _osd_set_osd_clut(sd, OSDWIN_OSD1, + osdwin_state->clut); + _osd_set_blending_factor(sd, OSDWIN_OSD1, + osdwin_state->blend); + if (osdwin_state->colorkey_blending) { + _osd_enable_color_key(sd, OSDWIN_OSD1, + osdwin_state-> + colorkey, + lconfig->pixfmt); + } else + _osd_disable_color_key(sd, OSDWIN_OSD1); + _osd_set_rec601_attenuation(sd, OSDWIN_OSD1, + osdwin_state-> + rec601_attenuation); + } else if ((lconfig->pixfmt == PIXFMT_OSD_ATTR) && + (cfg->pixfmt != PIXFMT_OSD_ATTR)) { + /* + * We just switched OSD1 from normal mode to attribute + * mode, so we must initialize the blink enable and + * blink interval bits in the OSDATRMD register. + */ + _osd_set_blink_attribute(sd, osd->is_blinking, + osd->blink); + } + } + + /* + * If we just switched to a 1-, 2-, or 4-bits-per-pixel bitmap format + * then configure a default palette map. + */ + if ((lconfig->pixfmt != cfg->pixfmt) && + ((lconfig->pixfmt == PIXFMT_1BPP) || + (lconfig->pixfmt == PIXFMT_2BPP) || + (lconfig->pixfmt == PIXFMT_4BPP))) { + enum osd_win_layer osdwin = + ((layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1); + struct osd_osdwin_state *osdwin_state = + &osd->osdwin[osdwin]; + unsigned char clut_index; + unsigned char clut_entries = 0; + + switch (lconfig->pixfmt) { + case PIXFMT_1BPP: + clut_entries = 2; + break; + case PIXFMT_2BPP: + clut_entries = 4; + break; + case PIXFMT_4BPP: + clut_entries = 16; + break; + default: + break; + } + /* + * The default palette map maps the pixel value to the clut + * index, i.e. pixel value 0 maps to clut entry 0, pixel value + * 1 maps to clut entry 1, etc. + */ + for (clut_index = 0; clut_index < 16; clut_index++) { + osdwin_state->palette_map[clut_index] = clut_index; + if (clut_index < clut_entries) { + _osd_set_palette_map(sd, osdwin, clut_index, + clut_index, + lconfig->pixfmt); + } + } + } + + *cfg = *lconfig; + /* DM6446: configure the RGB888 enable and window selection */ + if (osd->win[WIN_VID0].lconfig.pixfmt == PIXFMT_RGB888) + _osd_enable_vid_rgb888(sd, WIN_VID0); + else if (osd->win[WIN_VID1].lconfig.pixfmt == PIXFMT_RGB888) + _osd_enable_vid_rgb888(sd, WIN_VID1); + else + _osd_disable_vid_rgb888(sd); + + if (layer == WIN_VID0) { + osd->pingpong = + _osd_dm6446_vid0_pingpong(sd, osd->field_inversion, + win->fb_base_phys, + cfg); + } + + spin_unlock_irqrestore(&osd->lock, flags); + + return 0; +} + +static void osd_init_layer(struct osd_state *sd, enum osd_layer layer) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + enum osd_win_layer osdwin; + struct osd_osdwin_state *osdwin_state; + struct osd_layer_config *cfg = &win->lconfig; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + win->is_enabled = 0; + _osd_disable_layer(sd, layer); + + win->h_zoom = ZOOM_X1; + win->v_zoom = ZOOM_X1; + _osd_set_zoom(sd, layer, win->h_zoom, win->v_zoom); + + win->fb_base_phys = 0; + _osd_start_layer(sd, layer, win->fb_base_phys, 0); + + cfg->line_length = 0; + cfg->xsize = 0; + cfg->ysize = 0; + cfg->xpos = 0; + cfg->ypos = 0; + cfg->interlaced = 0; + switch (layer) { + case WIN_OSD0: + case WIN_OSD1: + osdwin = (layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1; + osdwin_state = &osd->osdwin[osdwin]; + /* + * Other code relies on the fact that OSD windows default to a + * bitmap pixel format when they are deallocated, so don't + * change this default pixel format. + */ + cfg->pixfmt = PIXFMT_8BPP; + _osd_set_layer_config(sd, layer, cfg); + osdwin_state->clut = RAM_CLUT; + _osd_set_osd_clut(sd, osdwin, osdwin_state->clut); + osdwin_state->colorkey_blending = 0; + _osd_disable_color_key(sd, osdwin); + osdwin_state->blend = OSD_8_VID_0; + _osd_set_blending_factor(sd, osdwin, osdwin_state->blend); + osdwin_state->rec601_attenuation = 0; + _osd_set_rec601_attenuation(sd, osdwin, + osdwin_state-> + rec601_attenuation); + if (osdwin == OSDWIN_OSD1) { + osd->is_blinking = 0; + osd->blink = BLINK_X1; + } + break; + case WIN_VID0: + case WIN_VID1: + cfg->pixfmt = osd->yc_pixfmt; + _osd_set_layer_config(sd, layer, cfg); + break; + } + + spin_unlock_irqrestore(&osd->lock, flags); +} + +static void osd_release_layer(struct osd_state *sd, enum osd_layer layer) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + if (!win->is_allocated) { + spin_unlock_irqrestore(&osd->lock, flags); + return; + } + + spin_unlock_irqrestore(&osd->lock, flags); + osd_init_layer(sd, layer); + spin_lock_irqsave(&osd->lock, flags); + + win->is_allocated = 0; + + spin_unlock_irqrestore(&osd->lock, flags); +} + +static int osd_request_layer(struct osd_state *sd, enum osd_layer layer) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + if (win->is_allocated) { + spin_unlock_irqrestore(&osd->lock, flags); + return -1; + } + win->is_allocated = 1; + + spin_unlock_irqrestore(&osd->lock, flags); + + return 0; +} + +static void _osd_init(struct osd_state *sd) +{ + osd_write(sd, 0, OSD_MODE); + osd_write(sd, 0, OSD_VIDWINMD); + osd_write(sd, 0, OSD_OSDWIN0MD); + osd_write(sd, 0, OSD_OSDWIN1MD); + osd_write(sd, 0, OSD_RECTCUR); + osd_write(sd, 0, OSD_MISCCTL); +} + +static void osd_set_left_margin(struct osd_state *sd, u32 val) +{ + osd_write(sd, val, OSD_BASEPX); +} + +static void osd_set_top_margin(struct osd_state *sd, u32 val) +{ + osd_write(sd, val, OSD_BASEPY); +} + +static int osd_initialize(struct osd_state *osd) +{ + if (osd == NULL) + return -ENODEV; + _osd_init(osd); + + /* set default Cb/Cr order */ + osd->yc_pixfmt = PIXFMT_YCbCrI; + + _osd_set_field_inversion(osd, osd->field_inversion); + _osd_set_rom_clut(osd, osd->rom_clut); + + osd_init_layer(osd, WIN_OSD0); + osd_init_layer(osd, WIN_VID0); + osd_init_layer(osd, WIN_OSD1); + osd_init_layer(osd, WIN_VID1); + + return 0; +} + +static const struct vpbe_osd_ops osd_ops = { + .initialize = osd_initialize, + .request_layer = osd_request_layer, + .release_layer = osd_release_layer, + .enable_layer = osd_enable_layer, + .disable_layer = osd_disable_layer, + .set_layer_config = osd_set_layer_config, + .get_layer_config = osd_get_layer_config, + .start_layer = osd_start_layer, + .set_left_margin = osd_set_left_margin, + .set_top_margin = osd_set_top_margin, +}; + +static int osd_probe(struct platform_device *pdev) +{ + struct osd_platform_data *pdata; + struct osd_state *osd; + struct resource *res; + int ret = 0; + + osd = kzalloc(sizeof(struct osd_state), GFP_KERNEL); + if (osd == NULL) + return -ENOMEM; + + osd->dev = &pdev->dev; + pdata = (struct osd_platform_data *)pdev->dev.platform_data; + osd->vpbe_type = (enum vpbe_version)pdata->vpbe_type; + if (NULL == pdev->dev.platform_data) { + dev_err(osd->dev, "No platform data defined for OSD" + " sub device\n"); + ret = -ENOENT; + goto free_mem; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(osd->dev, "Unable to get OSD register address map\n"); + ret = -ENODEV; + goto free_mem; + } + osd->osd_base_phys = res->start; + osd->osd_size = res->end - res->start + 1; + if (!request_mem_region(osd->osd_base_phys, osd->osd_size, + MODULE_NAME)) { + dev_err(osd->dev, "Unable to reserve OSD MMIO region\n"); + ret = -ENODEV; + goto free_mem; + } + osd->osd_base = (unsigned long)ioremap_nocache(res->start, + osd->osd_size); + if (!osd->osd_base) { + dev_err(osd->dev, "Unable to map the OSD region\n"); + ret = -ENODEV; + goto release_mem_region; + } + spin_lock_init(&osd->lock); + osd->ops = osd_ops; + platform_set_drvdata(pdev, osd); + dev_notice(osd->dev, "OSD sub device probe success\n"); + return ret; + +release_mem_region: + release_mem_region(osd->osd_base_phys, osd->osd_size); +free_mem: + kfree(osd); + return ret; +} + +static int osd_remove(struct platform_device *pdev) +{ + struct osd_state *osd = platform_get_drvdata(pdev); + + iounmap((void *)osd->osd_base); + release_mem_region(osd->osd_base_phys, osd->osd_size); + kfree(osd); + return 0; +} + +static struct platform_driver osd_driver = { + .probe = osd_probe, + .remove = osd_remove, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + }, +}; + +static int osd_init(void) +{ + if (platform_driver_register(&osd_driver)) { + printk(KERN_ERR "Unable to register davinci osd driver\n"); + return -ENODEV; + } + + return 0; +} + +static void osd_exit(void) +{ + platform_driver_unregister(&osd_driver); +} + +module_init(osd_init); +module_exit(osd_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DaVinci OSD Manager Driver"); +MODULE_AUTHOR("Texas Instruments"); diff --git a/drivers/media/video/davinci/vpbe_osd_regs.h b/drivers/media/video/davinci/vpbe_osd_regs.h new file mode 100644 index 0000000..584520f --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd_regs.h @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2006-2010 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 _VPBE_OSD_REGS_H +#define _VPBE_OSD_REGS_H + +/* VPBE Global Registers */ +#define VPBE_PID 0x0 +#define VPBE_PCR 0x4 + +/* VPSS CLock Registers */ +#define VPSSCLK_PID 0x00 +#define VPSSCLK_CLKCTRL 0x04 + +/* VPSS Buffer Logic Registers */ +#define VPSSBL_PID 0x00 +#define VPSSBL_PCR 0x04 +#define VPSSBL_BCR 0x08 +#define VPSSBL_INTSTAT 0x0C +#define VPSSBL_INTSEL 0x10 +#define VPSSBL_EVTSEL 0x14 +#define VPSSBL_MEMCTRL 0x18 +#define VPSSBL_CCDCMUX 0x1C + +/* DM365 ISP5 system configuration */ +#define ISP5_PID 0x0 +#define ISP5_PCCR 0x4 +#define ISP5_BCR 0x8 +#define ISP5_INTSTAT 0xC +#define ISP5_INTSEL1 0x10 +#define ISP5_INTSEL2 0x14 +#define ISP5_INTSEL3 0x18 +#define ISP5_EVTSEL 0x1c +#define ISP5_CCDCMUX 0x20 + +/* VPBE On-Screen Display Subsystem Registers (OSD) */ +#define OSD_MODE 0x00 +#define OSD_VIDWINMD 0x04 +#define OSD_OSDWIN0MD 0x08 +#define OSD_OSDWIN1MD 0x0C +#define OSD_OSDATRMD 0x0C +#define OSD_RECTCUR 0x10 +#define OSD_VIDWIN0OFST 0x18 +#define OSD_VIDWIN1OFST 0x1C +#define OSD_OSDWIN0OFST 0x20 +#define OSD_OSDWIN1OFST 0x24 +#define OSD_VIDWINADH 0x28 +#define OSD_VIDWIN0ADL 0x2C +#define OSD_VIDWIN0ADR 0x2C +#define OSD_VIDWIN1ADL 0x30 +#define OSD_VIDWIN1ADR 0x30 +#define OSD_OSDWINADH 0x34 +#define OSD_OSDWIN0ADL 0x38 +#define OSD_OSDWIN0ADR 0x38 +#define OSD_OSDWIN1ADL 0x3C +#define OSD_OSDWIN1ADR 0x3C +#define OSD_BASEPX 0x40 +#define OSD_BASEPY 0x44 +#define OSD_VIDWIN0XP 0x48 +#define OSD_VIDWIN0YP 0x4C +#define OSD_VIDWIN0XL 0x50 +#define OSD_VIDWIN0YL 0x54 +#define OSD_VIDWIN1XP 0x58 +#define OSD_VIDWIN1YP 0x5C +#define OSD_VIDWIN1XL 0x60 +#define OSD_VIDWIN1YL 0x64 +#define OSD_OSDWIN0XP 0x68 +#define OSD_OSDWIN0YP 0x6C +#define OSD_OSDWIN0XL 0x70 +#define OSD_OSDWIN0YL 0x74 +#define OSD_OSDWIN1XP 0x78 +#define OSD_OSDWIN1YP 0x7C +#define OSD_OSDWIN1XL 0x80 +#define OSD_OSDWIN1YL 0x84 +#define OSD_CURXP 0x88 +#define OSD_CURYP 0x8C +#define OSD_CURXL 0x90 +#define OSD_CURYL 0x94 +#define OSD_W0BMP01 0xA0 +#define OSD_W0BMP23 0xA4 +#define OSD_W0BMP45 0xA8 +#define OSD_W0BMP67 0xAC +#define OSD_W0BMP89 0xB0 +#define OSD_W0BMPAB 0xB4 +#define OSD_W0BMPCD 0xB8 +#define OSD_W0BMPEF 0xBC +#define OSD_W1BMP01 0xC0 +#define OSD_W1BMP23 0xC4 +#define OSD_W1BMP45 0xC8 +#define OSD_W1BMP67 0xCC +#define OSD_W1BMP89 0xD0 +#define OSD_W1BMPAB 0xD4 +#define OSD_W1BMPCD 0xD8 +#define OSD_W1BMPEF 0xDC +#define OSD_VBNDRY 0xE0 +#define OSD_EXTMODE 0xE4 +#define OSD_MISCCTL 0xE8 +#define OSD_CLUTRAMYCB 0xEC +#define OSD_CLUTRAMCR 0xF0 +#define OSD_TRANSPVAL 0xF4 +#define OSD_TRANSPVALL 0xF4 +#define OSD_TRANSPVALU 0xF8 +#define OSD_TRANSPBMPIDX 0xFC +#define OSD_PPVWIN0ADR 0xFC + +/* bit definitions */ +#define VPBE_PCR_VENC_DIV (1 << 1) +#define VPBE_PCR_CLK_OFF (1 << 0) + +#define VPSSBL_INTSTAT_HSSIINT (1 << 14) +#define VPSSBL_INTSTAT_CFALDINT (1 << 13) +#define VPSSBL_INTSTAT_IPIPE_INT5 (1 << 12) +#define VPSSBL_INTSTAT_IPIPE_INT4 (1 << 11) +#define VPSSBL_INTSTAT_IPIPE_INT3 (1 << 10) +#define VPSSBL_INTSTAT_IPIPE_INT2 (1 << 9) +#define VPSSBL_INTSTAT_IPIPE_INT1 (1 << 8) +#define VPSSBL_INTSTAT_IPIPE_INT0 (1 << 7) +#define VPSSBL_INTSTAT_IPIPEIFINT (1 << 6) +#define VPSSBL_INTSTAT_OSDINT (1 << 5) +#define VPSSBL_INTSTAT_VENCINT (1 << 4) +#define VPSSBL_INTSTAT_H3AINT (1 << 3) +#define VPSSBL_INTSTAT_CCDC_VDINT2 (1 << 2) +#define VPSSBL_INTSTAT_CCDC_VDINT1 (1 << 1) +#define VPSSBL_INTSTAT_CCDC_VDINT0 (1 << 0) + +/* DM365 ISP5 bit definitions */ +#define ISP5_INTSTAT_VENCINT (1 << 21) +#define ISP5_INTSTAT_OSDINT (1 << 20) + +/* VMOD TVTYP options for HDMD=0 */ +#define SDTV_NTSC 0 +#define SDTV_PAL 1 +/* VMOD TVTYP options for HDMD=1 */ +#define HDTV_525P 0 +#define HDTV_625P 1 +#define HDTV_1080I 2 +#define HDTV_720P 3 + +#define OSD_MODE_CS (1 << 15) +#define OSD_MODE_OVRSZ (1 << 14) +#define OSD_MODE_OHRSZ (1 << 13) +#define OSD_MODE_EF (1 << 12) +#define OSD_MODE_VVRSZ (1 << 11) +#define OSD_MODE_VHRSZ (1 << 10) +#define OSD_MODE_FSINV (1 << 9) +#define OSD_MODE_BCLUT (1 << 8) +#define OSD_MODE_CABG_SHIFT 0 +#define OSD_MODE_CABG (0xff << 0) + +#define OSD_VIDWINMD_VFINV (1 << 15) +#define OSD_VIDWINMD_V1EFC (1 << 14) +#define OSD_VIDWINMD_VHZ1_SHIFT 12 +#define OSD_VIDWINMD_VHZ1 (3 << 12) +#define OSD_VIDWINMD_VVZ1_SHIFT 10 +#define OSD_VIDWINMD_VVZ1 (3 << 10) +#define OSD_VIDWINMD_VFF1 (1 << 9) +#define OSD_VIDWINMD_ACT1 (1 << 8) +#define OSD_VIDWINMD_V0EFC (1 << 6) +#define OSD_VIDWINMD_VHZ0_SHIFT 4 +#define OSD_VIDWINMD_VHZ0 (3 << 4) +#define OSD_VIDWINMD_VVZ0_SHIFT 2 +#define OSD_VIDWINMD_VVZ0 (3 << 2) +#define OSD_VIDWINMD_VFF0 (1 << 1) +#define OSD_VIDWINMD_ACT0 (1 << 0) + +#define OSD_OSDWIN0MD_ATN0E (1 << 14) +#define OSD_OSDWIN0MD_RGB0E (1 << 13) +#define OSD_OSDWIN0MD_BMP0MD_SHIFT 13 +#define OSD_OSDWIN0MD_BMP0MD (3 << 13) +#define OSD_OSDWIN0MD_CLUTS0 (1 << 12) +#define OSD_OSDWIN0MD_OHZ0_SHIFT 10 +#define OSD_OSDWIN0MD_OHZ0 (3 << 10) +#define OSD_OSDWIN0MD_OVZ0_SHIFT 8 +#define OSD_OSDWIN0MD_OVZ0 (3 << 8) +#define OSD_OSDWIN0MD_BMW0_SHIFT 6 +#define OSD_OSDWIN0MD_BMW0 (3 << 6) +#define OSD_OSDWIN0MD_BLND0_SHIFT 3 +#define OSD_OSDWIN0MD_BLND0 (7 << 3) +#define OSD_OSDWIN0MD_TE0 (1 << 2) +#define OSD_OSDWIN0MD_OFF0 (1 << 1) +#define OSD_OSDWIN0MD_OACT0 (1 << 0) + +#define OSD_OSDWIN1MD_OASW (1 << 15) +#define OSD_OSDWIN1MD_ATN1E (1 << 14) +#define OSD_OSDWIN1MD_RGB1E (1 << 13) +#define OSD_OSDWIN1MD_BMP1MD_SHIFT 13 +#define OSD_OSDWIN1MD_BMP1MD (3 << 13) +#define OSD_OSDWIN1MD_CLUTS1 (1 << 12) +#define OSD_OSDWIN1MD_OHZ1_SHIFT 10 +#define OSD_OSDWIN1MD_OHZ1 (3 << 10) +#define OSD_OSDWIN1MD_OVZ1_SHIFT 8 +#define OSD_OSDWIN1MD_OVZ1 (3 << 8) +#define OSD_OSDWIN1MD_BMW1_SHIFT 6 +#define OSD_OSDWIN1MD_BMW1 (3 << 6) +#define OSD_OSDWIN1MD_BLND1_SHIFT 3 +#define OSD_OSDWIN1MD_BLND1 (7 << 3) +#define OSD_OSDWIN1MD_TE1 (1 << 2) +#define OSD_OSDWIN1MD_OFF1 (1 << 1) +#define OSD_OSDWIN1MD_OACT1 (1 << 0) + +#define OSD_OSDATRMD_OASW (1 << 15) +#define OSD_OSDATRMD_OHZA_SHIFT 10 +#define OSD_OSDATRMD_OHZA (3 << 10) +#define OSD_OSDATRMD_OVZA_SHIFT 8 +#define OSD_OSDATRMD_OVZA (3 << 8) +#define OSD_OSDATRMD_BLNKINT_SHIFT 6 +#define OSD_OSDATRMD_BLNKINT (3 << 6) +#define OSD_OSDATRMD_OFFA (1 << 1) +#define OSD_OSDATRMD_BLNK (1 << 0) + +#define OSD_RECTCUR_RCAD_SHIFT 8 +#define OSD_RECTCUR_RCAD (0xff << 8) +#define OSD_RECTCUR_CLUTSR (1 << 7) +#define OSD_RECTCUR_RCHW_SHIFT 4 +#define OSD_RECTCUR_RCHW (7 << 4) +#define OSD_RECTCUR_RCVW_SHIFT 1 +#define OSD_RECTCUR_RCVW (7 << 1) +#define OSD_RECTCUR_RCACT (1 << 0) + +#define OSD_VIDWIN0OFST_V0LO (0x1ff << 0) + +#define OSD_VIDWIN1OFST_V1LO (0x1ff << 0) + +#define OSD_OSDWIN0OFST_O0LO (0x1ff << 0) + +#define OSD_OSDWIN1OFST_O1LO (0x1ff << 0) + +#define OSD_WINOFST_AH_SHIFT 9 + +#define OSD_VIDWIN0OFST_V0AH (0xf << 9) +#define OSD_VIDWIN1OFST_V1AH (0xf << 9) +#define OSD_OSDWIN0OFST_O0AH (0xf << 9) +#define OSD_OSDWIN1OFST_O1AH (0xf << 9) + +#define OSD_VIDWINADH_V1AH_SHIFT 8 +#define OSD_VIDWINADH_V1AH (0x7f << 8) +#define OSD_VIDWINADH_V0AH_SHIFT 0 +#define OSD_VIDWINADH_V0AH (0x7f << 0) + +#define OSD_VIDWIN0ADL_V0AL (0xffff << 0) + +#define OSD_VIDWIN1ADL_V1AL (0xffff << 0) + +#define OSD_OSDWINADH_O1AH_SHIFT 8 +#define OSD_OSDWINADH_O1AH (0x7f << 8) +#define OSD_OSDWINADH_O0AH_SHIFT 0 +#define OSD_OSDWINADH_O0AH (0x7f << 0) + +#define OSD_OSDWIN0ADL_O0AL (0xffff << 0) + +#define OSD_OSDWIN1ADL_O1AL (0xffff << 0) + +#define OSD_BASEPX_BPX (0x3ff << 0) + +#define OSD_BASEPY_BPY (0x1ff << 0) + +#define OSD_VIDWIN0XP_V0X (0x7ff << 0) + +#define OSD_VIDWIN0YP_V0Y (0x7ff << 0) + +#define OSD_VIDWIN0XL_V0W (0x7ff << 0) + +#define OSD_VIDWIN0YL_V0H (0x7ff << 0) + +#define OSD_VIDWIN1XP_V1X (0x7ff << 0) + +#define OSD_VIDWIN1YP_V1Y (0x7ff << 0) + +#define OSD_VIDWIN1XL_V1W (0x7ff << 0) + +#define OSD_VIDWIN1YL_V1H (0x7ff << 0) + +#define OSD_OSDWIN0XP_W0X (0x7ff << 0) + +#define OSD_OSDWIN0YP_W0Y (0x7ff << 0) + +#define OSD_OSDWIN0XL_W0W (0x7ff << 0) + +#define OSD_OSDWIN0YL_W0H (0x7ff << 0) + +#define OSD_OSDWIN1XP_W1X (0x7ff << 0) + +#define OSD_OSDWIN1YP_W1Y (0x7ff << 0) + +#define OSD_OSDWIN1XL_W1W (0x7ff << 0) + +#define OSD_OSDWIN1YL_W1H (0x7ff << 0) + +#define OSD_CURXP_RCSX (0x7ff << 0) + +#define OSD_CURYP_RCSY (0x7ff << 0) + +#define OSD_CURXL_RCSW (0x7ff << 0) + +#define OSD_CURYL_RCSH (0x7ff << 0) + +#define OSD_EXTMODE_EXPMDSEL (1 << 15) +#define OSD_EXTMODE_SCRNHEXP_SHIFT 13 +#define OSD_EXTMODE_SCRNHEXP (3 << 13) +#define OSD_EXTMODE_SCRNVEXP (1 << 12) +#define OSD_EXTMODE_OSD1BLDCHR (1 << 11) +#define OSD_EXTMODE_OSD0BLDCHR (1 << 10) +#define OSD_EXTMODE_ATNOSD1EN (1 << 9) +#define OSD_EXTMODE_ATNOSD0EN (1 << 8) +#define OSD_EXTMODE_OSDHRSZ15 (1 << 7) +#define OSD_EXTMODE_VIDHRSZ15 (1 << 6) +#define OSD_EXTMODE_ZMFILV1HEN (1 << 5) +#define OSD_EXTMODE_ZMFILV1VEN (1 << 4) +#define OSD_EXTMODE_ZMFILV0HEN (1 << 3) +#define OSD_EXTMODE_ZMFILV0VEN (1 << 2) +#define OSD_EXTMODE_EXPFILHEN (1 << 1) +#define OSD_EXTMODE_EXPFILVEN (1 << 0) + +#define OSD_MISCCTL_BLDSEL (1 << 15) +#define OSD_MISCCTL_S420D (1 << 14) +#define OSD_MISCCTL_BMAPT (1 << 13) +#define OSD_MISCCTL_DM365M (1 << 12) +#define OSD_MISCCTL_RGBEN (1 << 7) +#define OSD_MISCCTL_RGBWIN (1 << 6) +#define OSD_MISCCTL_DMANG (1 << 6) +#define OSD_MISCCTL_TMON (1 << 5) +#define OSD_MISCCTL_RSEL (1 << 4) +#define OSD_MISCCTL_CPBSY (1 << 3) +#define OSD_MISCCTL_PPSW (1 << 2) +#define OSD_MISCCTL_PPRV (1 << 1) + +#define OSD_CLUTRAMYCB_Y_SHIFT 8 +#define OSD_CLUTRAMYCB_Y (0xff << 8) +#define OSD_CLUTRAMYCB_CB_SHIFT 0 +#define OSD_CLUTRAMYCB_CB (0xff << 0) + +#define OSD_CLUTRAMCR_CR_SHIFT 8 +#define OSD_CLUTRAMCR_CR (0xff << 8) +#define OSD_CLUTRAMCR_CADDR_SHIFT 0 +#define OSD_CLUTRAMCR_CADDR (0xff << 0) + +#define OSD_TRANSPVAL_RGBTRANS (0xffff << 0) + +#define OSD_TRANSPVALL_RGBL (0xffff << 0) + +#define OSD_TRANSPVALU_Y_SHIFT 8 +#define OSD_TRANSPVALU_Y (0xff << 8) +#define OSD_TRANSPVALU_RGBU_SHIFT 0 +#define OSD_TRANSPVALU_RGBU (0xff << 0) + +#define OSD_TRANSPBMPIDX_BMP1_SHIFT 8 +#define OSD_TRANSPBMPIDX_BMP1 (0xff << 8) +#define OSD_TRANSPBMPIDX_BMP0_SHIFT 0 +#define OSD_TRANSPBMPIDX_BMP0 0xff + +#endif /* _DAVINCI_VPBE_H_ */ diff --git a/include/media/davinci/vpbe_osd.h b/include/media/davinci/vpbe_osd.h new file mode 100644 index 0000000..d7e397a --- /dev/null +++ b/include/media/davinci/vpbe_osd.h @@ -0,0 +1,394 @@ +/* + * Copyright (C) 2007-2009 Texas Instruments Inc + * Copyright (C) 2007 MontaVista Software, Inc. + * + * Andy Lowe (alowe at mvista.com), MontaVista Software + * - Initial version + * Murali Karicheri (mkaricheri at gmail.com), Texas Instruments Ltd. + * - ported to sub device interface + * + * 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 _OSD_H +#define _OSD_H + +#include + +#define VPBE_OSD_SUBDEV_NAME "vpbe-osd" + +/** + * enum osd_layer + * @WIN_OSD0: On-Screen Display Window 0 + * @WIN_VID0: Video Window 0 + * @WIN_OSD1: On-Screen Display Window 1 + * @WIN_VID1: Video Window 1 + * + * Description: + * An enumeration of the osd display layers. + */ +enum osd_layer { + WIN_OSD0, + WIN_VID0, + WIN_OSD1, + WIN_VID1, +}; + +/** + * enum osd_win_layer + * @OSDWIN_OSD0: On-Screen Display Window 0 + * @OSDWIN_OSD1: On-Screen Display Window 1 + * + * Description: + * An enumeration of the OSD Window layers. + */ +enum osd_win_layer { + OSDWIN_OSD0, + OSDWIN_OSD1, +}; + +/** + * enum osd_pix_format + * @PIXFMT_1BPP: 1-bit-per-pixel bitmap + * @PIXFMT_2BPP: 2-bits-per-pixel bitmap + * @PIXFMT_4BPP: 4-bits-per-pixel bitmap + * @PIXFMT_8BPP: 8-bits-per-pixel bitmap + * @PIXFMT_RGB565: 16-bits-per-pixel RGB565 + * @PIXFMT_YCbCrI: YUV 4:2:2 + * @PIXFMT_RGB888: 24-bits-per-pixel RGB888 + * @PIXFMT_YCrCbI: YUV 4:2:2 with chroma swap + * @PIXFMT_NV12: YUV 4:2:0 planar + * @PIXFMT_OSD_ATTR: OSD Attribute Window pixel format (4bpp) + * + * Description: + * An enumeration of the DaVinci pixel formats. + */ +enum osd_pix_format { + PIXFMT_1BPP = 0, + PIXFMT_2BPP, + PIXFMT_4BPP, + PIXFMT_8BPP, + PIXFMT_RGB565, + PIXFMT_YCbCrI, + PIXFMT_RGB888, + PIXFMT_YCrCbI, + PIXFMT_NV12, + PIXFMT_OSD_ATTR, +}; + +/** + * enum osd_h_exp_ratio + * @H_EXP_OFF: no expansion (1/1) + * @H_EXP_9_OVER_8: 9/8 expansion ratio + * @H_EXP_3_OVER_2: 3/2 expansion ratio + * + * Description: + * An enumeration of the available horizontal expansion ratios. + */ +enum osd_h_exp_ratio { + H_EXP_OFF, + H_EXP_9_OVER_8, + H_EXP_3_OVER_2, +}; + +/** + * enum osd_v_exp_ratio + * @V_EXP_OFF: no expansion (1/1) + * @V_EXP_6_OVER_5: 6/5 expansion ratio + * + * Description: + * An enumeration of the available vertical expansion ratios. + */ +enum osd_v_exp_ratio { + V_EXP_OFF, + V_EXP_6_OVER_5, +}; + +/** + * enum osd_zoom_factor + * @ZOOM_X1: no zoom (x1) + * @ZOOM_X2: x2 zoom + * @ZOOM_X4: x4 zoom + * + * Description: + * An enumeration of the available zoom factors. + */ +enum osd_zoom_factor { + ZOOM_X1, + ZOOM_X2, + ZOOM_X4, +}; + +/** + * enum osd_clut + * @ROM_CLUT: ROM CLUT + * @RAM_CLUT: RAM CLUT + * + * Description: + * An enumeration of the available Color Lookup Tables (CLUTs). + */ +enum osd_clut { + ROM_CLUT, + RAM_CLUT, +}; + +/** + * enum osd_rom_clut + * @ROM_CLUT0: Macintosh CLUT + * @ROM_CLUT1: CLUT from DM270 and prior devices + * + * Description: + * An enumeration of the ROM Color Lookup Table (CLUT) options. + */ +enum osd_rom_clut { + ROM_CLUT0, + ROM_CLUT1, +}; + +/** + * enum osd_blending_factor + * @OSD_0_VID_8: OSD pixels are fully transparent + * @OSD_1_VID_7: OSD pixels contribute 1/8, video pixels contribute 7/8 + * @OSD_2_VID_6: OSD pixels contribute 2/8, video pixels contribute 6/8 + * @OSD_3_VID_5: OSD pixels contribute 3/8, video pixels contribute 5/8 + * @OSD_4_VID_4: OSD pixels contribute 4/8, video pixels contribute 4/8 + * @OSD_5_VID_3: OSD pixels contribute 5/8, video pixels contribute 3/8 + * @OSD_6_VID_2: OSD pixels contribute 6/8, video pixels contribute 2/8 + * @OSD_8_VID_0: OSD pixels are fully opaque + * + * Description: + * An enumeration of the DaVinci pixel blending factor options. + */ +enum osd_blending_factor { + OSD_0_VID_8, + OSD_1_VID_7, + OSD_2_VID_6, + OSD_3_VID_5, + OSD_4_VID_4, + OSD_5_VID_3, + OSD_6_VID_2, + OSD_8_VID_0, +}; + +/** + * enum osd_blink_interval + * @BLINK_X1: blink interval is 1 vertical refresh cycle + * @BLINK_X2: blink interval is 2 vertical refresh cycles + * @BLINK_X3: blink interval is 3 vertical refresh cycles + * @BLINK_X4: blink interval is 4 vertical refresh cycles + * + * Description: + * An enumeration of the DaVinci pixel blinking interval options. + */ +enum osd_blink_interval { + BLINK_X1, + BLINK_X2, + BLINK_X3, + BLINK_X4, +}; + +/** + * enum osd_cursor_h_width + * @H_WIDTH_1: horizontal line width is 1 pixel + * @H_WIDTH_4: horizontal line width is 4 pixels + * @H_WIDTH_8: horizontal line width is 8 pixels + * @H_WIDTH_12: horizontal line width is 12 pixels + * @H_WIDTH_16: horizontal line width is 16 pixels + * @H_WIDTH_20: horizontal line width is 20 pixels + * @H_WIDTH_24: horizontal line width is 24 pixels + * @H_WIDTH_28: horizontal line width is 28 pixels + */ +enum osd_cursor_h_width { + H_WIDTH_1, + H_WIDTH_4, + H_WIDTH_8, + H_WIDTH_12, + H_WIDTH_16, + H_WIDTH_20, + H_WIDTH_24, + H_WIDTH_28, +}; + +/** + * enum davinci_cursor_v_width + * @V_WIDTH_1: vertical line width is 1 line + * @V_WIDTH_2: vertical line width is 2 lines + * @V_WIDTH_4: vertical line width is 4 lines + * @V_WIDTH_6: vertical line width is 6 lines + * @V_WIDTH_8: vertical line width is 8 lines + * @V_WIDTH_10: vertical line width is 10 lines + * @V_WIDTH_12: vertical line width is 12 lines + * @V_WIDTH_14: vertical line width is 14 lines + */ +enum osd_cursor_v_width { + V_WIDTH_1, + V_WIDTH_2, + V_WIDTH_4, + V_WIDTH_6, + V_WIDTH_8, + V_WIDTH_10, + V_WIDTH_12, + V_WIDTH_14, +}; + +/** + * struct osd_cursor_config + * @xsize: horizontal size in pixels + * @ysize: vertical size in lines + * @xpos: horizontal offset in pixels from the left edge of the display + * @ypos: vertical offset in lines from the top of the display + * @interlaced: Non-zero if the display is interlaced, or zero otherwise + * @h_width: horizontal line width + * @v_width: vertical line width + * @clut: the CLUT selector (ROM or RAM) for the cursor color + * @clut_index: an index into the CLUT for the cursor color + * + * Description: + * A structure describing the configuration parameters of the hardware + * rectangular cursor. + */ +struct osd_cursor_config { + unsigned xsize; + unsigned ysize; + unsigned xpos; + unsigned ypos; + int interlaced; + enum osd_cursor_h_width h_width; + enum osd_cursor_v_width v_width; + enum osd_clut clut; + unsigned char clut_index; +}; + +/** + * struct osd_layer_config + * @pixfmt: pixel format + * @line_length: offset in bytes between start of each line in memory + * @xsize: number of horizontal pixels displayed per line + * @ysize: number of lines displayed + * @xpos: horizontal offset in pixels from the left edge of the display + * @ypos: vertical offset in lines from the top of the display + * @interlaced: Non-zero if the display is interlaced, or zero otherwise + * + * Description: + * A structure describing the configuration parameters of an On-Screen Display + * (OSD) or video layer related to how the image is stored in memory. + * @line_length must be a multiple of the cache line size (32 bytes). + */ +struct osd_layer_config { + enum osd_pix_format pixfmt; + unsigned line_length; + unsigned xsize; + unsigned ysize; + unsigned xpos; + unsigned ypos; + int interlaced; +}; + +/* parameters that apply on a per-window (OSD or video) basis */ +struct osd_window_state { + int is_allocated; + int is_enabled; + unsigned long fb_base_phys; + enum osd_zoom_factor h_zoom; + enum osd_zoom_factor v_zoom; + struct osd_layer_config lconfig; +}; + +/* parameters that apply on a per-OSD-window basis */ +struct osd_osdwin_state { + enum osd_clut clut; + enum osd_blending_factor blend; + int colorkey_blending; + unsigned colorkey; + int rec601_attenuation; + /* index is pixel value */ + unsigned char palette_map[16]; +}; + +/* hardware rectangular cursor parameters */ +struct osd_cursor_state { + int is_enabled; + struct osd_cursor_config config; +}; + +struct osd_state; + +struct vpbe_osd_ops { + int (*initialize)(struct osd_state *sd); + int (*request_layer)(struct osd_state *sd, enum osd_layer layer); + void (*release_layer)(struct osd_state *sd, enum osd_layer layer); + int (*enable_layer)(struct osd_state *sd, enum osd_layer layer, + int otherwin); + void (*disable_layer)(struct osd_state *sd, enum osd_layer layer); + int (*set_layer_config)(struct osd_state *sd, enum osd_layer layer, + struct osd_layer_config *lconfig); + void (*get_layer_config)(struct osd_state *sd, enum osd_layer layer, + struct osd_layer_config *lconfig); + void (*start_layer)(struct osd_state *sd, enum osd_layer layer, + unsigned long fb_base_phys, + unsigned long cbcr_ofst); + void (*set_left_margin)(struct osd_state *sd, u32 val); + void (*set_top_margin)(struct osd_state *sd, u32 val); + void (*set_interpolation_filter)(struct osd_state *sd, int filter); + int (*set_vid_expansion)(struct osd_state *sd, + enum osd_h_exp_ratio h_exp, + enum osd_v_exp_ratio v_exp); + void (*get_vid_expansion)(struct osd_state *sd, + enum osd_h_exp_ratio *h_exp, + enum osd_v_exp_ratio *v_exp); + void (*set_zoom)(struct osd_state *sd, enum osd_layer layer, + enum osd_zoom_factor h_zoom, + enum osd_zoom_factor v_zoom); +}; + +struct osd_state { + enum vpbe_version vpbe_type; + spinlock_t lock; + struct device *dev; + dma_addr_t osd_base_phys; + unsigned long osd_base; + unsigned long osd_size; + /* 1-->the isr will toggle the VID0 ping-pong buffer */ + int pingpong; + int interpolation_filter; + int field_inversion; + enum osd_h_exp_ratio osd_h_exp; + enum osd_v_exp_ratio osd_v_exp; + enum osd_h_exp_ratio vid_h_exp; + enum osd_v_exp_ratio vid_v_exp; + enum osd_clut backg_clut; + unsigned backg_clut_index; + enum osd_rom_clut rom_clut; + int is_blinking; + /* attribute window blinking enabled */ + enum osd_blink_interval blink; + /* YCbCrI or YCrCbI */ + enum osd_pix_format yc_pixfmt; + /* columns are Y, Cb, Cr */ + unsigned char clut_ram[256][3]; + struct osd_cursor_state cursor; + /* OSD0, VID0, OSD1, VID1 */ + struct osd_window_state win[4]; + /* OSD0, OSD1 */ + struct osd_osdwin_state osdwin[2]; + /* OSD device Operations */ + struct vpbe_osd_ops ops; +}; + +struct osd_platform_data { + enum vpbe_version vpbe_type; + int field_inv_wa_enable; +}; + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Fri Jun 17 02:01:31 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 17 Jun 2011 12:31:31 +0530 Subject: [RESEND PATCH v19 1/6] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: <1308294096-25743-1-git-send-email-manjunath.hadli@ti.com> References: <1308294096-25743-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1308294096-25743-2-git-send-email-manjunath.hadli@ti.com> This is the display driver for Texas Instruments's DM644X family SoC. This patch contains the main implementation of the driver with the V4L2 interface. The driver implements the streaming model with support for both kernel allocated buffers and user pointers. It also implements all of the necessary IOCTLs necessary and supported by the video display device. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- drivers/media/video/davinci/vpbe_display.c | 1860 ++++++++++++++++++++++++++++ include/media/davinci/vpbe_display.h | 147 +++ include/media/davinci/vpbe_types.h | 91 ++ 3 files changed, 2098 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpbe_display.c create mode 100644 include/media/davinci/vpbe_display.h create mode 100644 include/media/davinci/vpbe_types.h diff --git a/drivers/media/video/davinci/vpbe_display.c b/drivers/media/video/davinci/vpbe_display.c new file mode 100644 index 0000000..7f1d83a --- /dev/null +++ b/drivers/media/video/davinci/vpbe_display.c @@ -0,0 +1,1860 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vpbe_venc_regs.h" + +#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" + +static int debug; + +#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) +#define VPBE_DEFAULT_NUM_BUFS 3 + +module_param(debug, int, 0644); + +static int venc_is_second_field(struct vpbe_display *disp_dev) +{ + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + int ret; + int val; + + ret = v4l2_subdev_call(vpbe_dev->venc, + core, + ioctl, + VENC_GET_FLD, + &val); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in getting Field ID 0\n"); + } + return val; +} + +static void vpbe_isr_even_field(struct vpbe_display *disp_obj, + struct vpbe_layer *layer) +{ + struct timespec timevalue; + + if (layer->cur_frm == layer->next_frm) + return; + ktime_get_ts(&timevalue); + layer->cur_frm->ts.tv_sec = timevalue.tv_sec; + layer->cur_frm->ts.tv_usec = timevalue.tv_nsec / NSEC_PER_USEC; + layer->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible(&layer->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + layer->cur_frm = layer->next_frm; +} + +static void vpbe_isr_odd_field(struct vpbe_display *disp_obj, + struct vpbe_layer *layer) +{ + struct osd_state *osd_device = disp_obj->osd_device; + unsigned long addr; + + spin_lock(&disp_obj->dma_queue_lock); + if (list_empty(&layer->dma_queue) || + (layer->cur_frm != layer->next_frm)) { + spin_unlock(&disp_obj->dma_queue_lock); + return; + } + /* + * one field is displayed configure + * the next frame if it is available + * otherwise hold on current frame + * Get next from the buffer queue + */ + layer->next_frm = list_entry( + layer->dma_queue.next, + struct videobuf_buffer, + queue); + /* Remove that from the buffer queue */ + list_del(&layer->next_frm->queue); + spin_unlock(&disp_obj->dma_queue_lock); + /* Mark state of the frame to active */ + layer->next_frm->state = VIDEOBUF_ACTIVE; + addr = videobuf_to_dma_contig(layer->next_frm); + osd_device->ops.start_layer(osd_device, + layer->layer_info.id, + addr, + disp_obj->cbcr_ofst); +} + +/* interrupt service routine */ +static irqreturn_t venc_isr(int irq, void *arg) +{ + struct vpbe_display *disp_dev = (struct vpbe_display *)arg; + struct vpbe_layer *layer; + static unsigned last_event; + unsigned event = 0; + int fid; + int i; + + if ((NULL == arg) || (NULL == disp_dev->dev[0])) + return IRQ_HANDLED; + + if (venc_is_second_field(disp_dev)) + event |= VENC_SECOND_FIELD; + else + event |= VENC_FIRST_FIELD; + + if (event == (last_event & ~VENC_END_OF_FRAME)) { + /* + * If the display is non-interlaced, then we need to flag the + * end-of-frame event at every interrupt regardless of the + * value of the FIDST bit. We can conclude that the display is + * non-interlaced if the value of the FIDST bit is unchanged + * from the previous interrupt. + */ + event |= VENC_END_OF_FRAME; + } else if (event == VENC_SECOND_FIELD) { + /* end-of-frame for interlaced display */ + event |= VENC_END_OF_FRAME; + } + last_event = event; + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + layer = disp_dev->dev[i]; + /* If streaming is started in this layer */ + if (!layer->started) + continue; + + if (layer->layer_first_int) { + layer->layer_first_int = 0; + continue; + } + /* Check the field format */ + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && + (event & VENC_END_OF_FRAME)) { + /* Progressive mode */ + + vpbe_isr_even_field(disp_dev, layer); + vpbe_isr_odd_field(disp_dev, layer); + } else { + /* Interlaced mode */ + + layer->field_id ^= 1; + if (event & VENC_FIRST_FIELD) + fid = 0; + else + fid = 1; + + /* + * If field id does not match with store + * field id + */ + if (fid != layer->field_id) { + /* Make them in sync */ + layer->field_id = fid; + continue; + } + /* + * device field id and local field id are + * in sync. If this is even field + */ + if (0 == fid) + vpbe_isr_even_field(disp_dev, layer); + else /* odd field */ + vpbe_isr_odd_field(disp_dev, layer); + } + } + + return IRQ_HANDLED; +} + +/* + * vpbe_buffer_prepare() + * This is the callback function called from videobuf_qbuf() function + * the buffer is prepared and user space virtual address is converted into + * physical address + */ +static int vpbe_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vpbe_fh *fh = q->priv_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + unsigned long addr; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "vpbe_buffer_prepare\n"); + + /* If buffer is not initialized, initialize it */ + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = layer->pix_fmt.width; + vb->height = layer->pix_fmt.height; + vb->size = layer->pix_fmt.sizeimage; + vb->field = field; + + ret = videobuf_iolock(q, vb, NULL); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, "Failed to map \ + user address\n"); + return -EINVAL; + } + + addr = videobuf_to_dma_contig(vb); + + if (q->streaming) { + if (!IS_ALIGNED(addr, 8)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "buffer_prepare:offset is \ + not aligned to 32 bytes\n"); + return -EINVAL; + } + } + vb->state = VIDEOBUF_PREPARED; + } + return 0; +} + +/* + * vpbe_buffer_setup() + * This function allocates memory for the buffers + */ +static int vpbe_buffer_setup(struct videobuf_queue *q, + unsigned int *count, + unsigned int *size) +{ + /* Get the file handle object and layer object */ + struct vpbe_fh *fh = q->priv_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); + + *size = layer->pix_fmt.sizeimage; + + /* Store number of buffers allocated in numbuffer member */ + if (*count < VPBE_DEFAULT_NUM_BUFS) + *count = layer->numbuffers = VPBE_DEFAULT_NUM_BUFS; + + return 0; +} + +/* + * vpbe_buffer_queue() + * This function adds the buffer to DMA queue + */ +static void vpbe_buffer_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and layer object */ + struct vpbe_fh *fh = q->priv_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_display *disp = fh->disp_dev; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + unsigned long flags; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "vpbe_buffer_queue\n"); + + /* add the buffer to the DMA queue */ + spin_lock_irqsave(&disp->dma_queue_lock, flags); + list_add_tail(&vb->queue, &layer->dma_queue); + spin_unlock_irqrestore(&disp->dma_queue_lock, flags); + /* Change state of the buffer */ + vb->state = VIDEOBUF_QUEUED; +} + +/* + * vpbe_buffer_release() + * This function is called from the videobuf layer to free memory allocated to + * the buffers + */ +static void vpbe_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and layer object */ + struct vpbe_fh *fh = q->priv_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "vpbe_buffer_release\n"); + + if (V4L2_MEMORY_USERPTR != layer->memory) + videobuf_dma_contig_free(q, vb); + + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops video_qops = { + .buf_setup = vpbe_buffer_setup, + .buf_prepare = vpbe_buffer_prepare, + .buf_queue = vpbe_buffer_queue, + .buf_release = vpbe_buffer_release, +}; + +static +struct vpbe_layer* +_vpbe_display_get_other_win_layer(struct vpbe_display *disp_dev, + struct vpbe_layer *layer) +{ + enum vpbe_display_device_id thiswin, otherwin; + thiswin = layer->device_id; + + otherwin = (thiswin == VPBE_DISPLAY_DEVICE_0) ? + VPBE_DISPLAY_DEVICE_1 : VPBE_DISPLAY_DEVICE_0; + return disp_dev->dev[otherwin]; +} + +static int vpbe_set_osd_display_params(struct vpbe_display *disp_dev, + struct vpbe_layer *layer) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + struct osd_state *osd_device = disp_dev->osd_device; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + unsigned long addr; + int ret; + + addr = videobuf_to_dma_contig(layer->cur_frm); + /* Set address in the display registers */ + osd_device->ops.start_layer(osd_device, + layer->layer_info.id, + addr, + disp_dev->cbcr_ofst); + + ret = osd_device->ops.enable_layer(osd_device, + layer->layer_info.id, 0); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in enabling osd window layer 0\n"); + return -1; + } + + /* Enable the window */ + layer->layer_info.enable = 1; + if (cfg->pixfmt == PIXFMT_NV12) { + struct vpbe_layer *otherlayer = + _vpbe_display_get_other_win_layer(disp_dev, layer); + + ret = osd_device->ops.enable_layer(osd_device, + otherlayer->layer_info.id, 1); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in enabling osd window layer 1\n"); + return -1; + } + otherlayer->layer_info.enable = 1; + } + return 0; +} + +static void +vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev, + struct vpbe_layer *layer, + int expected_xsize, int expected_ysize) +{ + struct display_layer_info *layer_info = &layer->layer_info; + struct v4l2_pix_format *pixfmt = &layer->pix_fmt; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + int calculated_xsize; + int h_exp = 0; + int v_exp = 0; + int h_scale; + int v_scale; + + v4l2_std_id standard_id = vpbe_dev->current_timings.timings.std_id; + + /* + * Application initially set the image format. Current display + * size is obtained from the vpbe display controller. expected_xsize + * and expected_ysize are set through S_CROP ioctl. Based on this, + * driver will calculate the scale factors for vertical and + * horizontal direction so that the image is displayed scaled + * and expanded. Application uses expansion to display the image + * in a square pixel. Otherwise it is displayed using displays + * pixel aspect ratio.It is expected that application chooses + * the crop coordinates for cropped or scaled display. if crop + * size is less than the image size, it is displayed cropped or + * it is displayed scaled and/or expanded. + * + * to begin with, set the crop window same as expected. Later we + * will override with scaled window size + */ + + cfg->xsize = pixfmt->width; + cfg->ysize = pixfmt->height; + layer_info->h_zoom = ZOOM_X1; /* no horizontal zoom */ + layer_info->v_zoom = ZOOM_X1; /* no horizontal zoom */ + layer_info->h_exp = H_EXP_OFF; /* no horizontal zoom */ + layer_info->v_exp = V_EXP_OFF; /* no horizontal zoom */ + + if (pixfmt->width < expected_xsize) { + h_scale = vpbe_dev->current_timings.xres / pixfmt->width; + if (h_scale < 2) + h_scale = 1; + else if (h_scale >= 4) + h_scale = 4; + else + h_scale = 2; + cfg->xsize *= h_scale; + if (cfg->xsize < expected_xsize) { + if ((standard_id & V4L2_STD_525_60) || + (standard_id & V4L2_STD_625_50)) { + calculated_xsize = (cfg->xsize * + VPBE_DISPLAY_H_EXP_RATIO_N) / + VPBE_DISPLAY_H_EXP_RATIO_D; + if (calculated_xsize <= expected_xsize) { + h_exp = 1; + cfg->xsize = calculated_xsize; + } + } + } + if (h_scale == 2) + layer_info->h_zoom = ZOOM_X2; + else if (h_scale == 4) + layer_info->h_zoom = ZOOM_X4; + if (h_exp) + layer_info->h_exp = H_EXP_9_OVER_8; + } else { + /* no scaling, only cropping. Set display area to crop area */ + cfg->xsize = expected_xsize; + } + + if (pixfmt->height < expected_ysize) { + v_scale = expected_ysize / pixfmt->height; + if (v_scale < 2) + v_scale = 1; + else if (v_scale >= 4) + v_scale = 4; + else + v_scale = 2; + cfg->ysize *= v_scale; + if (cfg->ysize < expected_ysize) { + if ((standard_id & V4L2_STD_625_50)) { + calculated_xsize = (cfg->ysize * + VPBE_DISPLAY_V_EXP_RATIO_N) / + VPBE_DISPLAY_V_EXP_RATIO_D; + if (calculated_xsize <= expected_ysize) { + v_exp = 1; + cfg->ysize = calculated_xsize; + } + } + } + if (v_scale == 2) + layer_info->v_zoom = ZOOM_X2; + else if (v_scale == 4) + layer_info->v_zoom = ZOOM_X4; + if (v_exp) + layer_info->h_exp = V_EXP_6_OVER_5; + } else { + /* no scaling, only cropping. Set display area to crop area */ + cfg->ysize = expected_ysize; + } + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "crop display xsize = %d, ysize = %d\n", + cfg->xsize, cfg->ysize); +} + +static void vpbe_disp_adj_position(struct vpbe_display *disp_dev, + struct vpbe_layer *layer, + int top, int left) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + + cfg->xpos = min((unsigned int)left, + vpbe_dev->current_timings.xres - cfg->xsize); + cfg->ypos = min((unsigned int)top, + vpbe_dev->current_timings.yres - cfg->ysize); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "new xpos = %d, ypos = %d\n", + cfg->xpos, cfg->ypos); +} + +static void vpbe_disp_check_window_params(struct vpbe_display *disp_dev, + struct v4l2_rect *c) +{ + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + + if ((c->width == 0) || + ((c->width + c->left) > vpbe_dev->current_timings.xres)) + c->width = vpbe_dev->current_timings.xres - c->left; + + if ((c->height == 0) || ((c->height + c->top) > + vpbe_dev->current_timings.yres)) + c->height = vpbe_dev->current_timings.yres - c->top; + + /* window height must be even for interlaced display */ + if (vpbe_dev->current_timings.interlaced) + c->height &= (~0x01); + +} + +/** + * vpbe_try_format() + * If user application provides width and height, and have bytesperline set + * to zero, driver calculates bytesperline and sizeimage based on hardware + * limits. + */ +static int vpbe_try_format(struct vpbe_display *disp_dev, + struct v4l2_pix_format *pixfmt, int check) +{ + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + int min_height = 1; + int min_width = 32; + int max_height; + int max_width; + int bpp; + + if ((pixfmt->pixelformat != V4L2_PIX_FMT_UYVY) && + (pixfmt->pixelformat != V4L2_PIX_FMT_NV12)) + /* choose default as V4L2_PIX_FMT_UYVY */ + pixfmt->pixelformat = V4L2_PIX_FMT_UYVY; + + /* Check the field format */ + if ((pixfmt->field != V4L2_FIELD_INTERLACED) && + (pixfmt->field != V4L2_FIELD_NONE)) { + if (vpbe_dev->current_timings.interlaced) + pixfmt->field = V4L2_FIELD_INTERLACED; + else + pixfmt->field = V4L2_FIELD_NONE; + } + + if (pixfmt->field == V4L2_FIELD_INTERLACED) + min_height = 2; + + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + bpp = 1; + else + bpp = 2; + + max_width = vpbe_dev->current_timings.xres; + max_height = vpbe_dev->current_timings.yres; + + min_width /= bpp; + + if (!pixfmt->width || (pixfmt->width < min_width) || + (pixfmt->width > max_width)) { + pixfmt->width = vpbe_dev->current_timings.xres; + } + + if (!pixfmt->height || (pixfmt->height < min_height) || + (pixfmt->height > max_height)) { + pixfmt->height = vpbe_dev->current_timings.yres; + } + + if (pixfmt->bytesperline < (pixfmt->width * bpp)) + pixfmt->bytesperline = pixfmt->width * bpp; + + /* Make the bytesperline 32 byte aligned */ + pixfmt->bytesperline = ((pixfmt->width * bpp + 31) & ~31); + + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height + + (pixfmt->bytesperline * pixfmt->height >> 1); + else + pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height; + + return 0; +} + +static int vpbe_display_g_priority(struct file *file, void *priv, + enum v4l2_priority *p) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + + *p = v4l2_prio_max(&layer->prio); + + return 0; +} + +static int vpbe_display_s_priority(struct file *file, void *priv, + enum v4l2_priority p) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + int ret; + + ret = v4l2_prio_change(&layer->prio, &fh->prio, p); + + return ret; +} + +static int vpbe_display_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + cap->version = VPBE_DISPLAY_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + strlcpy(cap->driver, VPBE_DISPLAY_DRIVER, sizeof(cap->driver)); + strlcpy(cap->bus_info, "platform", sizeof(cap->bus_info)); + strlcpy(cap->card, vpbe_dev->cfg->module_name, sizeof(cap->card)); + + return 0; +} + +static int vpbe_display_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct osd_state *osd_device = disp_dev->osd_device; + struct v4l2_rect *rect = &crop->c; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_S_CROP, layer id = %d\n", layer->device_id); + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + if (rect->top < 0) + rect->top = 0; + if (rect->left < 0) + rect->left = 0; + + vpbe_disp_check_window_params(disp_dev, rect); + + osd_device->ops.get_layer_config(osd_device, + layer->layer_info.id, cfg); + + vpbe_disp_calculate_scale_factor(disp_dev, layer, + rect->width, + rect->height); + vpbe_disp_adj_position(disp_dev, layer, rect->top, + rect->left); + ret = osd_device->ops.set_layer_config(osd_device, + layer->layer_info.id, cfg); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in set layer config:\n"); + return -EINVAL; + } + + /* apply zooming and h or v expansion */ + osd_device->ops.set_zoom(osd_device, + layer->layer_info.id, + layer->layer_info.h_zoom, + layer->layer_info.v_zoom); + ret = osd_device->ops.set_vid_expansion(osd_device, + layer->layer_info.h_exp, + layer->layer_info.v_exp); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in set vid expansion:\n"); + return -EINVAL; + } + + if ((layer->layer_info.h_zoom != ZOOM_X1) || + (layer->layer_info.v_zoom != ZOOM_X1) || + (layer->layer_info.h_exp != H_EXP_OFF) || + (layer->layer_info.v_exp != V_EXP_OFF)) + /* Enable expansion filter */ + osd_device->ops.set_interpolation_filter(osd_device, 1); + else + osd_device->ops.set_interpolation_filter(osd_device, 0); + + return 0; +} + +static int vpbe_display_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct osd_state *osd_device = fh->disp_dev->osd_device; + struct v4l2_rect *rect = &crop->c; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_G_CROP, layer id = %d\n", + layer->device_id); + + if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + ret = -EINVAL; + } + osd_device->ops.get_layer_config(osd_device, + layer->layer_info.id, cfg); + rect->top = cfg->ypos; + rect->left = cfg->xpos; + rect->width = cfg->xsize; + rect->height = cfg->ysize; + + return 0; +} + +static int vpbe_display_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_CROPCAP ioctl\n"); + + cropcap->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + cropcap->bounds.left = 0; + cropcap->bounds.top = 0; + cropcap->bounds.width = vpbe_dev->current_timings.xres; + cropcap->bounds.height = vpbe_dev->current_timings.yres; + cropcap->pixelaspect = vpbe_dev->current_timings.aspect; + cropcap->defrect = cropcap->bounds; + return 0; +} + +static int vpbe_display_g_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_G_FMT, layer id = %d\n", + layer->device_id); + + /* If buffer type is video output */ + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + /* Fill in the information about format */ + fmt->fmt.pix = layer->pix_fmt; + + return 0; +} + +static int vpbe_display_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + unsigned int index = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_ENUM_FMT, layer id = %d\n", + layer->device_id); + if (fmt->index > 1) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid format index\n"); + return -EINVAL; + } + + /* Fill in the information about format */ + index = fmt->index; + memset(fmt, 0, sizeof(*fmt)); + fmt->index = index; + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (index == 0) { + strcpy(fmt->description, "YUV 4:2:2 - UYVY"); + fmt->pixelformat = V4L2_PIX_FMT_UYVY; + } else { + strcpy(fmt->description, "Y/CbCr 4:2:0"); + fmt->pixelformat = V4L2_PIX_FMT_NV12; + } + + return 0; +} + +static int vpbe_display_s_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + struct osd_state *osd_device = disp_dev->osd_device; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_S_FMT, layer id = %d\n", + layer->device_id); + + /* If streaming is started, return error */ + if (layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + /* Check for valid pixel format */ + ret = vpbe_try_format(disp_dev, pixfmt, 1); + if (ret) + return ret; + + /* YUV420 is requested, check availability of the + other video window */ + + layer->pix_fmt = *pixfmt; + + /* Get osd layer config */ + osd_device->ops.get_layer_config(osd_device, + layer->layer_info.id, cfg); + /* Store the pixel format in the layer object */ + cfg->xsize = pixfmt->width; + cfg->ysize = pixfmt->height; + cfg->line_length = pixfmt->bytesperline; + cfg->ypos = 0; + cfg->xpos = 0; + cfg->interlaced = vpbe_dev->current_timings.interlaced; + + if (V4L2_PIX_FMT_UYVY == pixfmt->pixelformat) + cfg->pixfmt = PIXFMT_YCbCrI; + + /* Change of the default pixel format for both video windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { + struct vpbe_layer *otherlayer; + cfg->pixfmt = PIXFMT_NV12; + otherlayer = _vpbe_display_get_other_win_layer(disp_dev, + layer); + otherlayer->layer_info.config.pixfmt = PIXFMT_NV12; + } + + /* Set the layer config in the osd window */ + ret = osd_device->ops.set_layer_config(osd_device, + layer->layer_info.id, cfg); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in S_FMT params:\n"); + return -EINVAL; + } + + /* Readback and fill the local copy of current pix format */ + osd_device->ops.get_layer_config(osd_device, + layer->layer_info.id, cfg); + + return 0; +} + +static int vpbe_display_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + + /* Check for valid field format */ + return vpbe_try_format(disp_dev, pixfmt, 0); + +} + +/** + * vpbe_display_s_std - Set the given standard in the encoder + * + * Sets the standard if supported by the current encoder. Return the status. + * 0 - success & -EINVAL on error + */ +static int vpbe_display_s_std(struct file *file, void *priv, + v4l2_std_id *std_id) +{ + struct vpbe_fh *fh = priv; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_STD\n"); + + /* If streaming is started, return error */ + if (layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + if (NULL != vpbe_dev->ops.s_std) { + ret = vpbe_dev->ops.s_std(vpbe_dev, std_id); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Failed to set standard for sub devices\n"); + return -EINVAL; + } + } else { + return -EINVAL; + } + + return 0; +} + +/** + * vpbe_display_g_std - Get the standard in the current encoder + * + * Get the standard in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int vpbe_display_g_std(struct file *file, void *priv, + v4l2_std_id *std_id) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n"); + + /* Get the standard from the current encoder */ + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { + *std_id = vpbe_dev->current_timings.timings.std_id; + return 0; + } + + return -EINVAL; +} + +/** + * vpbe_display_enum_output - enumerate outputs + * + * Enumerates the outputs available at the vpbe display + * returns the status, -EINVAL if end of output list + */ +static int vpbe_display_enum_output(struct file *file, void *priv, + struct v4l2_output *output) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); + + /* Enumerate outputs */ + + if (NULL == vpbe_dev->ops.enum_outputs) + return -EINVAL; + + ret = vpbe_dev->ops.enum_outputs(vpbe_dev, output); + if (ret) { + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "Failed to enumerate outputs\n"); + return -EINVAL; + } + + return 0; +} + +/** + * vpbe_display_s_output - Set output to + * the output specified by the index + */ +static int vpbe_display_s_output(struct file *file, void *priv, + unsigned int i) +{ + struct vpbe_fh *fh = priv; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_OUTPUT\n"); + /* If streaming is started, return error */ + if (layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + if (NULL == vpbe_dev->ops.set_output) + return -EINVAL; + + ret = vpbe_dev->ops.set_output(vpbe_dev, i); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Failed to set output for sub devices\n"); + return -EINVAL; + } + + return 0; +} + +/** + * vpbe_display_g_output - Get output from subdevice + * for a given by the index + */ +static int vpbe_display_g_output(struct file *file, void *priv, + unsigned int *i) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_OUTPUT\n"); + /* Get the standard from the current encoder */ + *i = vpbe_dev->current_out_index; + + return 0; +} + +/** + * vpbe_display_enum_dv_presets - Enumerate the dv presets + * + * enum the preset in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int +vpbe_display_enum_dv_presets(struct file *file, void *priv, + struct v4l2_dv_enum_preset *preset) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n"); + + /* Enumerate outputs */ + if (NULL == vpbe_dev->ops.enum_dv_presets) + return -EINVAL; + + ret = vpbe_dev->ops.enum_dv_presets(vpbe_dev, preset); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Failed to enumerate dv presets info\n"); + return -EINVAL; + } + + return 0; +} + +/** + * vpbe_display_s_dv_preset - Set the dv presets + * + * Set the preset in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int +vpbe_display_s_dv_preset(struct file *file, void *priv, + struct v4l2_dv_preset *preset) +{ + struct vpbe_fh *fh = priv; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_DV_PRESETS\n"); + + + /* If streaming is started, return error */ + if (layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + + /* Set the given standard in the encoder */ + if (NULL != vpbe_dev->ops.s_dv_preset) + return -EINVAL; + + ret = vpbe_dev->ops.s_dv_preset(vpbe_dev, preset); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Failed to set the dv presets info\n"); + return -EINVAL; + } + /* set the current norm to zero to be consistent. If STD is used + * v4l2 layer will set the norm properly on successful s_std call + */ + layer->video_dev.current_norm = 0; + + return 0; +} + +/** + * vpbe_display_g_dv_preset - Set the dv presets + * + * Get the preset in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int +vpbe_display_g_dv_preset(struct file *file, void *priv, + struct v4l2_dv_preset *dv_preset) +{ + struct vpbe_fh *fh = priv; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_DV_PRESETS\n"); + + /* Get the given standard in the encoder */ + + if (vpbe_dev->current_timings.timings_type & + VPBE_ENC_DV_PRESET) { + dv_preset->preset = + vpbe_dev->current_timings.timings.dv_preset; + } else { + return -EINVAL; + } + + return 0; +} + +static int vpbe_display_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct osd_state *osd_device = fh->disp_dev->osd_device; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_STREAMOFF,layer id = %d\n", + layer->device_id); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "streaming not started in layer" + " id = %d\n", layer->device_id); + return -EINVAL; + } + + osd_device->ops.disable_layer(osd_device, + layer->layer_info.id); + layer->started = 0; + ret = videobuf_streamoff(&layer->buffer_queue); + + return ret; +} + +static int vpbe_display_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + struct osd_state *osd_device = disp_dev->osd_device; + int ret; + + osd_device->ops.disable_layer(osd_device, + layer->layer_info.id); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_STREAMON, layerid=%d\n", + layer->device_id); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); + return -EACCES; + } + /* If Streaming is already started, return error */ + if (layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "layer is already streaming\n"); + return -EBUSY; + } + + /* + * Call videobuf_streamon to start streaming + * in videobuf + */ + ret = videobuf_streamon(&layer->buffer_queue); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, + "error in videobuf_streamon\n"); + return ret; + } + /* If buffer queue is empty, return error */ + if (list_empty(&layer->dma_queue)) { + v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n"); + goto streamoff; + } + /* Get the next frame from the buffer queue */ + layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove buffer from the buffer queue */ + list_del(&layer->cur_frm->queue); + /* Mark state of the current frame to active */ + layer->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + layer->field_id = 0; + + /* Set parameters in OSD and VENC */ + ret = vpbe_set_osd_display_params(disp_dev, layer); + if (ret < 0) + goto streamoff; + + /* + * if request format is yuv420 semiplanar, need to + * enable both video windows + */ + layer->started = 1; + + layer->layer_first_int = 1; + + return ret; +streamoff: + ret = videobuf_streamoff(&layer->buffer_queue); + return ret; +} + +static int vpbe_display_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_DQBUF, layer id = %d\n", + layer->device_id); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + /* If this file handle is not allowed to do IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); + return -EACCES; + } + if (file->f_flags & O_NONBLOCK) + /* Call videobuf_dqbuf for non blocking mode */ + ret = videobuf_dqbuf(&layer->buffer_queue, buf, 1); + else + /* Call videobuf_dqbuf for blocking mode */ + ret = videobuf_dqbuf(&layer->buffer_queue, buf, 0); + + return ret; +} + +static int vpbe_display_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_QBUF, layer id = %d\n", + layer->device_id); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + /* If this file handle is not allowed to do IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); + return -EACCES; + } + + return videobuf_qbuf(&layer->buffer_queue, p); +} + +static int vpbe_display_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_QUERYBUF, layer id = %d\n", + layer->device_id); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + /* Call videobuf_querybuf to get information */ + ret = videobuf_querybuf(&layer->buffer_queue, buf); + + return ret; +} + +static int vpbe_display_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + int ret; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_reqbufs\n"); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + /* If io users of the layer is not zero, return error */ + if (0 != layer->io_usrs) { + v4l2_err(&vpbe_dev->v4l2_dev, "not IO user\n"); + return -EBUSY; + } + /* Initialize videobuf queue as per the buffer type */ + videobuf_queue_dma_contig_init(&layer->buffer_queue, + &video_qops, + vpbe_dev->pdev, + &layer->irqlock, + V4L2_BUF_TYPE_VIDEO_OUTPUT, + layer->pix_fmt.field, + sizeof(struct videobuf_buffer), + fh, NULL); + + /* Set io allowed member of file handle to TRUE */ + fh->io_allowed = 1; + /* Increment io usrs member of layer object to 1 */ + layer->io_usrs = 1; + /* Store type of memory requested in layer object */ + layer->memory = req_buf->memory; + /* Initialize buffer queue */ + INIT_LIST_HEAD(&layer->dma_queue); + /* Allocate buffers */ + ret = videobuf_reqbufs(&layer->buffer_queue, req_buf); + + return ret; +} + +/* + * vpbe_display_mmap() + * It is used to map kernel space buffers into user spaces + */ +static int vpbe_display_mmap(struct file *filep, struct vm_area_struct *vma) +{ + /* Get the layer object and file handle object */ + struct vpbe_fh *fh = filep->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_mmap\n"); + + return videobuf_mmap_mapper(&layer->buffer_queue, vma); +} + +/* vpbe_display_poll(): It is used for select/poll system call + */ +static unsigned int vpbe_display_poll(struct file *filep, poll_table *wait) +{ + struct vpbe_fh *fh = filep->private_data; + struct vpbe_layer *layer = fh->layer; + struct vpbe_device *vpbe_dev = fh->disp_dev->vpbe_dev; + unsigned int err = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_poll\n"); + if (layer->started) + err = videobuf_poll_stream(filep, &layer->buffer_queue, wait); + return err; +} + +/* + * vpbe_display_open() + * It creates object of file handle structure and stores it in private_data + * member of filepointer + */ +static int vpbe_display_open(struct file *file) +{ + struct vpbe_fh *fh = NULL; + struct vpbe_layer *layer = video_drvdata(file); + struct vpbe_display *disp_dev = layer->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_state *osd_device = disp_dev->osd_device; + int err; + + /* Allocate memory for the file handle object */ + fh = kmalloc(sizeof(struct vpbe_fh), GFP_KERNEL); + if (fh == NULL) { + v4l2_err(&vpbe_dev->v4l2_dev, + "unable to allocate memory for file handle object\n"); + return -ENOMEM; + } + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "vpbe display open plane = %d\n", + layer->device_id); + + /* store pointer to fh in private_data member of filep */ + file->private_data = fh; + fh->layer = layer; + fh->disp_dev = disp_dev; + + if (!layer->usrs) { + + /* First claim the layer for this device */ + err = osd_device->ops.request_layer(osd_device, + layer->layer_info.id); + if (err < 0) { + /* Couldn't get layer */ + v4l2_err(&vpbe_dev->v4l2_dev, + "Display Manager failed to allocate layer\n"); + kfree(fh); + return -EINVAL; + } + } + /* Increment layer usrs counter */ + layer->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(&layer->prio, &fh->prio); + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "vpbe display device opened successfully\n"); + return 0; +} + +/* + * vpbe_display_release() + * This function deletes buffer queue, frees the buffers and the davinci + * display file * handle + */ +static int vpbe_display_release(struct file *file) +{ + /* Get the layer object and file handle object */ + struct vpbe_fh *fh = file->private_data; + struct vpbe_layer *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_display *disp_dev = fh->disp_dev; + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct osd_state *osd_device = disp_dev->osd_device; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); + + /* if this instance is doing IO */ + if (fh->io_allowed) { + /* Reset io_usrs member of layer object */ + layer->io_usrs = 0; + + osd_device->ops.disable_layer(osd_device, + layer->layer_info.id); + layer->started = 0; + /* Free buffers allocated */ + videobuf_queue_cancel(&layer->buffer_queue); + videobuf_mmap_free(&layer->buffer_queue); + } + + /* Decrement layer usrs counter */ + layer->usrs--; + /* If this file handle has initialize encoder device, reset it */ + if (!layer->usrs) { + if (cfg->pixfmt == PIXFMT_NV12) { + struct vpbe_layer *otherlayer; + otherlayer = + _vpbe_display_get_other_win_layer(disp_dev, layer); + osd_device->ops.disable_layer(osd_device, + otherlayer->layer_info.id); + osd_device->ops.release_layer(osd_device, + otherlayer->layer_info.id); + } + osd_device->ops.disable_layer(osd_device, + layer->layer_info.id); + osd_device->ops.release_layer(osd_device, + layer->layer_info.id); + } + /* Close the priority */ + v4l2_prio_close(&layer->prio, fh->prio); + file->private_data = NULL; + + /* Free memory allocated to file handle object */ + kfree(fh); + + disp_dev->cbcr_ofst = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vpbe_display_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct v4l2_dbg_match *match = ®->match; + + if (match->type >= 2) { + v4l2_subdev_call(vpbe_dev->venc, + core, + g_register, + reg); + } + + return 0; +} + +static int vpbe_display_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + return 0; +} +#endif + +/* vpbe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpbe_ioctl_ops = { + .vidioc_querycap = vpbe_display_querycap, + .vidioc_g_fmt_vid_out = vpbe_display_g_fmt, + .vidioc_enum_fmt_vid_out = vpbe_display_enum_fmt, + .vidioc_s_fmt_vid_out = vpbe_display_s_fmt, + .vidioc_try_fmt_vid_out = vpbe_display_try_fmt, + .vidioc_reqbufs = vpbe_display_reqbufs, + .vidioc_querybuf = vpbe_display_querybuf, + .vidioc_qbuf = vpbe_display_qbuf, + .vidioc_dqbuf = vpbe_display_dqbuf, + .vidioc_streamon = vpbe_display_streamon, + .vidioc_streamoff = vpbe_display_streamoff, + .vidioc_cropcap = vpbe_display_cropcap, + .vidioc_g_crop = vpbe_display_g_crop, + .vidioc_s_crop = vpbe_display_s_crop, + .vidioc_g_priority = vpbe_display_g_priority, + .vidioc_s_priority = vpbe_display_s_priority, + .vidioc_s_std = vpbe_display_s_std, + .vidioc_g_std = vpbe_display_g_std, + .vidioc_enum_output = vpbe_display_enum_output, + .vidioc_s_output = vpbe_display_s_output, + .vidioc_g_output = vpbe_display_g_output, + .vidioc_s_dv_preset = vpbe_display_s_dv_preset, + .vidioc_g_dv_preset = vpbe_display_g_dv_preset, + .vidioc_enum_dv_presets = vpbe_display_enum_dv_presets, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vpbe_display_g_register, + .vidioc_s_register = vpbe_display_s_register, +#endif +}; + +static struct v4l2_file_operations vpbe_fops = { + .owner = THIS_MODULE, + .open = vpbe_display_open, + .release = vpbe_display_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vpbe_display_mmap, + .poll = vpbe_display_poll +}; + +static int vpbe_device_get(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct vpbe_display *vpbe_disp = data; + + if (strcmp("vpbe_controller", pdev->name) == 0) + vpbe_disp->vpbe_dev = platform_get_drvdata(pdev); + + if (strcmp("vpbe-osd", pdev->name) == 0) + vpbe_disp->osd_device = platform_get_drvdata(pdev); + + return 0; +} + +static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev, + struct platform_device *pdev) +{ + struct vpbe_layer *vpbe_display_layer = NULL; + struct video_device *vbd = NULL; + + /* Allocate memory for four plane display objects */ + + disp_dev->dev[i] = + kzalloc(sizeof(struct vpbe_layer), GFP_KERNEL); + + /* If memory allocation fails, return error */ + if (!disp_dev->dev[i]) { + printk(KERN_ERR "ran out of memory\n"); + return -ENOMEM; + } + spin_lock_init(&disp_dev->dev[i]->irqlock); + mutex_init(&disp_dev->dev[i]->opslock); + + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[i]; + vbd = &vpbe_display_layer->video_dev; + /* Initialize field of video device */ + vbd->release = video_device_release_empty; + vbd->fops = &vpbe_fops; + vbd->ioctl_ops = &vpbe_ioctl_ops; + vbd->minor = -1; + vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev; + vbd->lock = &vpbe_display_layer->opslock; + + if (disp_dev->vpbe_dev->current_timings.timings_type & + VPBE_ENC_STD) { + vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); + vbd->current_norm = + disp_dev->vpbe_dev-> + current_timings.timings.std_id; + } else + vbd->current_norm = 0; + + snprintf(vbd->name, sizeof(vbd->name), + "DaVinci_VPBE Display_DRIVER_V%d.%d.%d", + (VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff, + (VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff, + (VPBE_DISPLAY_VERSION_CODE) & 0xff); + + vpbe_display_layer->device_id = i; + + vpbe_display_layer->layer_info.id = + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); + + /* Initialize prio member of layer object */ + v4l2_prio_init(&vpbe_display_layer->prio); + + return 0; +} + +static __devinit int register_device(struct vpbe_layer *vpbe_display_layer, + struct vpbe_display *disp_dev, + struct platform_device *pdev) { + int err; + + v4l2_info(&disp_dev->vpbe_dev->v4l2_dev, + "Trying to register VPBE display device.\n"); + v4l2_info(&disp_dev->vpbe_dev->v4l2_dev, + "layer=%x,layer->video_dev=%x\n", + (int)vpbe_display_layer, + (int)&vpbe_display_layer->video_dev); + + err = video_register_device(&vpbe_display_layer->video_dev, + VFL_TYPE_GRABBER, + -1); + if (err) + return -ENODEV; + + vpbe_display_layer->disp_dev = disp_dev; + /* set the driver data in platform device */ + platform_set_drvdata(pdev, disp_dev); + video_set_drvdata(&vpbe_display_layer->video_dev, + vpbe_display_layer); + + return 0; +} + + + +/* + * vpbe_display_probe() + * This function creates device entries by register itself to the V4L2 driver + * and initializes fields of each layer objects + */ +static __devinit int vpbe_display_probe(struct platform_device *pdev) +{ + struct vpbe_layer *vpbe_display_layer; + struct vpbe_display *disp_dev; + struct resource *res = NULL; + int k; + int i; + int err; + int irq; + + printk(KERN_DEBUG "vpbe_display_probe\n"); + /* Allocate memory for vpbe_display */ + disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL); + if (!disp_dev) { + printk(KERN_ERR "ran out of memory\n"); + return -ENOMEM; + } + + spin_lock_init(&disp_dev->dma_queue_lock); + /* + * Scan all the platform devices to find the vpbe + * controller device and get the vpbe_dev object + */ + err = bus_for_each_dev(&platform_bus_type, NULL, disp_dev, + vpbe_device_get); + if (err < 0) + return err; + /* Initialize the vpbe display controller */ + if (NULL != disp_dev->vpbe_dev->ops.initialize) { + err = disp_dev->vpbe_dev->ops.initialize(&pdev->dev, + disp_dev->vpbe_dev); + if (err) { + v4l2_err(&disp_dev->vpbe_dev->v4l2_dev, + "Error initing vpbe\n"); + err = -ENOMEM; + goto probe_out; + } + } + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + if (init_vpbe_layer(i, disp_dev, pdev)) { + err = -ENODEV; + goto probe_out; + } + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + v4l2_err(&disp_dev->vpbe_dev->v4l2_dev, + "Unable to get VENC interrupt resource\n"); + err = -ENODEV; + goto probe_out; + } + + irq = res->start; + if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER, + disp_dev)) { + v4l2_err(&disp_dev->vpbe_dev->v4l2_dev, + "Unable to request interrupt\n"); + err = -ENODEV; + goto probe_out; + } + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + if (register_device(disp_dev->dev[i], disp_dev, pdev)) { + err = -ENODEV; + goto probe_out; + } + } + + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); + return 0; + +probe_out: + free_irq(res->start, disp_dev); + for (k = 0; k < VPBE_DISPLAY_MAX_DEVICES; k++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[k]; + /* Unregister video device */ + if (vpbe_display_layer) { + video_unregister_device( + &vpbe_display_layer->video_dev); + kfree(disp_dev->dev[k]); + } + } + kfree(disp_dev); + return err; +} + +/* + * vpbe_display_remove() + * It un-register hardware layer from V4L2 driver + */ +static int vpbe_display_remove(struct platform_device *pdev) +{ + struct vpbe_layer *vpbe_display_layer; + struct vpbe_display *disp_dev = platform_get_drvdata(pdev); + struct vpbe_device *vpbe_dev = disp_dev->vpbe_dev; + struct resource *res; + int i; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_remove\n"); + + /* unregister irq */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + free_irq(res->start, disp_dev); + + /* deinitialize the vpbe display controller */ + if (NULL != vpbe_dev->ops.deinitialize) + vpbe_dev->ops.deinitialize(&pdev->dev, vpbe_dev); + /* un-register device */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[i]; + /* Unregister video device */ + video_unregister_device(&vpbe_display_layer->video_dev); + + } + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + kfree(disp_dev->dev[i]); + disp_dev->dev[i] = NULL; + } + + return 0; +} + +static struct platform_driver vpbe_display_driver = { + .driver = { + .name = VPBE_DISPLAY_DRIVER, + .owner = THIS_MODULE, + .bus = &platform_bus_type, + }, + .probe = vpbe_display_probe, + .remove = __devexit_p(vpbe_display_remove), +}; + +/* + * vpbe_display_init() + * This function registers device and driver to the kernel, requests irq + * handler and allocates memory for layer objects + */ +static __devinit int vpbe_display_init(void) +{ + int err; + + printk(KERN_DEBUG "vpbe_display_init\n"); + + /* Register driver to the kernel */ + err = platform_driver_register(&vpbe_display_driver); + if (0 != err) + return err; + + printk(KERN_DEBUG "vpbe_display_init:" + "VPBE V4L2 Display Driver V1.0 loaded\n"); + return 0; +} + +/* + * vpbe_display_cleanup() + * This function un-registers device and driver to the kernel, frees requested + * irq handler and de-allocates memory allocated for layer objects. + */ +static void vpbe_display_cleanup(void) +{ + printk(KERN_DEBUG "vpbe_display_cleanup\n"); + + /* platform driver unregister */ + platform_driver_unregister(&vpbe_display_driver); +} + +/* Function for module initialization and cleanup */ +module_init(vpbe_display_init); +module_exit(vpbe_display_cleanup); + +MODULE_DESCRIPTION("TI DM644x/DM355/DM365 VPBE Display controller"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); diff --git a/include/media/davinci/vpbe_display.h b/include/media/davinci/vpbe_display.h new file mode 100644 index 0000000..dbf6b37 --- /dev/null +++ b/include/media/davinci/vpbe_display.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed 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 VPBE_DISPLAY_H +#define VPBE_DISPLAY_H + +/* Header files */ +#include +#include +#include +#include +#include +#include + +#define VPBE_DISPLAY_MAX_DEVICES 2 + +enum vpbe_display_device_id { + VPBE_DISPLAY_DEVICE_0, + VPBE_DISPLAY_DEVICE_1 +}; + +#define VPBE_DISPLAY_DRV_NAME "vpbe-display" + +#define VPBE_DISPLAY_MAJOR_RELEASE 1 +#define VPBE_DISPLAY_MINOR_RELEASE 0 +#define VPBE_DISPLAY_BUILD 1 +#define VPBE_DISPLAY_VERSION_CODE ((VPBE_DISPLAY_MAJOR_RELEASE << 16) | \ + (VPBE_DISPLAY_MINOR_RELEASE << 8) | \ + VPBE_DISPLAY_BUILD) + +#define VPBE_DISPLAY_VALID_FIELD(field) ((V4L2_FIELD_NONE == field) || \ + (V4L2_FIELD_ANY == field) || (V4L2_FIELD_INTERLACED == field)) + +/* Exp ratio numerator and denominator constants */ +#define VPBE_DISPLAY_H_EXP_RATIO_N 9 +#define VPBE_DISPLAY_H_EXP_RATIO_D 8 +#define VPBE_DISPLAY_V_EXP_RATIO_N 6 +#define VPBE_DISPLAY_V_EXP_RATIO_D 5 + +/* Zoom multiplication factor */ +#define VPBE_DISPLAY_ZOOM_4X 4 +#define VPBE_DISPLAY_ZOOM_2X 2 + +/* Structures */ +struct display_layer_info { + int enable; + /* Layer ID used by Display Manager */ + enum osd_layer id; + struct osd_layer_config config; + enum osd_zoom_factor h_zoom; + enum osd_zoom_factor v_zoom; + enum osd_h_exp_ratio h_exp; + enum osd_v_exp_ratio v_exp; +}; + +/* vpbe display object structure */ +struct vpbe_layer { + /* number of buffers in fbuffers */ + unsigned int numbuffers; + /* Pointer to the vpbe_display */ + struct vpbe_display *disp_dev; + /* Pointer pointing to current v4l2_buffer */ + struct videobuf_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct videobuf_buffer *next_frm; + /* videobuf specific parameters + * 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; + /* V4l2 specific parameters */ + /* Identifies video device for this layer */ + struct video_device video_dev; + /* 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; + /* Used to store pixel format */ + struct v4l2_pix_format pix_fmt; + enum v4l2_field buf_field; + /* Video layer configuration params */ + struct display_layer_info layer_info; + /* vpbe specific parameters + * enable window for display + */ + unsigned char window_enable; + /* number of open instances of the layer */ + unsigned int usrs; + /* number of users performing IO */ + unsigned int io_usrs; + /* Indicates id of the field which is being displayed */ + unsigned int field_id; + /* Indicates whether streaming started */ + unsigned char started; + /* Identifies device object */ + enum vpbe_display_device_id device_id; + /* facilitation of ioctl ops lock by v4l2*/ + struct mutex opslock; + u8 layer_first_int; +}; + +/* vpbe device structure */ +struct vpbe_display { + /* layer specific parameters */ + /* lock for isr updates to buf layers*/ + spinlock_t dma_queue_lock; + /* C-Plane offset from start of y-plane */ + unsigned int cbcr_ofst; + struct vpbe_layer *dev[VPBE_DISPLAY_MAX_DEVICES]; + struct vpbe_device *vpbe_dev; + struct osd_state *osd_device; +}; + +/* File handle structure */ +struct vpbe_fh { + /* vpbe device structure */ + struct vpbe_display *disp_dev; + /* pointer to layer object for opened device */ + struct vpbe_layer *layer; + /* Indicates whether this file handle is doing IO */ + unsigned char io_allowed; + /* Used to keep track priority of this instance */ + enum v4l2_priority prio; +}; + +struct buf_config_params { + unsigned char min_numbuffers; + unsigned char numbuffers[VPBE_DISPLAY_MAX_DEVICES]; + unsigned int min_bufsize[VPBE_DISPLAY_MAX_DEVICES]; + unsigned int layer_bufsize[VPBE_DISPLAY_MAX_DEVICES]; +}; + +#endif /* VPBE_DISPLAY_H */ diff --git a/include/media/davinci/vpbe_types.h b/include/media/davinci/vpbe_types.h new file mode 100644 index 0000000..727f551 --- /dev/null +++ b/include/media/davinci/vpbe_types.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010 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 _VPBE_TYPES_H +#define _VPBE_TYPES_H + +enum vpbe_version { + VPBE_VERSION_1 = 1, + VPBE_VERSION_2, + VPBE_VERSION_3, +}; + +/* vpbe_timing_type - Timing types used in vpbe device */ +enum vpbe_enc_timings_type { + VPBE_ENC_STD = 0x1, + VPBE_ENC_DV_PRESET = 0x2, + VPBE_ENC_CUSTOM_TIMINGS = 0x4, + /* Used when set timings through FB device interface */ + VPBE_ENC_TIMINGS_INVALID = 0x8, +}; + +union vpbe_timings { + v4l2_std_id std_id; + unsigned int dv_preset; +}; + +/* + * struct vpbe_enc_mode_info + * @name: ptr to name string of the standard, "NTSC", "PAL" etc + * @std: standard or non-standard mode. 1 - standard, 0 - nonstandard + * @interlaced: 1 - interlaced, 0 - non interlaced/progressive + * @xres: x or horizontal resolution of the display + * @yres: y or vertical resolution of the display + * @fps: frame per second + * @left_margin: left margin of the display + * @right_margin: right margin of the display + * @upper_margin: upper margin of the display + * @lower_margin: lower margin of the display + * @hsync_len: h-sync length + * @vsync_len: v-sync length + * @flags: bit field: bit usage is documented below + * + * Description: + * Structure holding timing and resolution information of a standard. + * Used by vpbe_device to set required non-standard timing in the + * venc when lcd controller output is connected to a external encoder. + * A table of timings is maintained in vpbe device to set this in + * venc when external encoder is connected to lcd controller output. + * Encoder may provide a g_dv_timings() API to override these values + * as needed. + * + * Notes + * ------ + * if_type should be used only by encoder manager and encoder. + * flags usage + * b0 (LSB) - hsync polarity, 0 - negative, 1 - positive + * b1 - vsync polarity, 0 - negative, 1 - positive + * b2 - field id polarity, 0 - negative, 1 - positive + */ +struct vpbe_enc_mode_info { + unsigned char *name; + enum vpbe_enc_timings_type timings_type; + union vpbe_timings timings; + unsigned int interlaced; + unsigned int xres; + unsigned int yres; + struct v4l2_fract aspect; + struct v4l2_fract fps; + unsigned int left_margin; + unsigned int right_margin; + unsigned int upper_margin; + unsigned int lower_margin; + unsigned int hsync_len; + unsigned int vsync_len; + unsigned int flags; +}; + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Fri Jun 17 02:03:34 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Fri, 17 Jun 2011 12:33:34 +0530 Subject: [RESEND PATCH v19 0/6] davinci vpbe: dm6446 v4l2 driver In-Reply-To: <1308294096-25743-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Mauro, Can you consider this patch series for a pull? -Manju On Fri, Jun 17, 2011 at 12:31:30, Hadli, Manjunath wrote: > fixed a wrong file inclusion in one of the patches > > Manjunath Hadli (6): > davinci vpbe: V4L2 display driver for DM644X SoC > davinci vpbe: VPBE display driver > davinci vpbe: OSD(On Screen Display) block > davinci vpbe: VENC( Video Encoder) implementation > davinci vpbe: Build infrastructure for VPBE driver > davinci vpbe: Readme text for Dm6446 vpbe > > Documentation/video4linux/README.davinci-vpbe | 93 ++ > drivers/media/video/davinci/Kconfig | 23 + > drivers/media/video/davinci/Makefile | 2 + > drivers/media/video/davinci/vpbe.c | 864 ++++++++++++ > drivers/media/video/davinci/vpbe_display.c | 1860 +++++++++++++++++++++++++ > drivers/media/video/davinci/vpbe_osd.c | 1231 ++++++++++++++++ > drivers/media/video/davinci/vpbe_osd_regs.h | 364 +++++ > drivers/media/video/davinci/vpbe_venc.c | 566 ++++++++ > drivers/media/video/davinci/vpbe_venc_regs.h | 177 +++ > include/media/davinci/vpbe.h | 184 +++ > include/media/davinci/vpbe_display.h | 147 ++ > include/media/davinci/vpbe_osd.h | 394 ++++++ > include/media/davinci/vpbe_types.h | 91 ++ > include/media/davinci/vpbe_venc.h | 45 + > 14 files changed, 6041 insertions(+), 0 deletions(-) create mode 100644 Documentation/video4linux/README.davinci-vpbe > create mode 100644 drivers/media/video/davinci/vpbe.c > create mode 100644 drivers/media/video/davinci/vpbe_display.c > create mode 100644 drivers/media/video/davinci/vpbe_osd.c > create mode 100644 drivers/media/video/davinci/vpbe_osd_regs.h > create mode 100644 drivers/media/video/davinci/vpbe_venc.c > create mode 100644 drivers/media/video/davinci/vpbe_venc_regs.h > create mode 100644 include/media/davinci/vpbe.h create mode 100644 include/media/davinci/vpbe_display.h > create mode 100644 include/media/davinci/vpbe_osd.h create mode 100644 include/media/davinci/vpbe_types.h > create mode 100644 include/media/davinci/vpbe_venc.h > > From nsekhar at ti.com Mon Jun 20 12:46:56 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 20 Jun 2011 23:16:56 +0530 Subject: [PATCH] davinci: da850 EVM: read mac address from SPI flash In-Reply-To: <1307538155-27197-1-git-send-email-sudhakar.raj@ti.com> References: <1307538155-27197-1-git-send-email-sudhakar.raj@ti.com> Message-ID: Hi Sudhakar, On Wed, Jun 08, 2011 at 18:32:35, Rajashekhara, Sudhakar wrote: > DA850/OMAP-L138 EMAC driver uses random mac address instead of > a fixed one because the mac address is not stuffed into EMAC > platform data. > > This patch provides a function which reads the mac address > stored in SPI flash (registered as MTD device) and populates the > EMAC platform data. The function which reads the mac address is > registered as a callback which gets called upon addition of MTD > device. > > NOTE: In case the MAC address stored in SPI flash is erased, follow > the instructions at [1] to restore it. > > [1] http://processors.wiki.ti.com/index.php/GSG:_OMAP-L138_DVEVM_Additional_Procedures#Restoring_MAC_address_on_SPI_Flash > > Signed-off-by: Rajashekhara, Sudhakar > --- > arch/arm/mach-davinci/board-da850-evm.c | 20 ++++++++++++++++++++ > 1 files changed, 20 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index a7b41bf..3ff64b8 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -115,6 +115,23 @@ static struct spi_board_info da850evm_spi_info[] = { > }, > }; > > +static void da850_evm_m25p80_notify_add(struct mtd_info *mtd) > +{ > + char *mac_addr = davinci_soc_info.emac_pdata->mac_addr; > + size_t retlen; > + > + if (!strcmp(mtd->name, "MAC-Address")) { > + mtd->read(mtd, 0, ETH_ALEN, &retlen, mac_addr); > + if (retlen == ETH_ALEN) > + pr_info("Read MAC addr from SPI Flash: %pM\n", > + mac_addr); > + } > +} > + > +static struct mtd_notifier da850evm_spi_notifier = { > + .add = da850_evm_m25p80_notify_add, > +}; > + > static struct mtd_partition da850_evm_norflash_partition[] = { > { > .name = "bootloaders + env", > @@ -1237,6 +1254,9 @@ static __init void da850_evm_init(void) > if (ret) > pr_warning("da850_evm_init: spi 1 registration failed: %d\n", > ret); > + > + if (mtd_has_partitions()) > + register_mtd_user(&da850evm_spi_notifier); The v3.0 merge removes mtd_has_partitions() so this fails to build. Can you please fix and re-submit? Thanks, Sekhar From arnd at arndb.de Tue Jun 21 09:20:01 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Tue, 21 Jun 2011 16:20:01 +0200 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: <1308640714-17961-1-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> Message-ID: <201106211620.02065.arnd@arndb.de> Hi Ohad, On Tuesday 21 June 2011, Ohad Ben-Cohen wrote: > This patch set adds a generic AMP/IPC framework which makes it possible to > control (power on, boot, power off) and communicate (simply send and receive > messages) with those remote processors. This looks really nice overall, but I don't currently have time for a more in-depth review. My feeling is that you are definitely on the right track here, and the plans you list as TODO to continue are all good. One point I noticed is the use of debugfs, which you should probably replace at some point with a stable API, e.g. your own debugfs-like file system, but there is no hurry to do that now. > The gory part of remoteproc is the firmware handling. We tried to come up > with a simple binary format that will require minimum kernel code to handle, > but at the same time be generic enough in the hopes that it would prove > useful to others as well. We're not at all hang onto the binary format > we picked: if there's any technical reason to change it to support other > platforms, please let us know. We do realize that a single binary firmware > structure might eventually not work for everyone. it did prove useful for > us though; we adopted both the OMAP and Davinci platforms (and their > completely different remote processor devices) to this simple binary > structure, so we don't have to duplicate the firmware handling code. Regarding the firmware, I think it would be nice to have the option to stick the firmware blob into the flattened device tree as a bininclude, so you can build systems using the external processors without special user space support. The binary format looks reasonable, but if you need anything more complex, you should probably go straight for ELF files ;-) The structures you use like +struct fw_section { + u32 type; + u64 da; + u32 len; + char content[0]; +} __packed; Unfortunately require __packed. It would be better to sort the members differently so that each member is naturally aligned in order to avoid the need for padding or __packed attributes, like: struct fw_section { u32 type; u32 len; u64 da; char content[0]; }; Arnd From grant.likely at secretlab.ca Tue Jun 21 10:54:35 2011 From: grant.likely at secretlab.ca (Grant Likely) Date: Tue, 21 Jun 2011 09:54:35 -0600 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: <201106211620.02065.arnd@arndb.de> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <201106211620.02065.arnd@arndb.de> Message-ID: On Tue, Jun 21, 2011 at 8:20 AM, Arnd Bergmann wrote: > Hi Ohad, > > On Tuesday 21 June 2011, Ohad Ben-Cohen wrote: > >> This patch set adds a generic AMP/IPC framework which makes it possible to >> control (power on, boot, power off) and communicate (simply send and receive >> messages) with those remote processors. > > This looks really nice overall, but I don't currently have time for a > more in-depth review. My feeling is that you are definitely on the right > track here, and the plans you list as TODO to continue are all good. Second that. I'm happy to see the shift over to virtio, I think that is the right direction overall. I'll try to carve out some time for a detailed review. g. From mchehab at redhat.com Tue Jun 21 14:24:01 2011 From: mchehab at redhat.com (Mauro Carvalho Chehab) Date: Tue, 21 Jun 2011 16:24:01 -0300 Subject: [RESEND PATCH v19 0/6] davinci vpbe: dm6446 v4l2 driver In-Reply-To: References: Message-ID: <4E00EFD1.40307@redhat.com> Em 17-06-2011 04:03, Hadli, Manjunath escreveu: > Mauro, > > Can you consider this patch series for a pull? Next time, could you please add on your tree and send me a git pull request? Patchwork is currently not reliable. I have a backup process, but a git pull request works better and I won't have the risk of applying the wrong patches or at a wrong order. In this specific case, as all patches were caught by patchwork, I'll apply from your emails after reviewing them. Thanks, Mauro > > -Manju > > On Fri, Jun 17, 2011 at 12:31:30, Hadli, Manjunath wrote: >> fixed a wrong file inclusion in one of the patches >> >> Manjunath Hadli (6): >> davinci vpbe: V4L2 display driver for DM644X SoC >> davinci vpbe: VPBE display driver >> davinci vpbe: OSD(On Screen Display) block >> davinci vpbe: VENC( Video Encoder) implementation >> davinci vpbe: Build infrastructure for VPBE driver >> davinci vpbe: Readme text for Dm6446 vpbe >> >> Documentation/video4linux/README.davinci-vpbe | 93 ++ >> drivers/media/video/davinci/Kconfig | 23 + >> drivers/media/video/davinci/Makefile | 2 + >> drivers/media/video/davinci/vpbe.c | 864 ++++++++++++ >> drivers/media/video/davinci/vpbe_display.c | 1860 +++++++++++++++++++++++++ >> drivers/media/video/davinci/vpbe_osd.c | 1231 ++++++++++++++++ >> drivers/media/video/davinci/vpbe_osd_regs.h | 364 +++++ >> drivers/media/video/davinci/vpbe_venc.c | 566 ++++++++ >> drivers/media/video/davinci/vpbe_venc_regs.h | 177 +++ >> include/media/davinci/vpbe.h | 184 +++ >> include/media/davinci/vpbe_display.h | 147 ++ >> include/media/davinci/vpbe_osd.h | 394 ++++++ >> include/media/davinci/vpbe_types.h | 91 ++ >> include/media/davinci/vpbe_venc.h | 45 + >> 14 files changed, 6041 insertions(+), 0 deletions(-) create mode 100644 Documentation/video4linux/README.davinci-vpbe >> create mode 100644 drivers/media/video/davinci/vpbe.c >> create mode 100644 drivers/media/video/davinci/vpbe_display.c >> create mode 100644 drivers/media/video/davinci/vpbe_osd.c >> create mode 100644 drivers/media/video/davinci/vpbe_osd_regs.h >> create mode 100644 drivers/media/video/davinci/vpbe_venc.c >> create mode 100644 drivers/media/video/davinci/vpbe_venc_regs.h >> create mode 100644 include/media/davinci/vpbe.h create mode 100644 include/media/davinci/vpbe_display.h >> create mode 100644 include/media/davinci/vpbe_osd.h create mode 100644 include/media/davinci/vpbe_types.h >> create mode 100644 include/media/davinci/vpbe_venc.h >> >> > > -- > To unsubscribe from this list: send the line "unsubscribe linux-media" in > the body of a message to majordomo at vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html From nsekhar at ti.com Tue Jun 21 23:05:58 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 22 Jun 2011 09:35:58 +0530 Subject: [PATCH 1/1] davinci: dm646x: move vpif related code to driver core header from platform In-Reply-To: References: <1305899929-2509-1-git-send-email-manjunath.hadli@ti.com> Message-ID: On Thu, Jun 02, 2011 at 22:51:58, Nori, Sekhar wrote: > Hi Mauro, > > On Fri, May 20, 2011 at 19:28:49, Hadli, Manjunath wrote: > > move vpif related code for capture and display drivers > > from dm646x platform header file to vpif.h as these definitions > > are related to driver code more than the platform or board. > > > > Signed-off-by: Manjunath Hadli > > Will you be taking this patch through your tree? > > If not, with your ack, I can queue it for inclusion > through the ARM tree. > Ping :) Thanks, Sekhar > > --- > > 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.h | 73 +++++++++++++++++++++++++++ > > 5 files changed, 77 insertions(+), 53 deletions(-) > > create mode 100644 include/media/davinci/vpif.h From sudhakar.raj at ti.com Tue Jun 21 23:13:54 2011 From: sudhakar.raj at ti.com (Rajashekhara, Sudhakar) Date: Wed, 22 Jun 2011 09:43:54 +0530 Subject: [PATCH] davinci: da850 EVM: read mac address from SPI flash In-Reply-To: References: <1307538155-27197-1-git-send-email-sudhakar.raj@ti.com> Message-ID: Hi, On Mon, Jun 20, 2011 at 23:16:56, Nori, Sekhar wrote: > Hi Sudhakar, > > On Wed, Jun 08, 2011 at 18:32:35, Rajashekhara, Sudhakar wrote: > > DA850/OMAP-L138 EMAC driver uses random mac address instead of a fixed > > one because the mac address is not stuffed into EMAC platform data. > > > > This patch provides a function which reads the mac address stored in > > SPI flash (registered as MTD device) and populates the EMAC platform > > data. The function which reads the mac address is registered as a > > callback which gets called upon addition of MTD device. > > > > NOTE: In case the MAC address stored in SPI flash is erased, follow > > the instructions at [1] to restore it. > > > > [1] > > http://processors.wiki.ti.com/index.php/GSG:_OMAP-L138_DVEVM_Additiona > > l_Procedures#Restoring_MAC_address_on_SPI_Flash > > > > Signed-off-by: Rajashekhara, Sudhakar > > --- [...] > > + > > + if (mtd_has_partitions()) > > + register_mtd_user(&da850evm_spi_notifier); > > The v3.0 merge removes mtd_has_partitions() so this fails to build. Can you please fix and re-submit? > I'll rework on this patch and submit it again. Thanks, Sudhakar From arnd at arndb.de Wed Jun 22 08:05:32 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 22 Jun 2011 15:05:32 +0200 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <201106211620.02065.arnd@arndb.de> Message-ID: <201106221505.32531.arnd@arndb.de> On Wednesday 22 June 2011, Ohad Ben-Cohen wrote: > > One point I noticed is the use of debugfs, which you should probably > > replace at some point with a stable API, e.g. your own debugfs-like > > file system, but there is no hurry to do that now. > > debugfs is only being used to expose debugging info (namely the power > state of the remote processor and its trace log messages), which is > mostly useful for developers trying to understand what went wrong. > > It seems like debugfs fits quite nicely here (e.g. it's perfectly fine > if this is completely compiled out on production systems), but sure, > we can always revisit this later too. Ok, I see. In that case I agree that using debugfs is fine, but I would recommend trying to use fewer macros and just open-coding the file operations for better readability. > > Unfortunately require __packed. It would be better to sort the members > > differently so that each member is naturally aligned in order to > > avoid the need for padding or __packed attributes > > Definitely. __packed is being used just to be on the safe side; we > didn't intend to introduce unnatural alignment intentionally. will be > fixed. Ok. Arnd From vinod.koul at intel.com Thu Jun 16 00:45:19 2011 From: vinod.koul at intel.com (Koul, Vinod) Date: Thu, 16 Jun 2011 11:15:19 +0530 Subject: [RFC] dmaengine: add new api for preparing simple slave transfer In-Reply-To: References: <20110609124723.GA24636@n2100.arm.linux.org.uk> <1307686140.10976.111.camel@vkoul-udesk3> <1307724439.10976.188.camel@vkoul-udesk3> Message-ID: <1308203119.10976.234.camel@vkoul-udesk3> On Tue, 2011-06-14 at 12:12 +0530, Raju, Sundaram wrote: > > > > > The overall conclusion which I'm coming to is that we already support > > what you're asking for, but the problem is that we're using different > > (and I'd argue standard) terminology to describe what we have. > > > > The only issue which I see that we don't cover is the case where you want > > to describe a single buffer which is organised as N bytes to be transferred, > > M following bytes to be skipped, N bytes to be transferred, M bytes to be > > skipped. I doubt there are many controllers which can be programmed with > > both 'N' and 'M' parameters directly. > > > > Yes this is what I wanted to communicate and discuss in the list. > Thanks for describing it in the standard terminology, and pointing me in the > right direction. > > Also please note that if the gap between the buffers in a scatterlist is > uniform and that can again be skipped by the DMAC automatically > by programming that gap size, then that feature also is not available > in the current framework. > > I understand it can be done with the existing scatterlist, but just writing > a value in a DMAC register is much better if available, than preparing > a scatterlist and passing to the API. > > > > > Please, don't generate special cases. Design proper APIs from the > > outset rather than abusing what's already there. So no, don't abuse > > the address width stuff. > > > > In any case, the address width stuff must still be used to describe the > > peripherals FIFO register. > > > > I did not intend to abuse the existing address width. It might look > like that because of how I described it here. > I agree that the dma_slave_config is for peripheral related > properties to be stored. I was pointing out that the chunk size > variable in the dma_buffer_config I proposed will be in most cases > equal to FIFO register width, to describe what I actually meant by > chunk size. > > > IIUC, you have a buffer with gaps in between (given by above params). > > Is your DMA h/w capable of parsing this buffer and directly do a > > transfer based on above parameters (without any work for SW), or you are > > doing this in your dma driver and then programming a list of buffers? > > In former case (although i haven't seen such dma controller yet), can > > you share the datasheet? It would make very little sense to change the > > current API for your extra special case. This special case needs to be > > handled differently rather than making rule out of it!! > > > > Yes, Vinod. This is directly handled in the DMAC h/w. > > This capability is present in 2 TI DMACs. > EDMA (Enhanced DMA) in all TI DaVinci SoCs and the > SDMA (System DMA) in all TI OMAP SoCs. The drivers of these > controllers are present in the respective DaVinci tree and OMAP tree > under the SoC folders. > > > > SDMA and EDMA are TI SoC specific DMA controllers. Their drivers have > > been maintained in the respective SoC folders till now. > > arch/arm/plat-omap/dma.c > > arch/arm/mach-davinci/dma.c > > > The Manual of the EDMA controller in TI DaVinci SoCs is available at > http://www.ti.com/litv/pdf/sprue23d > Section 2.2 in the page 23 explains how transfers are made based > on the gaps programmed. It also explains how the 3D buffer is > internally split in EDMA based on the gaps programmed. > > The complete Reference manual of TI OMAP SoCs is available at > http://www.ti.com/litv/pdf/spruf98s > Chapter 9 in this document describes the SDMA controller. > Section 9.4.3 in page 981 explains the various address modes, > how the address is incremented by the DMAC and about the gaps > in between buffers and frames and how the DMAC handles them. > > Linus, > > > > Is it really so bad? It is a custom configuration after all. Even if > > there were many DMACs out there supporting it, we'd probably > > model it like this, just pass something like DMA_STRIDE_CONFIG > > instead of something named after a specific slave controller. > > > > This way DMACs that didn't support striding could NACK a > > transfer for device drivers requesting it and then it could figure > > out what to do. > > > > If we can get some indication as to whether more DMACs can > > do this kind of stuff, we'd maybe like to introduce > > DMA_STRIDE_CONFIG already now. > > > > I wanted to discuss this feature in the list and get this feature > added to the current dmaengine framework. If the current APIs > can handle this feature, then its very good > and I will follow that only. > > If what you suggest is the right way to go then I am okay. > IMHO the framework should always handle the complex case > and individual implementations should implement a subset of > the capability and hence I suggest the changes I posted to the list. Okay now things are more clear on what you are trying to do... 1) The changes you need are to describe your buffer and convey to dmac so please don't talk about slave here as that is specific to dmac config 2) I think this can be achieved in two ways: a) you use current standard sg_list mechanism, the dmac driver parses the list and programs the dma offsets into dmac Pros: you can use existing APIs, no changes to i/f. If dmac has this capability they program dmac accordingly Cons: you need to create sg-list in client driver b) create a new api to describe these offset values, something like: prep_buffer_offset(struct offset_description *buffer,.....) I would not like to change the current API for this and rather have a new API for this, this should better then overriding current. Overall I would prefer you to use approach 2-a above -- ~Vinod From ohad at wizery.com Tue Jun 21 02:18:26 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Tue, 21 Jun 2011 10:18:26 +0300 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework Message-ID: <1308640714-17961-1-git-send-email-ohad@wizery.com> Modern SoCs typically employ a central symmetric multiprocessing (SMP) application processor running Linux, with several other asymmetric multiprocessing (AMP) heterogeneous processors running different instances of operating system, whether Linux or any other flavor of real-time OS. OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP. Typically, the dual cortex-A9 is running Linux in a SMP configuration, and each of the other three cores (two M3 cores and a DSP) is running its own instance of RTOS in an AMP configuration. AMP remote processors typically employ dedicated DSP codecs and multimedia hardware accelerators, and therefore are often used to offload cpu-intensive multimedia tasks from the main application processor. They could also be used to control latency-sensitive sensors, drive "random" hardware blocks, or just perform background tasks while the main CPU is idling. Users of those remote processors can either be userland apps (e.g. multimedia frameworks talking with remote OMX components) or kernel drivers (controlling hardware accessible only by the remote processor, reserving kernel-controlled resources on behalf of the remote processor, etc..). This patch set adds a generic AMP/IPC framework which makes it possible to control (power on, boot, power off) and communicate (simply send and receive messages) with those remote processors. Specifically, we're adding: * Rpmsg: a virtio-based messaging bus that allows kernel drivers to communicate with remote processors available on the system. In turn, drivers could then expose appropriate user space interfaces, if needed (tasks running on remote processors often have direct access to sensitive resources like the system's physical memory, gpios, i2c buses, dma controllers, etc.. so one normally wouldn't want to allow userland to send everything/everywhere it wants). Every rpmsg device is a communication channel with a service running on a remote processor (thus rpmsg devices are called channels). Channels are identified by a textual name (which is used to match drivers to devices) and have a local ("source") rpmsg address, and remote ("destination") rpmsg address. When a driver starts listening on a channel (most commonly when it is probed), the bus assigns the driver a unique rpmsg src address (a 32 bit integer) and binds it with the driver's rx callback handler. This way when inbound messages arrive to this src address, the rpmsg core dispatches them to that driver, by invoking the driver's rx handler with the payload of the incoming message. Once probed, rpmsg drivers can also immediately start sending messages to the remote rpmsg service by using simple sending API; no need even to specify a destination address, since that's part of the rpmsg channel, and the rpmsg bus uses the channel's dst address when it constructs the message (for more demanding use cases, there's also an extended API, which does allow full control of both the src and dst addresses). The rpmsg bus is using virtio to send and receive messages: every pair of processors share two vrings, which are used to send and receive the messages over shared memory (one vring is used for tx, and the other one for rx). Kicking the remote processor (i.e. letting it know it has a pending message on its vring) is accomplished by means available on the platform we run on (e.g. OMAP is using its mailbox to both interrupt the remote processor and tell it which vring is kicked at the same time). The header of every message sent on the rpmsg bus contains src and dst addresses, which make it possible to multiplex several rpmsg channels on the same vring. One nice property of the rpmsg bus is that device creation is completely dynamic: remote processors can announce the existence of remote rpmsg services by sending a "name service" messages (which contain the name and rpmsg addr of the remote service). Those messages are picked by the rpmsg bus, which in turn dynamically creates and registers the rpmsg channels (i.e devices) which represents the remote services. If/when a relevant rpmsg driver is registered, it will be immediately probed by the bus, and can then start "talking" to the remote service. Similarly, we can use this technique to dynamically create virtio devices (and new vrings) which would then represent e.g. remote network, console and block devices that will be driven by the existing virtio drivers (this is still not implemented though; it requires some RTOS work as we're not booting Linux on OMAP's remote processors). Creating new vrings might also be desired by users who just don't want to use the shared rpmsg vrings (for performance or any other functionality reasons). In addition to dynamic creation of rpmsg channels, the rpmsg bus also supports creation of static channels. This is needed in two cases: - when a certain remote processor doesn't support sending those "name service" announcements. In that case, a static table of remote rpmsg services must be used to create the rpmsg channels. - to support rpmsg server drivers, which aren't bound to a specific remote rpmsg address. Instead, they just listen on a local address, waiting for incoming messages. To send a message, those server drivers need to use the rpmsg_sendto() API, so they can explicitly indicate the dst address every time. There are already several immediate use cases for rpmsg drivers: OMX offloading (already being used on OMAP4), hardware resource manager (remote processors on OMAP4 need to ask Linux to enable/disable hardware resources on its behalf), remote display driver on Netra (dm8168), where the display is controlled by a remote M3 processor (and a Linux v4l2/fbdev driver will use rpmsg to communicate with that remote display driver). * Remoteproc: a generic driver that maintains the state of the remote processor(s). Simple rproc_get() and rproc_put() API is exposed, which drivers can use when needed (first driver to call get() will load a firmware, configure an iommu if needed, and boot the remote processor, while last driver to call put() will power it down). Hardware differences are abstracted as usual: a platform-specific driver registers its own start/stop handlers in the generic remoteproc driver, and those are invoked when its time to power up/down the processor. As a reference, this patch set include remoteproc support for both OMAP4's cortex-M3 and Davinci's DSP, tested on the pandaboard and hawkboard, respectively. The gory part of remoteproc is the firmware handling. We tried to come up with a simple binary format that will require minimum kernel code to handle, but at the same time be generic enough in the hopes that it would prove useful to others as well. We're not at all hang onto the binary format we picked: if there's any technical reason to change it to support other platforms, please let us know. We do realize that a single binary firmware structure might eventually not work for everyone. it did prove useful for us though; we adopted both the OMAP and Davinci platforms (and their completely different remote processor devices) to this simple binary structure, so we don't have to duplicate the firmware handling code. It's also not entirely clear whether remoteproc should really be an independent layer, or if it should just be squashed with the host-specific component of the rpmsg framework (there isn't really a remoteproc use case that doesn't need rpmsg anyway). Looking ahead, functionality like error recovery and power management might require coupling those two together as well. Important stuff: * Thanks Brian Swetland for great design ideas and fruitful meetings and Arnd Bergmann for pointing us at virtio, which is just awesome. * Thanks Bhavin Shah, Mark Grosen, Suman Anna, Fernando Guzman Lugo, Shreyas Prasad, Gilbert Pitney, Armando Uribe De Leon, Robert Tivy and Alan DeMars for all your help. You know what you did. * Patches are based on 3.0-rc3. Code was tested on OMAP4 using a panda board (and remoteproc was also tested on Davinci da850-evm and hawkboard). * I will be replying to this email with an attachment of the M3 firmware image I work with, so anyone with a panda board can take the code for a ride (put the image in /lib/firmware, and tell me if stuff doesn't work for you: there are a few omap iommu fixes that are circulating but not merged yet). * Licensing: definitions that needs to be shared with remote processors were put in BSD-licensed header files, so anyone can use them to develop compatible peers. * The M3 RTOS source code itself is BSD licensed and will be publicly released too (it's in the works). * This work is not complete; there are still several things to implement, improve and clean up, but the intention is to start the review, and not wait until everything is finished. Guzman Lugo, Fernando (2): remoteproc: add omap implementation omap: add remoteproc devices Mark Grosen (2): remoteproc: add davinci implementation davinci: da850: add remoteproc dsp device Ohad Ben-Cohen (4): drivers: add generic remoteproc framework omap: add carveout memory support for remoteproc drivers: introduce rpmsg, a remote-processor messaging bus rpmsg: add omap host backend Documentation/ABI/testing/sysfs-bus-rpmsg | 75 ++ Documentation/remoteproc.txt | 170 ++++ Documentation/rpmsg.txt | 308 +++++++ arch/arm/mach-davinci/board-da850-evm.c | 4 + arch/arm/mach-davinci/board-omapl138-hawk.c | 4 + arch/arm/mach-davinci/da850.c | 14 + arch/arm/mach-davinci/devices-da8xx.c | 15 + arch/arm/mach-davinci/include/mach/da8xx.h | 1 + arch/arm/mach-davinci/include/mach/remoteproc.h | 28 + arch/arm/mach-omap2/Makefile | 2 + arch/arm/mach-omap2/remoteproc.c | 159 ++++ arch/arm/plat-omap/Kconfig | 8 + arch/arm/plat-omap/devices.c | 14 +- arch/arm/plat-omap/include/plat/dsp.h | 6 +- arch/arm/plat-omap/include/plat/remoteproc.h | 41 + drivers/Kconfig | 3 + drivers/Makefile | 2 + drivers/remoteproc/Kconfig | 44 + drivers/remoteproc/Makefile | 7 + drivers/remoteproc/davinci_remoteproc.c | 147 ++++ drivers/remoteproc/omap_remoteproc.c | 243 ++++++ drivers/remoteproc/remoteproc.c | 780 +++++++++++++++++ drivers/rpmsg/Kconfig | 16 + drivers/rpmsg/Makefile | 3 + drivers/rpmsg/host/Kconfig | 11 + drivers/rpmsg/host/Makefile | 1 + drivers/rpmsg/host/omap_rpmsg.c | 540 ++++++++++++ drivers/rpmsg/host/omap_rpmsg.h | 69 ++ drivers/rpmsg/virtio_rpmsg_bus.c | 1036 +++++++++++++++++++++++ include/linux/mod_devicetable.h | 10 + include/linux/remoteproc.h | 273 ++++++ include/linux/rpmsg.h | 421 +++++++++ include/linux/virtio_ids.h | 1 + 33 files changed, 4453 insertions(+), 3 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-rpmsg create mode 100644 Documentation/remoteproc.txt create mode 100644 Documentation/rpmsg.txt create mode 100644 arch/arm/mach-davinci/include/mach/remoteproc.h create mode 100644 arch/arm/mach-omap2/remoteproc.c create mode 100644 arch/arm/plat-omap/include/plat/remoteproc.h create mode 100644 drivers/remoteproc/Kconfig create mode 100644 drivers/remoteproc/Makefile create mode 100644 drivers/remoteproc/davinci_remoteproc.c create mode 100644 drivers/remoteproc/omap_remoteproc.c create mode 100644 drivers/remoteproc/remoteproc.c create mode 100644 drivers/rpmsg/Kconfig create mode 100644 drivers/rpmsg/Makefile create mode 100644 drivers/rpmsg/host/Kconfig create mode 100644 drivers/rpmsg/host/Makefile create mode 100644 drivers/rpmsg/host/omap_rpmsg.c create mode 100644 drivers/rpmsg/host/omap_rpmsg.h create mode 100644 drivers/rpmsg/virtio_rpmsg_bus.c create mode 100644 include/linux/remoteproc.h create mode 100644 include/linux/rpmsg.h From ohad at wizery.com Tue Jun 21 02:18:27 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Tue, 21 Jun 2011 10:18:27 +0300 Subject: [RFC 1/8] drivers: add generic remoteproc framework In-Reply-To: <1308640714-17961-1-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> Message-ID: <1308640714-17961-2-git-send-email-ohad@wizery.com> Some systems have slave heterogeneous remote processor devices, that are usually used to offload cpu-intensive computations (e.g. multimedia codec tasks). Booting a remote processor typically involves: - Loading a firmware which contains the OS image (mainly text and data) - If needed, programming an IOMMU - Powering on the device This patch introduces a generic remoteproc framework that allows drivers to start and stop those remote processor devices, load up their firmware (which might not necessarily be Linux-based), and in the future also support power management and error recovery. It's still not clear how much this is really reusable for other platforms/architectures, especially the part that deals with the firmware. Moreover, it's not entirely clear whether this should really be an independent layer, or if it should just be squashed with the host-specific component of the rpmsg framework (there isn't really a remoteproc use case that doesn't require rpmsg). That said, it did prove useful for us on two completely different platforms: OMAP and Davinci, each with its different remote processor (Cortex-M3 and a C674x DSP, respectively). So to avoid egregious duplication of code, remoteproc must not be omap-only. Firmware loader is based on code by Mark Grosen . TODO: - drop rproc_da_to_pa(), use iommu_iova_to_phys() instead (requires completion of omap's iommu migration and some generic iommu API work) - instead of ioremapping reserved memory and handling IOMMUs, consider moving to the generic DMA mapping API (with a CMA backend) Signed-off-by: Ohad Ben-Cohen --- Documentation/remoteproc.txt | 170 +++++++++ drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/remoteproc/Kconfig | 7 + drivers/remoteproc/Makefile | 5 + drivers/remoteproc/remoteproc.c | 780 +++++++++++++++++++++++++++++++++++++++ include/linux/remoteproc.h | 273 ++++++++++++++ 7 files changed, 1238 insertions(+), 0 deletions(-) create mode 100644 Documentation/remoteproc.txt create mode 100644 drivers/remoteproc/Kconfig create mode 100644 drivers/remoteproc/Makefile create mode 100644 drivers/remoteproc/remoteproc.c create mode 100644 include/linux/remoteproc.h diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt new file mode 100644 index 0000000..3075813 --- /dev/null +++ b/Documentation/remoteproc.txt @@ -0,0 +1,170 @@ +Remote Processor Framework + +1. Introduction + +Modern SoCs typically have heterogeneous remote processor devices in asymmetric +multiprocessing (AMP) configurations, which may be running different instances +of operating system, whether it's Linux or any other flavor of real-time OS. + +OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP. +In a typical configuration, the dual cortex-A9 is running Linux in a SMP +configuration, and each of the other three cores (two M3 cores and a DSP) +is running its own instance of RTOS in an AMP configuration. + +The generic remoteproc driver allows different platforms/architectures to +control (power on, load firmware, power off) those remote processors while +abstracting the hardware differences, so the entire driver doesn't need to be +duplicated. + +2. User API + + struct rproc *rproc_get(const char *name); + - power up the remote processor, identified by the 'name' argument, + and boot it. If the remote processor is already powered on, the + function immediately succeeds. + On success, returns the rproc handle. On failure, NULL is returned. + + void rproc_put(struct rproc *rproc); + - power off the remote processor, identified by the rproc handle. + Every call to rproc_get() must be (eventually) accompanied by a call + to rproc_put(). Calling rproc_put() redundantly is a bug. + Note: the remote processor will actually be powered off only when the + last user calls rproc_put(). + +3. Typical usage + +#include + +int dummy_rproc_example(void) +{ + struct rproc *my_rproc; + + /* let's power on and boot the image processing unit */ + my_rproc = rproc_get("ipu"); + if (!my_rproc) { + /* + * something went wrong. handle it and leave. + */ + } + + /* + * the 'ipu' remote processor is now powered on... let it work ! + */ + + /* if we no longer need ipu's services, power it down */ + rproc_put(my_rproc); +} + +4. API for implementors + + int rproc_register(struct device *dev, const char *name, + const struct rproc_ops *ops, + const char *firmware, + const struct rproc_mem_entry *memory_maps, + struct module *owner); + - should be called from the underlying platform-specific implementation, in + order to register a new remoteproc device. 'dev' is the underlying + device, 'name' is the name of the remote processor, which will be + specified by users calling rproc_get(), 'ops' is the platform-specific + start/stop handlers, 'firmware' is the name of the firmware file to + boot the processor with, 'memory_maps' is a table of da<->pa memory + mappings which should be used to configure the IOMMU (if not relevant, + just pass NULL here), 'owner' is the underlying module that should + not be removed while the remote processor is in use. + + Returns 0 on success, or an appropriate error code on failure. + + int rproc_unregister(const char *name); + - should be called from the underlying platform-specific implementation, in + order to unregister a remoteproc device that was previously registered + with rproc_register(). + +5. Implementation callbacks + +Every remoteproc implementation must provide these handlers: + +struct rproc_ops { + int (*start)(struct rproc *rproc, u64 bootaddr); + int (*stop)(struct rproc *rproc); +}; + +The ->start() handler takes a rproc handle and an optional bootaddr argument, +and should power on the device and boot it (using the bootaddr argument +if the hardware requires one). +On success, 0 is returned, and on failure, an appropriate error code. + +The ->stop() handler takes a rproc handle and powers the device off. +On success, 0 is returned, and on failure, an appropriate error code. + +6. Binary Firmware Structure + +The following enums and structures define the binary format of the images +remoteproc loads and boot the remote processors with. + +The general binary format is as follows: + +struct { + char magic[4] = { 'R', 'P', 'R', 'C' }; + u32 version; + u32 header_len; + char header[...] = { header_len bytes of unformatted, textual header }; + struct section { + u32 type; + u64 da; + u32 len; + u8 content[...] = { len bytes of binary data }; + } [ no limit on number of sections ]; +} __packed; + +The image begins with a 4-bytes "RPRC" magic, a version number, and a +free-style textual header that users can easily read. + +After the header, the firmware contains several sections that should be +loaded to memory so the remote processor can access them. + +Every section begins with its type, device address (da) where the remote +processor expects to find this section at (exact meaning depends whether +the device accesses memory through an IOMMU or not. if not, da might just +be physical addresses), the section length and its content. + +Most of the sections are either text or data (which currently are treated +exactly the same), but there is one special "resource" section that allows +the remote processor to announce/request certain resources from the host. + +A resource section is just a packed array of the following struct: + +struct fw_resource { + u32 type; + u64 da; + u64 pa; + u32 len; + u32 flags; + u8 name[48]; +} __packed; + +The way a resource is really handled strongly depends on its type. +Some resources are just one-way announcements, e.g., a RSC_TRACE type means +that the remote processor will be writing log messages into a trace buffer +which is located at the address specified in 'da'. In that case, 'len' is +the size of that buffer. A RSC_BOOTADDR resource type announces the boot +address (i.e. the first instruction the remote processor should be booted with) +in 'da'. + +Other resources entries might be a two-way request/respond negotiation where +a certain resource (memory or any other hardware resource) is requested +by specifying the appropriate type and name. The host should then allocate +such a resource and "reply" by writing the identifier (physical address +or any other device id that will be meaningful to the remote processor) +back into the relevant member of the resource structure. Obviously this +approach can only be used _before_ booting the remote processor. After +the remote processor is powered up, the resource section is expected +to stay static. Runtime resource management (i.e. handling requests after +the remote processor has booted) will be achieved using a dedicated rpmsg +driver. + +The latter two-way approach is still preliminary and has not been implemented +yet. It's left to see how this all works out. + +Most likely this kind of static allocations of hardware resources for +remote processors can also use DT, so it's interesting to see how +this all work out when DT materializes. diff --git a/drivers/Kconfig b/drivers/Kconfig index 3bb154d..1f6d6d3 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -126,4 +126,6 @@ source "drivers/hwspinlock/Kconfig" source "drivers/clocksource/Kconfig" +source "drivers/remoteproc/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 09f3232..4d53a18 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -122,3 +122,4 @@ obj-y += ieee802154/ obj-y += clk/ obj-$(CONFIG_HWSPINLOCK) += hwspinlock/ +obj-$(CONFIG_REMOTE_PROC) += remoteproc/ diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig new file mode 100644 index 0000000..a60bb12 --- /dev/null +++ b/drivers/remoteproc/Kconfig @@ -0,0 +1,7 @@ +# +# Generic framework for controlling remote processors +# + +# REMOTE_PROC gets selected by whoever wants it +config REMOTE_PROC + tristate diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile new file mode 100644 index 0000000..d0f60c7 --- /dev/null +++ b/drivers/remoteproc/Makefile @@ -0,0 +1,5 @@ +# +# Generic framework for controlling remote processors +# + +obj-$(CONFIG_REMOTE_PROC) += remoteproc.o diff --git a/drivers/remoteproc/remoteproc.c b/drivers/remoteproc/remoteproc.c new file mode 100644 index 0000000..2b0514b --- /dev/null +++ b/drivers/remoteproc/remoteproc.c @@ -0,0 +1,780 @@ +/* + * Remote Processor Framework + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * + * Ohad Ben-Cohen + * Mark Grosen + * Brian Swetland + * Fernando Guzman Lugo + * Robert Tivy + * Armando Uribe De Leon + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* list of the available remote processors */ +static LIST_HEAD(rprocs); +/* + * This lock should be taken when the list of rprocs is accessed. + * Consider using RCU instead, since remote processors only get registered + * once (usually at boot), and then the list is only read accessed. + * Though right now the list is pretty short, and only rarely accessed. + */ +static DEFINE_SPINLOCK(rprocs_lock); + +/* debugfs parent dir */ +static struct dentry *rproc_dbg; + +/* + * Some remote processors may support dumping trace logs into a shared + * memory buffer. We expose this trace buffer using debugfs, so users + * can easily tell what's going on remotely. + */ +static ssize_t rproc_format_trace_buf(char __user *userbuf, size_t count, + loff_t *ppos, const void *src, int size) +{ + const char *buf = (const char *) src; + int i; + + /* + * find the end of trace buffer (does not account for wrapping). + * desirable improvement: use a ring buffer instead. + */ + for (i = 0; i < size && buf[i]; i++); + + return simple_read_from_buffer(userbuf, count, ppos, src, i); +} + +static int rproc_open_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +#define DEBUGFS_READONLY_FILE(name, value, len) \ +static ssize_t name## _rproc_read(struct file *filp, \ + char __user *userbuf, size_t count, loff_t *ppos) \ +{ \ + struct rproc *rproc = filp->private_data; \ + return rproc_format_trace_buf(userbuf, count, ppos, value, len);\ +} \ + \ +static const struct file_operations name ##_rproc_ops = { \ + .read = name ##_rproc_read, \ + .open = rproc_open_generic, \ + .llseek = generic_file_llseek, \ +}; + +/* + * Currently we allow two trace buffers for each remote processor. + * This is helpful in case a single remote processor has two independent + * cores, each of which is running an independent OS image. + * The current implementation is straightforward and simple, and is + * rather limited to 2 trace buffers. If, in time, we'd realize we + * need additional trace buffers, then the code should be refactored + * and generalized. + */ +DEBUGFS_READONLY_FILE(trace0, rproc->trace_buf0, rproc->trace_len0); +DEBUGFS_READONLY_FILE(trace1, rproc->trace_buf1, rproc->trace_len1); + +/* The state of the remote processor is exposed via debugfs, too */ +const char *rproc_state_string(int state) +{ + const char *result; + + switch (state) { + case RPROC_OFFLINE: + result = "offline"; + break; + case RPROC_SUSPENDED: + result = "suspended"; + break; + case RPROC_RUNNING: + result = "running"; + break; + case RPROC_LOADING: + result = "loading"; + break; + case RPROC_CRASHED: + result = "crashed"; + break; + default: + result = "invalid state"; + break; + } + + return result; +} + +static ssize_t rproc_state_read(struct file *filp, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct rproc *rproc = filp->private_data; + int state = rproc->state; + char buf[100]; + int i; + + i = snprintf(buf, 100, "%s (%d)\n", rproc_state_string(state), state); + + return simple_read_from_buffer(userbuf, count, ppos, buf, i); +} + +static const struct file_operations rproc_state_ops = { + .read = rproc_state_read, + .open = rproc_open_generic, + .llseek = generic_file_llseek, +}; + +/* The name of the remote processor is exposed via debugfs, too */ +static ssize_t rproc_name_read(struct file *filp, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct rproc *rproc = filp->private_data; + /* need room for the name, a newline and a terminating null */ + char buf[RPROC_MAX_NAME + 2]; + int i; + + i = snprintf(buf, RPROC_MAX_NAME + 2, "%s\n", rproc->name); + + return simple_read_from_buffer(userbuf, count, ppos, buf, i); +} + +static const struct file_operations rproc_name_ops = { + .read = rproc_name_read, + .open = rproc_open_generic, + .llseek = generic_file_llseek, +}; + +#define DEBUGFS_ADD(name) \ + debugfs_create_file(#name, 0400, rproc->dbg_dir, \ + rproc, &name## _rproc_ops) + +/** + * __find_rproc_by_name() - find a registered remote processor by name + * @name: name of the remote processor + * + * Internal function that returns the rproc @name, or NULL if @name does + * not exists. + */ +static struct rproc *__find_rproc_by_name(const char *name) +{ + struct rproc *rproc; + struct list_head *tmp; + + spin_lock(&rprocs_lock); + + list_for_each(tmp, &rprocs) { + rproc = list_entry(tmp, struct rproc, next); + if (!strcmp(rproc->name, name)) + break; + rproc = NULL; + } + + spin_unlock(&rprocs_lock); + + return rproc; +} + +/** + * __rproc_da_to_pa() - device to physical address conversion + * @maps: the remote processor's memory mappings table + * @da: a device address (as seen by the remote processor) + * @pa: pointer where the physical address result should be stored + * + * This function converts @da to its physical address (pa) by going through + * @maps, looking for a mapping that contains @da, and then calculating the + * appropriate pa. + * + * Not all remote processors are behind an IOMMU, so if @maps is NULL, + * we just return @da (after doing a basic sanity check). + * + * *** This function will be removed. Instead, iommu_iova_to_phys should + * *** be used. This will be done once the OMAP IOMMU migration will + * *** complete, and the missing parts in the generic IOMMU API will be + * *** added. + * + * On success 0 is returned, and @pa is updated with the result. + * Otherwise, -EINVAL is returned. + */ +static int +rproc_da_to_pa(const struct rproc_mem_entry *maps, u64 da, phys_addr_t *pa) +{ + int i; + u64 offset; + + /* + * If we're not being an IOMMU, then the remoteproc is accessing + * physical addresses directly + */ + if (!maps) { + if (da > ((phys_addr_t)(~0U))) + return -EINVAL; + + *pa = (phys_addr_t) da; + return 0; + } + + for (i = 0; maps[i].size; i++) { + const struct rproc_mem_entry *me = &maps[i]; + + if (da >= me->da && da < (me->da + me->size)) { + offset = da - me->da; + pr_debug("%s: matched mem entry no. %d\n", __func__, i); + *pa = me->pa + offset; + return 0; + } + } + + return -EINVAL; +} + +/** + * rproc_start() - boot the remote processor + * @rproc: the remote processor + * @bootaddr: address of first instruction to execute (optional) + * + * Boot a remote processor (i.e. power it on, take it out of reset, etc..) + */ +static void rproc_start(struct rproc *rproc, u64 bootaddr) +{ + struct device *dev = rproc->dev; + int err; + + err = mutex_lock_interruptible(&rproc->lock); + if (err) { + dev_err(dev, "can't lock remote processor %d\n", err); + return; + } + + err = rproc->ops->start(rproc, bootaddr); + if (err) { + dev_err(dev, "can't start rproc %s: %d\n", rproc->name, err); + goto unlock_mutex; + } + + rproc->state = RPROC_RUNNING; + + dev_info(dev, "remote processor %s is now up\n", rproc->name); + +unlock_mutex: + mutex_unlock(&rproc->lock); +} + +/** + * rproc_handle_trace_rsc() - handle a shared trace buffer resource + * @rproc: the remote processor + * @rsc: the trace resource descriptor + * + * In case the remote processor dumps trace logs into memory, ioremap it + * and make it available to the user via debugfs. + * Consider using the DMA mapping API here. + * + * Returns 0 on success, or an appropriate error code otherwise + */ +static int rproc_handle_trace_rsc(struct rproc *rproc, struct fw_resource *rsc) +{ + struct device *dev = rproc->dev; + void *ptr; + phys_addr_t pa; + int ret; + + ret = rproc_da_to_pa(rproc->memory_maps, rsc->da, &pa); + if (ret) { + dev_err(dev, "invalid device address\n"); + return -EINVAL; + } + + /* allow two trace buffers per rproc (can be extended if needed) */ + if (rproc->trace_buf0 && rproc->trace_buf1) { + dev_warn(dev, "skipping extra trace rsc %s\n", rsc->name); + return -EBUSY; + } + + /* + * trace buffer memory is normal memory, so we cast away the __iomem + * to make sparse happy + */ + ptr = (__force void *) ioremap_nocache(pa, rsc->len); + if (!ptr) { + dev_err(dev, "can't ioremap trace buffer %s\n", rsc->name); + return -ENOMEM; + } + + /* add the trace0 debugfs entry. If it already exists, add trace1 */ + if (!rproc->trace_buf0) { + rproc->trace_len0 = rsc->len; + rproc->trace_buf0 = ptr; + DEBUGFS_ADD(trace0); + } else { + rproc->trace_len1 = rsc->len; + rproc->trace_buf1 = ptr; + DEBUGFS_ADD(trace1); + } + + return 0; +} + +/** + * rproc_handle_resources - go over and handle the resource section + * @rproc: rproc handle + * @rsc: the resource secion + * @len: length of the resource section + * @bootaddr: if found a boot address, put it here + */ +static int rproc_handle_resources(struct rproc *rproc, struct fw_resource *rsc, + int len, u64 *bootaddr) +{ + struct device *dev = rproc->dev; + int ret = 0; + + while (len >= sizeof(*rsc)) { + dev_dbg(dev, "resource: type %d, da 0x%llx, pa 0x%llx, len 0x%x" + ", flags 0x%x, name %s\n", rsc->type, rsc->da, rsc->pa, + rsc->len, rsc->flags, rsc->name); + + switch (rsc->type) { + case RSC_TRACE: + ret = rproc_handle_trace_rsc(rproc, rsc); + if (ret) + dev_err(dev, "failed handling rsc\n"); + break; + case RSC_BOOTADDR: + if (*bootaddr) + dev_warn(dev, "bootaddr already set\n"); + *bootaddr = rsc->da; + break; + default: + /* we don't support much yet, so don't be noisy */ + dev_dbg(dev, "unsupported resource %d\n", rsc->type); + break; + } + + if (ret) + break; + rsc++; + len -= sizeof(*rsc); + } + + /* unmap trace buffers on failure */ + if (ret && rproc->trace_buf0) + iounmap((__force void __iomem *) rproc->trace_buf0); + if (ret && rproc->trace_buf1) + iounmap((__force void __iomem *) rproc->trace_buf1); + + return ret; +} + +static int rproc_process_fw(struct rproc *rproc, struct fw_section *section, + int left, u64 *bootaddr) +{ + struct device *dev = rproc->dev; + phys_addr_t pa; + u32 len, type; + u64 da; + int ret = 0; + void *ptr; + + while (left > sizeof(struct fw_section)) { + da = section->da; + len = section->len; + type = section->type; + + dev_dbg(dev, "section: type %d da 0x%llx len 0x%x\n", + type, da, len); + + left -= sizeof(struct fw_section); + if (left < section->len) { + dev_err(dev, "firmware image is truncated\n"); + ret = -EINVAL; + break; + } + + ret = rproc_da_to_pa(rproc->memory_maps, da, &pa); + if (ret) { + dev_err(dev, "rproc_da_to_pa failed: %d\n", ret); + break; + } + + dev_dbg(dev, "da 0x%llx pa 0x%x len 0x%x\n", da, pa, len); + + /* ioremaping normal memory, so make sparse happy */ + ptr = (__force void *) ioremap_nocache(pa, len); + if (!ptr) { + dev_err(dev, "can't ioremap 0x%x\n", pa); + ret = -ENOMEM; + break; + } + + /* put the section where the remoteproc will expect it */ + memcpy(ptr, section->content, len); + + /* a resource table needs special handling */ + if (section->type == FW_RESOURCE) + ret = rproc_handle_resources(rproc, + (struct fw_resource *) ptr, + len, bootaddr); + + /* iounmap normal memory; make sparse happy */ + iounmap((__force void __iomem *) ptr); + + /* rproc_handle_resources may have failed */ + if (ret) + break; + + section = (struct fw_section *)(section->content + len); + left -= len; + } + + return ret; +} + +static void rproc_load_fw(const struct firmware *fw, void *context) +{ + struct rproc *rproc = context; + struct device *dev = rproc->dev; + const char *fwfile = rproc->firmware; + u64 bootaddr = 0; + struct fw_header *image; + struct fw_section *section; + int left, ret; + + if (!fw) { + dev_err(dev, "%s: failed to load %s\n", __func__, fwfile); + goto complete_fw; + } + + dev_info(dev, "Loaded fw image %s, size %d\n", fwfile, fw->size); + + /* make sure this image is sane */ + if (fw->size < sizeof(struct fw_header)) { + dev_err(dev, "Image is too small\n"); + goto out; + } + + image = (struct fw_header *) fw->data; + + if (memcmp(image->magic, "RPRC", 4)) { + dev_err(dev, "Image is corrupted (bad magic)\n"); + goto out; + } + + dev_info(dev, "BIOS image version is %d\n", image->version); + + /* now process the image, section by section */ + section = (struct fw_section *)(image->header + image->header_len); + + left = fw->size - sizeof(struct fw_header) - image->header_len; + + ret = rproc_process_fw(rproc, section, left, &bootaddr); + if (ret) { + dev_err(dev, "Failed to process the image: %d\n", ret); + goto out; + } + + rproc_start(rproc, bootaddr); + +out: + release_firmware(fw); +complete_fw: + /* allow all contexts calling rproc_put() to proceed */ + complete_all(&rproc->firmware_loading_complete); +} + +/** + * rproc_get() - boot the remote processor + * @name: name of the remote processor + * + * Boot a remote processor (i.e. load its firmware, power it on, take it + * out of reset, etc..). + * + * If the remote processor is already powered on, immediately return + * its rproc handle. + * + * On success, returns the rproc handle. On failure, NULL is returned. + */ +struct rproc *rproc_get(const char *name) +{ + struct rproc *rproc, *ret = NULL; + struct device *dev; + int err; + + rproc = __find_rproc_by_name(name); + if (!rproc) { + pr_err("can't find remote processor %s\n", name); + return NULL; + } + + dev = rproc->dev; + + err = mutex_lock_interruptible(&rproc->lock); + if (err) { + dev_err(dev, "can't lock remote processor %s\n", name); + return NULL; + } + + /* prevent underlying implementation from being removed */ + if (!try_module_get(rproc->owner)) { + dev_err(dev, "%s: can't get owner\n", __func__); + goto unlock_mutex; + } + + /* skip the boot process if rproc is already (being) powered up */ + if (rproc->count++) { + ret = rproc; + goto unlock_mutex; + } + + /* rproc_put() calls should wait until async loader completes */ + init_completion(&rproc->firmware_loading_complete); + + dev_info(dev, "powering up %s\n", name); + + /* loading a firmware is required */ + if (!rproc->firmware) { + dev_err(dev, "%s: no firmware to load\n", __func__); + goto deref_rproc; + } + + /* + * Initiate an asynchronous firmware loading, to allow building + * remoteproc as built-in kernel code, without hanging the boot process + */ + err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + rproc->firmware, dev, GFP_KERNEL, rproc, rproc_load_fw); + if (err < 0) { + dev_err(dev, "request_firmware_nowait failed: %d\n", err); + goto deref_rproc; + } + + rproc->state = RPROC_LOADING; + ret = rproc; + goto unlock_mutex; + +deref_rproc: + complete_all(&rproc->firmware_loading_complete); + module_put(rproc->owner); + --rproc->count; +unlock_mutex: + mutex_unlock(&rproc->lock); + return ret; +} +EXPORT_SYMBOL(rproc_get); + +/** + * rproc_put() - power off the remote processor + * @rproc: the remote processor + * + * Release an rproc handle previously acquired with rproc_get(), + * and if we're the last user, power the processor off. + * + * Every call to rproc_get() must be (eventually) accompanied by a call + * to rproc_put(). Calling rproc_put() redundantly is a bug. + */ +void rproc_put(struct rproc *rproc) +{ + struct device *dev = rproc->dev; + int ret; + + /* + * make sure rproc_get() was called beforehand. + * it should be safe to check for zero without taking the lock. + */ + if (!rproc->count) { + dev_err(dev, "asymmetric put (fogot to call rproc_get ?)\n"); + ret = -EINVAL; + goto out; + } + + /* if rproc is just being loaded now, wait */ + wait_for_completion(&rproc->firmware_loading_complete); + + ret = mutex_lock_interruptible(&rproc->lock); + if (ret) { + dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret); + return; + } + + /* if the remote proc is still needed, bail out */ + if (--rproc->count) + goto out; + + if (rproc->trace_buf0) + /* iounmap normal memory, so make sparse happy */ + iounmap((__force void __iomem *) rproc->trace_buf0); + if (rproc->trace_buf1) + /* iounmap normal memory, so make sparse happy */ + iounmap((__force void __iomem *) rproc->trace_buf1); + + rproc->trace_buf0 = rproc->trace_buf1 = NULL; + + /* + * make sure rproc is really running before powering it off. + * this is important, because the fw loading might have failed. + */ + if (rproc->state == RPROC_RUNNING) { + ret = rproc->ops->stop(rproc); + if (ret) { + dev_err(dev, "can't stop rproc: %d\n", ret); + goto out; + } + } + + rproc->state = RPROC_OFFLINE; + + dev_info(dev, "stopped remote processor %s\n", rproc->name); + +out: + mutex_unlock(&rproc->lock); + if (!ret) + module_put(rproc->owner); +} +EXPORT_SYMBOL(rproc_put); + +/** + * rproc_register() - register a remote processor + * @dev: the underlying device + * @name: name of this remote processor + * @ops: platform-specific handlers (mainly start/stop) + * @firmware: name of firmware file to load + * @memory_maps: IOMMU settings for this rproc (optional) + * @owner: owning module + * + * Registers a new remote processor in the remoteproc framework. + * + * This is called by the underlying platform-specific implementation, + * whenever a new remote processor device is probed. + * + * On succes, 0 is return, and on failure an appropriate error code. + */ +int rproc_register(struct device *dev, const char *name, + const struct rproc_ops *ops, + const char *firmware, + const struct rproc_mem_entry *memory_maps, + struct module *owner) +{ + struct rproc *rproc; + + if (!dev || !name || !ops) + return -EINVAL; + + rproc = kzalloc(sizeof(struct rproc), GFP_KERNEL); + if (!rproc) { + dev_err(dev, "%s: kzalloc failed\n", __func__); + return -ENOMEM; + } + + rproc->dev = dev; + rproc->name = name; + rproc->ops = ops; + rproc->firmware = firmware; + rproc->memory_maps = memory_maps; + rproc->owner = owner; + + mutex_init(&rproc->lock); + + rproc->state = RPROC_OFFLINE; + + spin_lock(&rprocs_lock); + list_add_tail(&rproc->next, &rprocs); + spin_unlock(&rprocs_lock); + + dev_info(dev, "%s is available\n", name); + + if (!rproc_dbg) + goto out; + + rproc->dbg_dir = debugfs_create_dir(dev_name(dev), rproc_dbg); + if (!rproc->dbg_dir) { + dev_err(dev, "can't create debugfs dir\n"); + goto out; + } + + debugfs_create_file("name", 0400, rproc->dbg_dir, rproc, + &rproc_name_ops); + debugfs_create_file("state", 0400, rproc->dbg_dir, rproc, + &rproc_state_ops); + +out: + return 0; +} +EXPORT_SYMBOL(rproc_register); + +/** + * rproc_unregister() - unregister a remote processor + * @name: name of this remote processor + * + * Unregisters a remote processor. + * + * On succes, 0 is return. If this remote processor isn't found, -EINVAL + * is returned. + */ +int rproc_unregister(const char *name) +{ + struct rproc *rproc; + + rproc = __find_rproc_by_name(name); + if (!rproc) { + pr_err("can't find remote processor %s\n", name); + return -EINVAL; + } + + dev_info(rproc->dev, "removing %s\n", name); + + if (rproc->dbg_dir) + debugfs_remove_recursive(rproc->dbg_dir); + + spin_lock(&rprocs_lock); + list_del(&rproc->next); + spin_unlock(&rprocs_lock); + + kfree(rproc); + + return 0; +} +EXPORT_SYMBOL(rproc_unregister); + +static int __init remoteproc_init(void) +{ + if (debugfs_initialized()) { + rproc_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (!rproc_dbg) + pr_err("can't create debugfs dir\n"); + } + + return 0; +} +/* must be ready in time for device_initcall users */ +subsys_initcall(remoteproc_init); + +static void __exit remoteproc_exit(void) +{ + if (rproc_dbg) + debugfs_remove(rproc_dbg); +} +module_exit(remoteproc_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Generic Remote Processor Framework"); diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h new file mode 100644 index 0000000..6cdb966 --- /dev/null +++ b/include/linux/remoteproc.h @@ -0,0 +1,273 @@ +/* + * Remote Processor Framework + * + * Copyright(c) 2011 Texas Instruments, Inc. + * Copyright(c) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Texas Instruments nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef REMOTEPROC_H +#define REMOTEPROC_H + +#include +#include + +/** + * DOC: The Binary Structure of the Firmware + * + * The following enums and structures define the binary format of the image + * we load and run the remote processors with. + * + * The binary format is as follows: + * + * struct { + * char magic[4] = { 'R', 'P', 'R', 'C' }; + * u32 version; + * u32 header_len; + * char header[...] = { header_len bytes of unformatted, textual header }; + * struct section { + * u32 type; + * u64 da; + * u32 len; + * u8 content[...] = { len bytes of binary data }; + * } [ no limit on number of sections ]; + * } __packed; + */ + +/** + * struct fw_header - header of the firmware image + * @magic: 4-bytes magic (should contain "RPRC") + * @version: version number, should be bumped on binary changes + * @header_len: length, in bytes, of the following text header + * @header: free-style textual header, users can read with 'head' + * + * This structure defines the header of the remoteproc firmware. + */ +struct fw_header { + char magic[4]; + u32 version; + u32 header_len; + char header[0]; +} __packed; + +/** + * struct fw_section - header of a firmware section + * @type: section type + * @da: device address that the rproc expects to find this section at. + * @len: length of the section (in bytes) + * @content: the section data + * + * This structure defines the header of a firmware section. All sections + * should be loaded to the address specified by @da, so the remote processor + * will find them. + * + * Note: if the remote processor is not behind an IOMMU, then da is a + * mere physical address + */ +struct fw_section { + u32 type; + u64 da; + u32 len; + char content[0]; +} __packed; + +/** + * enum fw_section_type - section type values + * + * @FW_RESOURCE: a resource section. this section contains static + * resource requests (/announcements) that the remote + * processor requires (/supports). Most of these requests + * require that the host fulfill them (and usually + * "reply" with a result) before the remote processor + * is booted. See Documentation/remoteproc.h for more info + * @FW_TEXT: a text section + * @FW_DATA: a data section + * + * Note: text and data sections have different types so we can support stuff + * like crash dumps (which only requires dumping data sections) or loading + * text sections into faster memory. Currently, though, both section types + * are treated exactly the same. + */ +enum fw_section_type { + FW_RESOURCE = 0, + FW_TEXT = 1, + FW_DATA = 2, +}; + +/** + * struct fw_resource - describes an entry from the resource section + * @type: resource type + * @da: depends on the resource type + * @pa: depends on the resource type + * @len: depends on the resource type + * @flags: depends on the resource type + * @name: name of resource + * + * Some resources entries are mere announcements, where the host is informed + * of specific remoteproc configuration. Other entries require the host to + * do something (e.g. reserve a requested resource) and reply by overwriting + * a member inside struct fw_resource with the id of the allocated resource. + * There could also be resource entries where the remoteproc's image suggests + * a configuration, but the host may overwrite it with its own preference. + * + * Note: the vast majority of the resource types are not implemented yet, + * and this is all very much preliminary. + */ +struct fw_resource { + u32 type; + u64 da; + u64 pa; + u32 len; + u32 flags; + u8 name[48]; +} __packed; + +/** + * enum fw_resource_type - types of resource entries + * + * @RSC_TRACE: announces the availability of a trace buffer into which + * the remote processor will be writing logs. In this case, + * 'da' indicates the device address where logs are written to, + * and 'len' is the size of the trace buffer. + * Currently we support two trace buffers per remote processor, + * to support two autonomous cores running in a single rproc + * device. + * If additional trace buffers are needed, this should be + * extended/generalized. + * @RSC_BOOTADDR: announces the address of the first instruction the remote + * processor should be booted with (address indicated in 'da'). + * + * Note: most of the resource types are not implemented yet, so they are + * not documented yet. + */ +enum fw_resource_type { + RSC_CARVEOUT = 0, + RSC_DEVMEM = 1, + RSC_DEVICE = 2, + RSC_IRQ = 3, + RSC_TRACE = 4, + RSC_BOOTADDR = 5, +}; + +/** + * struct rproc_mem_entry - memory mapping descriptor + * @da: device address as seen by the remote processor + * @pa: physical address + * @size: size of this memory region + * + * Board file will use this struct to define the IOMMU configuration + * for this remote processor. If the rproc device accesses physical memory + * directly (and not through an IOMMU), this is not needed. + */ +struct rproc_mem_entry { + u64 da; + phys_addr_t pa; + u32 size; +}; + +struct rproc; + +/** + * struct rproc_ops - platform-specific device handlers + * @start: power on the device and boot it. implementation may require + * specifyng a boot address + * @stop: power off the device + */ +struct rproc_ops { + int (*start)(struct rproc *rproc, u64 bootaddr); + int (*stop)(struct rproc *rproc); +}; + +/* + * enum rproc_state - remote processor states + * + * @RPROC_OFFLINE: device is powered off + * @RPROC_SUSPENDED: device is suspended; needs to be woken up to receive + * a message. + * @RPROC_RUNNING: device is up and running + * @RPROC_LOADING: asynchronous firmware loading has started + * @RPROC_CRASHED: device has crashed; need to start recovery + */ +enum rproc_state { + RPROC_OFFLINE, + RPROC_SUSPENDED, + RPROC_RUNNING, + RPROC_LOADING, + RPROC_CRASHED, +}; + +#define RPROC_MAX_NAME 100 + +/* + * struct rproc - represents a physical remote processor device + * + * @next: next rproc entry in the list + * @name: human readable name of the rproc, cannot exceed RPROC_MAX_NAME bytes + * @memory_maps: table of da-to-pa memory maps (relevant if device is behind + * an iommu) + * @firmware: name of firmware file to be loaded + * @owner: reference to the platform-specific rproc module + * @priv: private data which belongs to the platform-specific rproc module + * @ops: platform-specific start/stop rproc handlers + * @dev: underlying device + * @count: usage refcount + * @state: state of the device + * @lock: lock which protects concurrent manipulations of the rproc + * @dbg_dir: debugfs directory of this rproc device + * @trace_buf0: main trace buffer of the remote processor + * @trace_buf1: second, optional, trace buffer of the remote processor + * @trace_len0: length of main trace buffer of the remote processor + * @trace_len1: length of the second (and optional) trace buffer + * @firmware_loading_complete: marks e/o asynchronous firmware loading + */ +struct rproc { + struct list_head next; + const char *name; + const struct rproc_mem_entry *memory_maps; + const char *firmware; + struct module *owner; + void *priv; + const struct rproc_ops *ops; + struct device *dev; + int count; + int state; + struct mutex lock; + struct dentry *dbg_dir; + char *trace_buf0, *trace_buf1; + int trace_len0, trace_len1; + struct completion firmware_loading_complete; +}; + +struct rproc *rproc_get(const char *); +void rproc_put(struct rproc *); +int rproc_register(struct device *, const char *, const struct rproc_ops *, + const char *, const struct rproc_mem_entry *, struct module *); +int rproc_unregister(const char *); + +#endif /* REMOTEPROC_H */ -- 1.7.1 From ohad at wizery.com Tue Jun 21 02:18:28 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Tue, 21 Jun 2011 10:18:28 +0300 Subject: [RFC 2/8] remoteproc: add omap implementation In-Reply-To: <1308640714-17961-1-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> Message-ID: <1308640714-17961-3-git-send-email-ohad@wizery.com> From: Guzman Lugo, Fernando Add remoteproc implementation for OMAP4, so we can load the M3 and DSP remote processors. Based on code by Hari Kanigeri While this code is functional, and works on OMAP4 & its M3's, it is still preliminary and going to substantially change: 1. Splitting physically contiguous regions to pages should happen inside the IOMMU API framework, and not here (so omap_rproc_map_unmap should go away). 2. IOMMU programming in general should go away too; it should be handled by generic code (preferably using the DMA mapping API), and not by an OMAP specific component. This patch depends on OMAP's IOMMU API migration, as posted here: https://lkml.org/lkml/2011/6/2/369 [ohad at wizery.com: commit log, generic iommu api migration, refactoring, cleanups] Signed-off-by: Guzman Lugo, Fernando Signed-off-by: Ohad Ben-Cohen --- arch/arm/plat-omap/include/plat/remoteproc.h | 41 +++++ drivers/remoteproc/Kconfig | 21 +++ drivers/remoteproc/Makefile | 1 + drivers/remoteproc/omap_remoteproc.c | 243 ++++++++++++++++++++++++++ 4 files changed, 306 insertions(+), 0 deletions(-) create mode 100644 arch/arm/plat-omap/include/plat/remoteproc.h create mode 100644 drivers/remoteproc/omap_remoteproc.c diff --git a/arch/arm/plat-omap/include/plat/remoteproc.h b/arch/arm/plat-omap/include/plat/remoteproc.h new file mode 100644 index 0000000..6494570 --- /dev/null +++ b/arch/arm/plat-omap/include/plat/remoteproc.h @@ -0,0 +1,41 @@ +/* + * Remote Processor - omap-specific bits + * + * 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 + * version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef _PLAT_REMOTEPROC_H +#define _PLAT_REMOTEPROC_H + +#include + +/* + * struct omap_rproc_pdata - omap remoteproc's platform data + * @name: the remoteproc's name, cannot exceed RPROC_MAX_NAME bytes + * @iommu_name: iommu device we're behind of + * @oh_name: omap hwmod device + * @oh_name_opt: optional, secondary omap hwmod device + * @firmware: name of firmware file to load + * @ops: start/stop rproc handlers + * @memory_maps: table of da-to-pa iommu memory maps + */ +struct omap_rproc_pdata { + const char *name; + const char *iommu_name; + const char *oh_name; + const char *oh_name_opt; + const char *firmware; + const struct rproc_ops *ops; + const struct rproc_mem_entry *memory_maps; +}; + +#endif /* _PLAT_REMOTEPROC_H */ diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index a60bb12..88421bd 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -5,3 +5,24 @@ # REMOTE_PROC gets selected by whoever wants it config REMOTE_PROC tristate + +# for tristate, either expose omap_device_* or pass it via pdata +config OMAP_REMOTE_PROC + bool "OMAP remoteproc support" + depends on ARCH_OMAP4 + select OMAP_IOMMU + select REMOTE_PROC + select OMAP_MBOX_FWK + default y + help + Say y here to support OMAP's remote processors (dual M3 + and DSP on OMAP4) via the remote processor framework. + + Currently only supported on OMAP4. + + Usually you want to say y here, in order to enable multimedia + use-cases to run on your platform (multimedia codecs are + offloaded to remote DSP processors using this framework). + + It's safe to say n here if you're not interested in multimedia + offloading or just want a bare minimum kernel. diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index d0f60c7..0198b1d 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_REMOTE_PROC) += remoteproc.o +obj-$(CONFIG_OMAP_REMOTE_PROC) += omap_remoteproc.o diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c new file mode 100644 index 0000000..91f7f11 --- /dev/null +++ b/drivers/remoteproc/omap_remoteproc.c @@ -0,0 +1,243 @@ +/* + * Remote processor machine-specific module for OMAP4 + * + * 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 + * version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct omap_rproc_priv { + struct iommu_domain *domain; + struct device *iommu; +}; + +/* + * Map a physically contiguous memory region using biggest pages possible. + * TODO: this code should go away; it belongs in the generic IOMMU layer + */ +static int omap_rproc_map_unmap(struct iommu_domain *domain, + const struct rproc_mem_entry *me, bool map) +{ + u32 all_bits; + /* these are the page sizes supported by OMAP's IOMMU */ + u32 pg_size[] = {SZ_16M, SZ_1M, SZ_64K, SZ_4K}; + int i, ret, size = me->size; + u32 da = me->da; + u32 pa = me->pa; + int order; + int flags; + + /* must be aligned at least with the smallest supported iommu page */ + if (!IS_ALIGNED(size, SZ_4K) || !IS_ALIGNED(da | pa, SZ_4K)) { + pr_err("misaligned: size %x da 0x%x pa 0x%x\n", size, da, pa); + return -EINVAL; + } + + while (size) { + /* find the max page size with which both pa, da are aligned */ + all_bits = pa | da; + for (i = 0; i < ARRAY_SIZE(pg_size); i++) { + if ((size >= pg_size[i]) && + ((all_bits & (pg_size[i] - 1)) == 0)) { + break; + } + } + + order = get_order(pg_size[i]); + + /* OMAP4's M3 is little endian, so no need for conversions */ + flags = MMU_RAM_ENDIAN_LITTLE | MMU_RAM_ELSZ_NONE; + + if (map) + ret = iommu_map(domain, da, pa, order, flags); + else + ret = iommu_unmap(domain, da, order); + + if (ret) + return ret; + + size -= pg_size[i]; + da += pg_size[i]; + pa += pg_size[i]; + } + + return 0; +} + +/* bootaddr isn't needed for the dual M3's */ +static inline int omap_rproc_start(struct rproc *rproc, u64 bootaddr) +{ + struct device *dev = rproc->dev; + struct platform_device *pdev = to_platform_device(dev); + struct omap_rproc_pdata *pdata = dev->platform_data; + struct iommu_domain *domain; + struct device *iommu; + struct omap_rproc_priv *priv; + int ret, i; + + if (!iommu_found()) { + dev_err(&pdev->dev, "iommu not found\n"); + return -ENODEV; + } + + priv = kmalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(&pdev->dev, "kzalloc failed\n"); + return -ENOMEM; + } + + /* + * Use the specified iommu name to find our iommu device. + * TODO: solve this generically so other platforms can use it, too + */ + iommu = omap_find_iommu_device(pdata->iommu_name); + if (!iommu) { + dev_err(dev, "omap_find_iommu_device failed\n"); + ret = -ENODEV; + goto free_mem; + } + + domain = iommu_domain_alloc(); + if (!domain) { + dev_err(&pdev->dev, "can't alloc iommu domain\n"); + ret = -ENODEV; + goto free_mem; + } + + priv->iommu = iommu; + priv->domain = domain; + rproc->priv = priv; + + ret = iommu_attach_device(domain, iommu); + if (ret) { + dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret); + goto free_domain; + } + + for (i = 0; rproc->memory_maps[i].size; i++) { + const struct rproc_mem_entry *me = &rproc->memory_maps[i]; + + ret = omap_rproc_map_unmap(domain, me, true); + if (ret) { + dev_err(&pdev->dev, "iommu_map failed: %d\n", ret); + goto unmap_regions; + } + } + + /* power on the remote processor itself */ + ret = omap_device_enable(pdev); + if (ret) + goto unmap_regions; + + return 0; + +unmap_regions: + for (--i; i >= 0 && rproc->memory_maps[i].size; i--) { + const struct rproc_mem_entry *me = &rproc->memory_maps[i]; + omap_rproc_map_unmap(domain, me, false); + } +free_domain: + iommu_domain_free(domain); +free_mem: + kfree(priv); + return ret; +} + +static inline int omap_rproc_stop(struct rproc *rproc) +{ + struct device *dev = rproc->dev; + struct platform_device *pdev = to_platform_device(dev); + struct omap_rproc_priv *priv = rproc->priv; + struct iommu_domain *domain = priv->domain; + struct device *iommu = priv->iommu; + int ret, i; + + /* power off the remote processor itself */ + ret = omap_device_shutdown(pdev); + if (ret) { + dev_err(dev, "failed to shutdown: %d\n", ret); + goto out; + } + + for (i = 0; rproc->memory_maps[i].size; i++) { + const struct rproc_mem_entry *me = &rproc->memory_maps[i]; + + ret = omap_rproc_map_unmap(domain, me, false); + if (ret) { + dev_err(&pdev->dev, "iommu_unmap failed: %d\n", ret); + goto out; + } + } + + iommu_detach_device(domain, iommu); + iommu_domain_free(domain); + +out: + return ret; +} + +static struct rproc_ops omap_rproc_ops = { + .start = omap_rproc_start, + .stop = omap_rproc_stop, +}; + +static int omap_rproc_probe(struct platform_device *pdev) +{ + struct omap_rproc_pdata *pdata = pdev->dev.platform_data; + + return rproc_register(&pdev->dev, pdata->name, &omap_rproc_ops, + pdata->firmware, pdata->memory_maps, + THIS_MODULE); +} + +static int __devexit omap_rproc_remove(struct platform_device *pdev) +{ + struct omap_rproc_pdata *pdata = pdev->dev.platform_data; + + return rproc_unregister(pdata->name); +} + +static struct platform_driver omap_rproc_driver = { + .probe = omap_rproc_probe, + .remove = __devexit_p(omap_rproc_remove), + .driver = { + .name = "omap-rproc", + .owner = THIS_MODULE, + }, +}; + +static int __init omap_rproc_init(void) +{ + return platform_driver_register(&omap_rproc_driver); +} +/* must be ready in time for device_initcall users */ +subsys_initcall(omap_rproc_init); + +static void __exit omap_rproc_exit(void) +{ + platform_driver_unregister(&omap_rproc_driver); +} +module_exit(omap_rproc_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("OMAP Remote Processor control driver"); -- 1.7.1 From ohad at wizery.com Tue Jun 21 02:18:29 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Tue, 21 Jun 2011 10:18:29 +0300 Subject: [RFC 3/8] omap: add carveout memory support for remoteproc In-Reply-To: <1308640714-17961-1-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> Message-ID: <1308640714-17961-4-git-send-email-ohad@wizery.com> This is a temporary patch to get things going, and is by no means a suggestion for inclusion (read only if interested, but don't waste much review energies here). The way to go forward here is to use CMA (together with the generic DMA API, so we also get IOMMU programming "for free"). This patch also breaks tidspbridge. Use it only if you want to try out rpmsg/remoteproc on OMAP4, and you don't care too much about multi-board kernels. Signed-off-by: Ohad Ben-Cohen --- arch/arm/plat-omap/Kconfig | 8 ++++++++ arch/arm/plat-omap/devices.c | 14 ++++++++++++-- arch/arm/plat-omap/include/plat/dsp.h | 6 +++++- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index 1c3acb5..09c91d1 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig @@ -211,6 +211,14 @@ config OMAP_SERIAL_WAKE to data on the serial RX line. This allows you to wake the system from serial console. +config OMAP_CARVEOUT_MEMPOOL_SIZE + hex "Physical carveout memory pool size (Byte)" + depends on OMAP_REMOTE_PROC + default 0x3300000 + help + Allocate specified size of memory at boot time so we can ioremap + it safely. + choice prompt "OMAP PM layer selection" depends on ARCH_OMAP diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c index ea28f98..6922f3b 100644 --- a/arch/arm/plat-omap/devices.c +++ b/arch/arm/plat-omap/devices.c @@ -234,13 +234,16 @@ static void omap_init_uwire(void) static inline void omap_init_uwire(void) {} #endif -#if defined(CONFIG_TIDSPBRIDGE) || defined(CONFIG_TIDSPBRIDGE_MODULE) +#if defined(CONFIG_TIDSPBRIDGE) || defined(CONFIG_TIDSPBRIDGE_MODULE) || \ + defined(CONFIG_OMAP_REMOTE_PROC) static phys_addr_t omap_dsp_phys_mempool_base; +static phys_addr_t omap_dsp_phys_mempool_size; void __init omap_dsp_reserve_sdram_memblock(void) { - phys_addr_t size = CONFIG_TIDSPBRIDGE_MEMPOOL_SIZE; + /* ignoring TIDSPBRIDGE for a moment here... */ + phys_addr_t size = CONFIG_OMAP_CARVEOUT_MEMPOOL_SIZE; phys_addr_t paddr; if (!size) @@ -256,6 +259,7 @@ void __init omap_dsp_reserve_sdram_memblock(void) memblock_remove(paddr, size); omap_dsp_phys_mempool_base = paddr; + omap_dsp_phys_mempool_size = size; } phys_addr_t omap_dsp_get_mempool_base(void) @@ -263,6 +267,12 @@ phys_addr_t omap_dsp_get_mempool_base(void) return omap_dsp_phys_mempool_base; } EXPORT_SYMBOL(omap_dsp_get_mempool_base); + +phys_addr_t omap_dsp_get_mempool_size(void) +{ + return omap_dsp_phys_mempool_size; +} +EXPORT_SYMBOL(omap_dsp_get_mempool_size); #endif /* diff --git a/arch/arm/plat-omap/include/plat/dsp.h b/arch/arm/plat-omap/include/plat/dsp.h index 9c604b3..31ee386 100644 --- a/arch/arm/plat-omap/include/plat/dsp.h +++ b/arch/arm/plat-omap/include/plat/dsp.h @@ -22,10 +22,14 @@ struct omap_dsp_platform_data { phys_addr_t phys_mempool_size; }; -#if defined(CONFIG_TIDSPBRIDGE) || defined(CONFIG_TIDSPBRIDGE_MODULE) +#if defined(CONFIG_TIDSPBRIDGE) || defined(CONFIG_TIDSPBRIDGE_MODULE) || \ + defined(CONFIG_OMAP_REMOTE_PROC) extern void omap_dsp_reserve_sdram_memblock(void); #else static inline void omap_dsp_reserve_sdram_memblock(void) { } #endif +phys_addr_t omap_dsp_get_mempool_size(void); +phys_addr_t omap_dsp_get_mempool_base(void); + #endif -- 1.7.1 From ohad at wizery.com Tue Jun 21 02:18:30 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Tue, 21 Jun 2011 10:18:30 +0300 Subject: [RFC 4/8] omap: add remoteproc devices In-Reply-To: <1308640714-17961-1-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> Message-ID: <1308640714-17961-5-git-send-email-ohad@wizery.com> From: Guzman Lugo, Fernando Add omap remoteproc devices for the ipu and dsp remote processors. TODO: - rework to use CMA instead of reserving memory at boot [ohad at wizery.com: commit log, refactored and simplified, still wip] Signed-off-by: Guzman Lugo, Fernando Signed-off-by: Ohad Ben-Cohen --- arch/arm/mach-omap2/Makefile | 2 + arch/arm/mach-omap2/remoteproc.c | 159 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+), 0 deletions(-) create mode 100644 arch/arm/mach-omap2/remoteproc.c diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index b148077..2b04fe6 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -270,3 +270,5 @@ disp-$(CONFIG_OMAP2_DSS) := display.o obj-y += $(disp-m) $(disp-y) obj-y += common-board-devices.o + +obj-$(CONFIG_OMAP_REMOTE_PROC) += remoteproc.o diff --git a/arch/arm/mach-omap2/remoteproc.c b/arch/arm/mach-omap2/remoteproc.c new file mode 100644 index 0000000..4f846cb --- /dev/null +++ b/arch/arm/mach-omap2/remoteproc.c @@ -0,0 +1,159 @@ +/* + * Remote processor machine-specific module for OMAP4 + * + * 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 + * version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define L4_PERIPHERAL_L4CFG (L4_44XX_BASE) +#define IPU_PERIPHERAL_L4CFG 0xAA000000 + +#define IPU_MEM_TEXT 0x0 +#define IPU_MEM_DATA 0x80000000 +#define IPU_MEM_IPC 0xA0000000 + +/* + * Memory mappings for the remote M3 subsystem + * + * Don't change the device addresses (first parameter), otherwise you'd have + * to update the firmware (BIOS image) accordingly. + * + * A 0 physical address (second parameter) means this physical region should + * be dynamically carved out at boot time. + * + * Sizes should be in the form of 2 ^ n * PAGE_SIZE (where n = 0, 1, 2, ...) + * + * Alignment requirement: at least page-based + */ +static struct rproc_mem_entry ipu_memory_maps[] = { + {IPU_MEM_IPC, 0, SZ_1M}, /* keep this IPC region first */ + {IPU_MEM_TEXT, 0, SZ_4M}, + {IPU_MEM_DATA, 0, SZ_32M}, + {IPU_PERIPHERAL_L4CFG, L4_PERIPHERAL_L4CFG, SZ_16M}, + { } +}; + +static struct omap_rproc_pdata omap4_rproc_data[] = { + { + .name = "dsp", + .iommu_name = "tesla", + .firmware = "tesla-dsp.bin", + .oh_name = "dsp_c0", + }, + { + .name = "ipu", + .iommu_name = "ducati", + .firmware = "ducati-m3.bin", + .oh_name = "ipu_c0", + .oh_name_opt = "ipu_c1", + .memory_maps = ipu_memory_maps, + }, +}; + +static struct omap_device_pm_latency omap_rproc_latency[] = { + { + .deactivate_func = omap_device_idle_hwmods, + .activate_func = omap_device_enable_hwmods, + .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST, + }, +}; + +static int __init omap_rproc_init(void) +{ + const char *pdev_name = "omap-rproc"; + struct omap_hwmod *oh[2]; + struct omap_device *od; + int i, ret = 0, oh_count; + phys_addr_t paddr, size; + + /* names like ipu_cx/dsp_cx might show up on other OMAPs, too */ + if (!cpu_is_omap44xx()) + return 0; + + paddr = omap_dsp_get_mempool_base(); + size = omap_dsp_get_mempool_size(); + if (!paddr || !size) { + pr_warn("carveout memory is unavailable: 0x%x, 0x%x\n", + paddr, size); + return -ENOMEM; + } + + /* dynamically allocate carveout memory as required by the ipu */ + for (i = 0; i < ARRAY_SIZE(ipu_memory_maps); i++) { + struct rproc_mem_entry *me = &ipu_memory_maps[i]; + + if (!me->pa && me->size) { + if (me->size > size) { + pr_warn("out of carveout memory\n"); + return -ENOMEM; + } + + me->pa = paddr; + paddr += me->size; + size -= me->size; + + pr_info("0x%x bytes at 0x%x %d\n", me->size, me->pa, i); + } + } + + /* build the remote proc devices */ + for (i = 0; i < ARRAY_SIZE(omap4_rproc_data); i++) { + const char *oh_name = omap4_rproc_data[i].oh_name; + const char *oh_name_opt = omap4_rproc_data[i].oh_name_opt; + oh_count = 0; + + oh[0] = omap_hwmod_lookup(oh_name); + if (!oh[0]) { + pr_err("could not look up %s\n", oh_name); + continue; + } + oh_count++; + + /* ipu has a secondary hwmod entry */ + if (oh_name_opt) { + oh[1] = omap_hwmod_lookup(oh_name_opt); + if (!oh[1]) { + pr_err("could not look up %s\n", oh_name_opt); + continue; + } + oh_count++; + } + + od = omap_device_build_ss(pdev_name, i, oh, oh_count, + &omap4_rproc_data[i], + sizeof(struct omap_rproc_pdata), + omap_rproc_latency, + ARRAY_SIZE(omap_rproc_latency), + false); + if (IS_ERR(od)) { + pr_err("Could not build omap_device for %s:%s\n", + pdev_name, oh_name); + ret = PTR_ERR(od); + } + } + + return ret; +} +/* must be ready in time for device_initcall users */ +subsys_initcall(omap_rproc_init); -- 1.7.1 From ohad at wizery.com Tue Jun 21 02:18:31 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Tue, 21 Jun 2011 10:18:31 +0300 Subject: [RFC 5/8] remoteproc: add davinci implementation In-Reply-To: <1308640714-17961-1-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> Message-ID: <1308640714-17961-6-git-send-email-ohad@wizery.com> From: Mark Grosen Add remoteproc implementation for da850, so we can boot its DSP. Signed-off-by: Mark Grosen Signed-off-by: Ohad Ben-Cohen --- arch/arm/mach-davinci/include/mach/remoteproc.h | 28 +++++ drivers/remoteproc/Kconfig | 16 +++ drivers/remoteproc/Makefile | 1 + drivers/remoteproc/davinci_remoteproc.c | 147 +++++++++++++++++++++++ 4 files changed, 192 insertions(+), 0 deletions(-) create mode 100644 arch/arm/mach-davinci/include/mach/remoteproc.h create mode 100644 drivers/remoteproc/davinci_remoteproc.c diff --git a/arch/arm/mach-davinci/include/mach/remoteproc.h b/arch/arm/mach-davinci/include/mach/remoteproc.h new file mode 100644 index 0000000..af6e88c --- /dev/null +++ b/arch/arm/mach-davinci/include/mach/remoteproc.h @@ -0,0 +1,28 @@ +/* + * Remote Processor + * + * 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 + * version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef _DAVINCI_REMOTEPROC_H +#define _DAVINCI_REMOTEPROC_H + +#include + +struct davinci_rproc_pdata { + struct rproc_ops *ops; + char *name; + char *clk_name; + char *firmware; +}; + +#endif /* _DAVINCI_REMOTEPROC_H */ diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index 88421bd..1e594b5 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -26,3 +26,19 @@ config OMAP_REMOTE_PROC It's safe to say n here if you're not interested in multimedia offloading or just want a bare minimum kernel. + +config DAVINCI_REMOTE_PROC + tristate "Davinci remoteproc support" + depends on ARCH_DAVINCI_DA850 + select REMOTE_PROC + default y + help + Say y here to support Davinci's DSP remote processor via the remote + processor framework (currently only da850 is supported). + + Usually you want to say y here, in order to enable multimedia + use-cases to run on your platform (multimedia codecs are + offloaded to remote DSP processors using this framework). + + It's safe to say n here if you're not interested in multimedia + offloading or just want a bare minimum kernel. diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 0198b1d..e83dac9 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_REMOTE_PROC) += remoteproc.o obj-$(CONFIG_OMAP_REMOTE_PROC) += omap_remoteproc.o +obj-$(CONFIG_DAVINCI_REMOTE_PROC) += davinci_remoteproc.o diff --git a/drivers/remoteproc/davinci_remoteproc.c b/drivers/remoteproc/davinci_remoteproc.c new file mode 100644 index 0000000..0e26fe9 --- /dev/null +++ b/drivers/remoteproc/davinci_remoteproc.c @@ -0,0 +1,147 @@ +/* + * Remote processor machine-specific module for Davinci + * + * 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 + * version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Technical Reference: + * OMAP-L138 Applications Processor System Reference Guide + * http://www.ti.com/litv/pdf/sprugm7d + */ + +/* local reset bit (0 is asserted) in MDCTL15 register (section 9.6.18) */ +#define LRST BIT(8) + +/* next state bits in MDCTL15 register (section 9.6.18) */ +#define NEXT_ENABLED 0x3 + +/* register for DSP boot address in SYSCFG0 module (section 11.5.6) */ +#define HOST1CFG 0x44 + +static inline int davinci_rproc_start(struct rproc *rproc, u64 bootaddr) +{ + struct device *dev = rproc->dev; + struct davinci_rproc_pdata *pdata = dev->platform_data; + struct davinci_soc_info *soc_info = &davinci_soc_info; + void __iomem *psc_base; + struct clk *dsp_clk; + + /* hw requires the start (boot) address be on 1KB boundary */ + if (bootaddr & 0x3ff) { + dev_err(dev, "invalid boot address: must be aligned to 1KB\n"); + return -EINVAL; + } + + dsp_clk = clk_get(dev, pdata->clk_name); + if (IS_ERR_OR_NULL(dsp_clk)) { + dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk)); + return PTR_ERR(dsp_clk); + } + + clk_enable(dsp_clk); + rproc->priv = dsp_clk; + + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K); + + /* insure local reset is asserted before writing start address */ + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 * DA8XX_LPSC0_GEM); + + __raw_writel(bootaddr, DA8XX_SYSCFG0_VIRT(HOST1CFG)); + + /* de-assert local reset to start the dsp running */ + __raw_writel(LRST | NEXT_ENABLED, psc_base + MDCTL + + 4 * DA8XX_LPSC0_GEM); + + iounmap(psc_base); + + return 0; +} + +static inline int davinci_rproc_stop(struct rproc *rproc) +{ + struct davinci_soc_info *soc_info = &davinci_soc_info; + void __iomem *psc_base; + struct clk *dsp_clk = rproc->priv; + + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K); + + /* halt the dsp by asserting local reset */ + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 * DA8XX_LPSC0_GEM); + + clk_disable(dsp_clk); + clk_put(dsp_clk); + + iounmap(psc_base); + + return 0; +} + +static struct rproc_ops davinci_rproc_ops = { + .start = davinci_rproc_start, + .stop = davinci_rproc_stop, +}; + +static int davinci_rproc_probe(struct platform_device *pdev) +{ + struct davinci_rproc_pdata *pdata = pdev->dev.platform_data; + + return rproc_register(&pdev->dev, pdata->name, &davinci_rproc_ops, + pdata->firmware, NULL, THIS_MODULE); +} + +static int __devexit davinci_rproc_remove(struct platform_device *pdev) +{ + struct davinci_rproc_pdata *pdata = pdev->dev.platform_data; + + return rproc_unregister(pdata->name); +} + +static struct platform_driver davinci_rproc_driver = { + .probe = davinci_rproc_probe, + .remove = __devexit_p(davinci_rproc_remove), + .driver = { + .name = "davinci-rproc", + .owner = THIS_MODULE, + }, +}; + +static int __init davinci_rproc_init(void) +{ + return platform_driver_register(&davinci_rproc_driver); +} +module_init(davinci_rproc_init); + +static void __exit davinci_rproc_exit(void) +{ + platform_driver_unregister(&davinci_rproc_driver); +} +module_exit(davinci_rproc_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Davinci Remote Processor control driver"); -- 1.7.1 From ohad at wizery.com Tue Jun 21 02:18:32 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Tue, 21 Jun 2011 10:18:32 +0300 Subject: [RFC 6/8] davinci: da850: add remoteproc dsp device In-Reply-To: <1308640714-17961-1-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> Message-ID: <1308640714-17961-7-git-send-email-ohad@wizery.com> From: Mark Grosen Add davinci remoteproc device for the da850's C674x dsp remote processor, and support it on the da850-evm and omapl138-hawk boards. Signed-off-by: Mark Grosen Signed-off-by: Ohad Ben-Cohen --- It'd be nice to split this patch to the different components it changes, but it's probably not so important at this early stage. arch/arm/mach-davinci/board-da850-evm.c | 4 ++++ arch/arm/mach-davinci/board-omapl138-hawk.c | 4 ++++ arch/arm/mach-davinci/da850.c | 14 ++++++++++++++ arch/arm/mach-davinci/devices-da8xx.c | 15 +++++++++++++++ arch/arm/mach-davinci/include/mach/da8xx.h | 1 + 5 files changed, 38 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..6b35f0a 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1176,6 +1176,10 @@ static __init void da850_evm_init(void) i2c_register_board_info(1, da850_evm_i2c_devices, ARRAY_SIZE(da850_evm_i2c_devices)); + ret = da850_register_rproc(); + if (ret) + pr_warning("dsp/rproc registration failed: %d\n", ret); + /* * shut down uart 0 and 1; they are not used on the board and * accessing them causes endless "too much work in irq53" messages diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index 67c38d0..52c8434 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -319,6 +319,10 @@ static __init void omapl138_hawk_init(void) pr_warning("omapl138_hawk_init: " "watchdog registration failed: %d\n", ret); + + ret = da850_register_rproc(); + if (ret) + pr_warning("dsp/rproc registration failed: %d\n", ret); } #ifdef CONFIG_SERIAL_8250_CONSOLE diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 133aac4..9280b1e 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -74,6 +74,13 @@ static struct clk pll0_aux_clk = { .flags = CLK_PLL | PRE_PLL, }; +static struct clk pll0_sysclk1 = { + .name = "pll0_sysclk1", + .parent = &pll0_clk, + .flags = CLK_PLL, + .div_reg = PLLDIV1, +}; + static struct clk pll0_sysclk2 = { .name = "pll0_sysclk2", .parent = &pll0_clk, @@ -373,6 +380,12 @@ static struct clk spi1_clk = { .flags = DA850_CLK_ASYNC3, }; +static struct clk dsp_clk = { + .name = "dsp", + .parent = &pll0_sysclk1, + .lpsc = DA8XX_LPSC0_GEM, +}; + static struct clk_lookup da850_clks[] = { CLK(NULL, "ref", &ref_clk), CLK(NULL, "pll0", &pll0_clk), @@ -419,6 +432,7 @@ static struct clk_lookup da850_clks[] = { CLK(NULL, "usb20", &usb20_clk), CLK("spi_davinci.0", NULL, &spi0_clk), CLK("spi_davinci.1", NULL, &spi1_clk), + CLK(NULL, "dsp", &dsp_clk), CLK(NULL, NULL, NULL), }; diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index fc4e98e..3becfd1 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "clock.h" @@ -647,6 +648,20 @@ int __init da850_register_mmcsd1(struct davinci_mmc_config *config) da850_mmcsd1_device.dev.platform_data = config; return platform_device_register(&da850_mmcsd1_device); } + +int __init da850_register_rproc(void) +{ + struct platform_device *rproc; + struct davinci_rproc_pdata rproc_pdata = { + .name = "dsp", + .firmware = "davinci-dsp.bin", + .clk_name = "dsp", + }; + + rproc = platform_device_register_data(NULL, "davinci-rproc", 0, + &rproc_pdata, sizeof(rproc_pdata)); + return PTR_RET(rproc); +}; #endif static struct resource da8xx_rtc_resources[] = { diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index ad64da7..a6f024f 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -74,6 +74,7 @@ void __init da850_init(void); int da830_register_edma(struct edma_rsv_info *rsv); int da850_register_edma(struct edma_rsv_info *rsv[2]); +int da850_register_rproc(void); int da8xx_register_i2c(int instance, struct davinci_i2c_platform_data *pdata); int da8xx_register_spi(int instance, struct spi_board_info *info, unsigned len); int da8xx_register_watchdog(void); -- 1.7.1 From ohad at wizery.com Tue Jun 21 02:18:33 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Tue, 21 Jun 2011 10:18:33 +0300 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: <1308640714-17961-1-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> Message-ID: <1308640714-17961-8-git-send-email-ohad@wizery.com> Add a virtio-based IPC bus, which enables kernel users to communicate with remote processors over shared memory using a simple messaging protocol. Assign a local address for every local endpoint that is created, and bind it to the user's callback. Invoke that callback when the destination of an inbound message is the user's address. Signed-off-by: Ohad Ben-Cohen --- Documentation/ABI/testing/sysfs-bus-rpmsg | 75 +++ Documentation/rpmsg.txt | 308 +++++++++ drivers/Kconfig | 1 + drivers/Makefile | 1 + drivers/rpmsg/Kconfig | 14 + drivers/rpmsg/Makefile | 1 + drivers/rpmsg/virtio_rpmsg_bus.c | 1036 +++++++++++++++++++++++++++++ include/linux/mod_devicetable.h | 10 + include/linux/rpmsg.h | 421 ++++++++++++ include/linux/virtio_ids.h | 1 + 10 files changed, 1868 insertions(+), 0 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-rpmsg create mode 100644 Documentation/rpmsg.txt create mode 100644 drivers/rpmsg/Kconfig create mode 100644 drivers/rpmsg/Makefile create mode 100644 drivers/rpmsg/virtio_rpmsg_bus.c create mode 100644 include/linux/rpmsg.h diff --git a/Documentation/ABI/testing/sysfs-bus-rpmsg b/Documentation/ABI/testing/sysfs-bus-rpmsg new file mode 100644 index 0000000..7bddf70 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-rpmsg @@ -0,0 +1,75 @@ +What: /sys/bus/rpmsg/devices/.../name +Date: June 2011 +KernelVersion: 3.2 +Contact: Ohad Ben-Cohen +Description: + Every rpmsg device is a communication channel with a remote + processor. Channels are identified with a (textual) name, + which is maximum 32 bytes long (defined as RPMSG_NAME_SIZE in + rpmsg.h). + + This sysfs entry contains the name of this channel. + +What: /sys/bus/rpmsg/devices/.../src +Date: June 2011 +KernelVersion: 3.2 +Contact: Ohad Ben-Cohen +Description: + Every rpmsg device is a communication channel with a remote + processor. Channels have a local ("source") rpmsg address, + and remote ("destination") rpmsg address. When an entity + starts listening on one end of a channel, it assigns it with + a unique rpmsg address (a 32 bits integer). This way when + inbound messages arrive to this address, the rpmsg core + dispatches them to the listening entity (a kernel driver). + + This sysfs entry contains the src (local) rpmsg address + of this channel. If it contains 0xffffffff, then an address + wasn't assigned (can happen if no driver exists for this + channel). + +What: /sys/bus/rpmsg/devices/.../dst +Date: June 2011 +KernelVersion: 3.2 +Contact: Ohad Ben-Cohen +Description: + Every rpmsg device is a communication channel with a remote + processor. Channels have a local ("source") rpmsg address, + and remote ("destination") rpmsg address. When an entity + starts listening on one end of a channel, it assigns it with + a unique rpmsg address (a 32 bits integer). This way when + inbound messages arrive to this address, the rpmsg core + dispatches them to the listening entity. + + This sysfs entry contains the dst (remote) rpmsg address + of this channel. If it contains 0xffffffff, then an address + wasn't assigned (can happen if the kernel driver that + is attached to this channel is exposing a service to the + remote processor. This make it a local rpmsg server, + and it is listening for inbound messages that may be sent + from any remote rpmsg client; it is not bound to a single + remote entity). + +What: /sys/bus/rpmsg/devices/.../announce +Date: June 2011 +KernelVersion: 3.2 +Contact: Ohad Ben-Cohen +Description: + Every rpmsg device is a communication channel with a remote + processor. Channels are identified by a textual name (see + /sys/bus/rpmsg/devices/.../name above) and have a local + ("source") rpmsg address, and remote ("destination") rpmsg + address. + + A channel is first created when an entity, whether local + or remote, starts listening on it for messages (and is thus + called an rpmsg server). + + When that happens, a "name service" announcement is sent + to the other processor, in order to let it know about the + creation of the channel (this way remote clients know they + can start sending messages). + + This sysfs entry tells us whether the channel is a local + server channel that is announced (values are either + true or false). diff --git a/Documentation/rpmsg.txt b/Documentation/rpmsg.txt new file mode 100644 index 0000000..0a7c820 --- /dev/null +++ b/Documentation/rpmsg.txt @@ -0,0 +1,308 @@ +Remote Processor Messaging (rpmsg) Framework + +1. Introduction + +Modern SoCs typically employ heterogeneous remote processor devices in +asymmetric multiprocessing (AMP) configurations, which may be running +different instances of operating system, whether it's Linux or any other +flavor of real-time OS. + +OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP. +Typically, the dual cortex-A9 is running Linux in a SMP configuration, +and each of the other three cores (two M3 cores and a DSP) is running +its own instance of RTOS in an AMP configuration. + +Typically AMP remote processors employ dedicated DSP codecs and multimedia +hardware accelerators, and therefore are often used to offload cpu-intensive +multimedia tasks from the main application processor. + +These remote processors could also be used to control latency-sensitive +sensors, drive random hardware blocks, or just perform background tasks +while the main CPU is idling. + +Users of those remote processors can either be userland apps (e.g. multimedia +frameworks talking with remote OMX components) or kernel drivers (controlling +hardware accessible only by the remote processor, reserving kernel-controlled +resources on behalf of the remote processor, etc..). + +Rpmsg is a virtio-based messaging bus that allows kernel drivers to communicate +with remote processors available on the system. In turn, drivers could then +expose appropriate user space interfaces, if needed. + +When writing a driver that exposes rpmsg communication to userland, please +keep in mind that remote processors might have direct access to the +system's physical memory and/or other sensitive hardware resources (e.g. on +OMAP4, remote cores (/hardware accelerators) may have direct access to the +physical memory, gpio banks, dma controllers, i2c bus, gptimers, mailbox +devices, hwspinlocks, etc..). Moreover, those remote processors might be +running RTOS where every task can access the entire memory/devices exposed +to the processor. To minimize the risks of rogue (/buggy) userland code +exploiting (/triggering) remote bugs, and by that taking over (/down) the +system, it is often desired to limit userland to specific rpmsg channels (see +definition below) it is allowed to send messages on, and if possible/relevant, +minimize the amount of control it has over the content of the messages. + +Every rpmsg device is a communication channel with a remote processor (thus +rpmsg devices are called channels). Channels are identified by a textual name +and have a local ("source") rpmsg address, and remote ("destination") rpmsg +address. + +When a driver starts listening on a channel, it binds it with a unique +rpmsg src address (a 32 bits integer). This way when inbound messages arrive +to this src address, the rpmsg core dispatches them to that driver (by invoking +the driver's rx handler with the payload of the incoming message). + + +2. User API + + int rpmsg_send(struct rpmsg_channel *rpdev, void *data, int len); + - sends a message across to the remote processor on a given channel. + The caller should specify the channel, the data it wants to send, + and its length (in bytes). The message will be sent on the specified + channel, i.e. its source and destination address fields will be + set to the channel's src and dst addresses. + + In case there are no TX buffers available, the function will block until + one becomes available (i.e. until the remote processor will consume + a tx buffer and put it back on virtio's used descriptor ring), + or a timeout of 15 seconds elapses. When the latter happens, + -ERESTARTSYS is returned. + The function can only be called from a process context (for now). + Returns 0 on success and an appropriate error value on failure. + + int rpmsg_sendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst); + - sends a message across to the remote processor on a given channel, + to a destination address provided by the caller. + The caller should specify the channel, the data it wants to send, + its length (in bytes), and an explicit destination address. + The message will then be sent to the remote processor to which the + channel belongs to, using the channel's src address, and the user-provided + dst address (thus the channel's dst address will be ignored). + + In case there are no TX buffers available, the function will block until + one becomes available (i.e. until the remote processor will consume + a tx buffer and put it back on virtio's used descriptor ring), + or a timeout of 15 seconds elapses. When the latter happens, + -ERESTARTSYS is returned. + The function can only be called from a process context (for now). + Returns 0 on success and an appropriate error value on failure. + + int rpmsg_send_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst, + void *data, int len); + - sends a message across to the remote processor, using the src and dst + addresses provided by the user. + The caller should specify the channel, the data it wants to send, + its length (in bytes), and explicit source and destination addresses. + The message will then be sent to the remote processor to which the + channel belongs to, but the channel's src and dst addresses will be + ignored (and the user-provided addresses will be used instead). + + In case there are no TX buffers available, the function will block until + one becomes available (i.e. until the remote processor will consume + a tx buffer and put it back on virtio's used descriptor ring), + or a timeout of 15 seconds elapses. When the latter happens, + -ERESTARTSYS is returned. + The function can only be called from a process context (for now). + Returns 0 on success and an appropriate error value on failure. + + int rpmsg_trysend(struct rpmsg_channel *rpdev, void *data, int len); + - sends a message across to the remote processor on a given channel. + The caller should specify the channel, the data it wants to send, + and its length (in bytes). The message will be sent on the specified + channel, i.e. its source and destination address fields will be + set to the channel's src and dst addresses. + + In case there are no TX buffers available, the function will immediately + return -ENOMEM without waiting until one becomes available. + The function can only be called from a process context (for now). + Returns 0 on success and an appropriate error value on failure. + + int rpmsg_trysendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst) + - sends a message across to the remote processor on a given channel, + to a destination address provided by the user. + The user should specify the channel, the data it wants to send, + its length (in bytes), and an explicit destination address. + The message will then be sent to the remote processor to which the + channel belongs to, using the channel's src address, and the user-provided + dst address (thus the channel's dst address will be ignored). + + In case there are no TX buffers available, the function will immediately + return -ENOMEM without waiting until one becomes available. + The function can only be called from a process context (for now). + Returns 0 on success and an appropriate error value on failure. + + int rpmsg_trysend_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst, + void *data, int len); + - sends a message across to the remote processor, using source and + destination addresses provided by the user. + The user should specify the channel, the data it wants to send, + its length (in bytes), and explicit source and destination addresses. + The message will then be sent to the remote processor to which the + channel belongs to, but the channel's src and dst addresses will be + ignored (and the user-provided addresses will be used instead). + + In case there are no TX buffers available, the function will immediately + return -ENOMEM without waiting until one becomes available. + The function can only be called from a process context (for now). + Returns 0 on success and an appropriate error value on failure. + + struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev, + void (*cb)(struct rpmsg_channel *, void *, int, void *, u32), + void *priv, u32 addr); + - every rpmsg address in the system is bound to an rx callback (so when + inbound messages arrive, they are dispatched by the rpmsg bus using the + appropriate callback handler) by means of an rpmsg_endpoint struct. + + This function allows drivers to create such an endpoint, and by that, + bind a callback, and possibly some private data too, to an rpmsg address + (either one that is known in advance, or one that will be dynamically + assigned for them). + + Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint + is already created for them when they are probed by the rpmsg bus + (using the rx callback they provide when they registered to the rpmsg bus). + + So things should just work for simple drivers: they already have an + endpoint, their rx callback is bound to their rpmsg address, and when + relevant inbound messages arrive (i.e. messages which their dst address + equals to the src address of their rpmsg channel), the driver's handler + is invoked to process it. + + That said, more complicated drivers might do need to allocate + additional rpmsg addresses, and bind them to different rx callbacks. + To accomplish that, those drivers need to call this function. + Driver should provide their channel (so the new endpoint would bind + to the same remote processor their channel belongs to), an rx callback + function, an optional private data (which is provided back when the + rx callback is invoked), and an address they want to bind with the + callback. If addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will + dynamically assign them an available rpmsg address (drivers should have + a very good reason why not to always use RPMSG_ADDR_ANY here). + + Returns a pointer to the endpoint on success, or NULL on error. + + void rpmsg_destroy_ept(struct rpmsg_endpoint *ept); + - destroys an existing rpmsg endpoint. user should provide a pointer + to an rpmsg endpoint that was previously created with rpmsg_create_ept(). + + int register_rpmsg_driver(struct rpmsg_driver *rpdrv); + - registers an rpmsg driver with the rpmsg bus. user should provide + a pointer to an rpmsg_driver struct, which contains the driver's + ->probe() and ->remove() functions, an rx callback, and an id_table + specifying the names of the channels this driver is interested to + be probed with. + + void unregister_rpmsg_driver(struct rpmsg_driver *rpdrv); + - unregisters an rpmsg driver from the rpmsg bus. user should provide + a pointer to a previously-registerd rpmsg_driver struct. + Returns 0 on success, and an appropriate error value on failure. + + +3. Typical usage + +The following is a simple rpmsg driver, that sends an "hello!" message +on probe(), and whenever it receives an incoming message, it dumps its +content to the console. + +#include +#include +#include + +static void rpmsg_sample_cb(struct rpmsg_channel *rpdev, void *data, int len, + void *priv, u32 src) +{ + print_hex_dump(KERN_INFO, "incoming message:", DUMP_PREFIX_NONE, + 16, 1, data, len, true); +} + +static int rpmsg_sample_probe(struct rpmsg_channel *rpdev) +{ + int err; + + dev_info(&rpdev->dev, "chnl: 0x%x -> 0x%x\n", rpdev->src, rpdev->dst); + + /* send a message on our channel */ + err = rpmsg_send(rpdev, "hello!", 6); + if (err) { + pr_err("rpmsg_send failed: %d\n", err); + return err; + } + + return 0; +} + +static void __devexit rpmsg_sample_remove(struct rpmsg_channel *rpdev) +{ + dev_info(&rpdev->dev, "rpmsg sample client driver is removed\n"); +} + +static struct rpmsg_device_id rpmsg_driver_sample_id_table[] = { + { .name = "rpmsg-client-sample" }, + { }, +}; +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sample_id_table); + +static struct rpmsg_driver rpmsg_sample_client = { + .drv.name = KBUILD_MODNAME, + .drv.owner = THIS_MODULE, + .id_table = rpmsg_driver_sample_id_table, + .probe = rpmsg_sample_probe, + .callback = rpmsg_sample_cb, + .remove = __devexit_p(rpmsg_sample_remove), +}; + +static int __init init(void) +{ + return register_rpmsg_driver(&rpmsg_sample_client); +} + +static void __exit fini(void) +{ + unregister_rpmsg_driver(&rpmsg_sample_client); +} +module_init(init); +module_exit(fini); + + +4. API for implementors + +Adding rpmsg support for a new platform is relatively easy; one just needs +to register a VIRTIO_ID_RPMSG virtio device with the usual virtio_config_ops +handlers. + +For simplicity, it is recommended to register a single virtio device for +every physical remote processor we have in the system, but there are no +hard rules here, and this decision largely depends on the use cases, +platform capabilities, performance requirements, etc. + +OMAP4, e.g., registers two virtio devices to communicate with the remote dual +Cortex-M3 processor, because each of the M3 cores executes its own OS instance +(see omap_rpmsg.c for reference). +This way each of the remote cores may have different rpmsg channels, and the +rpmsg core will treat them as completely independent processors (despite +the fact that both of are part of the same physical device, and they are +powered on/off together). + +Notable virtio implementation bits: + +* virtio features: VIRTIO_RPMSG_F_NS should be enabled if the remote + processor supports dynamic name service announcement messages. + + Enabling this means that rpmsg device (i.e. channel) creation is + completely dynamic: the remote processor announces the existence of a + remote rpmsg service by sending a name service message (which contains + the name and rpmsg addr of the remote service, see struct rpmsg_ns_msg). + + This message is then handled by the rpmsg bus, which in turn dynamically + creates and registers an rpmsg channel (which represents the remote service). + If/when a relevant rpmsg driver is registered, it will be immediately probed + by the bus, and can then start sending messages to the remote service. + +* virtqueue's notify handler: should inform the remote processor whenever + it is kicked by virtio. OMAP4 is using its mailbox device to interrupt + the remote processor, and inform it which virtqueue number is kicked + (the index of the kicked virtqueue is written to the mailbox register). + +* virtio_config_ops's ->get() handler: the rpmsg bus uses this handler + to request for platform-specific configuration values. + see enum rpmsg_platform_requests for more info. diff --git a/drivers/Kconfig b/drivers/Kconfig index 1f6d6d3..840f835 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -128,4 +128,5 @@ source "drivers/clocksource/Kconfig" source "drivers/remoteproc/Kconfig" +source "drivers/rpmsg/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 4d53a18..2980a15 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_ARM_AMBA) += amba/ obj-$(CONFIG_DMA_ENGINE) += dma/ obj-$(CONFIG_VIRTIO) += virtio/ +obj-$(CONFIG_RPMSG) += rpmsg/ obj-$(CONFIG_XEN) += xen/ # regulators early, since some subsystems rely on them to initialize diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig new file mode 100644 index 0000000..41303f5 --- /dev/null +++ b/drivers/rpmsg/Kconfig @@ -0,0 +1,14 @@ +# RPMSG always gets selected by whoever wants it +config RPMSG + tristate + select VIRTIO + select VIRTIO_RING + +if RPMSG + +# OK, it's a little counter-intuitive to do this, but it puts it neatly under +# the rpmsg menu (and it's the approach preferred by the virtio folks). + +source "drivers/virtio/Kconfig" + +endif # RPMSG diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile new file mode 100644 index 0000000..7617fcb --- /dev/null +++ b/drivers/rpmsg/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_RPMSG) += virtio_rpmsg_bus.o diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c new file mode 100644 index 0000000..3e98b02 --- /dev/null +++ b/drivers/rpmsg/virtio_rpmsg_bus.c @@ -0,0 +1,1036 @@ +/* + * Virtio-based remote processor messaging bus + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * + * Ohad Ben-Cohen + * Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct virtproc_info - virtual remote processor state + * @vdev: the virtio device + * @rvq: rx virtqueue (from pov of local processor) + * @svq: tx virtqueue (from pov of local processor) + * @rbufs: address of rx buffers + * @sbufs: address of tx buffers + * @last_sbuf: index of last tx buffer used + * @phys_base: physical base addr of the buffers + * @tx_lock: protects svq, sbufs and sleepers, to allow concurrent senders + * @num_bufs: total number of buffers allocated for communicating with this + * virtual remote processor. half is used for rx and half for tx. + * @buf_size: size of buffers allocated for communications + * @endpoints: idr of local endpoints, allows fast retrieval + * @endpoints_lock: lock of the endpoints set + * @sendq: wait queue of sending contexts waiting for a tx buffers + * @sleepers: number of senders that are waiting for a tx buffer + * @ns_ept: the bus's name service endpoint + * + * This structure stores the rpmsg state of a given virtio remote processor + * device (there might be several virtio proc devices for each physical + * remote processor). + */ +struct virtproc_info { + struct virtio_device *vdev; + struct virtqueue *rvq, *svq; + void *rbufs, *sbufs; + int last_sbuf; + phys_addr_t phys_base; + spinlock_t tx_lock; + int num_bufs; + int buf_size; + struct idr endpoints; + spinlock_t endpoints_lock; + wait_queue_head_t sendq; + int sleepers; + struct rpmsg_endpoint *ns_ept; +}; + +#define to_rpmsg_channel(d) container_of(d, struct rpmsg_channel, dev) +#define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv) + +/* + * Local addresses are dynamically allocated on-demand. + * We do not dynamically assign addresses from the low 1024 range, + * in order to reserve that address range for predefined services. + */ +#define RPMSG_RESERVED_ADDRESSES (1024) + +/* Address 53 is reserved for advertising remote services */ +#define RPMSG_NS_ADDR (53) + +/* show configuration fields */ +#define rpmsg_show_attr(field, path, format_string) \ +static ssize_t \ +field##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); \ + \ + return sprintf(buf, format_string, rpdev->path); \ +} + +/* for more info, see Documentation/ABI/testing/sysfs-bus-rpmsg */ +rpmsg_show_attr(name, id.name, "%s\n"); +rpmsg_show_attr(src, src, "0x%x\n"); +rpmsg_show_attr(dst, dst, "0x%x\n"); +rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n"); + +/* unique (free running) index for rpmsg devices */ +static unsigned int rpmsg_dev_index; + +static ssize_t modalias_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + + return sprintf(buf, RPMSG_DEVICE_MODALIAS_FMT "\n", rpdev->id.name); +} + +static struct device_attribute rpmsg_dev_attrs[] = { + __ATTR_RO(name), + __ATTR_RO(modalias), + __ATTR_RO(dst), + __ATTR_RO(src), + __ATTR_RO(announce), + __ATTR_NULL +}; + +/* rpmsg devices and drivers are matched using the service name */ +static inline int rpmsg_id_match(const struct rpmsg_channel *rpdev, + const struct rpmsg_device_id *id) +{ + if (strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE)) + return 0; + + return 1; +} + +/* match rpmsg channel and rpmsg driver */ +static int rpmsg_dev_match(struct device *dev, struct device_driver *drv) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv); + const struct rpmsg_device_id *ids = rpdrv->id_table; + unsigned int i; + + for (i = 0; ids[i].name[0]; i++) { + if (rpmsg_id_match(rpdev, &ids[i])) + return 1; + } + + return 0; +} + +static int rpmsg_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + + return add_uevent_var(env, "MODALIAS=" RPMSG_DEVICE_MODALIAS_FMT, + rpdev->id.name); +} + +/* for more info, see below documentation of rpmsg_create_ept() */ +static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, + struct rpmsg_channel *rpdev, + void (*cb)(struct rpmsg_channel *, void *, int, void *, u32), + void *priv, u32 addr) +{ + int err, tmpaddr, request; + struct rpmsg_endpoint *ept; + struct device *dev = rpdev ? &rpdev->dev : &vrp->vdev->dev; + + if (!idr_pre_get(&vrp->endpoints, GFP_KERNEL)) + return NULL; + + ept = kzalloc(sizeof(*ept), GFP_KERNEL); + if (!ept) { + dev_err(dev, "failed to kzalloc a new ept\n"); + return NULL; + } + + ept->rpdev = rpdev; + ept->cb = cb; + ept->priv = priv; + + /* do we need to allocate a local address ? */ + request = addr == RPMSG_ADDR_ANY ? RPMSG_RESERVED_ADDRESSES : addr; + + spin_lock(&vrp->endpoints_lock); + + /* bind the endpoint to an rpmsg address (and allocate one if needed) */ + err = idr_get_new_above(&vrp->endpoints, ept, request, &tmpaddr); + if (err) { + dev_err(dev, "idr_get_new_above failed: %d\n", err); + goto free_ept; + } + + /* make sure the user's address request is fulfilled, if relevant */ + if (addr != RPMSG_ADDR_ANY && tmpaddr != addr) { + dev_err(dev, "address 0x%x already in use\n", addr); + goto rem_idr; + } + + ept->addr = tmpaddr; + + spin_unlock(&vrp->endpoints_lock); + + return ept; + +rem_idr: + idr_remove(&vrp->endpoints, request); +free_ept: + spin_unlock(&vrp->endpoints_lock); + kfree(ept); + return NULL; +} + +/** + * rpmsg_create_ept() - create a new rpmsg_endpoint + * @rpdev: rpmsg channel device + * @cb: rx callback handler + * @priv: private data for the driver's use + * @addr: local rpmsg address to bind with @cb + * + * Every rpmsg address in the system is bound to an rx callback (so when + * inbound messages arrive, they are dispatched by the rpmsg bus using the + * appropriate callback handler) by means of an rpmsg_endpoint struct. + * + * This function allows drivers to create such an endpoint, and by that, + * bind a callback, and possibly some private data too, to an rpmsg address + * (either one that is known in advance, or one that will be dynamically + * assigned for them). + * + * Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint + * is already created for them when they are probed by the rpmsg bus + * (using the rx callback provided when they registered to the rpmsg bus). + * + * So things should just work for simple drivers: they already have an + * endpoint, their rx callback is bound to their rpmsg address, and when + * relevant inbound messages arrive (i.e. messages which their dst address + * equals to the src address of their rpmsg channel), the driver's handler + * is invoked to process it. + * + * That said, more complicated drivers might do need to allocate + * additional rpmsg addresses, and bind them to different rx callbacks. + * To accomplish that, those drivers need to call this function. + * + * Drivers should provide their @rpdev channel (so the new endpoint would belong + * to the same remote processor their channel belongs to), an rx callback + * function, an optional private data (which is provided back when the + * rx callback is invoked), and an address they want to bind with the + * callback. If @addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will + * dynamically assign them an available rpmsg address (drivers should have + * a very good reason why not to always use RPMSG_ADDR_ANY here). + * + * Returns a pointer to the endpoint on success, or NULL on error. + */ +struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev, + void (*cb)(struct rpmsg_channel *, void *, int, void *, u32), + void *priv, u32 addr) +{ + return __rpmsg_create_ept(rpdev->vrp, rpdev, cb, priv, addr); +} +EXPORT_SYMBOL(rpmsg_create_ept); + +/** + * rpmsg_destroy_ept() - destroy an existing rpmsg endpoint + * @ept: endpoing to destroy + * + * Should be used by drivers to destroy an rpmsg endpoint previously + * created with rpmsg_create_ept(). + */ +void rpmsg_destroy_ept(struct rpmsg_endpoint *ept) +{ + struct virtproc_info *vrp = ept->rpdev->vrp; + + spin_lock(&vrp->endpoints_lock); + idr_remove(&vrp->endpoints, ept->addr); + spin_unlock(&vrp->endpoints_lock); + + kfree(ept); +} +EXPORT_SYMBOL(rpmsg_destroy_ept); + +/* + * when an rpmsg driver is probed with a channel, we seamlessly create + * it an endpoint, binding its rx callback to a unique local rpmsg + * address. + * + * if we need to, we also announce about this channel to the remote + * processor (needed in case the driver is exposing an rpmsg service). + */ +static int rpmsg_dev_probe(struct device *dev) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); + struct virtproc_info *vrp = rpdev->vrp; + struct rpmsg_endpoint *ept; + int err; + + ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, rpdev->src); + if (!ept) { + dev_err(dev, "failed to create endpoint\n"); + err = -ENOMEM; + goto out; + } + + rpdev->ept = ept; + rpdev->src = ept->addr; + + err = rpdrv->probe(rpdev); + if (err) { + dev_err(dev, "%s: failed: %d\n", __func__, err); + rpmsg_destroy_ept(ept); + goto out; + } + + /* need to tell remote processor's name service about this channel ? */ + if (rpdev->announce && + virtio_has_feature(vrp->vdev, VIRTIO_RPMSG_F_NS)) { + struct rpmsg_ns_msg nsm; + + strncpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE); + nsm.addr = rpdev->src; + nsm.flags = RPMSG_NS_CREATE; + + err = rpmsg_sendto(rpdev, &nsm, sizeof(nsm), RPMSG_NS_ADDR); + if (err) + dev_err(dev, "failed to announce service %d\n", err); + } + +out: + return err; +} + +static int rpmsg_dev_remove(struct device *dev) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver); + struct virtproc_info *vrp = rpdev->vrp; + int err = 0; + + /* tell remote processor's name service we're removing this channel */ + if (rpdev->announce && + virtio_has_feature(vrp->vdev, VIRTIO_RPMSG_F_NS)) { + struct rpmsg_ns_msg nsm; + + strncpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE); + nsm.addr = rpdev->src; + nsm.flags = RPMSG_NS_DESTROY; + + err = rpmsg_sendto(rpdev, &nsm, sizeof(nsm), RPMSG_NS_ADDR); + if (err) + dev_err(dev, "failed to announce service %d\n", err); + } + + rpdrv->remove(rpdev); + + rpmsg_destroy_ept(rpdev->ept); + + return err; +} + +static struct bus_type rpmsg_bus = { + .name = "rpmsg", + .match = rpmsg_dev_match, + .dev_attrs = rpmsg_dev_attrs, + .uevent = rpmsg_uevent, + .probe = rpmsg_dev_probe, + .remove = rpmsg_dev_remove, +}; + +/** + * register_rpmsg_driver() - register an rpmsg driver with the rpmsg bus + * @rpdrv: pointer to a struct rpmsg_driver + * + * Returns 0 on success, and an appropriate error value on failure. + */ +int register_rpmsg_driver(struct rpmsg_driver *rpdrv) +{ + rpdrv->drv.bus = &rpmsg_bus; + return driver_register(&rpdrv->drv); +} +EXPORT_SYMBOL(register_rpmsg_driver); + +/** + * unregister_rpmsg_driver() - unregister an rpmsg driver from the rpmsg bus + * @rpdrv: pointer to a struct rpmsg_driver + * + * Returns 0 on success, and an appropriate error value on failure. + */ +void unregister_rpmsg_driver(struct rpmsg_driver *rpdrv) +{ + driver_unregister(&rpdrv->drv); +} +EXPORT_SYMBOL(unregister_rpmsg_driver); + +static void rpmsg_release_device(struct device *dev) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + + kfree(rpdev); +} + +/* + * match an rpmsg channel with a channel info struct. + * this is used to make sure we're not creating rpmsg devices for channels + * that already exist. + */ +static int rpmsg_channel_match(struct device *dev, void *data) +{ + struct rpmsg_channel_info *chinfo = data; + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + + if (chinfo->src != RPMSG_ADDR_ANY && chinfo->src != rpdev->src) + return 0; + + if (chinfo->dst != RPMSG_ADDR_ANY && chinfo->dst != rpdev->dst) + return 0; + + if (strncmp(chinfo->name, rpdev->id.name, RPMSG_NAME_SIZE)) + return 0; + + /* found a match ! */ + return 1; +} + +/* + * create an rpmsg channel using its name and address info. + * this function will be used to create both static and dynamic + * channels. + */ +static struct rpmsg_channel *rpmsg_create_channel(struct virtproc_info *vrp, + struct rpmsg_channel_info *chinfo) +{ + struct rpmsg_channel *rpdev; + struct device *tmp, *dev = &vrp->vdev->dev; + int ret; + + /* make sure a similar channel doesn't already exist */ + tmp = device_find_child(dev, chinfo, rpmsg_channel_match); + if (tmp) { + /* decrement the matched device's refcount back */ + put_device(tmp); + dev_err(dev, "channel %s:%x:%x already exist\n", + chinfo->name, chinfo->src, chinfo->dst); + return NULL; + } + + rpdev = kzalloc(sizeof(struct rpmsg_channel), GFP_KERNEL); + if (!rpdev) { + pr_err("kzalloc failed\n"); + return NULL; + } + + rpdev->vrp = vrp; + rpdev->src = chinfo->src; + rpdev->dst = chinfo->dst; + + /* + * rpmsg server channels has predefined local address (for now), + * and their existence needs to be announced remotely + */ + rpdev->announce = rpdev->src != RPMSG_ADDR_ANY ? true : false; + + strncpy(rpdev->id.name, chinfo->name, RPMSG_NAME_SIZE); + + /* very simple device indexing plumbing which is enough for now */ + dev_set_name(&rpdev->dev, "rpmsg%d", rpmsg_dev_index++); + + rpdev->dev.parent = &vrp->vdev->dev; + rpdev->dev.bus = &rpmsg_bus; + rpdev->dev.release = rpmsg_release_device; + + ret = device_register(&rpdev->dev); + if (ret) { + dev_err(dev, "device_register failed: %d\n", ret); + kfree(rpdev); + return NULL; + } + + return rpdev; +} + +/* + * find an existing channel using its name + address properties, + * and destroy it + */ +static int rpmsg_destroy_channel(struct virtproc_info *vrp, + struct rpmsg_channel_info *chinfo) +{ + struct virtio_device *vdev = vrp->vdev; + struct device *dev; + + dev = device_find_child(&vdev->dev, chinfo, rpmsg_channel_match); + if (!dev) + return -EINVAL; + + device_unregister(dev); + + put_device(dev); + + kfree(to_rpmsg_channel(dev)); + + return 0; +} + +/* super simple buffer "allocator" that is just enough for now */ +static void *get_a_tx_buf(struct virtproc_info *vrp) +{ + unsigned int len; + void *ret; + + /* support multiple concurrent senders */ + spin_lock(&vrp->tx_lock); + + /* + * either pick the next unused tx buffer + * (half of our buffers are used for sending messages) + */ + if (vrp->last_sbuf < vrp->num_bufs / 2) + ret = vrp->sbufs + vrp->buf_size * vrp->last_sbuf++; + /* or recycle a used one */ + else + ret = virtqueue_get_buf(vrp->svq, &len); + + spin_unlock(&vrp->tx_lock); + + return ret; +} + +/** + * rpmsg_upref_sleepers() - enable "tx-complete" interrupts, if needed + * @vrp: virtual remote processor state + * + * This function is called before a sender is blocked, waiting for + * a tx buffer to become available. + * + * If we already have blocking senders, this function merely increases + * the "sleepers" reference count, and exits. + * + * Otherwise, if this is the first sender to block, we also enable + * virtio's tx callbacks, so we'd be immediately notified when a tx + * buffer is consumed (we rely on virtio's tx callback in order + * to wake up sleeping senders as soon as a tx buffer is used by the + * remote processor). + */ +static void rpmsg_upref_sleepers(struct virtproc_info *vrp) +{ + /* support multiple concurrent senders */ + spin_lock(&vrp->tx_lock); + + /* are we the first sleeping context waiting for tx buffers ? */ + if (!vrp->sleepers++) + /* enable "tx-complete" interrupts before dozing off */ + virtqueue_enable_cb(vrp->svq); + + spin_unlock(&vrp->tx_lock); +} + +/** + * rpmsg_downref_sleepers() - disable "tx-complete" interrupts, if needed + * @vrp: virtual remote processor state + * + * This function is called after a sender, that waited for a tx buffer + * to become available, is unblocked. + * + * If we still have blocking senders, this function merely decreases + * the "sleepers" reference count, and exits. + * + * Otherwise, if there are no more blocking senders, we also disable + * virtio's tx callbacks, to avoid the overhead incurred with handling + * those (now redundant) interrupts. + */ +static void rpmsg_downref_sleepers(struct virtproc_info *vrp) +{ + /* support multiple concurrent senders */ + spin_lock(&vrp->tx_lock); + + /* are we the last sleeping context waiting for tx buffers ? */ + if (!--vrp->sleepers) + /* disable "tx-complete" interrupts */ + virtqueue_disable_cb(vrp->svq); + + spin_unlock(&vrp->tx_lock); +} + +/** + * rpmsg_send_offchannel_raw() - send a message across to the remote processor + * @rpdev: the rpmsg channel + * @src: source address + * @dst: destination address + * @data: payload of message + * @len: length of payload + * @wait: indicates whether caller should block in case no TX buffers available + * + * This function is the base implementation for all of the rpmsg sending API. + * + * It will send @data of length @len to @dst, and say it's from @src. The + * message will be sent to the remote processor which the @rpdev channel + * belongs to. + * + * The message is sent using one of the TX buffers that are available for + * communication with this remote processor. + * + * If @wait is true, the caller will be blocked until either a TX buffer is + * available, or 15 seconds elapses (we don't want callers to + * sleep indefinitely due to misbehaving remote processors), and in that + * case -ERESTARTSYS is returned. The number '15' itself was picked + * arbitrarily; there's little point in asking drivers to provide a timeout + * value themselves. + * + * Otherwise, if @wait is false, and there are no TX buffers available, + * the function will immediately fail, and -ENOMEM will be returned. + * + * Normally drivers shouldn't use this function directly; instead, drivers + * should use the appropriate rpmsg_{try}send{to, _offchannel} API + * (see include/linux/rpmsg.h). + * + * Returns 0 on success and an appropriate error value on failure. + */ +int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst, + void *data, int len, bool wait) +{ + struct virtproc_info *vrp = rpdev->vrp; + struct device *dev = &rpdev->dev; + struct scatterlist sg; + struct rpmsg_hdr *msg; + int err; + unsigned long offset; + phys_addr_t phys_addr; + + if (src == RPMSG_ADDR_ANY || dst == RPMSG_ADDR_ANY) { + dev_err(dev, "invalid addr (src 0x%x, dst 0x%x)\n", src, dst); + return -EINVAL; + } + + /* + * The size of the payload is currently limited according to the + * buffers size picked by the platform. + * + * One of the possible improvements here is either to support + * user-provided buffers (and then we can also support zero-copy + * messaging), or to improve the buffer allocator, to support + * variable-length buffer sizes. + */ + if (len > vrp->buf_size - sizeof(struct rpmsg_hdr)) { + dev_err(dev, "message is too big (%d)\n", len); + return -EMSGSIZE; + } + + /* grab a buffer */ + msg = get_a_tx_buf(vrp); + if (!msg && !wait) + return -ENOMEM; + + /* no free buffer ? wait for one (but bail after 15 seconds) */ + while (!msg) { + /* enable "tx-complete" interrupts, if not already enabled */ + rpmsg_upref_sleepers(vrp); + + /* + * sleep until a free buffer is available or 15 secs elapse. + * the timeout period is not configurable because there's + * little point in asking drivers to specify that. + * if later this happens to be required, it'd be easy to add. + */ + err = wait_event_interruptible_timeout(vrp->sendq, + (msg = get_a_tx_buf(vrp)), + msecs_to_jiffies(15000)); + + /* disable "tx-complete" interrupts if we're the last sleeper */ + rpmsg_downref_sleepers(vrp); + + /* timeout ? */ + if (!err) { + dev_err(dev, "timeout waiting for a tx buffer\n"); + return -ERESTARTSYS; + } + } + + msg->len = len; + msg->flags = 0; + msg->src = src; + msg->dst = dst; + msg->reserved = 0; + memcpy(msg->data, data, len); + + dev_dbg(dev, "TX From 0x%x, To 0x%x, Len %d, Flags %d, Reserved %d\n", + msg->src, msg->dst, msg->len, + msg->flags, msg->reserved); + print_hex_dump(KERN_DEBUG, "rpmsg_virtio TX: ", DUMP_PREFIX_NONE, 16, 1, + msg, sizeof(*msg) + msg->len, true); + + offset = ((unsigned long) msg) - ((unsigned long) vrp->rbufs); + + phys_addr = vrp->phys_base + offset; + + sg_init_table(&sg, 1); + + /* + * can't use sg_set_buf because buffers might not be residing + * in virt_to_page()-able memory. + * revisit this to achieve a cleaner solution (e.g. DMA API). + */ + sg_set_page(&sg, phys_to_page(phys_addr), sizeof(*msg) + len, + offset_in_page(phys_addr)); + + spin_lock(&vrp->tx_lock); + + /* add message to the remote processor's virtqueue */ + err = virtqueue_add_buf_gfp(vrp->svq, &sg, 1, 0, msg, GFP_KERNEL); + if (err < 0) { + /* + * need to reclaim the buffer here, otherwise it's lost + * (memory won't leak, but rpmsg won't use it again for TX). + * this will wait for a buffer management overhaul. + */ + dev_err(dev, "virtqueue_add_buf_gfp failed: %d\n", err); + goto out; + } + + /* tell the remote processor it has a pending message to read */ + virtqueue_kick(vrp->svq); + + err = 0; +out: + spin_unlock(&vrp->tx_lock); + return err; +} +EXPORT_SYMBOL(rpmsg_send_offchannel_raw); + +/* called when an rx buffer is used, and it's time to digest a message */ +static void rpmsg_recv_done(struct virtqueue *rvq) +{ + struct rpmsg_hdr *msg; + unsigned int len; + struct rpmsg_endpoint *ept; + struct scatterlist sg; + unsigned long offset; + phys_addr_t phys_addr; + struct virtproc_info *vrp = rvq->vdev->priv; + struct device *dev = &rvq->vdev->dev; + int err; + + msg = virtqueue_get_buf(rvq, &len); + if (!msg) { + dev_err(dev, "uhm, incoming signal, but no used buffer ?\n"); + return; + } + + dev_dbg(dev, "From: 0x%x, To: 0x%x, Len: %d, Flags: %d, Reserved: %d\n", + msg->src, msg->dst, msg->len, + msg->flags, msg->reserved); + print_hex_dump(KERN_DEBUG, "rpmsg_virtio RX: ", DUMP_PREFIX_NONE, 16, 1, + msg, sizeof(*msg) + msg->len, true); + + /* use the dst addr to fetch the callback of the appropriate user */ + spin_lock(&vrp->endpoints_lock); + ept = idr_find(&vrp->endpoints, msg->dst); + spin_unlock(&vrp->endpoints_lock); + + if (ept && ept->cb) + ept->cb(ept->rpdev, msg->data, msg->len, ept->priv, msg->src); + else + dev_warn(dev, "msg received with no recepient\n"); + + /* add the buffer back to the remote processor's virtqueue */ + offset = ((unsigned long) msg) - ((unsigned long) vrp->rbufs); + phys_addr = vrp->phys_base + offset; + + sg_init_table(&sg, 1); + + /* + * can't use sg_set_buf because buffers might not be residing + * in virt_to_page()-able memory. + * revisit this to achieve a cleaner solution. + */ + sg_set_page(&sg, phys_to_page(phys_addr), sizeof(*msg) + len, + offset_in_page(phys_addr)); + + err = virtqueue_add_buf_gfp(vrp->rvq, &sg, 0, 1, msg, GFP_KERNEL); + if (err < 0) { + dev_err(dev, "failed to add a virtqueue buffer: %d\n", err); + return; + } + + /* tell the remote processor we added another available rx buffer */ + virtqueue_kick(vrp->rvq); +} + +/* + * This is invoked whenever the remote processor completed processing + * a TX msg we just sent it, and the buffer is put back to the used ring. + * + * Normally, though, we suppress this "tx complete" interrupt in order to + * avoid the incurred overhead. + */ +static void rpmsg_xmit_done(struct virtqueue *svq) +{ + struct virtproc_info *vrp = svq->vdev->priv; + + dev_dbg(&svq->vdev->dev, "%s\n", __func__); + + /* wake up potential senders that are waiting for a tx buffer */ + wake_up_interruptible(&vrp->sendq); +} + +/* invoked when a name service announcement arrives */ +static void rpmsg_ns_cb(struct rpmsg_channel *rpdev, void *data, int len, + void *priv, u32 src) +{ + struct rpmsg_ns_msg *msg = data; + struct rpmsg_channel *newch; + struct rpmsg_channel_info chinfo; + struct virtproc_info *vrp = priv; + struct device *dev = &vrp->vdev->dev; + int ret; + + print_hex_dump(KERN_DEBUG, "NS announcement: ", + DUMP_PREFIX_NONE, 16, 1, + data, len, true); + + if (len != sizeof(*msg)) { + dev_err(dev, "malformed ns msg (%d)\n", len); + return; + } + + /* + * the name service ept does _not_ belong to a real rpmsg channel, + * and is handled by the rpmsg bus itself. + * for sanity reasons, make sure a valid rpdev has _not_ sneaked + * in somehow. + */ + if (rpdev) { + dev_err(dev, "anomaly: ns ept has an rpdev handle\n"); + return; + } + + /* don't trust the remote processor for null terminating the name */ + msg->name[RPMSG_NAME_SIZE - 1] = '\0'; + + dev_info(dev, "%sing channel %s addr 0x%x\n", + msg->flags & RPMSG_NS_DESTROY ? "destroy" : "creat", + msg->name, msg->addr); + + strncpy(chinfo.name, msg->name, sizeof(chinfo.name)); + chinfo.src = RPMSG_ADDR_ANY; + chinfo.dst = msg->addr; + + if (msg->flags & RPMSG_NS_DESTROY) { + ret = rpmsg_destroy_channel(vrp, &chinfo); + if (ret) + dev_err(dev, "rpmsg_destroy_channel failed: %d\n", ret); + } else { + newch = rpmsg_create_channel(vrp, &chinfo); + if (!newch) + dev_err(dev, "rpmsg_create_channel failed\n"); + } +} + +static int rpmsg_probe(struct virtio_device *vdev) +{ + vq_callback_t *vq_cbs[] = { rpmsg_recv_done, rpmsg_xmit_done }; + const char *names[] = { "input", "output" }; + struct virtqueue *vqs[2]; + struct virtproc_info *vrp; + void *addr; + int err, i, num_bufs, buf_size, total_buf_size; + struct rpmsg_channel_info *ch; + + vrp = kzalloc(sizeof(*vrp), GFP_KERNEL); + if (!vrp) + return -ENOMEM; + + vrp->vdev = vdev; + + idr_init(&vrp->endpoints); + spin_lock_init(&vrp->endpoints_lock); + spin_lock_init(&vrp->tx_lock); + init_waitqueue_head(&vrp->sendq); + + /* We expect two virtqueues, rx and tx (and in this order) */ + err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, names); + if (err) + goto free_vi; + + vrp->rvq = vqs[0]; + vrp->svq = vqs[1]; + + /* Platform must supply pre-allocated uncached buffers for now */ + vdev->config->get(vdev, VPROC_BUF_ADDR, &addr, sizeof(addr)); + vdev->config->get(vdev, VPROC_BUF_NUM, &num_bufs, sizeof(num_bufs)); + vdev->config->get(vdev, VPROC_BUF_SZ, &buf_size, sizeof(buf_size)); + vdev->config->get(vdev, VPROC_BUF_PADDR, &vrp->phys_base, + sizeof(vrp->phys_base)); + + total_buf_size = num_bufs * buf_size; + + dev_dbg(&vdev->dev, "%d buffers, size %d, addr 0x%x, total 0x%x\n", + num_bufs, buf_size, (unsigned int) addr, total_buf_size); + + vrp->num_bufs = num_bufs; + vrp->buf_size = buf_size; + + /* the first half of the buffers is dedicated for RX */ + vrp->rbufs = addr; + + /* the second half of the buffers is dedicated for TX */ + vrp->sbufs = addr + total_buf_size / 2; + + /* set up the receive buffers */ + for (i = 0; i < num_bufs / 2; i++) { + struct scatterlist sg; + void *cpu_addr = vrp->rbufs + i * buf_size; + phys_addr_t phys_addr = vrp->phys_base + i * buf_size; + + sg_init_table(&sg, 1); + + /* + * can't use sg_set_buf because buffers might not be residing + * in virt_to_page()-able memory. + * revisit this to achieve a cleaner solution. + */ + sg_set_page(&sg, phys_to_page(phys_addr), buf_size, + offset_in_page(phys_addr)); + + err = virtqueue_add_buf_gfp(vrp->rvq, &sg, 0, 1, cpu_addr, + GFP_KERNEL); + WARN_ON(err < 0); /* sanity check; this can't really happen */ + } + + /* suppress "tx-complete" interrupts */ + virtqueue_disable_cb(vrp->svq); + + vdev->priv = vrp; + + /* if supported by the remote processor, enable the name service */ + if (virtio_has_feature(vdev, VIRTIO_RPMSG_F_NS)) { + /* a dedicated endpoint handles the name service msgs */ + vrp->ns_ept = __rpmsg_create_ept(vrp, NULL, rpmsg_ns_cb, + vrp, RPMSG_NS_ADDR); + if (!vrp->ns_ept) { + dev_err(&vdev->dev, "failed to create the ns ept\n"); + err = -ENOMEM; + goto vqs_del; + } + } + + /* tell the remote processor it can start sending messages */ + virtqueue_kick(vrp->rvq); + + /* do we have a platform-specific static channels table ? */ + vdev->config->get(vdev, VPROC_STATIC_CHANNELS, &ch, sizeof(ch)); + + /* if we do, create the appropriate channels */ + for (i = 0; ch && ch[i].name[0]; i++) + rpmsg_create_channel(vrp, &ch[i]); + + dev_info(&vdev->dev, "rpmsg backend virtproc probed successfully\n"); + + return 0; + +vqs_del: + vdev->config->del_vqs(vrp->vdev); +free_vi: + kfree(vrp); + return err; +} + +static int rpmsg_remove_device(struct device *dev, void *data) +{ + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); + + device_unregister(dev); + + kfree(rpdev); + + return 0; +} + +static void __devexit rpmsg_remove(struct virtio_device *vdev) +{ + struct virtproc_info *vrp = vdev->priv; + int ret; + + ret = device_for_each_child(&vdev->dev, NULL, rpmsg_remove_device); + if (ret) + dev_warn(&vdev->dev, "can't remove rpmsg device: %d\n", ret); + + idr_remove_all(&vrp->endpoints); + idr_destroy(&vrp->endpoints); + + vdev->config->del_vqs(vrp->vdev); + + kfree(vrp); +} + +static struct virtio_device_id id_table[] = { + { VIRTIO_ID_RPMSG, VIRTIO_DEV_ANY_ID }, + { 0 }, +}; + +static unsigned int features[] = { + VIRTIO_RPMSG_F_NS, +}; + +static struct virtio_driver virtio_ipc_driver = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), + .driver.name = KBUILD_MODNAME, + .driver.owner = THIS_MODULE, + .id_table = id_table, + .probe = rpmsg_probe, + .remove = __devexit_p(rpmsg_remove), +}; + +static int __init init(void) +{ + int ret; + + ret = bus_register(&rpmsg_bus); + if (ret) { + pr_err("failed to register rpmsg bus: %d\n", ret); + return ret; + } + + return register_virtio_driver(&virtio_ipc_driver); +} +module_init(init); + +static void __exit fini(void) +{ + unregister_virtio_driver(&virtio_ipc_driver); + bus_unregister(&rpmsg_bus); +} +module_exit(fini); + +MODULE_DEVICE_TABLE(virtio, id_table); +MODULE_DESCRIPTION("Virtio-based remote processor messaging bus"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index ae28e93..561567e 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -533,4 +533,14 @@ struct isapnp_device_id { kernel_ulong_t driver_data; /* data private to the driver */ }; +/* rpmsg */ + +#define RPMSG_NAME_SIZE 32 +#define RPMSG_DEVICE_MODALIAS_FMT "rpmsg:%s" + +struct rpmsg_device_id { + char name[RPMSG_NAME_SIZE]; + kernel_ulong_t driver_data /* Data private to the driver */ + __attribute__((aligned(sizeof(kernel_ulong_t)))); +}; #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h new file mode 100644 index 0000000..73f9231 --- /dev/null +++ b/include/linux/rpmsg.h @@ -0,0 +1,421 @@ +/* + * Remote processor messaging + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Texas Instruments nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LINUX_RPMSG_H +#define _LINUX_RPMSG_H + +#include +#include +#include + +/* The feature bitmap for virtio rpmsg */ +#define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */ + +/** + * struct rpmsg_hdr - common header for all rpmsg messages + * @src: source address + * @dst: destination address + * @reserved: reserved for future use + * @len: length of payload (in bytes) + * @flags: message flags + * @data: @len bytes of message payload data + * + * Every message sent(/received) on the rpmsg bus begins with this header. + */ +struct rpmsg_hdr { + u32 src; + u32 dst; + u32 reserved; + u16 len; + u16 flags; + u8 data[0]; +} __packed; + +/** + * struct rpmsg_ns_msg - dynamic name service announcement message + * @name: name of remote service that is published + * @addr: address of remote service that is published + * @flags: indicates whether service is created or destroyed + * + * This message is sent across to publish a new service (or announce + * about its removal). When Linux receives these messages, an appropriate + * rpmsg channel (i.e device) is created/destroyed. In turn, the ->probe() + * or ->remove() handler of the appropriate rpmsg driver will be invoked + * (if/as-soon-as one is registered). + */ +struct rpmsg_ns_msg { + char name[RPMSG_NAME_SIZE]; + u32 addr; + u32 flags; +} __packed; + +/** + * struct rpmsg_ns_flags - dynamic name service announcement flags + * + * @RPMSG_NS_CREATE: a new remote service was just created + * @RPMSG_NS_DESTROY: a known remote service was just destroyed + */ +enum rpmsg_ns_flags { + RPMSG_NS_CREATE = 0, + RPMSG_NS_DESTROY = 1, +}; + +/** + * enum rpmsg_platform_requests - platform-specific rpmsg configuration requests + * + * These configuration requests are required by the rpmsg bus, and must be + * implemented by the platform-specific rpmsg backend. + * + * @VPROC_BUF_ADDR: Kernel virtual address of an uncached memory region, + * shared with the remote processor, that will be splitted + * to buffers and then used to send messages across to the + * remote processor. Those buffers will be added to the + * appropriate vrings by the rpmsg bus in order to send and + * receive messages. + * + * @VPROC_BUF_PADDR: Physical address of the above memory region, needed + * for the "wire" protocol between the two processors + * (i.e. for the vring's buffer descriptors). + * + * @VPROC_BUF_NUM: Number of buffers to split the shared memory region to + * @VPROC_BUF_SZ: Size of each buffer that the shared memory region will be + * split into. It is the responsibility of the underlying + * implementation to make sure that the size of the memory + * region provided by @VPROC_BUF_ADDR is exactly + * @VPROC_BUF_NUM * @VPROC_BUF_SZ bytes. + * + * @VPROC_STATIC_CHANNELS: Table of static channels that this platform + * expects to have. See struct rpmsg_channel_info + * for additional information. + * This configuration is optional: it is perfectly + * fine not to have any pre-configured static channels. + * + * The number and size of buffers to use are considered platform-specific, + * because this is strongly tied with the performance/functionality + * requirements of the specific use cases that the platform needs rpmsg + * for. This should be revisited when we'll have a bigger requirement + * picture. + * + * We might also want to add support for user-provided buffers. This will + * allow bigger buffer size flexibility, and might also be used to achieve + * zero-copy messaging. + */ +enum rpmsg_platform_requests { + VPROC_BUF_ADDR, + VPROC_BUF_PADDR, + VPROC_BUF_NUM, + VPROC_BUF_SZ, + VPROC_STATIC_CHANNELS, +}; + +#define RPMSG_ADDR_ANY 0xFFFFFFFF + +struct virtproc_info; + +/** + * rpmsg_channel - the rpmsg bus devices are called channels + * @vrp: the remote processor this channel belongs to + * @dev: the device struct + * @id: device id (used to match between rpmsg drivers and devices) + * @src: local address + * @dst: destination address + * @ept: the rpmsg endpoint of this channel + * @announce: if set, rpmsg will announce the creation/removal of this channel + */ +struct rpmsg_channel { + struct virtproc_info *vrp; + struct device dev; + struct rpmsg_device_id id; + u32 src; + u32 dst; + struct rpmsg_endpoint *ept; + bool announce; +}; + +/** + * struct rpmsg_channel_info - static channel info + * @name: name of service + * @src: local address + * @dst: destination address + * + * This struct is used to define static channel information, namely name + * and addresses, which is used by platform-specific code to create static + * channels (i.e. channels that always exists). + * + * Use cases of static channels are: + * + * 1. In case the remote processor does not support dynamic name service + * announcements (i.e. remote channel creation/removal). + * In this case, we predefine a static channel table which contains the + * remote rpmsg services supported by the remote processor. The rpmsg + * bus then iterates through this table, and creates rpmsg channels + * (i.e. devices) accordingly. @dst will contain the rpmsg address + * of the remote service, and @src will most likely contain RPMSG_ADDR_ANY. + * + * 2. To define channels which will be probed with local rpmsg "server" + * driver (i.e. drivers that expose a service). In this case, @src will + * contain the local rpmsg address of the service (but this can made + * dynamic too, if remote processor supports listening to our own dynamic + * name service announcements), and @dst will most likely be RPMSG_ADDR_ANY. + * + * Use the RPMSG_REMOTE_CHNL and RMSG_SERVER_CHNL macros (see below) + * to populate the static channels table for the above two use cases. + */ +struct rpmsg_channel_info { + char name[RPMSG_NAME_SIZE]; + u32 src; + u32 dst; +}; + +/* + * rpmsg server channels have a local predefined address, and accept + * any remote address + */ +#define RMSG_SERVER_CHNL(name, addr) { name, addr, RPMSG_ADDR_ANY } + +/* + * rpmsg remote service channels have a remote predefined address, and + * don't care about the local address + */ +#define RMSG_REMOTE_CHNL(name, addr) { name, RPMSG_ADDR_ANY, addr } + +/** + * struct rpmsg_endpoint - binds a local rpmsg address to its user + * @rpdev: rpmsg channel device + * @cb: rx callback handler + * @addr: local rpmsg address + * @priv: private data for the driver's use + * + * In essence, an rpmsg endpoint represents a listener on the rpmsg bus, as + * it binds together an rpmsg address with an rx callback handler. + * + * Simple rpmsg drivers shouldn't be aware of this detail, because + * things just work: every rpmsg driver provides an rx callback upon + * registering to the bus, and that callback is then bound to its rpmsg + * address when the driver is probed. When relevant inbound messages arrive + * (i.e. messages which their dst address equals to the src address of + * the rpmsg channel), the driver's handler is invoked to process it. + * + * More complicated drivers though, that do need to allocate additional rpmsg + * addresses, and bind them to different rx callbacks, must explicitly + * create themselves additional endpoints (see rpmsg_create_ept()). + */ +struct rpmsg_endpoint { + struct rpmsg_channel *rpdev; + void (*cb)(struct rpmsg_channel *, void *, int, void *, u32); + u32 addr; + void *priv; +}; + +/** + * struct rpmsg_driver - rpmsg driver struct + * @drv: underlying device driver + * @id_table: rpmsg ids serviced by this driver + * @probe: invoked when a matching rpmsg channel (i.e. device) is found + * @remove: invoked when the rpmsg channel is removed + * @callback: invoked when an inbound message is received on the channel + */ +struct rpmsg_driver { + struct device_driver drv; + const struct rpmsg_device_id *id_table; + int (*probe)(struct rpmsg_channel *dev); + void (*remove)(struct rpmsg_channel *dev); + void (*callback)(struct rpmsg_channel *, void *, int, void *, u32); +}; + +int register_rpmsg_device(struct rpmsg_channel *dev); +void unregister_rpmsg_device(struct rpmsg_channel *dev); +int register_rpmsg_driver(struct rpmsg_driver *drv); +void unregister_rpmsg_driver(struct rpmsg_driver *drv); +void rpmsg_destroy_ept(struct rpmsg_endpoint *); +struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *, + void (*cb)(struct rpmsg_channel *, void *, int, void *, u32), + void *priv, u32 addr); + +int +rpmsg_send_offchannel_raw(struct rpmsg_channel *, u32, u32, void *, int, bool); + +/** + * rpmsg_send() - send a message across to the remote processor + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len on the @rpdev channel. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source and destination addresses. + * In case there are no TX buffers available, the function will block until + * one becomes available, or a timeout of 15 seconds elapses. When the latter + * happens, -ERESTARTSYS is returned. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline int rpmsg_send(struct rpmsg_channel *rpdev, void *data, int len) +{ + u32 src = rpdev->src, dst = rpdev->dst; + + return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true); +} + +/** + * rpmsg_sendto() - send a message across to the remote processor, specify dst + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * @dst: destination address + * + * This function sends @data of length @len to the remote @dst address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source address. + * In case there are no TX buffers available, the function will block until + * one becomes available, or a timeout of 15 seconds elapses. When the latter + * happens, -ERESTARTSYS is returned. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_sendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst) +{ + u32 src = rpdev->src; + + return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true); +} + +/** + * rpmsg_send_offchannel() - send a message using explicit src/dst addresses + * @rpdev: the rpmsg channel + * @src: source address + * @dst: destination address + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len to the remote @dst address, + * and uses @src as the source address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to. + * In case there are no TX buffers available, the function will block until + * one becomes available, or a timeout of 15 seconds elapses. When the latter + * happens, -ERESTARTSYS is returned. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_send_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst, + void *data, int len) +{ + return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true); +} + +/** + * rpmsg_send() - send a message across to the remote processor + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len on the @rpdev channel. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source and destination addresses. + * In case there are no TX buffers available, the function will immediately + * return -ENOMEM without waiting until one becomes available. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_trysend(struct rpmsg_channel *rpdev, void *data, int len) +{ + u32 src = rpdev->src, dst = rpdev->dst; + + return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false); +} + +/** + * rpmsg_sendto() - send a message across to the remote processor, specify dst + * @rpdev: the rpmsg channel + * @data: payload of message + * @len: length of payload + * @dst: destination address + * + * This function sends @data of length @len to the remote @dst address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to, using @rpdev's source address. + * In case there are no TX buffers available, the function will immediately + * return -ENOMEM without waiting until one becomes available. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_trysendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst) +{ + u32 src = rpdev->src; + + return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false); +} + +/** + * rpmsg_send_offchannel() - send a message using explicit src/dst addresses + * @rpdev: the rpmsg channel + * @src: source address + * @dst: destination address + * @data: payload of message + * @len: length of payload + * + * This function sends @data of length @len to the remote @dst address, + * and uses @src as the source address. + * The message will be sent to the remote processor which the @rpdev + * channel belongs to. + * In case there are no TX buffers available, the function will immediately + * return -ENOMEM without waiting until one becomes available. + * + * Can only be called from process context (for now). + * + * Returns 0 on success and an appropriate error value on failure. + */ +static inline +int rpmsg_trysend_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst, + void *data, int len) +{ + return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false); +} + +#endif /* _LINUX_RPMSG_H */ diff --git a/include/linux/virtio_ids.h b/include/linux/virtio_ids.h index 85bb0bb..1520c06 100644 --- a/include/linux/virtio_ids.h +++ b/include/linux/virtio_ids.h @@ -35,5 +35,6 @@ #define VIRTIO_ID_RNG 4 /* virtio ring */ #define VIRTIO_ID_BALLOON 5 /* virtio balloon */ #define VIRTIO_ID_9P 9 /* 9p virtio console */ +#define VIRTIO_ID_RPMSG 10 /* virtio remote processor messaging */ #endif /* _LINUX_VIRTIO_IDS_H */ -- 1.7.1 From ohad at wizery.com Tue Jun 21 02:18:34 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Tue, 21 Jun 2011 10:18:34 +0300 Subject: [RFC 8/8] rpmsg: add omap host backend In-Reply-To: <1308640714-17961-1-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> Message-ID: <1308640714-17961-9-git-send-email-ohad@wizery.com> Add rpmsg devices that wires virtio to the OMAP mailbox and enabled A9<->M3 communications on OMAP4. Each M3 core gets its own virtio device, since it is running its own independent instance of RTOS, and is therefore treated as a completely separate processor by rpmsg. Both M3 cores use the same mailbox device to communicate with the A9, and the mailbox payload is used to indicate which of the virtqueues is triggered (each M3 core gets 2 virtqueues, for rx and tx). Map the two vrings and the IPC buffers as noncacheable; use dynamically carved-out physical memory for that. Put this omap implementation in a dedicated drivers/rpmsg/host folder, with the intention of stacking different rpmsg host implementations in one place. Signed-off-by: Ohad Ben-Cohen --- drivers/rpmsg/Kconfig | 2 + drivers/rpmsg/Makefile | 2 + drivers/rpmsg/host/Kconfig | 11 + drivers/rpmsg/host/Makefile | 1 + drivers/rpmsg/host/omap_rpmsg.c | 540 +++++++++++++++++++++++++++++++++++++++ drivers/rpmsg/host/omap_rpmsg.h | 69 +++++ 6 files changed, 625 insertions(+), 0 deletions(-) create mode 100644 drivers/rpmsg/host/Kconfig create mode 100644 drivers/rpmsg/host/Makefile create mode 100644 drivers/rpmsg/host/omap_rpmsg.c create mode 100644 drivers/rpmsg/host/omap_rpmsg.h diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig index 41303f5..9544d62 100644 --- a/drivers/rpmsg/Kconfig +++ b/drivers/rpmsg/Kconfig @@ -12,3 +12,5 @@ if RPMSG source "drivers/virtio/Kconfig" endif # RPMSG + +source "drivers/rpmsg/host/Kconfig" diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile index 7617fcb..368a526 100644 --- a/drivers/rpmsg/Makefile +++ b/drivers/rpmsg/Makefile @@ -1 +1,3 @@ obj-$(CONFIG_RPMSG) += virtio_rpmsg_bus.o + +obj-$(CONFIG_RPMSG) += host/ diff --git a/drivers/rpmsg/host/Kconfig b/drivers/rpmsg/host/Kconfig new file mode 100644 index 0000000..fd2140c --- /dev/null +++ b/drivers/rpmsg/host/Kconfig @@ -0,0 +1,11 @@ +config OMAP_RPMSG + tristate "OMAP virtio-based remote processor messaging support" + depends on ARCH_OMAP4 && OMAP_REMOTE_PROC + select CONFIG_OMAP_MBOX_FWK + select RPMSG + help + Say Y if you want to enable OMAP's virtio-based remote-processor + messaging, currently only available on OMAP4. This is required + for offloading cpu-intensive and/or latency-sensitive tasks to + the remote on-chip M3s or C64x+ dsp, usually used by multimedia + frameworks. diff --git a/drivers/rpmsg/host/Makefile b/drivers/rpmsg/host/Makefile new file mode 100644 index 0000000..9823745 --- /dev/null +++ b/drivers/rpmsg/host/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_OMAP_RPMSG) += omap_rpmsg.o diff --git a/drivers/rpmsg/host/omap_rpmsg.c b/drivers/rpmsg/host/omap_rpmsg.c new file mode 100644 index 0000000..b8c2305 --- /dev/null +++ b/drivers/rpmsg/host/omap_rpmsg.c @@ -0,0 +1,540 @@ +/* + * Remote processor messaging transport (OMAP platform-specific bits) + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * + * Ohad Ben-Cohen + * Brian Swetland + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "omap_rpmsg.h" + +/** + * struct omap_rpmsg_vproc - omap's virtio remote processor state + * @vdev: virtio device + * @vring: phys address of two vrings; first one used for rx, 2nd one for tx + * @buf_paddr: physical address of the IPC buffer region + * @buf_size: size of IPC buffer region + * @buf_mapped: kernel (ioremap'ed) address of IPC buffer region + * @mbox_name: name of omap mailbox device to use with this vproc + * @rproc_name: name of remote proc device to use with this vproc + * @mbox: omap mailbox handle + * @rproc: remoteproc handle + * @nb: notifier block that will be invoked on inbound mailbox messages + * @vq: virtio's virtqueues + * @base_vq_id: index of first virtqueue that belongs to this vproc + * @num_of_vqs: number of virtqueues this vproc owns + * @static_chnls: table of static channels for this vproc + */ +struct omap_rpmsg_vproc { + struct virtio_device vdev; + unsigned int vring[2]; /* mpu owns first vring, ipu owns the 2nd */ + unsigned int buf_paddr; + unsigned int buf_size; /* size must be page-aligned */ + void *buf_mapped; + char *mbox_name; + char *rproc_name; + struct omap_mbox *mbox; + struct rproc *rproc; + struct notifier_block nb; + struct virtqueue *vq[2]; + int base_vq_id; + int num_of_vqs; + struct rpmsg_channel_info *static_chnls; +}; + +#define to_omap_vproc(vd) container_of(vd, struct omap_rpmsg_vproc, vdev) + +/** + * struct omap_rpmsg_vq_info - virtqueue state + * @num: number of buffers supported by the vring + * @vq_id: a unique index of this virtqueue + * @addr: address where the vring is mapped onto + * @vproc: the virtual remote processor state + * + * Such a struct will be maintained for every virtqueue we're + * using to communicate with the remote processor + */ +struct omap_rpmsg_vq_info { + __u16 num; + __u16 vq_id; + void *addr; + struct omap_rpmsg_vproc *vproc; +}; + +/* + * For now, allocate 256 buffers of 512 bytes for each side. each buffer + * will then have 16B for the msg header and 496B for the payload. + * This will require a total space of 256KB for the buffers themselves, and + * 3 pages for every vring (the size of the vring depends on the number of + * buffers it supports). + */ +#define RPMSG_NUM_BUFS (512) +#define RPMSG_BUF_SIZE (512) +#define RPMSG_BUFS_SPACE (RPMSG_NUM_BUFS * RPMSG_BUF_SIZE) + +/* + * The alignment between the consumer and producer parts of the vring. + * Note: this is part of the "wire" protocol. If you change this, you need + * to update your BIOS image as well + */ +#define RPMSG_VRING_ALIGN (4096) + +/* With 256 buffers, our vring will occupy 3 pages */ +#define RPMSG_RING_SIZE ((DIV_ROUND_UP(vring_size(RPMSG_NUM_BUFS / 2, \ + RPMSG_VRING_ALIGN), PAGE_SIZE)) * PAGE_SIZE) + +/* The total IPC space needed to communicate with a remote processor */ +#define RPMSG_IPC_MEM (RPMSG_BUFS_SPACE + 2 * RPMSG_RING_SIZE) + +/* + * Provide rpmsg core with platform-specific configuration. + * Since user data is at stake here, bugs can't be tolerated. hence + * the BUG_ON approach on invalid lengths. + * + * For more info on these configuration requests, see enum + * rpmsg_platform_requests. + */ +static void omap_rpmsg_get(struct virtio_device *vdev, unsigned int request, + void *buf, unsigned len) +{ + struct omap_rpmsg_vproc *vproc = to_omap_vproc(vdev); + int tmp; + + switch (request) { + case VPROC_BUF_ADDR: + BUG_ON(len != sizeof(vproc->buf_mapped)); + memcpy(buf, &vproc->buf_mapped, len); + break; + case VPROC_BUF_PADDR: + BUG_ON(len != sizeof(vproc->buf_paddr)); + memcpy(buf, &vproc->buf_paddr, len); + break; + case VPROC_BUF_NUM: + BUG_ON(len != sizeof(tmp)); + tmp = RPMSG_NUM_BUFS; + memcpy(buf, &tmp, len); + break; + case VPROC_BUF_SZ: + BUG_ON(len != sizeof(tmp)); + tmp = RPMSG_BUF_SIZE; + memcpy(buf, &tmp, len); + break; + case VPROC_STATIC_CHANNELS: + BUG_ON(len != sizeof(vproc->static_chnls)); + memcpy(buf, &vproc->static_chnls, len); + break; + default: + dev_err(&vdev->dev, "invalid request: %d\n", request); + } +} + +/* kick the remote processor, and let it know which virtqueue to poke at */ +static void omap_rpmsg_notify(struct virtqueue *vq) +{ + struct omap_rpmsg_vq_info *rpvq = vq->priv; + int ret; + + pr_debug("sending mailbox msg: %d\n", rpvq->vq_id); + /* send the index of the triggered virtqueue in the mailbox payload */ + ret = omap_mbox_msg_send(rpvq->vproc->mbox, rpvq->vq_id); + if (ret) + pr_err("ugh, omap_mbox_msg_send() failed: %d\n", ret); +} + +/** + * omap_rpmsg_mbox_callback() - inbound mailbox message handler + * @this: notifier block + * @index: unused + * @data: mailbox payload + * + * This handler is invoked by omap's mailbox driver whenever a mailbox + * message is received. Usually, the mailbox payload simply contains + * the index of the virtqueue that is kicked by the remote processor, + * and we let virtio handle it. + * + * In addition to virtqueue indices, we also have some out-of-band values + * that indicates different events. Those values are deliberately very + * big so they don't coincide with virtqueue indices. Moreover, + * they are rarely used, if used at all, and their necessity should + * be revisited. + */ +static int omap_rpmsg_mbox_callback(struct notifier_block *this, + unsigned long index, void *data) +{ + mbox_msg_t msg = (mbox_msg_t) data; + struct omap_rpmsg_vproc *vproc; + + vproc = container_of(this, struct omap_rpmsg_vproc, nb); + + pr_debug("mbox msg: 0x%x\n", msg); + + switch (msg) { + case RP_MBOX_CRASH: + pr_err("%s has just crashed !\n", vproc->rproc_name); + /* todo: smarter error handling here */ + break; + case RP_MBOX_ECHO_REPLY: + pr_info("received echo reply from %s !\n", vproc->rproc_name); + break; + case RP_MBOX_PENDING_MSG: + /* + * a new inbound message is waiting in our rx vring (1st vring). + * Let's pretend the message explicitly contained the rx vring + * index number and handle it generically. + */ + msg = vproc->base_vq_id; + /* intentional fall-through */ + default: + /* ignore vq indices which are clearly not for us */ + if (msg < vproc->base_vq_id) + break; + + msg -= vproc->base_vq_id; + + /* + * Currently both PENDING_MSG and explicit-virtqueue-index + * messaging are supported. + * Whatever approach is taken, at this point 'msg' contains + * the index of the vring which was just triggered. + */ + if (msg < vproc->num_of_vqs) + vring_interrupt(msg, vproc->vq[msg]); + } + + return NOTIFY_DONE; +} + +/* prepare a virtqueue */ +static struct virtqueue *rp_find_vq(struct virtio_device *vdev, + unsigned index, + void (*callback)(struct virtqueue *vq), + const char *name) +{ + struct omap_rpmsg_vproc *vproc = to_omap_vproc(vdev); + struct omap_rpmsg_vq_info *rpvq; + struct virtqueue *vq; + int err; + + rpvq = kmalloc(sizeof(*rpvq), GFP_KERNEL); + if (!rpvq) + return ERR_PTR(-ENOMEM); + + /* ioremap'ing normal memory, so we cast away sparse's complaints */ + rpvq->addr = (__force void *) ioremap_nocache(vproc->vring[index], + RPMSG_RING_SIZE); + if (!rpvq->addr) { + err = -ENOMEM; + goto free_rpvq; + } + + memset(rpvq->addr, 0, RPMSG_RING_SIZE); + + pr_debug("vring%d: phys 0x%x, virt 0x%x\n", index, vproc->vring[index], + (unsigned int) rpvq->addr); + + vq = vring_new_virtqueue(RPMSG_NUM_BUFS / 2, RPMSG_VRING_ALIGN, vdev, + rpvq->addr, omap_rpmsg_notify, callback, name); + if (!vq) { + pr_err("vring_new_virtqueue failed\n"); + err = -ENOMEM; + goto unmap_vring; + } + + vproc->vq[index] = vq; + vq->priv = rpvq; + /* unique id for this virtqueue */ + rpvq->vq_id = vproc->base_vq_id + index; + rpvq->vproc = vproc; + + return vq; + +unmap_vring: + /* iounmap normal memory, so make sparse happy */ + iounmap((__force void __iomem *) rpvq->addr); +free_rpvq: + kfree(rpvq); + return ERR_PTR(err); +} + +static void omap_rpmsg_del_vqs(struct virtio_device *vdev) +{ + struct virtqueue *vq, *n; + struct omap_rpmsg_vproc *vproc = to_omap_vproc(vdev); + + if (vproc->rproc) + rproc_put(vproc->rproc); + + if (vproc->mbox) + omap_mbox_put(vproc->mbox, &vproc->nb); + + if (vproc->buf_mapped) + /* iounmap normal memory, so make sparse happy */ + iounmap((__force void __iomem *)vproc->buf_mapped); + + list_for_each_entry_safe(vq, n, &vdev->vqs, list) { + struct omap_rpmsg_vq_info *rpvq = vq->priv; + vring_del_virtqueue(vq); + /* iounmap normal memory, so make sparse happy */ + iounmap((__force void __iomem *) rpvq->addr); + kfree(rpvq); + } +} + +static int omap_rpmsg_find_vqs(struct virtio_device *vdev, unsigned nvqs, + struct virtqueue *vqs[], + vq_callback_t *callbacks[], + const char *names[]) +{ + struct omap_rpmsg_vproc *vproc = to_omap_vproc(vdev); + int i, err; + + /* we maintain two virtqueues per remote processor (for RX and TX) */ + if (nvqs != 2) + return -EINVAL; + + for (i = 0; i < nvqs; ++i) { + vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]); + if (IS_ERR(vqs[i])) { + err = PTR_ERR(vqs[i]); + goto error; + } + } + + vproc->num_of_vqs = nvqs; + + /* ioremap'ing normal memory, so we cast away sparse's complaints */ + vproc->buf_mapped = (__force void *) ioremap_nocache(vproc->buf_paddr, + vproc->buf_size); + if (!vproc->buf_mapped) { + pr_err("ioremap failed\n"); + err = -ENOMEM; + goto error; + } + + /* for now, use mailbox's notifiers. later that can be optimized */ + vproc->nb.notifier_call = omap_rpmsg_mbox_callback; + vproc->mbox = omap_mbox_get(vproc->mbox_name, &vproc->nb); + if (IS_ERR(vproc->mbox)) { + pr_err("failed to get mailbox %s\n", vproc->mbox_name); + err = -EINVAL; + goto error; + } + + pr_debug("buf: phys 0x%x, virt 0x%x\n", vproc->buf_paddr, + (unsigned int) vproc->buf_mapped); + + /* tell the M3 we're ready (so M3 will know we're sane) */ + err = omap_mbox_msg_send(vproc->mbox, RP_MBOX_READY); + if (err) { + pr_err("ugh, omap_mbox_msg_send() failed: %d\n", err); + goto error; + } + + /* send it the physical address of the vrings + IPC buffer */ + err = omap_mbox_msg_send(vproc->mbox, (mbox_msg_t) vproc->buf_paddr); + if (err) { + pr_err("ugh, omap_mbox_msg_send() failed: %d\n", err); + goto error; + } + + /* ping the remote processor. this is only for sanity-sake; + * there is no functional effect whatsoever */ + err = omap_mbox_msg_send(vproc->mbox, RP_MBOX_ECHO_REQUEST); + if (err) { + pr_err("ugh, omap_mbox_msg_send() failed: %d\n", err); + goto error; + } + + /* now load the firmware, and boot the M3 */ + vproc->rproc = rproc_get(vproc->rproc_name); + if (!vproc->rproc) { + pr_err("failed to get rproc %s\n", vproc->rproc_name); + err = -EINVAL; + goto error; + } + + return 0; + +error: + omap_rpmsg_del_vqs(vdev); + return err; +} + +/* + * should be nice to add firmware support for these handlers. + * for now provide them so virtio doesn't crash + */ +static u8 omap_rpmsg_get_status(struct virtio_device *vdev) +{ + return 0; +} + +static void omap_rpmsg_set_status(struct virtio_device *vdev, u8 status) +{ + dev_dbg(&vdev->dev, "new status: %d\n", status); +} + +static void omap_rpmsg_reset(struct virtio_device *vdev) +{ + dev_dbg(&vdev->dev, "reset !\n"); +} + +static u32 omap_rpmsg_get_features(struct virtio_device *vdev) +{ + /* for now, use hardcoded bitmap. later this should be provided + * by the firmware itself */ + return 1 << VIRTIO_RPMSG_F_NS; +} + +static void omap_rpmsg_finalize_features(struct virtio_device *vdev) +{ + /* Give virtio_ring a chance to accept features */ + vring_transport_features(vdev); +} + +static void omap_rpmsg_vproc_release(struct device *dev) +{ + /* this handler is provided so driver core doesn't yell at us */ +} + +static struct virtio_config_ops omap_rpmsg_config_ops = { + .get_features = omap_rpmsg_get_features, + .finalize_features = omap_rpmsg_finalize_features, + .get = omap_rpmsg_get, + .find_vqs = omap_rpmsg_find_vqs, + .del_vqs = omap_rpmsg_del_vqs, + .reset = omap_rpmsg_reset, + .set_status = omap_rpmsg_set_status, + .get_status = omap_rpmsg_get_status, +}; + +/* + * Populating the static channels table. + * + * This is not always required, and the example below just demonstrates + * how to populate it with a static server channel. + * + * This example should be moved to Documentation/rpmsh.h before merging. + * + * For more info, see 'struct rpmsg_channel_info'. + */ +static struct rpmsg_channel_info omap_ipuc0_static_chnls[] = { + RMSG_SERVER_CHNL("rpmsg-server-sample", 137), + { }, +}; + +static struct rpmsg_channel_info omap_ipuc1_static_chnls[] = { + { }, +}; + +static struct omap_rpmsg_vproc omap_rpmsg_vprocs[] = { + /* ipu_c0's rpmsg backend */ + { + .vdev.id.device = VIRTIO_ID_RPMSG, + .vdev.config = &omap_rpmsg_config_ops, + .mbox_name = "mailbox-1", + .rproc_name = "ipu", + /* core 0 is using indices 0 + 1 for its vqs */ + .base_vq_id = 0, + .static_chnls = omap_ipuc0_static_chnls, + }, + /* ipu_c1's rpmsg backend */ + { + .vdev.id.device = VIRTIO_ID_RPMSG, + .vdev.config = &omap_rpmsg_config_ops, + .mbox_name = "mailbox-1", + .rproc_name = "ipu", + /* core 1 is using indices 2 + 3 for its vqs */ + .base_vq_id = 2, + .static_chnls = omap_ipuc1_static_chnls, + }, +}; + +static int __init omap_rpmsg_ini(void) +{ + int i, ret = 0; + /* + * This whole area generally needs some rework. + * E.g, consider using dma_alloc_coherent for the IPC buffers and + * vrings, use CMA, etc... + */ + phys_addr_t paddr = omap_dsp_get_mempool_base(); + phys_addr_t psize = omap_dsp_get_mempool_size(); + + /* + * allocate carverout memory for the buffers and vring, and + * then register the vproc virtio device + */ + for (i = 0; i < ARRAY_SIZE(omap_rpmsg_vprocs); i++) { + struct omap_rpmsg_vproc *vproc = &omap_rpmsg_vprocs[i]; + + if (psize < RPMSG_IPC_MEM) { + pr_err("out of carveout memory: %d (%d)\n", psize, i); + return -ENOMEM; + } + + vproc->buf_paddr = paddr; + vproc->buf_size = RPMSG_BUFS_SPACE; + vproc->vring[0] = paddr + RPMSG_BUFS_SPACE; + vproc->vring[1] = paddr + RPMSG_BUFS_SPACE + RPMSG_RING_SIZE; + + paddr += RPMSG_IPC_MEM; + psize -= RPMSG_IPC_MEM; + + pr_debug("vproc%d: buf 0x%x, vring0 0x%x, vring1 0x%x\n", i, + vproc->buf_paddr, vproc->vring[0], vproc->vring[1]); + + vproc->vdev.dev.release = omap_rpmsg_vproc_release; + + ret = register_virtio_device(&vproc->vdev); + if (ret) { + pr_err("failed to register vproc: %d\n", ret); + break; + } + } + + return ret; +} +module_init(omap_rpmsg_ini); + +static void __exit omap_rpmsg_fini(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(omap_rpmsg_vprocs); i++) { + struct omap_rpmsg_vproc *vproc = &omap_rpmsg_vprocs[i]; + + unregister_virtio_device(&vproc->vdev); + } +} +module_exit(omap_rpmsg_fini); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("OMAP Remote processor messaging virtio device"); diff --git a/drivers/rpmsg/host/omap_rpmsg.h b/drivers/rpmsg/host/omap_rpmsg.h new file mode 100644 index 0000000..f6d2036 --- /dev/null +++ b/drivers/rpmsg/host/omap_rpmsg.h @@ -0,0 +1,69 @@ +/* + * Remote processor messaging + * + * Copyright (C) 2011 Texas Instruments, Inc. + * Copyright (C) 2011 Google, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Texas Instruments nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _OMAP_RPMSG_H +#define _OMAP_RPMSG_H + +/* + * enum - Predefined Mailbox Messages + * + * @RP_MBOX_READY: informs the M3's that we're up and running. this is + * part of the init sequence sent that the M3 expects to see immediately + * after it is booted. + * + * @RP_MBOX_PENDING_MSG: informs the receiver that there is an inbound + * message waiting in its own receive-side vring. please note that currently + * this message is optional: alternatively, one can explicitly send the index + * of the triggered virtqueue itself. the preferred approach will be decided + * as we progress and experiment with those two different approaches. + * + * @RP_MBOX_CRASH: this message is sent if BIOS crashes + * + * @RP_MBOX_ECHO_REQUEST: a mailbox-level "ping" message. + * + * @RP_MBOX_ECHO_REPLY: a mailbox-level reply to a "ping" + * + * @RP_MBOX_ABORT_REQUEST: a "please crash" request, used for testing the + * recovery mechanism (to some extent). + */ +enum omap_rp_mbox_messages { + RP_MBOX_READY = 0xFFFFFF00, + RP_MBOX_PENDING_MSG = 0xFFFFFF01, + RP_MBOX_CRASH = 0xFFFFFF02, + RP_MBOX_ECHO_REQUEST = 0xFFFFFF03, + RP_MBOX_ECHO_REPLY = 0xFFFFFF04, + RP_MBOX_ABORT_REQUEST = 0xFFFFFF05, +}; + +#endif /* _OMAP_RPMSG_H */ -- 1.7.1 From ohad at wizery.com Tue Jun 21 02:50:10 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Tue, 21 Jun 2011 10:50:10 +0300 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: <1308640714-17961-1-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> Message-ID: On Tue, Jun 21, 2011 at 10:18 AM, Ohad Ben-Cohen wrote: > * I will be replying to this email with an attachment of the M3 firmware > ?image I work with, so anyone with a panda board can take the code for a > ?ride (put the image in /lib/firmware, and tell me if stuff doesn't work > ?for you: there are a few omap iommu fixes that are circulating but not > ?merged yet). Attaching an OMAP4 M3 firmware. Below is a simple session with it: root at omap4430-panda:~# cat /debug/remoteproc/omap-rproc.1/name ipu root at omap4430-panda:~# cat /debug/remoteproc/omap-rproc.1/state offline (0) root at omap4430-panda:~# modprobe omap_rpmsg [ 222.245758] omap_device: omap-mailbox.-1: new worst case activate latency 0: 30517 [ 222.254669] omap-rproc omap-rproc.1: powering up ipu [ 222.263580] virtio_rpmsg_bus virtio0: rpmsg backend virtproc probed successfully [ 222.272033] virtio_rpmsg_bus virtio1: rpmsg backend virtproc probed successfully [ 222.325317] omap-rproc omap-rproc.1: Loaded fw image ducati-m3.bin, size 161736 [ 222.333068] omap-rproc omap-rproc.1: BIOS image version is 2 [ 222.346099] omap-iommu omap-iommu.0: ducati: version 2.1 [ 222.352661] omap_device: omap-rproc.1: new worst case activate latency 0: 30517 [ 222.360351] omap-rproc omap-rproc.1: remote processor ipu is now up [ 222.709411] omap_rpmsg_mbox_callback: received echo reply from ipu ! [ 222.716156] omap_rpmsg_mbox_callback: received echo reply from ipu ! [ 222.722869] omap_rpmsg_mbox_callback: received echo reply from ipu ! [ 222.729583] omap_rpmsg_mbox_callback: received echo reply from ipu ! [ 222.736419] virtio_rpmsg_bus virtio0: creating channel rpmsg-client-sample addr 0x32 [ 222.745361] virtio_rpmsg_bus virtio0: creating channel rpmsg-client-sample addr 0x33 [ 222.753814] virtio_rpmsg_bus virtio0: creating channel rpmsg-omx addr 0x3c [ 222.761535] virtio_rpmsg_bus virtio1: creating channel rpmsg-client-sample addr 0x32 [ 222.769989] virtio_rpmsg_bus virtio1: creating channel rpmsg-client-sample addr 0x33 [ 222.778411] virtio_rpmsg_bus virtio1: creating channel rpmsg-omx addr 0x3c root at omap4430-panda:~# cat /debug/remoteproc/omap-rproc.1/state running (2) root at omap4430-panda:~# modprobe rpmsg_server_sample [ 509.860748] rpmsg_server_sample rpmsg0: new channel: 0x89 -> 0xffffffff! [ 509.871307] rpmsg_server_sample rpmsg0: incoming msg 1 (src: 0x32) [ 509.879608] rpmsg_server_sample rpmsg0: incoming msg 2 (src: 0x32) [ 509.887451] rpmsg_server_sample rpmsg0: incoming msg 3 (src: 0x32) ... -------------- next part -------------- A non-text attachment was scrubbed... Name: ducati-m3.bin Type: application/octet-stream Size: 161736 bytes Desc: not available URL: From ohad at wizery.com Tue Jun 21 04:30:21 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Tue, 21 Jun 2011 12:30:21 +0300 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> Message-ID: On Tue, Jun 21, 2011 at 10:50 AM, Ohad Ben-Cohen wrote: > root at omap4430-panda:~# cat /debug/remoteproc/omap-rproc.1/state > running (2) At this point, the two remote M3 cores also start dumping trace logs to shared memory buffers, which are exposed by debugfs entries: root at omap4430-panda:~# cat /debug/remoteproc/omap-rproc.1/trace0 CORE0 starting.. ... root at omap4430-panda:~# cat /debug/remoteproc/omap-rproc.1/trace1 CORE1 starting.. ... From rusty at rustcorp.com.au Tue Jun 21 21:42:33 2011 From: rusty at rustcorp.com.au (Rusty Russell) Date: Wed, 22 Jun 2011 12:12:33 +0930 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: <1308640714-17961-8-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-8-git-send-email-ohad@wizery.com> Message-ID: <87liwu8u3q.fsf@rustcorp.com.au> On Tue, 21 Jun 2011 10:18:33 +0300, Ohad Ben-Cohen wrote: > Add a virtio-based IPC bus, which enables kernel users to communicate > with remote processors over shared memory using a simple messaging > protocol. Wow, sometimes one writes a standard and people use it. Thanks! I'm still digesting exactly what you're doing, but this is a bit unconventional: > + /* Platform must supply pre-allocated uncached buffers for now */ > + vdev->config->get(vdev, VPROC_BUF_ADDR, &addr, sizeof(addr)); > + vdev->config->get(vdev, VPROC_BUF_NUM, &num_bufs, sizeof(num_bufs)); > + vdev->config->get(vdev, VPROC_BUF_SZ, &buf_size, sizeof(buf_size)); > + vdev->config->get(vdev, VPROC_BUF_PADDR, &vrp->phys_base, > + sizeof(vrp->phys_base)); The normal way is to think of the config space as a structure, and use offsets rather than using an enum value to distinguish the fields. > +#define RPMSG_NAME_SIZE 32 > +#define RPMSG_DEVICE_MODALIAS_FMT "rpmsg:%s" > + > +struct rpmsg_device_id { > + char name[RPMSG_NAME_SIZE]; > + kernel_ulong_t driver_data /* Data private to the driver */ > + __attribute__((aligned(sizeof(kernel_ulong_t)))); > +}; This alignment directive seems overkill... > +#define VIRTIO_ID_RPMSG 10 /* virtio remote processor messaging */ I think you want 6. Plan 9 jumped ahead to grab 9 :) Cheers, Rusty. From levinsasha928 at gmail.com Tue Jun 21 22:11:27 2011 From: levinsasha928 at gmail.com (Sasha Levin) Date: Tue, 21 Jun 2011 23:11:27 -0400 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: <87liwu8u3q.fsf@rustcorp.com.au> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-8-git-send-email-ohad@wizery.com> <87liwu8u3q.fsf@rustcorp.com.au> Message-ID: <1308712287.11435.4.camel@lappy> On Wed, 2011-06-22 at 12:12 +0930, Rusty Russell wrote: > > +#define VIRTIO_ID_RPMSG 10 /* virtio remote processor messaging */ > > I think you want 6. Plan 9 jumped ahead to grab 9 :) Maybe it's worth adding an appendix to the virtio spec as part of this (and really any) patch series which takes a virtio ID. Also, I understand that virtio itself as an idea isn't Linux specific, but maybe it's worth including the spec (or at least a variation of it) as part of the kernel documentation. -- Sasha. From will.newton at gmail.com Wed Jun 22 05:05:41 2011 From: will.newton at gmail.com (Will Newton) Date: Wed, 22 Jun 2011 11:05:41 +0100 Subject: [RFC 2/8] remoteproc: add omap implementation In-Reply-To: <1308640714-17961-3-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-3-git-send-email-ohad@wizery.com> Message-ID: On Tue, Jun 21, 2011 at 8:18 AM, Ohad Ben-Cohen wrote: > +/* bootaddr isn't needed for the dual M3's */ > +static inline int omap_rproc_start(struct rproc *rproc, u64 bootaddr) > +static inline int omap_rproc_stop(struct rproc *rproc) These two functions don't need to be inline as far as I can see. > +static struct rproc_ops omap_rproc_ops = { > + ? ? ? .start = omap_rproc_start, > + ? ? ? .stop = omap_rproc_stop, > +}; From ohad at wizery.com Wed Jun 22 05:46:37 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Wed, 22 Jun 2011 13:46:37 +0300 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: <87liwu8u3q.fsf@rustcorp.com.au> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-8-git-send-email-ohad@wizery.com> <87liwu8u3q.fsf@rustcorp.com.au> Message-ID: On Wed, Jun 22, 2011 at 5:42 AM, Rusty Russell wrote: > On Tue, 21 Jun 2011 10:18:33 +0300, Ohad Ben-Cohen wrote: >> Add a virtio-based IPC bus, which enables kernel users to communicate >> with remote processors over shared memory using a simple messaging >> protocol. > > Wow, sometimes one writes a standard and people use it. ?Thanks! And we really liked it: virtio_rpmsg_bus.c, the virtio driver which does most of the magic here ended up pretty small thanks to virtio. and the performance numbers are really good, too. >> + ? ? /* Platform must supply pre-allocated uncached buffers for now */ >> + ? ? vdev->config->get(vdev, VPROC_BUF_ADDR, &addr, sizeof(addr)); >> + ? ? vdev->config->get(vdev, VPROC_BUF_NUM, &num_bufs, sizeof(num_bufs)); >> + ? ? vdev->config->get(vdev, VPROC_BUF_SZ, &buf_size, sizeof(buf_size)); >> + ? ? vdev->config->get(vdev, VPROC_BUF_PADDR, &vrp->phys_base, >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sizeof(vrp->phys_base)); > > The normal way is to think of the config space as a structure, and use > offsets rather than using an enum value to distinguish the fields. Yes, I was (mis-)using the config space for now to talk with platform-specific code (on the host), and not with the peer, so I opted for simplicity. But this is definitely one thing that is going away: I don't see any reason why not just use dma_alloc_coherent (or even dma_pool_create) directly from the driver here in order to get those buffers. >> +#define RPMSG_NAME_SIZE ? ? ? ? ? ? ? ? ? ? ?32 >> +#define RPMSG_DEVICE_MODALIAS_FMT ? ?"rpmsg:%s" >> + >> +struct rpmsg_device_id { >> + ? ? char name[RPMSG_NAME_SIZE]; >> + ? ? kernel_ulong_t driver_data ? ? ?/* Data private to the driver */ >> + ? ? ? ? ? ? ? ? ? ? __attribute__((aligned(sizeof(kernel_ulong_t)))); >> +}; > > This alignment directive seems overkill... Yes, looks like I can remove this. thanks. >> +#define VIRTIO_ID_RPMSG ? ? ? ? ? ? ?10 /* virtio remote processor messaging */ > > I think you want 6. ?Plan 9 jumped ahead to grab 9 :) 6 it is :) Thanks, Ohad. From ohad at wizery.com Wed Jun 22 05:50:28 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Wed, 22 Jun 2011 13:50:28 +0300 Subject: [RFC 2/8] remoteproc: add omap implementation In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-3-git-send-email-ohad@wizery.com> Message-ID: On Wed, Jun 22, 2011 at 1:05 PM, Will Newton wrote: > On Tue, Jun 21, 2011 at 8:18 AM, Ohad Ben-Cohen wrote: > >> +/* bootaddr isn't needed for the dual M3's */ >> +static inline int omap_rproc_start(struct rproc *rproc, u64 bootaddr) > >> +static inline int omap_rproc_stop(struct rproc *rproc) > > These two functions don't need to be inline as far as I can see. They definitely don't need to. Thanks ! From ohad at wizery.com Wed Jun 22 06:41:38 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Wed, 22 Jun 2011 14:41:38 +0300 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: <201106211620.02065.arnd@arndb.de> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <201106211620.02065.arnd@arndb.de> Message-ID: Hi Arnd, On Tue, Jun 21, 2011 at 5:20 PM, Arnd Bergmann wrote: > This looks really nice overall, but I don't currently have time for a > more in-depth review. My feeling is that you are definitely on the right > track here, and the plans you list as TODO to continue are all good. Thanks ! > One point I noticed is the use of debugfs, which you should probably > replace at some point with a stable API, e.g. your own debugfs-like > file system, but there is no hurry to do that now. debugfs is only being used to expose debugging info (namely the power state of the remote processor and its trace log messages), which is mostly useful for developers trying to understand what went wrong. It seems like debugfs fits quite nicely here (e.g. it's perfectly fine if this is completely compiled out on production systems), but sure, we can always revisit this later too. > Regarding the firmware, I think it would be nice to have the option > to stick the firmware blob into the flattened device tree as a bininclude, > so you can build systems using the external processors without special > user space support. That's interesting. we'll definitely explore this - thanks ! I was also hoping that DT will simplify remote resource allocations as well (just like any other device, remote processors usually needs some hw resources) and planned to try it out soon to see how it all works out together. It might completely eliminate the preliminary "resource section" thingy we have in the firmware structure currently. > The binary format looks reasonable, but if you need anything more complex, > you should probably go straight for ELF files ;-) Hopefully we won't need to :) > +struct fw_section { > + ? ? ? u32 type; > + ? ? ? u64 da; > + ? ? ? u32 len; > + ? ? ? char content[0]; > +} __packed; > > Unfortunately require __packed. It would be better to sort the members > differently so that each member is naturally aligned in order to > avoid the need for padding or __packed attributes Definitely. __packed is being used just to be on the safe side; we didn't intend to introduce unnatural alignment intentionally. will be fixed. Thanks! Ohad. From ohad at wizery.com Wed Jun 22 08:09:35 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Wed, 22 Jun 2011 16:09:35 +0300 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: <201106221505.32531.arnd@arndb.de> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <201106211620.02065.arnd@arndb.de> <201106221505.32531.arnd@arndb.de> Message-ID: On Wed, Jun 22, 2011 at 4:05 PM, Arnd Bergmann wrote: > Ok, I see. In that case I agree that using debugfs is fine, but I would > recommend trying to use fewer macros and just open-coding the file > operations for better readability. Sure thing. It didn't end up saving much code eventually, too, so I'll just remove it. From rdunlap at xenotime.net Wed Jun 22 12:55:24 2011 From: rdunlap at xenotime.net (Randy Dunlap) Date: Wed, 22 Jun 2011 10:55:24 -0700 Subject: [RFC 1/8] drivers: add generic remoteproc framework In-Reply-To: <1308640714-17961-2-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-2-git-send-email-ohad@wizery.com> Message-ID: <20110622105524.b95c4df1.rdunlap@xenotime.net> On Tue, 21 Jun 2011 10:18:27 +0300 Ohad Ben-Cohen wrote: Hi, Just a few minor nits inline... > diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt > new file mode 100644 > index 0000000..3075813 > --- /dev/null > +++ b/Documentation/remoteproc.txt > @@ -0,0 +1,170 @@ > +Remote Processor Framework > + > +1. Introduction > + > +Modern SoCs typically have heterogeneous remote processor devices in asymmetric > +multiprocessing (AMP) configurations, which may be running different instances > +of operating system, whether it's Linux or any other flavor of real-time OS. > + > +OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP. > +In a typical configuration, the dual cortex-A9 is running Linux in a SMP > +configuration, and each of the other three cores (two M3 cores and a DSP) > +is running its own instance of RTOS in an AMP configuration. > + > +The generic remoteproc driver allows different platforms/architectures to > +control (power on, load firmware, power off) those remote processors while > +abstracting the hardware differences, so the entire driver doesn't need to be > +duplicated. > + > +2. User API > + > + struct rproc *rproc_get(const char *name); > + - power up the remote processor, identified by the 'name' argument, > + and boot it. If the remote processor is already powered on, the > + function immediately succeeds. > + On success, returns the rproc handle. On failure, NULL is returned. > + > + void rproc_put(struct rproc *rproc); > + - power off the remote processor, identified by the rproc handle. > + Every call to rproc_get() must be (eventually) accompanied by a call > + to rproc_put(). Calling rproc_put() redundantly is a bug. > + Note: the remote processor will actually be powered off only when the > + last user calls rproc_put(). > + > +3. Typical usage > + > +#include > + > +int dummy_rproc_example(void) > +{ > + struct rproc *my_rproc; > + > + /* let's power on and boot the image processing unit */ > + my_rproc = rproc_get("ipu"); > + if (!my_rproc) { > + /* > + * something went wrong. handle it and leave. > + */ > + } > + > + /* > + * the 'ipu' remote processor is now powered on... let it work ! > + */ > + > + /* if we no longer need ipu's services, power it down */ > + rproc_put(my_rproc); > +} > + > +4. API for implementors > + > + int rproc_register(struct device *dev, const char *name, > + const struct rproc_ops *ops, > + const char *firmware, > + const struct rproc_mem_entry *memory_maps, > + struct module *owner); > + - should be called from the underlying platform-specific implementation, in > + order to register a new remoteproc device. 'dev' is the underlying > + device, 'name' is the name of the remote processor, which will be > + specified by users calling rproc_get(), 'ops' is the platform-specific > + start/stop handlers, 'firmware' is the name of the firmware file to > + boot the processor with, 'memory_maps' is a table of da<->pa memory > + mappings which should be used to configure the IOMMU (if not relevant, > + just pass NULL here), 'owner' is the underlying module that should > + not be removed while the remote processor is in use. > + > + Returns 0 on success, or an appropriate error code on failure. > + > + int rproc_unregister(const char *name); > + - should be called from the underlying platform-specific implementation, in > + order to unregister a remoteproc device that was previously registered > + with rproc_register(). > + > +5. Implementation callbacks > + > +Every remoteproc implementation must provide these handlers: > + > +struct rproc_ops { > + int (*start)(struct rproc *rproc, u64 bootaddr); > + int (*stop)(struct rproc *rproc); > +}; > + > +The ->start() handler takes a rproc handle and an optional bootaddr argument, an rproc > +and should power on the device and boot it (using the bootaddr argument > +if the hardware requires one). > +On success, 0 is returned, and on failure, an appropriate error code. > + > +The ->stop() handler takes a rproc handle and powers the device off. an rproc > +On success, 0 is returned, and on failure, an appropriate error code. > + > +6. Binary Firmware Structure > + > +The following enums and structures define the binary format of the images > +remoteproc loads and boot the remote processors with. boots > + > +The general binary format is as follows: > + > +struct { > + char magic[4] = { 'R', 'P', 'R', 'C' }; > + u32 version; > + u32 header_len; > + char header[...] = { header_len bytes of unformatted, textual header }; > + struct section { > + u32 type; > + u64 da; > + u32 len; > + u8 content[...] = { len bytes of binary data }; > + } [ no limit on number of sections ]; > +} __packed; > + > +The image begins with a 4-bytes "RPRC" magic, a version number, and a > +free-style textual header that users can easily read. > + > +After the header, the firmware contains several sections that should be > +loaded to memory so the remote processor can access them. > + > +Every section begins with its type, device address (da) where the remote > +processor expects to find this section at (exact meaning depends whether drop: at > +the device accesses memory through an IOMMU or not. if not, da might just > +be physical addresses), the section length and its content. > + > +Most of the sections are either text or data (which currently are treated > +exactly the same), but there is one special "resource" section that allows > +the remote processor to announce/request certain resources from the host. > + > +A resource section is just a packed array of the following struct: > + > +struct fw_resource { > + u32 type; > + u64 da; > + u64 pa; > + u32 len; > + u32 flags; > + u8 name[48]; > +} __packed; > + > +The way a resource is really handled strongly depends on its type. > +Some resources are just one-way announcements, e.g., a RSC_TRACE type means > +that the remote processor will be writing log messages into a trace buffer > +which is located at the address specified in 'da'. In that case, 'len' is > +the size of that buffer. A RSC_BOOTADDR resource type announces the boot > +address (i.e. the first instruction the remote processor should be booted with) > +in 'da'. > + > +Other resources entries might be a two-way request/respond negotiation where > +a certain resource (memory or any other hardware resource) is requested > +by specifying the appropriate type and name. The host should then allocate > +such a resource and "reply" by writing the identifier (physical address > +or any other device id that will be meaningful to the remote processor) > +back into the relevant member of the resource structure. Obviously this > +approach can only be used _before_ booting the remote processor. After > +the remote processor is powered up, the resource section is expected > +to stay static. Runtime resource management (i.e. handling requests after > +the remote processor has booted) will be achieved using a dedicated rpmsg > +driver. > + > +The latter two-way approach is still preliminary and has not been implemented > +yet. It's left to see how this all works out. > + > +Most likely this kind of static allocations of hardware resources for > +remote processors can also use DT, so it's interesting to see how > +this all work out when DT materializes. works out thanks, --- ~Randy *** Remember to use Documentation/SubmitChecklist when testing your code *** From ohad at wizery.com Wed Jun 22 14:11:46 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Wed, 22 Jun 2011 22:11:46 +0300 Subject: [RFC 1/8] drivers: add generic remoteproc framework In-Reply-To: <20110622105524.b95c4df1.rdunlap@xenotime.net> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-2-git-send-email-ohad@wizery.com> <20110622105524.b95c4df1.rdunlap@xenotime.net> Message-ID: Hi Randy, On Wed, Jun 22, 2011 at 8:55 PM, Randy Dunlap wrote: > On Tue, 21 Jun 2011 10:18:27 +0300 Ohad Ben-Cohen wrote: > > Hi, > Just a few minor nits inline... Thanks! Ohad. From Jon.Povey at racelogic.co.uk Thu Jun 23 03:35:30 2011 From: Jon.Povey at racelogic.co.uk (Jon Povey) Date: Thu, 23 Jun 2011 09:35:30 +0100 Subject: How to investigate latency causes Message-ID: <70E876B0EA86DD4BAF101844BC814DFE09504ACB41@Cloud.RL.local> Hi folks, I am working on a video recording app on DM365, using DVSDK 4.02 which means kernel 2.6.32. Anyone got any advice on how to trace latency issues on this kernel? I was hoping to use tools/perf, but it's not supported for ARM on this old kernel it seems. I want to see what is going on when my process stalls so I can look at things like user+kernel thread priorities, threaded ISRs etc. to meet realtime deadlines. Kprobes maybe? There are various historical performance analysis tools that seem to have come and gone for Linux, any tips that can save me wasting time looking into less useful ones would be welcome. Related, I'd be interested to know how possible it is to use DMAI etc. on top of the current mainline kernel, or what's missing. For now I'm sticking with the TI DVSDK+PSP bundle but might want to change kernels before too long. Maybe I should ask that separately, later. Thanks, -- Jon Povey jon.povey at racelogic.co.uk Racelogic is a limited company registered in England. Registered number 2743719 . Registered Office Unit 10, Swan Business Centre, Osier Way, Buckingham, Bucks, MK18 1TB . The information contained in this electronic mail transmission is intended by Racelogic Ltd for the use of the named individual or entity to which it is directed and may contain information that is confidential or privileged. If you have received this electronic mail transmission in error, please delete it from your system without copying or forwarding it, and notify the sender of the error by reply email so that the sender's address records can be corrected. The views expressed by the sender of this communication do not necessarily represent those of Racelogic Ltd. Please note that Racelogic reserves the right to monitor e-mail communications passing through its network From michael.williamson at criticallink.com Thu Jun 23 07:23:29 2011 From: michael.williamson at criticallink.com (Michael Williamson) Date: Thu, 23 Jun 2011 08:23:29 -0400 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: <1308640714-17961-1-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> Message-ID: <4E033041.9090606@criticallink.com> On 6/21/2011 3:18 AM, Ohad Ben-Cohen wrote: > Modern SoCs typically employ a central symmetric multiprocessing (SMP) > application processor running Linux, with several other asymmetric > multiprocessing (AMP) heterogeneous processors running different instances > of operating system, whether Linux or any other flavor of real-time OS. > > OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP. > Typically, the dual cortex-A9 is running Linux in a SMP configuration, and > each of the other three cores (two M3 cores and a DSP) is running its own > instance of RTOS in an AMP configuration. > > AMP remote processors typically employ dedicated DSP codecs and multimedia > hardware accelerators, and therefore are often used to offload cpu-intensive > multimedia tasks from the main application processor. They could also be > used to control latency-sensitive sensors, drive "random" hardware blocks, > or just perform background tasks while the main CPU is idling. > > Users of those remote processors can either be userland apps (e.g. > multimedia frameworks talking with remote OMX components) or kernel drivers > (controlling hardware accessible only by the remote processor, reserving > kernel-controlled resources on behalf of the remote processor, etc..). > > This patch set adds a generic AMP/IPC framework which makes it possible to > control (power on, boot, power off) and communicate (simply send and receive > messages) with those remote processors. > > Specifically, we're adding: > > * Rpmsg: a virtio-based messaging bus that allows kernel drivers to > communicate with remote processors available on the system. In turn, > drivers could then expose appropriate user space interfaces, if needed > (tasks running on remote processors often have direct access to sensitive > resources like the system's physical memory, gpios, i2c buses, dma > controllers, etc.. so one normally wouldn't want to allow userland to > send everything/everywhere it wants). > > Every rpmsg device is a communication channel with a service running on a > remote processor (thus rpmsg devices are called channels). Channels are > identified by a textual name (which is used to match drivers to devices) > and have a local ("source") rpmsg address, and remote ("destination") rpmsg > address. When a driver starts listening on a channel (most commonly when it > is probed), the bus assigns the driver a unique rpmsg src address (a 32 bit > integer) and binds it with the driver's rx callback handler. This way > when inbound messages arrive to this src address, the rpmsg core dispatches > them to that driver, by invoking the driver's rx handler with the payload > of the incoming message. > > Once probed, rpmsg drivers can also immediately start sending messages to the > remote rpmsg service by using simple sending API; no need even to specify > a destination address, since that's part of the rpmsg channel, and the rpmsg > bus uses the channel's dst address when it constructs the message (for > more demanding use cases, there's also an extended API, which does allow > full control of both the src and dst addresses). > > The rpmsg bus is using virtio to send and receive messages: every pair > of processors share two vrings, which are used to send and receive the > messages over shared memory (one vring is used for tx, and the other one > for rx). Kicking the remote processor (i.e. letting it know it has a pending > message on its vring) is accomplished by means available on the platform we > run on (e.g. OMAP is using its mailbox to both interrupt the remote processor > and tell it which vring is kicked at the same time). The header of every > message sent on the rpmsg bus contains src and dst addresses, which make it > possible to multiplex several rpmsg channels on the same vring. > > One nice property of the rpmsg bus is that device creation is completely > dynamic: remote processors can announce the existence of remote rpmsg > services by sending a "name service" messages (which contain the name and > rpmsg addr of the remote service). Those messages are picked by the rpmsg > bus, which in turn dynamically creates and registers the rpmsg channels > (i.e devices) which represents the remote services. If/when a relevant rpmsg > driver is registered, it will be immediately probed by the bus, and can then > start "talking" to the remote service. > > Similarly, we can use this technique to dynamically create virtio devices > (and new vrings) which would then represent e.g. remote network, console > and block devices that will be driven by the existing virtio drivers > (this is still not implemented though; it requires some RTOS work as we're > not booting Linux on OMAP's remote processors). Creating new vrings might > also be desired by users who just don't want to use the shared rpmsg vrings > (for performance or any other functionality reasons). > > In addition to dynamic creation of rpmsg channels, the rpmsg bus also > supports creation of static channels. This is needed in two cases: > - when a certain remote processor doesn't support sending those "name > service" announcements. In that case, a static table of remote rpmsg > services must be used to create the rpmsg channels. > - to support rpmsg server drivers, which aren't bound to a specific remote > rpmsg address. Instead, they just listen on a local address, waiting for > incoming messages. To send a message, those server drivers need to use > the rpmsg_sendto() API, so they can explicitly indicate the dst address > every time. > > There are already several immediate use cases for rpmsg drivers: OMX > offloading (already being used on OMAP4), hardware resource manager (remote > processors on OMAP4 need to ask Linux to enable/disable hardware resources > on its behalf), remote display driver on Netra (dm8168), where the display > is controlled by a remote M3 processor (and a Linux v4l2/fbdev driver will > use rpmsg to communicate with that remote display driver). > > * Remoteproc: a generic driver that maintains the state of the remote > processor(s). Simple rproc_get() and rproc_put() API is exposed, which > drivers can use when needed (first driver to call get() will load a firmware, > configure an iommu if needed, and boot the remote processor, while last > driver to call put() will power it down). > > Hardware differences are abstracted as usual: a platform-specific driver > registers its own start/stop handlers in the generic remoteproc driver, > and those are invoked when its time to power up/down the processor. As a > reference, this patch set include remoteproc support for both OMAP4's > cortex-M3 and Davinci's DSP, tested on the pandaboard and hawkboard, > respectively. > > The gory part of remoteproc is the firmware handling. We tried to come up > with a simple binary format that will require minimum kernel code to handle, > but at the same time be generic enough in the hopes that it would prove > useful to others as well. We're not at all hang onto the binary format > we picked: if there's any technical reason to change it to support other > platforms, please let us know. We do realize that a single binary firmware > structure might eventually not work for everyone. it did prove useful for > us though; we adopted both the OMAP and Davinci platforms (and their > completely different remote processor devices) to this simple binary > structure, so we don't have to duplicate the firmware handling code. > I'd like to kick the tires on this with a da850 based platform (MityDSP-L138). Any chance you might be able to share the stuff you did on the remote side (DSP/BIOS adaptations for rpmsg, utils for ELF or COFF conversion to firmware format, etc.) for the DSP side of your tests done with the hawkboard? It looks like, at least for the da850, this subsumes or obsoletes DSPLINK in order to drive a more general purpose architecture (which looks great, so far, BTW). Is that the intent? -Mike From schen at mvista.com Thu Jun 23 08:15:56 2011 From: schen at mvista.com (Steve Chen) Date: Thu, 23 Jun 2011 08:15:56 -0500 Subject: How to investigate latency causes In-Reply-To: <70E876B0EA86DD4BAF101844BC814DFE09504ACB41@Cloud.RL.local> References: <70E876B0EA86DD4BAF101844BC814DFE09504ACB41@Cloud.RL.local> Message-ID: On Thu, Jun 23, 2011 at 3:35 AM, Jon Povey wrote: > Hi folks, > > I am working on a video recording app on DM365, using DVSDK 4.02 > which means kernel 2.6.32. > > Anyone got any advice on how to trace latency issues on this kernel? > I was hoping to use tools/perf, but it's not supported for ARM on > this old kernel it seems. > > I want to see what is going on when my process stalls so I can look > at things like user+kernel thread priorities, threaded ISRs etc. to > meet realtime deadlines. > > Kprobes maybe? There are various historical performance analysis tools > that seem to have come and gone for Linux, any tips that can save me > wasting time looking into less useful ones would be welcome. > > Related, I'd be interested to know how possible it is to use DMAI etc. > on top of the current mainline kernel, or what's missing. For now I'm > sticking with the TI DVSDK+PSP bundle but might want to change kernels > before too long. Maybe I should ask that separately, later. Hell Jon, I debug a video stall problem on DaVinci a few years ago, and I used LTTng. Regards, Steve From Daniele.Bosi at mta.it Thu Jun 23 08:54:12 2011 From: Daniele.Bosi at mta.it (Bosi Daniele) Date: Thu, 23 Jun 2011 15:54:12 +0200 Subject: R: How to investigate latency causes In-Reply-To: <70E876B0EA86DD4BAF101844BC814DFE09504ACB41@Cloud.RL.local> Message-ID: <531B6536C9F737458807DF75064873C801ED70DBD674@mta-digimail.MTA.INT> Jon, you can also use Oprofile. You have to configure your kernel for using it, then you have to run the Oprofile application while your application is running. Please check http://oprofile.sourceforge.net for more info. Bye Daniele -----Messaggio originale----- Da: davinci-linux-open-source-bounces at linux.davincidsp.com [mailto:davinci-linux-open-source-bounces at linux.davincidsp.com] Per conto di Jon Povey Inviato: gioved? 23 giugno 2011 10.36 A: davinci-linux-open-source at linux.davincidsp.com Oggetto: How to investigate latency causes Hi folks, I am working on a video recording app on DM365, using DVSDK 4.02 which means kernel 2.6.32. Anyone got any advice on how to trace latency issues on this kernel? I was hoping to use tools/perf, but it's not supported for ARM on this old kernel it seems. I want to see what is going on when my process stalls so I can look at things like user+kernel thread priorities, threaded ISRs etc. to meet realtime deadlines. Kprobes maybe? There are various historical performance analysis tools that seem to have come and gone for Linux, any tips that can save me wasting time looking into less useful ones would be welcome. Related, I'd be interested to know how possible it is to use DMAI etc. on top of the current mainline kernel, or what's missing. For now I'm sticking with the TI DVSDK+PSP bundle but might want to change kernels before too long. Maybe I should ask that separately, later. Thanks, -- Jon Povey jon.povey at racelogic.co.uk Racelogic is a limited company registered in England. Registered number 2743719 . Registered Office Unit 10, Swan Business Centre, Osier Way, Buckingham, Bucks, MK18 1TB . The information contained in this electronic mail transmission is intended by Racelogic Ltd for the use of the named individual or entity to which it is directed and may contain information that is confidential or privileged. If you have received this electronic mail transmission in error, please delete it from your system without copying or forwarding it, and notify the sender of the error by reply email so that the sender's address records can be corrected. The views expressed by the sender of this communication do not necessarily represent those of Racelogic Ltd. Please note that Racelogic reserves the right to monitor e-mail communications passing through its network _______________________________________________ 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 sshtylyov at mvista.com Thu Jun 23 10:27:52 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Thu, 23 Jun 2011 19:27:52 +0400 Subject: [RFC 5/8] remoteproc: add davinci implementation In-Reply-To: <1308640714-17961-6-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-6-git-send-email-ohad@wizery.com> Message-ID: <4E035B78.3080200@mvista.com> Hello. Ohad Ben-Cohen wrote: > From: Mark Grosen > Add remoteproc implementation for da850, so we can boot its DSP. > Signed-off-by: Mark Grosen > Signed-off-by: Ohad Ben-Cohen > --- > arch/arm/mach-davinci/include/mach/remoteproc.h | 28 +++++ > drivers/remoteproc/Kconfig | 16 +++ > drivers/remoteproc/Makefile | 1 + > drivers/remoteproc/davinci_remoteproc.c | 147 +++++++++++++++++++++++ > 4 files changed, 192 insertions(+), 0 deletions(-) > create mode 100644 arch/arm/mach-davinci/include/mach/remoteproc.h > create mode 100644 drivers/remoteproc/davinci_remoteproc.c > diff --git a/arch/arm/mach-davinci/include/mach/remoteproc.h b/arch/arm/mach-davinci/include/mach/remoteproc.h > new file mode 100644 > index 0000000..af6e88c > --- /dev/null > +++ b/arch/arm/mach-davinci/include/mach/remoteproc.h > @@ -0,0 +1,28 @@ > +/* > + * Remote Processor > + * > + * 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 > + * version 2 as published by the Free Software Foundation. > + * > + * 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. > + */ > + > +#ifndef _DAVINCI_REMOTEPROC_H > +#define _DAVINCI_REMOTEPROC_H > + > +#include > + > +struct davinci_rproc_pdata { > + struct rproc_ops *ops; > + char *name; > + char *clk_name; > + char *firmware; > +}; > + > +#endif /* _DAVINCI_REMOTEPROC_H */ > diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig > index 88421bd..1e594b5 100644 > --- a/drivers/remoteproc/Kconfig > +++ b/drivers/remoteproc/Kconfig > @@ -26,3 +26,19 @@ config OMAP_REMOTE_PROC > > It's safe to say n here if you're not interested in multimedia > offloading or just want a bare minimum kernel. > + > +config DAVINCI_REMOTE_PROC > + tristate "Davinci remoteproc support" > + depends on ARCH_DAVINCI_DA850 It should work on DA830 as well, but not on real DaVinci, so the name is misleading... [...] > diff --git a/drivers/remoteproc/davinci_remoteproc.c b/drivers/remoteproc/davinci_remoteproc.c > new file mode 100644 > index 0000000..0e26fe9 > --- /dev/null > +++ b/drivers/remoteproc/davinci_remoteproc.c > @@ -0,0 +1,147 @@ > +/* > + * Remote processor machine-specific module for Davinci > + * > + * 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 > + * version 2 as published by the Free Software Foundation. > + * > + * 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. > + */ > + > +#define pr_fmt(fmt) "%s: " fmt, __func__ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > + > +/* > + * Technical Reference: > + * OMAP-L138 Applications Processor System Reference Guide > + * http://www.ti.com/litv/pdf/sprugm7d > + */ > + > +/* local reset bit (0 is asserted) in MDCTL15 register (section 9.6.18) */ > +#define LRST BIT(8) Perhaps this should be named nLRST or something if the sense is inverted? > +/* next state bits in MDCTL15 register (section 9.6.18) */ > +#define NEXT_ENABLED 0x3 Isn't this already declared in as PSC_STATE_ENABLED? > +/* register for DSP boot address in SYSCFG0 module (section 11.5.6) */ > +#define HOST1CFG 0x44 Worth declaring in instead... > +static inline int davinci_rproc_start(struct rproc *rproc, u64 bootaddr) > +{ > + struct device *dev = rproc->dev; > + struct davinci_rproc_pdata *pdata = dev->platform_data; > + struct davinci_soc_info *soc_info = &davinci_soc_info; > + void __iomem *psc_base; > + struct clk *dsp_clk; > + > + /* hw requires the start (boot) address be on 1KB boundary */ > + if (bootaddr & 0x3ff) { > + dev_err(dev, "invalid boot address: must be aligned to 1KB\n"); > + return -EINVAL; > + } > + > + dsp_clk = clk_get(dev, pdata->clk_name); We could match using clkdev functionality, but the clock entry would need to be changed then... > + if (IS_ERR_OR_NULL(dsp_clk)) { > + dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk)); > + return PTR_ERR(dsp_clk); > + } > + > + clk_enable(dsp_clk); This seems rather senseless activity as on DA8xx the DSP core boots the ARM core, so the DSP clock will be already enabled. > + rproc->priv = dsp_clk; > + > + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K); > + > + /* insure local reset is asserted before writing start address */ > + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 * DA8XX_LPSC0_GEM); > + > + __raw_writel(bootaddr, DA8XX_SYSCFG0_VIRT(HOST1CFG)); DA8XX_SYSCFG0_VIRT() is not supposed to be used outside mach-davinci. The variable it refers is not exported, so driver module won't work. > + /* de-assert local reset to start the dsp running */ > + __raw_writel(LRST | NEXT_ENABLED, psc_base + MDCTL + > + 4 * DA8XX_LPSC0_GEM); > + > + iounmap(psc_base); > + > + return 0; > +} > + > +static inline int davinci_rproc_stop(struct rproc *rproc) > +{ > + struct davinci_soc_info *soc_info = &davinci_soc_info; > + void __iomem *psc_base; > + struct clk *dsp_clk = rproc->priv; > + > + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K); > + > + /* halt the dsp by asserting local reset */ > + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 * DA8XX_LPSC0_GEM); > + > + clk_disable(dsp_clk); > + clk_put(dsp_clk); > + > + iounmap(psc_base); > + > + return 0; > +} All this is DA8xx specific code which won't fly on real DaVincis, so I suggest that you rename the file to da8xx_remoteproc.c for clarity; and rename the patch as well... WBR, Sergei From mgrosen at ti.com Thu Jun 23 11:27:10 2011 From: mgrosen at ti.com (Grosen, Mark) Date: Thu, 23 Jun 2011 16:27:10 +0000 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: <4E033041.9090606@criticallink.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <4E033041.9090606@criticallink.com> Message-ID: > From: Michael Williamson > Sent: Thursday, June 23, 2011 5:23 AM ... > I'd like to kick the tires on this with a da850 based platform (MityDSP-L138). > Any chance you might be able to share the stuff you did on the remote side > (DSP/BIOS adaptations for rpmsg, utils for ELF or COFF conversion to > firmware format, etc.) for the DSP side of your tests done with the > hawkboard? We have only implemented the remoteproc (load, start, stop) part for OMAPL138 so far, i.e., not the rpmsg part (communications). I am not sure when we will get to the rpmsg part. We will be publishing a Git tree soon with the "remote processor" code under a BSD license. This code works with our TI RTOS, SYS/BIOS (also BSD), on both M3 and DSP CPUs. This will include the utility to generate the RPRC firmware format from an ELF file. Note that the Linux side does not have any explicit binding or dependency on the runtime environment on the remote processor, so alternate RTOS or bare-metal implementations could also be done. We will post a follow-up to the list with a URL soon. > It looks like, at least for the da850, this subsumes or obsoletes > DSPLINK in order to drive a more general purpose architecture > (which looks great, so far, BTW). > Is that the intent? First, we are not abandoning DSPLINK. We have many users of this, even though it is out-of-tree, and we will continue to support it. That said, we do intend to make this new design the basis for DSPLINK-like functionality. It's designed to be done "the right way" for Linux (and we are looking for feedback to make it better). It is also designed to be more scalable and extensible in userspace. With a solid kernel foundation, we can provide lots of functionality in userspace, or users can implement their own custom solutions. One of the key things to do is map our existing DSPLINK APIs, like MessageQ, to the new rpmsg transport. Mark From arnd at arndb.de Thu Jun 23 13:46:35 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Thu, 23 Jun 2011 20:46:35 +0200 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <4E033041.9090606@criticallink.com> Message-ID: <201106232046.35846.arnd@arndb.de> On Thursday 23 June 2011 18:27:10 Grosen, Mark wrote: > First, we are not abandoning DSPLINK. We have many users of this, even > though it is out-of-tree, and we will continue to support it. That said, we > do intend to make this new design the basis for DSPLINK-like > functionality. It's designed to be done "the right way" for Linux (and we > are looking for feedback to make it better). It is also designed to be more > scalable and extensible in userspace. With a solid kernel foundation, we can > provide lots of functionality in userspace, or users can implement their own > custom solutions. One of the key things to do is map our existing DSPLINK > APIs, like MessageQ, to the new rpmsg transport. Sounds all good. What about the PRUSS code? Does that fit into the new model as well? Arnd From mgrosen at ti.com Thu Jun 23 23:25:14 2011 From: mgrosen at ti.com (Grosen, Mark) Date: Fri, 24 Jun 2011 04:25:14 +0000 Subject: [RFC 5/8] remoteproc: add davinci implementation In-Reply-To: <4E035B78.3080200@mvista.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-6-git-send-email-ohad@wizery.com> <4E035B78.3080200@mvista.com> Message-ID: > From: Sergei Shtylyov > Sent: Thursday, June 23, 2011 8:28 AM > Subject: Re: [RFC 5/8] remoteproc: add davinci implementation > > Hello. Sergei, thanks for the feedback. Comments below. Mark > > It should work on DA830 as well, but not on real DaVinci, so the > name is misleading... Yes, we debated calling it da8xx, but felt that with minor changes it could accomodate the other SoCs in the davinci family. However, it may be better to start with just the da8xx/omapl13x parts and then rename if we add the others. > [...] > > + > > +/* > > + * Technical Reference: > > + * OMAP-L138 Applications Processor System Reference Guide > > + * http://www.ti.com/litv/pdf/sprugm7d > > + */ > > + > > +/* local reset bit (0 is asserted) in MDCTL15 register (section > 9.6.18) */ > > +#define LRST BIT(8) > > Perhaps this should be named nLRST or something if the sense is inverted? If there is an established naming convention for this, I'll adopt it. > > > +/* next state bits in MDCTL15 register (section 9.6.18) */ > > +#define NEXT_ENABLED 0x3 > > Isn't this already declared in as PSC_STATE_ENABLED? Yes, thanks, I missed it. > > +/* register for DSP boot address in SYSCFG0 module (section 11.5.6) > */ > > +#define HOST1CFG 0x44 > > Worth declaring in instead... Possibly - since it is only used for the DSP, I thought it would be better to keep local to this implementation. I'll adopt whichever approach is the convention. > > +static inline int davinci_rproc_start(struct rproc *rproc, u64 > bootaddr) > > +{ > > + struct device *dev = rproc->dev; > > + struct davinci_rproc_pdata *pdata = dev->platform_data; > > + struct davinci_soc_info *soc_info = &davinci_soc_info; > > + void __iomem *psc_base; > > + struct clk *dsp_clk; > > + > > + /* hw requires the start (boot) address be on 1KB boundary */ > > + if (bootaddr & 0x3ff) { > > + dev_err(dev, "invalid boot address: must be aligned to > 1KB\n"); > > + return -EINVAL; > > + } > > + > > + dsp_clk = clk_get(dev, pdata->clk_name); > > We could match using clkdev functionality, but the clock entry > would need to be changed then... I followed the existing pattern I saw in other drivers. If there is a new, better way, please point me to an example. > > > + if (IS_ERR_OR_NULL(dsp_clk)) { > > + dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk)); > > + return PTR_ERR(dsp_clk); > > + } > > + > > + clk_enable(dsp_clk); > > This seems rather senseless activity as on DA8xx the DSP core > boots the ARM core, so the DSP clock will be already enabled. I think it is needed. It's true that the DSP initiates the boot, but then it is reset and the clock disabled. See Section 13.2 of http://focus.ti.com/lit/ug/sprugm7e/sprugm7e.pdf: 13.2 DSP Wake Up Following deassertion of device reset, the DSP intializes the ARM296 so that it can execute the ARM ROM bootloader. Upon successful wake up, the ARM places the DSP in a reset and clock gated (SwRstDisable) state that is controlled by the LPSC and the SYSCFG modules. Besides, the boot loader could have disabled it to save power. The ARM and DSP are clocked independently, so I think it's best to use clock management. > > + rproc->priv = dsp_clk; > > + > > + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K); > > + > > + /* insure local reset is asserted before writing start address */ > > + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 * > DA8XX_LPSC0_GEM); > > + > > + __raw_writel(bootaddr, DA8XX_SYSCFG0_VIRT(HOST1CFG)); > > DA8XX_SYSCFG0_VIRT() is not supposed to be used outside mach-davinci. The > variable it refers is not exported, so driver module won't work. Ooops, I clearly did not build this as a module. Suggestion how to fix this? > > + /* de-assert local reset to start the dsp running */ > > + __raw_writel(LRST | NEXT_ENABLED, psc_base + MDCTL + > > + 4 * DA8XX_LPSC0_GEM); > > + > > + iounmap(psc_base); > > + > > + return 0; > > +} > > + > > +static inline int davinci_rproc_stop(struct rproc *rproc) > > +{ > > + struct davinci_soc_info *soc_info = &davinci_soc_info; > > + void __iomem *psc_base; > > + struct clk *dsp_clk = rproc->priv; > > + > > + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K); > > + > > + /* halt the dsp by asserting local reset */ > > + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 * > DA8XX_LPSC0_GEM); > > + > > + clk_disable(dsp_clk); > > + clk_put(dsp_clk); > > + > > + iounmap(psc_base); > > + > > + return 0; > > +} > > All this is DA8xx specific code which won't fly on real DaVincis, so I > suggest that you rename the file to da8xx_remoteproc.c for clarity; and > rename the patch as well... This is probably the right thing to do ... Mark From mgrosen at ti.com Thu Jun 23 23:32:08 2011 From: mgrosen at ti.com (Grosen, Mark) Date: Fri, 24 Jun 2011 04:32:08 +0000 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: <201106232046.35846.arnd@arndb.de> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <4E033041.9090606@criticallink.com> <201106232046.35846.arnd@arndb.de> Message-ID: > From: Arnd Bergmann > Sent: Thursday, June 23, 2011 11:47 AM > Subject: Re: [RFC 0/8] Introducing a generic AMP/IPC framework > > On Thursday 23 June 2011 18:27:10 Grosen, Mark wrote: > > First, we are not abandoning DSPLINK. We have many users of this, even > > though it is out-of-tree, and we will continue to support it. That said, we > > do intend to make this new design the basis for DSPLINK-like > > functionality. It's designed to be done "the right way" for Linux (and we > > are looking for feedback to make it better). It is also designed to be more > > scalable and extensible in userspace. With a solid kernel foundation, we can > > provide lots of functionality in userspace, or users can implement their own > > custom solutions. One of the key things to do is map our existing DSPLINK > > APIs, like MessageQ, to the new rpmsg transport. > > Sounds all good. What about the PRUSS code? Does that fit into the new > model as well? > > Arnd Arnd, Yes, I have been following some of the PRUSS discussion. I think the remoteproc driver could be used to manage the basic load/start/stop of the PRUSS processor. I am not sure if the virio/rpmsg part would be a good fit. The PRUSS processor is pretty limited, so the generality of virtio might be too much to fit and too much overhead. However, one of the good things about remoteproc currently is that it is standalone, so other transports could use it via the rproc_get/put methods. Mark From sshtylyov at mvista.com Fri Jun 24 10:13:50 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 24 Jun 2011 19:13:50 +0400 Subject: [RFC 5/8] remoteproc: add davinci implementation In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-6-git-send-email-ohad@wizery.com> <4E035B78.3080200@mvista.com> Message-ID: <4E04A9AE.3030801@mvista.com> Hello. Grosen, Mark wrote: >> It should work on DA830 as well, So please make it dependent on ARCH_DAVINCI_DA8XX. >> but not on real DaVinci, so the name is misleading... > Yes, we debated calling it da8xx, but felt that with minor changes it could > accomodate the other SoCs in the davinci family. I don't think it's a good idea. Using cpu_is_*() is drivers is bad. Using #ifdef's is not an option either. > However, it may be better > to start with just the da8xx/omapl13x parts and then rename if we add the > others. >> [...] >>> + >>> +/* >>> + * Technical Reference: >>> + * OMAP-L138 Applications Processor System Reference Guide >>> + * http://www.ti.com/litv/pdf/sprugm7d >>> + */ >>> + >>> +/* local reset bit (0 is asserted) in MDCTL15 register (section >> 9.6.18) */ >>> +#define LRST BIT(8) >> Perhaps this should be named nLRST or something if the sense is inverted? > If there is an established naming convention for this, I'll adopt it. Looking into my old PSC manual (can't get the recent documentation from TI's site right now), the bit is called LRSTz. It's worth moving this #define into as well. >>> +/* register for DSP boot address in SYSCFG0 module (section 11.5.6) >> */ >>> +#define HOST1CFG 0x44 >> Worth declaring in instead... > Possibly - since it is only used for the DSP, I thought it would be better > to keep local to this implementation. I'll adopt whichever approach is the > convention. Well, the general approach is to keep the #define's where they are used, so maybe we should keep this #define here. >>> +static inline int davinci_rproc_start(struct rproc *rproc, u64 >> bootaddr) >>> +{ >>> + struct device *dev = rproc->dev; >>> + struct davinci_rproc_pdata *pdata = dev->platform_data; >>> + struct davinci_soc_info *soc_info = &davinci_soc_info; >>> + void __iomem *psc_base; >>> + struct clk *dsp_clk; >>> + >>> + /* hw requires the start (boot) address be on 1KB boundary */ >>> + if (bootaddr & 0x3ff) { >>> + dev_err(dev, "invalid boot address: must be aligned to >> 1KB\n"); >>> + return -EINVAL; >>> + } >>> + >>> + dsp_clk = clk_get(dev, pdata->clk_name); >> We could match using clkdev functionality, but the clock entry >> would need to be changed then... > I followed the existing pattern I saw in other drivers. Probably MUSB? We're trying to move away from passing the clock name to thge drivers, using match by device instead. > If there is a new, better way, please point me to an example. Look at the da850_clks[] in mach-davinci/da850.c: if the device name is specified as a first argument to CLK() macro (instead of clock name the second argument), the matching is done by device, so you don't need to specify the clock name to clk_get(), passing NULL instead. >>> + if (IS_ERR_OR_NULL(dsp_clk)) { >>> + dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk)); >>> + return PTR_ERR(dsp_clk); >>> + } >>> + >>> + clk_enable(dsp_clk); >> This seems rather senseless activity as on DA8xx the DSP core >> boots the ARM core, so the DSP clock will be already enabled. > I think it is needed. It's true that the DSP initiates the boot, but then it is > reset and the clock disabled. See Section 13.2 of Hm, didn't know that. Contrarywise, we had to work around the races between ARM and DSP cores on accessing locked SYSCFG registers -- in kernel. So I was under impression that DSP code continues running some stuff of its own. > http://focus.ti.com/lit/ug/sprugm7e/sprugm7e.pdf: > 13.2 DSP Wake Up > Following deassertion of device reset, the DSP intializes the ARM296 so that > it can execute the ARM ROM bootloader. Upon successful wake up, the ARM > places the DSP in a reset and clock gated (SwRstDisable) state that is > controlled by the LPSC and the SYSCFG modules. > Besides, the boot loader could have disabled it to save power. The ARM and > DSP are clocked independently, so I think it's best to use clock management. OK, agreed. >>> + rproc->priv = dsp_clk; >>> + >>> + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K); >>> + >>> + /* insure local reset is asserted before writing start address */ >>> + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 * >> DA8XX_LPSC0_GEM); >>> + >>> + __raw_writel(bootaddr, DA8XX_SYSCFG0_VIRT(HOST1CFG)); >> DA8XX_SYSCFG0_VIRT() is not supposed to be used outside mach-davinci. The >> variable it refers is not exported, so driver module won't work. > Ooops, I clearly did not build this as a module. Suggestion how to fix this? Using the normal ioremap() of SYSCFG0 space, I suppose. > Mark WBR, Sergei From nsekhar at ti.com Fri Jun 24 10:43:49 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 24 Jun 2011 21:13:49 +0530 Subject: [RFC 5/8] remoteproc: add davinci implementation In-Reply-To: <4E04A9AE.3030801@mvista.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-6-git-send-email-ohad@wizery.com> <4E035B78.3080200@mvista.com> <4E04A9AE.3030801@mvista.com> Message-ID: Hi Mark, On Fri, Jun 24, 2011 at 20:43:50, Sergei Shtylyov wrote: > >>> + rproc->priv = dsp_clk; > >>> + > >>> + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K); > >>> + > >>> + /* insure local reset is asserted before writing start address */ > >>> + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 * > >> DA8XX_LPSC0_GEM); > >>> + > >>> + __raw_writel(bootaddr, DA8XX_SYSCFG0_VIRT(HOST1CFG)); > > >> DA8XX_SYSCFG0_VIRT() is not supposed to be used outside mach-davinci. The > >> variable it refers is not exported, so driver module won't work. > > > Ooops, I clearly did not build this as a module. Suggestion how to fix this? > > Using the normal ioremap() of SYSCFG0 space, I suppose. Since procedure to set the boot address varies across DaVinci platforms, you could have a callback populated in platform data which will be implemented differently for original DaVinci and DA8xx devices. Also, all PSC accesses are better off going through clock framework to ensure proper locking and modularity. To assert/de-assert local reset when enabling or disabling PSC, you could use a flag in the clock structure to indicate the need for this. This way, if there is any other module needing a local reset, it can just define the same flag. Similarly, if the DSP does not need a local reset on a particular platform, that platform can simply skip the flag. This can be done in a manner similar to how the support for a forced transition PSC was added here: https://patchwork.kernel.org/patch/662941/ Thanks, Sekhar From nsekhar at ti.com Fri Jun 24 11:45:27 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 24 Jun 2011 22:15:27 +0530 Subject: [PATCH 1/4] davinci: psc.h: clean up indentation done using spaces In-Reply-To: <1301072400-4036-1-git-send-email-nsekhar@ti.com> References: <1301072400-4036-1-git-send-email-nsekhar@ti.com> Message-ID: Hi Russell, SubArch team, This series of 4 patches added SATA support on DaVinci DA850 platform. This was ready for last merge window, but was not queued because of the negative diffstat rule. Is there a possibility of queuing this for the v3.1 merge? Here is the archive link of this posting: http://linux.davincidsp.com/pipermail/davinci-linux-open-source/2011-March/022473.html Here is the summary diffstat: arch/arm/mach-davinci/board-da850-evm.c | 7 ++ arch/arm/mach-davinci/clock.c | 8 +- arch/arm/mach-davinci/clock.h | 1 + arch/arm/mach-davinci/da850.c | 9 ++ arch/arm/mach-davinci/devices-da8xx.c | 122 ++++++++++++++++++++++ arch/arm/mach-davinci/include/mach/da8xx.h | 2 + arch/arm/mach-davinci/include/mach/psc.h | 151 ++++++++++++++-------------- arch/arm/mach-davinci/psc.c | 4 +- 8 files changed, 225 insertions(+), 79 deletions(-) Thanks, Sekhar From const at makelinux.com Fri Jun 24 13:51:31 2011 From: const at makelinux.com (Constantine Shulyupin) Date: Fri, 24 Jun 2011 21:51:31 +0300 Subject: [PATCH] Enable USB on TI DM365 Message-ID: Enable USB on TI DM365 Signed-off-by: Constantine Shulyupin --- arch/arm/mach-davinci/Makefile | 3 +- arch/arm/mach-davinci/include/mach/usb.h | 2 +- arch/arm/mach-davinci/usb.c | 32 ++++++++++++++++++++++++++--- drivers/usb/musb/davinci.c | 13 ++++++++++++ drivers/usb/musb/davinci.h | 1 + 5 files changed, 45 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile index 0b87a1c..52961a8 100644 --- a/arch/arm/mach-davinci/Makefile +++ b/arch/arm/mach-davinci/Makefile @@ -5,7 +5,7 @@ # Common objects obj-y := time.o clock.o serial.o io.o psc.o \ - gpio.o dma.o usb.o common.o sram.o aemif.o + gpio.o dma.o common.o sram.o aemif.o obj-$(CONFIG_DAVINCI_MUX) += mux.o @@ -40,3 +40,4 @@ obj-$(CONFIG_MACH_OMAPL138_HAWKBOARD) += board-omapl138-hawk.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_SUSPEND) += pm.o sleep.o +obj-$(CONFIG_USB_MUSB_DAVINCI) += usb.o diff --git a/arch/arm/mach-davinci/include/mach/usb.h b/arch/arm/mach-davinci/include/mach/usb.h index e0bc4ab..863c27f 100644 --- a/arch/arm/mach-davinci/include/mach/usb.h +++ b/arch/arm/mach-davinci/include/mach/usb.h @@ -54,6 +54,6 @@ struct da8xx_ohci_root_hub { u8 potpgt; }; -void davinci_setup_usb(unsigned mA, unsigned potpgt_ms); +int davinci_setup_usb(unsigned mA, unsigned potpgt_ms); #endif /* ifndef __ASM_ARCH_USB_H */ diff --git a/arch/arm/mach-davinci/usb.c b/arch/arm/mach-davinci/usb.c index 23d2b6d..8b12206 100644 --- a/arch/arm/mach-davinci/usb.c +++ b/arch/arm/mach-davinci/usb.c @@ -4,19 +4,22 @@ #include #include #include - #include #include #include #include #include +#include +#include #define DAVINCI_USB_OTG_BASE 0x01c64000 #define DA8XX_USB0_BASE 0x01e00000 #define DA8XX_USB1_BASE 0x01e25000 +static int retval; + #if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE) static struct musb_hdrc_eps_bits musb_eps[] = { { "ep1_tx", 8, }, @@ -87,7 +90,7 @@ static struct platform_device usb_dev = { .num_resources = ARRAY_SIZE(usb_resources), }; -void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) +int __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) { usb_data.power = mA > 510 ? 255 : mA / 2; usb_data.potpgt = (potpgt_ms + 1) / 2; @@ -99,7 +102,8 @@ void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) } else /* other devices don't have dedicated CPPI IRQ */ usb_dev.num_resources = 2; - platform_device_register(&usb_dev); + retval = platform_device_register(&usb_dev); + return retval; } #ifdef CONFIG_ARCH_DAVINCI_DA8XX @@ -132,7 +136,7 @@ int __init da8xx_register_usb20(unsigned mA, unsigned potpgt) #else -void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) +int __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) { } @@ -178,3 +182,23 @@ int __init da8xx_register_usb11(struct da8xx_ohci_root_hub *pdata) return platform_device_register(&da8xx_usb11_device); } #endif /* CONFIG_DAVINCI_DA8XX */ + +#ifdef CONFIG_MACH_DAVINCI_DM365_EVM +int __init dm365evm_usb_configure(void) +{ + davinci_cfg_reg(DM365_GPIO33); + gpio_request(33, "usb"); + gpio_direction_output(33, 1); + retval = davinci_setup_usb(500, 8); + return retval; +} +subsys_initcall(dm365evm_usb_configure); + +static void __exit dm365evm_usb_exit(void) +{ + platform_device_unregister(&usb_dev); +} +module_exit(dm365evm_usb_exit); + +#endif +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 2a2adf6..0147f5c 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -72,6 +72,16 @@ static inline void phy_on(void) /* power everything up; start the on-chip PHY and its PLL */ phy_ctrl &= ~(USBPHY_OSCPDWN | USBPHY_OTGPDWN | USBPHY_PHYPDWN); phy_ctrl |= USBPHY_SESNDEN | USBPHY_VBDTCTEN | USBPHY_PHYPLLON; + + if (cpu_is_davinci_dm365()) { + /* + * DM365 PHYCLKFREQ field [15:12] is set to 2 + * to get clock from 24MHz crystal + */ + phy_ctrl |= USBPHY_CLKFREQ_24MHZ; + /*phy_ctrl &= ~USBPHY_PHYPDWN;*/ + } + __raw_writel(phy_ctrl, USB_PHY_CTRL); /* wait for PLL to lock before proceeding */ @@ -193,6 +203,9 @@ static void davinci_musb_source_power(struct musb *musb, int is_on, int immediat else schedule_work(&evm_vbus_work); } + + if (cpu_is_davinci_dm365()) + gpio_set_value(33, is_on); if (immediate) vbus_state = is_on; #endif diff --git a/drivers/usb/musb/davinci.h b/drivers/usb/musb/davinci.h index 046c844..1bf50e6 100644 --- a/drivers/usb/musb/davinci.h +++ b/drivers/usb/musb/davinci.h @@ -17,6 +17,7 @@ /* Integrated highspeed/otg PHY */ #define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34) #define USBPHY_DATAPOL BIT(11) /* (dm355) switch D+/D- */ +#define USBPHY_CLKFREQ_24MHZ BIT(13) #define USBPHY_PHYCLKGD BIT(8) #define USBPHY_SESNDEN BIT(7) /* v(sess_end) comparator */ #define USBPHY_VBDTCTEN BIT(6) /* v(bus) comparator */ -- 1.7.0.4 -- Constantine Shulyupin http://www.MakeLinux.com/ Embedded Linux Systems, Device Drivers, TI DaVinci From bsky-fish at 163.com Sat Jun 25 05:22:02 2011 From: bsky-fish at 163.com (bskyfish) Date: Sat, 25 Jun 2011 18:22:02 +0800 (CST) Subject: dm6446 USB mouse VBUS_ERR Message-ID: <5da0e1f.4428.130c652075d.Coremail.bsky-fish@163.com> hi,everyone my board is not using the usb mouse, the terminal print VBUS_ERR. please help me! ======================================================= Starting kernel ... Uncompressing Linux.............................................................Linux version 2.6.10_mvl401-davinci_evm (root at localhost.localdomain) (gcc version 3.4.3 (MontaVista 3.4.3-25.0.30.0501131 2005-07-23)) #46 Thu Oct 23 04:34:17 EDT 2008 CPU: ARM926EJ-Sid(wb) [41069265] revision 5 (ARMv5TEJ) CPU0: D VIVT write-back cache CPU0: I cache: 16384 bytes, associativity 4, 32 byte lines, 128 sets CPU0: D cache: 8192 bytes, associativity 4, 32 byte lines, 64 sets Machine: DaVinci EVM Memory policy: ECC disabled, Data cache writeback Built 1 zonelists Kernel command line: mem=60M console=ttyS0,115200n8 root=/dev/nfs noinitrd rw ip=192.168.1.197:255.255.255.0:192.168.1.1 nfsroot=192.168.1.196:/opt/nfs video=dm64xxfb:output=pal PID hash table entries: 256 (order: 8, 4096 bytes) Console: colour dummy device 80x30 Dentry cache hash table entries: 8192 (order: 3, 32768 bytes) Inode-cache hash table entries: 4096 (order: 2, 16384 bytes) Memory: 60MB = 60MB total Memory: 57472KB available (2577K code, 629K data, 144K init) Mount-cache hash table entries: 512 (order: 0, 4096 bytes) CPU: Testing write buffer coherency: ok spawn_desched_task(00000000) desched cpu_callback 3/00000000 ksoftirqd started up. desched cpu_callback 2/00000000 desched thread 0 started up. NET: Registered protocol family 16 Registering platform device 'nor_davinci.0'. Parent at platform Registering platform device 'nand_davinci.0'. Parent at platform Registering platform device 'musb_hdrc'. Parent at platform DaVinci I2C DEBUG: 12:14:45 May 16 2008 Registering platform device 'i2c'. Parent at platform SCSI subsystem initialized usbcore: registered new driver usbfs usbcore: registered new driver hub NTFS driver 2.1.22 [Flags: R/W]. JFFS2 version 2.2. (NAND) (C) 2001-2003 Red Hat, Inc. yaffs May 16 2008 12:14:06 Installing. Registering platform device 'davincifb.0'. Parent at platform Console: switching to colour frame buffer device 90x36 Registering platform device 'davinci_resizer.2'. Parent at platform Registering platform device 'davinci_previewer.2'. Parent at platform Registering platform device 'davinci_pwm0.0'. Parent at platform Registering platform device 'davinci_pwm1.1'. Parent at platform Registering platform device 'davinci_pwm2.2'. Parent at platform Serial: 8250/16550 driver $Revision: 1.90 $ 2 ports, IRQ sharing disabled Registering platform device 'serial8250'. Parent at platform ttyS0 at MMIO 0x1c20000 (irq = 40) is a 16550A ttyS1 at MMIO 0x1c20400 (irq = 41) is a 16550A io scheduler noop registered io scheduler anticipatory registered RAMDISK driver initialized: 1 RAM disks of 32768K size 1024 blocksize Registering platform device 'ti_davinci_emac'. Parent at platform TI DaVinci EMAC Linux version updated 4.0 TI DaVinci EMAC: Installed 1 instances. netconsole: not configured, aborting i2c /dev entries driver Linux video capture interface: v1.00 Registering platform device 'vpfe.1'. Parent at platform TVP5150 support for SEED_DVS6446 by Stephen Zhang DaVinci v4l2 capture driver V1.0 loaded Uniform Multi-Platform E-IDE driver Revision: 7.00alpha2 ide: Assuming 50MHz system bus speed for PIO modes; override with idebus=xx hda: CSD CAV040H, ATA DISK drive elevator: using anticipatory as default io scheduler ide0 at 0xe10661f0-0xe10661f7,0xe10663f6 on irq 22 hda: max request size: 128KiB hda: 78140160 sectors (40007 MB) w/8192KiB Cache, CHS=65535/16/63 hda: hda1 hda2 ide0: BM-DMA at 0xe1066000-0xe1066007, BIOS settings: hda:pio, hdb:pio Initializing USB Mass Storage driver... usbcore: registered new driver usb-storage USB Mass Storage support registered. usbcore: registered new driver usbhid drivers/usb/input/hid-core.c: v2.0:USB HID core driver mice: PS/2 mouse device common for all mice musb_hdrc: version 2.2a/db-0.4.8 [pio] [host] [debug=0] musb_hdrc: USB Host mode controller at c4060000 using PIO, IRQ 12 musb_hdrc musb_hdrc: new USB bus registered, assigned bus number 1 hub 1-0:1.0: USB hub found hub 1-0:1.0: 1 port detected VBUS_ERR Registering platform device 'mmc0.1'. Parent at platform : Supporting 4-bit mode Registering platform device 'davinci-audio.0'. Parent at platform DAVINCI AIC23 audio support initialized NET: Registered protocol family 2 IP: routing cache hash table of 512 buckets, 4Kbytes TCP: Hash tables configured (established 4096 bind 8192) NET: Registered protocol family 1 NET: Registered protocol family 17 IP-Config: Guessing netmask 255.255.255.0 IP-Config: Complete: device=eth0, addr=192.168.1.197, mask=255.255.255.0, gw=192.168.1.1, host=192.168.1.197, domain=, nis-domain=(none), bootserver=255.255.255.0, rootserver=192.168.1.196, rootpath= Looking up port of RPC 100003/2 on 192.168.1.196 Looking up port of RPC 100005/1 on 192.168.1.196 VFS: Mounted root (nfs filesystem). Freeing init memory: 144K INIT: version 2.85 booting Mounting a tmpfs over /dev...done. Creating initial device nodes...done. Activating swap...done. Remounting root filesystem...done. Calculating module dependencies WARNING: Couldn't open directory /lib/modules/2.6.10_mvl401-davinci_evm: No such file or directory FATAL: Could not open /lib/modules/2.6.10_mvl401-davinci_evm/modules.dep.temp for writing: No such file or directory Loading modules: Starting checking all file systems: fsck fsck 1.35 (28-Feb-2004) Starting mounting local filesystems: mount nothing was mounted Cleaning: /etc/network/ifstate. Setting up IP spoofing protection: rp_filter. Disable TCP/IP Explicit Congestion Notification: done. Starting network interfaces: done. Starting hotplug subsystem: pci pci [success] usb usb [success] isapnp isapnp [success] ide ide [success] input input [success] scsi scsi [success] done. Starting portmap daemon: portmap. Cleaning: /tmp /var/lock /var/run done. Setting pseudo-terminal access permissions...done. Updating /etc/motd...done. INIT: Entering runlevel: 3 Starting system log daemon: syslogd klogd. Starting NFS common utilities: statd lockd. Starting internet superserver: inetd. Starting MontaVista target tools daemon: mvltdmvltd version 2.1 MontaVista Software,Inc. . MontaVista(R) Linux(R) Professional Edition 4.0 (0501140) ======================================================= -------------- next part -------------- An HTML attachment was scrubbed... URL: From arnd at arndb.de Sat Jun 25 12:48:19 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Sat, 25 Jun 2011 19:48:19 +0200 Subject: [PATCH 1/4] davinci: psc.h: clean up indentation done using spaces In-Reply-To: References: <1301072400-4036-1-git-send-email-nsekhar@ti.com> Message-ID: <201106251948.19834.arnd@arndb.de> On Friday 24 June 2011, Nori, Sekhar wrote: > This series of 4 patches added SATA support on DaVinci > DA850 platform. This was ready for last merge window, > but was not queued because of the negative diffstat > rule. > > Is there a possibility of queuing this for the v3.1 merge? > > Here is the archive link of this posting: > > http://linux.davincidsp.com/pipermail/davinci-linux-open-source/2011-March/022473.html My general idea is to have multiple branches per subarchitecture, and I started merging stuff into the tree now. Your series contains both cleanups and support for additional features, and I'd like to see those split into separate branches. Please make sure the patches apply on top of 2.6.39 and resubmit them, separating out the first cleanup patch from the others. We can definitely forward all cleanups and bug fixes in the each merge window. For new features, it mostly depends on how much code gets added that should not really be needed in a perfect world, relative to the amount of cleanups that is actually going into the platform in order to get there. In case of the SATA support, the feature is relatively small and requires little board-specific code, which is good. However, if you are planning to push a lot of small additions like this, I would expect you spend much more effort on consolidation within davinci and migration towards device tree probing first. What else do you have pending for davinci in 3.1? Can you send pull requests for cleanups/fixes/device-tree/features? Arnd From ohad at wizery.com Sat Jun 25 20:11:47 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Sun, 26 Jun 2011 04:11:47 +0300 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: <4E04F0B0.4030408@codeaurora.org> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <4E04F0B0.4030408@codeaurora.org> Message-ID: Hi Stephen, On Fri, Jun 24, 2011 at 11:16 PM, Stephen Boyd wrote: > This sounds a lot like SMD (shared memory driver) on MSM. The main > difference I see is that SMD uses the platform bus instead of the virtio > bus and it has its own protocol for channel allocation. Yeah, virtio is a key factor in this work; it was suggested to us by Arnd at the AMP plumbers discussions last year, where it was apparent that many vendors have their own IPC drivers/buses/channels over shared memory with some vendor-ish binary protocol. I must say we really liked virtio: it considerably simplified the code (we're not adding any new binary protocol), it's very nicely optimized and flexible, and it comes with a set of virtio drivers (e.g. network, console, block) so we don't have to write our own. We also cared about adding this functionality as an IPC bus, so the driver core will help matching drivers to channels - it simplified the code (in both setup and tear down of channels) and kept it flexible. It will also facilitate error recovery (on remote crash, we just remove the virtio device, and then the driver core will in turn start ->remove()ing the rpmsg drivers) and power management (via runtime PM). About SMD: I'm not familiar with it too much, but Brian naturally is (just for the sake of everyone who are not reading headers - Brian Swetland wrote the Linux SMD driver, and is also an author of this Google+TI joint work). Btw, I'm sure SMD is conceptually not MSM-specific, and have wondered whether you guys would like to use rpmsg/virtio (I know you have several drivers like network/tty/etc over SMD, somewhat similarly to virtio). Probably the biggest reason why not to is the pain in changing the binary protocol with the modem/dsp side. If you ever do think about it, I'd be happy to work with you to make it happen. > This remote proc code is eerily similar to PIL (peripheral image loader, > yes we love our acronyms) which I posted a few months back[1]. Was it > inspiration for this patch series? No, we weren't (or at least I wasn't) aware of PIL. > In terms of API, s/pil/rproc/ and it would be 95% identical. There are > some low-level differences though (see below). Indeed, eerily similar :O I just guess the API is so simple that probably most kernel hackers would use refcounting get/put semantics here. > This is an important difference between remote proc and PIL. In PIL, we > do image authentication in addition to processor boot. Yes, we have this too (secure code will need to authenticate the image in some use cases) - but that's not ready yet for submission and we decided to start off with the basics first and then evolve. > Instead of devising a new firmware format, we decided > to just stick with elf and parse the headers in the kernel because we > needed them for authentication anyway. Is this reason enough to move to > an ELF format instead? I think that consolidation of code is enough reason to make an effort. I know that our firmware format was chosen for simplicity, but I'm not sure if we have the tools yet to build standard ELF files for the remote processors (IIRC it's in the works though). I'll let Mark comment this one. > Another difference is inter-processor dependencies. For example, on > msm8660 the modem can't boot until the dsp has been booted. I suppose we > could hide this detail in the platform specific get() implementation by > calling rproc_get() on the dependent processor (hopefully no locking > issues arise). I'd rather have it built into the core though as it isn't > really specific to the hardware. No problems, I'm sure we can solve this one easily. > If we can resolve these differences I think we can easily support remote > processor boot on MSM via remoteproc. That'd be very cool, I sure do hope we can work together. Thanks for your comments ! Ohad. From bsky-fish at 163.com Sat Jun 25 22:12:56 2011 From: bsky-fish at 163.com (bskyfish) Date: Sun, 26 Jun 2011 11:12:56 +0800 (CST) Subject: Davinci-linux-open-source Digest, Vol 66, Issue 32 In-Reply-To: References: Message-ID: <1db254b.49b3.130c9ef8891.Coremail.bsky-fish@163.com> hi? Unless I power on the board and reboot it using "reboot" on the terminal, the usb mouse can be found and used. every time,I must power and reboot, in fact i do the same thing twice, but why? and how to avoid doing that? Thx! At 2011-06-26?davinci-linux-open-source-request at linux.davincidsp.com wrote: >Send Davinci-linux-open-source mailing list submissions to > davinci-linux-open-source at linux.davincidsp.com > >To subscribe or unsubscribe via the World Wide Web, visit > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source > >or, via email, send a message with subject or body 'help' to > davinci-linux-open-source-request at linux.davincidsp.com > >You can reach the person managing the list at > davinci-linux-open-source-owner at linux.davincidsp.com > >When replying, please edit your Subject line so it is more specific >than "Re: Contents of Davinci-linux-open-source digest..." > > >Today's Topics: > > 1. [PATCH] Enable USB on TI DM365 (Constantine Shulyupin) > 2. dm6446 USB mouse VBUS_ERR (bskyfish) > > >---------------------------------------------------------------------- > >Message: 1 >Date: Fri, 24 Jun 2011 21:51:31 +0300 >From: Constantine Shulyupin >To: linux-kernel at vger.kernel.org, linux-embedded at vger.kernel.org, > davinci-linux-open-source at linux.davincidsp.com >Subject: [PATCH] Enable USB on TI DM365 >Message-ID: >Content-Type: text/plain; charset=ISO-8859-1 > >Enable USB on TI DM365 > >Signed-off-by: Constantine Shulyupin >--- > arch/arm/mach-davinci/Makefile | 3 +- > arch/arm/mach-davinci/include/mach/usb.h | 2 +- > arch/arm/mach-davinci/usb.c | 32 ++++++++++++++++++++++++++--- > drivers/usb/musb/davinci.c | 13 ++++++++++++ > drivers/usb/musb/davinci.h | 1 + > 5 files changed, 45 insertions(+), 6 deletions(-) > >diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile >index 0b87a1c..52961a8 100644 >--- a/arch/arm/mach-davinci/Makefile >+++ b/arch/arm/mach-davinci/Makefile >@@ -5,7 +5,7 @@ > > # Common objects > obj-y := time.o clock.o serial.o io.o psc.o \ >- gpio.o dma.o usb.o common.o sram.o aemif.o >+ gpio.o dma.o common.o sram.o aemif.o > > obj-$(CONFIG_DAVINCI_MUX) += mux.o > >@@ -40,3 +40,4 @@ obj-$(CONFIG_MACH_OMAPL138_HAWKBOARD) += board-omapl138-hawk.o > obj-$(CONFIG_CPU_FREQ) += cpufreq.o > obj-$(CONFIG_CPU_IDLE) += cpuidle.o > obj-$(CONFIG_SUSPEND) += pm.o sleep.o >+obj-$(CONFIG_USB_MUSB_DAVINCI) += usb.o >diff --git a/arch/arm/mach-davinci/include/mach/usb.h >b/arch/arm/mach-davinci/include/mach/usb.h >index e0bc4ab..863c27f 100644 >--- a/arch/arm/mach-davinci/include/mach/usb.h >+++ b/arch/arm/mach-davinci/include/mach/usb.h >@@ -54,6 +54,6 @@ struct da8xx_ohci_root_hub { > u8 potpgt; > }; > >-void davinci_setup_usb(unsigned mA, unsigned potpgt_ms); >+int davinci_setup_usb(unsigned mA, unsigned potpgt_ms); > > #endif /* ifndef __ASM_ARCH_USB_H */ >diff --git a/arch/arm/mach-davinci/usb.c b/arch/arm/mach-davinci/usb.c >index 23d2b6d..8b12206 100644 >--- a/arch/arm/mach-davinci/usb.c >+++ b/arch/arm/mach-davinci/usb.c >@@ -4,19 +4,22 @@ > #include > #include > #include >- > #include > > #include > #include > #include > #include >+#include >+#include > > #define DAVINCI_USB_OTG_BASE 0x01c64000 > > #define DA8XX_USB0_BASE 0x01e00000 > #define DA8XX_USB1_BASE 0x01e25000 > >+static int retval; >+ > #if defined(CONFIG_USB_MUSB_HDRC) || defined(CONFIG_USB_MUSB_HDRC_MODULE) > static struct musb_hdrc_eps_bits musb_eps[] = { > { "ep1_tx", 8, }, >@@ -87,7 +90,7 @@ static struct platform_device usb_dev = { > .num_resources = ARRAY_SIZE(usb_resources), > }; > >-void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) >+int __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) > { > usb_data.power = mA > 510 ? 255 : mA / 2; > usb_data.potpgt = (potpgt_ms + 1) / 2; >@@ -99,7 +102,8 @@ void __init davinci_setup_usb(unsigned mA, unsigned >potpgt_ms) > } else /* other devices don't have dedicated CPPI IRQ */ > usb_dev.num_resources = 2; > >- platform_device_register(&usb_dev); >+ retval = platform_device_register(&usb_dev); >+ return retval; > } > > #ifdef CONFIG_ARCH_DAVINCI_DA8XX >@@ -132,7 +136,7 @@ int __init da8xx_register_usb20(unsigned mA, >unsigned potpgt) > > #else > >-void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) >+int __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) > { > } > >@@ -178,3 +182,23 @@ int __init da8xx_register_usb11(struct >da8xx_ohci_root_hub *pdata) > return platform_device_register(&da8xx_usb11_device); > } > #endif /* CONFIG_DAVINCI_DA8XX */ >+ >+#ifdef CONFIG_MACH_DAVINCI_DM365_EVM >+int __init dm365evm_usb_configure(void) >+{ >+ davinci_cfg_reg(DM365_GPIO33); >+ gpio_request(33, "usb"); >+ gpio_direction_output(33, 1); >+ retval = davinci_setup_usb(500, 8); >+ return retval; >+} >+subsys_initcall(dm365evm_usb_configure); >+ >+static void __exit dm365evm_usb_exit(void) >+{ >+ platform_device_unregister(&usb_dev); >+} >+module_exit(dm365evm_usb_exit); >+ >+#endif >+MODULE_LICENSE("GPL"); >diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c >index 2a2adf6..0147f5c 100644 >--- a/drivers/usb/musb/davinci.c >+++ b/drivers/usb/musb/davinci.c >@@ -72,6 +72,16 @@ static inline void phy_on(void) > /* power everything up; start the on-chip PHY and its PLL */ > phy_ctrl &= ~(USBPHY_OSCPDWN | USBPHY_OTGPDWN | USBPHY_PHYPDWN); > phy_ctrl |= USBPHY_SESNDEN | USBPHY_VBDTCTEN | USBPHY_PHYPLLON; >+ >+ if (cpu_is_davinci_dm365()) { >+ /* >+ * DM365 PHYCLKFREQ field [15:12] is set to 2 >+ * to get clock from 24MHz crystal >+ */ >+ phy_ctrl |= USBPHY_CLKFREQ_24MHZ; >+ /*phy_ctrl &= ~USBPHY_PHYPDWN;*/ >+ } >+ > __raw_writel(phy_ctrl, USB_PHY_CTRL); > > /* wait for PLL to lock before proceeding */ >@@ -193,6 +203,9 @@ static void davinci_musb_source_power(struct musb >*musb, int is_on, int immediat > else > schedule_work(&evm_vbus_work); > } >+ >+ if (cpu_is_davinci_dm365()) >+ gpio_set_value(33, is_on); > if (immediate) > vbus_state = is_on; > #endif >diff --git a/drivers/usb/musb/davinci.h b/drivers/usb/musb/davinci.h >index 046c844..1bf50e6 100644 >--- a/drivers/usb/musb/davinci.h >+++ b/drivers/usb/musb/davinci.h >@@ -17,6 +17,7 @@ > /* Integrated highspeed/otg PHY */ > #define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34) > #define USBPHY_DATAPOL BIT(11) /* (dm355) switch D+/D- */ >+#define USBPHY_CLKFREQ_24MHZ BIT(13) > #define USBPHY_PHYCLKGD BIT(8) > #define USBPHY_SESNDEN BIT(7) /* v(sess_end) comparator */ > #define USBPHY_VBDTCTEN BIT(6) /* v(bus) comparator */ >-- >1.7.0.4 > > >-- >Constantine Shulyupin >http://www.MakeLinux.com/ >Embedded Linux Systems, >Device Drivers, TI DaVinci > > >------------------------------ > >Message: 2 >Date: Sat, 25 Jun 2011 18:22:02 +0800 (CST) >From: bskyfish >To: davinci-linux-open-source at linux.davincidsp.com >Subject: dm6446 USB mouse VBUS_ERR >Message-ID: <5da0e1f.4428.130c652075d.Coremail.bsky-fish at 163.com> >Content-Type: text/plain; charset="gbk" > >hi,everyone >my board is not using the usb mouse, the terminal print VBUS_ERR. >please help me! > > >======================================================= >Starting kernel ... >Uncompressing Linux.............................................................Linux version 2.6.10_mvl401-davinci_evm (root at localhost.localdomain) (gcc version 3.4.3 (MontaVista 3.4.3-25.0.30.0501131 2005-07-23)) #46 Thu Oct 23 04:34:17 EDT 2008 >CPU: ARM926EJ-Sid(wb) [41069265] revision 5 (ARMv5TEJ) >CPU0: D VIVT write-back cache >CPU0: I cache: 16384 bytes, associativity 4, 32 byte lines, 128 sets >CPU0: D cache: 8192 bytes, associativity 4, 32 byte lines, 64 sets >Machine: DaVinci EVM >Memory policy: ECC disabled, Data cache writeback >Built 1 zonelists >Kernel command line: mem=60M console=ttyS0,115200n8 root=/dev/nfs noinitrd rw ip=192.168.1.197:255.255.255.0:192.168.1.1 nfsroot=192.168.1.196:/opt/nfs video=dm64xxfb:output=pal >PID hash table entries: 256 (order: 8, 4096 bytes) >Console: colour dummy device 80x30 >Dentry cache hash table entries: 8192 (order: 3, 32768 bytes) >Inode-cache hash table entries: 4096 (order: 2, 16384 bytes) >Memory: 60MB = 60MB total >Memory: 57472KB available (2577K code, 629K data, 144K init) >Mount-cache hash table entries: 512 (order: 0, 4096 bytes) >CPU: Testing write buffer coherency: ok >spawn_desched_task(00000000) >desched cpu_callback 3/00000000 >ksoftirqd started up. >desched cpu_callback 2/00000000 >desched thread 0 started up. >NET: Registered protocol family 16 >Registering platform device 'nor_davinci.0'. Parent at platform >Registering platform device 'nand_davinci.0'. Parent at platform >Registering platform device 'musb_hdrc'. Parent at platform >DaVinci I2C DEBUG: 12:14:45 May 16 2008 >Registering platform device 'i2c'. Parent at platform >SCSI subsystem initialized >usbcore: registered new driver usbfs >usbcore: registered new driver hub >NTFS driver 2.1.22 [Flags: R/W]. >JFFS2 version 2.2. (NAND) (C) 2001-2003 Red Hat, Inc. >yaffs May 16 2008 12:14:06 Installing. >Registering platform device 'davincifb.0'. Parent at platform >Console: switching to colour frame buffer device 90x36 >Registering platform device 'davinci_resizer.2'. Parent at platform >Registering platform device 'davinci_previewer.2'. Parent at platform >Registering platform device 'davinci_pwm0.0'. Parent at platform >Registering platform device 'davinci_pwm1.1'. Parent at platform >Registering platform device 'davinci_pwm2.2'. Parent at platform >Serial: 8250/16550 driver $Revision: 1.90 $ 2 ports, IRQ sharing disabled >Registering platform device 'serial8250'. Parent at platform >ttyS0 at MMIO 0x1c20000 (irq = 40) is a 16550A >ttyS1 at MMIO 0x1c20400 (irq = 41) is a 16550A >io scheduler noop registered >io scheduler anticipatory registered >RAMDISK driver initialized: 1 RAM disks of 32768K size 1024 blocksize >Registering platform device 'ti_davinci_emac'. Parent at platform >TI DaVinci EMAC Linux version updated 4.0 >TI DaVinci EMAC: Installed 1 instances. >netconsole: not configured, aborting >i2c /dev entries driver >Linux video capture interface: v1.00 >Registering platform device 'vpfe.1'. Parent at platform >TVP5150 support for SEED_DVS6446 by Stephen Zhang >DaVinci v4l2 capture driver V1.0 loaded >Uniform Multi-Platform E-IDE driver Revision: 7.00alpha2 >ide: Assuming 50MHz system bus speed for PIO modes; override with idebus=xx >hda: CSD CAV040H, ATA DISK drive >elevator: using anticipatory as default io scheduler >ide0 at 0xe10661f0-0xe10661f7,0xe10663f6 on irq 22 >hda: max request size: 128KiB >hda: 78140160 sectors (40007 MB) w/8192KiB Cache, CHS=65535/16/63 > hda: hda1 hda2 > ide0: BM-DMA at 0xe1066000-0xe1066007, BIOS settings: hda:pio, hdb:pio >Initializing USB Mass Storage driver... >usbcore: registered new driver usb-storage >USB Mass Storage support registered. >usbcore: registered new driver usbhid >drivers/usb/input/hid-core.c: v2.0:USB HID core driver >mice: PS/2 mouse device common for all mice >musb_hdrc: version 2.2a/db-0.4.8 [pio] [host] [debug=0] >musb_hdrc: USB Host mode controller at c4060000 using PIO, IRQ 12 >musb_hdrc musb_hdrc: new USB bus registered, assigned bus number 1 >hub 1-0:1.0: USB hub found >hub 1-0:1.0: 1 port detected >VBUS_ERR >Registering platform device 'mmc0.1'. Parent at platform > : Supporting 4-bit mode >Registering platform device 'davinci-audio.0'. Parent at platform >DAVINCI AIC23 audio support initialized >NET: Registered protocol family 2 >IP: routing cache hash table of 512 buckets, 4Kbytes >TCP: Hash tables configured (established 4096 bind 8192) >NET: Registered protocol family 1 >NET: Registered protocol family 17 >IP-Config: Guessing netmask 255.255.255.0 >IP-Config: Complete: > device=eth0, addr=192.168.1.197, mask=255.255.255.0, gw=192.168.1.1, > host=192.168.1.197, domain=, nis-domain=(none), > bootserver=255.255.255.0, rootserver=192.168.1.196, rootpath= >Looking up port of RPC 100003/2 on 192.168.1.196 >Looking up port of RPC 100005/1 on 192.168.1.196 >VFS: Mounted root (nfs filesystem). >Freeing init memory: 144K >INIT: version 2.85 booting >Mounting a tmpfs over /dev...done. >Creating initial device nodes...done. >Activating swap...done. >Remounting root filesystem...done. >Calculating module dependencies >WARNING: Couldn't open directory /lib/modules/2.6.10_mvl401-davinci_evm: No such file or directory >FATAL: Could not open /lib/modules/2.6.10_mvl401-davinci_evm/modules.dep.temp for writing: No such file or directory >Loading modules: >Starting checking all file systems: fsck >fsck 1.35 (28-Feb-2004) >Starting mounting local filesystems: mount nothing was mounted >Cleaning: /etc/network/ifstate. >Setting up IP spoofing protection: rp_filter. >Disable TCP/IP Explicit Congestion Notification: done. >Starting network interfaces: done. >Starting hotplug subsystem: > pci > pci [success] > usb > usb [success] > isapnp > isapnp [success] > ide > ide [success] > input > input [success] > scsi > scsi [success] >done. >Starting portmap daemon: portmap. >Cleaning: /tmp /var/lock /var/run done. >Setting pseudo-terminal access permissions...done. >Updating /etc/motd...done. >INIT: Entering runlevel: 3 >Starting system log daemon: syslogd klogd. >Starting NFS common utilities: statd lockd. >Starting internet superserver: inetd. >Starting MontaVista target tools daemon: mvltdmvltd version 2.1 MontaVista Software,Inc. >. >MontaVista(R) Linux(R) Professional Edition 4.0 (0501140) >======================================================= > > >-------------- next part -------------- >An HTML attachment was scrubbed... >URL: > >------------------------------ > >_______________________________________________ >Davinci-linux-open-source mailing list >Davinci-linux-open-source at linux.davincidsp.com >http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source > > >End of Davinci-linux-open-source Digest, Vol 66, Issue 32 >********************************************************* -------------- next part -------------- An HTML attachment was scrubbed... URL: From manjunath.hadli at ti.com Mon Jun 27 01:45:40 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Mon, 27 Jun 2011 12:15:40 +0530 Subject: [RESEND PATCH v19 0/6] davinci vpbe: dm6446 v4l2 driver In-Reply-To: <4E00EFD1.40307@redhat.com> Message-ID: Thanks Mauro. I will make sure I send you a pull request from a git tree. -Manju On Wed, Jun 22, 2011 at 00:54:01, Mauro Carvalho Chehab wrote: > Em 17-06-2011 04:03, Hadli, Manjunath escreveu: > > Mauro, > > > > Can you consider this patch series for a pull? > > Next time, could you please add on your tree and send me a git pull request? > > Patchwork is currently not reliable. I have a backup process, but a git pull request works better and I won't have the risk of applying the wrong patches or at a wrong order. > > In this specific case, as all patches were caught by patchwork, I'll apply from your emails after reviewing them. > > Thanks, > Mauro > > > > > -Manju > > > > On Fri, Jun 17, 2011 at 12:31:30, Hadli, Manjunath wrote: > >> fixed a wrong file inclusion in one of the patches > >> > >> Manjunath Hadli (6): > >> davinci vpbe: V4L2 display driver for DM644X SoC > >> davinci vpbe: VPBE display driver > >> davinci vpbe: OSD(On Screen Display) block > >> davinci vpbe: VENC( Video Encoder) implementation > >> davinci vpbe: Build infrastructure for VPBE driver > >> davinci vpbe: Readme text for Dm6446 vpbe > >> > >> Documentation/video4linux/README.davinci-vpbe | 93 ++ > >> drivers/media/video/davinci/Kconfig | 23 + > >> drivers/media/video/davinci/Makefile | 2 + > >> drivers/media/video/davinci/vpbe.c | 864 ++++++++++++ > >> drivers/media/video/davinci/vpbe_display.c | 1860 +++++++++++++++++++++++++ > >> drivers/media/video/davinci/vpbe_osd.c | 1231 ++++++++++++++++ > >> drivers/media/video/davinci/vpbe_osd_regs.h | 364 +++++ > >> drivers/media/video/davinci/vpbe_venc.c | 566 ++++++++ > >> drivers/media/video/davinci/vpbe_venc_regs.h | 177 +++ > >> include/media/davinci/vpbe.h | 184 +++ > >> include/media/davinci/vpbe_display.h | 147 ++ > >> include/media/davinci/vpbe_osd.h | 394 ++++++ > >> include/media/davinci/vpbe_types.h | 91 ++ > >> include/media/davinci/vpbe_venc.h | 45 + > >> 14 files changed, 6041 insertions(+), 0 deletions(-) create mode > >> 100644 Documentation/video4linux/README.davinci-vpbe > >> create mode 100644 drivers/media/video/davinci/vpbe.c > >> create mode 100644 drivers/media/video/davinci/vpbe_display.c > >> create mode 100644 drivers/media/video/davinci/vpbe_osd.c > >> create mode 100644 drivers/media/video/davinci/vpbe_osd_regs.h > >> create mode 100644 drivers/media/video/davinci/vpbe_venc.c > >> create mode 100644 drivers/media/video/davinci/vpbe_venc_regs.h > >> create mode 100644 include/media/davinci/vpbe.h create mode 100644 > >> include/media/davinci/vpbe_display.h > >> create mode 100644 include/media/davinci/vpbe_osd.h create mode > >> 100644 include/media/davinci/vpbe_types.h > >> create mode 100644 include/media/davinci/vpbe_venc.h > >> > >> > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-media" > > in the body of a message to majordomo at vger.kernel.org More majordomo > > info at http://vger.kernel.org/majordomo-info.html > > From sboyd at codeaurora.org Fri Jun 24 15:16:48 2011 From: sboyd at codeaurora.org (Stephen Boyd) Date: Fri, 24 Jun 2011 13:16:48 -0700 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: <1308640714-17961-1-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> Message-ID: <4E04F0B0.4030408@codeaurora.org> On 06/21/2011 12:18 AM, Ohad Ben-Cohen wrote: > * Rpmsg: a virtio-based messaging bus that allows kernel drivers to > communicate with remote processors available on the system. In turn, > drivers could then expose appropriate user space interfaces, if needed > (tasks running on remote processors often have direct access to sensitive > resources like the system's physical memory, gpios, i2c buses, dma > controllers, etc.. so one normally wouldn't want to allow userland to > send everything/everywhere it wants). > > Every rpmsg device is a communication channel with a service running on a > remote processor (thus rpmsg devices are called channels). Channels are > identified by a textual name (which is used to match drivers to devices) > and have a local ("source") rpmsg address, and remote ("destination") rpmsg > address. When a driver starts listening on a channel (most commonly when it > is probed), the bus assigns the driver a unique rpmsg src address (a 32 bit > integer) and binds it with the driver's rx callback handler. This way > when inbound messages arrive to this src address, the rpmsg core dispatches > them to that driver, by invoking the driver's rx handler with the payload > of the incoming message. > [snip] > > In addition to dynamic creation of rpmsg channels, the rpmsg bus also > supports creation of static channels. This is needed in two cases: > - when a certain remote processor doesn't support sending those "name > service" announcements. In that case, a static table of remote rpmsg > services must be used to create the rpmsg channels. > - to support rpmsg server drivers, which aren't bound to a specific remote > rpmsg address. Instead, they just listen on a local address, waiting for > incoming messages. To send a message, those server drivers need to use > the rpmsg_sendto() API, so they can explicitly indicate the dst address > every time. > This sounds a lot like SMD (shared memory driver) on MSM. The main difference I see is that SMD uses the platform bus instead of the virtio bus and it has its own protocol for channel allocation. > > * Remoteproc: a generic driver that maintains the state of the remote > processor(s). Simple rproc_get() and rproc_put() API is exposed, which > drivers can use when needed (first driver to call get() will load a firmware, > configure an iommu if needed, and boot the remote processor, while last > driver to call put() will power it down). > > Hardware differences are abstracted as usual: a platform-specific driver > registers its own start/stop handlers in the generic remoteproc driver, > and those are invoked when its time to power up/down the processor. As a > reference, this patch set include remoteproc support for both OMAP4's > cortex-M3 and Davinci's DSP, tested on the pandaboard and hawkboard, > respectively. This remote proc code is eerily similar to PIL (peripheral image loader, yes we love our acronyms) which I posted a few months back[1]. Was it inspiration for this patch series? In terms of API, s/pil/rproc/ and it would be 95% identical. There are some low-level differences though (see below). > > The gory part of remoteproc is the firmware handling. We tried to come up > with a simple binary format that will require minimum kernel code to handle, > but at the same time be generic enough in the hopes that it would prove > useful to others as well. We're not at all hang onto the binary format > we picked: if there's any technical reason to change it to support other > platforms, please let us know. We do realize that a single binary firmware > structure might eventually not work for everyone. it did prove useful for > us though; we adopted both the OMAP and Davinci platforms (and their > completely different remote processor devices) to this simple binary > structure, so we don't have to duplicate the firmware handling code. This is an important difference between remote proc and PIL. In PIL, we do image authentication in addition to processor boot. In this case, we need to give the authentication engine the elf header and program headers for the firmware so that it knows what parts of memory need to be authenticated. Instead of devising a new firmware format, we decided to just stick with elf and parse the headers in the kernel because we needed them for authentication anyway. Is this reason enough to move to an ELF format instead? Another difference is inter-processor dependencies. For example, on msm8660 the modem can't boot until the dsp has been booted. I suppose we could hide this detail in the platform specific get() implementation by calling rproc_get() on the dependent processor (hopefully no locking issues arise). I'd rather have it built into the core though as it isn't really specific to the hardware. If we can resolve these differences I think we can easily support remote processor boot on MSM via remoteproc. [1] https://lkml.org/lkml/2011/3/9/490 -- Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum. From swetland at google.com Sat Jun 25 20:17:05 2011 From: swetland at google.com (Brian Swetland) Date: Sat, 25 Jun 2011 18:17:05 -0700 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <4E04F0B0.4030408@codeaurora.org> Message-ID: On Sat, Jun 25, 2011 at 6:11 PM, Ohad Ben-Cohen wrote: > Hi Stephen, > > On Fri, Jun 24, 2011 at 11:16 PM, Stephen Boyd wrote: >> This sounds a lot like SMD (shared memory driver) on MSM. The main >> difference I see is that SMD uses the platform bus instead of the virtio >> bus and it has its own protocol for channel allocation. > > Yeah, virtio is a key factor in this work; it was suggested to us by > Arnd at the AMP plumbers discussions last year, where it was apparent > that many vendors have their own IPC drivers/buses/channels over > shared memory with some vendor-ish binary protocol. I must say we > really liked virtio: it considerably simplified the code (we're not > adding any new binary protocol), it's very nicely optimized and > flexible, and it comes with a set of virtio drivers (e.g. network, > console, block) so we don't have to write our own. > > We also cared about adding this functionality as an IPC bus, so the > driver core will help matching drivers to channels - it simplified the > code (in both setup and tear down of channels) and kept it flexible. > It will also facilitate error recovery (on remote crash, we just > remove the virtio device, and then the driver core will in turn start > ->remove()ing the rpmsg drivers) and power management (via runtime > PM). > > About SMD: I'm not familiar with it too much, but Brian naturally is > (just for the sake of everyone who are not reading headers - Brian > Swetland wrote the Linux SMD driver, and is also an author of this > Google+TI joint work). rpmsg definitely shares some design features with SMD (given that I wrote the linux SMD driver and was involved in the design of rpmsg, this is maybe unsurprising), but whereas in SMD we had to be compatible with existing AMSS modems (to a degree), we had more flexibility in the "wire protocol" on rpmsg and virtio looked like a really nice fit that already was in the kernel. Ohad had a number of great ideas for making the dynamic discovery, publishing, and binding of remote services very clean and straightforward -- I wish I had a chance to pick his brain about this stuff back when building the SMD interfaces, which started out fairly static, but then evolved into publishing platform devices, etc. Of course some of this is the benefit of hindsight. It's easier to get it right (er?) the second or third time around. The TI SYS/BIOS folks were quite helpful and patient as we redesigned the wire protocol and publishing/resource model several times along the way here. Brian From shess at hessware.de Sun Jun 26 07:11:17 2011 From: shess at hessware.de (Sebastian =?iso-8859-1?q?He=DF?=) Date: Sun, 26 Jun 2011 14:11:17 +0200 Subject: [PATCH] Enable USB on TI DM365 In-Reply-To: References: Message-ID: <201106261411.17850.shess@hessware.de> On Friday 24 June 2011 20:51:31 Constantine Shulyupin wrote: Hi, > -void davinci_setup_usb(unsigned mA, unsigned potpgt_ms); > +int davinci_setup_usb(unsigned mA, unsigned potpgt_ms); > > #endif /* ifndef __ASM_ARCH_USB_H */ > diff --git a/arch/arm/mach-davinci/usb.c b/arch/arm/mach-davinci/usb.c > index 23d2b6d..8b12206 100644 > --- a/arch/arm/mach-davinci/usb.c > +++ b/arch/arm/mach-davinci/usb.c > @@ -4,19 +4,22 @@ > #include > #include > #include > - > #include > > #include > #include > #include > #include > +#include > +#include > > #define DAVINCI_USB_OTG_BASE 0x01c64000 > > #define DA8XX_USB0_BASE 0x01e00000 > #define DA8XX_USB1_BASE 0x01e25000 > > +static int retval; > + Why do you add this as a global static var? As no locking is involved here this may cause problems as soon as we see SMP systems using this controller. Please put the variable into the functions needing it. Best regards Sebastian -- Hessware - Open Source Solutions From sshtylyov at mvista.com Mon Jun 27 04:12:05 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Mon, 27 Jun 2011 13:12:05 +0400 Subject: [PATCH] Enable USB on TI DM365 In-Reply-To: References: Message-ID: <4E084965.4020409@mvista.com> Hello. On 24-06-2011 22:51, Constantine Shulyupin wrote: > Enable USB on TI DM365 On DM365 EVM board, you should have said. Do not duplicate the summary in the description. Also, more details here wouldn't hurt... > Signed-off-by: Constantine Shulyupin [...] > diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile > index 0b87a1c..52961a8 100644 > --- a/arch/arm/mach-davinci/Makefile > +++ b/arch/arm/mach-davinci/Makefile > @@ -5,7 +5,7 @@ > > # Common objects > obj-y := time.o clock.o serial.o io.o psc.o \ > - gpio.o dma.o usb.o common.o sram.o aemif.o > + gpio.o dma.o common.o sram.o aemif.o > > obj-$(CONFIG_DAVINCI_MUX) += mux.o > > @@ -40,3 +40,4 @@ obj-$(CONFIG_MACH_OMAPL138_HAWKBOARD) += board-omapl138-hawk.o > obj-$(CONFIG_CPU_FREQ) += cpufreq.o > obj-$(CONFIG_CPU_IDLE) += cpuidle.o > obj-$(CONFIG_SUSPEND) += pm.o sleep.o > +obj-$(CONFIG_USB_MUSB_DAVINCI) += usb.o And kernel linking will happily fail if CONFIG_USB_MUSB_DAVINCI=n. :-/ Also, I don't think making usb.c a module is a good idea... > diff --git a/arch/arm/mach-davinci/usb.c b/arch/arm/mach-davinci/usb.c > index 23d2b6d..8b12206 100644 > --- a/arch/arm/mach-davinci/usb.c > +++ b/arch/arm/mach-davinci/usb.c > @@ -4,19 +4,22 @@ > #include > #include > #include > - > #include > > #include > #include > #include > #include > +#include > +#include > > #define DAVINCI_USB_OTG_BASE 0x01c64000 > > #define DA8XX_USB0_BASE 0x01e00000 > #define DA8XX_USB1_BASE 0x01e25000 > > +static int retval; Why in the world it's 'static'? :-O > @@ -87,7 +90,7 @@ static struct platform_device usb_dev = { > .num_resources = ARRAY_SIZE(usb_resources), > }; > > -void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) > +int __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) > { > usb_data.power = mA> 510 ? 255 : mA / 2; > usb_data.potpgt = (potpgt_ms + 1) / 2; > @@ -99,7 +102,8 @@ void __init davinci_setup_usb(unsigned mA, unsigned > potpgt_ms) The patch is line-wrapped... > } else /* other devices don't have dedicated CPPI IRQ */ > usb_dev.num_resources = 2; > > - platform_device_register(&usb_dev); > + retval = platform_device_register(&usb_dev); > + return retval; Why not just: return platform_device_register(&usb_dev); > } > > #ifdef CONFIG_ARCH_DAVINCI_DA8XX > @@ -132,7 +136,7 @@ int __init da8xx_register_usb20(unsigned mA, > unsigned potpgt) > > #else > > -void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) > +int __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) > { > } > > @@ -178,3 +182,23 @@ int __init da8xx_register_usb11(struct > da8xx_ohci_root_hub *pdata) > return platform_device_register(&da8xx_usb11_device); > } > #endif /* CONFIG_DAVINCI_DA8XX */ > + > +#ifdef CONFIG_MACH_DAVINCI_DM365_EVM How do you think why do we have arch/arm/mach-davinci/board-dm365-evm.c? > +int __init dm365evm_usb_configure(void) > +{ > + davinci_cfg_reg(DM365_GPIO33); > + gpio_request(33, "usb"); > + gpio_direction_output(33, 1); > + retval = davinci_setup_usb(500, 8); > + return retval; Why not just: return davinci_setup_usb(500, 8); > diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c > index 2a2adf6..0147f5c 100644 > --- a/drivers/usb/musb/davinci.c > +++ b/drivers/usb/musb/davinci.c > @@ -72,6 +72,16 @@ static inline void phy_on(void) > /* power everything up; start the on-chip PHY and its PLL */ > phy_ctrl&= ~(USBPHY_OSCPDWN | USBPHY_OTGPDWN | USBPHY_PHYPDWN); > phy_ctrl |= USBPHY_SESNDEN | USBPHY_VBDTCTEN | USBPHY_PHYPLLON; > + > + if (cpu_is_davinci_dm365()) { > + /* > + * DM365 PHYCLKFREQ field [15:12] is set to 2 > + * to get clock from 24MHz crystal > + */ > + phy_ctrl |= USBPHY_CLKFREQ_24MHZ; I do think this should be set in the board specific code instead, like we do it on DA830. > + /*phy_ctrl&= ~USBPHY_PHYPDWN;*/ Don't include commented out code. > + } > + > __raw_writel(phy_ctrl, USB_PHY_CTRL); > > /* wait for PLL to lock before proceeding */ > @@ -193,6 +203,9 @@ static void davinci_musb_source_power(struct musb > *musb, int is_on, int immediat > else > schedule_work(&evm_vbus_work); > } > + > + if (cpu_is_davinci_dm365()) > + gpio_set_value(33, is_on); This is board specific code, not CPU specific. > diff --git a/drivers/usb/musb/davinci.h b/drivers/usb/musb/davinci.h > index 046c844..1bf50e6 100644 > --- a/drivers/usb/musb/davinci.h > +++ b/drivers/usb/musb/davinci.h > @@ -17,6 +17,7 @@ > /* Integrated highspeed/otg PHY */ > #define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34) > #define USBPHY_DATAPOL BIT(11) /* (dm355) switch D+/D- */ > +#define USBPHY_CLKFREQ_24MHZ BIT(13) > #define USBPHY_PHYCLKGD BIT(8) > #define USBPHY_SESNDEN BIT(7) /* v(sess_end) comparator */ > #define USBPHY_VBDTCTEN BIT(6) /* v(bus) comparator */ From nsekhar at ti.com Mon Jun 27 09:27:17 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 27 Jun 2011 19:57:17 +0530 Subject: [PATCH 1/4] davinci: psc.h: clean up indentation done using spaces In-Reply-To: <201106251948.19834.arnd@arndb.de> References: <1301072400-4036-1-git-send-email-nsekhar@ti.com> <201106251948.19834.arnd@arndb.de> Message-ID: Hi Arnd, On Sat, Jun 25, 2011 at 23:18:19, Arnd Bergmann wrote: > On Friday 24 June 2011, Nori, Sekhar wrote: > > This series of 4 patches added SATA support on DaVinci > > DA850 platform. This was ready for last merge window, > > but was not queued because of the negative diffstat > > rule. > > > > Is there a possibility of queuing this for the v3.1 merge? > > > > Here is the archive link of this posting: > > > > http://linux.davincidsp.com/pipermail/davinci-linux-open-source/2011-March/022473.html > > My general idea is to have multiple branches per subarchitecture, > and I started merging stuff into the tree now. > > Your series contains both cleanups and support for additional > features, and I'd like to see those split into separate branches. Okay. > > Please make sure the patches apply on top of 2.6.39 and resubmit > them, separating out the first cleanup patch from the others. Is it 2.6.39 or v3.0-rc? Linux-next is currently at v3.0-rc4. I can create a branch with just the clean-up stuff and another branch with the features (and its dependencies from fixes/clean-up). By "resubmit" I assume you are not asking for a re-send of the patches to the mailing list (I can do that if you want). > We can definitely forward all cleanups and bug fixes in the each > merge window. For new features, it mostly depends on how much > code gets added that should not really be needed in a perfect > world, relative to the amount of cleanups that is actually > going into the platform in order to get there. Okay. > In case of the SATA support, the feature is relatively small > and requires little board-specific code, which is good. > However, if you are planning to push a lot of small additions > like this, I would expect you spend much more effort on > consolidation within davinci and migration towards device > tree probing first. I don't see new SoCs/boards getting added to the DaVinci tree in the near term but there are some features of existing platforms (like USB on DM365 or SATA on DA850) which are missing in mainline for which there are patches being sent. To my knowledge, there isn't any ongoing work on moving DaVinci to device tree. I am working on getting the low(er) hanging things like GPIO movement to drivers/gpio done. DaVinci will eventually move to device-tree, but may be not as soon as OMAP where there is a lot more active work ongoing. > What else do you have pending for davinci in 3.1? Can you > send pull requests for cleanups/fixes/device-tree/features? Actually not quite a lot at the moment, but there are some patches which are waiting for acks/dependencies and some pending re-work. I will send a pull request with what has been reviewed and accepted already. I guess I also need to ask Stephen Rothwell to remove the DaVinci next branch from the list of branches he is merging for linux-next. Thanks, Sekhar From arnd at arndb.de Mon Jun 27 10:25:14 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Mon, 27 Jun 2011 17:25:14 +0200 Subject: [PATCH 1/4] davinci: psc.h: clean up indentation done using spaces In-Reply-To: References: <1301072400-4036-1-git-send-email-nsekhar@ti.com> <201106251948.19834.arnd@arndb.de> Message-ID: <201106271725.14310.arnd@arndb.de> On Monday 27 June 2011, Nori, Sekhar wrote: > On Sat, Jun 25, 2011 at 23:18:19, Arnd Bergmann wrote: > > On Friday 24 June 2011, Nori, Sekhar wrote: > > Please make sure the patches apply on top of 2.6.39 and resubmit > > them, separating out the first cleanup patch from the others. > > Is it 2.6.39 or v3.0-rc? Linux-next is currently at v3.0-rc4. If there are no conflicts with 3.0-rc changes, submitting based on the previous release (2.6.39) is ok, otherwise just pick the most recent -rc release. Best avoid basing on top of an arbitrary commit between two -rc though. > I can create a branch with just the clean-up stuff and > another branch with the features (and its dependencies > from fixes/clean-up). > > By "resubmit" I assume you are not asking for a re-send > of the patches to the mailing list (I can do that if you > want). I'd prefer submission in form of a git-pull request in this case, but sending to the mailing list is ok, too. > > In case of the SATA support, the feature is relatively small > > and requires little board-specific code, which is good. > > However, if you are planning to push a lot of small additions > > like this, I would expect you spend much more effort on > > consolidation within davinci and migration towards device > > tree probing first. > > I don't see new SoCs/boards getting added to the DaVinci > tree in the near term but there are some features of existing > platforms (like USB on DM365 or SATA on DA850) which are missing > in mainline for which there are patches being sent. > > To my knowledge, there isn't any ongoing work on moving DaVinci > to device tree. I am working on getting the low(er) hanging things > like GPIO movement to drivers/gpio done. DaVinci will eventually > move to device-tree, but may be not as soon as OMAP where > there is a lot more active work ongoing. Yes, makes sense since you are working on both. > > What else do you have pending for davinci in 3.1? Can you > > send pull requests for cleanups/fixes/device-tree/features? > > Actually not quite a lot at the moment, but there are some > patches which are waiting for acks/dependencies and some > pending re-work. > > I will send a pull request with what has been reviewed and > accepted already. Ok. Do you also have a public tree with all the patches that you would hope to get merged eventually? I'd just like to get an impression of what's to come. > I guess I also need to ask Stephen Rothwell to remove the > DaVinci next branch from the list of branches he is merging > for linux-next. Yes, that would be good, but we might need to do that in a more coordinated way for all the ARM platforms once they are reasonably merged into my tree. Arnd From mgrosen at ti.com Mon Jun 27 13:20:25 2011 From: mgrosen at ti.com (Grosen, Mark) Date: Mon, 27 Jun 2011 18:20:25 +0000 Subject: [RFC 5/8] remoteproc: add davinci implementation In-Reply-To: <4E04A9AE.3030801@mvista.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-6-git-send-email-ohad@wizery.com> <4E035B78.3080200@mvista.com> <4E04A9AE.3030801@mvista.com> Message-ID: > From: Sergei Shtylyov > Sent: Friday, June 24, 2011 8:14 AM > > Grosen, Mark wrote: > > >> It should work on DA830 as well, > > So please make it dependent on ARCH_DAVINCI_DA8XX. > > >> but not on real DaVinci, so the name is misleading... > > > Yes, we debated calling it da8xx, but felt that with minor changes it could > > accomodate the other SoCs in the davinci family. > > I don't think it's a good idea. Using cpu_is_*() is drivers is bad. Using > #ifdef's is not an option either. > > > However, it may be better > > to start with just the da8xx/omapl13x parts and then rename if we add the > > others. Sergei, I'll respond more to this in a response to Sekhar's ideas. We may be able to make this work generically for davinci w/o idef's. > Looking into my old PSC manual (can't get the recent documentation from TI's > site right now), the bit is called LRSTz. > It's worth moving this #define into as well. Ok, I agree we should try to match the HW names as much as possible > >>> +/* register for DSP boot address in SYSCFG0 module (section 11.5.6) */ > >>> +#define HOST1CFG 0x44 > > >> Worth declaring in instead... > > > Possibly - since it is only used for the DSP, I thought it would be better > > to keep local to this implementation. I'll adopt whichever approach is the > > convention. > > Well, the general approach is to keep the #define's where they are > used, so maybe we should keep this #define here. Will review as part of the general cleanup. > > >>> +static inline int davinci_rproc_start(struct rproc *rproc, u64 > >> bootaddr) > >>> +{ > >>> + struct device *dev = rproc->dev; > >>> + struct davinci_rproc_pdata *pdata = dev->platform_data; > >>> + struct davinci_soc_info *soc_info = &davinci_soc_info; > >>> + void __iomem *psc_base; > >>> + struct clk *dsp_clk; > >>> + > >>> + /* hw requires the start (boot) address be on 1KB boundary */ > >>> + if (bootaddr & 0x3ff) { > >>> + dev_err(dev, "invalid boot address: must be aligned to > >> 1KB\n"); > >>> + return -EINVAL; > >>> + } > >>> + > >>> + dsp_clk = clk_get(dev, pdata->clk_name); > > >> We could match using clkdev functionality, but the clock entry > >> would need to be changed then... > > > I followed the existing pattern I saw in other drivers. > > Probably MUSB? We're trying to move away from passing the clock name to thge > drivers, using match by device instead. > > > If there is a new, better way, please point me to an example. > > Look at the da850_clks[] in mach-davinci/da850.c: if the device name is > specified as a first argument to CLK() macro (instead of clock name the second > argument), the matching is done by device, so you don't need to specify the > clock name to clk_get(), passing NULL instead. Thanks, I'll look at this and ask for Sekhar and Kevin's preferences. Mark From mgrosen at ti.com Mon Jun 27 13:31:41 2011 From: mgrosen at ti.com (Grosen, Mark) Date: Mon, 27 Jun 2011 18:31:41 +0000 Subject: [RFC 5/8] remoteproc: add davinci implementation In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-6-git-send-email-ohad@wizery.com> <4E035B78.3080200@mvista.com> <4E04A9AE.3030801@mvista.com> Message-ID: > From: Nori, Sekhar > Sent: Friday, June 24, 2011 8:44 AM > > Hi Mark, Sekhar, thanks for your feedback and ideas. Comments below. Mark > Since procedure to set the boot address varies across DaVinci > platforms, you could have a callback populated in platform data > which will be implemented differently for original DaVinci and > DA8xx devices. I looked at DM6467 and it's the same as OMAPL13x, except at a different address. Rather than a callback, it could be just an address in the platform data. > > Also, all PSC accesses are better off going through clock > framework to ensure proper locking and modularity. > > To assert/de-assert local reset when enabling or disabling PSC, > you could use a flag in the clock structure to indicate the need > for this. This way, if there is any other module needing a local > reset, it can just define the same flag. Similarly, if the DSP > does not need a local reset on a particular platform, that > platform can simply skip the flag. > > This can be done in a manner similar to how the support for > a forced transition PSC was added here: > > https://patchwork.kernel.org/patch/662941/ Yes, I like this idea - much cleaner. For example, the start() method becomes (pseudo-code): start() { /* bootaddrreg derived from platform data */ bootaddrreg = boot_address; clk_enable(); } Referring to your patch above, would it be better to just pass the flags into the davinci_psc_config() function rather than breaking out more arguments for each flag? Mark From grant.likely at secretlab.ca Mon Jun 27 15:49:58 2011 From: grant.likely at secretlab.ca (Grant Likely) Date: Mon, 27 Jun 2011 14:49:58 -0600 Subject: [RFC 1/8] drivers: add generic remoteproc framework In-Reply-To: <1308640714-17961-2-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-2-git-send-email-ohad@wizery.com> Message-ID: <20110627204958.GB20865@ponder.secretlab.ca> On Tue, Jun 21, 2011 at 10:18:27AM +0300, Ohad Ben-Cohen wrote: > Some systems have slave heterogeneous remote processor devices, > that are usually used to offload cpu-intensive computations > (e.g. multimedia codec tasks). > > Booting a remote processor typically involves: > - Loading a firmware which contains the OS image (mainly text and data) > - If needed, programming an IOMMU > - Powering on the device > > This patch introduces a generic remoteproc framework that allows drivers > to start and stop those remote processor devices, load up their firmware > (which might not necessarily be Linux-based), and in the future also > support power management and error recovery. > > It's still not clear how much this is really reusable for other > platforms/architectures, especially the part that deals with the > firmware. > > Moreover, it's not entirely clear whether this should really be an > independent layer, or if it should just be squashed with the host-specific > component of the rpmsg framework (there isn't really a remoteproc use case > that doesn't require rpmsg). > > That said, it did prove useful for us on two completely different > platforms: OMAP and Davinci, each with its different remote > processor (Cortex-M3 and a C674x DSP, respectively). So to avoid > egregious duplication of code, remoteproc must not be omap-only. > > Firmware loader is based on code by Mark Grosen . > > TODO: > - drop rproc_da_to_pa(), use iommu_iova_to_phys() instead > (requires completion of omap's iommu migration and some generic iommu > API work) > - instead of ioremapping reserved memory and handling IOMMUs, consider > moving to the generic DMA mapping API (with a CMA backend) > > Signed-off-by: Ohad Ben-Cohen Hi Ohad, Overall, looks pretty nice to me. Comments below... > --- > Documentation/remoteproc.txt | 170 +++++++++ > drivers/Kconfig | 2 + > drivers/Makefile | 1 + > drivers/remoteproc/Kconfig | 7 + > drivers/remoteproc/Makefile | 5 + > drivers/remoteproc/remoteproc.c | 780 +++++++++++++++++++++++++++++++++++++++ > include/linux/remoteproc.h | 273 ++++++++++++++ > 7 files changed, 1238 insertions(+), 0 deletions(-) > create mode 100644 Documentation/remoteproc.txt > create mode 100644 drivers/remoteproc/Kconfig > create mode 100644 drivers/remoteproc/Makefile > create mode 100644 drivers/remoteproc/remoteproc.c > create mode 100644 include/linux/remoteproc.h > > diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt > new file mode 100644 > index 0000000..3075813 > --- /dev/null > +++ b/Documentation/remoteproc.txt > @@ -0,0 +1,170 @@ > +Remote Processor Framework > + > +1. Introduction > + > +Modern SoCs typically have heterogeneous remote processor devices in asymmetric > +multiprocessing (AMP) configurations, which may be running different instances > +of operating system, whether it's Linux or any other flavor of real-time OS. > + > +OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP. > +In a typical configuration, the dual cortex-A9 is running Linux in a SMP > +configuration, and each of the other three cores (two M3 cores and a DSP) > +is running its own instance of RTOS in an AMP configuration. > + > +The generic remoteproc driver allows different platforms/architectures to > +control (power on, load firmware, power off) those remote processors while > +abstracting the hardware differences, so the entire driver doesn't need to be > +duplicated. > + > +2. User API > + > + struct rproc *rproc_get(const char *name); > + - power up the remote processor, identified by the 'name' argument, > + and boot it. If the remote processor is already powered on, the > + function immediately succeeds. > + On success, returns the rproc handle. On failure, NULL is returned. > + > + void rproc_put(struct rproc *rproc); > + - power off the remote processor, identified by the rproc handle. > + Every call to rproc_get() must be (eventually) accompanied by a call > + to rproc_put(). Calling rproc_put() redundantly is a bug. > + Note: the remote processor will actually be powered off only when the > + last user calls rproc_put(). > + > +3. Typical usage > + > +#include > + > +int dummy_rproc_example(void) > +{ > + struct rproc *my_rproc; > + > + /* let's power on and boot the image processing unit */ > + my_rproc = rproc_get("ipu"); I tend to be suspicious of apis whose primary interface is by-name lookup. It works fine when the system is small, but it can get unwieldy when the client driver doesn't have a direct relation to the setup code that chooses the name. At some point I suspect that there will need to be different lookup mechanism, such as which AMP processor is currently available (if there are multiple of the same type). It also leaves no option for drivers to obtain a reference to the rproc instance, and bring it up/down as needed (without the name lookup every time). That said, it looks like only the rproc_get() api is using by-name lookup, and everything else is via the structure. Can (should) the by-name lookup part be factored out into a rproc_get_by_name() accessor? > + if (!my_rproc) { > + /* > + * something went wrong. handle it and leave. > + */ > + } > + > + /* > + * the 'ipu' remote processor is now powered on... let it work ! > + */ > + > + /* if we no longer need ipu's services, power it down */ > + rproc_put(my_rproc); > +} > + > +4. API for implementors > + > + int rproc_register(struct device *dev, const char *name, > + const struct rproc_ops *ops, > + const char *firmware, > + const struct rproc_mem_entry *memory_maps, > + struct module *owner); > + - should be called from the underlying platform-specific implementation, in > + order to register a new remoteproc device. 'dev' is the underlying > + device, 'name' is the name of the remote processor, which will be > + specified by users calling rproc_get(), 'ops' is the platform-specific > + start/stop handlers, 'firmware' is the name of the firmware file to > + boot the processor with, 'memory_maps' is a table of da<->pa memory > + mappings which should be used to configure the IOMMU (if not relevant, > + just pass NULL here), 'owner' is the underlying module that should > + not be removed while the remote processor is in use. Since rproc_register is allocating a struct rproc instance that represent the device, shouldn't the pointer to that device be returned to the caller? Also, consider the use case that at some point someone will need separate rproc_alloc and rproc_add steps so that it can modify the structure between allocating and adding. Otherwise you're stuck in the model of having to modify the function signature to rproc_register() every time a new feature is added that required additional data; regardless of whether or not all drivers will use it. > + > + Returns 0 on success, or an appropriate error code on failure. > + > + int rproc_unregister(const char *name); I definitely would not do this by name. I think it is better to pass the actual instance pointer to rproc_unregister. > + - should be called from the underlying platform-specific implementation, in > + order to unregister a remoteproc device that was previously registered > + with rproc_register(). > + > +5. Implementation callbacks > + > +Every remoteproc implementation must provide these handlers: > + > +struct rproc_ops { > + int (*start)(struct rproc *rproc, u64 bootaddr); > + int (*stop)(struct rproc *rproc); > +}; > + > +The ->start() handler takes a rproc handle and an optional bootaddr argument, > +and should power on the device and boot it (using the bootaddr argument > +if the hardware requires one). Naive question: Why is bootaddr an argument? Wouldn't rproc drivers keep track of the boot address in their driver private data? > +On success, 0 is returned, and on failure, an appropriate error code. > + > +The ->stop() handler takes a rproc handle and powers the device off. > +On success, 0 is returned, and on failure, an appropriate error code. > + > +6. Binary Firmware Structure > + > +The following enums and structures define the binary format of the images > +remoteproc loads and boot the remote processors with. > + > +The general binary format is as follows: > + > +struct { > + char magic[4] = { 'R', 'P', 'R', 'C' }; > + u32 version; > + u32 header_len; > + char header[...] = { header_len bytes of unformatted, textual header }; > + struct section { > + u32 type; > + u64 da; > + u32 len; > + u8 content[...] = { len bytes of binary data }; > + } [ no limit on number of sections ]; > +} __packed; Other have commented on the image format, so I'll skip this bit other than saying that I agree it would be great to have a common format. > +Most likely this kind of static allocations of hardware resources for > +remote processors can also use DT, so it's interesting to see how > +this all work out when DT materializes. I imagine that it will be quite straight forward. There will probably be a node in the tree to represent each slave AMP processor, and other devices attached to it could be represented using 'phandle' links between the nodes. Any configuration of the AMP process can be handled with arbitrary device-specific properties in the AMP processor's node. > diff --git a/drivers/Kconfig b/drivers/Kconfig > index 3bb154d..1f6d6d3 100644 > --- a/drivers/Kconfig > +++ b/drivers/Kconfig > @@ -126,4 +126,6 @@ source "drivers/hwspinlock/Kconfig" > > source "drivers/clocksource/Kconfig" > > +source "drivers/remoteproc/Kconfig" > + Hmmm, I wonder if the end of the drivers list is the best place for this. The drivers menu in kconfig is getting quite unwieldy. > endmenu > diff --git a/drivers/Makefile b/drivers/Makefile > index 09f3232..4d53a18 100644 > --- a/drivers/Makefile > +++ b/drivers/Makefile > @@ -122,3 +122,4 @@ obj-y += ieee802154/ > obj-y += clk/ > > obj-$(CONFIG_HWSPINLOCK) += hwspinlock/ > +obj-$(CONFIG_REMOTE_PROC) += remoteproc/ > diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig > new file mode 100644 > index 0000000..a60bb12 > --- /dev/null > +++ b/drivers/remoteproc/Kconfig > @@ -0,0 +1,7 @@ > +# > +# Generic framework for controlling remote processors > +# > + > +# REMOTE_PROC gets selected by whoever wants it > +config REMOTE_PROC > + tristate > diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile > new file mode 100644 > index 0000000..d0f60c7 > --- /dev/null > +++ b/drivers/remoteproc/Makefile > @@ -0,0 +1,5 @@ > +# > +# Generic framework for controlling remote processors > +# > + > +obj-$(CONFIG_REMOTE_PROC) += remoteproc.o > diff --git a/drivers/remoteproc/remoteproc.c b/drivers/remoteproc/remoteproc.c > new file mode 100644 > index 0000000..2b0514b > --- /dev/null > +++ b/drivers/remoteproc/remoteproc.c > @@ -0,0 +1,780 @@ > +/* > + * Remote Processor Framework > + * > + * Copyright (C) 2011 Texas Instruments, Inc. > + * Copyright (C) 2011 Google, Inc. > + * > + * Ohad Ben-Cohen > + * Mark Grosen > + * Brian Swetland > + * Fernando Guzman Lugo > + * Robert Tivy > + * Armando Uribe De Leon > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * version 2 as published by the Free Software Foundation. > + * > + * 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. > + */ > + > +#define pr_fmt(fmt) "%s: " fmt, __func__ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* list of the available remote processors */ > +static LIST_HEAD(rprocs); > +/* > + * This lock should be taken when the list of rprocs is accessed. > + * Consider using RCU instead, since remote processors only get registered > + * once (usually at boot), and then the list is only read accessed. > + * Though right now the list is pretty short, and only rarely accessed. > + */ > +static DEFINE_SPINLOCK(rprocs_lock); > + > +/* debugfs parent dir */ > +static struct dentry *rproc_dbg; > + > +/* > + * Some remote processors may support dumping trace logs into a shared > + * memory buffer. We expose this trace buffer using debugfs, so users > + * can easily tell what's going on remotely. > + */ > +static ssize_t rproc_format_trace_buf(char __user *userbuf, size_t count, > + loff_t *ppos, const void *src, int size) > +{ > + const char *buf = (const char *) src; > + int i; > + > + /* > + * find the end of trace buffer (does not account for wrapping). > + * desirable improvement: use a ring buffer instead. > + */ > + for (i = 0; i < size && buf[i]; i++); Hmmm, I wonder if this could make use of the ftrace ring buffer. > + > + return simple_read_from_buffer(userbuf, count, ppos, src, i); > +} > + > +static int rproc_open_generic(struct inode *inode, struct file *file) > +{ > + file->private_data = inode->i_private; > + return 0; > +} > + > +#define DEBUGFS_READONLY_FILE(name, value, len) \ > +static ssize_t name## _rproc_read(struct file *filp, \ > + char __user *userbuf, size_t count, loff_t *ppos) \ > +{ \ > + struct rproc *rproc = filp->private_data; \ > + return rproc_format_trace_buf(userbuf, count, ppos, value, len);\ > +} \ > + \ > +static const struct file_operations name ##_rproc_ops = { \ > + .read = name ##_rproc_read, \ > + .open = rproc_open_generic, \ > + .llseek = generic_file_llseek, \ > +}; > + > +/* > + * Currently we allow two trace buffers for each remote processor. > + * This is helpful in case a single remote processor has two independent > + * cores, each of which is running an independent OS image. > + * The current implementation is straightforward and simple, and is > + * rather limited to 2 trace buffers. If, in time, we'd realize we > + * need additional trace buffers, then the code should be refactored > + * and generalized. > + */ > +DEBUGFS_READONLY_FILE(trace0, rproc->trace_buf0, rproc->trace_len0); > +DEBUGFS_READONLY_FILE(trace1, rproc->trace_buf1, rproc->trace_len1); > + > +/* The state of the remote processor is exposed via debugfs, too */ > +const char *rproc_state_string(int state) > +{ > + const char *result; > + > + switch (state) { > + case RPROC_OFFLINE: > + result = "offline"; > + break; > + case RPROC_SUSPENDED: > + result = "suspended"; > + break; > + case RPROC_RUNNING: > + result = "running"; > + break; > + case RPROC_LOADING: > + result = "loading"; > + break; > + case RPROC_CRASHED: > + result = "crashed"; > + break; > + default: > + result = "invalid state"; > + break; > + } Me thinks this is asking for a lookup table. > + > + return result; > +} > + > +static ssize_t rproc_state_read(struct file *filp, char __user *userbuf, > + size_t count, loff_t *ppos) > +{ > + struct rproc *rproc = filp->private_data; > + int state = rproc->state; > + char buf[100]; 100 bytes? I count at most ~30. > + int i; > + > + i = snprintf(buf, 100, "%s (%d)\n", rproc_state_string(state), state); > + > + return simple_read_from_buffer(userbuf, count, ppos, buf, i); > +} > + > +static const struct file_operations rproc_state_ops = { > + .read = rproc_state_read, > + .open = rproc_open_generic, > + .llseek = generic_file_llseek, > +}; > + > +/* The name of the remote processor is exposed via debugfs, too */ > +static ssize_t rproc_name_read(struct file *filp, char __user *userbuf, > + size_t count, loff_t *ppos) > +{ > + struct rproc *rproc = filp->private_data; > + /* need room for the name, a newline and a terminating null */ > + char buf[RPROC_MAX_NAME + 2]; > + int i; > + > + i = snprintf(buf, RPROC_MAX_NAME + 2, "%s\n", rproc->name); > + > + return simple_read_from_buffer(userbuf, count, ppos, buf, i); > +} > + > +static const struct file_operations rproc_name_ops = { > + .read = rproc_name_read, > + .open = rproc_open_generic, > + .llseek = generic_file_llseek, > +}; > + > +#define DEBUGFS_ADD(name) \ > + debugfs_create_file(#name, 0400, rproc->dbg_dir, \ > + rproc, &name## _rproc_ops) You might want to split the debug stuff off into a separate patch, just to keep the review load down. (up to you though). > + > +/** > + * __find_rproc_by_name() - find a registered remote processor by name > + * @name: name of the remote processor > + * > + * Internal function that returns the rproc @name, or NULL if @name does > + * not exists. > + */ > +static struct rproc *__find_rproc_by_name(const char *name) > +{ > + struct rproc *rproc; > + struct list_head *tmp; > + > + spin_lock(&rprocs_lock); > + > + list_for_each(tmp, &rprocs) { > + rproc = list_entry(tmp, struct rproc, next); > + if (!strcmp(rproc->name, name)) > + break; > + rproc = NULL; > + } > + > + spin_unlock(&rprocs_lock); Unless you're going to be looking up the device at irq time, a mutex is probably a better choice here. > + > + return rproc; > +} > + [ignoring da_to_pa bits because they are subject to change] > + > +/** > + * rproc_start() - boot the remote processor > + * @rproc: the remote processor > + * @bootaddr: address of first instruction to execute (optional) > + * > + * Boot a remote processor (i.e. power it on, take it out of reset, etc..) > + */ > +static void rproc_start(struct rproc *rproc, u64 bootaddr) > +{ > + struct device *dev = rproc->dev; > + int err; > + > + err = mutex_lock_interruptible(&rproc->lock); > + if (err) { > + dev_err(dev, "can't lock remote processor %d\n", err); > + return; > + } > + > + err = rproc->ops->start(rproc, bootaddr); > + if (err) { > + dev_err(dev, "can't start rproc %s: %d\n", rproc->name, err); > + goto unlock_mutex; > + } > + > + rproc->state = RPROC_RUNNING; > + > + dev_info(dev, "remote processor %s is now up\n", rproc->name); How often are remote processors likely to be brought up/down? Do PM events hard stop remote processors? I only ask because I wonder if this dev_info() will end up flooding the kernel log. > +/** > + * rproc_get() - boot the remote processor > + * @name: name of the remote processor > + * > + * Boot a remote processor (i.e. load its firmware, power it on, take it > + * out of reset, etc..). > + * > + * If the remote processor is already powered on, immediately return > + * its rproc handle. > + * > + * On success, returns the rproc handle. On failure, NULL is returned. > + */ > +struct rproc *rproc_get(const char *name) > +{ > + struct rproc *rproc, *ret = NULL; > + struct device *dev; > + int err; > + > + rproc = __find_rproc_by_name(name); > + if (!rproc) { > + pr_err("can't find remote processor %s\n", name); > + return NULL; > + } > + > + dev = rproc->dev; > + > + err = mutex_lock_interruptible(&rproc->lock); > + if (err) { > + dev_err(dev, "can't lock remote processor %s\n", name); > + return NULL; > + } > + > + /* prevent underlying implementation from being removed */ > + if (!try_module_get(rproc->owner)) { > + dev_err(dev, "%s: can't get owner\n", __func__); > + goto unlock_mutex; > + } > + > + /* skip the boot process if rproc is already (being) powered up */ > + if (rproc->count++) { > + ret = rproc; > + goto unlock_mutex; > + } > + > + /* rproc_put() calls should wait until async loader completes */ > + init_completion(&rproc->firmware_loading_complete); > + > + dev_info(dev, "powering up %s\n", name); > + > + /* loading a firmware is required */ > + if (!rproc->firmware) { > + dev_err(dev, "%s: no firmware to load\n", __func__); > + goto deref_rproc; > + } > + > + /* > + * Initiate an asynchronous firmware loading, to allow building > + * remoteproc as built-in kernel code, without hanging the boot process > + */ > + err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, > + rproc->firmware, dev, GFP_KERNEL, rproc, rproc_load_fw); > + if (err < 0) { > + dev_err(dev, "request_firmware_nowait failed: %d\n", err); > + goto deref_rproc; > + } > + > + rproc->state = RPROC_LOADING; > + ret = rproc; > + goto unlock_mutex; > + > +deref_rproc: > + complete_all(&rproc->firmware_loading_complete); > + module_put(rproc->owner); > + --rproc->count; > +unlock_mutex: > + mutex_unlock(&rproc->lock); > + return ret; > +} > +EXPORT_SYMBOL(rproc_get); > + > +/** > + * rproc_put() - power off the remote processor > + * @rproc: the remote processor > + * > + * Release an rproc handle previously acquired with rproc_get(), > + * and if we're the last user, power the processor off. > + * > + * Every call to rproc_get() must be (eventually) accompanied by a call > + * to rproc_put(). Calling rproc_put() redundantly is a bug. > + */ > +void rproc_put(struct rproc *rproc) > +{ > + struct device *dev = rproc->dev; > + int ret; > + > + /* > + * make sure rproc_get() was called beforehand. > + * it should be safe to check for zero without taking the lock. > + */ However, it may be non-zero here, but drop to zero by the time you take the lock. Best be safe and put it inside the mutex. Having it under the mutex shouldn't be a performance hit since only buggy code will get this test wrong. In fact, it is probably appropriate to WARN_ON() on the !rproc->count condition. Actually, using a hand coded reference count like this shouldn't be done. Use a kobject or a kref instead. Looking at the code, I suspect you'll want separate reference counting for object references and power up/down count so that clients can control power to a device without giving up the pointer to the rproc instance. > + if (!rproc->count) { > + dev_err(dev, "asymmetric put (fogot to call rproc_get ?)\n"); > + ret = -EINVAL; > + goto out; > + } > + > + /* if rproc is just being loaded now, wait */ > + wait_for_completion(&rproc->firmware_loading_complete); > + > + ret = mutex_lock_interruptible(&rproc->lock); > + if (ret) { > + dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret); > + return; > + } > + > + /* if the remote proc is still needed, bail out */ > + if (--rproc->count) > + goto out; > + > + if (rproc->trace_buf0) > + /* iounmap normal memory, so make sparse happy */ > + iounmap((__force void __iomem *) rproc->trace_buf0); > + if (rproc->trace_buf1) > + /* iounmap normal memory, so make sparse happy */ > + iounmap((__force void __iomem *) rproc->trace_buf1); Icky casting! That suggests that how the trace buffer pointer is managed needs work. > + > + rproc->trace_buf0 = rproc->trace_buf1 = NULL; > + > + /* > + * make sure rproc is really running before powering it off. > + * this is important, because the fw loading might have failed. > + */ > + if (rproc->state == RPROC_RUNNING) { > + ret = rproc->ops->stop(rproc); > + if (ret) { > + dev_err(dev, "can't stop rproc: %d\n", ret); > + goto out; > + } > + } > + > + rproc->state = RPROC_OFFLINE; > + > + dev_info(dev, "stopped remote processor %s\n", rproc->name); > + > +out: > + mutex_unlock(&rproc->lock); > + if (!ret) > + module_put(rproc->owner); > +} > +EXPORT_SYMBOL(rproc_put); > + > +/** > + * rproc_register() - register a remote processor > + * @dev: the underlying device > + * @name: name of this remote processor > + * @ops: platform-specific handlers (mainly start/stop) > + * @firmware: name of firmware file to load > + * @memory_maps: IOMMU settings for this rproc (optional) > + * @owner: owning module > + * > + * Registers a new remote processor in the remoteproc framework. > + * > + * This is called by the underlying platform-specific implementation, > + * whenever a new remote processor device is probed. > + * > + * On succes, 0 is return, and on failure an appropriate error code. > + */ > +int rproc_register(struct device *dev, const char *name, > + const struct rproc_ops *ops, > + const char *firmware, > + const struct rproc_mem_entry *memory_maps, > + struct module *owner) > +{ > + struct rproc *rproc; > + > + if (!dev || !name || !ops) > + return -EINVAL; > + > + rproc = kzalloc(sizeof(struct rproc), GFP_KERNEL); > + if (!rproc) { > + dev_err(dev, "%s: kzalloc failed\n", __func__); > + return -ENOMEM; > + } > + > + rproc->dev = dev; > + rproc->name = name; > + rproc->ops = ops; > + rproc->firmware = firmware; > + rproc->memory_maps = memory_maps; > + rproc->owner = owner; > + > + mutex_init(&rproc->lock); > + > + rproc->state = RPROC_OFFLINE; > + > + spin_lock(&rprocs_lock); > + list_add_tail(&rproc->next, &rprocs); > + spin_unlock(&rprocs_lock); > + > + dev_info(dev, "%s is available\n", name); > + > + if (!rproc_dbg) > + goto out; > + > + rproc->dbg_dir = debugfs_create_dir(dev_name(dev), rproc_dbg); > + if (!rproc->dbg_dir) { > + dev_err(dev, "can't create debugfs dir\n"); > + goto out; > + } > + > + debugfs_create_file("name", 0400, rproc->dbg_dir, rproc, > + &rproc_name_ops); > + debugfs_create_file("state", 0400, rproc->dbg_dir, rproc, > + &rproc_state_ops); > + > +out: > + return 0; > +} > +EXPORT_SYMBOL(rproc_register); > + > +/** > + * rproc_unregister() - unregister a remote processor > + * @name: name of this remote processor > + * > + * Unregisters a remote processor. > + * > + * On succes, 0 is return. If this remote processor isn't found, -EINVAL > + * is returned. > + */ > +int rproc_unregister(const char *name) > +{ > + struct rproc *rproc; > + > + rproc = __find_rproc_by_name(name); > + if (!rproc) { > + pr_err("can't find remote processor %s\n", name); > + return -EINVAL; > + } > + > + dev_info(rproc->dev, "removing %s\n", name); > + > + if (rproc->dbg_dir) > + debugfs_remove_recursive(rproc->dbg_dir); > + > + spin_lock(&rprocs_lock); > + list_del(&rproc->next); > + spin_unlock(&rprocs_lock); > + > + kfree(rproc); > + > + return 0; > +} > +EXPORT_SYMBOL(rproc_unregister); > + > +static int __init remoteproc_init(void) > +{ > + if (debugfs_initialized()) { > + rproc_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); > + if (!rproc_dbg) > + pr_err("can't create debugfs dir\n"); > + } > + > + return 0; > +} > +/* must be ready in time for device_initcall users */ > +subsys_initcall(remoteproc_init); > + > +static void __exit remoteproc_exit(void) > +{ > + if (rproc_dbg) > + debugfs_remove(rproc_dbg); > +} > +module_exit(remoteproc_exit); > + > +MODULE_LICENSE("GPL v2"); > +MODULE_DESCRIPTION("Generic Remote Processor Framework"); > diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h > new file mode 100644 > index 0000000..6cdb966 > --- /dev/null > +++ b/include/linux/remoteproc.h > @@ -0,0 +1,273 @@ > +/* > + * Remote Processor Framework > + * > + * Copyright(c) 2011 Texas Instruments, Inc. > + * Copyright(c) 2011 Google, Inc. > + * All rights reserved. > + * > + * Redistribution and use in source and binary forms, with or without > + * modification, are permitted provided that the following conditions > + * are met: > + * > + * * Redistributions of source code must retain the above copyright > + * notice, this list of conditions and the following disclaimer. > + * * Redistributions in binary form must reproduce the above copyright > + * notice, this list of conditions and the following disclaimer in > + * the documentation and/or other materials provided with the > + * distribution. > + * * Neither the name Texas Instruments nor the names of its > + * contributors may be used to endorse or promote products derived > + * from this software without specific prior written permission. > + * > + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS > + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT > + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR > + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT > + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, > + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT > + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, > + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY > + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT > + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE > + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. > + */ > + > +#ifndef REMOTEPROC_H > +#define REMOTEPROC_H > + > +#include > +#include > + > +/** > + * DOC: The Binary Structure of the Firmware > + * > + * The following enums and structures define the binary format of the image > + * we load and run the remote processors with. > + * > + * The binary format is as follows: > + * > + * struct { > + * char magic[4] = { 'R', 'P', 'R', 'C' }; > + * u32 version; > + * u32 header_len; > + * char header[...] = { header_len bytes of unformatted, textual header }; > + * struct section { > + * u32 type; > + * u64 da; > + * u32 len; > + * u8 content[...] = { len bytes of binary data }; > + * } [ no limit on number of sections ]; > + * } __packed; > + */ > + > +/** > + * struct fw_header - header of the firmware image > + * @magic: 4-bytes magic (should contain "RPRC") > + * @version: version number, should be bumped on binary changes > + * @header_len: length, in bytes, of the following text header > + * @header: free-style textual header, users can read with 'head' > + * > + * This structure defines the header of the remoteproc firmware. > + */ > +struct fw_header { > + char magic[4]; > + u32 version; > + u32 header_len; > + char header[0]; > +} __packed; > + > +/** > + * struct fw_section - header of a firmware section > + * @type: section type > + * @da: device address that the rproc expects to find this section at. > + * @len: length of the section (in bytes) > + * @content: the section data > + * > + * This structure defines the header of a firmware section. All sections > + * should be loaded to the address specified by @da, so the remote processor > + * will find them. > + * > + * Note: if the remote processor is not behind an IOMMU, then da is a > + * mere physical address > + */ > +struct fw_section { > + u32 type; > + u64 da; > + u32 len; > + char content[0]; > +} __packed; > + > +/** > + * enum fw_section_type - section type values > + * > + * @FW_RESOURCE: a resource section. this section contains static > + * resource requests (/announcements) that the remote > + * processor requires (/supports). Most of these requests > + * require that the host fulfill them (and usually > + * "reply" with a result) before the remote processor > + * is booted. See Documentation/remoteproc.h for more info > + * @FW_TEXT: a text section > + * @FW_DATA: a data section > + * > + * Note: text and data sections have different types so we can support stuff > + * like crash dumps (which only requires dumping data sections) or loading > + * text sections into faster memory. Currently, though, both section types > + * are treated exactly the same. > + */ > +enum fw_section_type { > + FW_RESOURCE = 0, > + FW_TEXT = 1, > + FW_DATA = 2, > +}; > + > +/** > + * struct fw_resource - describes an entry from the resource section > + * @type: resource type > + * @da: depends on the resource type > + * @pa: depends on the resource type > + * @len: depends on the resource type > + * @flags: depends on the resource type > + * @name: name of resource > + * > + * Some resources entries are mere announcements, where the host is informed > + * of specific remoteproc configuration. Other entries require the host to > + * do something (e.g. reserve a requested resource) and reply by overwriting > + * a member inside struct fw_resource with the id of the allocated resource. > + * There could also be resource entries where the remoteproc's image suggests > + * a configuration, but the host may overwrite it with its own preference. > + * > + * Note: the vast majority of the resource types are not implemented yet, > + * and this is all very much preliminary. > + */ > +struct fw_resource { > + u32 type; > + u64 da; > + u64 pa; > + u32 len; > + u32 flags; > + u8 name[48]; > +} __packed; > + > +/** > + * enum fw_resource_type - types of resource entries > + * > + * @RSC_TRACE: announces the availability of a trace buffer into which > + * the remote processor will be writing logs. In this case, > + * 'da' indicates the device address where logs are written to, > + * and 'len' is the size of the trace buffer. > + * Currently we support two trace buffers per remote processor, > + * to support two autonomous cores running in a single rproc > + * device. > + * If additional trace buffers are needed, this should be > + * extended/generalized. > + * @RSC_BOOTADDR: announces the address of the first instruction the remote > + * processor should be booted with (address indicated in 'da'). > + * > + * Note: most of the resource types are not implemented yet, so they are > + * not documented yet. > + */ > +enum fw_resource_type { > + RSC_CARVEOUT = 0, > + RSC_DEVMEM = 1, > + RSC_DEVICE = 2, > + RSC_IRQ = 3, > + RSC_TRACE = 4, > + RSC_BOOTADDR = 5, > +}; > + > +/** > + * struct rproc_mem_entry - memory mapping descriptor > + * @da: device address as seen by the remote processor > + * @pa: physical address > + * @size: size of this memory region > + * > + * Board file will use this struct to define the IOMMU configuration > + * for this remote processor. If the rproc device accesses physical memory > + * directly (and not through an IOMMU), this is not needed. > + */ > +struct rproc_mem_entry { > + u64 da; > + phys_addr_t pa; > + u32 size; > +}; > + > +struct rproc; > + > +/** > + * struct rproc_ops - platform-specific device handlers > + * @start: power on the device and boot it. implementation may require > + * specifyng a boot address > + * @stop: power off the device > + */ > +struct rproc_ops { > + int (*start)(struct rproc *rproc, u64 bootaddr); > + int (*stop)(struct rproc *rproc); > +}; > + > +/* > + * enum rproc_state - remote processor states > + * > + * @RPROC_OFFLINE: device is powered off > + * @RPROC_SUSPENDED: device is suspended; needs to be woken up to receive > + * a message. > + * @RPROC_RUNNING: device is up and running > + * @RPROC_LOADING: asynchronous firmware loading has started > + * @RPROC_CRASHED: device has crashed; need to start recovery > + */ > +enum rproc_state { > + RPROC_OFFLINE, > + RPROC_SUSPENDED, > + RPROC_RUNNING, > + RPROC_LOADING, > + RPROC_CRASHED, > +}; > + > +#define RPROC_MAX_NAME 100 I wouldn't even bother with this. The only place it is used is in one of the debugfs files, and you can protect against too large a static buffer by using %100s (or whatever) in the snprintf(). > + > +/* > + * struct rproc - represents a physical remote processor device > + * > + * @next: next rproc entry in the list > + * @name: human readable name of the rproc, cannot exceed RPROC_MAX_NAME bytes > + * @memory_maps: table of da-to-pa memory maps (relevant if device is behind > + * an iommu) > + * @firmware: name of firmware file to be loaded > + * @owner: reference to the platform-specific rproc module > + * @priv: private data which belongs to the platform-specific rproc module > + * @ops: platform-specific start/stop rproc handlers > + * @dev: underlying device > + * @count: usage refcount > + * @state: state of the device > + * @lock: lock which protects concurrent manipulations of the rproc > + * @dbg_dir: debugfs directory of this rproc device > + * @trace_buf0: main trace buffer of the remote processor > + * @trace_buf1: second, optional, trace buffer of the remote processor > + * @trace_len0: length of main trace buffer of the remote processor > + * @trace_len1: length of the second (and optional) trace buffer > + * @firmware_loading_complete: marks e/o asynchronous firmware loading > + */ > +struct rproc { > + struct list_head next; > + const char *name; > + const struct rproc_mem_entry *memory_maps; > + const char *firmware; > + struct module *owner; > + void *priv; > + const struct rproc_ops *ops; > + struct device *dev; > + int count; > + int state; > + struct mutex lock; > + struct dentry *dbg_dir; > + char *trace_buf0, *trace_buf1; > + int trace_len0, trace_len1; > + struct completion firmware_loading_complete; > +}; > + > +struct rproc *rproc_get(const char *); > +void rproc_put(struct rproc *); > +int rproc_register(struct device *, const char *, const struct rproc_ops *, > + const char *, const struct rproc_mem_entry *, struct module *); > +int rproc_unregister(const char *); > + > +#endif /* REMOTEPROC_H */ > -- > 1.7.1 > From grant.likely at secretlab.ca Mon Jun 27 16:00:07 2011 From: grant.likely at secretlab.ca (Grant Likely) Date: Mon, 27 Jun 2011 15:00:07 -0600 Subject: [RFC 2/8] remoteproc: add omap implementation In-Reply-To: <1308640714-17961-3-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-3-git-send-email-ohad@wizery.com> Message-ID: <20110627210007.GC20865@ponder.secretlab.ca> On Tue, Jun 21, 2011 at 10:18:28AM +0300, Ohad Ben-Cohen wrote: > From: Guzman Lugo, Fernando > > Add remoteproc implementation for OMAP4, so we can load the M3 and DSP > remote processors. > > Based on code by Hari Kanigeri > > While this code is functional, and works on OMAP4 & its M3's, > it is still preliminary and going to substantially change: > > 1. Splitting physically contiguous regions to pages should happen > inside the IOMMU API framework, and not here (so omap_rproc_map_unmap > should go away). > 2. IOMMU programming in general should go away too; it should be handled by > generic code (preferably using the DMA mapping API), and not by an OMAP > specific component. > > This patch depends on OMAP's IOMMU API migration, as posted here: > https://lkml.org/lkml/2011/6/2/369 > > [ohad at wizery.com: commit log, generic iommu api migration, refactoring, cleanups] > > Signed-off-by: Guzman Lugo, Fernando > Signed-off-by: Ohad Ben-Cohen > --- > arch/arm/plat-omap/include/plat/remoteproc.h | 41 +++++ > drivers/remoteproc/Kconfig | 21 +++ > drivers/remoteproc/Makefile | 1 + > drivers/remoteproc/omap_remoteproc.c | 243 ++++++++++++++++++++++++++ > 4 files changed, 306 insertions(+), 0 deletions(-) > create mode 100644 arch/arm/plat-omap/include/plat/remoteproc.h > create mode 100644 drivers/remoteproc/omap_remoteproc.c > > diff --git a/arch/arm/plat-omap/include/plat/remoteproc.h b/arch/arm/plat-omap/include/plat/remoteproc.h > new file mode 100644 > index 0000000..6494570 > --- /dev/null > +++ b/arch/arm/plat-omap/include/plat/remoteproc.h > @@ -0,0 +1,41 @@ > +/* > + * Remote Processor - omap-specific bits > + * > + * 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 > + * version 2 as published by the Free Software Foundation. > + * > + * 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. > + */ > + > +#ifndef _PLAT_REMOTEPROC_H > +#define _PLAT_REMOTEPROC_H > + > +#include > + > +/* > + * struct omap_rproc_pdata - omap remoteproc's platform data > + * @name: the remoteproc's name, cannot exceed RPROC_MAX_NAME bytes > + * @iommu_name: iommu device we're behind of > + * @oh_name: omap hwmod device > + * @oh_name_opt: optional, secondary omap hwmod device > + * @firmware: name of firmware file to load > + * @ops: start/stop rproc handlers > + * @memory_maps: table of da-to-pa iommu memory maps > + */ > +struct omap_rproc_pdata { > + const char *name; > + const char *iommu_name; > + const char *oh_name; > + const char *oh_name_opt; > + const char *firmware; > + const struct rproc_ops *ops; > + const struct rproc_mem_entry *memory_maps; > +}; > + > +#endif /* _PLAT_REMOTEPROC_H */ > diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig > index a60bb12..88421bd 100644 > --- a/drivers/remoteproc/Kconfig > +++ b/drivers/remoteproc/Kconfig > @@ -5,3 +5,24 @@ > # REMOTE_PROC gets selected by whoever wants it > config REMOTE_PROC > tristate > + > +# for tristate, either expose omap_device_* or pass it via pdata > +config OMAP_REMOTE_PROC > + bool "OMAP remoteproc support" > + depends on ARCH_OMAP4 > + select OMAP_IOMMU > + select REMOTE_PROC > + select OMAP_MBOX_FWK > + default y > + help > + Say y here to support OMAP's remote processors (dual M3 > + and DSP on OMAP4) via the remote processor framework. > + > + Currently only supported on OMAP4. > + > + Usually you want to say y here, in order to enable multimedia > + use-cases to run on your platform (multimedia codecs are > + offloaded to remote DSP processors using this framework). > + > + It's safe to say n here if you're not interested in multimedia > + offloading or just want a bare minimum kernel. > diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile > index d0f60c7..0198b1d 100644 > --- a/drivers/remoteproc/Makefile > +++ b/drivers/remoteproc/Makefile > @@ -3,3 +3,4 @@ > # > > obj-$(CONFIG_REMOTE_PROC) += remoteproc.o > +obj-$(CONFIG_OMAP_REMOTE_PROC) += omap_remoteproc.o > diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c > new file mode 100644 > index 0000000..91f7f11 > --- /dev/null > +++ b/drivers/remoteproc/omap_remoteproc.c > @@ -0,0 +1,243 @@ > +/* > + * Remote processor machine-specific module for OMAP4 > + * > + * 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 > + * version 2 as published by the Free Software Foundation. > + * > + * 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. > + */ > + > +#define pr_fmt(fmt) "%s: " fmt, __func__ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > + > +struct omap_rproc_priv { > + struct iommu_domain *domain; > + struct device *iommu; > +}; > + > +/* > + * Map a physically contiguous memory region using biggest pages possible. > + * TODO: this code should go away; it belongs in the generic IOMMU layer > + */ > +static int omap_rproc_map_unmap(struct iommu_domain *domain, > + const struct rproc_mem_entry *me, bool map) > +{ > + u32 all_bits; > + /* these are the page sizes supported by OMAP's IOMMU */ > + u32 pg_size[] = {SZ_16M, SZ_1M, SZ_64K, SZ_4K}; > + int i, ret, size = me->size; > + u32 da = me->da; > + u32 pa = me->pa; > + int order; > + int flags; > + > + /* must be aligned at least with the smallest supported iommu page */ > + if (!IS_ALIGNED(size, SZ_4K) || !IS_ALIGNED(da | pa, SZ_4K)) { > + pr_err("misaligned: size %x da 0x%x pa 0x%x\n", size, da, pa); > + return -EINVAL; > + } > + > + while (size) { > + /* find the max page size with which both pa, da are aligned */ > + all_bits = pa | da; > + for (i = 0; i < ARRAY_SIZE(pg_size); i++) { > + if ((size >= pg_size[i]) && > + ((all_bits & (pg_size[i] - 1)) == 0)) { > + break; > + } > + } > + > + order = get_order(pg_size[i]); > + > + /* OMAP4's M3 is little endian, so no need for conversions */ > + flags = MMU_RAM_ENDIAN_LITTLE | MMU_RAM_ELSZ_NONE; > + > + if (map) > + ret = iommu_map(domain, da, pa, order, flags); > + else > + ret = iommu_unmap(domain, da, order); > + > + if (ret) > + return ret; > + > + size -= pg_size[i]; > + da += pg_size[i]; > + pa += pg_size[i]; > + } > + > + return 0; > +} > + > +/* bootaddr isn't needed for the dual M3's */ > +static inline int omap_rproc_start(struct rproc *rproc, u64 bootaddr) > +{ > + struct device *dev = rproc->dev; > + struct platform_device *pdev = to_platform_device(dev); > + struct omap_rproc_pdata *pdata = dev->platform_data; > + struct iommu_domain *domain; > + struct device *iommu; > + struct omap_rproc_priv *priv; > + int ret, i; > + > + if (!iommu_found()) { > + dev_err(&pdev->dev, "iommu not found\n"); > + return -ENODEV; > + } > + > + priv = kmalloc(sizeof(*priv), GFP_KERNEL); > + if (!priv) { > + dev_err(&pdev->dev, "kzalloc failed\n"); > + return -ENOMEM; > + } > + > + /* > + * Use the specified iommu name to find our iommu device. > + * TODO: solve this generically so other platforms can use it, too > + */ > + iommu = omap_find_iommu_device(pdata->iommu_name); > + if (!iommu) { > + dev_err(dev, "omap_find_iommu_device failed\n"); > + ret = -ENODEV; > + goto free_mem; > + } > + > + domain = iommu_domain_alloc(); > + if (!domain) { > + dev_err(&pdev->dev, "can't alloc iommu domain\n"); > + ret = -ENODEV; > + goto free_mem; > + } > + > + priv->iommu = iommu; > + priv->domain = domain; > + rproc->priv = priv; > + > + ret = iommu_attach_device(domain, iommu); > + if (ret) { > + dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret); > + goto free_domain; > + } > + > + for (i = 0; rproc->memory_maps[i].size; i++) { > + const struct rproc_mem_entry *me = &rproc->memory_maps[i]; > + > + ret = omap_rproc_map_unmap(domain, me, true); > + if (ret) { > + dev_err(&pdev->dev, "iommu_map failed: %d\n", ret); > + goto unmap_regions; > + } > + } > + > + /* power on the remote processor itself */ > + ret = omap_device_enable(pdev); > + if (ret) > + goto unmap_regions; > + > + return 0; > + > +unmap_regions: > + for (--i; i >= 0 && rproc->memory_maps[i].size; i--) { > + const struct rproc_mem_entry *me = &rproc->memory_maps[i]; > + omap_rproc_map_unmap(domain, me, false); > + } > +free_domain: > + iommu_domain_free(domain); > +free_mem: > + kfree(priv); > + return ret; > +} > + > +static inline int omap_rproc_stop(struct rproc *rproc) > +{ > + struct device *dev = rproc->dev; > + struct platform_device *pdev = to_platform_device(dev); > + struct omap_rproc_priv *priv = rproc->priv; > + struct iommu_domain *domain = priv->domain; > + struct device *iommu = priv->iommu; > + int ret, i; > + > + /* power off the remote processor itself */ > + ret = omap_device_shutdown(pdev); > + if (ret) { > + dev_err(dev, "failed to shutdown: %d\n", ret); > + goto out; > + } > + > + for (i = 0; rproc->memory_maps[i].size; i++) { > + const struct rproc_mem_entry *me = &rproc->memory_maps[i]; > + > + ret = omap_rproc_map_unmap(domain, me, false); > + if (ret) { > + dev_err(&pdev->dev, "iommu_unmap failed: %d\n", ret); > + goto out; > + } > + } > + > + iommu_detach_device(domain, iommu); > + iommu_domain_free(domain); > + > +out: > + return ret; > +} > + > +static struct rproc_ops omap_rproc_ops = { > + .start = omap_rproc_start, > + .stop = omap_rproc_stop, > +}; > + > +static int omap_rproc_probe(struct platform_device *pdev) > +{ > + struct omap_rproc_pdata *pdata = pdev->dev.platform_data; > + > + return rproc_register(&pdev->dev, pdata->name, &omap_rproc_ops, > + pdata->firmware, pdata->memory_maps, > + THIS_MODULE); Very little for me to comment on here. However, something I just noticed. Why is it necessary to pass in THIS_MODULE to the rproc_register function? Having a reference to the pdev gives you the pointer to the driver, which has the THIS_MODULE value in it. That should be sufficient. /me also isn't sure if incrementing the refcount on the module is the best way to prevent the rproc from going away, but I haven't dug into the details in the driver code to find out. Drivers can get unbound from devices without the driver being unloaded, so I imagine there is an in-use count on the device itself that would prevent driver unbinding. > +} > + > +static int __devexit omap_rproc_remove(struct platform_device *pdev) > +{ > + struct omap_rproc_pdata *pdata = pdev->dev.platform_data; > + > + return rproc_unregister(pdata->name); > +} > + > +static struct platform_driver omap_rproc_driver = { > + .probe = omap_rproc_probe, > + .remove = __devexit_p(omap_rproc_remove), > + .driver = { > + .name = "omap-rproc", > + .owner = THIS_MODULE, > + }, > +}; > + > +static int __init omap_rproc_init(void) > +{ > + return platform_driver_register(&omap_rproc_driver); > +} > +/* must be ready in time for device_initcall users */ > +subsys_initcall(omap_rproc_init); > + > +static void __exit omap_rproc_exit(void) > +{ > + platform_driver_unregister(&omap_rproc_driver); > +} > +module_exit(omap_rproc_exit); > + > +MODULE_LICENSE("GPL v2"); > +MODULE_DESCRIPTION("OMAP Remote Processor control driver"); > -- > 1.7.1 > From mgrosen at ti.com Mon Jun 27 16:22:15 2011 From: mgrosen at ti.com (Grosen, Mark) Date: Mon, 27 Jun 2011 21:22:15 +0000 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <4E04F0B0.4030408@codeaurora.org> Message-ID: > From: Ohad Ben-Cohen > Sent: Saturday, June 25, 2011 6:12 PM > > Hi Stephen, > > On Fri, Jun 24, 2011 at 11:16 PM, Stephen Boyd wrote: > > Instead of devising a new firmware format, we decided > > to just stick with elf and parse the headers in the kernel because we > > needed them for authentication anyway. Is this reason enough to move to > > an ELF format instead? > > I think that consolidation of code is enough reason to make an effort. > I know that our firmware format was chosen for simplicity, but I'm not > sure if we have the tools yet to build standard ELF files for the > remote processors (IIRC it's in the works though). I'll let Mark > comment this one. Yes, we are converting from "standard" ELF to the simple format. I've used the GNU binutils to work with our ELF files. There were a few motivations: 1. Concern about complexity of parsing ELF files in kernel; however, the PIL implementation looks pretty clean to me. 2. We added a special section (resource table) that is interpreted as part of the loading process. The tool that generates our simple format just recognizes a named section (".resource_table"), so perhaps that could be done with the PIL ELF loader. 3. Smaller firmware file sizes. Our ELF files are large relative to the payload, but this might be addressed by a better ELF "strip" utility. > > Another difference is inter-processor dependencies. For example, on > > msm8660 the modem can't boot until the dsp has been booted. I suppose we > > could hide this detail in the platform specific get() implementation by > > calling rproc_get() on the dependent processor (hopefully no locking > > issues arise). I'd rather have it built into the core though as it isn't > > really specific to the hardware. > > No problems, I'm sure we can solve this one easily. > > > If we can resolve these differences I think we can easily support remote > > processor boot on MSM via remoteproc. > > That'd be very cool, I sure do hope we can work together. Yes, I hope we can merge our efforts on PIL and remoteproc since they seem quite close in function and design. Mark From mgrosen at ti.com Mon Jun 27 16:52:30 2011 From: mgrosen at ti.com (Grosen, Mark) Date: Mon, 27 Jun 2011 21:52:30 +0000 Subject: [RFC 1/8] drivers: add generic remoteproc framework In-Reply-To: <20110627204958.GB20865@ponder.secretlab.ca> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-2-git-send-email-ohad@wizery.com> <20110627204958.GB20865@ponder.secretlab.ca> Message-ID: > From: Grant Likely > Sent: Monday, June 27, 2011 1:50 PM Grant, thanks for the feedback. I'll try to answer one of your questions below and leave the rest for Ohad. Mark > > +Every remoteproc implementation must provide these handlers: > > + > > +struct rproc_ops { > > + int (*start)(struct rproc *rproc, u64 bootaddr); > > + int (*stop)(struct rproc *rproc); > > +}; > > + > > +The ->start() handler takes a rproc handle and an optional bootaddr > argument, > > +and should power on the device and boot it (using the bootaddr > argument > > +if the hardware requires one). > > Naive question: Why is bootaddr an argument? Wouldn't rproc drivers > keep track of the boot address in their driver private data? Our AMPs (remote processors) have a variety of boot mechanisms that vary across the different SoCs (yes, TI likes HW diversity). In some cases, the boot address is more like an entry point and that comes from the firmware, so it is not a static attribute of a driver. Correct me if I misunderstood your question. Mark From grant.likely at secretlab.ca Mon Jun 27 17:21:21 2011 From: grant.likely at secretlab.ca (Grant Likely) Date: Mon, 27 Jun 2011 16:21:21 -0600 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: <1308640714-17961-8-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-8-git-send-email-ohad@wizery.com> Message-ID: <20110627222121.GD20865@ponder.secretlab.ca> On Tue, Jun 21, 2011 at 10:18:33AM +0300, Ohad Ben-Cohen wrote: > Add a virtio-based IPC bus, which enables kernel users to communicate > with remote processors over shared memory using a simple messaging > protocol. > > Assign a local address for every local endpoint that is created, > and bind it to the user's callback. Invoke that callback when the > destination of an inbound message is the user's address. > > Signed-off-by: Ohad Ben-Cohen Hey Ohad, Nice patch. I'm quite thrilled to see this implemented. Some comments below, but otherwise I think it looks pretty good. > --- > Documentation/ABI/testing/sysfs-bus-rpmsg | 75 +++ > Documentation/rpmsg.txt | 308 +++++++++ > drivers/Kconfig | 1 + > drivers/Makefile | 1 + > drivers/rpmsg/Kconfig | 14 + > drivers/rpmsg/Makefile | 1 + > drivers/rpmsg/virtio_rpmsg_bus.c | 1036 +++++++++++++++++++++++++++++ > include/linux/mod_devicetable.h | 10 + > include/linux/rpmsg.h | 421 ++++++++++++ > include/linux/virtio_ids.h | 1 + > 10 files changed, 1868 insertions(+), 0 deletions(-) > create mode 100644 Documentation/ABI/testing/sysfs-bus-rpmsg > create mode 100644 Documentation/rpmsg.txt > create mode 100644 drivers/rpmsg/Kconfig > create mode 100644 drivers/rpmsg/Makefile > create mode 100644 drivers/rpmsg/virtio_rpmsg_bus.c > create mode 100644 include/linux/rpmsg.h > > diff --git a/Documentation/rpmsg.txt b/Documentation/rpmsg.txt > new file mode 100644 > index 0000000..0a7c820 > --- /dev/null > +++ b/Documentation/rpmsg.txt [...] Great documentation! Thanks! > diff --git a/drivers/Kconfig b/drivers/Kconfig > index 1f6d6d3..840f835 100644 > --- a/drivers/Kconfig > +++ b/drivers/Kconfig > @@ -128,4 +128,5 @@ source "drivers/clocksource/Kconfig" > > source "drivers/remoteproc/Kconfig" > > +source "drivers/rpmsg/Kconfig" > endmenu > diff --git a/drivers/Makefile b/drivers/Makefile > index 4d53a18..2980a15 100644 > --- a/drivers/Makefile > +++ b/drivers/Makefile > @@ -22,6 +22,7 @@ obj-$(CONFIG_ARM_AMBA) += amba/ > obj-$(CONFIG_DMA_ENGINE) += dma/ > > obj-$(CONFIG_VIRTIO) += virtio/ > +obj-$(CONFIG_RPMSG) += rpmsg/ > obj-$(CONFIG_XEN) += xen/ > > # regulators early, since some subsystems rely on them to initialize > diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig > new file mode 100644 > index 0000000..41303f5 > --- /dev/null > +++ b/drivers/rpmsg/Kconfig > @@ -0,0 +1,14 @@ > +# RPMSG always gets selected by whoever wants it > +config RPMSG > + tristate > + select VIRTIO > + select VIRTIO_RING > + > +if RPMSG > + > +# OK, it's a little counter-intuitive to do this, but it puts it neatly under > +# the rpmsg menu (and it's the approach preferred by the virtio folks). > + > +source "drivers/virtio/Kconfig" What happens when kvm and rpmsg both get enabled on the same kernel. ARM virtualization is currently being worked on, so it will happen. Also, I can see this finding use in the x86 world to talk to coprocessor boards (like the Xilinx FPGA plugin board which can have a soft core on it). > + > +endif # RPMSG > diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile > new file mode 100644 > index 0000000..7617fcb > --- /dev/null > +++ b/drivers/rpmsg/Makefile > @@ -0,0 +1 @@ > +obj-$(CONFIG_RPMSG) += virtio_rpmsg_bus.o > diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c > new file mode 100644 > index 0000000..3e98b02 > --- /dev/null > +++ b/drivers/rpmsg/virtio_rpmsg_bus.c [...] > +/* rpmsg devices and drivers are matched using the service name */ > +static inline int rpmsg_id_match(const struct rpmsg_channel *rpdev, > + const struct rpmsg_device_id *id) > +{ > + if (strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE)) > + return 0; > + > + return 1; > +} or simply: 'return strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE) == 0;' :-) > +/* for more info, see below documentation of rpmsg_create_ept() */ > +static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp, > + struct rpmsg_channel *rpdev, > + void (*cb)(struct rpmsg_channel *, void *, int, void *, u32), > + void *priv, u32 addr) > +{ > + int err, tmpaddr, request; > + struct rpmsg_endpoint *ept; > + struct device *dev = rpdev ? &rpdev->dev : &vrp->vdev->dev; > + > + if (!idr_pre_get(&vrp->endpoints, GFP_KERNEL)) > + return NULL; > + > + ept = kzalloc(sizeof(*ept), GFP_KERNEL); > + if (!ept) { > + dev_err(dev, "failed to kzalloc a new ept\n"); > + return NULL; > + } > + > + ept->rpdev = rpdev; > + ept->cb = cb; > + ept->priv = priv; > + > + /* do we need to allocate a local address ? */ > + request = addr == RPMSG_ADDR_ANY ? RPMSG_RESERVED_ADDRESSES : addr; > + > + spin_lock(&vrp->endpoints_lock); Is a spin_lock the right choice for endpoints, or should it be a mutex (do the endpoints operations need to be atomic)? > +/* > + * find an existing channel using its name + address properties, > + * and destroy it > + */ > +static int rpmsg_destroy_channel(struct virtproc_info *vrp, > + struct rpmsg_channel_info *chinfo) > +{ > + struct virtio_device *vdev = vrp->vdev; > + struct device *dev; > + > + dev = device_find_child(&vdev->dev, chinfo, rpmsg_channel_match); > + if (!dev) > + return -EINVAL; > + > + device_unregister(dev); > + > + put_device(dev); > + > + kfree(to_rpmsg_channel(dev)); At put_device time, it is conceivable that the dev pointer is no longer valid. You'll need to get do the to_rpmsg_channel() before putting the dev. > + > + return 0; > +} > + > +/* super simple buffer "allocator" that is just enough for now */ > +static void *get_a_tx_buf(struct virtproc_info *vrp) > +{ > + unsigned int len; > + void *ret; > + > + /* support multiple concurrent senders */ > + spin_lock(&vrp->tx_lock); > + > + /* > + * either pick the next unused tx buffer > + * (half of our buffers are used for sending messages) > + */ > + if (vrp->last_sbuf < vrp->num_bufs / 2) > + ret = vrp->sbufs + vrp->buf_size * vrp->last_sbuf++; > + /* or recycle a used one */ > + else > + ret = virtqueue_get_buf(vrp->svq, &len); > + > + spin_unlock(&vrp->tx_lock); > + > + return ret; > +} > + > +/** > + * rpmsg_upref_sleepers() - enable "tx-complete" interrupts, if needed > + * @vrp: virtual remote processor state > + * > + * This function is called before a sender is blocked, waiting for > + * a tx buffer to become available. > + * > + * If we already have blocking senders, this function merely increases > + * the "sleepers" reference count, and exits. > + * > + * Otherwise, if this is the first sender to block, we also enable > + * virtio's tx callbacks, so we'd be immediately notified when a tx > + * buffer is consumed (we rely on virtio's tx callback in order > + * to wake up sleeping senders as soon as a tx buffer is used by the > + * remote processor). > + */ > +static void rpmsg_upref_sleepers(struct virtproc_info *vrp) > +{ > + /* support multiple concurrent senders */ > + spin_lock(&vrp->tx_lock); > + > + /* are we the first sleeping context waiting for tx buffers ? */ > + if (!vrp->sleepers++) Maybe use a kref? It might be useful to have a kref_get_first() that takes a callback used for the first increment. > +static int rpmsg_remove_device(struct device *dev, void *data) > +{ > + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); > + > + device_unregister(dev); > + > + kfree(rpdev); put_device() I think. > + > + return 0; > +} > + > +static void __devexit rpmsg_remove(struct virtio_device *vdev) > +{ > + struct virtproc_info *vrp = vdev->priv; > + int ret; > + > + ret = device_for_each_child(&vdev->dev, NULL, rpmsg_remove_device); > + if (ret) > + dev_warn(&vdev->dev, "can't remove rpmsg device: %d\n", ret); > + > + idr_remove_all(&vrp->endpoints); > + idr_destroy(&vrp->endpoints); > + > + vdev->config->del_vqs(vrp->vdev); > + > + kfree(vrp); > +} > + > +static struct virtio_device_id id_table[] = { > + { VIRTIO_ID_RPMSG, VIRTIO_DEV_ANY_ID }, > + { 0 }, > +}; > + > +static unsigned int features[] = { > + VIRTIO_RPMSG_F_NS, > +}; > + > +static struct virtio_driver virtio_ipc_driver = { > + .feature_table = features, > + .feature_table_size = ARRAY_SIZE(features), > + .driver.name = KBUILD_MODNAME, > + .driver.owner = THIS_MODULE, > + .id_table = id_table, > + .probe = rpmsg_probe, > + .remove = __devexit_p(rpmsg_remove), > +}; > + > +static int __init init(void) Even for static functions, it's a really good idea to prefix all function names and file scoped symbol with a common prefix like "rpmsg_". Doing so avoids even the outside chance of a namespace conflict. > +{ > + int ret; > + > + ret = bus_register(&rpmsg_bus); > + if (ret) { > + pr_err("failed to register rpmsg bus: %d\n", ret); > + return ret; > + } > + > + return register_virtio_driver(&virtio_ipc_driver); And if register_virtio_driver fails? > +} > +module_init(init); > + > +static void __exit fini(void) > +{ > + unregister_virtio_driver(&virtio_ipc_driver); > + bus_unregister(&rpmsg_bus); > +} > +module_exit(fini); > + > +MODULE_DEVICE_TABLE(virtio, id_table); > +MODULE_DESCRIPTION("Virtio-based remote processor messaging bus"); > +MODULE_LICENSE("GPL v2"); > diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h > index ae28e93..561567e 100644 > --- a/include/linux/mod_devicetable.h > +++ b/include/linux/mod_devicetable.h > @@ -533,4 +533,14 @@ struct isapnp_device_id { > kernel_ulong_t driver_data; /* data private to the driver */ > }; > > +/* rpmsg */ > + > +#define RPMSG_NAME_SIZE 32 > +#define RPMSG_DEVICE_MODALIAS_FMT "rpmsg:%s" > + > +struct rpmsg_device_id { > + char name[RPMSG_NAME_SIZE]; > + kernel_ulong_t driver_data /* Data private to the driver */ > + __attribute__((aligned(sizeof(kernel_ulong_t)))); > +}; Should this be co-located with vio_device_id? It makes it easier to dereference in kernel code if you do: #ifdef __KERNEL__ void *data; #else kernel_ulong_t data; #endif From grant.likely at secretlab.ca Mon Jun 27 17:24:01 2011 From: grant.likely at secretlab.ca (Grant Likely) Date: Mon, 27 Jun 2011 16:24:01 -0600 Subject: [RFC 1/8] drivers: add generic remoteproc framework In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-2-git-send-email-ohad@wizery.com> <20110627204958.GB20865@ponder.secretlab.ca> Message-ID: <20110627222401.GE20865@ponder.secretlab.ca> On Mon, Jun 27, 2011 at 09:52:30PM +0000, Grosen, Mark wrote: > > From: Grant Likely > > Sent: Monday, June 27, 2011 1:50 PM > > Grant, thanks for the feedback. I'll try to answer one of your > questions below and leave the rest for Ohad. > > Mark > > > > +Every remoteproc implementation must provide these handlers: > > > + > > > +struct rproc_ops { > > > + int (*start)(struct rproc *rproc, u64 bootaddr); > > > + int (*stop)(struct rproc *rproc); > > > +}; > > > + > > > +The ->start() handler takes a rproc handle and an optional bootaddr > > argument, > > > +and should power on the device and boot it (using the bootaddr > > argument > > > +if the hardware requires one). > > > > Naive question: Why is bootaddr an argument? Wouldn't rproc drivers > > keep track of the boot address in their driver private data? > > Our AMPs (remote processors) have a variety of boot mechanisms that vary > across the different SoCs (yes, TI likes HW diversity). In some cases, the > boot address is more like an entry point and that comes from the firmware, > so it is not a static attribute of a driver. Correct me if I misunderstood > your question. More to the point, I would expect the boot_address to be a property of the rproc instance because it represents the configuration of the remote processor. It seems odd that the caller of ->start would know better than the rproc driver about the entry point of the processor. g. From linux at arm.linux.org.uk Mon Jun 27 18:29:03 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Tue, 28 Jun 2011 00:29:03 +0100 Subject: [RFC 1/8] drivers: add generic remoteproc framework In-Reply-To: <20110627204958.GB20865@ponder.secretlab.ca> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-2-git-send-email-ohad@wizery.com> <20110627204958.GB20865@ponder.secretlab.ca> Message-ID: <20110627232903.GB7736@n2100.arm.linux.org.uk> On Mon, Jun 27, 2011 at 02:49:58PM -0600, Grant Likely wrote: > > +struct { > > + char magic[4] = { 'R', 'P', 'R', 'C' }; > > + u32 version; > > + u32 header_len; > > + char header[...] = { header_len bytes of unformatted, textual header }; > > + struct section { > > + u32 type; > > + u64 da; > > + u32 len; > > + u8 content[...] = { len bytes of binary data }; > > + } [ no limit on number of sections ]; > > +} __packed; > > Other have commented on the image format, so I'll skip this bit other > than saying that I agree it would be great to have a common format. (Don't have the original message to reply to...) Do we really want to end up with header being 5 bytes, header_len set as 5, and having to load/store all this data using byte loads/stores ? If we don't want that, then I suggest we get rid of the packed attribute, and require stuff to be naturally aligned. First issue is that struct section could be much better layed out: struct section { u32 type; u32 len; u64 da; u8 content[]; }; and require sizeof(struct section) % sizeof(u64) == 0 - iow, to find the next section, round len up to sizeof(u64). Ditto for the header. From grant.likely at secretlab.ca Mon Jun 27 18:35:49 2011 From: grant.likely at secretlab.ca (Grant Likely) Date: Mon, 27 Jun 2011 17:35:49 -0600 Subject: [RFC 1/8] drivers: add generic remoteproc framework In-Reply-To: <20110627232903.GB7736@n2100.arm.linux.org.uk> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-2-git-send-email-ohad@wizery.com> <20110627204958.GB20865@ponder.secretlab.ca> <20110627232903.GB7736@n2100.arm.linux.org.uk> Message-ID: On Mon, Jun 27, 2011 at 5:29 PM, Russell King - ARM Linux wrote: > On Mon, Jun 27, 2011 at 02:49:58PM -0600, Grant Likely wrote: >> > +struct { >> > + ? ? ?char magic[4] = { 'R', 'P', 'R', 'C' }; >> > + ? ? ?u32 version; >> > + ? ? ?u32 header_len; >> > + ? ? ?char header[...] = { header_len bytes of unformatted, textual header }; >> > + ? ? ?struct section { >> > + ? ? ? ? ?u32 type; >> > + ? ? ? ? ?u64 da; >> > + ? ? ? ? ?u32 len; >> > + ? ? ? ? ?u8 content[...] = { len bytes of binary data }; >> > + ? ? ?} [ no limit on number of sections ]; >> > +} __packed; >> >> Other have commented on the image format, so I'll skip this bit other >> than saying that I agree it would be great to have a common format. > > (Don't have the original message to reply to...) > > Do we really want to end up with header being 5 bytes, header_len set > as 5, and having to load/store all this data using byte loads/stores ? > > If we don't want that, then I suggest we get rid of the packed attribute, > and require stuff to be naturally aligned. > > First issue is that struct section could be much better layed out: > > struct section { > ? ? ? ?u32 type; > ? ? ? ?u32 len; > ? ? ? ?u64 da; > ? ? ? ?u8 content[]; > }; > > and require sizeof(struct section) % sizeof(u64) == 0 - iow, to find > the next section, round len up to sizeof(u64). ?Ditto for the header. Hopefully this will all be moot since it has been proposed to use elf images directly. g. -- Grant Likely, B.Sc., P.Eng. Secret Lab Technologies Ltd. From mgrosen at ti.com Mon Jun 27 18:54:30 2011 From: mgrosen at ti.com (Grosen, Mark) Date: Mon, 27 Jun 2011 23:54:30 +0000 Subject: [RFC 1/8] drivers: add generic remoteproc framework In-Reply-To: <20110627222401.GE20865@ponder.secretlab.ca> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-2-git-send-email-ohad@wizery.com> <20110627204958.GB20865@ponder.secretlab.ca> <20110627222401.GE20865@ponder.secretlab.ca> Message-ID: > From: Grant Likely > Sent: Monday, June 27, 2011 3:24 PM > > Our AMPs (remote processors) have a variety of boot mechanisms that vary > > across the different SoCs (yes, TI likes HW diversity). In some cases, the > > boot address is more like an entry point and that comes from the firmware, > > so it is not a static attribute of a driver. Correct me if I misunderstood > > your question. > > More to the point, I would expect the boot_address to be a property of > the rproc instance because it represents the configuration of the > remote processor. It seems odd that the caller of ->start would know > better than the rproc driver about the entry point of the processor. > > g. Yes, in many cases the boot_address will be defined by the HW. However, we have processors that are "soft" - the boot_address comes from the particular firmware being loaded and can (will) be different with each firmware image. We factored out the firmware loader to be device-independent (in remoteproc.c) so it's not repeated in each device-specific implementation like omap_remoteproc.c and davinci_remoteproc.c. In the cases where the HW dictates what happens, the start() method should just ignore the boot_address. Mark From nsekhar at ti.com Tue Jun 28 04:36:22 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 28 Jun 2011 15:06:22 +0530 Subject: [RFC 5/8] remoteproc: add davinci implementation In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-6-git-send-email-ohad@wizery.com> <4E035B78.3080200@mvista.com> <4E04A9AE.3030801@mvista.com> Message-ID: Hi Mark, On Mon, Jun 27, 2011 at 23:50:25, Grosen, Mark wrote: > > >>> +static inline int davinci_rproc_start(struct rproc *rproc, u64 > > >> bootaddr) > > >>> +{ > > >>> + struct device *dev = rproc->dev; > > >>> + struct davinci_rproc_pdata *pdata = dev->platform_data; > > >>> + struct davinci_soc_info *soc_info = &davinci_soc_info; > > >>> + void __iomem *psc_base; > > >>> + struct clk *dsp_clk; > > >>> + > > >>> + /* hw requires the start (boot) address be on 1KB boundary */ > > >>> + if (bootaddr & 0x3ff) { > > >>> + dev_err(dev, "invalid boot address: must be aligned to > > >> 1KB\n"); > > >>> + return -EINVAL; > > >>> + } > > >>> + > > >>> + dsp_clk = clk_get(dev, pdata->clk_name); > > > > >> We could match using clkdev functionality, but the clock entry > > >> would need to be changed then... > > > > > I followed the existing pattern I saw in other drivers. > > > > Probably MUSB? We're trying to move away from passing the clock name to thge > > drivers, using match by device instead. > > > > > If there is a new, better way, please point me to an example. > > > > Look at the da850_clks[] in mach-davinci/da850.c: if the device name is > > specified as a first argument to CLK() macro (instead of clock name the second > > argument), the matching is done by device, so you don't need to specify the > > clock name to clk_get(), passing NULL instead. > > Thanks, I'll look at this and ask for Sekhar and Kevin's preferences. The second argument is not a (SoC specific) clock name, it is the "connection id" which is specific to the IP (not to the SoC). For example, the EMAC IP may want to deal with interface clock, functional clock and mii clock. So, it passes a connection identifier to tell the clock framework which particular clock it is interested in. The names of these connection identifiers will not change with SoC as they are IP property. So, it will not be right to get this through platform data. If there is no connection identifier in your case, you can just pass the second argument as NULL. The clock matching will happen using device name (which should remain same across SoCs too). Of course, this means that any incorrectly defined clock lookup structures for dsp clock in platform code needs to be corrected. Thanks, Sekhar From sshtylyov at mvista.com Tue Jun 28 05:18:19 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Tue, 28 Jun 2011 14:18:19 +0400 Subject: [RFC 6/8] davinci: da850: add remoteproc dsp device In-Reply-To: <1308640714-17961-7-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-7-git-send-email-ohad@wizery.com> Message-ID: <4E09AA6B.9090609@mvista.com> Hello. On 21.06.2011 11:18, Ohad Ben-Cohen wrote: > From: Mark Grosen > Add davinci remoteproc device for the da850's C674x dsp remote > processor, and support it on the da850-evm and omapl138-hawk boards. > Signed-off-by: Mark Grosen > Signed-off-by: Ohad Ben-Cohen [...] > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 133aac4..9280b1e 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -74,6 +74,13 @@ static struct clk pll0_aux_clk = { > .flags = CLK_PLL | PRE_PLL, > }; > > +static struct clk pll0_sysclk1 = { > + .name = "pll0_sysclk1", > + .parent = &pll0_clk, > + .flags = CLK_PLL, > + .div_reg = PLLDIV1, > +}; > + > static struct clk pll0_sysclk2 = { > .name = "pll0_sysclk2", > .parent = &pll0_clk, > @@ -373,6 +380,12 @@ static struct clk spi1_clk = { > .flags = DA850_CLK_ASYNC3, > }; > > +static struct clk dsp_clk = { > + .name = "dsp", > + .parent = &pll0_sysclk1, > + .lpsc = DA8XX_LPSC0_GEM, > +}; > + > static struct clk_lookup da850_clks[] = { > CLK(NULL, "ref", &ref_clk), > CLK(NULL, "pll0", &pll0_clk), > @@ -419,6 +432,7 @@ static struct clk_lookup da850_clks[] = { > CLK(NULL, "usb20", &usb20_clk), > CLK("spi_davinci.0", NULL, &spi0_clk), > CLK("spi_davinci.1", NULL, &spi1_clk), > + CLK(NULL, "dsp", &dsp_clk), > CLK(NULL, NULL, NULL), > }; How about also adding the clock for DA830? > diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c > index fc4e98e..3becfd1 100644 > --- a/arch/arm/mach-davinci/devices-da8xx.c > +++ b/arch/arm/mach-davinci/devices-da8xx.c [...] > @@ -647,6 +648,20 @@ int __init da850_register_mmcsd1(struct davinci_mmc_config *config) > da850_mmcsd1_device.dev.platform_data = config; > return platform_device_register(&da850_mmcsd1_device); > } > + > +int __init da850_register_rproc(void) Please rename to da8xx_register_rproc() -- there's nothing DA850-specific here. > +{ > + struct platform_device *rproc; > + struct davinci_rproc_pdata rproc_pdata = { > + .name = "dsp", > + .firmware = "davinci-dsp.bin", Isn't the firmware DA8xx-specific? WBR, Sergei From nsekhar at ti.com Tue Jun 28 06:23:27 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 28 Jun 2011 16:53:27 +0530 Subject: [PATCH] davinci: da850: add a .set_rate method to ref_clk In-Reply-To: <1308073157-22993-1-git-send-email-christian.riesch@omicron.at> References: <1308073157-22993-1-git-send-email-christian.riesch@omicron.at> Message-ID: Hi Christian, On Tue, Jun 14, 2011 at 23:09:17, Christian Riesch wrote: > This patch allows setting the input clock frequency of the SoC from > the board specific using the davinci_set_refclk_rate function. "board specific code" > > Suggested-by: Kevin Hilman > Cc: Sekhar Nori > Signed-off-by: Christian Riesch > --- > > This patch applies on top of Sekhar's patch. > Best regards, Christian > > arch/arm/mach-davinci/da850.c | 1 + > 1 files changed, 1 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 133aac4..27fe3ac 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -58,6 +58,7 @@ static struct pll_data pll0_data = { > static struct clk ref_clk = { > .name = "ref_clk", > .rate = DA850_REF_FREQ, > + .set_rate = davinci_simple_set_rate, Please use tabs for indentation. Thanks, Sekhar From ohad at wizery.com Tue Jun 28 06:26:01 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Tue, 28 Jun 2011 14:26:01 +0300 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <4E04F0B0.4030408@codeaurora.org> Message-ID: On Tue, Jun 28, 2011 at 12:22 AM, Grosen, Mark wrote: > 2. We added a special section (resource table) that is interpreted as part > of the loading process. The tool that generates our simple format just > recognizes a named section (".resource_table"), so perhaps that could be > done with the PIL ELF loader. I hope that DT will completely replace that "resource table" section eventually. We still need to try it out (as soon as DT materializes on ARM/pandaboard) and see how it all fits together, but in general it looks like DT could provide all the necessary static resource configuration, and then we just need to expose it to the remote processor. From manjunath.hadli at ti.com Tue Jun 28 07:32:01 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Tue, 28 Jun 2011 18:02:01 +0530 Subject: [RFC] Media Controller Capture driver for DM365 In-Reply-To: <201105020006.24338.laurent.pinchart@ideasonboard.com> References: <201105020006.24338.laurent.pinchart@ideasonboard.com> Message-ID: Laurent, Sorry for getting back late on this. I was also tied up. I am following it up with the patches. -Manju On Mon, May 02, 2011 at 03:36:23, Laurent Pinchart wrote: > Hi Manjunath, > > On Wednesday 27 April 2011 16:14:03 Hadli, Manjunath wrote: > > Introduction > > ------------ > > This is the proposal of the initial version of design and > > implementation of the DM365 VPFE (Video Port Front End) drivers using > > Media Controloler , the initial version which supports the following: > > 1) dm365 vpfe > > 2) ccdc,previewer,resizer,h3a,af blocks > > 3) supports both continuous and single-shot modes > > 4) supports user pointer exchange and memory mapped modes for buffer > > allocation > > > > This driver bases its design on Laurent Pinchart's Media Controller > > Design whose patches for Media Controller and subdev enhancements form the base. > > The driver also takes copious elements taken from Laurent Pinchart and > > others' OMAP ISP driver based on Media Controller. So thank you all > > the people who are responsible for the Media Controller and the OMAP > > ISP driver. > > You're welcome. > > > Also, the core functionality of the driver comes from the arago vpfe > > capture driver of which the CCDC capture was based on V4L2, with other > > drivers like Previwer, and Resizer. > > > > The specification for DM365 can be found here: > > dm365 vpfe: http://www.ti.com/litv/pdf/sprufg8c > > > > The initial version of the driver implementation can be found here: > > > > http://git.linuxtv.org/mhadli/v4l-dvb-davinci_devices.git?a=shortlog;h > > =refs > > /heads/mc_release > > I'll try to review the code, but I got the DM644x V4L2 display driver on my todo-list first :-) > > > Driver Design: Main entities > > ---------------------------- > > The hardware modules for dm355,dm365 are mainly ipipe, ipipeif,isif. > > These hardware modules are generically exposed to the user level in > > the for of > > dm6446 style modules. Mainly - > > ccdc, previewer, resizer in addition to the other histogram and auto > > color/white balance correction and auto focus modules. > > I'm curious, why do you name the modules CCDC and previewer in the code, when they're called ISIF, IPIPEIF and IPIPE in the documentation ? That's quite confusing. The driver is intended to support Dm365, Dm355 and Dm6446. The three are similar and there are some internal differences. The counterparts for IPIPE in Dm6446 are known by names as CCDC , previewer etc and a good part of the world knows the driver by that name. So rather than changing the names, we decided to keep them as-is. > > > 1)MT9P031 sensor module for RAW capture > > 2)TVP7002 decoder module for HD inputs 3)TVP514x decoder module for SD > > inputs 4)CCDC capture module 5)Previewer Module for Bayer to YUV > > conversion 6)Resizer Module for scaling > > > > Connection for on-the-fly capture > > --------------------------------- > > Mt9P031 > > ------>CCDC--->Previewer(optional)--->Resizer(optional)--->Video > > > > TVP7002 --- > > > > TV514x --- > > > > File Organisation > > ----------------- > > > > main driver files > > ---------------- > > drivers/media/video/davinci/vpfe_capture.c > > include/media/davinci/vpfe_capture.h > > > > Instantiatiation of the v4l2 device, media device and all subdevs > > from here. > > > > video Interface files > > --------------------- > > drivers/media/video/davinci/vpfe_video.c > > include/media/davinci/vpfe_video.h > > > > Implements all the v4l2 video operations with a generic implementation > > for continuous and one shot mode. > > > > subdev interface files > > ---------------------- > > These file implement the subdev interface functionality for each of > > the subdev entities - mainly the entry points and their > > implementations in a IP generic way. > > > > drivers/media/video/davinci/vpfe_ccdc.c > > drivers/media/video/davinci/vpfe_previewer.c > > drivers/media/video/davinci/vpfe_resizer.c > > drivers/media/video/davinci/vpfe_af.c > > drivers/media/video/davinci/vpfe_aew.c > > drivers/media/video/mt9p031.c > > drivers/media/video/tvp514x.c > > drivers/media/video/tvp7002.c > > drivers/media/video/ths7353.c > > > > include/media/davinci/vpfe_ccdc.h > > include/media/davinci/vpfe_previewer.h > > include/media/davinci/vpfe_resizer.h > > include/media/davinci/vpfe_af.h > > include/media/davinci/vpfe_aew.h > > include/media/tvp514x.h > > drivers/media/video/tvp514x_regs.h > > include/media/tvp7002.h > > drivers/media/video/tvp7002_reg.h > > > > core implementation files > > ------------------------- > > These provide a core implementation routines for ccdc, ipipeif, > > ipipe,aew, af, resizer hardware modules. > > > > drivers/media/video/davinci/dm365_ccdc.c > > drivers/media/video/davinci/dm365_ipipe.c > > drivers/media/video/davinci/dm365_def_para.c > > drivers/media/video/davinci/dm365_ipipe_hw.c > > drivers/media/video/davinci/dm3xx_ipipe.c > > drivers/media/video/davinci/dm365_aew.c > > drivers/media/video/davinci/dm365_af.c > > drivers/media/video/davinci/dm365_a3_hw.c > > Why don't you combine the davinci/vpfe_*.c files with the davinci/dm365_*.c files ? What's the reason for splitting functionality between vpfe_ccdc.c and dm365_ccdc.c for instance ? The driver is intended to support DM365, DM355 and DM6446. So the vpfe_ files implement the high level interfaces whereas the dm365_ files implement the specifics for the device. > > > include/media/davinci/imp_common.h > > include/media/davinci/dm365_ccdc.h > > include/media/davinci/dm365_ipipe.h > > include/media/davinci/imp_hw_if.h > > include/media/davinci/dm3xx_ipipe.h > > include/media/davinci/dm365_aew.h > > include/media/davinci/dm365_af.h > > include/media/davinci/dm365_a3_hw.h > > include/media/davinci/vpfe_types.h > > > > drivers/media/video/davinci/dm365_ccdc_regs.h > > drivers/media/video/davinci/ccdc_hw_device.h > > drivers/media/video/davinci/dm365_def_para.h > > drivers/media/video/davinci/dm365_ipipe_hw.h > > > > TODOs: > > ====== > > > > 1. Support NV12/YUYV format > > 2. Support more than 1 buffer for single-shot mode 3. Enable Resizer-B > > 4. Remove duplicate format setting in ipipe 5. Remove function > > declarations in dm365_ipipe.c by removing function pointer table 6. > > Remove function declarations in dm365_ccdc.c by removing function > > pointer table 7. Make multilevel previewer module ioctl into single > > level 8. Move kernel only headers into drivers/media/video/davinci > > from include/media/davinci > > -- > Regards, > > Laurent Pinchart > From swetland at google.com Tue Jun 28 06:36:46 2011 From: swetland at google.com (Brian Swetland) Date: Tue, 28 Jun 2011 04:36:46 -0700 Subject: [RFC 0/8] Introducing a generic AMP/IPC framework In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <4E04F0B0.4030408@codeaurora.org> Message-ID: On Tue, Jun 28, 2011 at 4:26 AM, Ohad Ben-Cohen wrote: > On Tue, Jun 28, 2011 at 12:22 AM, Grosen, Mark wrote: >> 2. We added a special section (resource table) that is interpreted as part >> of the loading process. The tool that generates our simple format just >> recognizes a named section (".resource_table"), so perhaps that could be >> done with the PIL ELF loader. > > I hope that DT will completely replace that "resource table" section > eventually. We still need to try it out (as soon as DT materializes on > ARM/pandaboard) and see how it all fits together, but in general it > looks like DT could provide all the necessary static resource > configuration, and then we just need to expose it to the remote > processor. I'm not sure I see the benefit to pulling the resource information out of the firmware image. The resource information is a description of what resources the firmware requires to work properly (it needs certain amounts of working memory, timers, peripheral interfaces like i2c to control camera hw, etc), which will be specific to a given firmware build. Pulling that information out into another place adds new ways for things to break as the firmware and its resource requirements are now separate and could get out of sync, etc. Brian From nsekhar at ti.com Tue Jun 28 08:24:24 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 28 Jun 2011 18:54:24 +0530 Subject: [PATCH 1/4] davinci: psc.h: clean up indentation done using spaces In-Reply-To: <201106271725.14310.arnd@arndb.de> References: <1301072400-4036-1-git-send-email-nsekhar@ti.com> <201106251948.19834.arnd@arndb.de> <201106271725.14310.arnd@arndb.de> Message-ID: Hi Arnd, On Mon, Jun 27, 2011 at 20:55:14, Arnd Bergmann wrote: > Ok. Do you also have a public tree with all the patches that > you would hope to get merged eventually? I'd just like to > get an impression of what's to come. You can have a look at the git tree: git://gitorious.org/linux-davinci/linux-davinci.git The branch "davinci-next-2" is where the accepted patches have been committed. I have deliberately kept them in a branch not included in linux-next. Thanks, Sekhar From christian.riesch at omicron.at Tue Jun 28 10:10:51 2011 From: christian.riesch at omicron.at (Christian Riesch) Date: Tue, 28 Jun 2011 17:10:51 +0200 Subject: [PATCH v2] davinci: da850: add a .set_rate method to ref_clk In-Reply-To: References: Message-ID: <1309273851-9238-1-git-send-email-christian.riesch@omicron.at> This patch allows setting the input clock frequency of the SoC from the board specific code using the davinci_set_refclk_rate function. Suggested-by: Kevin Hilman Signed-off-by: Christian Riesch --- v2: fixed the commit message and indentation. Regards, Christian arch/arm/mach-davinci/da850.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 133aac4..27fe3ac 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -58,6 +58,7 @@ static struct pll_data pll0_data = { static struct clk ref_clk = { .name = "ref_clk", .rate = DA850_REF_FREQ, + .set_rate = davinci_simple_set_rate, }; static struct clk pll0_clk = { -- 1.7.0.4 From greg at kroah.com Tue Jun 28 16:01:12 2011 From: greg at kroah.com (Greg KH) Date: Tue, 28 Jun 2011 14:01:12 -0700 Subject: [PATCH v5 1/1] drivers:staging:pruss: add pruss staging mfd driver. In-Reply-To: <1306827939-4133-2-git-send-email-subhasish@mistralsolutions.com> References: <1306827939-4133-1-git-send-email-subhasish@mistralsolutions.com> <1306827939-4133-2-git-send-email-subhasish@mistralsolutions.com> Message-ID: <20110628210112.GA31081@kroah.com> On Tue, May 31, 2011 at 01:15:39PM +0530, Subhasish Ghosh wrote: > This patch adds the pruss MFD driver and associated include files. > For details regarding the PRUSS please refer the folowing link: > http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem > > The rational behind the MFD driver being the fact that multiple devices can > be implemented on the cores independently. This is determined by the nature > of the program which is loaded into the PRU's instruction memory. > A device may be de-initialized and another loaded or two different devices > can be run simultaneously on the two cores. > It's also possible, as in our case, to implement a single device on both > the PRU's resulting in improved load sharing. > > Signed-off-by: Subhasish Ghosh Please refresh my memory as to why this can't get merged into the "normal" part of the kernel? > --- /dev/null > +++ b/drivers/staging/pruss/TODO > @@ -0,0 +1,14 @@ > +TODO: > + > +0. Functionality wise, everything works. > + > +1. Currently the plan is to add sysfs attributes for > + a. prux/load > + b. prux/unload > + c. prux/run > + d. prux/halt > + These will add to a more dynamic firmware management for the PRU. Why sysfs? > + > +2. But, not sure how the fdt will effect these entries. > + > +Please send patches to Greg Kroah-Hartman . Don't you want people to Cc: you as well? I'm not going to adopt this driver, I have enough as it is :) thanks, greg k-h From ohad at wizery.com Tue Jun 28 16:41:35 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Wed, 29 Jun 2011 00:41:35 +0300 Subject: [RFC 1/8] drivers: add generic remoteproc framework In-Reply-To: <20110627204958.GB20865@ponder.secretlab.ca> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-2-git-send-email-ohad@wizery.com> <20110627204958.GB20865@ponder.secretlab.ca> Message-ID: Hi Grant, Thanks a lot for the exhaustive review and comments ! On Mon, Jun 27, 2011 at 11:49 PM, Grant Likely wrote: >> + ? ? my_rproc = rproc_get("ipu"); > > I tend to be suspicious of apis whose primary interface is by-name > lookup. ?It works fine when the system is small, but it can get > unwieldy when the client driver doesn't have a direct relation to the > setup code that chooses the name. ?At some point I suspect that there > will need to be different lookup mechanism, such as which AMP > processor is currently available (if there are multiple of the same > type). Yeah, this might be too limiting on some systems. I gave this a little thought, but decided to wait until those systems show up first, so I we can better understand them/their requirements/use-cases. For now, I've just followed this simple name-based API model (which still seem a bit popular in several SoC drivers I've looked at, probably due to the general simplicity of it and its use cases). > It also leaves no option for drivers to obtain a reference to the > rproc instance, and bring it up/down as needed (without the name > lookup every time). .. > That said, it looks like only the rproc_get() api is using by-name > lookup, and everything else is via the structure. ?Can (should) the > by-name lookup part be factored out into a rproc_get_by_name() > accessor? I think you are looking for a different set of API here, probably something that is better implemented using runtime PM. When a driver calls rproc_get(), not only does it power on the remote processor, but it also makes sure the underlying implementation cannot go away (i.e. platform-specific remoteproc module cannot be removed, and the rproc cannot be unregistered). After it calls rproc_put(), it cannot rely anymore on the remote processor to stick around (the rproc can be unregistered at this point), so the next time it needs it, it must go through the full get-by-name (or any other get API we will come up with eventually) getter API. If drivers need to hold onto the rproc instance, but still explicitly allow it to power off at times, they should probably call something like pm_runtime_put(rproc->dev). (remoteproc runtime PM support is not implemented yet, but is definitely planned, so we can suspend remote processors on inactivity). > Since rproc_register is allocating a struct rproc instance that > represent the device, shouldn't the pointer to that device be returned > to the caller? Yes, it definitely should. We will have the underlying implementation remember it, and then pass it to rproc_unregister when needed. >> + ?int rproc_unregister(const char *name); > > I definitely would not do this by name. ?I think it is better to pass > the actual instance pointer to rproc_unregister. Much better, yeah. > Naive question: Why is bootaddr an argument? ?Wouldn't rproc drivers > keep track of the boot address in their driver private data? Mark already got that one, but basically the boot address comes from the firmware image: we need to let the implementation know the physical address where the text section is mapped. This is ignored on implementations where that address is fixed (e.g. OMAP's M3). > Other have commented on the image format, so I'll skip this bit other > than saying that I agree it would be great to have a common format. We are evaluating now moving to ELF; let's see how it goes. Using a standard format is an advantage (as long as it's not overly complicated), but I wonder if achieving a common format is really feasible and whether eventually different platforms will need different binary formats anyway, and we'll have to abstract this out of remoteproc (guess that as usual, we just need to start off with something, and then evolve as requirements show up). >> +Most likely this kind of static allocations of hardware resources for >> +remote processors can also use DT, so it's interesting to see how >> +this all work out when DT materializes. > > I imagine that it will be quite straight forward. ?There will probably > be a node in the tree to represent each slave AMP processor, and other > devices attached to it could be represented using 'phandle' links > between the nodes. ?Any configuration of the AMP process can be > handled with arbitrary device-specific properties in the AMP > processor's node. That sounds good. The dilemma is bigger, though. The kind of stuff we need to synchronize about are not really describing the hardware; it's more a runtime policy/configuration than a hardware description. As Brian mentioned in the other thread: > The resource information is a description of > what resources the firmware requires to work properly (it needs > certain amounts of working memory, timers, peripheral interfaces like > i2c to control camera hw, etc), which will be specific to a given > firmware build. Some of those resources will be allocated dynamically using an rpmsg driver (developed by Fernando Guzman Lugo), but some must be supplied before booting the firmware (memory ?). We're also using the existing resource table today to announce the boot address and the trace buffer address. So the question is whether/if DT can help here. On one hand, we're not describing the hardware here. it's pure configuration, but that seem fine, as DT seem to be taking runtime configuration, too (e.g. bootargs, initrd addresses, etc..). Moreover, some of those remoteproc configurations should handed early to the host, too (e.g. we might need to reserve specific physical memory that must be used by the remote processor, and this can't wait until the firmware is loaded). OTOH, as Brian mentioned, it does make sense to couple those configurations with the specific firmware image, so risk of breaking stuff when the firmware is changed is minimized. Maybe we can have a secondary .dts file as part of the firmware sources, and have it included in the primary .dts (and let the remoteproc access that respective secondary .dtb) ? These are just raw ideas - I never tried working with DT yet myself. >> +source "drivers/remoteproc/Kconfig" >> + > > Hmmm, I wonder if the end of the drivers list is the best place for > this. ?The drivers menu in kconfig is getting quite unwieldy. We can arbitrarily choose a better location in that file but I'm not sure I can objectively justify it :) (alternatively, we can source that Kconfig from the relevant platform's Kconfig, like virtio does). >> + ? ? /* >> + ? ? ?* find the end of trace buffer (does not account for wrapping). >> + ? ? ?* desirable improvement: use a ring buffer instead. >> + ? ? ?*/ >> + ? ? for (i = 0; i < size && buf[i]; i++); > > Hmmm, I wonder if this could make use of the ftrace ring buffer. I thought about it, but I'm not sure we want to. To do that, we'd need the remote processor to send us a message (via rpmsg probably...) for every trace log we want to write into that ring buffer. That would mean significant overhead for every remote trace message, but would also mean you can't debug low level issues with rpmsg, because you need it to deliver the debug messages themselves. Instead, we just use a 'dumb' non-cacheable memory region into which the remote processor unilaterally writes its trace messages. If/when we're interested in the last remote log messages, we just read that shared buffer (e.g. cat /debug/remoteproc/omap-rproc.1/trace0). This means zero overhead on the host, and the ability to debug very low level remote issues: all you need is a shared memory buffer and remote traces work. Currently this shared buffer is really dumb: we just dump its entire content when asked. One nice improvement we can do is handling the inevitable wrapping, by maintaining a shared "head" offset into the buffer. >> + ? ? switch (state) { .. >> + ? ? } > > Me thinks this is asking for a lookup table. sounds good. >> +static ssize_t rproc_state_read(struct file *filp, char __user *userbuf, >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? size_t count, loff_t *ppos) >> +{ >> + ? ? struct rproc *rproc = filp->private_data; >> + ? ? int state = rproc->state; >> + ? ? char buf[100]; > > 100 bytes? ?I count at most ~30. 30 it is. >> +#define DEBUGFS_ADD(name) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\ >> + ? ? debugfs_create_file(#name, 0400, rproc->dbg_dir, ? ? ? ? ? ? ? ?\ >> + ? ? ? ? ? ? ? ? ? ? rproc, &name## _rproc_ops) > > You might want to split the debug stuff off into a separate patch, > just to keep the review load down. ?(up to you though). Sure. I thought maybe to even split it to a separate file as well. >> + ? ? spin_unlock(&rprocs_lock); > > Unless you're going to be looking up the device at irq time, a mutex > is probably a better choice here. mutex it is. We can also completely remove the lock and just use RCU, as the list is rarely changed. Since it's so short today, and rarely accessed at all (even read access is pretty rare), it probably won't matter too much. >> + ? ? dev_info(dev, "remote processor %s is now up\n", rproc->name); > > How often are remote processors likely to be brought up/down? Very rarely. Today we bring it up on boot, and keep it loaded (it will then be suspended on inactivity and won't consume power when we don't need it to do anything). > However, it may be non-zero here, but drop to zero by the time you > take the lock. ?Best be safe and put it inside the mutex. ?Having it > under the mutex shouldn't be a performance hit since only buggy code > will get this test wrong. ?In fact, it is probably appropriate to > WARN_ON() on the !rproc->count condition. good points, thanks. > Actually, using a hand coded reference count like this shouldn't be > done. yeah, i planned to switch to an atomic variable here. > Looking at the code, I > suspect you'll want separate reference counting for object references > and power up/down count so that clients can control power to a device > without giving up the pointer to the rproc instance. Eventually the plan is to use runtime PM for the second refcount, so we get all this plumbing for free. >> + ? ? ? ? ? ? /* iounmap normal memory, so make sparse happy */ >> + ? ? ? ? ? ? iounmap((__force void __iomem *) rproc->trace_buf1); > > Icky casting! ?That suggests that how the trace buffer pointer is > managed needs work. The plan is to replace those ioremaps with dma coherent memory, and then we don't need no casting. We just need the generic dma API (which is in the works) to handle omap's iommu transparently (in the works too), and then tell the remoteproc where to write logs to. It might take some time, but it sounds very clean. >> +#define RPROC_MAX_NAME ? ? ? 100 > > I wouldn't even bother with this. ?The only place it is used is in one > of the debugfs files, and you can protect against too large a static > buffer by using %100s (or whatever) in the snprintf(). cool, thanks! Again, many thanks for the review, Ohad. From ohad at wizery.com Tue Jun 28 16:55:04 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Wed, 29 Jun 2011 00:55:04 +0300 Subject: [RFC 1/8] drivers: add generic remoteproc framework In-Reply-To: <20110627232903.GB7736@n2100.arm.linux.org.uk> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-2-git-send-email-ohad@wizery.com> <20110627204958.GB20865@ponder.secretlab.ca> <20110627232903.GB7736@n2100.arm.linux.org.uk> Message-ID: On Tue, Jun 28, 2011 at 2:29 AM, Russell King - ARM Linux wrote: > (Don't have the original message to reply to...) Sorry about that. My recent emails to linux-arm-kernel were bounced with a "Message has a suspicious header" reason. not sure what am I doing wrong.. > Do we really want to end up with header being 5 bytes, header_len set > as 5, and having to load/store all this data using byte loads/stores ? As Grant said, we're trying to move to ELF now, but if we do end up sticking with this custom format eventually, we'll sure do take care of all the alignment issues. Thanks! From ohad at wizery.com Tue Jun 28 17:46:44 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Wed, 29 Jun 2011 01:46:44 +0300 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: <20110627222121.GD20865@ponder.secretlab.ca> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-8-git-send-email-ohad@wizery.com> <20110627222121.GD20865@ponder.secretlab.ca> Message-ID: Hi Grant, On Tue, Jun 28, 2011 at 1:21 AM, Grant Likely wrote: > Nice patch. ?I'm quite thrilled to see this implemented. ?Some > comments below, but otherwise I think it looks pretty good. Thanks ! >> +source "drivers/virtio/Kconfig" > > What happens when kvm and rpmsg both get enabled on the same kernel. Sounds to me that eventually we'll have to source virtio's Kconfig in drivers/Kconfig, and remove all the other locations it is sourced at (currently it is directly sourced by several arch Kconfigs when VIRTUALIZATION is selected). >> + ? ? if (strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE)) >> + ? ? ? ? ? ? return 0; >> + >> + ? ? return 1; >> +} > > or simply: 'return strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE) == 0;' > > :-) that works too ;) >> + ? ? spin_lock(&vrp->endpoints_lock); > > Is a spin_lock the right choice for endpoints, or should it be a > mutex (do the endpoints operations need to be atomic)? Today it can be a mutex: it protects idr operations invoked either by users (creating new endpoints, definitely not requiring atomicity) or by platform code indicating that a new message has arrived (today it's omap's mailbox driver, which calls from a process context). I can change that, and if someone requires atomic operations later on, move to spinlocks again. But it probably won't matter too much as there's no contention on that lock today (notifications coming from omap's mailbox are serialized, and users creating new endpoints show up very infrequently). > At put_device time, it is conceivable that the dev pointer is no > longer valid. ?You'll need to get do the to_rpmsg_channel() before > putting the dev. sure. >> +static void rpmsg_upref_sleepers(struct virtproc_info *vrp) >> +{ >> + ? ? /* support multiple concurrent senders */ >> + ? ? spin_lock(&vrp->tx_lock); >> + >> + ? ? /* are we the first sleeping context waiting for tx buffers ? */ >> + ? ? if (!vrp->sleepers++) > > Maybe use a kref? I can change it to an atomic variable, but I don't think kref's memory barriers are needed here: we already have memory barriers induced by the spinlock. >> +static int rpmsg_remove_device(struct device *dev, void *data) >> +{ >> + ? ? struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); >> + >> + ? ? device_unregister(dev); >> + >> + ? ? kfree(rpdev); > > put_device() I think. Don't think so, we get the device handle from device_for_each_child here, which doesn't call get_device (unlike device_find_child). >> +static int __init init(void) > > Even for static functions, it's a really good idea to prefix all > function names and file scoped symbol with a common prefix like > "rpmsg_". ?Doing so avoids even the outside chance of a namespace > conflict. Sure thing. >> + ? ? ret = bus_register(&rpmsg_bus); >> + ? ? if (ret) { >> + ? ? ? ? ? ? pr_err("failed to register rpmsg bus: %d\n", ret); >> + ? ? ? ? ? ? return ret; >> + ? ? } >> + >> + ? ? return register_virtio_driver(&virtio_ipc_driver); > > And if register_virtio_driver fails? I'll handle that, thanks. >> +struct rpmsg_device_id { >> + ? ? char name[RPMSG_NAME_SIZE]; >> + ? ? kernel_ulong_t driver_data ? ? ?/* Data private to the driver */ >> + ? ? ? ? ? ? ? ? ? ? __attribute__((aligned(sizeof(kernel_ulong_t)))); >> +}; > > Should this be co-located with vio_device_id? Sure, can't hurt (I assume you meant virtio_device_id here). > It makes it easier to dereference in kernel code if you do: > > #ifdef __KERNEL__ > ? ? ? ?void *data; > #else > ? ? ? ?kernel_ulong_t data; > #endif Sure thing. Thanks! Ohad. From grant.likely at secretlab.ca Tue Jun 28 17:51:01 2011 From: grant.likely at secretlab.ca (Grant Likely) Date: Tue, 28 Jun 2011 16:51:01 -0600 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-8-git-send-email-ohad@wizery.com> <20110627222121.GD20865@ponder.secretlab.ca> Message-ID: On Tue, Jun 28, 2011 at 4:46 PM, Ohad Ben-Cohen wrote: > Hi Grant, > > On Tue, Jun 28, 2011 at 1:21 AM, Grant Likely wrote: >>> +static int rpmsg_remove_device(struct device *dev, void *data) >>> +{ >>> + ? ? struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); >>> + >>> + ? ? device_unregister(dev); >>> + >>> + ? ? kfree(rpdev); >> >> put_device() I think. > > Don't think so, we get the device handle from device_for_each_child > here, which doesn't call get_device (unlike device_find_child). It's not the device_for_each_child() that you're 'putting' back from here. Its the original kref initialization when the device was created. Once a device is initialized, it must never be directly kfree()'d. g. From ohad at wizery.com Tue Jun 28 18:00:31 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Wed, 29 Jun 2011 02:00:31 +0300 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-8-git-send-email-ohad@wizery.com> <20110627222121.GD20865@ponder.secretlab.ca> Message-ID: On Wed, Jun 29, 2011 at 1:51 AM, Grant Likely wrote: > It's not the device_for_each_child() that you're 'putting' back from > here. ?Its the original kref initialization when the device was > created. device_unregister() is already calling put_device(), doesn't that deal with the original kref init for us ? From rdunlap at xenotime.net Tue Jun 28 18:44:53 2011 From: rdunlap at xenotime.net (Randy Dunlap) Date: Tue, 28 Jun 2011 16:44:53 -0700 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: <1308640714-17961-8-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-8-git-send-email-ohad@wizery.com> Message-ID: <20110628164453.aa85c85c.rdunlap@xenotime.net> On Tue, 21 Jun 2011 10:18:33 +0300 Ohad Ben-Cohen wrote: > Add a virtio-based IPC bus, which enables kernel users to communicate > with remote processors over shared memory using a simple messaging > protocol. > > Assign a local address for every local endpoint that is created, > and bind it to the user's callback. Invoke that callback when the > destination of an inbound message is the user's address. > > Signed-off-by: Ohad Ben-Cohen > --- > Documentation/ABI/testing/sysfs-bus-rpmsg | 75 +++ > Documentation/rpmsg.txt | 308 +++++++++ > drivers/Kconfig | 1 + > drivers/Makefile | 1 + > drivers/rpmsg/Kconfig | 14 + > drivers/rpmsg/Makefile | 1 + > drivers/rpmsg/virtio_rpmsg_bus.c | 1036 +++++++++++++++++++++++++++++ > include/linux/mod_devicetable.h | 10 + > include/linux/rpmsg.h | 421 ++++++++++++ > include/linux/virtio_ids.h | 1 + > 10 files changed, 1868 insertions(+), 0 deletions(-) > create mode 100644 Documentation/ABI/testing/sysfs-bus-rpmsg > create mode 100644 Documentation/rpmsg.txt > create mode 100644 drivers/rpmsg/Kconfig > create mode 100644 drivers/rpmsg/Makefile > create mode 100644 drivers/rpmsg/virtio_rpmsg_bus.c > create mode 100644 include/linux/rpmsg.h > diff --git a/Documentation/rpmsg.txt b/Documentation/rpmsg.txt > new file mode 100644 > index 0000000..0a7c820 > --- /dev/null > +++ b/Documentation/rpmsg.txt > @@ -0,0 +1,308 @@ > +Remote Processor Messaging (rpmsg) Framework > + > +1. Introduction > + > +Modern SoCs typically employ heterogeneous remote processor devices in > +asymmetric multiprocessing (AMP) configurations, which may be running > +different instances of operating system, whether it's Linux or any other > +flavor of real-time OS. > + > +OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP. > +Typically, the dual cortex-A9 is running Linux in a SMP configuration, > +and each of the other three cores (two M3 cores and a DSP) is running > +its own instance of RTOS in an AMP configuration. > + > +Typically AMP remote processors employ dedicated DSP codecs and multimedia > +hardware accelerators, and therefore are often used to offload cpu-intensive prefer: CPU- throughout. > +multimedia tasks from the main application processor. > + > +These remote processors could also be used to control latency-sensitive > +sensors, drive random hardware blocks, or just perform background tasks > +while the main CPU is idling. > + > +Users of those remote processors can either be userland apps (e.g. multimedia > +frameworks talking with remote OMX components) or kernel drivers (controlling > +hardware accessible only by the remote processor, reserving kernel-controlled > +resources on behalf of the remote processor, etc..). > + > +Rpmsg is a virtio-based messaging bus that allows kernel drivers to communicate > +with remote processors available on the system. In turn, drivers could then > +expose appropriate user space interfaces, if needed. > + > +When writing a driver that exposes rpmsg communication to userland, please > +keep in mind that remote processors might have direct access to the > +system's physical memory and/or other sensitive hardware resources (e.g. on > +OMAP4, remote cores (/hardware accelerators) may have direct access to the > +physical memory, gpio banks, dma controllers, i2c bus, gptimers, mailbox > +devices, hwspinlocks, etc..). Moreover, those remote processors might be > +running RTOS where every task can access the entire memory/devices exposed > +to the processor. To minimize the risks of rogue (/buggy) userland code What is with the leading / here and above (/hardware) and below? Looks like they can all be dropped. > +exploiting (/triggering) remote bugs, and by that taking over (/down) the > +system, it is often desired to limit userland to specific rpmsg channels (see > +definition below) it is allowed to send messages on, and if possible/relevant, > +minimize the amount of control it has over the content of the messages. > + > +Every rpmsg device is a communication channel with a remote processor (thus > +rpmsg devices are called channels). Channels are identified by a textual name > +and have a local ("source") rpmsg address, and remote ("destination") rpmsg > +address. > + > +When a driver starts listening on a channel, it binds it with a unique > +rpmsg src address (a 32 bits integer). This way when inbound messages arrive (a 32-bit integer). > +to this src address, the rpmsg core dispatches them to that driver (by invoking > +the driver's rx handler with the payload of the incoming message). > + > + > +2. User API > + > + int rpmsg_send(struct rpmsg_channel *rpdev, void *data, int len); > + - sends a message across to the remote processor on a given channel. > + The caller should specify the channel, the data it wants to send, > + and its length (in bytes). The message will be sent on the specified > + channel, i.e. its source and destination address fields will be > + set to the channel's src and dst addresses. > + > + In case there are no TX buffers available, the function will block until > + one becomes available (i.e. until the remote processor will consume prefer: until the remote processor consumes and puts it back on > + a tx buffer and put it back on virtio's used descriptor ring), > + or a timeout of 15 seconds elapses. When the latter happens, > + -ERESTARTSYS is returned. > + The function can only be called from a process context (for now). > + Returns 0 on success and an appropriate error value on failure. > + > + int rpmsg_sendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst); > + - sends a message across to the remote processor on a given channel, > + to a destination address provided by the caller. > + The caller should specify the channel, the data it wants to send, > + its length (in bytes), and an explicit destination address. > + The message will then be sent to the remote processor to which the > + channel belongs to, using the channel's src address, and the user-provided channel belongs, > + dst address (thus the channel's dst address will be ignored). > + > + In case there are no TX buffers available, the function will block until > + one becomes available (i.e. until the remote processor will consume until the remote processor consumes a tx buffer and puts it back on > + a tx buffer and put it back on virtio's used descriptor ring), > + or a timeout of 15 seconds elapses. When the latter happens, > + -ERESTARTSYS is returned. > + The function can only be called from a process context (for now). > + Returns 0 on success and an appropriate error value on failure. > + > + int rpmsg_send_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst, > + void *data, int len); > + - sends a message across to the remote processor, using the src and dst > + addresses provided by the user. > + The caller should specify the channel, the data it wants to send, > + its length (in bytes), and explicit source and destination addresses. > + The message will then be sent to the remote processor to which the > + channel belongs to, but the channel's src and dst addresses will be channel belongs, > + ignored (and the user-provided addresses will be used instead). > + > + In case there are no TX buffers available, the function will block until > + one becomes available (i.e. until the remote processor will consume until the remote processor consumes a tx buffer and puts it back on > + a tx buffer and put it back on virtio's used descriptor ring), > + or a timeout of 15 seconds elapses. When the latter happens, > + -ERESTARTSYS is returned. > + The function can only be called from a process context (for now). > + Returns 0 on success and an appropriate error value on failure. > + > + int rpmsg_trysend(struct rpmsg_channel *rpdev, void *data, int len); > + - sends a message across to the remote processor on a given channel. > + The caller should specify the channel, the data it wants to send, > + and its length (in bytes). The message will be sent on the specified > + channel, i.e. its source and destination address fields will be > + set to the channel's src and dst addresses. > + > + In case there are no TX buffers available, the function will immediately > + return -ENOMEM without waiting until one becomes available. > + The function can only be called from a process context (for now). > + Returns 0 on success and an appropriate error value on failure. > + > + int rpmsg_trysendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst) > + - sends a message across to the remote processor on a given channel, > + to a destination address provided by the user. > + The user should specify the channel, the data it wants to send, > + its length (in bytes), and an explicit destination address. > + The message will then be sent to the remote processor to which the > + channel belongs to, using the channel's src address, and the user-provided channel belongs, > + dst address (thus the channel's dst address will be ignored). > + > + In case there are no TX buffers available, the function will immediately > + return -ENOMEM without waiting until one becomes available. > + The function can only be called from a process context (for now). > + Returns 0 on success and an appropriate error value on failure. > + > + int rpmsg_trysend_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst, > + void *data, int len); > + - sends a message across to the remote processor, using source and > + destination addresses provided by the user. > + The user should specify the channel, the data it wants to send, > + its length (in bytes), and explicit source and destination addresses. > + The message will then be sent to the remote processor to which the > + channel belongs to, but the channel's src and dst addresses will be channel belongs, > + ignored (and the user-provided addresses will be used instead). > + > + In case there are no TX buffers available, the function will immediately > + return -ENOMEM without waiting until one becomes available. > + The function can only be called from a process context (for now). > + Returns 0 on success and an appropriate error value on failure. > + > + struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev, > + void (*cb)(struct rpmsg_channel *, void *, int, void *, u32), > + void *priv, u32 addr); > + - every rpmsg address in the system is bound to an rx callback (so when > + inbound messages arrive, they are dispatched by the rpmsg bus using the > + appropriate callback handler) by means of an rpmsg_endpoint struct. > + > + This function allows drivers to create such an endpoint, and by that, > + bind a callback, and possibly some private data too, to an rpmsg address > + (either one that is known in advance, or one that will be dynamically > + assigned for them). > + > + Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint > + is already created for them when they are probed by the rpmsg bus > + (using the rx callback they provide when they registered to the rpmsg bus). > + > + So things should just work for simple drivers: they already have an > + endpoint, their rx callback is bound to their rpmsg address, and when > + relevant inbound messages arrive (i.e. messages which their dst address > + equals to the src address of their rpmsg channel), the driver's handler > + is invoked to process it. > + > + That said, more complicated drivers might do need to allocate > + additional rpmsg addresses, and bind them to different rx callbacks. > + To accomplish that, those drivers need to call this function. > + Driver should provide their channel (so the new endpoint would bind Drivers > + to the same remote processor their channel belongs to), an rx callback > + function, an optional private data (which is provided back when the > + rx callback is invoked), and an address they want to bind with the > + callback. If addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will > + dynamically assign them an available rpmsg address (drivers should have > + a very good reason why not to always use RPMSG_ADDR_ANY here). > + > + Returns a pointer to the endpoint on success, or NULL on error. > + > + void rpmsg_destroy_ept(struct rpmsg_endpoint *ept); > + - destroys an existing rpmsg endpoint. user should provide a pointer > + to an rpmsg endpoint that was previously created with rpmsg_create_ept(). > + > + int register_rpmsg_driver(struct rpmsg_driver *rpdrv); > + - registers an rpmsg driver with the rpmsg bus. user should provide > + a pointer to an rpmsg_driver struct, which contains the driver's > + ->probe() and ->remove() functions, an rx callback, and an id_table > + specifying the names of the channels this driver is interested to > + be probed with. > + > + void unregister_rpmsg_driver(struct rpmsg_driver *rpdrv); > + - unregisters an rpmsg driver from the rpmsg bus. user should provide > + a pointer to a previously-registerd rpmsg_driver struct. registered > + Returns 0 on success, and an appropriate error value on failure. > + > + > +3. Typical usage > + > +The following is a simple rpmsg driver, that sends an "hello!" message > +on probe(), and whenever it receives an incoming message, it dumps its > +content to the console. > + > +#include > +#include > +#include > + > +static void rpmsg_sample_cb(struct rpmsg_channel *rpdev, void *data, int len, > + void *priv, u32 src) > +{ > + print_hex_dump(KERN_INFO, "incoming message:", DUMP_PREFIX_NONE, > + 16, 1, data, len, true); > +} > + > +static int rpmsg_sample_probe(struct rpmsg_channel *rpdev) > +{ > + int err; > + > + dev_info(&rpdev->dev, "chnl: 0x%x -> 0x%x\n", rpdev->src, rpdev->dst); > + > + /* send a message on our channel */ > + err = rpmsg_send(rpdev, "hello!", 6); > + if (err) { > + pr_err("rpmsg_send failed: %d\n", err); > + return err; > + } > + > + return 0; > +} > + > +static void __devexit rpmsg_sample_remove(struct rpmsg_channel *rpdev) > +{ > + dev_info(&rpdev->dev, "rpmsg sample client driver is removed\n"); > +} > + > +static struct rpmsg_device_id rpmsg_driver_sample_id_table[] = { > + { .name = "rpmsg-client-sample" }, > + { }, > +}; > +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sample_id_table); > + > +static struct rpmsg_driver rpmsg_sample_client = { > + .drv.name = KBUILD_MODNAME, > + .drv.owner = THIS_MODULE, > + .id_table = rpmsg_driver_sample_id_table, > + .probe = rpmsg_sample_probe, > + .callback = rpmsg_sample_cb, > + .remove = __devexit_p(rpmsg_sample_remove), > +}; > + > +static int __init init(void) > +{ > + return register_rpmsg_driver(&rpmsg_sample_client); > +} > + > +static void __exit fini(void) > +{ > + unregister_rpmsg_driver(&rpmsg_sample_client); > +} > +module_init(init); > +module_exit(fini); > + > + > +4. API for implementors > + > +Adding rpmsg support for a new platform is relatively easy; one just needs > +to register a VIRTIO_ID_RPMSG virtio device with the usual virtio_config_ops > +handlers. > + > +For simplicity, it is recommended to register a single virtio device for > +every physical remote processor we have in the system, but there are no remote processor in the system, > +hard rules here, and this decision largely depends on the use cases, > +platform capabilities, performance requirements, etc. > + > +OMAP4, e.g., registers two virtio devices to communicate with the remote dual > +Cortex-M3 processor, because each of the M3 cores executes its own OS instance > +(see omap_rpmsg.c for reference). > +This way each of the remote cores may have different rpmsg channels, and the > +rpmsg core will treat them as completely independent processors (despite > +the fact that both of are part of the same physical device, and they are both of them are part > +powered on/off together). > + > +Notable virtio implementation bits: > + > +* virtio features: VIRTIO_RPMSG_F_NS should be enabled if the remote > + processor supports dynamic name service announcement messages. > + > + Enabling this means that rpmsg device (i.e. channel) creation is > + completely dynamic: the remote processor announces the existence of a > + remote rpmsg service by sending a name service message (which contains > + the name and rpmsg addr of the remote service, see struct rpmsg_ns_msg). > + > + This message is then handled by the rpmsg bus, which in turn dynamically > + creates and registers an rpmsg channel (which represents the remote service). > + If/when a relevant rpmsg driver is registered, it will be immediately probed > + by the bus, and can then start sending messages to the remote service. > + > +* virtqueue's notify handler: should inform the remote processor whenever > + it is kicked by virtio. OMAP4 is using its mailbox device to interrupt > + the remote processor, and inform it which virtqueue number is kicked > + (the index of the kicked virtqueue is written to the mailbox register). > + > +* virtio_config_ops's ->get() handler: the rpmsg bus uses this handler > + to request for platform-specific configuration values. > + see enum rpmsg_platform_requests for more info. > diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig > new file mode 100644 > index 0000000..41303f5 > --- /dev/null > +++ b/drivers/rpmsg/Kconfig > @@ -0,0 +1,14 @@ > +# RPMSG always gets selected by whoever wants it > +config RPMSG > + tristate > + select VIRTIO > + select VIRTIO_RING > + > +if RPMSG > + > +# OK, it's a little counter-intuitive to do this, but it puts it neatly under > +# the rpmsg menu (and it's the approach preferred by the virtio folks). > + > +source "drivers/virtio/Kconfig" It seems odd to have that Kconfig file sourced in multiple places. Are the kconfig tools happy with that? > + > +endif # RPMSG Sorry about the delay. I had most of this in my drafts folder and forgot about it... HTH. --- ~Randy *** Remember to use Documentation/SubmitChecklist when testing your code *** From member at linkedin.com Tue Jun 28 21:15:42 2011 From: member at linkedin.com (micro tiger via LinkedIn) Date: Wed, 29 Jun 2011 02:15:42 +0000 (UTC) Subject: Invitation to connect on LinkedIn Message-ID: <790987954.1016789.1309313742529.JavaMail.app@ela4-bed78.prod> LinkedIn ------------ micro tiger requested to add you as a connection on LinkedIn: ------------------------------------------ Markus, I'd like to add you to my professional network on LinkedIn. - micro Accept invitation from micro tiger http://www.linkedin.com/e/-6res9e-gphnlhh8-4c/vvFWCrP4iVDLPCMMvsUEhA0xEJ-zU4c1qDFPC6sbh2c67ksWv8wEE7JxEJ5LTxxFpNc/blk/I127751271_15/1BpC5vrmRLoRZcjkkZt5YCpnlOt3RApnhMpmdzgmhxrSNBszYRclYNdP8NdjsTcz59bTpTjCFRlSlhbP8PcP8VdzgTcPcLrCBxbOYWrSlI/EML_comm_afe/ View invitation from micro tiger http://www.linkedin.com/e/-6res9e-gphnlhh8-4c/vvFWCrP4iVDLPCMMvsUEhA0xEJ-zU4c1qDFPC6sbh2c67ksWv8wEE7JxEJ5LTxxFpNc/blk/I127751271_15/3kNnP4Tcz4RdPsOckALqnpPbOYWrSlI/svi/ ------------------------------------------ DID YOU KNOW you can use your LinkedIn profile as your website? Select a vanity URL and then promote this address on your business cards, email signatures, website, etc http://www.linkedin.com/e/-6res9e-gphnlhh8-4c/ewp/inv-21/ -- (c) 2011, LinkedIn Corporation -------------- next part -------------- An HTML attachment was scrubbed... URL: From ohad at wizery.com Wed Jun 29 01:30:38 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Wed, 29 Jun 2011 09:30:38 +0300 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: <20110628164453.aa85c85c.rdunlap@xenotime.net> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-8-git-send-email-ohad@wizery.com> <20110628164453.aa85c85c.rdunlap@xenotime.net> Message-ID: Hi Randy, Thanks for your comments ! On Wed, Jun 29, 2011 at 2:44 AM, Randy Dunlap wrote: >> +hardware accelerators, and therefore are often used to offload cpu-intensive > > prefer: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CPU- > throughout. Isn't that changing the meaning a bit ? Let's stick with the original version, I think it's more clear. >> +system's physical memory and/or other sensitive hardware resources (e.g. on >> +OMAP4, remote cores (/hardware accelerators) may have direct access to the >> +physical memory, gpio banks, dma controllers, i2c bus, gptimers, mailbox >> +devices, hwspinlocks, etc..). Moreover, those remote processors might be >> +running RTOS where every task can access the entire memory/devices exposed >> +to the processor. To minimize the risks of rogue (/buggy) userland code > > What is with the leading / here and above (/hardware) and below? / here means "or". You can read the sentence twice, either without the (/ ..) options or with them, and then you get two (different) examples. Any idea how to make it more readable ? I prefer not to drop the second example, as it's adding information. >> +if RPMSG >> + >> +# OK, it's a little counter-intuitive to do this, but it puts it neatly under >> +# the rpmsg menu (and it's the approach preferred by the virtio folks). >> + >> +source "drivers/virtio/Kconfig" > > It seems odd to have that Kconfig file sourced in multiple places. > Are the kconfig tools happy with that? They are, probably because these places are mutually exclusive today: $ git grep "drivers/virtio/Kconfig" arch/ia64/kvm/Kconfig:source drivers/virtio/Kconfig arch/mips/Kconfig:source drivers/virtio/Kconfig arch/powerpc/kvm/Kconfig:source drivers/virtio/Kconfig arch/s390/kvm/Kconfig:source drivers/virtio/Kconfig arch/sh/Kconfig:source drivers/virtio/Kconfig arch/tile/kvm/Kconfig:source drivers/virtio/Kconfig arch/x86/kvm/Kconfig:source drivers/virtio/Kconfig Now that we start using virtio for inter-processor communications, too, we might soon bump into a situation where virtio will be sourced twice. Probably the solution is to move 'source "drivers/virtio/Kconfig"' into drivers/Kconfig, and remove all other instances. Rusty, are you ok with that ? Thanks, Ohad. > Sorry about the delay. ?I had most of this in my drafts folder and > forgot about it... Np, thanks a lot ! Ohad. From arnd at arndb.de Wed Jun 29 06:59:13 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Wed, 29 Jun 2011 13:59:13 +0200 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <20110628164453.aa85c85c.rdunlap@xenotime.net> Message-ID: <201106291359.14026.arnd@arndb.de> On Wednesday 29 June 2011, Ohad Ben-Cohen wrote: > On Wed, Jun 29, 2011 at 2:44 AM, Randy Dunlap wrote: > >> +hardware accelerators, and therefore are often used to offload cpu-intensive > > > > prefer: CPU- > > throughout. > > Isn't that changing the meaning a bit ? Let's stick with the original > version, I think it's more clear. I think you misunderstood Randy, he meant you should do 's/cpu/CPU/' globally, which does not change the meaning. > >> +system's physical memory and/or other sensitive hardware resources (e.g. on > >> +OMAP4, remote cores (/hardware accelerators) may have direct access to the > >> +physical memory, gpio banks, dma controllers, i2c bus, gptimers, mailbox > >> +devices, hwspinlocks, etc..). Moreover, those remote processors might be > >> +running RTOS where every task can access the entire memory/devices exposed > >> +to the processor. To minimize the risks of rogue (/buggy) userland code > > > > What is with the leading / here and above (/hardware) and below? > > / here means "or". You can read the sentence twice, either without the > (/ ..) options or with them, and then you get two (different) > examples. > > Any idea how to make it more readable ? I prefer not to drop the > second example, as it's adding information. The easiest way would be to replace it with 'or', as in ... remote cores (or hardware accelerators) may have ... I would also suggest you drop the parentheses, especially in the first case where you have two levels of them: system's physical memory and/or other sensitive hardware resources, e.g. on OMAP4, remote cores and hardware accelerators may have direct access to the specific hardware blocks such as physical memory, gpio banks or dma controllers. Moreover, those remote processors might be... > >> +if RPMSG > >> + > >> +# OK, it's a little counter-intuitive to do this, but it puts it neatly under > >> +# the rpmsg menu (and it's the approach preferred by the virtio folks). > >> + > >> +source "drivers/virtio/Kconfig" > > > > It seems odd to have that Kconfig file sourced in multiple places. > > Are the kconfig tools happy with that? > > They are, probably because these places are mutually exclusive today: > > $ git grep "drivers/virtio/Kconfig" > arch/ia64/kvm/Kconfig:source drivers/virtio/Kconfig > arch/mips/Kconfig:source drivers/virtio/Kconfig > arch/powerpc/kvm/Kconfig:source drivers/virtio/Kconfig > arch/s390/kvm/Kconfig:source drivers/virtio/Kconfig > arch/sh/Kconfig:source drivers/virtio/Kconfig > arch/tile/kvm/Kconfig:source drivers/virtio/Kconfig > arch/x86/kvm/Kconfig:source drivers/virtio/Kconfig > > Now that we start using virtio for inter-processor communications, > too, we might soon bump into a situation where virtio will be sourced > twice. > > Probably the solution is to move 'source "drivers/virtio/Kconfig"' > into drivers/Kconfig, and remove all other instances. I think changing that would be good. However, you need to at least restructure the contents, or they will show up in the main driver menu. Arnd From ohad at wizery.com Wed Jun 29 07:29:23 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Wed, 29 Jun 2011 15:29:23 +0300 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: <201106291359.14026.arnd@arndb.de> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <20110628164453.aa85c85c.rdunlap@xenotime.net> <201106291359.14026.arnd@arndb.de> Message-ID: On Wed, Jun 29, 2011 at 2:59 PM, Arnd Bergmann wrote: > On Wednesday 29 June 2011, Ohad Ben-Cohen wrote: >> On Wed, Jun 29, 2011 at 2:44 AM, Randy Dunlap wrote: >> >> +hardware accelerators, and therefore are often used to offload cpu-intensive >> > >> > prefer: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CPU- >> > throughout. >> >> Isn't that changing the meaning a bit ? Let's stick with the original >> version, I think it's more clear. > > I think you misunderstood Randy, he meant you should do 's/cpu/CPU/' globally, Oh, sorry, Randy. For some reason I thought you meant s/cpu-intensive/CPU-throughout/ which didn't make a lot of sense to me :) s/cpu/CPU/ is of course nicer. thanks ! > The easiest way would be to replace it with 'or', as in > > ... remote cores (or hardware accelerators) may have ... yeah, i'll do it, thanks. It's a bit harder to get rid of the parentheses in the second sentence, but I'll think of something too. >> Probably the solution is to move 'source "drivers/virtio/Kconfig"' >> into drivers/Kconfig, and remove all other instances. > > I think changing that would be good. However, you need to at least > restructure the contents, or they will show up in the main driver menu. I'll do that. Thanks, Ohad. From nsekhar at ti.com Wed Jun 29 08:12:22 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 29 Jun 2011 18:42:22 +0530 Subject: [RFC PATCH v4] Consolidate SRAM support In-Reply-To: <1306371777-20431-1-git-send-email-plagnioj@jcrosoft.com> References: <20110512174546.GB8633@n2100.arm.linux.org.uk> <1306371777-20431-1-git-send-email-plagnioj@jcrosoft.com> Message-ID: Hi Jean-Christophe, On Thu, May 26, 2011 at 06:32:57, Jean-Christophe PLAGNIOL-VILLARD wrote: > From: Russell King - ARM Linux > > We have two SoCs using SRAM, both with their own allocation systems, > and both with their own ways of copying functions into the SRAM. > > Let's unify this before we have additional SoCs re-implementing this > obviously common functionality themselves. > > For this use the generic allocator and the newly introduce > gen_pool_add_virt and gen_pool_virt_to_phys > > Uio_pruss should probably take the SRAM pool pointer via > platform data so that it doesn't have to include Davinci specific > includes. > > Signed-off-by: Russell King > Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD > Cc: Sekhar Nori > Cc: Kevin Hilman > Cc: Tony Lindgren > Cc: Sascha Hauer Is this patch slated to be merged for v3.1? I did not find it in linux-next. There is some further cleanup done by Ben Gardiner for DaVinci which depends on this patch. > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > index f4b7dfa..5ec5e5f 100644 > --- a/arch/arm/Kconfig > +++ b/arch/arm/Kconfig > @@ -848,6 +848,7 @@ config ARCH_DAVINCI > bool "TI DaVinci" > select GENERIC_CLOCKEVENTS > select ARCH_REQUIRE_GPIOLIB > + select GENERIC_ALLOCATOR > select ZONE_DMA > select HAVE_IDE > select CLKDEV_LOOKUP Just noticed that GENERIC_ALLOCATOR is already selected For DaVinci (it is right below the CLKDEV_LOOKUP selection) so this part of the patch can be dropped. Thanks, Sekhar From ohad at wizery.com Wed Jun 29 10:04:37 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Wed, 29 Jun 2011 18:04:37 +0300 Subject: [RFC 2/8] remoteproc: add omap implementation In-Reply-To: <20110627210007.GC20865@ponder.secretlab.ca> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-3-git-send-email-ohad@wizery.com> <20110627210007.GC20865@ponder.secretlab.ca> Message-ID: On Tue, Jun 28, 2011 at 12:00 AM, Grant Likely wrote: > Very little for me to comment on here. ?However, something I just > noticed. ?Why is it necessary to pass in THIS_MODULE to the > rproc_register function? ?Having a reference to the pdev gives you the > pointer to the driver, which has the THIS_MODULE value in it. ?That > should be sufficient. Nice one, thanks ! > /me also isn't sure if incrementing the refcount on the module is the > best way to prevent the rproc from going away, but I haven't dug into > the details in the driver code to find out. ?Drivers can get unbound > from devices without the driver being unloaded, so I imagine there is > an in-use count on the device itself that would prevent driver > unbinding. Yes, increasing the module refcount is necessary to prevent the user from removing the driver when the rproc is used. If the underlying device goes away while rproc is used, then rproc_unregister should return -EBUSY, which would fail the underlying driver's ->remove() handler (gpiolib is doing something very similar). I have forgotten to add this check, and will add it now. Thanks ! From grant.likely at secretlab.ca Wed Jun 29 10:31:19 2011 From: grant.likely at secretlab.ca (Grant Likely) Date: Wed, 29 Jun 2011 09:31:19 -0600 Subject: [RFC 2/8] remoteproc: add omap implementation In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-3-git-send-email-ohad@wizery.com> <20110627210007.GC20865@ponder.secretlab.ca> Message-ID: On Wed, Jun 29, 2011 at 9:04 AM, Ohad Ben-Cohen wrote: > On Tue, Jun 28, 2011 at 12:00 AM, Grant Likely > wrote: >> Very little for me to comment on here. ?However, something I just >> noticed. ?Why is it necessary to pass in THIS_MODULE to the >> rproc_register function? ?Having a reference to the pdev gives you the >> pointer to the driver, which has the THIS_MODULE value in it. ?That >> should be sufficient. > > Nice one, thanks ! > >> /me also isn't sure if incrementing the refcount on the module is the >> best way to prevent the rproc from going away, but I haven't dug into >> the details in the driver code to find out. ?Drivers can get unbound >> from devices without the driver being unloaded, so I imagine there is >> an in-use count on the device itself that would prevent driver >> unbinding. > > Yes, increasing the module refcount is necessary to prevent the user > from removing the driver when the rproc is used. That prevents removing the module which necessitates unbinding the device. However, I believe it is possible to unbind a driver /without/ the module being unloaded. My question (for which I don't have an answer) is whether or not there is a way to increment a refcount on users of the driver bound to the device.. g. From grant.likely at secretlab.ca Wed Jun 29 10:43:10 2011 From: grant.likely at secretlab.ca (Grant Likely) Date: Wed, 29 Jun 2011 09:43:10 -0600 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-8-git-send-email-ohad@wizery.com> <20110627222121.GD20865@ponder.secretlab.ca> Message-ID: On Tue, Jun 28, 2011 at 5:00 PM, Ohad Ben-Cohen wrote: > On Wed, Jun 29, 2011 at 1:51 AM, Grant Likely wrote: >> It's not the device_for_each_child() that you're 'putting' back from >> here. ?Its the original kref initialization when the device was >> created. > > device_unregister() is already calling put_device(), doesn't that deal > with the original kref init for us ? /me digs deeper: device_register() has 2 parts; device_initialize() and device_add() device_init() initialized the kref to 1 (via kobject_init() device_add() calls get_device() to increment it to 2 Then similarly for device_unregister(): device_del() calls put_device() to decrement the kref to 1 a final put_device() call decrements the kref to 0 - which triggers a call to the release method that kfrees the object. So you are right that device_unregister drops the refcount to zero, but the code still needs to be fixed to not call kfree() directly. It also looks like rpmsg_destroy_channel() needs to be fixed to remove the kfree call and an extra put_device() call. This is important because the last put_device() call above might /not/ decrement the refcount to zero is for some reason something still holds a reference to the device. But the device will still get freed correctly when the other holder finally calls device_put(). g. From manjunath.hadli at ti.com Thu Jun 30 08:13:09 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 30 Jun 2011 18:43:09 +0530 Subject: [ RFC PATCH 0/8] RFC for Media Controller capture driver for DM365 Message-ID: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> Thease are the RFC patches for the DM365 video capture, of which the current set includes only CCDC and the VPFE framework. Once the present set is reviewed, I will send out the other parts like H3A, sensor additions etc. Introduction ------------ This is the proposal of the initial version of design and implementation of the Davinci family (dm644x,dm355,dm365)VPFE (Video Port Front End) drivers using Media Controloler , the initial version which supports the following: 1) dm365 vpfe 2) ccdc,previewer,resizer,h3a,af blocks 3) supports both continuous and single-shot modes 4) supports user pointer exchange and memory mapped modes for buffer allocation This driver bases its design on Laurent Pinchart's Media Controller Design whose patches for Media Controller and subdev enhancements form the base. The driver also takes copious elements taken from Laurent Pinchart and others' OMAP ISP driver based on Media Controller. So thank you all the people who are responsible for the Media Controller and the OMAP ISP driver. Also, the core functionality of the driver comes from the arago vpfe capture driver of which the CCDC capture was based on V4L2, with other drivers like Previwer, and Resizer. The current driver caters to dm6446,dm355 and dm365 of which the current implementation works for dm365. The three VPFE IPs have some common elements in terms of some highe level functionality but there are differences in terms of register definitions and some core blocks. The individual specifications for each of these can be found here: dm6446 vpfe: http://www.ti.com/litv/pdf/sprue38h dm355 vpfe: http://www.ti.com/litv/pdf/spruf71a dm365 vpfe: http://www.ti.com/litv/pdf/sprufg8c The initial version of the driver implementation can be found here: http://git.linuxtv.org/mhadli/v4l-dvb-davinci_devices.git?a=shortlog;h=refs/heads/mc_release Driver Design: Main entities ---------------------------- The hardware modules for dm355,dm365 are mainly ipipe, ipipeif,isif. These hardware modules are generically exposed to the user level in the for of dm6446 style modules. Mainly - ccdc, previewer, resizer in addition to the other histogram and auto color/white balance correction and auto focus modules. 1)MT9P031 sensor module for RAW capture 2)TVP7002 decoder module for HD inputs 3)TVP514x decoder module for SD inputs 4)CCDC capture module 5)Previewer Module for Bayer to YUV conversion 6)Resizer Module for scaling Connection for on-the-fly capture --------------------------------- Mt9P031 ------>CCDC--->Previewer(optional)--->Resizer(optional)--->Video | TVP7002 --- | TV514x --- Manjunath Hadli (3): davinci: vpfe: add dm3xx IPIPEIF hardware support module davinci: vpfe: add support for CCDC hardware for dm365 davinci: vpfe: build infrastructure for dm365 Nagabhushana Netagunte (5): davinci: vpfe: add IPIPE hardware layer support davinci: vpfe: add IPIPE support for media controller driver davinci: vpfe: add ccdc driver with media controller interface davinci: vpfe: add v4l2 video driver support davinci: vpfe: v4l2 capture driver with media interface drivers/media/video/davinci/Kconfig | 46 +- drivers/media/video/davinci/Makefile | 17 +- drivers/media/video/davinci/ccdc_hw_device.h | 6 +- drivers/media/video/davinci/dm365_ccdc.c | 1517 +++++++++ drivers/media/video/davinci/dm365_ccdc_regs.h | 309 ++ drivers/media/video/davinci/dm365_def_para.c | 485 +++ drivers/media/video/davinci/dm365_def_para.h | 39 + drivers/media/video/davinci/dm365_ipipe.c | 4086 +++++++++++++++++++++++++ drivers/media/video/davinci/dm365_ipipe_hw.c | 1012 ++++++ drivers/media/video/davinci/dm365_ipipe_hw.h | 539 ++++ drivers/media/video/davinci/dm3xx_ipipeif.c | 368 +++ drivers/media/video/davinci/vpfe_capture.c | 793 +++++ drivers/media/video/davinci/vpfe_ccdc.c | 813 +++++ drivers/media/video/davinci/vpfe_video.c | 1712 +++++++++++ include/media/davinci/dm365_ccdc.h | 722 +++++ include/media/davinci/dm365_ipipe.h | 1353 ++++++++ include/media/davinci/dm3xx_ipipeif.h | 292 ++ include/media/davinci/imp_common.h | 231 ++ include/media/davinci/imp_hw_if.h | 177 ++ include/media/davinci/vpfe_capture.h | 158 + include/media/davinci/vpfe_ccdc.h | 89 + include/media/davinci/vpfe_types.h | 50 +- include/media/davinci/vpfe_video.h | 142 + 23 files changed, 14914 insertions(+), 42 deletions(-) create mode 100644 drivers/media/video/davinci/dm365_ccdc.c 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_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/vpfe_capture.c create mode 100644 drivers/media/video/davinci/vpfe_ccdc.c create mode 100644 drivers/media/video/davinci/vpfe_video.c create mode 100644 include/media/davinci/dm365_ccdc.h create mode 100644 include/media/davinci/dm365_ipipe.h create mode 100644 include/media/davinci/dm3xx_ipipeif.h create mode 100644 include/media/davinci/imp_common.h create mode 100644 include/media/davinci/imp_hw_if.h create mode 100644 include/media/davinci/vpfe_capture.h create mode 100644 include/media/davinci/vpfe_ccdc.h create mode 100644 include/media/davinci/vpfe_video.h From manjunath.hadli at ti.com Thu Jun 30 08:13:17 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 30 Jun 2011 18:43:17 +0530 Subject: [RFC PATCH 8/8] davinci: vpfe: build infrastructure for dm365 In-Reply-To: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1309439597-15998-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 Thu Jun 30 08:13:16 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 30 Jun 2011 18:43:16 +0530 Subject: [RFC PATCH 7/8] davinci: vpfe: v4l2 capture driver with media interface In-Reply-To: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1309439597-15998-8-git-send-email-manjunath.hadli@ti.com> From: Nagabhushana Netagunte 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 | 793 ++++++++++++++++++++++++++++ include/media/davinci/vpfe_capture.h | 158 ++++++ 2 files changed, 951 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpfe_capture.c create mode 100644 include/media/davinci/vpfe_capture.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..6c57c19 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -0,0 +1,793 @@ +/* + * 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 + +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) +{ + /* TODO: revisit for other format support*/ + 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_UYVY; + 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; + default: + printk(KERN_ERR "invalid mbus code\n"); + } + + /* pitch should be 32 bytes aligned */ + pix->bytesperline = ALIGN(pix->bytesperline, 32); + + 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(void) +{ + /* inform user choice on serializer to ipipe */ + enable_serializer(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 (NULL == vpfe_dev->clks) { + v4l2_err(vpfe_dev->pdev->driver, "Memory allocation failed\n"); + return -ENOMEM; + } + + for (i = 0; i < vpfe_cfg->num_clocks; i++) { + if (NULL == vpfe_cfg->clocks[i]) { + 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 (NULL == vpfe_dev->clks[i]) { + 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 (NULL == vpfe_dev->sd) { + 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) { + if (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_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); +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 (NULL == pdev->dev.platform_data) { + 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 (NULL == vpfe_dev->cfg->card_name || + NULL == vpfe_dev->cfg->sub_devs) { + 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; + + vpfe_initialize(); + + /* 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_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/include/media/davinci/vpfe_capture.h b/include/media/davinci/vpfe_capture.h new file mode 100644 index 0000000..6b4d729 --- /dev/null +++ b/include/media/davinci/vpfe_capture.h @@ -0,0 +1,158 @@ +/* +* 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 + +#ifdef __KERNEL__ + +/* Header files */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 CAPTURE_DRV_NAME "vpfe-capture" + +#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_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, +}; + +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[]; +}; + +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; +}; + +/* 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 /* End of __KERNEL__ */ +#endif /* _DAVINCI_VPFE_H */ -- 1.6.2.4 From manjunath.hadli at ti.com Thu Jun 30 08:13:10 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 30 Jun 2011 18:43:10 +0530 Subject: [RFC PATCH 1/8] davinci: vpfe: add dm3xx IPIPEIF hardware support module In-Reply-To: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1309439597-15998-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 | 368 +++++++++++++++++++++++++++ include/media/davinci/dm3xx_ipipeif.h | 292 +++++++++++++++++++++ 2 files changed, 660 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/dm3xx_ipipeif.c create mode 100644 include/media/davinci/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..36cb61b --- /dev/null +++ b/drivers/media/video/davinci/dm3xx_ipipeif.c @@ -0,0 +1,368 @@ +/* +* 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 + +#define DM355 0 +#define DM365 1 + +static void *__iomem ipipeif_base_addr; +static int device_type; + +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) +{ + unsigned int val1; + unsigned int val; + + if (params->source != 0) { + 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 */ + val1 = + ((address >> IPIPEIF_ADDRU_SHIFT) & IPIPEIF_ADDRU_MASK); + regw_if(val1, IPIPEIF_ADDRU); + } else + return -1; + + return 0; +} + +static void ipipeif_config_dpc(struct ipipeif_dpc *dpc) +{ + u32 val; + + if (dpc->en) { + val = ((dpc->en & 1) << IPIPEIF_DPC2_EN_SHIFT); + val |= (dpc->thr & IPIPEIF_DPC2_THR_MASK); + } else + val = 0; + + regw_if(val, IPIPEIF_DPC2); +} + +/* This function sets up IPIPEIF and is called from + * ipipe_hw_setup() + */ +int ipipeif_hw_setup(struct ipipeif *params) +{ + enum v4l2_mbus_pixelcode isif_port_if; + unsigned int val1 = 0x7; + unsigned int val; + + if (NULL == params) + return -1; + + /* 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 != SDRAM_YUV) + val |= params->var.if_5_1.data_shift << DATASFT_SHIFT; + else + val &= (~(val1 << DATASFT_SHIFT)); + } + regw_if(val, IPIPEIF_GFG1); + + switch (params->source) { + case CCDC: + { + regw_if(params->gain, IPIPEIF_GAIN); + break; + } + case SDRAM_RAW: + case CCDC_DARKFM: + { + regw_if(params->gain, IPIPEIF_GAIN); + /* fall through */ + } + case 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 */ + ; + } + + /*check if decimation is enable or not */ + if (params->decimation) + regw_if(params->rsz, IPIPEIF_RSZ); + + if (device_type == DM365) { + /* 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 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 */ + switch (isif_port_if) { + case V4L2_MBUS_FMT_YUYV8_1X16: + val |= + (0 << + IPIPEIF_CFG2_YUV8_SHIFT); + val |= + (1 << + IPIPEIF_CFG2_YUV16_SHIFT); + regw_if(val, IPIPEIF_CFG2); + break; + default: + val |= + (0 << + IPIPEIF_CFG2_YUV8_SHIFT); + val |= + (0 << + IPIPEIF_CFG2_YUV16_SHIFT); + regw_if(val, IPIPEIF_CFG2); + break; + } + } + case SDRAM_YUV: + { + /* Set clock divider */ + if (params->clock_select == SDRAM_CLK) { + 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 CCDC: + case 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: + { + val |= + (0 << + IPIPEIF_CFG2_YUV8_SHIFT); + val |= + (1 << + IPIPEIF_CFG2_YUV16_SHIFT); + break; + } + case V4L2_MBUS_FMT_YUYV8_2X8: + case V4L2_MBUS_FMT_Y8_1X8: + case V4L2_MBUS_FMT_YUYV10_2X10: + { + val |= + (1 << + IPIPEIF_CFG2_YUV8_SHIFT); + val |= + (1 << + 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); + val |= + (0 << + IPIPEIF_CFG2_YUV16_SHIFT); + } + } + regw_if(val, IPIPEIF_CFG2); + break; + } + default: + /* do nothing */ + ; + } + } + return 0; +} + +static int __devinit dm3xx_ipipeif_probe(struct platform_device *pdev) +{ + static resource_size_t res_len; + struct resource *res; + int status; + + if (NULL != pdev->dev.platform_data) + device_type = DM365; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + res_len = res->end - res->start + 1; + + 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, res->end - res->start + 1); + 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/include/media/davinci/dm3xx_ipipeif.h b/include/media/davinci/dm3xx_ipipeif.h new file mode 100644 index 0000000..87389ff --- /dev/null +++ b/include/media/davinci/dm3xx_ipipeif.h @@ -0,0 +1,292 @@ +/* +* 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 +/* 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 +}; + +/* IPIPE 5.1 interface types */ +/* dpcm predicator for IPIPE 5.1 */ +enum ipipeif_dpcm_pred { + DPCM_SIMPLE_PRED, + DPCM_ADV_PRED +}; +/* 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, +}; + +/* clockdiv for IPIPE 5.1 */ +struct ipipeif_5_1_clkdiv { + unsigned char m; + unsigned char n; +}; + +/* DPC at the if for IPIPE 5.1 */ +struct ipipeif_dpc { + /* 0 - disable, 1 - enable */ + unsigned char en; + /* threshold */ + unsigned short thr; +}; + +enum ipipeif_decimation { + IPIPEIF_DECIMATION_OFF, + IPIPEIF_DECIMATION_ON +}; + +enum ipipeif_pixel_order { + IPIPEIF_CBCR_Y, + IPIPEIF_Y_CBCR +}; + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include + +enum ipipeif_clock { + PIXCEL_CLK, + SDRAM_CLK +}; + +enum ipipeif_pack_mode { + IPIPEIF_PACK_16_BIT, + IPIPEIF_PACK_8_BIT +}; + +enum ipipe_oper_mode { + CONTINUOUS, + 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 { + AVG_OFF, + AVG_ON +}; + +enum ipipeif_input_source { + CCDC, + SDRAM_RAW, + CCDC_DARKFM, + SDRAM_YUV +}; + +enum ipipeif_ialaw { + ALAW_OFF, + 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 { + SRC1_PARALLEL_PORT, + SRC1_SDRAM_RAW, + SRC1_ISIF_DARKFM, + SRC1_SDRAM_YUV +}; + +enum ipipeif_dpcm_type { + DPCM_8BIT_10BIT, + 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_GFG1 (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) +#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); +u32 ipipeif_get_enable(void); + +#endif +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Jun 30 08:13:14 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 30 Jun 2011 18:43:14 +0530 Subject: [RFC PATCH 5/8] davinci: vpfe: add ccdc driver with media controller interface In-Reply-To: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1309439597-15998-6-git-send-email-manjunath.hadli@ti.com> From: Nagabhushana Netagunte 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 | 6 +- drivers/media/video/davinci/vpfe_ccdc.c | 813 ++++++++++++++++++++++++++ include/media/davinci/vpfe_ccdc.h | 89 +++ 3 files changed, 903 insertions(+), 5 deletions(-) create mode 100644 drivers/media/video/davinci/vpfe_ccdc.c create mode 100644 include/media/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..7d56ec9 100644 --- a/drivers/media/video/davinci/ccdc_hw_device.h +++ b/drivers/media/video/davinci/ccdc_hw_device.h @@ -57,7 +57,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 +102,5 @@ 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..e2453a5 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_ccdc.c @@ -0,0 +1,813 @@ +/* + * 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 "ccdc_hw_device.h" + +#define MAX_WIDTH 4096 +#define MAX_HEIGHT 4096 + +static const unsigned int ccdc_fmts[] = { + V4L2_MBUS_FMT_Y8_1X8, + V4L2_MBUS_FMT_YUYV8_2X8, + V4L2_MBUS_FMT_YUYV8_1X16, + V4L2_MBUS_FMT_YUYV10_1X20, + V4L2_MBUS_FMT_SBGGR10_1X10, +}; + +/* + * 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; + + 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); + } else { + int fid; + /* 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; + } else { + /* + * 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_SINK) + 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/include/media/davinci/vpfe_ccdc.h b/include/media/davinci/vpfe_ccdc.h new file mode 100644 index 0000000..1898a9e --- /dev/null +++ b/include/media/davinci/vpfe_ccdc.h @@ -0,0 +1,89 @@ +/* + * 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 + +#ifdef __KERNEL__ + +#ifdef CONFIG_ARCH_DAVINCI_DM365 +#include <../include/media/davinci/dm365_ccdc.h> +#endif + +#ifdef CONFIG_ARCH_DAVINCI_DM355 +#include <../include/media/davinci/dm355_ccdc.h> +#endif + +#ifdef CONFIG_ARCH_DAVINCI_DM644x +#include <../include/media/davinci/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 + +#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) +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Jun 30 08:13:15 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 30 Jun 2011 18:43:15 +0530 Subject: [RFC PATCH 6/8] davinci: vpfe: add v4l2 video driver support In-Reply-To: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1309439597-15998-7-git-send-email-manjunath.hadli@ti.com> From: Nagabhushana Netagunte 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 | 1712 ++++++++++++++++++++++++++++++ include/media/davinci/vpfe_types.h | 50 +- include/media/davinci/vpfe_video.h | 142 +++ 3 files changed, 1871 insertions(+), 33 deletions(-) create mode 100644 drivers/media/video/davinci/vpfe_video.c create mode 100644 include/media/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..00587d7 --- /dev/null +++ b/drivers/media/video/davinci/vpfe_video.c @@ -0,0 +1,1712 @@ +/* + * 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 +#include "ccdc_hw_device.h" + +/* number of buffers supported in single-shot mode */ +#define SS_NUM_BUFFERS 1 +/* 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 v4l2_subdev *vpfe_get_input_sd(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 media_entity_to_v4l2_subdev(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 -1; + } + + 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 -1; + } + + /* 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; +} + +/* return pointer to the VPFE video instance at the far end of the pipeline */ +static struct vpfe_video_device * +vpfe_video_far_end(struct vpfe_video_device *video) +{ + struct media_entity *entity = &video->video_dev.entity; + struct media_device *mdev = entity->parent; + struct vpfe_video_device *far_end = NULL; + struct media_entity_graph graph; + + 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 != video->type) + break; + + far_end = NULL; + } + + mutex_unlock(&mdev->graph_mutex); + + return far_end; +} + +/* update pipe state selected by user */ +static int vpfe_update_pipe_state(struct vpfe_video_device *video) +{ + struct vpfe_pipeline *pipe = &video->pipe; + struct vpfe_video_device *far_end = NULL; + int ret; + + far_end = vpfe_video_far_end(video); + if (far_end == NULL) { + /* possible that it is continous mode */ + ret = vpfe_update_current_ext_subdev(video); + if (ret) { + printk(KERN_ERR "invalid external subdev\n"); + return ret; + } + pipe->state = VPFE_PIPELINE_STREAM_CONTINUOUS; + pipe->output = video; + pipe->input_sd = vpfe_get_input_sd(video); + } else if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + pipe->state = VPFE_PIPELINE_STREAM_SINGLESHOT; + pipe->input = far_end; + pipe->output = video; + } else { + pipe->state = VPFE_PIPELINE_STREAM_SINGLESHOT; + pipe->output = far_end; + pipe->input = video; + } + + video->initialized = 1; + video->skip_frame_count = 1; + video->skip_frame_count_init = 1; + + return 0; +} + +/* checks wether pipeline is ready for enabling */ +static int is_pipe_ready(struct vpfe_pipeline *pipe) +{ + if (pipe->state == VPFE_PIPELINE_STREAM_SINGLESHOT) { + if (!pipe->input->started || + !(pipe->input->state & VPFE_VIDEO_BUFFER_QUEUED) || + !pipe->output->started || + !(pipe->output->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; + + subdev = vpfe_video_remote_subdev(pipe->output, 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 = &pipe->input_sd->entity; + else + entity = &pipe->input->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 = &pipe->input_sd->entity; + else + entity = &pipe->input->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); + else + return vpfe_pipeline_enable(pipe); +} + +/* + * 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 (NULL == fh) + 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) { + if (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; + + video->next_frm = list_entry(video->dma_queue.next, + struct videobuf_buffer, queue); + 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); +} + +/* 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); + /* for continous mode, proceed with next buffer */ + if (pipe->state == VPFE_PIPELINE_STREAM_CONTINUOUS) + video->cur_frm = video->next_frm; +} + +/* vpfe_stop_capture: stop streaming in ccdc/isif */ +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; + + 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); + 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) { + if (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(&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; + } else + /* for single-shot mode */ + *count = SS_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 buffer is not initialized, initialize it */ + if (VIDEOBUF_NEEDS_INIT == vb->state) { + 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; + + empty = list_empty(&video->dma_queue); + + /* add the buffer to the DMA queue */ + spin_lock_irqsave(&video->dma_queue_lock, flags); + 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)){ + 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); + /* we need to 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 ((V4L2_BUF_TYPE_VIDEO_CAPTURE != buf_type) && + (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type)) { + 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); + + video->pipe.state = VPFE_PIPELINE_STREAM_STOPPED; + + 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/include/media/davinci/vpfe_types.h b/include/media/davinci/vpfe_types.h index 76fb74b..05da7c7 100644 --- a/include/media/davinci/vpfe_types.h +++ b/include/media/davinci/vpfe_types.h @@ -1,23 +1,22 @@ /* - * 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 - */ +* 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_TYPES_H #define _VPFE_TYPES_H - +#include #ifdef __KERNEL__ enum vpfe_pin_pol { @@ -25,24 +24,9 @@ enum vpfe_pin_pol { 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 v4l2_mbus_pixelcode if_type; enum vpfe_pin_pol hdpol; enum vpfe_pin_pol vdpol; }; diff --git a/include/media/davinci/vpfe_video.h b/include/media/davinci/vpfe_video.h new file mode 100644 index 0000000..4034d6a --- /dev/null +++ b/include/media/davinci/vpfe_video.h @@ -0,0 +1,142 @@ +/* + * 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 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; + /* input subdev, tvp514x, mt9p031, tvp7002 + in case of continous mode */ + struct v4l2_subdev *input_sd; + /* input video node in case of single-shot mode */ + struct vpfe_video_device *input; + /* capturing video node */ + struct vpfe_video_device *output; +}; + +#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; +}; + +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 Jun 30 08:13:13 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 30 Jun 2011 18:43:13 +0530 Subject: [RFC PATCH 4/8] davinci: vpfe: add support for CCDC hardware for dm365 In-Reply-To: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1309439597-15998-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/dm365_ccdc.c | 1517 +++++++++++++++++++++++++ drivers/media/video/davinci/dm365_ccdc_regs.h | 309 +++++ include/media/davinci/dm365_ccdc.h | 722 ++++++++++++ 3 files changed, 2548 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/dm365_ccdc.c create mode 100644 drivers/media/video/davinci/dm365_ccdc_regs.h create mode 100644 include/media/davinci/dm365_ccdc.h diff --git a/drivers/media/video/davinci/dm365_ccdc.c b/drivers/media/video/davinci/dm365_ccdc.c new file mode 100644 index 0000000..e549382 --- /dev/null +++ b/drivers/media/video/davinci/dm365_ccdc.c @@ -0,0 +1,1517 @@ +/* +* 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 "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 = { + .en = 0, + .corr_shft = CCDC_NO_SHIFT, + .scale_fact = {1, 0}, + }, + .df_csc = { + .df_or_csc = 0, + .csc = { + .en = 0 + }, + }, + .dfc = { + .en = 0 + }, + .bclamp = { + .en = 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 + }, + .compress = { + .alg = CCDC_ALAW, + }, +}; + +/* 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 = { + .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 = { + .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, + }, + .test_pat_gen = 0, + }, + }, + .data_pack = CCDC_DATA_PACK8, +}; + +/* Raw Bayer formats */ +static u32 ccdc_raw_bayer_pix_formats[] = { V4L2_PIX_FMT_SBGGR8, + 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) { + 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 (module_params->compress.alg == CCDC_ALAW) + 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 (module_params->compress.alg == CCDC_DPCM) { + val = 1 << CCDC_DPCM_EN_SHIFT; + val |= (module_params->compress.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) { + 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) { + 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) { + if (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_SBGGR8) { + if ((ccdc_cfg.bayer.config_params.compress.alg != + CCDC_ALAW) && + (ccdc_cfg.bayer.config_params.compress.alg != + CCDC_DPCM)) { + dev_dbg(dev, "Either configure A-Law or" + "DPCM\n"); + return -EINVAL; + } + ccdc_cfg.data_pack = CCDC_PACK_8BIT; + } else if (pixfmt == V4L2_PIX_FMT_SBGGR16) { + ccdc_cfg.bayer.config_params.compress.alg = + CCDC_NO_COMPRESSION; + ccdc_cfg.data_pack = CCDC_PACK_16BIT; + } else { + return -EINVAL; + } + ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + } 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; + } + + return 0; +} + +static u32 ccdc_get_pixel_format(void) +{ + u32 pixfmt; + + if (ccdc_cfg.if_type == V4L2_MBUS_FMT_SBGGR10_1X10) + if (ccdc_cfg.bayer.config_params.compress.alg + == CCDC_ALAW + || ccdc_cfg.bayer.config_params.compress.alg + == CCDC_DPCM) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + 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; + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + break; + case V4L2_MBUS_FMT_YUYV10_1X20: + case V4L2_MBUS_FMT_YUYV8_1X16: + ccdc_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_16BIT; + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + 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; + } else + ret = -EINVAL; + + 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 -1; + } + 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 -1; + } + /* 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_regs.h b/drivers/media/video/davinci/dm365_ccdc_regs.h new file mode 100644 index 0000000..d11e6d4 --- /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/media/davinci/dm365_ccdc.h b/include/media/davinci/dm365_ccdc.h new file mode 100644 index 0000000..b4dd9c4 --- /dev/null +++ b/include/media/davinci/dm365_ccdc.h @@ -0,0 +1,722 @@ +/* +* 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 +/** + * ccdc float type S8Q8/U8Q8 + */ +struct ccdc_float_8 { + /* 8 bit integer part */ + unsigned char integer; + /* 8 bit decimal part */ + unsigned char decimal; +}; + +/** + * brief ccdc float type U16Q16/S16Q16 + */ +struct ccdc_float_16 { + /* 16 bit integer part */ + unsigned short integer; + /* 16 bit decimal part */ + unsigned short decimal; +}; + +/* + * ccdc image(target) window parameters + */ +struct ccdc_cropwin { + /* horzontal offset of the top left corner in pixels */ + unsigned int left; + /* vertical offset of the top left corner in pixels */ + unsigned int top; + /* width in pixels of the rectangle */ + unsigned int width; + /* height in lines of the rectangle */ + unsigned int height; +}; + +/************************************************************************ + * 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]; +}; + +/** + * CCDC image data size + */ +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 +}; + +/** + * CCDC image data shift to right + */ +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 +}; + +/** + * MSB of image data connected to sensor port + */ +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 +}; + +/************************************************************************* +** Black Compensation parameters +*************************************************************************/ +/** + * Structure used for Black Compensation + */ +struct ccdc_black_comp { + /* Comp for Red */ + char r_comp; + /* Comp for Gr */ + char gr_comp; + /* Comp for Blue */ + char b_comp; + /* Comp for Gb */ + char gb_comp; +}; + +/************************************************************************* +** 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; +}; + +enum ccdc_compress_alg { + CCDC_ALAW, + CCDC_DPCM, + CCDC_NO_COMPRESSION +}; + +struct ccdc_compress { + /* Enable or diable A-Law or DPCM compression. */ + enum ccdc_compress_alg alg; + /* Predictor for DPCM compression */ + enum ccdc_dpcm_predictor pred; +}; + +/* 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; + /* A-Law and DPCM compression options */ + struct ccdc_compress compress; + /* 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; +}; + +#ifdef __KERNEL__ +struct ccdc_ycbcr_config { + /* 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 { + /* 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 +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Jun 30 08:13:11 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 30 Jun 2011 18:43:11 +0530 Subject: [RFC PATCH 2/8] davinci: vpfe: add IPIPE hardware layer support In-Reply-To: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1309439597-15998-3-git-send-email-manjunath.hadli@ti.com> From: Nagabhushana Netagunte 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 | 1012 ++++++++++++++++++++++++++ drivers/media/video/davinci/dm365_ipipe_hw.h | 539 ++++++++++++++ 2 files changed, 1551 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..b88e8e8 --- /dev/null +++ b/drivers/media/video/davinci/dm365_ipipe_hw.c @@ -0,0 +1,1012 @@ +/* +* 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 "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 */ +int 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); + + return 0; +} + +static int 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); + + return 0; +} + +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); + if (rsz_id == RSZ_A) { + rsc_params = ¶ms->rsz_rsc_param[RSZ_A]; + rgb = ¶ms->rsz2rgb[RSZ_A]; + ext_mem = ¶ms->ext_mem_param[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 { + rsc_params = ¶ms->rsz_rsc_param[RSZ_B]; + rgb = ¶ms->rsz2rgb[RSZ_B]; + ext_mem = ¶ms->ext_mem_param[RSZ_B]; + 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); +} + +static int ipipe_setup_resizer(struct ipipe_params *params) +{ /*set the registers of either RSZ0 or RSZ1 */ + u32 val; + + /* 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[0], RSZ_EN_A); + if (params->rsz_en[0]) + /*setting rescale parameters */ + rsz_set_rsz_regs(RSZ_A, params); + + regw_rsz(params->rsz_en[1], RSZ_EN_B); + if (params->rsz_en[1]) + rsz_set_rsz_regs(RSZ_B, params); + + val = regr_rsz(RSZ_SRC_MODE); + + return 0; +} + +/* 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) < 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); + } + } + + return ipipe_setup_resizer(config); +} + +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 + */ +int 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); + + return 0; +} + +int 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 (1 != dpc->en) + return 0; + + /* 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) { + count = 0; + while (count < dpc->dpc_size) { + 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))); + count++; + } + } + return 0; +} + +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); +} + +int 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 (1 != otfdpc->en) + return 0; + + /* 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); + } else { + /* MinMax2 */ + if (otfdpc->alg == IPIPE_OTFDPC_2_0) + set_dpc_thresholds(dpc_2_0); + else { + 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); + } + } + + return 0; +} + +/* 2D Noise filter */ +int 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 (1 != noise_filter->en) + return 0; + + /*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); + count = 0; + while (count < IPIPE_NF_STR_TABLE_SIZE) { + regw_ip((noise_filter->str[count] & D2F_STR_VAL_MASK), + offset + D2F_STR + count * 4); + count++; + } + count = 0; + while (count < IPIPE_NF_THR_TABLE_SIZE) { + regw_ip(noise_filter->thr[count] & D2F_THR_VAL_MASK, + offset + D2F_THR + count * 4); + count++; + } + + return 0; +} + +#define IPIPE_U8Q5(decimal, integer) \ + (((decimal & 0x1f) | ((integer & 0x7) << 5))) + +/* Green Imbalance Correction */ +int ipipe_set_gic_regs(struct prev_gic *gic) +{ + u32 val; + + ipipe_clock_enable(); + regw_ip(gic->en & 1, GIC_EN); + + if (!gic->en) + return 0; + + /*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) { + 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); + } + } else + /* Constant Gain. Set threshold to maximum */ + regw_ip(GIC_THR_MASK, GIC_THR); + + return 0; +} + +#define IPIPE_U13Q9(decimal, integer) \ + (((decimal & 0x1ff) | ((integer & 0xf) << 9))) +/* White balance */ +int 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); + + return 0; +} + +/* CFA */ +int 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); + + return 0; +} + +int 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); + + return 0; +} + +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 */ +int 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) { + if (gamma->tbl_size == IPIPE_GAMMA_TBL_SZ_64) + table_size = 64; + else if (gamma->tbl_size == IPIPE_GAMMA_TBL_SZ_128) + table_size = 128; + else if (gamma->tbl_size == IPIPE_GAMMA_TBL_SZ_256) + table_size = 256; + else if (gamma->tbl_size == IPIPE_GAMMA_TBL_SZ_512) + table_size = 512; + else + table_size = 0; + + if (!(gamma->bypass_r)) { + if (gamma->table_r != NULL) + ipipe_update_gamma_tbl(gamma->table_r, + table_size, + GAMMA_R_START_ADDR); + } + if (!(gamma->bypass_b)) { + if (gamma->table_b != NULL) + ipipe_update_gamma_tbl(gamma->table_b, + table_size, + GAMMA_B_START_ADDR); + } + if (!(gamma->bypass_g)) { + if (gamma->table_g != NULL) + ipipe_update_gamma_tbl(gamma->table_g, + table_size, + GAMMA_G_START_ADDR); + } + } + + return 0; +} + +/* 3D LUT */ +int 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 0; + + /* lut_3d enabled */ + if (!lut_3d->table) + return 0; + + /* 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); + } + + return 0; +} + +/* Lumina adjustments */ +int 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); + + return 0; +} + +#define IPIPE_S12Q8(decimal, integer) \ + (((decimal & 0xff) | ((integer & 0xf) << 8))) +/* RGB2YUV */ +int 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); + + return 0; +} + +/* YUV 422 conversion */ +int 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); + + return 0; +} + +/* GBCE */ +int 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); + + /* set to 0 */ + val = 0; + + if (gbce->en) { + regw_ip(gbce->type, GBCE_TYP); + if (gbce->table) { + 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; + } + } + } + + return 0; +} +/* Edge Enhancement */ +int 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 (1 == ee->en) { + 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) { + 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; + } + } + } + + return 0; +} + +/* 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); +} + +int ipipe_set_car_regs(struct prev_car *car) +{ + u32 val; + + ipipe_clock_enable(); + regw_ip(car->en, CAR_EN); + + if (car->en) { + 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); + } + } + } + + return 0; +} + +/* Chromatic Gain Suppression */ +int ipipe_set_cgs_regs(struct prev_cgs *cgs) +{ + ipipe_clock_enable(); + regw_ip(cgs->en, CGS_EN); + + if (cgs->en) { + /* 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); + } + + return 0; +} + +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..c3c79c6 --- /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 << 1) +#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 << 9) +#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 Jun 30 08:13:12 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 30 Jun 2011 18:43:12 +0530 Subject: [RFC PATCH 3/8] davinci: vpfe: add IPIPE support for media controller driver In-Reply-To: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <1309439597-15998-4-git-send-email-manjunath.hadli@ti.com> From: Nagabhushana Netagunte 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 | 485 +++ drivers/media/video/davinci/dm365_def_para.h | 39 + drivers/media/video/davinci/dm365_ipipe.c | 4086 ++++++++++++++++++++++++++ include/media/davinci/dm365_ipipe.h | 1353 +++++++++ include/media/davinci/imp_common.h | 231 ++ include/media/davinci/imp_hw_if.h | 177 ++ 6 files changed, 6371 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 include/media/davinci/dm365_ipipe.h create mode 100644 include/media/davinci/imp_common.h create mode 100644 include/media/davinci/imp_hw_if.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..75b9972 --- /dev/null +++ b/drivers/media/video/davinci/dm365_def_para.c @@ -0,0 +1,485 @@ +/* +* 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 + +/* Defaults for lutdpc */ +struct prev_lutdpc dm365_lutdpc_defaults = { + .en = 0 +}; + +/* Defaults for otfdpc */ +struct prev_lutdpc dm365_otfdpc_defaults = { + .en = 0 +}; + +/* Defaults for 2D - nf */ +struct prev_nf dm365_nf_defaults = { + .en = 0 +}; + +/* defaults for GIC */ +struct prev_gic dm365_gic_defaults = { + .en = 0 +}; + +/* 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, + .hpf_thr_2dir = 0, + .hpf_slp_2dir = 0, + .hp_mix_thr_2dir = 0, + .hp_mix_slope_2dir = 0, + .dir_thr_2dir = 0, + .dir_slope_2dir = 0, + .nd_wt_2dir = 0, + .hue_fract_daa = 0, + .edge_thr_daa = 0, + .thr_min_daa = 0, + .thr_slope_daa = 0, + .slope_min_daa = 0, + .slope_slope_daa = 0, + .lp_wt_daa = 0 +}; + +/* 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 */ + .out_ofst_r = 0, + .out_ofst_g = 0, + .out_ofst_b = 0 +}; + +/* Defaults for gamma correction */ +struct prev_gamma dm365_gamma_defaults = { + .bypass_r = 0, + .bypass_b = 0, + .bypass_g = 0, + .tbl_sel = IPIPE_GAMMA_TBL_ROM +}; + +/* Defaults for 3d lut */ +struct prev_3d_lut dm365_3d_lut_defaults = { + .en = 0 +}; + +/* Defaults for lumina adjustments */ +struct prev_lum_adj dm365_lum_adj_defaults = { + .brightness = 0, + .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_y = 0, + .out_ofst_cb = 0x80, + .out_ofst_cr = 0x80 +}; + +/* Defaults for GBCE */ +struct prev_gbce dm365_gbce_defaults = { + .en = 0 +}; + +/* Defaults for yuv 422 conversion */ +struct prev_yuv422_conv dm365_yuv422_conv_defaults = { + .en_chrom_lpf = 0, + .chrom_pos = IPIPE_YUV422_CHR_POS_COSITE +}; + +/* Defaults for Edge Ehnancements */ +struct prev_yee dm365_yee_defaults = { + .en = 0, +}; + +/* Defaults for CAR conversion */ +struct prev_car dm365_car_defaults = { + .en = 0, +}; + +/* Defaults for CGS */ +struct prev_cgs dm365_cgs_defaults = { + .en = 0, +}; + +#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 = SRC1_PARALLEL_PORT, + .clk_div = { + .m = 1, /* clock = sdram clock * (m/n) */ + .n = 6 + }, + .dpc = { + .en = 0, + }, + .dpcm = { + .en = 0, + .type = DPCM_8BIT_12BIT, + .pred = 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, + .align_sync = 0, + .rsz_start = 0, + .df_gain_en = 0 +}; + +struct ipipe_params dm365_ipipe_defs = { + .ipipeif_param = { + .mode = ONE_SHOT, + .source = SDRAM_RAW, + .clock_select = 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 = AVG_OFF, + .gain = 0x200, /* U10Q9 */ + }, + .ipipe_mode = 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_vps = 0, + .ipipe_vsz = HEIGHT_I - 1, + .ipipe_hps = 0, + .ipipe_hsz = WIDTH_I - 1, + .rsz_common = { + .vps = 0, + .vsz = HEIGHT_I - 1, + .hps = 0, + .hsz = WIDTH_I - 1, + .src_img_fmt = RSZ_IMG_422, + .y_c = 0, + .raw_flip = 1, /* flip preserve Raw format */ + .source = IPIPE_DATA, + .passthrough = IPIPE_BYPASS_OFF, + .yuv_y_min = 0, + .yuv_y_max = 255, + .yuv_c_min = 0, + .yuv_c_max = 255, + .rsz_seq_crv = DISABLE, + .out_chr_pos = IPIPE_YUV422_CHR_POS_COSITE + }, + .rsz_rsc_param = { + { + .mode = ONE_SHOT, + .h_flip = DISABLE, + .v_flip = DISABLE, + .cen = DISABLE, + .yen = DISABLE, + .i_vps = 0, + .i_hps = 0, + .o_vsz = HEIGHT_O - 1, + .o_hsz = WIDTH_O - 1, + .v_phs_y = 0, + .v_phs_c = 0, + .v_dif = 256, + .v_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .v_lpf_int_y = 0, + .v_lpf_int_c = 0, + .h_phs = 0, + .h_dif = 256, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_lpf_int_y = 0, + .h_lpf_int_c = 0, + .dscale_en = 0, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .f_div.en = 0 + }, + { + .mode = ONE_SHOT, + .h_flip = DISABLE, + .v_flip = DISABLE, + .cen = DISABLE, + .yen = DISABLE, + .i_vps = 0, + .i_hps = 0, + .o_vsz = HEIGHT_O - 1, + .o_hsz = WIDTH_O - 1, + .v_phs_y = 0, + .v_phs_c = 0, + .v_dif = 256, + .v_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .v_lpf_int_y = 0, + .v_lpf_int_c = 0, + .h_phs = 0, + .h_dif = 256, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_lpf_int_y = 0, + .h_lpf_int_c = 0, + .dscale_en = 0, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .f_div.en = 0 + }, + }, + .rsz2rgb = { + { + .rgb_en = DISABLE + }, + { + .rgb_en = DISABLE + } + }, + .ext_mem_param = { + { + .rsz_sdr_oft_y = WIDTH_O << 1, + .rsz_sdr_ptr_s_y = 0, + .rsz_sdr_ptr_e_y = HEIGHT_O, + .rsz_sdr_oft_c = WIDTH_O, + .rsz_sdr_ptr_s_c = 0, + .rsz_sdr_ptr_e_c = HEIGHT_O >> 1, + .flip_ofst_y = 0, + .flip_ofst_c = 0, + .c_offset = 0, + .user_y_ofst = 0, + .user_c_ofst = 0 + }, + { + .rsz_sdr_oft_y = WIDTH_O << 1, + .rsz_sdr_ptr_s_y = 0, + .rsz_sdr_ptr_e_y = HEIGHT_O, + .rsz_sdr_oft_c = WIDTH_O, + .rsz_sdr_ptr_s_c = 0, + .rsz_sdr_ptr_e_c = HEIGHT_O, + .flip_ofst_y = 0, + .flip_ofst_c = 0, + .c_offset = 0, + .user_y_ofst = 0, + .user_c_ofst = 0 + }, + }, + .rsz_en[0] = ENABLE, + .rsz_en[1] = DISABLE +}; + +struct prev_single_shot_config dm365_prev_ss_config_defs = { + .bypass = IPIPE_BYPASS_OFF, + .input = { + .image_width = WIDTH_I, + .image_height = HEIGHT_I, + .vst = 0, + .hst = 0, + .ppln = WIDTH_I + 8, + .lpfr = HEIGHT_I + 10, + .pred = DPCM_SIMPLE_PRED, + .clk_div = {1, 6}, + .data_shift = IPIPEIF_BITS11_0, + .dec_en = 0, + .rsz = 16, /* resize ratio 16/rsz */ + .frame_div_mode_en = 0, + .avg_filter_en = AVG_OFF, + .dpc = {0, 0}, + .gain = 512, + .clip = 4095, + .align_sync = 0, + .rsz_start = 0, + .pix_fmt = IPIPE_BAYER, + .colp_olop = IPIPE_GREEN_BLUE, + .colp_olep = IPIPE_BLUE, + .colp_elop = IPIPE_RED, + .colp_elep = IPIPE_GREEN_RED + }, + .output = { + .pix_fmt = IPIPE_UYVY + } +}; + +struct prev_continuous_config dm365_prev_cont_config_defs = { + .bypass = IPIPE_BYPASS_OFF, + .input = { + .en_df_sub = 0, + .dec_en = 0, + .rsz = 16, + .avg_filter_en = 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 = { + .image_width = WIDTH_I, + .image_height = HEIGHT_I, + .vst = 0, + .hst = 0, + .ppln = WIDTH_I + 8, + .lpfr = HEIGHT_I + 10, + .clk_div = {1, 6}, + .dec_en = 0, + .rsz = 16, /* resize ratio 16/rsz */ + .frame_div_mode_en = 0, + .avg_filter_en = AVG_OFF, + .align_sync = 0, + .rsz_start = 0, + .pix_fmt = IPIPE_UYVY + }, + .output1 = { + .enable = 1, + .pix_fmt = IPIPE_UYVY, + .h_flip = 0, + .v_flip = 0, + .width = WIDTH_O, + .height = HEIGHT_O, + .vst_y = 0, + .vst_c = 0, + .v_typ_y = RSZ_INTP_CUBIC, + .v_typ_c = RSZ_INTP_CUBIC, + .v_lpf_int_y = 0, + .v_lpf_int_c = 0, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_lpf_int_y = 0, + .h_lpf_int_c = 0, + .en_down_scale = 0, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .user_y_ofst = 0, + .user_c_ofst = 0 + }, + .output2 = { + .enable = 1, + .pix_fmt = IPIPE_UYVY, + .h_flip = 0, + .v_flip = 0, + .width = WIDTH_O, + .height = HEIGHT_O, + .vst_y = 0, + .vst_c = 0, + .v_typ_y = RSZ_INTP_CUBIC, + .v_typ_c = RSZ_INTP_CUBIC, + .v_lpf_int_y = 0, + .v_lpf_int_c = 0, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_lpf_int_y = 0, + .h_lpf_int_c = 0, + .en_down_scale = 0, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .user_y_ofst = 0, + .user_c_ofst = 0 + }, + .chroma_sample_even = 0, + .yuv_y_min = 0, + .yuv_y_max = 255, + .yuv_c_min = 0, + .yuv_c_max = 255, + .out_chr_pos = IPIPE_YUV422_CHR_POS_COSITE, +}; + +struct rsz_continuous_config dm365_rsz_cont_config_defs = { + .output1 = { + .enable = 1, + .h_flip = 0, + .v_flip = 0, + .v_typ_y = RSZ_INTP_CUBIC, + .v_typ_c = RSZ_INTP_CUBIC, + .v_lpf_int_y = 0, + .v_lpf_int_c = 0, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_lpf_int_y = 0, + .h_lpf_int_c = 0, + .en_down_scale = 0, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .user_y_ofst = 0, + .user_c_ofst = 0 + }, + .output2 = { + .enable = 1, + .pix_fmt = IPIPE_UYVY, + .h_flip = 0, + .v_flip = 0, + .width = WIDTH_O, + .height = HEIGHT_O, + .vst_y = 0, + .vst_c = 0, + .v_typ_y = RSZ_INTP_CUBIC, + .v_typ_c = RSZ_INTP_CUBIC, + .v_lpf_int_y = 0, + .v_lpf_int_c = 0, + .h_typ_y = RSZ_INTP_CUBIC, + .h_typ_c = RSZ_INTP_CUBIC, + .h_lpf_int_y = 0, + .h_lpf_int_c = 0, + .en_down_scale = 0, + .h_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .v_dscale_ave_sz = IPIPE_DWN_SCALE_1_OVER_2, + .user_y_ofst = 0, + .user_c_ofst = 0 + }, + .chroma_sample_even = 0, + .yuv_y_min = 0, + .yuv_y_max = 255, + .yuv_c_min = 0, + .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..01e7e76 --- /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 +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..17d5919 --- /dev/null +++ b/drivers/media/video/davinci/dm365_ipipe.c @@ -0,0 +1,4086 @@ +/* +* 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 +#include + +#include "dm365_ipipe_hw.h" +#include "dm365_def_para.h" + +/* IPIPE module operation state */ +struct ipipe_oper_state { + /* Operation state in continuous mode */ + 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; + /* Buffer type, interleaved or field seperated for interlaced + * scan + */ + unsigned int buffer_type; + /* frame format, 0 - interlaced, 1 - progressive */ + unsigned int frame_format; + /* input pixel format */ + enum imp_pix_formats in_pixel_format; + /* input pixel format */ + enum imp_pix_formats out_pixel_format; +}; + +/* Operation mode of image processor (imp) */ +static u32 oper_mode = IMP_MODE_NOT_CONFIGURED; +/* enable/disable serializer */ +static u32 en_serializer; + +#define CONFIG_IPIPE_PARAM_VALIDATION +/* ipipe module operation state & configuration */ +static struct ipipe_oper_state oper_state; + +/* LUT Defect pixel correction data */ +static struct prev_lutdpc lutdpc; +static int set_lutdpc_params(struct device *dev, void *param, int len); +static int get_lutdpc_params(struct device *dev, void *param, int len); + +/* LUT Defect pixel correction data */ +static struct prev_otfdpc otfdpc; +static int set_otfdpc_params(struct device *dev, void *param, int len); +static int get_otfdpc_params(struct device *dev, void *param, int len); + +/* Noise filter */ +static struct prev_nf nf1; +static struct prev_nf nf2; +static int set_nf1_params(struct device *dev, void *param, int len); +static int get_nf1_params(struct device *dev, void *param, int len); +static int set_nf2_params(struct device *dev, void *param, int len); +static int get_nf2_params(struct device *dev, void *param, int len); + +/* Green Imbalance Correction */ +static struct prev_gic gic; +static int set_gic_params(struct device *dev, void *param, int len); +static int get_gic_params(struct device *dev, void *param, int len); + +/* White Balance */ +static struct prev_wb wb; +static int set_wb_params(struct device *dev, void *param, int len); +static int get_wb_params(struct device *dev, void *param, int len); + +/* CFA */ +static struct prev_cfa cfa; +static int set_cfa_params(struct device *dev, void *param, int len); +static int get_cfa_params(struct device *dev, void *param, int len); + +/* RGB2RGB conversion */ +static struct prev_rgb2rgb rgb2rgb_1; +static struct prev_rgb2rgb rgb2rgb_2; +static int set_rgb2rgb_1_params(struct device *dev, void *param, int len); +static int get_rgb2rgb_1_params(struct device *dev, void *param, int len); +static int set_rgb2rgb_2_params(struct device *dev, void *param, int len); +static int get_rgb2rgb_2_params(struct device *dev, void *param, int len); + +/* Gamma correction */ +static struct prev_gamma gamma; +static int set_gamma_params(struct device *dev, void *param, int len); +static int get_gamma_params(struct device *dev, void *param, int len); + +/* 3D LUT */ +static struct prev_3d_lut lut_3d; +static int set_3d_lut_params(struct device *dev, void *param, int len); +static int get_3d_lut_params(struct device *dev, void *param, int len); + +/* Lumina Adjustment */ +static struct prev_lum_adj lum_adj; +static int set_lum_adj_params(struct device *dev, void *param, int len); +static int get_lum_adj_params(struct device *dev, void *param, int len); + +/* RGB2YUV conversion */ +static struct prev_rgb2yuv rgb2yuv; +static int set_rgb2yuv_params(struct device *dev, void *param, int len); +static int get_rgb2yuv_params(struct device *dev, void *param, int len); + +/* YUV 422 conversion */ +static struct prev_yuv422_conv yuv422_conv; +static int set_yuv422_conv_params(struct device *dev, void *param, int len); +static int get_yuv422_conv_params(struct device *dev, void *param, int len); + +/* GBCE */ +static struct prev_gbce gbce; +static int set_gbce_params(struct device *dev, void *param, int len); +static int get_gbce_params(struct device *dev, void *param, int len); + +/* Edge Enhancement */ +static struct prev_yee yee; +static int set_yee_params(struct device *dev, void *param, int len); +static int get_yee_params(struct device *dev, void *param, int len); + +/* Chromatic Artifact Reduction, CAR */ +static struct prev_car car; +static int set_car_params(struct device *dev, void *param, int len); +static int get_car_params(struct device *dev, void *param, int len); + +/* Chromatic Artifact Reduction, CAR */ +static struct prev_cgs cgs; +static int set_cgs_params(struct device *dev, void *param, int len); +static int get_cgs_params(struct device *dev, void *param, int len); + +/* Tables for various tuning modules */ +static struct ipipe_lutdpc_entry ipipe_lutdpc_table[MAX_SIZE_DPC]; +static struct ipipe_3d_lut_entry ipipe_3d_lut_table[MAX_SIZE_3D_LUT]; +static unsigned short ipipe_gbce_table[MAX_SIZE_GBCE_LUT]; +static struct ipipe_gamma_entry ipipe_gamma_table_r[MAX_SIZE_GAMMA]; +static struct ipipe_gamma_entry ipipe_gamma_table_b[MAX_SIZE_GAMMA]; +static struct ipipe_gamma_entry ipipe_gamma_table_g[MAX_SIZE_GAMMA]; +static short ipipe_yee_table[MAX_SIZE_YEE_LUT]; + +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 + } +}; + +/* function prototypes */ +static struct prev_module_if *prev_enum_preview_cap(struct device *dev, + int index); +static unsigned int prev_get_oper_mode(void); +static int ipipe_set_oper_mode(unsigned int mode); +static void ipipe_reset_oper_mode(void); +static unsigned int ipipe_get_oper_state(void); +static void ipipe_set_oper_state(unsigned int state); +static unsigned int ipipe_rsz_chain_state(void); +static void *ipipe_alloc_config_block(struct device *dev); +static void ipipe_dealloc_config_block(struct device *dev, void *config_block); +static void ipipe_set_user_config_defaults(struct device *dev, + enum imp_log_chan_t chan_type, + void *config); +static int ipipe_set_preview_config(struct device *dev, + void *user_config, void *config); +static int ipipe_set_resize_config(struct device *dev, int resizer_chained, + void *user_config, void *config); +static int ipipe_reconfig_resizer(struct device *dev, + struct rsz_reconfig *reconfig, + void *config); + +static void ipipe_enable(unsigned char en, void *config); +static unsigned int ipipe_get_rsz_config_state(void); +static int ipipe_do_hw_setup(struct device *dev, void *config); +static unsigned int ipipe_get_prev_config_state(void); +static void ipipe_lock_chain(void); +static void ipipe_unlock_chain(void); +static void ipipe_dealloc_user_config_block(struct device *dev, + void *config_block); +static void *ipipe_alloc_user_config_block(struct device *dev, + enum imp_log_chan_t chan_type, + int *len); +static int ipipe_update_outbuf1_address(void *config, unsigned int address); +static int ipipe_update_outbuf2_address(void *config, unsigned int address); +static int ipipe_set_ipipe_if_address(void *config, unsigned int address); + +/* IPIPE hardware limits */ +#define IPIPE_MAX_OUTPUT_WIDTH_A 2176 +#define IPIPE_MAX_OUTPUT_WIDTH_B 640 + +/* 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 + +/* Raw YUV formats */ +static u32 ipipe_raw_yuv_pix_formats[] = { V4L2_PIX_FMT_UYVY, + V4L2_PIX_FMT_NV12}; + + +static int ipipe_enum_pix(u32 *pix, int i) +{ + if (i >= ARRAY_SIZE(ipipe_raw_yuv_pix_formats)) + return -EINVAL; + + *pix = ipipe_raw_yuv_pix_formats[i]; + return 0; +} + +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; +} + +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) +{ + return en_serializer; +} + +static int ipipe_set_input_win(struct imp_window *); +static int ipipe_get_input_win(struct imp_window *); +static int ipipe_set_in_pixel_format(enum imp_pix_formats); +static int ipipe_set_out_pixel_format(enum imp_pix_formats); +static int ipipe_set_buftype(unsigned char); +static int ipipe_set_frame_format(unsigned char); +static int ipipe_set_output_win(struct imp_window *win); +static int ipipe_get_output_state(unsigned char out_sel); +static int ipipe_get_line_length(unsigned char out_sel); +static int ipipe_get_image_height(unsigned char out_sel); +static int ipipe_set_hw_if_param(struct vpfe_hw_if_param *if_param); + +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, + .reconfig_resizer = ipipe_reconfig_resizer, + .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_input_win = ipipe_set_input_win, + .get_input_win = ipipe_get_input_win, + .set_hw_if_param = ipipe_set_hw_if_param, + .set_in_pixel_format = ipipe_set_in_pixel_format, + .set_out_pixel_format = ipipe_set_out_pixel_format, + .set_buftype = ipipe_set_buftype, + .set_frame_format = ipipe_set_frame_format, + .set_output_win = ipipe_set_output_win, + .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, +}; + +static int ipipe_set_ipipe_if_address(void *config, unsigned int address) +{ + struct ipipeif *if_params; + + if (ISNULL(config)) + return -1; + + if_params = &((struct ipipe_params *)config)->ipipeif_param; + + return ipipeif_set_address(if_params, address); +} + +static void ipipe_lock_chain(void) +{ + mutex_lock(&oper_state.lock); + oper_state.resource_in_use = 1; + mutex_unlock(&oper_state.lock); +} + +static void ipipe_unlock_chain(void) +{ + 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 ipipe_pix_formats in_pix_fmt, + enum ipipe_pix_formats out_pix_fmt, + struct ipipe_params *param) +{ + enum ipipe_pix_formats temp_pix_fmt; + + switch (in_pix_fmt) { + case IPIPE_BAYER_8BIT_PACK: + { + temp_pix_fmt = IPIPE_BAYER; + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_8_BIT; + break; + } + case IPIPE_BAYER_8BIT_PACK_ALAW: + { + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_8_BIT_A_LAW; + temp_pix_fmt = IPIPE_BAYER; + break; + } + case IPIPE_BAYER_8BIT_PACK_DPCM: + { + 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 = IPIPE_BAYER; + break; + } + case IPIPE_BAYER: + { + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_16_BIT; + temp_pix_fmt = IPIPE_BAYER; + break; + } + case IPIPE_BAYER_12BIT_PACK: + { + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_12_BIT; + temp_pix_fmt = IPIPE_BAYER; + break; + } + default: + { + temp_pix_fmt = IPIPE_UYVY; + } + } + + if (temp_pix_fmt == IPIPE_BAYER) + if (out_pix_fmt == IPIPE_BAYER) + param->ipipe_dpaths_fmt = IPIPE_RAW2RAW; + else if ((out_pix_fmt == IPIPE_UYVY) || + (out_pix_fmt == IPIPE_YUV420SP)) + param->ipipe_dpaths_fmt = IPIPE_RAW2YUV; + else + return -1; + else if (temp_pix_fmt == IPIPE_UYVY) { + if (out_pix_fmt == IPIPE_UYVY) + param->ipipe_dpaths_fmt = IPIPE_YUV2YUV; + else if (out_pix_fmt == IPIPE_YUV420SP) + param->ipipe_dpaths_fmt = IPIPE_YUV2YUV; + else + return -1; + } + 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 *config) +{ + 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 ((ISNULL(config)) && (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) +{ + return oper_state.rsz_chained; +} + +static int ipipe_update_outbuf1_address(void *config, unsigned int address) +{ + if ((ISNULL(config)) && (oper_mode == IMP_MODE_CONTINUOUS)) + return rsz_set_output_address(oper_state.shared_config_param, + 0, + address); + return rsz_set_output_address((struct ipipe_params *)config, + 0, + address); +} + +static int ipipe_update_outbuf2_address(void *config, unsigned int address) +{ + if ((ISNULL(config)) && (oper_mode == IMP_MODE_CONTINUOUS)) + return rsz_set_output_address(oper_state.shared_config_param, + 1, + address); + return rsz_set_output_address((struct ipipe_params *)config, + 1, + address); +} + +static void ipipe_enable(unsigned char en, void *config) +{ + struct ipipe_params *param = (struct ipipe_params *)config; + unsigned char val; + unsigned char ret; + + if (en) + val = 1; + + if (oper_mode == IMP_MODE_CONTINUOUS) + param = oper_state.shared_config_param; + + if ((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(val, 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_mode == IMP_MODE_SINGLE_SHOT) + ipipeif_set_enable(val, oper_mode); +} + +static int validate_lutdpc_params(struct device *dev) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + int i; + + if (lutdpc.en > 1 || + lutdpc.repl_white > 1 || + lutdpc.dpc_size > LUT_DPC_MAX_SIZE) + return -1; + if (lutdpc.en && (ISNULL(lutdpc.table))) + return -1; + for (i = 0; i < lutdpc.dpc_size; i++) { + if (lutdpc.table[i].horz_pos > LUT_DPC_H_POS_MASK || + lutdpc.table[i].vert_pos > LUT_DPC_V_POS_MASK) + return -1; + } +#endif + return 0; +} + +static int set_lutdpc_params(struct device *dev, void *param, int len) +{ + struct ipipe_lutdpc_entry *temp_lutdpc; + struct prev_lutdpc dpc_param; + + if (ISNULL(param)) { + /* Copy defaults for dfc */ + temp_lutdpc = lutdpc.table; + memcpy((void *)&lutdpc, + (void *)&dm365_lutdpc_defaults, + sizeof(struct prev_lutdpc)); + lutdpc.table = temp_lutdpc; + } else { + 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 (ISNULL(dpc_param.table)) { + dev_err(dev, "Invalid user dpc table ptr\n"); + return -EINVAL; + } + lutdpc.en = dpc_param.en; + lutdpc.repl_white = dpc_param.repl_white; + lutdpc.dpc_size = dpc_param.dpc_size; + if (copy_from_user + (lutdpc.table, + (struct ipipe_dpc_entry *)dpc_param.table, + (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) < 0) + return -EINVAL; + } + return ipipe_set_lutdpc_regs(&lutdpc); +} + +static int get_lutdpc_params(struct device *dev, void *param, int len) +{ + struct prev_lutdpc *lut_param = (struct prev_lutdpc *)param; + struct prev_lutdpc user_lutdpc; + + if (ISNULL(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 = lutdpc.en; + user_lutdpc.repl_white = lutdpc.repl_white; + user_lutdpc.dpc_size = lutdpc.dpc_size; + if (ISNULL(user_lutdpc.table)) { + dev_err(dev, "get_lutdpc_params:" " Invalid table ptr"); + return -EINVAL; + } + if (copy_to_user(user_lutdpc.table, + lutdpc.table, + (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) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + struct prev_otfdpc *dpc_param = (struct prev_otfdpc *)&otfdpc; + struct prev_otfdpc_2_0 *dpc_2_0; + struct prev_otfdpc_3_0 *dpc_3_0; + + if (dpc_param->en > 1) + return -1; + 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 -1; + } else { + 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 -1; + } +#endif + return 0; +} + +static int set_otfdpc_params(struct device *dev, void *param, int len) +{ + struct prev_otfdpc *dpc_param = (struct prev_otfdpc *)param; + + if (ISNULL(param)) { + /* Copy defaults for dpc2.0 defaults */ + memcpy((void *)&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(&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) < 0) + return -EINVAL; + } + + return ipipe_set_otfdpc_regs(&otfdpc); +} + +static int get_otfdpc_params(struct device *dev, void *param, int len) +{ + struct prev_otfdpc *dpc_param = (struct prev_otfdpc *)param; + + if (ISNULL(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, + &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, unsigned int id) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + struct prev_nf *nf_param = &nf1; + int i; + + if (id) + nf_param = &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 -1; + + for (i = 0; i < IPIPE_NF_THR_TABLE_SIZE; i++) + if (nf_param->thr[i] > D2F_THR_VAL_MASK) + return -1; + for (i = 0; i < IPIPE_NF_STR_TABLE_SIZE; i++) + if (nf_param->str[i] > D2F_STR_VAL_MASK) + return -1; +#endif + return 0; +} + +static int set_nf_params(struct device *dev, unsigned int id, + void *param, int len) +{ + struct prev_nf *nf_param = (struct prev_nf *)param; + struct prev_nf *nf = &nf1; + + if (id) + nf = &nf2; + + if (ISNULL(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, id) < 0) + return -EINVAL; + } + /* Now set the values in the hw */ + return ipipe_set_d2f_regs(id, nf); +} + +static int set_nf1_params(struct device *dev, void *param, int len) +{ + return set_nf_params(dev, 0, param, len); +} + +static int set_nf2_params(struct device *dev, void *param, int len) +{ + return set_nf_params(dev, 1, param, len); +} + +static int get_nf_params(struct device *dev, unsigned int id, void *param, + int len) +{ + struct prev_nf *nf_param = (struct prev_nf *)param; + struct prev_nf *nf = &nf1; + + if (ISNULL(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 = &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 *param, int len) +{ + return get_nf_params(dev, 0, param, len); +} + +static int get_nf2_params(struct device *dev, void *param, int len) +{ + return get_nf_params(dev, 1, param, len); +} + +static int validate_gic_params(struct device *dev) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + if (gic.en > 1 || + gic.gain > GIC_GAIN_MASK || + gic.thr > GIC_THR_MASK || + gic.slope > GIC_SLOPE_MASK || + gic.apply_lsc_gain > 1 || + gic.nf2_thr_gain.integer > GIC_NFGAN_INT_MASK || + gic.nf2_thr_gain.decimal > GIC_NFGAN_DECI_MASK) + return -1; +#endif + return 0; +} + +static int set_gic_params(struct device *dev, void *param, int len) +{ + struct prev_gic *gic_param = (struct prev_gic *)param; + + if (ISNULL(gic_param)) { + /* Copy defaults for nf */ + memcpy((void *)&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(&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) < 0) + return -EINVAL; + } + /* Now set the values in the hw */ + return ipipe_set_gic_regs(&gic); +} + +static int get_gic_params(struct device *dev, void *param, int len) +{ + struct prev_gic *gic_param = (struct prev_gic *)param; + + if (ISNULL(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, + &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) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + if (wb.ofst_r > WB_OFFSET_MASK || + wb.ofst_gr > WB_OFFSET_MASK || + wb.ofst_gb > WB_OFFSET_MASK || + wb.ofst_b > WB_OFFSET_MASK || + wb.gain_r.integer > WB_GAIN_INT_MASK || + wb.gain_r.decimal > WB_GAIN_DECI_MASK || + wb.gain_gr.integer > WB_GAIN_INT_MASK || + wb.gain_gr.decimal > WB_GAIN_DECI_MASK || + wb.gain_gb.integer > WB_GAIN_INT_MASK || + wb.gain_gb.decimal > WB_GAIN_DECI_MASK || + wb.gain_b.integer > WB_GAIN_INT_MASK || + wb.gain_b.decimal > WB_GAIN_DECI_MASK) + return -1; +#endif + return 0; +} +static int set_wb_params(struct device *dev, void *param, int len) +{ + struct prev_wb *wb_param = (struct prev_wb *)param; + + dev_dbg(dev, "set_wb_params"); + if (ISNULL(wb_param)) { + /* Copy defaults for wb */ + memcpy((void *)&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(&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) < 0) + return -EINVAL; + } + + /* Now set the values in the hw */ + return ipipe_set_wb_regs(&wb); +} +static int get_wb_params(struct device *dev, void *param, int len) +{ + struct prev_wb *wb_param = (struct prev_wb *)param; + + if (ISNULL(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, + &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) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + if (cfa.hpf_thr_2dir > CFA_HPF_THR_2DIR_MASK || + cfa.hpf_slp_2dir > CFA_HPF_SLOPE_2DIR_MASK || + cfa.hp_mix_thr_2dir > CFA_HPF_MIX_THR_2DIR_MASK || + cfa.hp_mix_slope_2dir > CFA_HPF_MIX_SLP_2DIR_MASK || + cfa.dir_thr_2dir > CFA_DIR_THR_2DIR_MASK || + cfa.dir_slope_2dir > CFA_DIR_SLP_2DIR_MASK || + cfa.nd_wt_2dir > CFA_ND_WT_2DIR_MASK || + cfa.hue_fract_daa > CFA_DAA_HUE_FRA_MASK || + cfa.edge_thr_daa > CFA_DAA_EDG_THR_MASK || + cfa.thr_min_daa > CFA_DAA_THR_MIN_MASK || + cfa.thr_slope_daa > CFA_DAA_THR_SLP_MASK || + cfa.slope_min_daa > CFA_DAA_SLP_MIN_MASK || + cfa.slope_slope_daa > CFA_DAA_SLP_SLP_MASK || + cfa.lp_wt_daa > CFA_DAA_LP_WT_MASK) + return -1; +#endif + return 0; +} +static int set_cfa_params(struct device *dev, void *param, int len) +{ + struct prev_cfa *cfa_param = (struct prev_cfa *)param; + + dev_dbg(dev, "set_cfa_params"); + if (ISNULL(cfa_param)) { + /* Copy defaults for wb */ + memcpy((void *)&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(&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) < 0) + return -EINVAL; + } + + /* Now set the values in the hw */ + return ipipe_set_cfa_regs(&cfa); +} +static int get_cfa_params(struct device *dev, void *param, int len) +{ + struct prev_cfa *cfa_param = (struct prev_cfa *)param; + + dev_dbg(dev, "get_cfa_params\n"); + if (ISNULL(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, + &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, unsigned int id) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + struct prev_rgb2rgb *rgb2rgb = &rgb2rgb_1; + u32 gain_int_upper = RGB2RGB_1_GAIN_INT_MASK; + u32 offset_upper = RGB2RGB_1_OFST_MASK; + + if (id) { + rgb2rgb = &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 -1; + + if (rgb2rgb->coef_gr.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gr.integer > gain_int_upper) + return -1; + + if (rgb2rgb->coef_br.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_br.integer > gain_int_upper) + return -1; + + if (rgb2rgb->coef_rg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rg.integer > gain_int_upper) + return -1; + + if (rgb2rgb->coef_gg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gg.integer > gain_int_upper) + return -1; + + if (rgb2rgb->coef_bg.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_bg.integer > gain_int_upper) + return -1; + + if (rgb2rgb->coef_rb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_rb.integer > gain_int_upper) + return -1; + + if (rgb2rgb->coef_gb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_gb.integer > gain_int_upper) + return -1; + + if (rgb2rgb->coef_bb.decimal > RGB2RGB_GAIN_DECI_MASK || + rgb2rgb->coef_bb.integer > gain_int_upper) + return -1; + + if (rgb2rgb->out_ofst_r > offset_upper || + rgb2rgb->out_ofst_g > offset_upper || + rgb2rgb->out_ofst_b > offset_upper) + return -1; +#endif + return 0; +} + +static int set_rgb2rgb_params(struct device *dev, unsigned int id, + void *param, int len) +{ + struct prev_rgb2rgb *rgb2rgb_param = (struct prev_rgb2rgb *)param; + struct prev_rgb2rgb *rgb2rgb = &rgb2rgb_1; + + if (id) + rgb2rgb = &rgb2rgb_2; + if (ISNULL(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, id) < 0) + return -EINVAL; + } + + return ipipe_set_rgb2rgb_regs(id, rgb2rgb); +} + +static int set_rgb2rgb_1_params(struct device *dev, void *param, int len) +{ + return set_rgb2rgb_params(dev, 0, param, len); +} + +static int set_rgb2rgb_2_params(struct device *dev, void *param, int len) +{ + return set_rgb2rgb_params(dev, 1, param, len); +} + +static int get_rgb2rgb_params(struct device *dev, unsigned int id, + void *param, int len) +{ + struct prev_rgb2rgb *rgb2rgb_param = (struct prev_rgb2rgb *)param; + struct prev_rgb2rgb *rgb2rgb = &rgb2rgb_1; + + if (ISNULL(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 = &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 *param, int len) +{ + return get_rgb2rgb_params(dev, 0, param, len); +} + +static int get_rgb2rgb_2_params(struct device *dev, void *param, int len) +{ + return get_rgb2rgb_params(dev, 1, param, len); +} + +static int validate_gamma_entry(struct ipipe_gamma_entry *table, int size) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + int i; + + if (ISNULL(table)) + return -1; + else { + for (i = 0; i < size; i++) { + if (table[i].slope > GAMMA_MASK || + table[i].offset > GAMMA_MASK) + return -1; + } + } +#endif + return 0; +} + +static int validate_gamma_params(struct device *dev) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + int table_size; + int err; + + if (gamma.bypass_r > 1 || + gamma.bypass_b > 1 || + gamma.bypass_g > 1) + return -1; + + if (gamma.tbl_sel == IPIPE_GAMMA_TBL_RAM) { + if (gamma.tbl_size == IPIPE_GAMMA_TBL_SZ_64) + table_size = 64; + if (gamma.tbl_size == IPIPE_GAMMA_TBL_SZ_128) + table_size = 128; + else if (gamma.tbl_size == IPIPE_GAMMA_TBL_SZ_256) + table_size = 256; + else if (gamma.tbl_size == IPIPE_GAMMA_TBL_SZ_512) + table_size = 512; + if (!gamma.bypass_r) { + err = validate_gamma_entry(gamma.table_r, table_size); + if (err) { + dev_err(dev, "GAMMA R - table entry invalid\n"); + return err; + } + } + if (!gamma.bypass_b) { + err = validate_gamma_entry(gamma.table_b, table_size); + if (err) { + dev_err(dev, "GAMMA B - table entry invalid\n"); + return err; + } + } + if (!gamma.bypass_g) { + err = validate_gamma_entry(gamma.table_g, table_size); + if (err) { + dev_err(dev, "GAMMA G - table entry invalid\n"); + return err; + } + } + } +#endif + return 0; +} +static int set_gamma_params(struct device *dev, void *param, int len) +{ + + struct prev_gamma *gamma_param = (struct prev_gamma *)param; + struct prev_gamma user_gamma; + int table_size; + + if (ISNULL(gamma_param)) { + /* Copy defaults for gamma */ + gamma.bypass_r = dm365_gamma_defaults.bypass_r; + gamma.bypass_g = dm365_gamma_defaults.bypass_g; + gamma.bypass_b = dm365_gamma_defaults.bypass_b; + gamma.tbl_sel = dm365_gamma_defaults.tbl_sel; + gamma.tbl_size = dm365_gamma_defaults.tbl_size; + /* By default, we bypass the gamma correction. + * So no values by default for tables + */ + } else { + 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; + } + gamma.bypass_r = user_gamma.bypass_r; + gamma.bypass_b = user_gamma.bypass_b; + gamma.bypass_g = user_gamma.bypass_g; + gamma.tbl_sel = user_gamma.tbl_sel; + gamma.tbl_size = user_gamma.tbl_size; + + if (user_gamma.tbl_sel == IPIPE_GAMMA_TBL_RAM) { + if (user_gamma.tbl_size == IPIPE_GAMMA_TBL_SZ_64) + table_size = 64; + else if (user_gamma.tbl_size == IPIPE_GAMMA_TBL_SZ_128) + table_size = 128; + else if (user_gamma.tbl_size == IPIPE_GAMMA_TBL_SZ_256) + table_size = 256; + else if (user_gamma.tbl_size == IPIPE_GAMMA_TBL_SZ_512) + table_size = 512; + else { + dev_err(dev, + "set_gamma_params: Invalid" + " table_size\n"); + return -EINVAL; + } + + if (!user_gamma.bypass_r) { + if (ISNULL(user_gamma.table_r)) { + dev_err(dev, + "set_gamma_params: Invalid" + " table ptr for R\n"); + return -EINVAL; + } + if (copy_from_user(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 (ISNULL(user_gamma.table_b)) { + dev_err(dev, + "set_gamma_params: Invalid" + " table ptr for B\n"); + return -EINVAL; + } + if (copy_from_user(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 (ISNULL(user_gamma.table_g)) { + dev_err(dev, + "set_gamma_params: Invalid" + " table ptr for G\n"); + return -EINVAL; + } + if (copy_from_user(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; + } + } + } + + if (validate_gamma_params(dev) < 0) + return -EINVAL; + } + + return ipipe_set_gamma_regs(&gamma); +} +static int get_gamma_params(struct device *dev, void *param, int len) +{ + struct prev_gamma *gamma_param = (struct prev_gamma *)param; + struct prev_gamma user_gamma; + int table_size; + + if (ISNULL(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 = gamma.bypass_r; + user_gamma.bypass_g = gamma.bypass_g; + user_gamma.bypass_b = gamma.bypass_b; + user_gamma.tbl_sel = gamma.tbl_sel; + user_gamma.tbl_size = gamma.tbl_size; + if (gamma.tbl_sel == IPIPE_GAMMA_TBL_RAM) { + if (gamma.tbl_size == IPIPE_GAMMA_TBL_SZ_128) + table_size = 128; + else if (gamma.tbl_size == IPIPE_GAMMA_TBL_SZ_256) + table_size = 256; + else if (gamma.tbl_size == IPIPE_GAMMA_TBL_SZ_512) + table_size = 512; + else + table_size = 0; + + if ((!gamma.bypass_r) && ((ISNULL(user_gamma.table_r)))) { + dev_err(dev, + "get_gamma_params: table ptr empty for R\n"); + return -EINVAL; + } else { + if (copy_to_user(user_gamma.table_r, + 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 ((!gamma.bypass_b) && ((ISNULL(user_gamma.table_b)))) { + dev_err(dev, + "get_gamma_params: table ptr empty for B\n"); + return -EINVAL; + } else { + if (copy_to_user(user_gamma.table_b, + 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 ((!gamma.bypass_g) && ((ISNULL(user_gamma.table_g)))) { + dev_err(dev, + "get_gamma_params: table ptr empty for G\n"); + return -EINVAL; + } else { + if (copy_from_user(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; + } + } + + } + 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) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + int i; + + if (lut_3d.en > 1) + return -1; + + if (lut_3d.en) { + for (i = 0; i < MAX_SIZE_3D_LUT; i++) { + if (lut_3d.table[i].r > D3_LUT_ENTRY_MASK || + lut_3d.table[i].g > D3_LUT_ENTRY_MASK || + lut_3d.table[i].b > D3_LUT_ENTRY_MASK) + return -1; + } + } +#endif + return 0; +} +static int set_3d_lut_params(struct device *dev, void *param, int len) +{ + struct prev_3d_lut *lut_param = (struct prev_3d_lut *)param; + struct prev_3d_lut user_3d_lut; + + if (ISNULL(lut_param)) { + /* Copy defaults for gamma */ + 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; + } + lut_3d.en = user_3d_lut.en; + if (ISNULL(user_3d_lut.table)) { + dev_err(dev, "set_3d_lut_params:" " Invalid table ptr"); + return -EINVAL; + } + if (copy_from_user(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) < 0) + return -EINVAL; + } + + return ipipe_set_3d_lut_regs(&lut_3d); +} +static int get_3d_lut_params(struct device *dev, void *param, int len) +{ + struct prev_3d_lut *lut_param = (struct prev_3d_lut *)param; + struct prev_3d_lut user_3d_lut; + + if (ISNULL(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 = lut_3d.en; + if (ISNULL(user_3d_lut.table)) { + dev_err(dev, "get_3d_lut_params:" " Invalid table ptr"); + return -EINVAL; + } + if (copy_to_user(user_3d_lut.table, 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 *param, int len) +{ + struct prev_lum_adj *lum_adj_param = (struct prev_lum_adj *)param; + + if (ISNULL(lum_adj_param)) { + /* Copy defaults for Luminance adjustments */ + memcpy((void *)&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(&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; + } + + return ipipe_set_lum_adj_regs(&lum_adj); +} + +static int get_lum_adj_params(struct device *dev, void *param, int len) +{ + struct prev_lum_adj *lum_adj_param = (struct prev_lum_adj *)param; + + if (ISNULL(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, + &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) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + if (rgb2yuv.coef_ry.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv.coef_ry.integer > RGB2YCBCR_COEF_INT_MASK) + return -1; + + if (rgb2yuv.coef_gy.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv.coef_gy.integer > RGB2YCBCR_COEF_INT_MASK) + return -1; + + if (rgb2yuv.coef_by.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv.coef_by.integer > RGB2YCBCR_COEF_INT_MASK) + return -1; + + if (rgb2yuv.coef_rcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv.coef_rcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -1; + + if (rgb2yuv.coef_gcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv.coef_gcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -1; + + if (rgb2yuv.coef_bcb.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv.coef_bcb.integer > RGB2YCBCR_COEF_INT_MASK) + return -1; + + if (rgb2yuv.coef_rcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv.coef_rcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -1; + + if (rgb2yuv.coef_gcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv.coef_gcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -1; + + if (rgb2yuv.coef_bcr.decimal > RGB2YCBCR_COEF_DECI_MASK || + rgb2yuv.coef_bcr.integer > RGB2YCBCR_COEF_INT_MASK) + return -1; + + if (rgb2yuv.out_ofst_y > RGB2YCBCR_OFST_MASK || + rgb2yuv.out_ofst_cb > RGB2YCBCR_OFST_MASK || + rgb2yuv.out_ofst_cr > RGB2YCBCR_OFST_MASK) + return -1; +#endif + return 0; +} +static int set_rgb2yuv_params(struct device *dev, void *param, int len) +{ + struct prev_rgb2yuv *rgb2yuv_param = (struct prev_rgb2yuv *)param; + + if (ISNULL(rgb2yuv_param)) { + /* Copy defaults for rgb2yuv conversion */ + memcpy((void *)&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(&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) < 0) + return -EINVAL; + } + + return ipipe_set_rgb2ycbcr_regs(&rgb2yuv); +} +static int get_rgb2yuv_params(struct device *dev, void *param, int len) +{ + struct prev_rgb2yuv *rgb2yuv_param = (struct prev_rgb2yuv *)param; + + if (ISNULL(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, + &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) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + u32 max = GBCE_Y_VAL_MASK; + int i; + + if (gbce.type == IPIPE_GBCE_GAIN_TBL) + max = GBCE_GAIN_VAL_MASK; + if (gbce.en > 1) + return -1; + if (gbce.en) { + for (i = 0; i < MAX_SIZE_GBCE_LUT; i++) + if (gbce.table[i] > max) + return -1; + } +#endif + return 0; +} +static int set_gbce_params(struct device *dev, void *param, int len) +{ + struct prev_gbce *gbce_param = (struct prev_gbce *)param; + struct prev_gbce user_gbce; + + if (ISNULL(gbce_param)) + /* Copy defaults for gamma */ + 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; + } + gbce.en = user_gbce.en; + gbce.type = user_gbce.type; + if (ISNULL(user_gbce.table)) { + dev_err(dev, "set_gbce_params:" " Invalid table ptr"); + return -EINVAL; + } + + if (copy_from_user(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) < 0) + return -EINVAL; + } + + return ipipe_set_gbce_regs(&gbce); +} +static int get_gbce_params(struct device *dev, void *param, int len) +{ + struct prev_gbce *gbce_param = (struct prev_gbce *)param; + struct prev_gbce user_gbce; + + if (ISNULL(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 = gbce.en; + user_gbce.type = gbce.type; + if (ISNULL(user_gbce.table)) { + dev_err(dev, "get_gbce_params:" " Invalid table ptr"); + return -EINVAL; + } + if (copy_to_user(user_gbce.table, + 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) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + if (yuv422_conv.en_chrom_lpf > 1) + return -1; +#endif + return 0; +} + +static int set_yuv422_conv_params(struct device *dev, void *param, int len) +{ + struct prev_yuv422_conv *yuv422_conv_param = + (struct prev_yuv422_conv *)param; + + if (ISNULL(yuv422_conv_param)) { + /* Copy defaults for yuv 422 conversion */ + memcpy((void *)&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(&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) < 0) + return -EINVAL; + } + + return ipipe_set_yuv422_conv_regs(&yuv422_conv); +} +static int get_yuv422_conv_params(struct device *dev, void *param, int len) +{ + struct prev_yuv422_conv *yuv422_conv_param = + (struct prev_yuv422_conv *)param; + + if (ISNULL(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, + &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) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + int i; + + if (yee.en > 1 || + yee.en_halo_red > 1 || + yee.hpf_shft > YEE_HPF_SHIFT_MASK) + return -1; + + if (yee.hpf_coef_00 > YEE_COEF_MASK || + yee.hpf_coef_01 > YEE_COEF_MASK || + yee.hpf_coef_02 > YEE_COEF_MASK || + yee.hpf_coef_10 > YEE_COEF_MASK || + yee.hpf_coef_11 > YEE_COEF_MASK || + yee.hpf_coef_12 > YEE_COEF_MASK || + yee.hpf_coef_20 > YEE_COEF_MASK || + yee.hpf_coef_21 > YEE_COEF_MASK || + yee.hpf_coef_22 > YEE_COEF_MASK) + return -1; + + if (yee.yee_thr > YEE_THR_MASK || + yee.es_gain > YEE_ES_GAIN_MASK || + yee.es_thr1 > YEE_ES_THR1_MASK || + yee.es_thr2 > YEE_THR_MASK || + yee.es_gain_grad > YEE_THR_MASK || + yee.es_ofst_grad > YEE_THR_MASK) + return -1; + + for (i = 0; i < MAX_SIZE_YEE_LUT ; i++) + if (yee.table[i] > YEE_ENTRY_MASK) + return -1; +#endif + return 0; +} +static int set_yee_params(struct device *dev, void *param, int len) +{ + struct prev_yee *yee_param = (struct prev_yee *)param; + struct prev_yee user_yee; + short *temp_table; + + if (ISNULL(yee_param)) { + temp_table = yee.table; + /* Copy defaults for ns */ + memcpy((void *)&yee, + (void *)&dm365_yee_defaults, + sizeof(struct prev_yee)); + 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 (ISNULL(user_yee.table)) { + dev_err(dev, "get_yee_params: yee table ptr null\n"); + return -EINVAL; + } + if (copy_from_user(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 = yee.table; + memcpy(&yee, &user_yee, sizeof(struct prev_yee)); + yee.table = temp_table; + if (validate_yee_params(dev) < 0) + return -EINVAL; + } + + return ipipe_set_ee_regs(&yee); +} +static int get_yee_params(struct device *dev, void *param, int len) +{ + struct prev_yee *yee_param = (struct prev_yee *)param; + struct prev_yee user_yee; + short *temp_table; + + if (ISNULL(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 (ISNULL(user_yee.table)) { + dev_err(dev, "get_yee_params: yee table ptr null\n"); + return -EINVAL; + } + if (copy_to_user(user_yee.table, 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, &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) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + if (car.en > 1 || + car.hpf_shft > CAR_HPF_SHIFT_MASK || + car.gain1.shft > CAR_GAIN1_SHFT_MASK || + car.gain1.gain_min > CAR_GAIN_MIN_MASK || + car.gain2.shft > CAR_GAIN2_SHFT_MASK || + car.gain2.gain_min > CAR_GAIN_MIN_MASK) + return -1; +#endif + return 0; +} + +static int set_car_params(struct device *dev, void *param, int len) +{ + struct prev_car *car_param = (struct prev_car *)param; + + if (ISNULL(car_param)) { + /* Copy defaults for ns */ + memcpy((void *)&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(&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) < 0) + return -EINVAL; + } + + return ipipe_set_car_regs(&car); +} +static int get_car_params(struct device *dev, void *param, int len) +{ + struct prev_car *car_param = (struct prev_car *)param; + + if (ISNULL(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, &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) +{ +#ifdef CONFIG_IPIPE_PARAM_VALIDATION + if (cgs.en > 1 || + cgs.h_shft > CAR_SHIFT_MASK) + return -1; +#endif + return 0; +} + +static int set_cgs_params(struct device *dev, void *param, int len) +{ + struct prev_cgs *cgs_param = (struct prev_cgs *)param; + + if (ISNULL(cgs_param)) { + /* Copy defaults for ns */ + memcpy((void *)&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(&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) < 0) + return -EINVAL; + } + + return ipipe_set_cgs_regs(&cgs); +} +static int get_cgs_params(struct device *dev, void *param, int len) +{ + struct prev_cgs *cgs_param = (struct prev_cgs *)param; + + if (ISNULL(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, &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_enum_preview_cap(struct device *dev, + int index) +{ + dev_dbg(dev, "prev_enum_preview_cap: index = %d\n", index); + + if ((index + 1) > PREV_MAX_MODULES) + return NULL; + + return &prev_modules[index]; +} + +static int ipipe_set_oper_mode(unsigned int mode) +{ + if (oper_mode == IMP_MODE_NOT_CONFIGURED) + oper_mode = mode; + else { + printk(KERN_ERR "IPIPE is already active!\n"); + return -1; + } + + return 0; +} + +static void ipipe_reset_oper_mode(void) +{ + 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; +} + +static unsigned int prev_get_oper_mode(void) +{ + return oper_mode; +} + +static unsigned int ipipe_get_oper_state(void) +{ + return oper_state.state; +} + +static void ipipe_set_oper_state(unsigned int state) +{ + mutex_lock(&oper_state.lock); + oper_state.state = state; + mutex_unlock(&oper_state.lock); +} + +static unsigned int ipipe_get_prev_config_state(void) +{ + return oper_state.prev_config_state; +} + +static unsigned int ipipe_get_rsz_config_state(void) +{ + 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 -1; + } + + rsz = (input_width << 8) / output_width; + val = rsz << 1; + val1 = ((input_width << 8) / val) + 1; + o = 14; + if (!(val1 % 2)) { + h1 = val1; + } else { + val1 = (input_width << 7); + val1 -= (rsz >> 1); + val1 /= (rsz << 1); + val1 <<= 1; + val1 += 2; + o += ((CEIL(rsz, 1024)) << 1); + h1 = val1; + } + 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 -1; + } + two_power = (1 << n); + rsz = (input_width << 8) / output_width; + val = rsz * two_power; + val1 = ((upper_h1 << 8) / val) + 1; + if (!(val1 % 2)) + h1 = val1; + else { + val1 = (upper_h1 << 8); + val1 >>= (n + 1); + val1 -= (rsz >> 1); + val1 /= (rsz << 1); + val1 <<= 1; + val1 += 2; + h1 = val1; + } + 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 -1; + } + 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) +{ + /* 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 *config_block) +{ + if (config_block) { + 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 *config_block) +{ + kfree(config_block); +} + +static void *ipipe_alloc_user_config_block(struct device *dev, + enum imp_log_chan_t chan_type, + int *len) +{ + void *config = NULL; + + if (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); + } + + } else { + 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, + enum imp_log_chan_t chan_type, + void *config) +{ + dev_dbg(dev, "ipipe_set_user_config_defaults\n"); + + if (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_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 bytesperline = 2; + int image_height; + int image_width; + int yuv_420; + int offset; + + if (!param->rsz_en[index]) + return -1; + + 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)) { + if ((param->rsz_rsc_param[index].cen) && + (param->rsz_rsc_param[index].yen)) { + /* YUV 420 */ + yuv_420 = 1; + bytesperline = 1; + } + } + + /* set offset value */ + offset = 0; + + if (param->rsz_rsc_param[index].h_flip) + /* width * bytesperline - 1 */ + offset = (image_width * bytesperline) - 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 * bytesperline - 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, enum enable_disable_t en) +{ + /* Enable 422 to 420 conversion */ + param->rsz_rsc_param[index].cen = en; + param->rsz_rsc_param[index].yen = en; +} + +static int configure_resizer_out_params(struct ipipe_params *param, + int index, + void *output_spec, + unsigned char partial, unsigned flag) +{ + if (partial) { + struct rsz_part_output_spec *partial_output = + (struct rsz_part_output_spec *)output_spec; + if (partial_output->enable) { + 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; + + } else + param->rsz_en[index] = DISABLE; + + } else { + struct rsz_output_spec *output = + (struct rsz_output_spec *)output_spec; + if (output->enable) { + param->rsz_en[index] = ENABLE; + param->rsz_rsc_param[index].o_vsz = output->height - 1; + param->rsz_rsc_param[index].o_hsz = output->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 = + output->height; + param->ext_mem_param[index].rsz_sdr_ptr_s_c = + output->vst_c; + param->ext_mem_param[index].rsz_sdr_ptr_e_c = + output->height; + + if (flag) { + /* 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; + } + } else + param->rsz_en[index] = DISABLE; + } + + return 0; +} + +/* function :calculate_line_length() + * This function calculates the line length of various image + * planes at the input and output + */ +static int calculate_line_length(enum ipipe_pix_formats pix, + int width, + int height, int *line_len, int *line_len_c) +{ + *line_len = 0; + *line_len_c = 0; + + if ((pix == IPIPE_UYVY) || (pix == IPIPE_BAYER)) + *line_len = width << 1; + else if (pix == IPIPE_420SP_Y || pix == IPIPE_420SP_C) { + *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; + + return 0; +} + +static inline int rsz_validate_input_image_format(struct device *dev, + enum ipipe_pix_formats pix, + int width, + int height, int *line_len) +{ + int val; + + if (pix != IPIPE_UYVY && pix != IPIPE_420SP_Y && pix != IPIPE_420SP_C) { + 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; + } + + if (pix == IPIPE_420SP_C) + return calculate_line_length(pix, + width, + height, + &val, + line_len); + else + return calculate_line_length(pix, + width, + height, + line_len, + &val); +} + +static inline int rsz_validate_output_image_format(struct device *dev, + enum ipipe_pix_formats pix, + int width, + int height, + int *in_line_len, + int *in_line_len_c) +{ + if (pix != IPIPE_UYVY + && pix != IPIPE_420SP_Y + && pix != IPIPE_420SP_C + && pix != IPIPE_YUV420SP && pix != IPIPE_BAYER) { + 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; + } + + return calculate_line_length(pix, + width, + height, in_line_len, in_line_len_c); +} + +static int 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; + + return 0; +} + +static int configure_common_rsz_in_params(struct device *dev, + struct ipipe_params *param, + int flag, int rsz_chained, + void *input_spec) +{ + enum ipipe_pix_formats pix; + + if (!flag) { + struct prev_ss_input_spec *in_specs = + (struct prev_ss_input_spec *)input_spec; + param->rsz_common.vsz = in_specs->image_height - 1; + param->rsz_common.hsz = in_specs->image_width - 1; + pix = in_specs->pix_fmt; + } 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 = in_specs->image_height - 1; + param->rsz_common.hsz = in_specs->image_width - 1; + pix = in_specs->pix_fmt; + } + switch (pix) { + case IPIPE_BAYER_8BIT_PACK: + case IPIPE_BAYER_8BIT_PACK_ALAW: + case IPIPE_BAYER_8BIT_PACK_DPCM: + case IPIPE_BAYER_12BIT_PACK: + case IPIPE_BAYER: + { + param->rsz_common.src_img_fmt = RSZ_IMG_422; + param->rsz_common.source = IPIPE_DATA; + break; + } + case IPIPE_UYVY: + { + 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 IPIPE_420SP_Y: + { + 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 IPIPE_420SP_C: + { + 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 -1; + } + + 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) { + 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 configure_resizer_in_ss_mode(struct device *dev, + 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; + int line_len_c; + int line_len; + int ret; + + ret = rsz_validate_input_image_format(dev, + ss_config->input.pix_fmt, + ss_config->input.image_width, + ss_config->input.image_height, + &line_len); + + if (ret) + return -EINVAL; + + /* shared block */ + if ((!ss_config->output1.enable) && (!ss_config->output2.enable)) { + dev_err(dev, "One of the resizer output must be enabled\n"); + 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->output1.enable) { + param->rsz_en[RSZ_A] = ENABLE; + param->rsz_rsc_param[RSZ_A].mode = ONE_SHOT; + ret = rsz_validate_output_image_format(dev, + ss_config->output1. + pix_fmt, + ss_config->output1.width, + ss_config->output1. + 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; + ret = configure_resizer_out_params(param, + RSZ_A, + &ss_config->output1, + 0, + 1); + + if (ss_config->output1.pix_fmt == IMP_BAYER) + param->rsz_common.raw_flip = 1; + else + param->rsz_common.raw_flip = 0; + + if (ss_config->output1.pix_fmt == IPIPE_YUV420SP) + enable_422_420_conversion(param, RSZ_A, ENABLE); + else + enable_422_420_conversion(param, RSZ_A, + DISABLE); + if (ret) { + mutex_unlock(&oper_state.lock); + dev_err(dev, "error configuring resizer output1\n"); + return ret; + } + } + + if (ss_config->output2.enable) { + param->rsz_en[RSZ_A] = ENABLE; + param->rsz_rsc_param[RSZ_B].mode = ONE_SHOT; + ret = rsz_validate_output_image_format(dev, + ss_config->output2. + pix_fmt, + ss_config->output2.width, + ss_config->output2. + 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; + ret = configure_resizer_out_params(param, + RSZ_B, + &ss_config->output2, + 0, + 1); + if (ret) { + dev_err(dev, + "error configuring resizer output2 params\n"); + mutex_unlock(&oper_state.lock); + return ret; + } + if (ss_config->output2.pix_fmt == IPIPE_YUV420SP) + 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, + ss_config->input.image_width); + if (ret) { + mutex_unlock(&oper_state.lock); + return ret; + } + + if (ipipe_process_pix_fmts(ss_config->input.pix_fmt, + ss_config->output1.pix_fmt, + param) < 0) { + dev_err(dev, "error in input or output pix format\n"); + mutex_unlock(&oper_state.lock); + return -EINVAL; + } + + param->ipipeif_param.source = 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 = ss_config->input.image_width; + param->ipipeif_param.vnum = ss_config->input.image_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 = + (((ss_config->input.image_width * + IPIPEIF_RSZ_CONST) / ss_config->input.rsz) - 1); + } + if (ss_config->input.pix_fmt == IPIPE_420SP_Y + || ss_config->input.pix_fmt == IPIPE_420SP_C) { + param->ipipeif_param.var.if_5_1.pack_mode + = IPIPEIF_5_1_PACK_8_BIT; + param->ipipeif_param.var.if_5_1.source1 = 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 = SDRAM_RAW; + + + } + if (ss_config->input.pix_fmt == IPIPE_420SP_C) + param->ipipeif_param.var.if_5_1.isif_port.if_type + = V4L2_MBUS_FMT_SBGGR10_1X10; + param->ipipe_hsz = ss_config->input.image_width - 1; + param->ipipe_vsz = ss_config->input.image_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, 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 (ss_config->input.pix_fmt == IPIPE_420SP_C) { + 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 (ss_config->input.pix_fmt == IPIPE_420SP_C) { + 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, + 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) { + ret = mutex_lock_interruptible(&oper_state.lock); + if (ret) + return ret; + if (!cont_config->output1.enable) { + 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 = CONTINUOUS; + ret = configure_resizer_out_params(param, + RSZ_A, + &cont_config->output1, + 1, + 0); + if (ret) { + dev_err(dev, "error configuring resizer output1\n"); + mutex_unlock(&oper_state.lock); + return -EINVAL; + } + param->rsz_en[RSZ_B] = DISABLE; + + if (cont_config->output2.enable) { + param->rsz_rsc_param[RSZ_B].mode = CONTINUOUS; + ret = rsz_validate_output_image_format(dev, + cont_config->output2. + pix_fmt, + cont_config->output2. + width, + cont_config->output2. + 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; + ret = configure_resizer_out_params(param, + RSZ_B, + &cont_config->output2, + 0, + 1); + if (cont_config->output2.pix_fmt == IPIPE_YUV420SP) + enable_422_420_conversion(param, + RSZ_B, ENABLE); + else + enable_422_420_conversion(param, + RSZ_B, DISABLE); + + if (ret) { + dev_err(dev, "error configuring resizer" + " output2\n"); + mutex_unlock(&oper_state.lock); + return -EINVAL; + } + } + oper_state.rsz_chained = 1; + oper_state.rsz_config_state = STATE_CONFIGURED; + mutex_unlock(&oper_state.lock); + } else { + dev_err(dev, "Resizer cannot be configured in standalone" + "for continuous mode\n"); + return -EINVAL; + } + + return 0; +} +static int ipipe_set_resize_config(struct device *dev, + int resizer_chained, + void *user_config, void *config) +{ + struct ipipe_params *param = (struct ipipe_params *)config; + int ret; + + dev_dbg(dev, "ipipe_set_resize_config, resizer_chained = %d\n", + resizer_chained); + if ((ISNULL(user_config)) || (ISNULL(config))) { + dev_err(dev, "Invalid user_config or config ptr\n"); + return -EINVAL; + } + + memcpy((void *)config, + (void *)&dm365_ipipe_defs, + sizeof(struct ipipe_params)); + + if (oper_mode == IMP_MODE_SINGLE_SHOT) { + ret = configure_resizer_in_ss_mode(dev, + user_config, + resizer_chained, + param); + if (!ret && (!en_serializer && !resizer_chained)) + ret = ipipe_hw_setup(config); + } else + ret = configure_resizer_in_cont_mode(dev, + user_config, + resizer_chained, + param); + + 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 ipipe_pix_formats pix, + int *line_len, + int in_width, int in_height) +{ + if (pix != IPIPE_UYVY && pix != IPIPE_BAYER) { + 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 ipipe_pix_formats pix, + int width, + int height, int *line_len) +{ + if (pix != IPIPE_UYVY + && pix != IPIPE_BAYER + && pix != IPIPE_BAYER_8BIT_PACK + && pix != IPIPE_BAYER_8BIT_PACK_ALAW + && pix != IPIPE_BAYER_8BIT_PACK_DPCM + && pix != IPIPE_BAYER_12BIT_PACK) { + 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 == IPIPE_UYVY || pix == IPIPE_BAYER) + *line_len = width * 2; + else if (pix == IPIPE_BAYER_8BIT_PACK || + pix == IPIPE_BAYER_8BIT_PACK_ALAW || + pix == IPIPE_BAYER_8BIT_PACK_DPCM) + *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, + 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 suV4L2_MBUS_FMT_SBGGR10_1X10btraction is not supported\n"); + return -EINVAL; + } + if (cont_config->input.dec_en) { + if ((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 = CCDC; + param->ipipeif_param.clock_select = PIXCEL_CLK; + param->ipipeif_param.mode = 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 = 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 = 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, + void *user_config, + struct ipipe_params *param) +{ + struct prev_single_shot_config *ss_config = + (struct prev_single_shot_config *)user_config; + int line_len; + int ret; + + ret = validate_preview_input_spec(dev, + ss_config->input.pix_fmt, + ss_config->input.image_width, + ss_config->input.image_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, + ss_config->input.image_width); + if (ret) { + mutex_unlock(&oper_state.lock); + return -EINVAL; + } + + if (!oper_state.rsz_chained) { + ret = prev_validate_output_image_format(dev, + ss_config->output. + pix_fmt, &line_len, + ss_config->input. + image_width, + ss_config->input. + image_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, + ss_config->input. + image_width, + ss_config->input. + image_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, + ss_config->input. + image_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, + ss_config-> + input.image_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, + ss_config->input. + image_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, + ss_config->input. + image_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(ss_config->input.pix_fmt, + ss_config->output.pix_fmt, + param) < 0) { + dev_err(dev, "error in input or output pix format\n"); + mutex_unlock(&oper_state.lock); + return -EINVAL; + } + param->ipipeif_param.hnum = ss_config->input.image_width; + param->ipipeif_param.vnum = ss_config->input.image_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 = DPCM_8BIT_12BIT; + } + param->ipipeif_param.var.if_5_1.data_shift = + ss_config->input.data_shift; + + param->ipipe_hsz = ss_config->input.image_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 = + (((ss_config->input.image_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 = ss_config->input.image_height - 1; + if (ss_config->input.pix_fmt == IPIPE_UYVY) + param->ipipeif_param.source = SDRAM_YUV; + else + param->ipipeif_param.source = SDRAM_RAW; + + configure_common_rsz_in_params(dev, 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 (ISNULL(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->enable = 1; + output_specs->pix_fmt = IPIPE_UYVY; + output_specs->width = ss_config->input.image_width; + output_specs->height = ss_config->input.image_height; + output_specs->vst_y = ss_config->input.vst; + configure_resizer_out_params(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_reconfig_resizer(struct device *dev, + struct rsz_reconfig *reconfig, + void *config) +{ + struct ipipe_params *param = (struct ipipe_params *)config; + + if (ISNULL(reconfig)) { + dev_err(dev, "Null User ptr received for reconfig"); + return -EINVAL; + } + + if ((reconfig->pix_format != IMP_420SP_Y) && + (reconfig->pix_format != IMP_420SP_C)) { + dev_err(dev, "reconfig - pixel format incorrect"); + return -EINVAL; + } + if (param->rsz_common.src_img_fmt != RSZ_IMG_420) { + dev_err(dev, "reconfig - source format originally" + "configured is not YUV420SP\n"); + return -EINVAL; + } + if ((param->rsz_common.y_c) && (reconfig->pix_format == IMP_420SP_C)) { + dev_err(dev, "reconfig - channel is already configured" + "for YUV420SP - C data\n"); + return -EINVAL; + } + if ((!param->rsz_common.y_c) && (reconfig->pix_format == IMP_420SP_Y)) { + dev_err(dev, "reconfig - channel is already configured" + "for YUV420SP - Y data\n"); + return -EINVAL; + } + if (reconfig->pix_format == IMP_420SP_Y) + param->rsz_common.y_c = 0; + else + param->rsz_common.y_c = 1; + if (!en_serializer) + rsz_set_in_pix_format(param->rsz_common.y_c); + + return 0; +} + +static int ipipe_set_preview_config(struct device *dev, + void *user_config, void *config) +{ + struct ipipe_params *param = (struct ipipe_params *)config; + int ret; + + dev_err(dev, "ipipe_set_preview_config\n"); + + if ((ISNULL(user_config)) || (ISNULL(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)); + } + + /* shared block */ + if (oper_mode == IMP_MODE_SINGLE_SHOT) { + /* 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, user_config, param); + + if ((!ret) && !en_serializer) + ret = ipipe_hw_setup(config); + } else + ret = configure_previewer_in_cont_mode(dev, user_config, param); + /* continuous mode */ + + return ret; +} +struct imp_hw_interface *imp_get_hw_if(void) +{ + return &dm365_ipipe_interface; +} + +static int ipipe_set_input_win(struct imp_window *win) +{ + struct ipipe_params *param = oper_state.shared_config_param; + int ret; + + ret = mutex_lock_interruptible(&oper_state.lock); + if (ret) + return ret; + 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.frame_format) { + 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; + mutex_unlock(&oper_state.lock); + + return 0; +} +static int ipipe_get_input_win(struct imp_window *win) +{ + struct ipipe_params *param = oper_state.shared_config_param; + int ret; + + ret = mutex_lock_interruptible(&oper_state.lock); + if (ret) + return ret; + if (param->ipipeif_param.decimation) + win->width = + (((param->ipipe_hsz + 1) * param->ipipeif_param.rsz) >> 4); + else + win->width = param->ipipe_hsz + 1; + if (!oper_state.frame_format) { + win->height = (param->ipipe_vsz + 1) << 1; + win->vst = (param->ipipe_vps << 1); + } else { + win->height = param->ipipe_vsz + 1; + win->vst = param->ipipe_vps; + } + win->hst = param->ipipe_hps; + mutex_unlock(&oper_state.lock); + + return 0; +} + +static int ipipe_set_in_pixel_format(enum imp_pix_formats pix_fmt) +{ + struct ipipe_params *param = oper_state.shared_config_param; + int ret; + + ret = mutex_lock_interruptible(&oper_state.lock); + if (ret) + return ret; + oper_state.in_pixel_format = pix_fmt; + param->rsz_common.src_img_fmt = RSZ_IMG_422; + mutex_unlock(&oper_state.lock); + + return 0; +} + +static int ipipe_set_out_pixel_format(enum imp_pix_formats pix_fmt) +{ + struct ipipe_params *param = oper_state.shared_config_param; + int err; + + /* if image is RAW, preserve raw image format while flipping. + * otherwise preserve, preserve ycbcr format while flipping + */ + if (pix_fmt == IMP_BAYER) + param->rsz_common.raw_flip = 1; + else + param->rsz_common.raw_flip = 0; + + err = mutex_lock_interruptible(&oper_state.lock); + if (err) + return err; + oper_state.out_pixel_format = pix_fmt; + err = ipipe_process_pix_fmts(oper_state.in_pixel_format, + oper_state.out_pixel_format, + param); + + mutex_unlock(&oper_state.lock); + + return err; +} + +static int ipipe_set_buftype(unsigned char buf_type) +{ + int ret; + + ret = mutex_lock_interruptible(&oper_state.lock); + if (ret) + return ret; + oper_state.buffer_type = buf_type; + mutex_unlock(&oper_state.lock); + + return 0; +} + +static int ipipe_set_frame_format(unsigned char frm_fmt) +{ + int ret; + + ret = mutex_lock_interruptible(&oper_state.lock); + if (ret) + return ret; + oper_state.frame_format = frm_fmt; + mutex_unlock(&oper_state.lock); + + return 0; +} + +static int ipipe_set_output_win(struct imp_window *win) +{ + struct ipipe_params *param = oper_state.shared_config_param; + struct rsz_output_spec output_specs; + int line_len_c; + int line_len; + int ret = -EINVAL; + + if (!param->rsz_en[0]) { + printk(KERN_ERR "resizer output1 not enabled\n"); + return ret; + } + output_specs.enable = 1; + output_specs.width = win->width; + /* Always set output height same as in height + for de-interlacing + */ + output_specs.height = win->height; + output_specs.vst_y = win->vst; + if (oper_state.out_pixel_format == IPIPE_YUV420SP) + output_specs.vst_c = win->vst; + ret = mutex_lock_interruptible(&oper_state.lock); + if (ret) + return ret; + ret = configure_resizer_out_params(param, RSZ_A, &output_specs, 0, 0); + if (ret) { + printk(KERN_ERR "error in configuring output1\n"); + mutex_unlock(&oper_state.lock); + return ret; + } + ret |= calculate_line_length(oper_state.out_pixel_format, + param->rsz_rsc_param[0].o_hsz + 1, + param->rsz_rsc_param[0].o_vsz + 1, + &line_len, + &line_len_c); + if (ret) { + printk(KERN_ERR "error in calculating line length\n"); + mutex_unlock(&oper_state.lock); + return ret; + } + 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_pixel_format == IPIPE_YUV420SP) + 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"); + mutex_unlock(&oper_state.lock); + + return ret; +} +static int ipipe_get_output_state(unsigned char out_sel) +{ + 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(unsigned char out_sel) +{ + struct ipipe_params *param = oper_state.shared_config_param; + + if ((out_sel != RSZ_A) && (out_sel != RSZ_B)) + return -1; + /* assume output is always UYVY. Change this if we + * support RGB + */ + if (!param->rsz_en[out_sel]) + return -1; + + return param->ext_mem_param[out_sel].rsz_sdr_oft_y; +} + +static int ipipe_get_image_height(unsigned char out_sel) +{ + struct ipipe_params *param = oper_state.shared_config_param; + + if ((out_sel != RSZ_A) && (out_sel != RSZ_B)) + return -1; + if (!param->rsz_en[out_sel]) + return -1; + + return param->rsz_rsc_param[out_sel].o_vsz + 1; +} + +/* Assume valid param ptr */ +static int ipipe_set_hw_if_param(struct vpfe_hw_if_param *if_param) +{ + struct ipipe_params *param = oper_state.shared_config_param; + int ret; + + ret = mutex_lock_interruptible(&oper_state.lock); + if (ret) + return ret; + param->ipipeif_param.var.if_5_1.isif_port = *if_param; + mutex_unlock(&oper_state.lock); + + return 0; +} + +void enable_serializer(int val) +{ + en_serializer = val; +} + +int ipipe_init(void) +{ + oper_state.shared_config_param = + kmalloc(sizeof(struct ipipe_params), GFP_KERNEL); + + if (ISNULL(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)); + lutdpc.table = ipipe_lutdpc_table; + lut_3d.table = ipipe_3d_lut_table; + gbce.table = ipipe_gbce_table; + gamma.table_r = ipipe_gamma_table_r; + gamma.table_b = ipipe_gamma_table_b; + gamma.table_g = ipipe_gamma_table_g; + yee.table = 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.frame_format = 1; + oper_state.in_pixel_format = IMP_BAYER; + oper_state.out_pixel_format = IMP_UYVY; + + return 0; +} + +void ipipe_cleanup(void) +{ + kfree(oper_state.shared_config_param); +} diff --git a/include/media/davinci/dm365_ipipe.h b/include/media/davinci/dm365_ipipe.h new file mode 100644 index 0000000..a53848b --- /dev/null +++ b/include/media/davinci/dm365_ipipe.h @@ -0,0 +1,1353 @@ +/* +* 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 + +/********************************************************************** +** 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, + IPIPE_GAMMA_TBL_SZ_128, + IPIPE_GAMMA_TBL_SZ_256, + IPIPE_GAMMA_TBL_SZ_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; +}; + +/* various pixel formats supported */ +enum ipipe_pix_formats { + IPIPE_BAYER_8BIT_PACK, + IPIPE_BAYER_8BIT_PACK_ALAW, + IPIPE_BAYER_8BIT_PACK_DPCM, + IPIPE_BAYER_12BIT_PACK, + IPIPE_BAYER, /* 16 bit */ + IPIPE_UYVY, + IPIPE_YUYV, + IPIPE_RGB565, + IPIPE_RGB888, + IPIPE_YUV420SP, + IPIPE_420SP_Y, + IPIPE_420SP_C +}; + +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 { + /* width of the image in SDRAM. */ + unsigned int image_width; + /* height of the image in SDRAM */ + unsigned int image_height; + /* 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; + /* Input pixels formats + */ + enum ipipe_pix_formats pix_fmt; + /* 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_ss_output_spec { + /* output pixel format */ + enum ipipe_pix_formats pix_fmt; +}; + +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; + /* Output specification for the image data */ + struct prev_ss_output_spec output; +}; + +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 { + /* width of the image in SDRAM. */ + unsigned int image_width; + /* height of the image in SDRAM */ + unsigned int image_height; + /* 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; + /* Input pixels formats + */ + enum ipipe_pix_formats pix_fmt; +}; + +struct rsz_output_spec { + /* enable the resizer output */ + unsigned char enable; + /* output pixel format. Has to be UYVY */ + enum ipipe_pix_formats pix_fmt; + /* enable horizontal flip */ + unsigned char h_flip; + /* enable vertical flip */ + unsigned char v_flip; + /* width in pixels. must be multiple of 16. */ + unsigned int width; + /* height in lines */ + unsigned int height; + /* 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 the resizer output */ + unsigned char enable; + /* 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; +}; + +#ifdef __KERNEL__ +#include + +/* 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; +}; + +enum enable_disable_t { + DISABLE, + ENABLE +}; + +#define CEIL(a, b) (((a) + (b-1)) / (b)) +#define IPIPE_MAX_PASSES 2 + +struct f_div_pass { + unsigned int o_hsz; + unsigned int i_hps; + unsigned int h_phs; + unsigned int src_hps; + unsigned int src_hsz; +}; + +struct f_div_param { + unsigned char en; + unsigned int num_passes; + struct f_div_pass pass[IPIPE_MAX_PASSES]; +}; + +/* Resizer Rescale Parameters*/ +struct ipipe_rsz_rescale_param { + enum ipipe_oper_mode mode; + enum enable_disable_t h_flip; + enum enable_disable_t v_flip; + enum enable_disable_t cen; + enum enable_disable_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; + enum enable_disable_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 { + enum enable_disable_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; + enum enable_disable_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]; + enum enable_disable_t rsz_en[2]; +}; + +int ipipe_set_d2f_regs(unsigned int id, struct prev_nf *noise_filter); +int ipipe_set_rgb2rgb_regs(unsigned int id, struct prev_rgb2rgb *rgb); +int rsz_set_output_address(struct ipipe_params *params, + int resize_no, unsigned int address); +int ipipe_set_yuv422_conv_regs(struct prev_yuv422_conv *conv); +int ipipe_set_lum_adj_regs(struct prev_lum_adj *lum_adj); +int ipipe_set_rgb2ycbcr_regs(struct prev_rgb2yuv *yuv); +int ipipe_set_lutdpc_regs(struct prev_lutdpc *lutdpc); +int ipipe_set_otfdpc_regs(struct prev_otfdpc *otfdpc); +int ipipe_set_3d_lut_regs(struct prev_3d_lut *lut_3d); +int ipipe_set_gamma_regs(struct prev_gamma *gamma); +int ipipe_hw_setup(struct ipipe_params *config); +int ipipe_set_gbce_regs(struct prev_gbce *gbce); +int ipipe_set_gic_regs(struct prev_gic *gic); +int ipipe_set_cfa_regs(struct prev_cfa *cfa); +int ipipe_set_car_regs(struct prev_car *car); +int ipipe_set_cgs_regs(struct prev_cgs *cgs); +int rsz_set_in_pix_format(unsigned char y_c); +int ipipe_set_ee_regs(struct prev_yee *ee); +int ipipe_set_wb_regs(struct prev_wb *wb); +int rsz_enable(int rsz_id, int enable); +void rsz_src_enable(int enable); + +#endif +#endif diff --git a/include/media/davinci/imp_common.h b/include/media/davinci/imp_common.h new file mode 100644 index 0000000..ebd4d22 --- /dev/null +++ b/include/media/davinci/imp_common.h @@ -0,0 +1,231 @@ +/* +* 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 + +#define IMP_MODE_CONTINUOUS 0 +#define IMP_MODE_SINGLE_SHOT 1 +#define IMP_MODE_INVALID 2 +#define IMP_MODE_NOT_CONFIGURED 4 +#define IMP_MAX_NAME_SIZE 40 + +enum imp_data_paths { + IMP_RAW2RAW = 1, + IMP_RAW2YUV = 2, + IMP_YUV2YUV = 4 +}; + +enum imp_pix_formats { + IMP_BAYER_8BIT_PACK, + IMP_BAYER_8BIT_PACK_ALAW, + IMP_BAYER_8BIT_PACK_DPCM, + IMP_BAYER_12BIT_PACK, + IMP_BAYER, /* 16 bit */ + IMP_UYVY, + IMP_YUYV, + IMP_RGB565, + IMP_RGB888, + IMP_YUV420SP, + IMP_420SP_Y, + IMP_420SP_C, +}; + +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 imp_pix_formats pix_format; +}; + +/* 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) + +#ifdef __KERNEL__ +#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) + +/* 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 +#endif diff --git a/include/media/davinci/imp_hw_if.h b/include/media/davinci/imp_hw_if.h new file mode 100644 index 0000000..4f76a2a --- /dev/null +++ b/include/media/davinci/imp_hw_if.h @@ -0,0 +1,177 @@ +/* +* 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 + +#ifdef __KERNEL__ +#include +#include + +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 *param, int len); + int (*get)(struct device *dev, 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, + int index); + /* + * set operating mode for IPIPE; 1-single shot, 0-continous + */ + int (*set_oper_mode) (unsigned int mode); + /* + * reset operating mode for IPIPE; + */ + void (*reset_oper_mode) (void); + /* + * get IPIPE operation mode + */ + unsigned int (*get_oper_mode) (void); + /* 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); + /* set hw state */ + void (*set_hw_state) (unsigned int state); + /* is resizer chained ? */ + unsigned int (*resizer_chain) (void); + /* this is used to lock shared resource */ + void (*lock_chain) (void); + /* this is used unlock shared resouce */ + void (*unlock_chain) (void); + /* Allocate a shared or exclusive config block for hardware + * configuration + */ + void *(*alloc_config_block) (struct device *dev); + /* hw serialization enabled ?? */ + int (*serialize) (void); + /* De-allocate the exclusive config block */ + void (*dealloc_config_block) (struct device *dev, void *config); + /* Allocate a user confguration block */ + void *(*alloc_user_config_block) (struct device *dev, + enum imp_log_chan_t chan_type, + int *len); + + /* de-allocate user config block */ + void (*dealloc_user_config_block) (struct device *dev, void *config); + + /* set default configuration in the config block */ + void (*set_user_config_defaults) (struct device *dev, + enum imp_log_chan_t chan_type, + void *user_config); + /* set user configuration for preview */ + int (*set_preview_config) (struct device *dev, + void *user_config, void *config); + /* set user configuration for resize */ + int (*set_resizer_config) (struct device *dev, + int resizer_chained, + void *user_config, void *config); + + int (*reconfig_resizer) (struct device *dev, + 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 + */ + int (*update_outbuf1_address) (void *config, unsigned int address); + /* update output buffer address for a channel + * if config is NULL, the shared config is assumed + */ + int (*update_outbuf2_address) (void *config, unsigned int address); + /* enable or disable hw */ + void (*enable) (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 *config); + /* Get configuration state of resizer in continuous mode */ + unsigned int (*get_resizer_config_state) (void); + /* Get configuration state of previewer in continuous mode */ + unsigned int (*get_previewer_config_state) (void); + + /* Below APIs assume we are using shared configuration since + * oper mode is continuous + */ + /* Set the input crop window at the IMP interface and IMP */ + int (*set_input_win) (struct imp_window *win); + /* Get current input crop window param at the IMP */ + int (*get_input_win) (struct imp_window *win); + /* Set interface parameter at IPIPEIF. Only valid for DM360 */ + int (*set_hw_if_param) (struct vpfe_hw_if_param *param); + /* Set input pixel format */ + int (*set_in_pixel_format) (enum imp_pix_formats pix_fmt); + /* set output pixel format */ + int (*set_out_pixel_format) (enum imp_pix_formats pix_fmt); + /* 0 - interleaved, 1 - field seperated */ + int (*set_buftype) (unsigned char buf_type); + /* 0 - interlaced, 1 - progressive */ + int (*set_frame_format) (unsigned char frm_fmt); + /* Set the output window at the IMP, output selection + * done by out_sel. 0 - output 1 and 1 - output 2 + */ + int (*set_output_win) (struct imp_window *win); + /* Get output enable/disable status */ + int (*get_output_state) (unsigned char out_sel); + /* Get output line lenght */ + int (*get_line_length) (unsigned char out_sel); + /* Get the output image height */ + int (*get_image_height) (unsigned char out_sel); + /* Get current output window param at the IMP */ + int (*get_output_win) (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) (u32 *output_pix, int index); +}; + +struct imp_hw_interface *imp_get_hw_if(void); + +#endif +#endif -- 1.6.2.4 From sakari.ailus at iki.fi Thu Jun 30 08:57:36 2011 From: sakari.ailus at iki.fi (Sakari Ailus) Date: Thu, 30 Jun 2011 16:57:36 +0300 Subject: [ RFC PATCH 0/8] RFC for Media Controller capture driver for DM365 In-Reply-To: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <20110630135736.GK12671@valkosipuli.localdomain> Hi Manjunath, Thanks for the patches. On Thu, Jun 30, 2011 at 06:43:09PM +0530, Manjunath Hadli wrote: > Thease are the RFC patches for the DM365 video capture, of which > the current set includes only CCDC and the VPFE framework. Once > the present set is reviewed, I will send out the other parts > like H3A, sensor additions etc. > > Introduction > ------------ > This is the proposal of the initial version of design and implementation of > the Davinci family (dm644x,dm355,dm365)VPFE (Video Port Front End) drivers > using Media Controloler , the initial version which supports > the following: > 1) dm365 vpfe > 2) ccdc,previewer,resizer,h3a,af blocks > 3) supports both continuous and single-shot modes > 4) supports user pointer exchange and memory mapped modes for buffer allocation > > This driver bases its design on Laurent Pinchart's Media Controller Design > whose patches for Media Controller and subdev enhancements form the base. > The driver also takes copious elements taken from Laurent Pinchart and > others' OMAP ISP driver based on Media Controller. So thank you all the > people who are responsible for the Media Controller and the OMAP ISP driver. This may be a stupid question, but how much changes are there between this driver and the OMAP 3 ISP driver? I understand that not all the blocks are there. Are there any major functional differences between those in Davinci and those in OMAP 3? Could the OMAP 3 ISP driver made support Davinci ISP as well? There are number of possible improvements that still should be made to the OMAP 3 ISP driver so this way both of the driver would easily get them. To mention some: - Multiple output pipeline - Switch to videobuf2 - Switch to GENIRQ Cc Laurent. Regards, -- Sakari Ailus sakari.ailus at iki.fi From grant.likely at secretlab.ca Mon Jun 27 16:00:07 2011 From: grant.likely at secretlab.ca (Grant Likely) Date: Mon, 27 Jun 2011 15:00:07 -0600 Subject: [RFC 2/8] remoteproc: add omap implementation In-Reply-To: <1308640714-17961-3-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-3-git-send-email-ohad@wizery.com> Message-ID: <20110627210007.GC20865@ponder.secretlab.ca> On Tue, Jun 21, 2011 at 10:18:28AM +0300, Ohad Ben-Cohen wrote: > From: Guzman Lugo, Fernando > > Add remoteproc implementation for OMAP4, so we can load the M3 and DSP > remote processors. > > Based on code by Hari Kanigeri > > While this code is functional, and works on OMAP4 & its M3's, > it is still preliminary and going to substantially change: > > 1. Splitting physically contiguous regions to pages should happen > inside the IOMMU API framework, and not here (so omap_rproc_map_unmap > should go away). > 2. IOMMU programming in general should go away too; it should be handled by > generic code (preferably using the DMA mapping API), and not by an OMAP > specific component. > > This patch depends on OMAP's IOMMU API migration, as posted here: > https://lkml.org/lkml/2011/6/2/369 > > [ohad at wizery.com: commit log, generic iommu api migration, refactoring, cleanups] > > Signed-off-by: Guzman Lugo, Fernando > Signed-off-by: Ohad Ben-Cohen > --- > arch/arm/plat-omap/include/plat/remoteproc.h | 41 +++++ > drivers/remoteproc/Kconfig | 21 +++ > drivers/remoteproc/Makefile | 1 + > drivers/remoteproc/omap_remoteproc.c | 243 ++++++++++++++++++++++++++ > 4 files changed, 306 insertions(+), 0 deletions(-) > create mode 100644 arch/arm/plat-omap/include/plat/remoteproc.h > create mode 100644 drivers/remoteproc/omap_remoteproc.c > > diff --git a/arch/arm/plat-omap/include/plat/remoteproc.h b/arch/arm/plat-omap/include/plat/remoteproc.h > new file mode 100644 > index 0000000..6494570 > --- /dev/null > +++ b/arch/arm/plat-omap/include/plat/remoteproc.h > @@ -0,0 +1,41 @@ > +/* > + * Remote Processor - omap-specific bits > + * > + * 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 > + * version 2 as published by the Free Software Foundation. > + * > + * 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. > + */ > + > +#ifndef _PLAT_REMOTEPROC_H > +#define _PLAT_REMOTEPROC_H > + > +#include > + > +/* > + * struct omap_rproc_pdata - omap remoteproc's platform data > + * @name: the remoteproc's name, cannot exceed RPROC_MAX_NAME bytes > + * @iommu_name: iommu device we're behind of > + * @oh_name: omap hwmod device > + * @oh_name_opt: optional, secondary omap hwmod device > + * @firmware: name of firmware file to load > + * @ops: start/stop rproc handlers > + * @memory_maps: table of da-to-pa iommu memory maps > + */ > +struct omap_rproc_pdata { > + const char *name; > + const char *iommu_name; > + const char *oh_name; > + const char *oh_name_opt; > + const char *firmware; > + const struct rproc_ops *ops; > + const struct rproc_mem_entry *memory_maps; > +}; > + > +#endif /* _PLAT_REMOTEPROC_H */ > diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig > index a60bb12..88421bd 100644 > --- a/drivers/remoteproc/Kconfig > +++ b/drivers/remoteproc/Kconfig > @@ -5,3 +5,24 @@ > # REMOTE_PROC gets selected by whoever wants it > config REMOTE_PROC > tristate > + > +# for tristate, either expose omap_device_* or pass it via pdata > +config OMAP_REMOTE_PROC > + bool "OMAP remoteproc support" > + depends on ARCH_OMAP4 > + select OMAP_IOMMU > + select REMOTE_PROC > + select OMAP_MBOX_FWK > + default y > + help > + Say y here to support OMAP's remote processors (dual M3 > + and DSP on OMAP4) via the remote processor framework. > + > + Currently only supported on OMAP4. > + > + Usually you want to say y here, in order to enable multimedia > + use-cases to run on your platform (multimedia codecs are > + offloaded to remote DSP processors using this framework). > + > + It's safe to say n here if you're not interested in multimedia > + offloading or just want a bare minimum kernel. > diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile > index d0f60c7..0198b1d 100644 > --- a/drivers/remoteproc/Makefile > +++ b/drivers/remoteproc/Makefile > @@ -3,3 +3,4 @@ > # > > obj-$(CONFIG_REMOTE_PROC) += remoteproc.o > +obj-$(CONFIG_OMAP_REMOTE_PROC) += omap_remoteproc.o > diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c > new file mode 100644 > index 0000000..91f7f11 > --- /dev/null > +++ b/drivers/remoteproc/omap_remoteproc.c > @@ -0,0 +1,243 @@ > +/* > + * Remote processor machine-specific module for OMAP4 > + * > + * 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 > + * version 2 as published by the Free Software Foundation. > + * > + * 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. > + */ > + > +#define pr_fmt(fmt) "%s: " fmt, __func__ > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > + > +struct omap_rproc_priv { > + struct iommu_domain *domain; > + struct device *iommu; > +}; > + > +/* > + * Map a physically contiguous memory region using biggest pages possible. > + * TODO: this code should go away; it belongs in the generic IOMMU layer > + */ > +static int omap_rproc_map_unmap(struct iommu_domain *domain, > + const struct rproc_mem_entry *me, bool map) > +{ > + u32 all_bits; > + /* these are the page sizes supported by OMAP's IOMMU */ > + u32 pg_size[] = {SZ_16M, SZ_1M, SZ_64K, SZ_4K}; > + int i, ret, size = me->size; > + u32 da = me->da; > + u32 pa = me->pa; > + int order; > + int flags; > + > + /* must be aligned at least with the smallest supported iommu page */ > + if (!IS_ALIGNED(size, SZ_4K) || !IS_ALIGNED(da | pa, SZ_4K)) { > + pr_err("misaligned: size %x da 0x%x pa 0x%x\n", size, da, pa); > + return -EINVAL; > + } > + > + while (size) { > + /* find the max page size with which both pa, da are aligned */ > + all_bits = pa | da; > + for (i = 0; i < ARRAY_SIZE(pg_size); i++) { > + if ((size >= pg_size[i]) && > + ((all_bits & (pg_size[i] - 1)) == 0)) { > + break; > + } > + } > + > + order = get_order(pg_size[i]); > + > + /* OMAP4's M3 is little endian, so no need for conversions */ > + flags = MMU_RAM_ENDIAN_LITTLE | MMU_RAM_ELSZ_NONE; > + > + if (map) > + ret = iommu_map(domain, da, pa, order, flags); > + else > + ret = iommu_unmap(domain, da, order); > + > + if (ret) > + return ret; > + > + size -= pg_size[i]; > + da += pg_size[i]; > + pa += pg_size[i]; > + } > + > + return 0; > +} > + > +/* bootaddr isn't needed for the dual M3's */ > +static inline int omap_rproc_start(struct rproc *rproc, u64 bootaddr) > +{ > + struct device *dev = rproc->dev; > + struct platform_device *pdev = to_platform_device(dev); > + struct omap_rproc_pdata *pdata = dev->platform_data; > + struct iommu_domain *domain; > + struct device *iommu; > + struct omap_rproc_priv *priv; > + int ret, i; > + > + if (!iommu_found()) { > + dev_err(&pdev->dev, "iommu not found\n"); > + return -ENODEV; > + } > + > + priv = kmalloc(sizeof(*priv), GFP_KERNEL); > + if (!priv) { > + dev_err(&pdev->dev, "kzalloc failed\n"); > + return -ENOMEM; > + } > + > + /* > + * Use the specified iommu name to find our iommu device. > + * TODO: solve this generically so other platforms can use it, too > + */ > + iommu = omap_find_iommu_device(pdata->iommu_name); > + if (!iommu) { > + dev_err(dev, "omap_find_iommu_device failed\n"); > + ret = -ENODEV; > + goto free_mem; > + } > + > + domain = iommu_domain_alloc(); > + if (!domain) { > + dev_err(&pdev->dev, "can't alloc iommu domain\n"); > + ret = -ENODEV; > + goto free_mem; > + } > + > + priv->iommu = iommu; > + priv->domain = domain; > + rproc->priv = priv; > + > + ret = iommu_attach_device(domain, iommu); > + if (ret) { > + dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret); > + goto free_domain; > + } > + > + for (i = 0; rproc->memory_maps[i].size; i++) { > + const struct rproc_mem_entry *me = &rproc->memory_maps[i]; > + > + ret = omap_rproc_map_unmap(domain, me, true); > + if (ret) { > + dev_err(&pdev->dev, "iommu_map failed: %d\n", ret); > + goto unmap_regions; > + } > + } > + > + /* power on the remote processor itself */ > + ret = omap_device_enable(pdev); > + if (ret) > + goto unmap_regions; > + > + return 0; > + > +unmap_regions: > + for (--i; i >= 0 && rproc->memory_maps[i].size; i--) { > + const struct rproc_mem_entry *me = &rproc->memory_maps[i]; > + omap_rproc_map_unmap(domain, me, false); > + } > +free_domain: > + iommu_domain_free(domain); > +free_mem: > + kfree(priv); > + return ret; > +} > + > +static inline int omap_rproc_stop(struct rproc *rproc) > +{ > + struct device *dev = rproc->dev; > + struct platform_device *pdev = to_platform_device(dev); > + struct omap_rproc_priv *priv = rproc->priv; > + struct iommu_domain *domain = priv->domain; > + struct device *iommu = priv->iommu; > + int ret, i; > + > + /* power off the remote processor itself */ > + ret = omap_device_shutdown(pdev); > + if (ret) { > + dev_err(dev, "failed to shutdown: %d\n", ret); > + goto out; > + } > + > + for (i = 0; rproc->memory_maps[i].size; i++) { > + const struct rproc_mem_entry *me = &rproc->memory_maps[i]; > + > + ret = omap_rproc_map_unmap(domain, me, false); > + if (ret) { > + dev_err(&pdev->dev, "iommu_unmap failed: %d\n", ret); > + goto out; > + } > + } > + > + iommu_detach_device(domain, iommu); > + iommu_domain_free(domain); > + > +out: > + return ret; > +} > + > +static struct rproc_ops omap_rproc_ops = { > + .start = omap_rproc_start, > + .stop = omap_rproc_stop, > +}; > + > +static int omap_rproc_probe(struct platform_device *pdev) > +{ > + struct omap_rproc_pdata *pdata = pdev->dev.platform_data; > + > + return rproc_register(&pdev->dev, pdata->name, &omap_rproc_ops, > + pdata->firmware, pdata->memory_maps, > + THIS_MODULE); Very little for me to comment on here. However, something I just noticed. Why is it necessary to pass in THIS_MODULE to the rproc_register function? Having a reference to the pdev gives you the pointer to the driver, which has the THIS_MODULE value in it. That should be sufficient. /me also isn't sure if incrementing the refcount on the module is the best way to prevent the rproc from going away, but I haven't dug into the details in the driver code to find out. Drivers can get unbound from devices without the driver being unloaded, so I imagine there is an in-use count on the device itself that would prevent driver unbinding. > +} > + > +static int __devexit omap_rproc_remove(struct platform_device *pdev) > +{ > + struct omap_rproc_pdata *pdata = pdev->dev.platform_data; > + > + return rproc_unregister(pdata->name); > +} > + > +static struct platform_driver omap_rproc_driver = { > + .probe = omap_rproc_probe, > + .remove = __devexit_p(omap_rproc_remove), > + .driver = { > + .name = "omap-rproc", > + .owner = THIS_MODULE, > + }, > +}; > + > +static int __init omap_rproc_init(void) > +{ > + return platform_driver_register(&omap_rproc_driver); > +} > +/* must be ready in time for device_initcall users */ > +subsys_initcall(omap_rproc_init); > + > +static void __exit omap_rproc_exit(void) > +{ > + platform_driver_unregister(&omap_rproc_driver); > +} > +module_exit(omap_rproc_exit); > + > +MODULE_LICENSE("GPL v2"); > +MODULE_DESCRIPTION("OMAP Remote Processor control driver"); > -- > 1.7.1 > _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From rdunlap at xenotime.net Tue Jun 28 18:44:53 2011 From: rdunlap at xenotime.net (Randy Dunlap) Date: Tue, 28 Jun 2011 16:44:53 -0700 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: <1308640714-17961-8-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-8-git-send-email-ohad@wizery.com> Message-ID: <20110628164453.aa85c85c.rdunlap@xenotime.net> On Tue, 21 Jun 2011 10:18:33 +0300 Ohad Ben-Cohen wrote: > Add a virtio-based IPC bus, which enables kernel users to communicate > with remote processors over shared memory using a simple messaging > protocol. > > Assign a local address for every local endpoint that is created, > and bind it to the user's callback. Invoke that callback when the > destination of an inbound message is the user's address. > > Signed-off-by: Ohad Ben-Cohen > --- > Documentation/ABI/testing/sysfs-bus-rpmsg | 75 +++ > Documentation/rpmsg.txt | 308 +++++++++ > drivers/Kconfig | 1 + > drivers/Makefile | 1 + > drivers/rpmsg/Kconfig | 14 + > drivers/rpmsg/Makefile | 1 + > drivers/rpmsg/virtio_rpmsg_bus.c | 1036 +++++++++++++++++++++++++++++ > include/linux/mod_devicetable.h | 10 + > include/linux/rpmsg.h | 421 ++++++++++++ > include/linux/virtio_ids.h | 1 + > 10 files changed, 1868 insertions(+), 0 deletions(-) > create mode 100644 Documentation/ABI/testing/sysfs-bus-rpmsg > create mode 100644 Documentation/rpmsg.txt > create mode 100644 drivers/rpmsg/Kconfig > create mode 100644 drivers/rpmsg/Makefile > create mode 100644 drivers/rpmsg/virtio_rpmsg_bus.c > create mode 100644 include/linux/rpmsg.h > diff --git a/Documentation/rpmsg.txt b/Documentation/rpmsg.txt > new file mode 100644 > index 0000000..0a7c820 > --- /dev/null > +++ b/Documentation/rpmsg.txt > @@ -0,0 +1,308 @@ > +Remote Processor Messaging (rpmsg) Framework > + > +1. Introduction > + > +Modern SoCs typically employ heterogeneous remote processor devices in > +asymmetric multiprocessing (AMP) configurations, which may be running > +different instances of operating system, whether it's Linux or any other > +flavor of real-time OS. > + > +OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP. > +Typically, the dual cortex-A9 is running Linux in a SMP configuration, > +and each of the other three cores (two M3 cores and a DSP) is running > +its own instance of RTOS in an AMP configuration. > + > +Typically AMP remote processors employ dedicated DSP codecs and multimedia > +hardware accelerators, and therefore are often used to offload cpu-intensive prefer: CPU- throughout. > +multimedia tasks from the main application processor. > + > +These remote processors could also be used to control latency-sensitive > +sensors, drive random hardware blocks, or just perform background tasks > +while the main CPU is idling. > + > +Users of those remote processors can either be userland apps (e.g. multimedia > +frameworks talking with remote OMX components) or kernel drivers (controlling > +hardware accessible only by the remote processor, reserving kernel-controlled > +resources on behalf of the remote processor, etc..). > + > +Rpmsg is a virtio-based messaging bus that allows kernel drivers to communicate > +with remote processors available on the system. In turn, drivers could then > +expose appropriate user space interfaces, if needed. > + > +When writing a driver that exposes rpmsg communication to userland, please > +keep in mind that remote processors might have direct access to the > +system's physical memory and/or other sensitive hardware resources (e.g. on > +OMAP4, remote cores (/hardware accelerators) may have direct access to the > +physical memory, gpio banks, dma controllers, i2c bus, gptimers, mailbox > +devices, hwspinlocks, etc..). Moreover, those remote processors might be > +running RTOS where every task can access the entire memory/devices exposed > +to the processor. To minimize the risks of rogue (/buggy) userland code What is with the leading / here and above (/hardware) and below? Looks like they can all be dropped. > +exploiting (/triggering) remote bugs, and by that taking over (/down) the > +system, it is often desired to limit userland to specific rpmsg channels (see > +definition below) it is allowed to send messages on, and if possible/relevant, > +minimize the amount of control it has over the content of the messages. > + > +Every rpmsg device is a communication channel with a remote processor (thus > +rpmsg devices are called channels). Channels are identified by a textual name > +and have a local ("source") rpmsg address, and remote ("destination") rpmsg > +address. > + > +When a driver starts listening on a channel, it binds it with a unique > +rpmsg src address (a 32 bits integer). This way when inbound messages arrive (a 32-bit integer). > +to this src address, the rpmsg core dispatches them to that driver (by invoking > +the driver's rx handler with the payload of the incoming message). > + > + > +2. User API > + > + int rpmsg_send(struct rpmsg_channel *rpdev, void *data, int len); > + - sends a message across to the remote processor on a given channel. > + The caller should specify the channel, the data it wants to send, > + and its length (in bytes). The message will be sent on the specified > + channel, i.e. its source and destination address fields will be > + set to the channel's src and dst addresses. > + > + In case there are no TX buffers available, the function will block until > + one becomes available (i.e. until the remote processor will consume prefer: until the remote processor consumes and puts it back on > + a tx buffer and put it back on virtio's used descriptor ring), > + or a timeout of 15 seconds elapses. When the latter happens, > + -ERESTARTSYS is returned. > + The function can only be called from a process context (for now). > + Returns 0 on success and an appropriate error value on failure. > + > + int rpmsg_sendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst); > + - sends a message across to the remote processor on a given channel, > + to a destination address provided by the caller. > + The caller should specify the channel, the data it wants to send, > + its length (in bytes), and an explicit destination address. > + The message will then be sent to the remote processor to which the > + channel belongs to, using the channel's src address, and the user-provided channel belongs, > + dst address (thus the channel's dst address will be ignored). > + > + In case there are no TX buffers available, the function will block until > + one becomes available (i.e. until the remote processor will consume until the remote processor consumes a tx buffer and puts it back on > + a tx buffer and put it back on virtio's used descriptor ring), > + or a timeout of 15 seconds elapses. When the latter happens, > + -ERESTARTSYS is returned. > + The function can only be called from a process context (for now). > + Returns 0 on success and an appropriate error value on failure. > + > + int rpmsg_send_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst, > + void *data, int len); > + - sends a message across to the remote processor, using the src and dst > + addresses provided by the user. > + The caller should specify the channel, the data it wants to send, > + its length (in bytes), and explicit source and destination addresses. > + The message will then be sent to the remote processor to which the > + channel belongs to, but the channel's src and dst addresses will be channel belongs, > + ignored (and the user-provided addresses will be used instead). > + > + In case there are no TX buffers available, the function will block until > + one becomes available (i.e. until the remote processor will consume until the remote processor consumes a tx buffer and puts it back on > + a tx buffer and put it back on virtio's used descriptor ring), > + or a timeout of 15 seconds elapses. When the latter happens, > + -ERESTARTSYS is returned. > + The function can only be called from a process context (for now). > + Returns 0 on success and an appropriate error value on failure. > + > + int rpmsg_trysend(struct rpmsg_channel *rpdev, void *data, int len); > + - sends a message across to the remote processor on a given channel. > + The caller should specify the channel, the data it wants to send, > + and its length (in bytes). The message will be sent on the specified > + channel, i.e. its source and destination address fields will be > + set to the channel's src and dst addresses. > + > + In case there are no TX buffers available, the function will immediately > + return -ENOMEM without waiting until one becomes available. > + The function can only be called from a process context (for now). > + Returns 0 on success and an appropriate error value on failure. > + > + int rpmsg_trysendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst) > + - sends a message across to the remote processor on a given channel, > + to a destination address provided by the user. > + The user should specify the channel, the data it wants to send, > + its length (in bytes), and an explicit destination address. > + The message will then be sent to the remote processor to which the > + channel belongs to, using the channel's src address, and the user-provided channel belongs, > + dst address (thus the channel's dst address will be ignored). > + > + In case there are no TX buffers available, the function will immediately > + return -ENOMEM without waiting until one becomes available. > + The function can only be called from a process context (for now). > + Returns 0 on success and an appropriate error value on failure. > + > + int rpmsg_trysend_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst, > + void *data, int len); > + - sends a message across to the remote processor, using source and > + destination addresses provided by the user. > + The user should specify the channel, the data it wants to send, > + its length (in bytes), and explicit source and destination addresses. > + The message will then be sent to the remote processor to which the > + channel belongs to, but the channel's src and dst addresses will be channel belongs, > + ignored (and the user-provided addresses will be used instead). > + > + In case there are no TX buffers available, the function will immediately > + return -ENOMEM without waiting until one becomes available. > + The function can only be called from a process context (for now). > + Returns 0 on success and an appropriate error value on failure. > + > + struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev, > + void (*cb)(struct rpmsg_channel *, void *, int, void *, u32), > + void *priv, u32 addr); > + - every rpmsg address in the system is bound to an rx callback (so when > + inbound messages arrive, they are dispatched by the rpmsg bus using the > + appropriate callback handler) by means of an rpmsg_endpoint struct. > + > + This function allows drivers to create such an endpoint, and by that, > + bind a callback, and possibly some private data too, to an rpmsg address > + (either one that is known in advance, or one that will be dynamically > + assigned for them). > + > + Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint > + is already created for them when they are probed by the rpmsg bus > + (using the rx callback they provide when they registered to the rpmsg bus). > + > + So things should just work for simple drivers: they already have an > + endpoint, their rx callback is bound to their rpmsg address, and when > + relevant inbound messages arrive (i.e. messages which their dst address > + equals to the src address of their rpmsg channel), the driver's handler > + is invoked to process it. > + > + That said, more complicated drivers might do need to allocate > + additional rpmsg addresses, and bind them to different rx callbacks. > + To accomplish that, those drivers need to call this function. > + Driver should provide their channel (so the new endpoint would bind Drivers > + to the same remote processor their channel belongs to), an rx callback > + function, an optional private data (which is provided back when the > + rx callback is invoked), and an address they want to bind with the > + callback. If addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will > + dynamically assign them an available rpmsg address (drivers should have > + a very good reason why not to always use RPMSG_ADDR_ANY here). > + > + Returns a pointer to the endpoint on success, or NULL on error. > + > + void rpmsg_destroy_ept(struct rpmsg_endpoint *ept); > + - destroys an existing rpmsg endpoint. user should provide a pointer > + to an rpmsg endpoint that was previously created with rpmsg_create_ept(). > + > + int register_rpmsg_driver(struct rpmsg_driver *rpdrv); > + - registers an rpmsg driver with the rpmsg bus. user should provide > + a pointer to an rpmsg_driver struct, which contains the driver's > + ->probe() and ->remove() functions, an rx callback, and an id_table > + specifying the names of the channels this driver is interested to > + be probed with. > + > + void unregister_rpmsg_driver(struct rpmsg_driver *rpdrv); > + - unregisters an rpmsg driver from the rpmsg bus. user should provide > + a pointer to a previously-registerd rpmsg_driver struct. registered > + Returns 0 on success, and an appropriate error value on failure. > + > + > +3. Typical usage > + > +The following is a simple rpmsg driver, that sends an "hello!" message > +on probe(), and whenever it receives an incoming message, it dumps its > +content to the console. > + > +#include > +#include > +#include > + > +static void rpmsg_sample_cb(struct rpmsg_channel *rpdev, void *data, int len, > + void *priv, u32 src) > +{ > + print_hex_dump(KERN_INFO, "incoming message:", DUMP_PREFIX_NONE, > + 16, 1, data, len, true); > +} > + > +static int rpmsg_sample_probe(struct rpmsg_channel *rpdev) > +{ > + int err; > + > + dev_info(&rpdev->dev, "chnl: 0x%x -> 0x%x\n", rpdev->src, rpdev->dst); > + > + /* send a message on our channel */ > + err = rpmsg_send(rpdev, "hello!", 6); > + if (err) { > + pr_err("rpmsg_send failed: %d\n", err); > + return err; > + } > + > + return 0; > +} > + > +static void __devexit rpmsg_sample_remove(struct rpmsg_channel *rpdev) > +{ > + dev_info(&rpdev->dev, "rpmsg sample client driver is removed\n"); > +} > + > +static struct rpmsg_device_id rpmsg_driver_sample_id_table[] = { > + { .name = "rpmsg-client-sample" }, > + { }, > +}; > +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sample_id_table); > + > +static struct rpmsg_driver rpmsg_sample_client = { > + .drv.name = KBUILD_MODNAME, > + .drv.owner = THIS_MODULE, > + .id_table = rpmsg_driver_sample_id_table, > + .probe = rpmsg_sample_probe, > + .callback = rpmsg_sample_cb, > + .remove = __devexit_p(rpmsg_sample_remove), > +}; > + > +static int __init init(void) > +{ > + return register_rpmsg_driver(&rpmsg_sample_client); > +} > + > +static void __exit fini(void) > +{ > + unregister_rpmsg_driver(&rpmsg_sample_client); > +} > +module_init(init); > +module_exit(fini); > + > + > +4. API for implementors > + > +Adding rpmsg support for a new platform is relatively easy; one just needs > +to register a VIRTIO_ID_RPMSG virtio device with the usual virtio_config_ops > +handlers. > + > +For simplicity, it is recommended to register a single virtio device for > +every physical remote processor we have in the system, but there are no remote processor in the system, > +hard rules here, and this decision largely depends on the use cases, > +platform capabilities, performance requirements, etc. > + > +OMAP4, e.g., registers two virtio devices to communicate with the remote dual > +Cortex-M3 processor, because each of the M3 cores executes its own OS instance > +(see omap_rpmsg.c for reference). > +This way each of the remote cores may have different rpmsg channels, and the > +rpmsg core will treat them as completely independent processors (despite > +the fact that both of are part of the same physical device, and they are both of them are part > +powered on/off together). > + > +Notable virtio implementation bits: > + > +* virtio features: VIRTIO_RPMSG_F_NS should be enabled if the remote > + processor supports dynamic name service announcement messages. > + > + Enabling this means that rpmsg device (i.e. channel) creation is > + completely dynamic: the remote processor announces the existence of a > + remote rpmsg service by sending a name service message (which contains > + the name and rpmsg addr of the remote service, see struct rpmsg_ns_msg). > + > + This message is then handled by the rpmsg bus, which in turn dynamically > + creates and registers an rpmsg channel (which represents the remote service). > + If/when a relevant rpmsg driver is registered, it will be immediately probed > + by the bus, and can then start sending messages to the remote service. > + > +* virtqueue's notify handler: should inform the remote processor whenever > + it is kicked by virtio. OMAP4 is using its mailbox device to interrupt > + the remote processor, and inform it which virtqueue number is kicked > + (the index of the kicked virtqueue is written to the mailbox register). > + > +* virtio_config_ops's ->get() handler: the rpmsg bus uses this handler > + to request for platform-specific configuration values. > + see enum rpmsg_platform_requests for more info. > diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig > new file mode 100644 > index 0000000..41303f5 > --- /dev/null > +++ b/drivers/rpmsg/Kconfig > @@ -0,0 +1,14 @@ > +# RPMSG always gets selected by whoever wants it > +config RPMSG > + tristate > + select VIRTIO > + select VIRTIO_RING > + > +if RPMSG > + > +# OK, it's a little counter-intuitive to do this, but it puts it neatly under > +# the rpmsg menu (and it's the approach preferred by the virtio folks). > + > +source "drivers/virtio/Kconfig" It seems odd to have that Kconfig file sourced in multiple places. Are the kconfig tools happy with that? > + > +endif # RPMSG Sorry about the delay. I had most of this in my drafts folder and forgot about it... HTH. --- ~Randy *** Remember to use Documentation/SubmitChecklist when testing your code *** _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel