From ghosh.subhasish at gmail.com Wed Dec 1 03:05:14 2010 From: ghosh.subhasish at gmail.com (Subhasish Ghosh) Date: Wed, 1 Dec 2010 14:35:14 +0530 Subject: [PATCH 1/2] da850-evm: Support for TI's PRU CAN Emulation. Message-ID: <1291194315-4629-1-git-send-email-subhasish@mistralsolutions.com> The patch adds support for the programmable realtime unit (PRU) available on OMAPL138. This defines the system resource requirements such as pin mux, clock, iomem, interrupt etc and registers the platform device as per the Linux driver model. Signed-off-by: Subhasish Ghosh --- arch/arm/mach-davinci/da850.c | 12 +++++++++++ arch/arm/mach-davinci/devices-da8xx.c | 29 ++++++++++++++++++++++++++++ arch/arm/mach-davinci/include/mach/da8xx.h | 2 + arch/arm/mach-davinci/include/mach/mux.h | 5 ++++ 4 files changed, 48 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 63916b9..ebc8700 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -238,6 +238,12 @@ static struct clk tptc2_clk = { .flags = ALWAYS_ENABLED, }; +static struct clk pru_clk = { + .name = "pru_ck", + .parent = &pll0_sysclk2, + .lpsc = DA8XX_LPSC0_DMAX, +}; + static struct clk uart0_clk = { .name = "uart0", .parent = &pll0_sysclk2, @@ -373,6 +379,7 @@ static struct clk_lookup da850_clks[] = { CLK(NULL, "tpcc1", &tpcc1_clk), CLK(NULL, "tptc2", &tptc2_clk), CLK(NULL, "uart0", &uart0_clk), + CLK(NULL, "pru_ck", &pru_clk), CLK(NULL, "uart1", &uart1_clk), CLK(NULL, "uart2", &uart2_clk), CLK(NULL, "aintc", &aintc_clk), @@ -542,7 +549,12 @@ static const struct mux_config da850_pins[] = { MUX_CFG(DA850, EMA_CLK, 6, 0, 15, 1, false) MUX_CFG(DA850, EMA_WAIT_1, 6, 24, 15, 1, false) MUX_CFG(DA850, NEMA_CS_2, 7, 0, 15, 1, false) + /* PRU functions for soft CAN */ + MUX_CFG(DA850, PRU0_R31_0, 7, 28, 15, 0, false) + MUX_CFG(DA850, PRU1_R30_15, 12, 0, 15, 4, false) + MUX_CFG(DA850, PRU1_R31_18, 11, 20, 15, 0, false) /* GPIO function */ + MUX_CFG(DA850, GPIO2_0, 6, 28, 15, 8, false) MUX_CFG(DA850, GPIO2_6, 6, 4, 15, 8, false) MUX_CFG(DA850, GPIO2_8, 5, 28, 15, 8, false) MUX_CFG(DA850, GPIO2_15, 5, 0, 15, 8, false) diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index 9eec630..9d1b110 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -85,6 +85,35 @@ struct platform_device da8xx_serial_device = { }, }; +/* Info specific to OMAPL138 */ +#define OMAPL138_PRU_MEM_BASE 0x01C30000 +#define OMAPL138_INT_PRU_CAN IRQ_DA8XX_EVTOUT0 +static struct resource omapl138_pru_can_resources[] = { + { + .start = OMAPL138_PRU_MEM_BASE, + .end = OMAPL138_PRU_MEM_BASE + 0xFFFF, + .flags = IORESOURCE_MEM, + }, + { + .start = OMAPL138_INT_PRU_CAN, + .end = OMAPL138_INT_PRU_CAN, + .flags = IORESOURCE_IRQ, + }, +}; + +/* Info specific to CAN conroller */ +static struct platform_device omapl138_pru_can_device = { + .name = "davinci_pru_can", + .id = -1, + .num_resources = ARRAY_SIZE(omapl138_pru_can_resources), + .resource = omapl138_pru_can_resources, +}; + +int __init da8xx_register_pru_can(void) +{ + return platform_device_register(&omapl138_pru_can_device); +} + static const s8 da8xx_queue_tc_mapping[][2] = { /* {event queue no, TC no} */ {0, 0}, diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index 4247b3f..3e88676 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -74,6 +74,7 @@ int da8xx_register_watchdog(void); int da8xx_register_usb20(unsigned mA, unsigned potpgt); int da8xx_register_usb11(struct da8xx_ohci_root_hub *pdata); int da8xx_register_emac(void); +int da8xx_register_pru_can(void); int da8xx_register_lcdc(struct da8xx_lcdc_platform_data *pdata); int da8xx_register_mmcsd0(struct davinci_mmc_config *config); int da850_register_mmcsd1(struct davinci_mmc_config *config); @@ -122,6 +123,7 @@ extern const short da850_uart2_pins[]; extern const short da850_i2c0_pins[]; extern const short da850_i2c1_pins[]; extern const short da850_cpgmac_pins[]; +extern const short da850_pru_can_pins[]; extern const short da850_mcasp_pins[]; extern const short da850_lcdcntl_pins[]; extern const short da850_mmcsd0_pins[]; diff --git a/arch/arm/mach-davinci/include/mach/mux.h b/arch/arm/mach-davinci/include/mach/mux.h index de11aac..7c07863 100644 --- a/arch/arm/mach-davinci/include/mach/mux.h +++ b/arch/arm/mach-davinci/include/mach/mux.h @@ -906,8 +906,13 @@ enum davinci_da850_index { DA850_EMA_CLK, DA850_EMA_WAIT_1, DA850_NEMA_CS_2, + /* PRU I/O */ + DA850_PRU0_R31_0, + DA850_PRU1_R30_15, + DA850_PRU1_R31_18, /* GPIO function */ + DA850_GPIO2_0, DA850_GPIO2_6, DA850_GPIO2_8, DA850_GPIO2_15, -- 1.7.2.3 From ghosh.subhasish at gmail.com Wed Dec 1 03:05:15 2010 From: ghosh.subhasish at gmail.com (Subhasish Ghosh) Date: Wed, 1 Dec 2010 14:35:15 +0530 Subject: [PATCH 2/2] da850-evm: Board file modifications TI's PRU CAN. In-Reply-To: <1291194315-4629-1-git-send-email-subhasish@mistralsolutions.com> References: <1291194315-4629-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1291194315-4629-2-git-send-email-subhasish@mistralsolutions.com> Signed-off-by: Subhasish Ghosh --- arch/arm/mach-davinci/board-da850-evm.c | 36 +++++++++++++++++++++++++++++++ 1 files changed, 36 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index f89b0b7..3563a46 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -43,6 +43,7 @@ #define DA850_MMCSD_CD_PIN GPIO_TO_PIN(4, 0) #define DA850_MMCSD_WP_PIN GPIO_TO_PIN(4, 1) +#define DA850_PRU_CAN_TRX_PIN GPIO_TO_PIN(2, 0) #define DA850_MII_MDIO_CLKEN_PIN GPIO_TO_PIN(2, 6) @@ -188,6 +189,41 @@ static struct platform_device *da850_evm_devices[] __initdata = { &da850_evm_norflash_device, }; +const short da850_pru_can_pins[] = { + DA850_PRU0_R31_0, DA850_PRU1_R30_15, DA850_PRU1_R31_18, + -1 +}; + +static int __init da850_evm_setup_pru_can(void) +{ + int ret; + + if (!machine_is_davinci_da850_evm()) + return 0; + + ret = davinci_cfg_reg_list(da850_pru_can_pins); + if (ret) + pr_warning("da850_evm_init: da850_pru_can_pins mux setup" + "failed:%d\n", ret); + + ret = davinci_cfg_reg(DA850_GPIO2_0); + if (ret) + pr_warning("da850_evm_init:GPIO(2,0) mux setup " + "failed\n"); + + /* value = 0 to enable the CAN transceiver */ + ret = gpio_request_one(DA850_PRU_CAN_TRX_PIN, GPIOF_OUT_INIT_LOW, "pru_can_en"); + if (ret) + pr_warning("Cannot setup GPIO %d\n", DA850_PRU_CAN_TRX_PIN); + + ret = da8xx_register_pru_can(); + if (ret) + pr_warning("da850_evm_init: pru can registration failed:" + "%d\n", ret); + return ret; +} +device_initcall(da850_evm_setup_pru_can); + #define DA8XX_AEMIF_CE2CFG_OFFSET 0x10 #define DA8XX_AEMIF_ASIZE_16BIT 0x1 -- 1.7.2.3 From lethal at linux-sh.org Wed Dec 1 03:19:59 2010 From: lethal at linux-sh.org (Paul Mundt) Date: Wed, 1 Dec 2010 18:19:59 +0900 Subject: [PATCH] video: da8xx: Register IRQ as last thing in driver probing. In-Reply-To: <1291147454-3393-1-git-send-email-caglarakyuz@gmail.com> References: <1291147454-3393-1-git-send-email-caglarakyuz@gmail.com> Message-ID: <20101201091959.GB2982@linux-sh.org> On Tue, Nov 30, 2010 at 10:04:14PM +0200, caglarakyuz at gmail.com wrote: > From: Caglar Akyuz > > Following commit exposed a bug in driver: > > "fbdev: da8xx/omap-l1xx: implement double buffering" > > Bug is, if interrupt handler is called before initialization is > finished, raster controller is enabled and following register > modifications causes hardware to stay in a broken state. > > By looking at this one may say that proper locking is missing in > this driver, and a more proper fix should be prepared. However, > aformentioned commit causes a regression in the driver and some > fix to current one should be applied first. > > Signed-off-by: Caglar Akyuz Barring any objections from the davinci folks, I've queued this up. From sshtylyov at mvista.com Wed Dec 1 05:46:52 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Wed, 01 Dec 2010 14:46:52 +0300 Subject: [PATCH 2/2] da850-evm: Board file modifications TI's PRU CAN. In-Reply-To: <1291194315-4629-2-git-send-email-subhasish@mistralsolutions.com> References: <1291194315-4629-1-git-send-email-subhasish@mistralsolutions.com> <1291194315-4629-2-git-send-email-subhasish@mistralsolutions.com> Message-ID: <4CF635AC.7000807@mvista.com> Hello. On 01-12-2010 12:05, Subhasish Ghosh wrote: > Signed-off-by: Subhasish Ghosh > --- > arch/arm/mach-davinci/board-da850-evm.c | 36 +++++++++++++++++++++++++++++++ > 1 files changed, 36 insertions(+), 0 deletions(-) > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index f89b0b7..3563a46 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -43,6 +43,7 @@ > > #define DA850_MMCSD_CD_PIN GPIO_TO_PIN(4, 0) > #define DA850_MMCSD_WP_PIN GPIO_TO_PIN(4, 1) > +#define DA850_PRU_CAN_TRX_PIN GPIO_TO_PIN(2, 0) Please align with the others. > @@ -188,6 +189,41 @@ static struct platform_device *da850_evm_devices[] __initdata = { > &da850_evm_norflash_device, > }; > > +const short da850_pru_can_pins[] = { Prefix should be da850_evm_. > + DA850_PRU0_R31_0, DA850_PRU1_R30_15, DA850_PRU1_R31_18, I thought you wanted to use GPIO2[0] too? > + -1 > +}; > + > +static int __init da850_evm_setup_pru_can(void) > +{ > + int ret; > + > + if (!machine_is_davinci_da850_evm()) > + return 0; > + > + ret = davinci_cfg_reg_list(da850_pru_can_pins); > + if (ret) > + pr_warning("da850_evm_init: You're not in that function. Use the '__func__' variable to print the current function's name. > da850_pru_can_pins mux setup" You forgot to add space after "setup", else it'll look loke "setupfailed". > + "failed:%d\n", ret); Probably need space before "%d"... > + ret = davinci_cfg_reg(DA850_GPIO2_0); > + if (ret) > + pr_warning("da850_evm_init:GPIO(2,0) mux setup " Again, you're not in that function. Probably need space before "GPIO"... > + ret = da8xx_register_pru_can(); > + if (ret) > + pr_warning("da850_evm_init: pru can registration failed:" Not in that function. Probably need space after "failed:"... > + "%d\n", ret); > + return ret; > +} > +device_initcall(da850_evm_setup_pru_can); > + > #define DA8XX_AEMIF_CE2CFG_OFFSET 0x10 > #define DA8XX_AEMIF_ASIZE_16BIT 0x1 > WBR, Sergei From sshtylyov at mvista.com Wed Dec 1 06:04:18 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Wed, 01 Dec 2010 15:04:18 +0300 Subject: setting gpios pinmux on a custom board In-Reply-To: References: <4CF53E8A.1040703@mvista.com> <4CF548DD.7020001@mvista.com> Message-ID: <4CF639C2.5020509@mvista.com> Hello. On 30-11-2010 23:02, Raffaele Recalcati wrote: > >>>> Looking at dm365.c I see that the pinmux settings are thinked for a > >>>> particular case. > >>>> How can I, in a clean way, to set my settings? > >>> By calls to davinci_cfg_reg() or davinci_cfg_reg_list() > >> ok, I'm doing that. > >>>> Can I override for instance dm365_pins in davinci_soc_info_dm365 > >>>> structure with my > >>>> settings for my basi board, dm365_basi_pins? > >>> No, and you don't need to. These specify the layout of the PinMux > >>> registers and are used by davinci_cfg_reg(). > >> The problem is, for instance, that UART1_TX can be used on two (or more?) > gpios. > > Sorry, I don't understand you. > UART1_TXD can be GIO25 or GIO16 instead in dm365 we have one of them Ah, you mean that UART1_TX has alternate function as GPIO... even as 2 GPIOs... > MUX_CFG(DM365, UART1_TXD, 3, 15, 3, 2, false) > That means GIO16. > So I can add : > MUX_CFG(DM365, UART1_TXD_GIO16, 3, 29, 3, 3, false) Bits 29-30 control GPIO25, not GIO16. > The same can be said for other peripherals, because the same periperal can be > available using different pins. > >> So I'll need to add more MUX_CFG lines.. > >> UART1_TX_GIOx > >> UART1_TX_GIOy > >> and then select the one I'm really using on my board. > > Still don't understand what you mean. Now I do, especially after looking into the manual. :-) > >> I'm afraid that setting gpios in my board file is not enough, because > >> I see that other modules change my settings and I'm getting crazy to > >> find where they set. > >> I'll search tomorrow the MUX_CFG defines around in my kernel. > > They should all be in dm365.c... > I check better in the latest kernel... Probably not everything is available here as I can see now... > Regards, > Raffaele WBR, Sergei From ghosh.subhasish at gmail.com Wed Dec 1 06:44:00 2010 From: ghosh.subhasish at gmail.com (S.Ghosh) Date: Wed, 1 Dec 2010 18:14:00 +0530 Subject: [PATCH 1/2] da850-evm: Support for TI's PRU CAN Emulation. In-Reply-To: <4CF6349B.9060904@mvista.com> References: <1291194315-4629-1-git-send-email-subhasish@mistralsolutions.com> <4CF6349B.9060904@mvista.com> Message-ID: Hello. On Wed, Dec 1, 2010 at 5:12 PM, Sergei Shtylyov wrote: > Hello. > > On 01-12-2010 12:05, Subhasish Ghosh wrote: > > Not sure why you mention DA850 EVM in the subject -- this patch doesn't > touch it. [SG] -- Should I mention it as "davinci: ". > > > The patch adds support for the programmable realtime unit (PRU) >> available on OMAPL138. This defines the system resource >> requirements such as pin mux, clock, iomem, interrupt etc >> and registers the platform device >> > > It registers CAN platfrom device, not just PRU, right? [SG] -- It only registers CAN. Will modify the comments as below: The patch adds support for an emulated CAN controller on the programmable realtime unit (PRU) available on OMAPL138. This defines the system resource requirements such as pin mux, clock, iomem, interrupt etc and registers the platform device as per the Linux driver model. > > > as per the Linux driver model. >> > > Signed-off-by: Subhasish Ghosh >> > > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c >> index 63916b9..ebc8700 100644 >> --- a/arch/arm/mach-davinci/da850.c >> +++ b/arch/arm/mach-davinci/da850.c >> @@ -238,6 +238,12 @@ static struct clk tptc2_clk = { >> .flags = ALWAYS_ENABLED, >> }; >> >> +static struct clk pru_clk = { >> + .name = "pru_ck", >> > > We don't add _ck postfixes in DaVinci code. [SG] -- Will change to pruss_clk > > > @@ -542,7 +549,12 @@ static const struct mux_config da850_pins[] = { >> MUX_CFG(DA850, EMA_CLK, 6, 0, 15, 1, >> false) >> MUX_CFG(DA850, EMA_WAIT_1, 6, 24, 15, 1, >> false) >> MUX_CFG(DA850, NEMA_CS_2, 7, 0, 15, 1, >> false) >> + /* PRU functions for soft CAN */ >> + MUX_CFG(DA850, PRU0_R31_0, 7, 28, 15, 0, false) >> + MUX_CFG(DA850, PRU1_R30_15, 12, 0, 15, 4, false) >> + MUX_CFG(DA850, PRU1_R31_18, 11, 20, 15, 0, false) >> > > Still not properly aligned. [SG] -- This is only occurring with the emails, the original patch does not have them. I am a newbie to open source, please let me know if I am doing something wrong. > > > /* GPIO function */ >> + MUX_CFG(DA850, GPIO2_0, 6, 28, 15, 8, >> false) >> > > Addition of the GPIO pin should probably be the patch of its own, since > it's done in the interests of EVM board, not being a PRU pin... [SG] -- Will add a separate patch for the GPIO. > > > diff --git a/arch/arm/mach-davinci/devices-da8xx.c >> b/arch/arm/mach-davinci/devices-da8xx.c >> index 9eec630..9d1b110 100644 >> --- a/arch/arm/mach-davinci/devices-da8xx.c >> +++ b/arch/arm/mach-davinci/devices-da8xx.c >> @@ -85,6 +85,35 @@ struct platform_device da8xx_serial_device = { >> > [...] > >> +/* Info specific to CAN conroller */ >> >> +static struct platform_device omapl138_pru_can_device = { >> + .name = "davinci_pru_can", >> + .id = -1, >> > > Please align. > > + .num_resources = ARRAY_SIZE(omapl138_pru_can_resources), >> >> + .resource = omapl138_pru_can_resources, >> +}; >> > [...] > > diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h >> b/arch/arm/mach-davinci/include/mach/da8xx.h >> index 4247b3f..3e88676 100644 >> --- a/arch/arm/mach-davinci/include/mach/da8xx.h >> +++ b/arch/arm/mach-davinci/include/mach/da8xx.h >> > [...] > > @@ -122,6 +123,7 @@ extern const short da850_uart2_pins[]; >> extern const short da850_i2c0_pins[]; >> extern const short da850_i2c1_pins[]; >> extern const short da850_cpgmac_pins[]; >> +extern const short da850_pru_can_pins[]; >> > > You forgot to delete this. > [SG] -- Will remove this. > > WBR, Sergei > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ghosh.subhasish at gmail.com Wed Dec 1 07:06:18 2010 From: ghosh.subhasish at gmail.com (S.Ghosh) Date: Wed, 1 Dec 2010 18:36:18 +0530 Subject: [PATCH 2/2] da850-evm: Board file modifications TI's PRU CAN. In-Reply-To: <4CF635AC.7000807@mvista.com> References: <1291194315-4629-1-git-send-email-subhasish@mistralsolutions.com> <1291194315-4629-2-git-send-email-subhasish@mistralsolutions.com> <4CF635AC.7000807@mvista.com> Message-ID: Hello. On Wed, Dec 1, 2010 at 5:16 PM, Sergei Shtylyov wrote: > Hello. > > > On 01-12-2010 12:05, Subhasish Ghosh wrote: > > Signed-off-by: Subhasish Ghosh >> --- >> arch/arm/mach-davinci/board-da850-evm.c | 36 >> +++++++++++++++++++++++++++++++ >> 1 files changed, 36 insertions(+), 0 deletions(-) >> > > diff --git a/arch/arm/mach-davinci/board-da850-evm.c >> b/arch/arm/mach-davinci/board-da850-evm.c >> index f89b0b7..3563a46 100644 >> --- a/arch/arm/mach-davinci/board-da850-evm.c >> +++ b/arch/arm/mach-davinci/board-da850-evm.c >> @@ -43,6 +43,7 @@ >> >> #define DA850_MMCSD_CD_PIN GPIO_TO_PIN(4, 0) >> #define DA850_MMCSD_WP_PIN GPIO_TO_PIN(4, 1) >> +#define DA850_PRU_CAN_TRX_PIN GPIO_TO_PIN(2, 0) >> > > Please align with the others. [SG] -- This is only occurring with the emails, the original patch does not have them. > > @@ -188,6 +189,41 @@ static struct platform_device *da850_evm_devices[] >> __initdata = { >> &da850_evm_norflash_device, >> }; >> >> +const short da850_pru_can_pins[] = { >> > > Prefix should be da850_evm_. > [SG] -- Will correct. > > + DA850_PRU0_R31_0, DA850_PRU1_R30_15, DA850_PRU1_R31_18, >> > > I thought you wanted to use GPIO2[0] too? > [SG] -- The GPIO has been removed from this list and added separately. > > + -1 >> >> +}; >> + >> +static int __init da850_evm_setup_pru_can(void) >> +{ >> + int ret; >> + >> + if (!machine_is_davinci_da850_evm()) >> + return 0; >> + >> + ret = davinci_cfg_reg_list(da850_pru_can_pins); >> + if (ret) >> + pr_warning("da850_evm_init: >> > > You're not in that function. Use the '__func__' variable to print the > current function's name. [SG] -- I have followed similar convention as the rest of the file. For example, da850_evm_setup_nor_nand. > > > da850_pru_can_pins mux setup" >> > > You forgot to add space after "setup", else it'll look loke > "setupfailed". > [SG] -- Will add the spaces for all of these. > + "failed:%d\n", ret); >> > > Probably need space before "%d"... > > + ret = davinci_cfg_reg(DA850_GPIO2_0); >> >> + if (ret) >> + pr_warning("da850_evm_init:GPIO(2,0) mux setup " >> > > Again, you're not in that function. Probably need space before "GPIO"... > > + ret = da8xx_register_pru_can(); >> >> + if (ret) >> + pr_warning("da850_evm_init: pru can registration failed:" >> > > Not in that function. Probably need space after "failed:"... > > > + "%d\n", ret); >> + return ret; >> +} >> +device_initcall(da850_evm_setup_pru_can); >> + >> #define DA8XX_AEMIF_CE2CFG_OFFSET 0x10 >> #define DA8XX_AEMIF_ASIZE_16BIT 0x1 >> >> > WBR, Sergei > -------------- next part -------------- An HTML attachment was scrubbed... URL: From nsekhar at ti.com Wed Dec 1 07:32:54 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 1 Dec 2010 19:02:54 +0530 Subject: [PATCH v8 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: <1289601535-6746-5-git-send-email-vm.rod25@gmail.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> <1289601535-6746-5-git-send-email-vm.rod25@gmail.com> Message-ID: Hi Victor, On Sat, Nov 13, 2010 at 04:08:50, vm.rod25 at gmail.com wrote: > From: Victor Rodriguez > > This patch defines Pin Mux configuration for MacASP > used on the Hawkboard-L138 system in order to add Audio support > > Signed-off-by: Victor Rodriguez > Tested-by: Rene Gonzalez > --- > arch/arm/mach-davinci/da850.c | 2 +- > 1 files changed, 1 insertions(+), 1 deletions(-) > > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 63916b9..f033a0a 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -591,7 +591,7 @@ const short da850_cpgmac_pins[] __initdata = { > const short da850_mcasp_pins[] __initdata = { > DA850_AHCLKX, DA850_ACLKX, DA850_AFSX, > DA850_AHCLKR, DA850_ACLKR, DA850_AFSR, DA850_AMUTE, > - DA850_AXR_11, DA850_AXR_12, > + DA850_AXR_11, DA850_AXR_12, DA850_AXR_13, DA850_AXR_14, To speed up acceptance of this series, can you please drop this patch and submit the rest of the patches as a reduced series? Audio support can be revisited as a separate patch. Thanks, Sekhar From nsekhar at ti.com Wed Dec 1 07:51:39 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 1 Dec 2010 19:21:39 +0530 Subject: [PATCH v3 1/1] DA8XX/OMAP-L1XX: FB: Implement double buffering In-Reply-To: <201011302124.15322.caglarakyuz@gmail.com> References: <1270504278-15088-1-git-send-email-martin@ti.com> <201009012341.17458.caglarakyuz@gmail.com> <201011302124.15322.caglarakyuz@gmail.com> Message-ID: On Wed, Dec 01, 2010 at 00:54:15, Caglar Akyuz wrote: > > I'll submit the patch in a few minutes. > Thanks for the super quick patch and its already queued too. http://git.kernel.org/?p=linux/kernel/git/lethal/fbdev-2.6.git;a=commit;h=93c176f39fedaeff854ccb7681d626d65bdffe52 Regards, Sekhar From vm.rod25 at gmail.com Wed Dec 1 09:20:04 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Wed, 1 Dec 2010 09:20:04 -0600 Subject: [PATCH v8 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> <1289601535-6746-5-git-send-email-vm.rod25@gmail.com> Message-ID: On Wed, Dec 1, 2010 at 7:32 AM, Nori, Sekhar wrote: > Hi Victor, > > On Sat, Nov 13, 2010 at 04:08:50, vm.rod25 at gmail.com wrote: >> From: Victor Rodriguez >> >> This patch defines Pin Mux configuration for MacASP >> used on the Hawkboard-L138 system in order to add Audio support >> >> Signed-off-by: Victor Rodriguez >> Tested-by: Rene Gonzalez >> --- >> ?arch/arm/mach-davinci/da850.c | ? ?2 +- >> ?1 files changed, 1 insertions(+), 1 deletions(-) >> >> diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c >> index 63916b9..f033a0a 100644 >> --- a/arch/arm/mach-davinci/da850.c >> +++ b/arch/arm/mach-davinci/da850.c >> @@ -591,7 +591,7 @@ const short da850_cpgmac_pins[] __initdata = { >> ?const short da850_mcasp_pins[] __initdata = { >> ? ? ? DA850_AHCLKX, DA850_ACLKX, DA850_AFSX, >> ? ? ? DA850_AHCLKR, DA850_ACLKR, DA850_AFSR, DA850_AMUTE, >> - ? ? DA850_AXR_11, DA850_AXR_12, >> + ? ? DA850_AXR_11, DA850_AXR_12, DA850_AXR_13, DA850_AXR_14, > > To speed up acceptance of this series, can you please drop this > patch and submit the rest of the patches as a reduced series? > > Audio support can be revisited as a separate patch. > > Thanks, > Sekhar > Yes Sekhar I will do it right now, sorry for the delay with the other patch that fix this Regards Victor Rodriguez From nsekhar at ti.com Wed Dec 1 10:04:17 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 1 Dec 2010 21:34:17 +0530 Subject: [PATCH v8 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> <1289601535-6746-5-git-send-email-vm.rod25@gmail.com> Message-ID: On Wed, Dec 01, 2010 at 20:50:04, Victor Rodriguez wrote: > > Yes Sekhar I will do it right now, sorry for the delay with the other > patch that fix this The issue was not yours to fix. The generic pin lists need to be cleaned up regardless. So, don't be sorry :) Thanks, Sekhar From vm.rod25 at gmail.com Wed Dec 1 10:13:01 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Wed, 1 Dec 2010 10:13:01 -0600 Subject: [PATCH v8 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> <1289601535-6746-5-git-send-email-vm.rod25@gmail.com> Message-ID: By the way I will eliminate 3 of my patches [PATCH v8 3/9] davinci: ASoC support for Omapl138-Hawkboard [PATCH v8 4/9] davinci: McASP configuration for Omapl138-Hawkboard [PATCH v8 5/9] davinci: Audio support for Omapl138-Hawkboard Because if I eliminate the McASP configuration I can not enable the audio support for the board, the kernel compiles and the sound card is recognized but when I try to play any file on the board it will not produce any error but no sound will be produced either So in order to do not give a wrong support I will eliminate the audio support for hawk board Regards Victor Rodriguez On Wed, Dec 1, 2010 at 10:04 AM, Nori, Sekhar wrote: > On Wed, Dec 01, 2010 at 20:50:04, Victor Rodriguez wrote: >> >> Yes Sekhar I will do it ?right now, sorry for the delay with the other >> patch that fix this > > The issue was not yours to fix. The generic pin lists need to be cleaned > up regardless. So, don't be sorry :) > > Thanks, > Sekhar > > From sshtylyov at mvista.com Wed Dec 1 10:32:22 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Wed, 01 Dec 2010 19:32:22 +0300 Subject: [PATCH v8 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> <1289601535-6746-5-git-send-email-vm.rod25@gmail.com> Message-ID: <4CF67896.1060002@mvista.com> Hello. Nori, Sekhar wrote: > On Sat, Nov 13, 2010 at 04:08:50, vm.rod25 at gmail.com wrote: >> From: Victor Rodriguez >> This patch defines Pin Mux configuration for MacASP >> used on the Hawkboard-L138 system in order to add Audio support >> Signed-off-by: Victor Rodriguez >> Tested-by: Rene Gonzalez >> --- >> arch/arm/mach-davinci/da850.c | 2 +- >> 1 files changed, 1 insertions(+), 1 deletions(-) >> diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c >> index 63916b9..f033a0a 100644 >> --- a/arch/arm/mach-davinci/da850.c >> +++ b/arch/arm/mach-davinci/da850.c >> @@ -591,7 +591,7 @@ const short da850_cpgmac_pins[] __initdata = { >> const short da850_mcasp_pins[] __initdata = { >> DA850_AHCLKX, DA850_ACLKX, DA850_AFSX, >> DA850_AHCLKR, DA850_ACLKR, DA850_AFSR, DA850_AMUTE, >> - DA850_AXR_11, DA850_AXR_12, >> + DA850_AXR_11, DA850_AXR_12, DA850_AXR_13, DA850_AXR_14, > To speed up acceptance of this series, can you please drop this > patch and submit the rest of the patches as a reduced series? BTW, I've seen more AXR pins in DA850, so this patch is far from being complete anyway. > Thanks, > Sekhar WBR, Sergei From vm.rod25 at gmail.com Wed Dec 1 10:41:48 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Wed, 1 Dec 2010 10:41:48 -0600 Subject: [PATCH v8 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: <4CF67896.1060002@mvista.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> <1289601535-6746-5-git-send-email-vm.rod25@gmail.com> <4CF67896.1060002@mvista.com> Message-ID: On Wed, Dec 1, 2010 at 10:32 AM, Sergei Shtylyov wrote: > Hello. > > Nori, Sekhar wrote: > >> On Sat, Nov 13, 2010 at 04:08:50, vm.rod25 at gmail.com wrote: >>> >>> From: Victor Rodriguez > >>> This patch defines Pin Mux configuration for MacASP >>> used on the Hawkboard-L138 system in order to add Audio support > >>> Signed-off-by: Victor Rodriguez >>> Tested-by: Rene Gonzalez >>> --- >>> ?arch/arm/mach-davinci/da850.c | ? ?2 +- >>> ?1 files changed, 1 insertions(+), 1 deletions(-) > >>> diff --git a/arch/arm/mach-davinci/da850.c >>> b/arch/arm/mach-davinci/da850.c >>> index 63916b9..f033a0a 100644 >>> --- a/arch/arm/mach-davinci/da850.c >>> +++ b/arch/arm/mach-davinci/da850.c >>> @@ -591,7 +591,7 @@ const short da850_cpgmac_pins[] __initdata = { >>> ?const short da850_mcasp_pins[] __initdata = { >>> ? ? ?DA850_AHCLKX, DA850_ACLKX, DA850_AFSX, >>> ? ? ?DA850_AHCLKR, DA850_ACLKR, DA850_AFSR, DA850_AMUTE, >>> - ? ? DA850_AXR_11, DA850_AXR_12, >>> + ? ? DA850_AXR_11, DA850_AXR_12, DA850_AXR_13, DA850_AXR_14, > >> To speed up acceptance of this series, can you please drop this >> patch and submit the rest of the patches as a reduced series? > > ? BTW, I've seen more AXR pins in DA850, so this patch is far from being > complete anyway. Ok Sergei we will need to work hard on it By the way is there any other comment to my patches with out this from audio and McASP pins ? Regards Victor Rodriguez >> Thanks, >> Sekhar > > WBR, Sergei > From sshtylyov at mvista.com Wed Dec 1 10:43:04 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Wed, 01 Dec 2010 19:43:04 +0300 Subject: [PATCH 1/2] da850-evm: Support for TI's PRU CAN Emulation. In-Reply-To: References: <1291194315-4629-1-git-send-email-subhasish@mistralsolutions.com> <4CF6349B.9060904@mvista.com> Message-ID: <4CF67B18.6090100@mvista.com> Hello. S.Ghosh wrote: Please don't use HTML, as it spoils message quoting when I reply. >> Not sure why you mention DA850 EVM in the subject -- this patch >> doesn't touch it. > [SG] -- Should I mention it as "davinci: ". Why not just DA850? >>> diff --git a/arch/arm/mach-davinci/da850.c >>> b/arch/arm/mach-davinci/da850.c >>> index 63916b9..ebc8700 100644 >>> --- a/arch/arm/mach-davinci/da850.c >>> +++ b/arch/arm/mach-davinci/da850.c >>> @@ -238,6 +238,12 @@ static struct clk tptc2_clk = { >>> .flags = ALWAYS_ENABLED, >>> }; >>> >>> +static struct clk pru_clk = { >>> + .name = "pru_ck", >> We don't add _ck postfixes in DaVinci code. > [SG] -- Will change to pruss_clk We don't add postfixes at all, so just "pruss". >>> @@ -542,7 +549,12 @@ static const struct mux_config da850_pins[] = { >>> MUX_CFG(DA850, EMA_CLK, 6, 0, 15, >>> 1, false) >>> MUX_CFG(DA850, EMA_WAIT_1, 6, 24, 15, >>> 1, false) >>> MUX_CFG(DA850, NEMA_CS_2, 7, 0, 15, >>> 1, false) >>> + /* PRU functions for soft CAN */ >>> + MUX_CFG(DA850, PRU0_R31_0, 7, 28, 15, 0, >>> false) >>> + MUX_CFG(DA850, PRU1_R30_15, 12, 0, 15, 4, >>> false) >>> + MUX_CFG(DA850, PRU1_R31_18, 11, 20, 15, 0, >>> false) >> Still not properly aligned. > [SG] -- This is only occurring with the emails, the original patch does > not have them. > I am a newbie to open source, please let me know if I am doing something > wrong. Perhaps you should use 'git send-email' to send the patches, so that they don't get spoiled. Ah, I've just looked at your mail again and realized that you already use it. Then I really don't know -- your following line (adding GPIO2[0]) looks properly aligned... WBR, Sergei From manjunath.hadli at ti.com Wed Dec 1 10:58:35 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Wed, 1 Dec 2010 22:28:35 +0530 Subject: [PATCHi v2 1/6] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: <201011261220.55748.hverkuil@xs4all.nl> Message-ID: > > You may want to consider using the new core-assisted locking support. That > will simplify your driver. It seems that this driver just locks at every > ioctl, so > in that case it is easier to let the core do that. Hans, I looked into the mailing lists discussion on the core-assisted locking support but could not get a clear picture. Could you point me to the implementation snippet or some example of where it is done? Or for now is it OK to use .unlocked_ioctl? Thanks, -Manju Thanks and Regards, -Manju On Fri, Nov 26, 2010 at 16:50:55, Hans Verkuil wrote: > Hi Manju, > > See the comments below... > > On Wednesday, November 24, 2010 15:09:15 Manjunath Hadli wrote: > > 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 is 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 > > Signed-off-by: Muralidharan Karicheri > > --- > > drivers/media/video/davinci/vpbe_display.c | 2282 ++++++++++++++++++++++++++++ > > include/media/davinci/vpbe_display.h | 144 ++ > > include/media/davinci/vpbe_types.h | 93 ++ > > 3 files changed, 2519 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..99e09a8 > > --- /dev/null > > +++ b/drivers/media/video/davinci/vpbe_display.c > > @@ -0,0 +1,2282 @@ > > +/* > > + * 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 "as is" WITHOUT ANY WARRANTY of any > > + * kind, whether express or implied; without even the implied warranty > > + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > + * GNU General Public License for more details. > > + */ > > +#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 > > +#include "vpbe_venc_regs.h" > > + > > + > > +#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" > > + > > +static int debug; > > +static u32 video2_numbuffers = 3; > > +static u32 video3_numbuffers = 3; > > + > > +#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) > > +#define VPBE_DEFAULT_NUM_BUFS 3 > > + > > +static u32 video2_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; > > +static u32 video3_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; > > + > > +module_param(video2_numbuffers, uint, S_IRUGO); > > +module_param(video3_numbuffers, uint, S_IRUGO); > > +module_param(video2_bufsize, uint, S_IRUGO); > > +module_param(video3_bufsize, uint, S_IRUGO); > > +module_param(debug, int, 0644); > > + > > +static struct buf_config_params display_buf_config_params = { > > + .min_numbuffers = VPBE_DEFAULT_NUM_BUFS, > > + .numbuffers[0] = VPBE_DEFAULT_NUM_BUFS, > > + .numbuffers[1] = VPBE_DEFAULT_NUM_BUFS, > > + .min_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, > > + .min_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, > > + .layer_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, > > + .layer_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, > > +}; > > + > > +static struct vpbe_device *vpbe_dev; > > +static struct osd_state *osd_device; > > +static int vpbe_display_nr[] = { 2, 3 }; > > + > > +static struct v4l2_capability vpbe_display_videocap = { > > + .driver = VPBE_DISPLAY_DRIVER, > > + .bus_info = "platform", > > + .version = VPBE_DISPLAY_VERSION_CODE, > > + .capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, > > +}; > > + > > +static u8 layer_first_int[VPBE_DISPLAY_MAX_DEVICES]; > > + > > +__iomem void *reg_base_venc; > > + > > +static int venc_is_second_field() > > +{ > > + u32 val; > > + val = __raw_readl(reg_base_venc + VENC_VSTAT); > > + return ((val & VENC_VSTAT_FIDST) > > + == VENC_VSTAT_FIDST); > > +} > > + > > +/* > > + * vpbe_display_isr() > > + * ISR function. It changes status of the displayed buffer, takes next buffer > > + * from the queue and sets its address in VPBE registers > > + */ > > +static void vpbe_display_isr(unsigned int event, void *disp_obj) > > +{ > > + unsigned long jiffies_time = get_jiffies_64(); > > + struct timeval timevalue; > > + int i, fid; > > + unsigned long addr = 0; > > + struct vpbe_display_obj *layer = NULL; > > + struct vpbe_display *disp_dev = (struct vpbe_display *)disp_obj; > > + > > + /* Convert time represention from jiffies to timeval */ > > + jiffies_to_timeval(jiffies_time, &timevalue); > > + > > + 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; > > + /* Check the field format */ > > + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && > > + (!list_empty(&layer->dma_queue)) && > > + (event & OSD_END_OF_FRAME)) { > > + /* Progressive mode */ > > + if (layer_first_int[i]) { > > + layer_first_int[i] = 0; > > + continue; > > + } else { > > 'else' is not needed here. This saves one indent level for the code below. > > > + /* > > + * Mark status of the cur_frm to > > + * done and unlock semaphore on it > > + */ > > + if (layer->cur_frm != layer->next_frm) { > > + layer->cur_frm->ts = timevalue; > > + 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; > > + } > > + } > > + /* Get the next buffer from buffer queue */ > > + layer->next_frm = > > + list_entry(layer->dma_queue.next, > > + struct videobuf_buffer, queue); > > + /* Remove that buffer from the buffer queue */ > > + list_del(&layer->next_frm->queue); > > I think this needs a spinlock since this is also accessed from non-interrupt > context. > > > + /* Mark status of the buffer as 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_dev->cbcr_ofst); > > + } else { > > + /* > > + * Interlaced mode > > + * If it is first interrupt, ignore it > > + */ > > + if (layer_first_int[i]) { > > + layer_first_int[i] = 0; > > + return; > > + } > > + > > + layer->field_id ^= 1; > > + if (event & OSD_FIRST_FIELD) > > + fid = 0; > > + else if (event & OSD_SECOND_FIELD) > > + fid = 1; > > + else > > + return; > > + > > + /* > > + * If field id does not match with stored > > + * field id > > + */ > > + if (fid != layer->field_id) { > > + /* Make them in sync */ > > + if (0 == fid) > > + layer->field_id = fid; > > + > > + return; > > + } > > + /* > > + * device field id and local field id are > > + * in sync. If this is even field > > + */ > > + if (0 == fid) { > > + if (layer->cur_frm == layer->next_frm) > > + continue; > > + /* > > + * one frame is displayed If next frame is > > + * available, release cur_frm and move on > > + * copy frame display time > > + */ > > + layer->cur_frm->ts = timevalue; > > + /* Change status of the cur_frm */ > > + layer->cur_frm->state = VIDEOBUF_DONE; > > + /* unlock semaphore on cur_frm */ > > + wake_up_interruptible(&layer->cur_frm->done); > > + /* Make cur_frm pointing to next_frm */ > > + layer->cur_frm = layer->next_frm; > > + } else if (1 == fid) { /* odd field */ > > + if (list_empty(&layer->dma_queue) > > + || (layer->cur_frm != layer->next_frm)) > > + continue; > > + > > + /* > > + * 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); > > + > > + /* 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_dev->cbcr_ofst); > > + } > > + } > > + } > > +} > > + > > +/* interrupt service routine */ > > +static irqreturn_t venc_isr(int irq, void *arg) > > +{ > > + static unsigned last_event; > > + unsigned event = 0; > > + > > + if (venc_is_second_field()) > > + 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; > > + > > + vpbe_display_isr(event, arg); > > + 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_display_obj *layer = fh->layer; > > + unsigned long addr; > > + int ret = 0; > > + > > + 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"); > > + goto buf_align_exit; > > Just do 'return -EINVAL'. No need for a goto. > > > + } > > + > > + 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"); > > + goto buf_align_exit; > > Ditto. > > > + } > > + } > > + vb->state = VIDEOBUF_PREPARED; > > + } > > + return 0; > > + > > +buf_align_exit: > > + return -EINVAL; > > +} > > +/* > > + * 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_display_obj *layer = fh->layer; > > + int buf_size; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); > > + > > + *size = layer->pix_fmt.sizeimage; > > + buf_size = > > + display_buf_config_params.layer_bufsize[layer->device_id]; > > + /* > > + * For MMAP, limit the memory allocation as per bootarg > > + * configured buffer size > > + */ > > + if (V4L2_MEMORY_MMAP == layer->memory) > > + if (*size > buf_size) > > + *size = buf_size; > > + > > + /* Store number of buffers allocated in numbuffer member */ > > + if (*count < display_buf_config_params.min_numbuffers) > > + *count = layer->numbuffers = > > + display_buf_config_params.numbuffers[layer->device_id]; > > + > > + 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_display_obj *layer = fh->layer; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "vpbe_buffer_queue\n"); > > + > > + /* add the buffer to the DMA queue */ > > + list_add_tail(&vb->queue, &layer->dma_queue); > > I'm pretty sure this needs a spinlock to protect against dma_queue access from > the interrupt handler. > > > + /* 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_display_obj *layer = fh->layer; > > + > > + 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_display_obj* > > +_vpbe_display_get_other_win(struct vpbe_display *disp_dev, > > + struct vpbe_display_obj *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_video_display_params(struct vpbe_display *disp_dev, > > + struct vpbe_display_obj *layer) > > +{ > > + unsigned long addr; > > + int ret = 0; > > + > > + 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 (layer->layer_info.config.pixfmt == PIXFMT_NV12) { > > + struct vpbe_display_obj *otherlayer = > > + _vpbe_display_get_other_win(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_display_obj *layer, > > + int expected_xsize, int expected_ysize) > > +{ > > + struct display_layer_info *layer_info = &layer->layer_info; > > + struct v4l2_pix_format *pixfmt = &layer->pix_fmt; > > + int h_scale = 0, v_scale = 0, h_exp = 0, v_exp = 0, temp; > > + /* > > + * 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 > > + */ > > + layer->layer_info.config.xsize = pixfmt->width; > > + layer->layer_info.config.ysize = pixfmt->height; > > Why use layer_info below but not for the two lines above? > > In fact, I'd make another temporary pointer to &layer->layer_info.config. > That struct is accessed a lot and the code would look much better if it could > just do 'config->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; > > + layer->layer_info.config.xsize *= h_scale; > > + if (layer->layer_info.config.xsize < expected_xsize) { > > + if ((vpbe_dev->current_timings.timings.std_id > > I'd put vpbe_dev->current_timings.timings.std_id into a temp variable as well. > That too would make the core more readable. > > > + == V4L2_STD_525_60) || > > + ((vpbe_dev->current_timings.timings.std_id > > + == V4L2_STD_625_50))) { > > + temp = (layer->layer_info.config.xsize * > > + VPBE_DISPLAY_H_EXP_RATIO_N) / > > + VPBE_DISPLAY_H_EXP_RATIO_D; > > + if (temp <= expected_xsize) { > > + h_exp = 1; > > + layer->layer_info.config.xsize = temp; > > + } > > + } > > + } > > + 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 */ > > + layer->layer_info.config.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; > > + layer->layer_info.config.ysize *= v_scale; > > + if (layer->layer_info.config.ysize < expected_ysize) { > > + if ((vpbe_dev->current_timings.timings.std_id > > + == V4L2_STD_625_50)) { > > + temp = (layer->layer_info.config.ysize * > > + VPBE_DISPLAY_V_EXP_RATIO_N) / > > + VPBE_DISPLAY_V_EXP_RATIO_D; > > + if (temp <= expected_ysize) { > > + v_exp = 1; > > + layer->layer_info.config.ysize = temp; > > + } > > + } > > + } > > + 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 */ > > + layer->layer_info.config.ysize = expected_ysize; > > + } > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "crop display xsize = %d, ysize = %d\n", > > + layer->layer_info.config.xsize, > > + layer->layer_info.config.ysize); > > +} > > + > > +static void vpbe_disp_adj_position(struct vpbe_display *disp_dev, > > + struct vpbe_display_obj *layer, > > + int top, int left) > > +{ > > + layer->layer_info.config.xpos = 0; > > + layer->layer_info.config.ypos = 0; > > + if (left + layer->layer_info.config.xsize <= > > + vpbe_dev->current_timings.xres) > > + layer->layer_info.config.xpos = left; > > Again, store &layer->layer_info.config into a temp variable and use that in > this function. This is much more readable: > > cfg->xpos = cfg->ypos = 0; > if (left + cfg->xsize <= vpbe_dev->current_timings.xres) > cfg->xpos = left; > if (top + cfg->ysize <= vpbe_dev->current_timings.yres) > cfg->ypos = top; > > v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > "new xpos = %d, ypos = %d\n", > cfg->xpos, cfg->ypos); > } > > > + if (top + layer->layer_info.config.ysize <= > > + vpbe_dev->current_timings.yres) > > + layer->layer_info.config.ypos = top; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "new xpos = %d, ypos = %d\n", > > + layer->layer_info.config.xpos, > > + layer->layer_info.config.ypos); > > +} > > + > > +static int vpbe_disp_check_window_params(struct vpbe_display *disp_dev, > > + struct v4l2_rect *c) > > +{ > > + if ((c->width == 0) > > + || ((c->width + c->left) > vpbe_dev->current_timings.xres) > > + || (c->height == 0) > > + || ((c->height + c->top) > vpbe_dev->current_timings.yres)) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid crop values\n"); > > + return -1; > > + } > > + if ((c->height & 0x1) && (vpbe_dev->current_timings.interlaced)) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "window height must be even for interlaced display\n"); > > + return -1; > > + } > > + return 0; > > +} > > + > > +/** > > + * 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. If application likes to add pads at the end of each line and > > + * end of the buffer , it can set bytesperline to line size and sizeimage to > > + * bytesperline * height of the buffer. If driver fills zero for active > > + * video width and height, and has requested user bytesperline and sizeimage, > > + * width and height is adjusted to maximum display limit or buffer width > > + * height which ever is lower > > + */ > > +static int vpbe_try_format(struct vpbe_display *disp_dev, > > + struct v4l2_pix_format *pixfmt, int check) > > +{ > > + int min_sizeimage, bpp, min_height = 1, min_width = 32, > > + max_width, max_height, user_info = 0; > > + > > + 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_ANY) { > > + 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->bytesperline) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "bytesperline and width" > > + " cannot be zero\n"); > > + return -EINVAL; > > + } > > + > > + /* if user provided bytesperline, it must provide sizeimage as well */ > > + if (pixfmt->bytesperline && !pixfmt->sizeimage) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "sizeimage must be non zero, when user" > > + " provides bytesperline\n"); > > + return -EINVAL; > > + } > > + > > + /* adjust bytesperline as per hardware - multiple of 32 */ > > + if (!pixfmt->width) > > + pixfmt->width = pixfmt->bytesperline / bpp; > > + > > + if (!pixfmt->bytesperline) > > + pixfmt->bytesperline = pixfmt->width * bpp; > > + else > > + user_info = 1; > > + pixfmt->bytesperline = ((pixfmt->bytesperline + 31) & ~31); > > + > > + if (pixfmt->width < min_width) { > > + if (check) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "height is less than minimum," > > + "input width = %d, min_width = %d\n", > > + pixfmt->width, min_width); > > + return -EINVAL; > > + } > > + pixfmt->width = min_width; > > + } > > + > > + if (pixfmt->width > max_width) { > > + if (check) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "width is more than maximum," > > + "input width = %d, max_width = %d\n", > > + pixfmt->width, max_width); > > + return -EINVAL; > > + } > > + pixfmt->width = max_width; > > + } > > + > > + /* > > + * If height is zero, then atleast we need to have sizeimage > > + * to calculate height > > + */ > > + if (!pixfmt->height) { > > + if (user_info) { > > + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) { > > + /* > > + * for NV12 format, sizeimage is y-plane size > > + * + CbCr plane which is half of y-plane > > + */ > > + pixfmt->height = pixfmt->sizeimage / > > + (pixfmt->bytesperline + > > + (pixfmt->bytesperline >> 1)); > > + } else > > + pixfmt->height = pixfmt->sizeimage/ > > + pixfmt->bytesperline; > > + } > > + } > > + > > + if (pixfmt->height > max_height) { > > + if (check && !user_info) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "height is more than maximum," > > + "input height = %d, max_height = %d\n", > > + pixfmt->height, max_height); > > + return -EINVAL; > > + } > > + pixfmt->height = max_height; > > + } > > + > > + if (pixfmt->height < min_height) { > > + if (check && !user_info) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "width is less than minimum," > > + "input height = %d, min_height = %d\n", > > + pixfmt->height, min_height); > > + return -EINVAL; > > + } > > + pixfmt->height = min_width; > > + } > > + > > + /* if user has not provided bytesperline calculate it based on width */ > > + if (!user_info) > > + pixfmt->bytesperline = (((pixfmt->width * bpp) + 31) & ~31); > > + > > + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) > > + min_sizeimage = pixfmt->bytesperline * pixfmt->height + > > + (pixfmt->bytesperline * pixfmt->height >> 1); > > + else > > + min_sizeimage = pixfmt->bytesperline * pixfmt->height; > > + > > + if (pixfmt->sizeimage < min_sizeimage) { > > + if (check && user_info) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "sizeimage is less, %d\n", > > + min_sizeimage); > > + return -EINVAL; > > + } > > + pixfmt->sizeimage = min_sizeimage; > > + } > > + 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_display_obj *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_display_obj *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_display_obj *layer = fh->layer; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id); > > + *cap = vpbe_display_videocap; > > + > > + return 0; > > +} > > + > > +static int vpbe_display_s_crop(struct file *file, void *priv, > > + struct v4l2_crop *crop) > > +{ > > + int ret = 0; > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + > > + 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) { > > + struct v4l2_rect *rect = &crop->c; > > + > > + if (rect->top < 0 || rect->left < 0) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Error in S_CROP params" > > + " Negative values for" > > + " top/left"); > > + return -EINVAL; > > + > > + } > > + > > + if (vpbe_disp_check_window_params(disp_dev, rect)) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Error in S_CROP params\n"); > > + return -EINVAL; > > + } > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + return ret; > > + > > + osd_device->ops.get_layer_config(osd_device, > > + layer->layer_info.id, > > + &layer->layer_info.config); > > + > > + 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, > > + &layer->layer_info.config); > > + if (ret < 0) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Error in set layer config:\n"); > > + mutex_unlock(&disp_dev->lock); > > + 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"); > > + mutex_unlock(&disp_dev->lock); > > + 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); > > + > > + mutex_unlock(&disp_dev->lock); > > + } else { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); > > + return -EINVAL; > > + } > > + > > + return ret; > > +} > > + > > +static int vpbe_display_g_crop(struct file *file, void *priv, > > + struct v4l2_crop *crop) > > +{ > > + int ret = 0; > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + > > + 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) { > > + struct v4l2_rect *rect = &crop->c; > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + return ret; > > + osd_device->ops.get_layer_config(osd_device, > > + layer->layer_info.id, > > + &layer->layer_info.config); > > + rect->top = layer->layer_info.config.ypos; > > + rect->left = layer->layer_info.config.xpos; > > + rect->width = layer->layer_info.config.xsize; > > + rect->height = layer->layer_info.config.ysize; > > + mutex_unlock(&disp_dev->lock); > > + } else { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); > > + ret = -EINVAL; > > + } > > + > > + return ret; > > +} > > + > > +static int vpbe_display_cropcap(struct file *file, void *priv, > > + struct v4l2_cropcap *cropcap) > > +{ > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + int ret = 0; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "VIDIOC_CROPCAP ioctl\n"); > > + > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + return ret; > > + > > + 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; > > + > > + mutex_unlock(&disp_dev->lock); > > + > > + return ret; > > +} > > + > > +static int vpbe_display_g_fmt(struct file *file, void *priv, > > + struct v4l2_format *fmt) > > +{ > > + int ret = 0; > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + > > + 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) { > > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > > + /* Fill in the information about format */ > > + *pixfmt = layer->pix_fmt; > > + } else { > > + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); > > + ret = -EINVAL; > > + } > > + > > + return ret; > > +} > > + > > +static int vpbe_display_enum_fmt(struct file *file, void *priv, > > + struct v4l2_fmtdesc *fmt) > > +{ > > + int ret = 0; > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + 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 > 0) { > > + 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 if (index == 1) { > > + strcpy(fmt->description, "Y/CbCr 4:2:0"); > > + fmt->pixelformat = V4L2_PIX_FMT_NV12; > > + } > > + > > + return ret; > > +} > > + > > +static int vpbe_display_s_fmt(struct file *file, void *priv, > > + struct v4l2_format *fmt) > > +{ > > + int ret = 0; > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + > > + 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) { > > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > > + /* 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 */ > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + return ret; > > + > > + layer->pix_fmt = *pixfmt; > > + > > + /* Get osd layer config */ > > + osd_device->ops.get_layer_config(osd_device, > > + layer->layer_info.id, > > + &layer->layer_info.config); > > + /* Store the pixel format in the layer object */ > > + layer->layer_info.config.xsize = pixfmt->width; > > + layer->layer_info.config.ysize = pixfmt->height; > > + layer->layer_info.config.line_length = pixfmt->bytesperline; > > + layer->layer_info.config.ypos = 0; > > + layer->layer_info.config.xpos = 0; > > + layer->layer_info.config.interlaced = > > + vpbe_dev->current_timings.interlaced; > > + > > + /* Change of the default pixel format for both video windows */ > > + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { > > + struct vpbe_display_obj *otherlayer; > > + layer->layer_info.config.pixfmt = PIXFMT_NV12; > > + otherlayer = _vpbe_display_get_other_win(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, > > + &layer->layer_info.config); > > + if (ret < 0) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Error in S_FMT params:\n"); > > + mutex_unlock(&disp_dev->lock); > > + return -EINVAL; > > + } > > + > > + /* Readback and fill the local copy of current pix format */ > > + osd_device->ops.get_layer_config(osd_device, > > + layer->layer_info.id, > > + &layer->layer_info.config); > > + > > + /* verify if readback values are as expected */ > > + if (layer->pix_fmt.width != layer->layer_info.config.xsize || > > + layer->pix_fmt.height != layer->layer_info.config.ysize || > > + layer->pix_fmt.bytesperline != layer->layer_info. > > + config.line_length || > > + (layer->layer_info.config.interlaced > > + && layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || > > + (!layer->layer_info.config.interlaced && layer->pix_fmt.field > > + != V4L2_FIELD_NONE)) { > > + > > + v4l2_err(&vpbe_dev->v4l2_dev, "mismatch with layer config" > > + " params:\n"); > > + v4l2_err(&vpbe_dev->v4l2_dev, "layer->layer_info.config.xsize =" > > + "%d layer->pix_fmt.width = %d\n", > > + layer->layer_info.config.xsize, > > + layer->pix_fmt.width); > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "layer->layer_info.config.ysize =" > > + "%d layer->pix_fmt.height = %d\n", > > + layer->layer_info.config.ysize, > > + layer->pix_fmt.height); > > + v4l2_err(&vpbe_dev->v4l2_dev, "layer->layer_info.config." > > + "line_length= %d layer->pix_fmt" > > + ".bytesperline = %d\n", > > + layer->layer_info.config.line_length, > > + layer->pix_fmt.bytesperline); > > + v4l2_err(&vpbe_dev->v4l2_dev, "layer->layer_info.config." > > + "interlaced =%d layer->pix_fmt." > > + "field = %d\n", > > + layer->layer_info.config.interlaced, > > + layer->pix_fmt.field); > > + mutex_unlock(&disp_dev->lock); > > + return -EFAULT; > > Should be -EINVAL, EFAULT is only for invalid addresses. > > > + } > > + > > + v4l2_dbg(2, debug, &vpbe_dev->v4l2_dev, > > + "Before finishing with S_FMT:\n" > > + "layer.pix_fmt.bytesperline = %d,\n" > > + "layer.pix_fmt.width = %d,\n" > > + "layer.pix_fmt.height = %d,\n" > > + "layer.pix_fmt.sizeimage =%d\n", > > + layer->pix_fmt.bytesperline, > > + layer->pix_fmt.width, > > + layer->pix_fmt.height, > > + layer->pix_fmt.sizeimage); > > + > > + v4l2_dbg(2, debug, &vpbe_dev->v4l2_dev, > > + "pixfmt->width = %d,\n" > > + " layer->layer_info.config.line_length" > > + "= %d\n", > > + pixfmt->width, > > + layer->layer_info.config.line_length); > > Do you really need all this debug info? I think that this driver is a bit > too verbose. > > > + > > + mutex_unlock(&disp_dev->lock); > > + } else { > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n"); > > + return -EINVAL; > > + } > > + > > + return ret; > > +} > > + > > +static int vpbe_display_try_fmt(struct file *file, void *priv, > > + struct v4l2_format *fmt) > > +{ > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + int ret = 0; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); > > + > > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { > > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > > + /* Check for valid field format */ > > + ret = vpbe_try_format(disp_dev, pixfmt, 0); > > + } else { > > + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); > > + ret = -EINVAL; > > + } > > + > > + return ret; > > +} > > + > > +/** > > + * 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_display_obj *layer = fh->layer; > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + int ret = 0; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_STD\n"); > > + > > + /* Set the given standard in the encoder */ > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + return ret; > > + > > + /* If streaming is started, return error */ > > + if (layer->started) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); > > + mutex_unlock(&disp_dev->lock); > > + 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"); > > + mutex_unlock(&disp_dev->lock); > > + return -EINVAL; > > + } > > + } > > + mutex_unlock(&disp_dev->lock); > > + > > + return ret; > > +} > > + > > +/** > > + * 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_display *disp_dev = video_drvdata(file); > > + int ret = 0; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n"); > > + > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + return ret; > > + > > + /* 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; > > + mutex_unlock(&disp_dev->lock); > > + return 0; > > + } > > + mutex_unlock(&disp_dev->lock); > > + 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_display *disp_dev = video_drvdata(file); > > + int ret = 0; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); > > + > > + /* Enumerate outputs */ > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + return ret; > > + > > + if (NULL != vpbe_dev->ops.enum_outputs) { > > + ret = vpbe_dev->ops.enum_outputs(vpbe_dev, output); > > + if (ret) { > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "Failed to enumerate outputs\n"); > > + mutex_unlock(&disp_dev->lock); > > + return -EINVAL; > > + } > > + } > > + mutex_unlock(&disp_dev->lock); > > + > > + return ret; > > +} > > + > > +/** > > + * 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_display_obj *layer = fh->layer; > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + int ret = 0; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_OUTPUT\n"); > > + > > + /* Set the given standard in the encoder */ > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + return ret; > > + > > + /* If streaming is started, return error */ > > + if (layer->started) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); > > + mutex_unlock(&disp_dev->lock); > > + return -EBUSY; > > + } > > + > > + if (NULL != vpbe_dev->ops.set_output) { > > + 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"); > > + mutex_unlock(&disp_dev->lock); > > + return -EINVAL; > > + } > > + } > > + mutex_unlock(&disp_dev->lock); > > + > > + return ret; > > +} > > + > > +/** > > + * 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_display *disp_dev = video_drvdata(file); > > + int ret = 0; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_OUTPUT\n"); > > + /* Get the standard from the current encoder */ > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + return ret; > > You may want to consider using the new core-assisted locking support. That will > simplify your driver. It seems that this driver just locks at every ioctl, so > in that case it is easier to let the core do that. > > > + *i = vpbe_dev->current_out_index; > > + > > + mutex_unlock(&disp_dev->lock); > > + return ret; > > +} > > + > > +/** > > + * 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_display *disp_dev = video_drvdata(file); > > + int ret = 0; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n"); > > + > > + /* Enumerate outputs */ > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + return ret; > > + > > + if (NULL != vpbe_dev->ops.enum_dv_presets) { > > + 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"); > > + mutex_unlock(&disp_dev->lock); > > + return -EINVAL; > > + } > > + } > > + mutex_unlock(&disp_dev->lock); > > + > > + return ret; > > +} > > + > > +/** > > + * 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_display_obj *layer = fh->layer; > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + int ret = 0; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_DV_PRESETS\n"); > > + > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + return ret; > > + > > + /* If streaming is started, return error */ > > + if (layer->started) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); > > + mutex_unlock(&disp_dev->lock); > > + return -EBUSY; > > + } > > + > > + /* Set the given standard in the encoder */ > > + if (NULL != vpbe_dev->ops.s_dv_preset) { > > + 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"); > > + mutex_unlock(&disp_dev->lock); > > + 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; > > + mutex_unlock(&disp_dev->lock); > > + return ret; > > +} > > + > > +/** > > + * 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_display *disp_dev = video_drvdata(file); > > + int ret = 0; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "VIDIOC_G_DV_PRESETS\n"); > > + > > + /* Get the given standard in the encoder */ > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + return ret; > > + > > + if (vpbe_dev->current_timings.timings_type & > > + VPBE_ENC_DV_PRESET) { > > + dv_preset->preset = > > + vpbe_dev->current_timings.timings.dv_preset; > > + } else { > > + mutex_unlock(&disp_dev->lock); > > + return -EINVAL; > > + } > > + mutex_unlock(&disp_dev->lock); > > + return ret; > > +} > > + > > +static int vpbe_display_streamoff(struct file *file, void *priv, > > + enum v4l2_buf_type buf_type) > > +{ > > + int ret = 0; > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + > > + 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; > > + } > > + > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + return ret; > > + > > + osd_device->ops.disable_layer(osd_device, > > + layer->layer_info.id); > > + layer->started = 0; > > + mutex_unlock(&disp_dev->lock); > > + ret = videobuf_streamoff(&layer->buffer_queue); > > + > > + return ret; > > +} > > + > > +static int vpbe_display_streamon(struct file *file, void *priv, > > + enum v4l2_buf_type buf_type) > > +{ > > + int ret = 0; > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + > > + osd_device->ops.disable_layer(osd_device, > > + layer->layer_info.id); > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "VIDIOC_STREAMON,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 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; > > + } > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + goto streamoff; > > + /* 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 unlock_out; > > + } > > + /* 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_video_display_params(disp_dev, layer); > > + if (ret < 0) > > + goto unlock_out; > > + > > + /* > > + * if request format is yuv420 semiplanar, need to > > + * enable both video windows > > + */ > > + layer->started = 1; > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "Started streaming on layer id = %d," > > + " ret = %d\n", layer->device_id, ret); > > + > > + layer_first_int[layer->device_id] = 1; > > + mutex_unlock(&disp_dev->lock); > > + > > + return ret; > > +unlock_out: > > + mutex_unlock(&disp_dev->lock); > > +streamoff: > > + ret = videobuf_streamoff(&layer->buffer_queue); > > + return ret; > > +} > > + > > +static int vpbe_display_dqbuf(struct file *file, void *priv, > > + struct v4l2_buffer *buf) > > +{ > > + int ret = 0; > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + > > + 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_display_obj *layer = fh->layer; > > + > > + 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) > > +{ > > + int ret = 0; > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + > > + 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) > > +{ > > + int ret = 0; > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + > > + 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; > > + } > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > > + "VIDIOC_REQBUFS, count= %d, type = %d," > > + "memory = %d\n", > > + req_buf->count, req_buf->type, > > + req_buf->memory); > > + > > + /* 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; > > + } > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + return ret; > > + /* 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); > > + mutex_unlock(&disp_dev->lock); > > + > > + 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_display_obj *layer = fh->layer; > > + int err = 0; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_mmap\n"); > > + err = videobuf_mmap_mapper(&layer->buffer_queue, vma); > > Just do 'return videobuf_mmap_mapper(&layer->buffer_queue, vma);' here. No need > for a temp err variable. > > > + return err; > > +} > > + > > +/* vpbe_display_poll(): It is used for select/poll system call > > + */ > > +static unsigned int vpbe_display_poll(struct file *filep, poll_table *wait) > > +{ > > + unsigned int err = 0; > > + struct vpbe_fh *fh = filep->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + > > + 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; > > +} > > + > > +static int vpbe_display_cfg_layer_default(enum vpbe_display_device_id id, > > + struct vpbe_display *disp_dev) > > +{ > > + int err = 0; > > + struct osd_layer_config *layer_config; > > + struct vpbe_display_obj *layer = disp_dev->dev[id]; > > + > > + /* 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"); > > + return -EBUSY; > > + } > > + > > + layer_config = &layer->layer_info.config; > > + /* Set the default image and crop values */ > > + layer_config->pixfmt = PIXFMT_YCbCrI; > > + layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY; > > + layer->pix_fmt.bytesperline = layer_config->line_length = > > + vpbe_dev->current_timings.xres * 2; > > + > > + layer->pix_fmt.width = layer_config->xsize = > > + vpbe_dev->current_timings.xres; > > + layer->pix_fmt.height = layer_config->ysize = > > + vpbe_dev->current_timings.yres; > > + layer->pix_fmt.sizeimage = > > + layer->pix_fmt.bytesperline * layer->pix_fmt.height; > > + layer_config->xpos = 0; > > + layer_config->ypos = 0; > > + layer_config->interlaced = vpbe_dev->current_timings.interlaced; > > + > > + /* > > + * turn off ping-pong buffer and field inversion to fix > > + * the image shaking problem in 1080I mode > > + */ > > + > > + if (layer->layer_info.config.interlaced) > > + layer->pix_fmt.field = V4L2_FIELD_INTERLACED; > > + else > > + layer->pix_fmt.field = V4L2_FIELD_NONE; > > + > > + err = osd_device->ops.set_layer_config(osd_device, > > + layer->layer_info.id, > > + layer_config); > > + if (err < 0) { > > + /* Couldn't set layer */ > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Display Manager failed to set osd layer\n"); > > + return -EBUSY; > > + } > > + > > + return 0; > > +} > > + > > +/* > > + * 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) > > +{ > > + int minor = iminor(file->f_path.dentry->d_inode); > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + struct vpbe_display_obj *layer; > > + struct vpbe_fh *fh = NULL; > > + int found = -1; > > + int i = 0; > > + > > + /* Check for valid minor number */ > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > + /* Get the pointer to the layer object */ > > + layer = disp_dev->dev[i]; > > + if (minor == layer->video_dev->minor) { > > + found = i; > > + break; > > + } > > + } > > + > > + /* If not found, return error no device */ > > + if (0 > found) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "device not found\n"); > > + return -ENODEV; > > + } > > + > > + /* 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) { > > + /* Configure the default values for the layer */ > > + if (vpbe_display_cfg_layer_default(layer->device_id, > > + disp_dev)) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Unable to configure video layer" > > + " for id = %d\n", layer->device_id); > > + 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) > > +{ > > + int ret = 0; > > + struct vpbe_display *disp_dev = video_drvdata(file); > > + /* Get the layer object and file handle object */ > > + struct vpbe_fh *fh = file->private_data; > > + struct vpbe_display_obj *layer = fh->layer; > > + > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); > > + /* If this is doing IO and other layer are not closed */ > > + if ((layer->usrs != 1) && fh->io_allowed) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Close other instances\n"); > > + return -EAGAIN; > > + } > > + /* Get the lock on layer object */ > > + ret = mutex_lock_interruptible(&disp_dev->lock); > > + if (ret) > > + return ret; > > + > > + /* 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 (layer->layer_info.config.pixfmt == PIXFMT_NV12) { > > + struct vpbe_display_obj *otherlayer; > > + otherlayer = > > + _vpbe_display_get_other_win(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); > > + /* unlock mutex on layer object */ > > + mutex_unlock(&disp_dev->lock); > > + > > + disp_dev->cbcr_ofst = 0; > > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); > > + 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, > > + .ioctl = video_ioctl2, > > Use .unlocked_ioctl! Don't rely on the BKL or BKL-replacements, instead use > either the core-assisted locking or do it all in the driver. > > Any new driver still using .ioctl will be NACKed. > > > + .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); > > + > > + if (strcmp("vpbe_controller", pdev->name) == 0) > > + vpbe_dev = platform_get_drvdata(pdev); > > + > > + if (strcmp("vpbe-osd", pdev->name) == 0) > > + osd_device = platform_get_drvdata(pdev); > > + > > + return 0; > > +} > > + > > +/*Configure the channels, buffer size */ > > +static int init_vpbe_layer_objects(int i) > > +{ > > + int free_buffer_index; > > + > > + /* Default number of buffers should be 3 */ > > + if ((video2_numbuffers > 0) && > > + (video2_numbuffers < display_buf_config_params.min_numbuffers)) > > + video2_numbuffers = display_buf_config_params.min_numbuffers; > > + if ((video3_numbuffers > 0) && > > + (video3_numbuffers < display_buf_config_params.min_numbuffers)) > > + video3_numbuffers = display_buf_config_params.min_numbuffers; > > + > > + /* > > + * Set buffer size to min buffers size if invalid > > + * buffer size is given > > + */ > > + if (video2_bufsize < > > + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]) > > + video2_bufsize = > > + display_buf_config_params. > > + min_bufsize[VPBE_DISPLAY_DEVICE_0]; > > + > > + if (video3_bufsize < > > + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]) > > + video3_bufsize = > > + display_buf_config_params. > > + min_bufsize[VPBE_DISPLAY_DEVICE_1]; > > + > > + /* set number of buffers, they could come from boot/args */ > > + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_0] = > > + video2_numbuffers; > > + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_1] = > > + video3_numbuffers; > > + > > + if (display_buf_config_params.numbuffers[0] == 0) > > + printk(KERN_ERR "no vid2 buffer allocated\n"); > > + if (display_buf_config_params.numbuffers[1] == 0) > > + printk(KERN_ERR "no vid3 buffer allocated\n"); > > + free_buffer_index = display_buf_config_params.numbuffers[i - 1]; > > + > > + 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 __init int vpbe_display_probe(struct platform_device *pdev) > > +{ > > + int i, j = 0, k, err = 0; > > + struct vpbe_display *disp_dev; > > + struct video_device *vbd = NULL; > > + struct vpbe_display_obj *vpbe_display_layer = NULL; > > + struct resource *res; > > + 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; > > + } > > + > > + /* Allocate memory for four plane display objects */ > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > + disp_dev->dev[i] = > > + kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL); > > + /* If memory allocation fails, return error */ > > + if (!disp_dev->dev[i]) { > > + printk(KERN_ERR "ran out of memory\n"); > > + err = -ENOMEM; > > + goto probe_out; > > + } > > + spin_lock_init(&disp_dev->dev[i]->irqlock); > > + } > > + > > + err = init_vpbe_layer_objects(i); > > + if (err) { > > + printk(KERN_ERR "Error initializing vpbe display\n"); > > + return err; > > + } > > + > > + /* > > + * 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, NULL, > > + vpbe_device_get); > > + if (err < 0) > > + return err; > > + > > + /* Initialize the vpbe display controller */ > > + if (NULL != vpbe_dev->ops.initialize) { > > + err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev); > > + if (err) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Unable to initalize the " > > + "vpbe display controller.\n"); > > + err = -ENOMEM; > > + goto probe_out; > > + } > > + } > > + > > + /* check the name of davinci device */ > > + if (vpbe_dev->cfg->module_name != NULL) > > + strcpy(vpbe_display_videocap.card, > > + vpbe_dev->cfg->module_name); > > + > > + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { > > + /* Get the pointer to the layer object */ > > + vpbe_display_layer = disp_dev->dev[i]; > > + /* Allocate memory for video device */ > > + vbd = video_device_alloc(); > > + if (vbd == NULL) { > > + for (j = 0; j < i; j++) { > > + video_device_release( > > + disp_dev->dev[j]->video_dev); > > + } > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "ran out of memory\n"); > > + err = -ENOMEM; > > + goto probe_out; > > + } > > + /* Initialize field of video device */ > > + vbd->release = video_device_release; > > + vbd->fops = &vpbe_fops; > > + vbd->ioctl_ops = &vpbe_ioctl_ops; > > + vbd->minor = -1; > > + vbd->v4l2_dev = &vpbe_dev->v4l2_dev; > > + > > + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { > > + vbd->tvnorms = (V4L2_STD_NTSC | V4L2_STD_PAL); > > + vbd->current_norm = > > + 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); > > + > > + /* Set video_dev to the video device */ > > + vpbe_display_layer->video_dev = vbd; > > + vpbe_display_layer->device_id = i; > > + > > + vpbe_display_layer->layer_info.id = > > + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); > > + if (display_buf_config_params.numbuffers[i] == 0) > > + vpbe_display_layer->memory = V4L2_MEMORY_USERPTR; > > + else > > + vpbe_display_layer->memory = V4L2_MEMORY_MMAP; > > + > > + /* Initialize field of the display layer objects */ > > + vpbe_display_layer->usrs = 0; > > + vpbe_display_layer->io_usrs = 0; > > + vpbe_display_layer->started = 0; > > + > > + /* Initialize prio member of layer object */ > > + v4l2_prio_init(&vpbe_display_layer->prio); > > + > > + /* Register video device */ > > + v4l2_info(&vpbe_dev->v4l2_dev, > > + "Trying to register VPBE display device.\n"); > > + v4l2_info(&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, > > + vpbe_display_nr[i]); > > + if (err) > > + goto probe_out; > > + /* set the driver data in platform device */ > > + platform_set_drvdata(pdev, disp_dev); > > + video_set_drvdata(vpbe_display_layer->video_dev, disp_dev); > > + } > > + /* Initialize mutex */ > > + mutex_init(&disp_dev->lock); > > + > > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > + if (!res) { > > + v4l2_err(&vpbe_dev->v4l2_dev, > > + "Unable to get VENC register address map\n"); > > + err = -ENODEV; > > + goto probe_out; > > + } > > + > > + reg_base_venc = ioremap_nocache(res->start, resource_size(res)); > > + if (!reg_base_venc) { > > + v4l2_err(&vpbe_dev->v4l2_dev, "Unable to map VENC IO space\n"); > > + err = -ENODEV; > > + goto probe_out; > > + } > > + > > + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); > > + if (!res) { > > + v4l2_err(&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(&vpbe_dev->v4l2_dev, "Unable to request interrupt\n"); > > + err = -ENODEV; > > + goto probe_out; > > + } > > + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); > > + return 0; > > +probe_out: > > + kfree(disp_dev); > > + > > + for (k = 0; k < j; k++) { > > + /* Get the pointer to the layer object */ > > + vpbe_display_layer = disp_dev->dev[k]; > > + /* Unregister video device */ > > + video_unregister_device(vpbe_display_layer->video_dev); > > + /* Release video device */ > > + video_device_release(vpbe_display_layer->video_dev); > > + vpbe_display_layer->video_dev = NULL; > > + } > > + return err; > > +} > > + > > +/* > > + * vpbe_display_remove() > > + * It un-register hardware layer from V4L2 driver > > + */ > > +static int vpbe_display_remove(struct platform_device *pdev) > > +{ > > + int i; > > + struct vpbe_display_obj *vpbe_display_layer; > > + struct vpbe_display *disp_dev = platform_get_drvdata(pdev); > > + struct resource *res; > > + > > + 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); > > + > > + vpbe_display_layer->video_dev = NULL; > > + } > > + 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 __init int vpbe_display_init(void) > > +{ > > + int err = 0; > > + > > + 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 DMXXX 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..6db5d75 > > --- /dev/null > > +++ b/include/media/davinci/vpbe_display.h > > @@ -0,0 +1,144 @@ > > +/* > > + * 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 "as is" WITHOUT ANY WARRANTY of any > > + * kind, whether express or implied; without even the implied warranty > > + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > + * GNU General Public License for more details. > > + */ > > +#ifndef VPBE_DISPLAY_H > > +#define VPBE_DISPLAY_H > > + > > +#ifdef __KERNEL__ > > + > > +/* 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_display_obj { > > + /* number of buffers in fbuffers */ > > + unsigned int numbuffers; > > + /* 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; > > +}; > > + > > +/* vpbe device structure */ > > +struct vpbe_display { > > + /* layer specifc parameters */ > > typo: specific > > > + /* lock used to access this structure */ > > + struct mutex lock; > > + /* C-Plane offset from start of y-plane */ > > + unsigned int cbcr_ofst; > > + struct vpbe_display_obj *dev[VPBE_DISPLAY_MAX_DEVICES]; > > +}; > > + > > +/* File handle structure */ > > +struct vpbe_fh { > > + /* vpbe device structure */ > > + struct vpbe_display *disp_dev; > > + /* pointer to layer object for opened device */ > > + struct vpbe_display_obj *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]; > > +}; > > + > > +static int venc_is_second_field(void); > > +#endif /* end of __KERNEL__ */ > > +#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..a4b8585 > > --- /dev/null > > +++ b/include/media/davinci/vpbe_types.h > > @@ -0,0 +1,93 @@ > > +/* > > + * 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; either version 2 of the License. > > + * > > + * 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_types { > > + DM644X_VPBE = 1, > > + DM355_VPBE, > > + DM365_VPBE, > > +}; > > + > > +/* 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 > > > > -- > Hans Verkuil - video4linux developer - sponsored by Cisco > From lamiaposta71 at gmail.com Wed Dec 1 13:01:27 2010 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Wed, 1 Dec 2010 20:01:27 +0100 Subject: setting gpios pinmux on a custom board In-Reply-To: References: <4CF53E8A.1040703@mvista.com> <4CF548DD.7020001@mvista.com> <4CF639C2.5020509@mvista.com> Message-ID: On Wed, Dec 1, 2010 at 1:04 PM, Sergei Shtylyov wrote: > Hello. > > On 30-11-2010 23:02, Raffaele Recalcati wrote: > >> >>>> Looking at dm365.c I see that the pinmux settings are thinked for a >> >>>> particular case. >> >>>> How can I, in a clean way, to set my settings? > >> >>> By calls to davinci_cfg_reg() or davinci_cfg_reg_list() > >> >> ok, I'm doing that. > >> >>>> Can I override for instance dm365_pins in davinci_soc_info_dm365 >> >>>> structure with my >> >>>> settings for my basi board, dm365_basi_pins? >> >>> No, and you don't need to. These specify the layout of the PinMux >> >>> registers and are used by davinci_cfg_reg(). > >> >> The problem is, for instance, that UART1_TX can be used on two (or >> more?) >> gpios. > >> > Sorry, I don't understand you. > >> UART1_TXD can be GIO25 or GIO16 instead in dm365 we have one of them > > Ah, you mean that UART1_TX has alternate function as GPIO... even as 2 > GPIOs... > >> MUX_CFG(DM365, UART1_TXD, 3, 15, 3, 2, false) > >> That means GIO16. >> So I can add : > >> MUX_CFG(DM365, UART1_TXD_GIO16, 3, 29, 3, 3, false) > > Bits 29-30 control GPIO25, not GIO16. Yes, I'm sorry. I'm doing like that. MUX_CFG(DM365, UART0_RXD, 3, 20, 1, 1, false) MUX_CFG(DM365, UART0_TXD, 3, 19, 1, 1, false) MUX_CFG(DM365, UART1_RXD, 3, 17, 3, 2, false) /* ENET_TXEN */ MUX_CFG(DM365, UART1_TXD, 3, 15, 3, 2, false) /* ENET_TXC */ MUX_CFG(DM365, UART1_RTS, 3, 23, 3, 1, false) MUX_CFG(DM365, UART1_CTS, 3, 21, 3, 1, false) MUX_CFG(DM365, UART1_RXD_34, 4, 14, 3, 3, false) /* uart1_rx */ MUX_CFG(DM365, UART1_TXD_25, 3, 29, 3, 3, false) /* uart1_tx */ I have checked all gpios settings, now I go on checking on my basi board. -------------- next part -------------- An HTML attachment was scrubbed... URL: From hverkuil at xs4all.nl Wed Dec 1 13:04:35 2010 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Wed, 1 Dec 2010 20:04:35 +0100 Subject: [PATCHi v2 1/6] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: References: Message-ID: <201012012004.35966.hverkuil@xs4all.nl> On Wednesday, December 01, 2010 17:58:35 Hadli, Manjunath wrote: > > > > You may want to consider using the new core-assisted locking support. That > will simplify your driver. It seems that this driver just locks at every > ioctl, so > > in that case it is easier to let the core do that. > > Hans, > I looked into the mailing lists discussion on the core-assisted locking support but could not get a clear picture. Could you point me to the implementation snippet or some example of where it is done? Or for now is it OK to use .unlocked_ioctl? Sure. But it is documented in v4l2-framework.txt, section 'v4l2_file_operations and locking'. The vivi driver uses this in fact. Basically it is nothing more then assigning a pointer to a mutex to the lock field of struct video_device: vdev->lock = &v4l2_dev->lock; After that all fops are serialized through this mutex. Regards, Hans -- Hans Verkuil - video4linux developer - sponsored by Cisco From vm.rod25 at gmail.com Wed Dec 1 13:32:23 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 1 Dec 2010 13:32:23 -0600 Subject: [PATCH v9 0/6] Add Omapl138-Hawkboard support Message-ID: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds EMAC, EDMA, MMC/SD and USB OHCI support for the Hawkboard-L138 system It is under the machine name "omapl138_hawkboard". This system is based on the da850 davinci CPU architecture. Victor Rodriguez (6): davinci: EMAC support for Omapl138-Hawkboard davinci: EDMA support for Omapl138-Hawkboard davinci: MMC/SD and USB-OHCI configuration for Omapl138-Hawkboard davinci: MMC/SD support for Omapl138-Hawkboard davinci: USB clocks for Omapl138-Hawkboard davinci: USB1.1 support for Omapl138-Hawkboard arch/arm/mach-davinci/board-omapl138-hawk.c | 267 +++++++++++++++++++++++++++ arch/arm/mach-davinci/da850.c | 20 ++ arch/arm/mach-davinci/include/mach/mux.h | 4 + 3 files changed, 291 insertions(+), 0 deletions(-) From vm.rod25 at gmail.com Wed Dec 1 13:32:24 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 1 Dec 2010 13:32:24 -0600 Subject: [PATCH v9 1/6] davinci: EMAC support for Omapl138-Hawkboard In-Reply-To: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1291231949-23835-2-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds EMAC support for the Hawkboard-L138 system Signed-off-by: Victor Rodriguez Tested-by: Rene Gonzalez --- arch/arm/mach-davinci/board-omapl138-hawk.c | 43 +++++++++++++++++++++++++++ 1 files changed, 43 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index 0b8dbdb..75b3fe2 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -19,6 +19,47 @@ #include #include +#include + +#define HAWKBOARD_PHY_ID "0:07" + +static short omapl138_hawk_mii_pins[] __initdata = { + DA850_MII_TXEN, DA850_MII_TXCLK, DA850_MII_COL, DA850_MII_TXD_3, + DA850_MII_TXD_2, DA850_MII_TXD_1, DA850_MII_TXD_0, DA850_MII_RXER, + DA850_MII_CRS, DA850_MII_RXCLK, DA850_MII_RXDV, DA850_MII_RXD_3, + DA850_MII_RXD_2, DA850_MII_RXD_1, DA850_MII_RXD_0, DA850_MDIO_CLK, + DA850_MDIO_D, + -1 +}; + +static __init void omapl138_hawk_config_emac(void) +{ + void __iomem *cfgchip3 = DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP3_REG); + int ret; + u32 val; + struct davinci_soc_info *soc_info = &davinci_soc_info; + + val = __raw_readl(cfgchip3); + val &= ~BIT(8); + ret = davinci_cfg_reg_list(omapl138_hawk_mii_pins); + if (ret) { + pr_warning("%s: cpgmac/mii mux setup failed: %d\n", + __func__, ret); + return; + } + + /* configure the CFGCHIP3 register for MII */ + __raw_writel(val, cfgchip3); + pr_info("EMAC: MII PHY configured\n"); + + soc_info->emac_pdata->phy_id = HAWKBOARD_PHY_ID; + + ret = da8xx_register_emac(); + if (ret) + pr_warning("%s: emac registration failed: %d\n", + __func__, ret); +} + static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { .enabled_uarts = 0x7, @@ -30,6 +71,8 @@ static __init void omapl138_hawk_init(void) davinci_serial_init(&omapl138_hawk_uart_config); + omapl138_hawk_config_emac(); + ret = da8xx_register_watchdog(); if (ret) pr_warning("omapl138_hawk_init: " -- 1.7.0.4 From vm.rod25 at gmail.com Wed Dec 1 13:32:25 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 1 Dec 2010 13:32:25 -0600 Subject: [PATCH v9 2/6] davinci: EDMA support for Omapl138-Hawkboard In-Reply-To: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1291231949-23835-3-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds EDMA support for the Hawkboard-L138 system Signed-off-by: Victor Rodriguez Tested-by: Rene Gonzalez --- arch/arm/mach-davinci/board-omapl138-hawk.c | 54 +++++++++++++++++++++++++++ 1 files changed, 54 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index 75b3fe2..cefff9b 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -60,6 +60,55 @@ static __init void omapl138_hawk_config_emac(void) __func__, ret); } +/* + * The following EDMA channels/slots are not being used by drivers (for + * example: Timer, GPIO, UART events etc) on da850/omap-l138 EVM/Hawkboard, + * hence they are being reserved for codecs on the DSP side. + */ +static const s16 da850_dma0_rsv_chans[][2] = { + /* (offset, number) */ + { 8, 6}, + {24, 4}, + {30, 2}, + {-1, -1} +}; + +static const s16 da850_dma0_rsv_slots[][2] = { + /* (offset, number) */ + { 8, 6}, + {24, 4}, + {30, 50}, + {-1, -1} +}; + +static const s16 da850_dma1_rsv_chans[][2] = { + /* (offset, number) */ + { 0, 28}, + {30, 2}, + {-1, -1} +}; + +static const s16 da850_dma1_rsv_slots[][2] = { + /* (offset, number) */ + { 0, 28}, + {30, 90}, + {-1, -1} +}; + +static struct edma_rsv_info da850_edma_cc0_rsv = { + .rsv_chans = da850_dma0_rsv_chans, + .rsv_slots = da850_dma0_rsv_slots, +}; + +static struct edma_rsv_info da850_edma_cc1_rsv = { + .rsv_chans = da850_dma1_rsv_chans, + .rsv_slots = da850_dma1_rsv_slots, +}; + +static struct edma_rsv_info *da850_edma_rsv[2] = { + &da850_edma_cc0_rsv, + &da850_edma_cc1_rsv, +}; static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { .enabled_uarts = 0x7, @@ -73,6 +122,11 @@ static __init void omapl138_hawk_init(void) omapl138_hawk_config_emac(); + ret = da850_register_edma(da850_edma_rsv); + if (ret) + pr_warning("%s: EDMA registration failed: %d\n", + __func__, ret); + ret = da8xx_register_watchdog(); if (ret) pr_warning("omapl138_hawk_init: " -- 1.7.0.4 From vm.rod25 at gmail.com Wed Dec 1 13:32:26 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 1 Dec 2010 13:32:26 -0600 Subject: [PATCH v9 3/6] davinci: MMC/SD and USB-OHCI configuration for Omapl138-Hawkboard In-Reply-To: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1291231949-23835-4-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch defines Pin Mux configuration to enable MMC/SD and USB-OHCI on the Hawkboard-L138 system Signed-off-by: Victor Rodriguez Tested-by: Rene Gonzalez --- arch/arm/mach-davinci/da850.c | 4 ++++ arch/arm/mach-davinci/include/mach/mux.h | 4 ++++ 2 files changed, 8 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 63916b9..80803fa 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -543,11 +543,15 @@ static const struct mux_config da850_pins[] = { MUX_CFG(DA850, EMA_WAIT_1, 6, 24, 15, 1, false) MUX_CFG(DA850, NEMA_CS_2, 7, 0, 15, 1, false) /* GPIO function */ + MUX_CFG(DA850, GPIO2_4, 6, 12, 15, 8, false) MUX_CFG(DA850, GPIO2_6, 6, 4, 15, 8, false) MUX_CFG(DA850, GPIO2_8, 5, 28, 15, 8, false) MUX_CFG(DA850, GPIO2_15, 5, 0, 15, 8, false) + MUX_CFG(DA850, GPIO3_12, 7, 12, 15, 8, false) + MUX_CFG(DA850, GPIO3_13, 7, 8, 15, 8, false) MUX_CFG(DA850, GPIO4_0, 10, 28, 15, 8, false) MUX_CFG(DA850, GPIO4_1, 10, 24, 15, 8, false) + MUX_CFG(DA850, GPIO6_13, 13, 8, 15, 8, false) MUX_CFG(DA850, RTC_ALARM, 0, 28, 15, 2, false) #endif }; diff --git a/arch/arm/mach-davinci/include/mach/mux.h b/arch/arm/mach-davinci/include/mach/mux.h index de11aac..5d4e0fe 100644 --- a/arch/arm/mach-davinci/include/mach/mux.h +++ b/arch/arm/mach-davinci/include/mach/mux.h @@ -908,11 +908,15 @@ enum davinci_da850_index { DA850_NEMA_CS_2, /* GPIO function */ + DA850_GPIO2_4, DA850_GPIO2_6, DA850_GPIO2_8, DA850_GPIO2_15, + DA850_GPIO3_12, + DA850_GPIO3_13, DA850_GPIO4_0, DA850_GPIO4_1, + DA850_GPIO6_13, DA850_RTC_ALARM, }; -- 1.7.0.4 From vm.rod25 at gmail.com Wed Dec 1 13:32:27 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 1 Dec 2010 13:32:27 -0600 Subject: [PATCH v9 4/6] davinci: MMC/SD support for Omapl138-Hawkboard In-Reply-To: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1291231949-23835-5-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds MMC/SD support for the Hawkboard-L138 system Signed-off-by: Victor Rodriguez Tested-by: Rene Gonzalez --- Notes: This patch works with da8xx_omapl_defconfig In order to test it select in menuconfig like insmodule MMC/SD/SDIO card support ---> MMC block device driver Use bounce buffer for simple hosts TI DAVINCI Multimedia Card Interface support arch/arm/mach-davinci/board-omapl138-hawk.c | 63 +++++++++++++++++++++++++++ 1 files changed, 63 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index cefff9b..d0c853f 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -22,6 +22,8 @@ #include #define HAWKBOARD_PHY_ID "0:07" +#define DA850_HAWK_MMCSD_CD_PIN GPIO_TO_PIN(3, 12) +#define DA850_HAWK_MMCSD_WP_PIN GPIO_TO_PIN(3, 13) static short omapl138_hawk_mii_pins[] __initdata = { DA850_MII_TXEN, DA850_MII_TXCLK, DA850_MII_COL, DA850_MII_TXD_3, @@ -110,6 +112,65 @@ static struct edma_rsv_info *da850_edma_rsv[2] = { &da850_edma_cc1_rsv, }; +static const short hawk_mmcsd0_pins[] = { + DA850_MMCSD0_DAT_0, DA850_MMCSD0_DAT_1, DA850_MMCSD0_DAT_2, + DA850_MMCSD0_DAT_3, DA850_MMCSD0_CLK, DA850_MMCSD0_CMD, + DA850_GPIO3_12, DA850_GPIO3_13, + -1 +}; + +static int da850_hawk_mmc_get_ro(int index) +{ + return gpio_get_value(DA850_HAWK_MMCSD_WP_PIN); +} + +static int da850_hawk_mmc_get_cd(int index) +{ + return !gpio_get_value(DA850_HAWK_MMCSD_CD_PIN); +} + +static struct davinci_mmc_config da850_mmc_config = { + .get_ro = da850_hawk_mmc_get_ro, + .get_cd = da850_hawk_mmc_get_cd, + .wires = 4, + .max_freq = 50000000, + .caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED, + .version = MMC_CTLR_VERSION_2, +}; + +static __init void omapl138_hawk_mmc_init(void) +{ + int ret; + + ret = davinci_cfg_reg_list(hawk_mmcsd0_pins); + if (ret) { + pr_warning("%s: MMC/SD0 mux setup failed: %d\n", + __func__, ret); + return; + } + + ret = gpio_request_one(DA850_HAWK_MMCSD_CD_PIN, + GPIOF_DIR_IN, "MMC CD"); + if (ret < 0) { + pr_warning("%s: can not open GPIO %d\n", + __func__, DA850_HAWK_MMCSD_CD_PIN); + return; + } + + ret = gpio_request_one(DA850_HAWK_MMCSD_WP_PIN, + GPIOF_DIR_IN, "MMC WP"); + if (ret < 0) { + pr_warning("%s: can not open GPIO %d\n", + __func__, DA850_HAWK_MMCSD_WP_PIN); + return; + } + + ret = da8xx_register_mmcsd0(&da850_mmc_config); + if (ret) + pr_warning("%s: MMC/SD0 registration failed: %d\n", + __func__, ret); +} + static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { .enabled_uarts = 0x7, }; @@ -127,6 +188,8 @@ static __init void omapl138_hawk_init(void) pr_warning("%s: EDMA registration failed: %d\n", __func__, ret); + omapl138_hawk_mmc_init(); + ret = da8xx_register_watchdog(); if (ret) pr_warning("omapl138_hawk_init: " -- 1.7.0.4 From vm.rod25 at gmail.com Wed Dec 1 13:32:28 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 1 Dec 2010 13:32:28 -0600 Subject: [PATCH v9 5/6] davinci: USB clocks for Omapl138-Hawkboard In-Reply-To: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1291231949-23835-6-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds USB1.1 and USB2.0 clocks for the Hawkboard-L138 system Signed-off-by: Victor Rodriguez Tested-by: Rene Gonzalez --- arch/arm/mach-davinci/da850.c | 16 ++++++++++++++++ 1 files changed, 16 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 80803fa..93a4df4 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -345,6 +345,20 @@ static struct clk aemif_clk = { .flags = ALWAYS_ENABLED, }; +static struct clk usb11_clk = { + .name = "usb11", + .parent = &pll0_sysclk4, + .lpsc = DA8XX_LPSC1_USB11, + .gpsc = 1, +}; + +static struct clk usb20_clk = { + .name = "usb20", + .parent = &pll0_sysclk2, + .lpsc = DA8XX_LPSC1_USB20, + .gpsc = 1, +}; + static struct clk_lookup da850_clks[] = { CLK(NULL, "ref", &ref_clk), CLK(NULL, "pll0", &pll0_clk), @@ -387,6 +401,8 @@ static struct clk_lookup da850_clks[] = { CLK("davinci_mmc.0", NULL, &mmcsd0_clk), CLK("davinci_mmc.1", NULL, &mmcsd1_clk), CLK(NULL, "aemif", &aemif_clk), + CLK(NULL, "usb11", &usb11_clk), + CLK(NULL, "usb20", &usb20_clk), CLK(NULL, NULL, NULL), }; -- 1.7.0.4 From vm.rod25 at gmail.com Wed Dec 1 13:32:29 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 1 Dec 2010 13:32:29 -0600 Subject: [PATCH v9 6/6] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1291231949-23835-7-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds USB1.1 support for the Hawkboard-L138 system Signed-off-by: Victor Rodriguez Tested-by: Rene Gonzalez --- Notes: This patch works with da8xx_omapl_defconfig In order to test it select in menuconfig like insmodule Device Drivers ---> SCSI device support ---> SCSI device support legacy /proc/scsi/ support SCSI disk support SCSI low-level drivers USB support ---> Support for Host-side USB OHCI HCD support (NEW) USB Mass Storage support (NEW) USB Gadget Support ---> USB Gadget Drivers (Ethernet Gadget\ (with CDC Ethernet support)) ---> NOP USB Transceiver Driver And you will be able to mount and USB pen drive In order to connect a keyboard or a mouse on a USB-hub select in menuconfig like insmodule HID Devices ---> Generic HID support USB Human Interface Device (full HID) support arch/arm/mach-davinci/board-omapl138-hawk.c | 106 +++++++++++++++++++++++++++ 1 files changed, 106 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index d0c853f..da51136 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -25,6 +25,9 @@ #define DA850_HAWK_MMCSD_CD_PIN GPIO_TO_PIN(3, 12) #define DA850_HAWK_MMCSD_WP_PIN GPIO_TO_PIN(3, 13) +#define DA850_USB1_VBUS_PIN GPIO_TO_PIN(2, 4) +#define DA850_USB1_OC_PIN GPIO_TO_PIN(6, 13) + static short omapl138_hawk_mii_pins[] __initdata = { DA850_MII_TXEN, DA850_MII_TXCLK, DA850_MII_COL, DA850_MII_TXD_3, DA850_MII_TXD_2, DA850_MII_TXD_1, DA850_MII_TXD_0, DA850_MII_RXER, @@ -171,6 +174,107 @@ static __init void omapl138_hawk_mmc_init(void) __func__, ret); } +static irqreturn_t omapl138_hawk_usb_ocic_irq(int irq, void *dev_id); +static da8xx_ocic_handler_t hawk_usb_ocic_handler; + +static const short da850_hawk_usb11_pins[] = { + DA850_GPIO2_4, DA850_GPIO6_13, + -1 +}; + +static int hawk_usb_set_power(unsigned port, int on) +{ + gpio_set_value(DA850_USB1_VBUS_PIN, on); + return 0; +} + +static int hawk_usb_get_power(unsigned port) +{ + return gpio_get_value(DA850_USB1_VBUS_PIN); +} + +static int hawk_usb_get_oci(unsigned port) +{ + return !gpio_get_value(DA850_USB1_OC_PIN); +} + +static int hawk_usb_ocic_notify(da8xx_ocic_handler_t handler) +{ + int irq = gpio_to_irq(DA850_USB1_OC_PIN); + int error = 0; + + if (handler != NULL) { + hawk_usb_ocic_handler = handler; + + error = request_irq(irq, omapl138_hawk_usb_ocic_irq, + IRQF_DISABLED | IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + "OHCI over-current indicator", NULL); + if (error) + pr_err(KERN_ERR "%s: could not request IRQ to watch " + "over-current indicator changes\n", __func__); + } else { + free_irq(irq, NULL); + } + return error; +} + +static struct da8xx_ohci_root_hub omapl138_hawk_usb11_pdata = { + .set_power = hawk_usb_set_power, + .get_power = hawk_usb_get_power, + .get_oci = hawk_usb_get_oci, + .ocic_notify = hawk_usb_ocic_notify, + /* TPS2087 switch @ 5V */ + .potpgt = (3 + 1) / 2, /* 3 ms max */ +}; + +static irqreturn_t omapl138_hawk_usb_ocic_irq(int irq, void *dev_id) +{ + hawk_usb_ocic_handler(&omapl138_hawk_usb11_pdata, 1); + return IRQ_HANDLED; +} + +static __init void omapl138_hawk_usb_init(void) +{ + int ret; + u32 cfgchip2; + + ret = davinci_cfg_reg_list(da850_hawk_usb11_pins); + if (ret) { + pr_warning("%s: USB 1.1 PinMux setup failed: %d\n", + __func__, ret); + return; + } + + /* Setup the Ref. clock frequency for the HAWK at 24 MHz. */ + + cfgchip2 = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); + cfgchip2 &= ~CFGCHIP2_REFFREQ; + cfgchip2 |= CFGCHIP2_REFFREQ_24MHZ; + __raw_writel(cfgchip2, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); + + ret = gpio_request_one(DA850_USB1_VBUS_PIN, + GPIOF_DIR_OUT, "USB1 VBUS"); + if (ret < 0) { + pr_err(KERN_ERR "%s: failed to request GPIO for USB 1.1 port " + "power control: %d\n", __func__, ret); + return; + } + + ret = gpio_request_one(DA850_USB1_OC_PIN, + GPIOF_DIR_IN, "USB1 OC"); + if (ret < 0) { + pr_err(KERN_ERR "%s: failed to request GPIO for USB 1.1 port " + "over-current indicator: %d\n", __func__, ret); + return; + } + + ret = da8xx_register_usb11(&omapl138_hawk_usb11_pdata); + if (ret) + pr_warning("%s: USB 1.1 registration failed: %d\n", + __func__, ret); +} + static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { .enabled_uarts = 0x7, }; @@ -190,6 +294,8 @@ static __init void omapl138_hawk_init(void) omapl138_hawk_mmc_init(); + omapl138_hawk_usb_init(); + ret = da8xx_register_watchdog(); if (ret) pr_warning("omapl138_hawk_init: " -- 1.7.0.4 From sshtylyov at mvista.com Wed Dec 1 13:38:32 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Wed, 01 Dec 2010 22:38:32 +0300 Subject: [PATCH 2/2] da850-evm: Board file modifications TI's PRU CAN. In-Reply-To: References: <1291194315-4629-1-git-send-email-subhasish@mistralsolutions.com> <1291194315-4629-2-git-send-email-subhasish@mistralsolutions.com> <4CF635AC.7000807@mvista.com> Message-ID: <4CF6A438.8050901@mvista.com> Hello. S.Ghosh wrote: >>> + DA850_PRU0_R31_0, DA850_PRU1_R30_15, DA850_PRU1_R31_18, >> I thought you wanted to use GPIO2[0] too? > [SG] -- The GPIO has been removed from this list and added separately. Where? If you intend to use GPIO2[0], you should configure PinMux for it. >>> + if (ret) >>> + pr_warning("da850_evm_init: >> You're not in that function. Use the '__func__' variable to print >> the current function's name. > [SG] -- I have followed similar convention as the rest of the file. For > example, da850_evm_setup_nor_nand. This is bad example then... WBR, Sergei From dedekind1 at gmail.com Wed Dec 1 21:50:15 2010 From: dedekind1 at gmail.com (Artem Bityutskiy) Date: Thu, 02 Dec 2010 05:50:15 +0200 Subject: [PATCH v4 1/2] mtd: NOR flash driver for OMAP-L137/AM17x In-Reply-To: <1291111325-25784-1-git-send-email-savinay.dharmappa@ti.com> References: <1291111325-25784-1-git-send-email-savinay.dharmappa@ti.com> Message-ID: <1291261815.14534.14.camel@koala> On Tue, 2010-11-30 at 15:32 +0530, Savinay Dharmappa wrote: > From: David Griego > > OMAP-L137/AM17x has limited number of dedicated EMIFA > address pins, enough to interface directly to an SDRAM. > If a device such as an asynchronous flash needs to be > attached to the EMIFA, then either GPIO pins or a chip > select may be used to control the flash device's upper > address lines. > > This patch adds support for the NOR flash on the OMAP-L137/ > AM17x user interface daughter board using the latch-addr-flash > MTD mapping driver which allows flashes to be partially > physically addressed. The upper address lines are set by > a board specific code which is a separate patch. > > Signed-off-by: David Griego > Signed-off-by: Aleksey Makarov > Signed-off-by: Sergei Shtylyov > Signed-off-by: Savinay Dharmappa Pushed these to l2-mtd-2.6.git, thanks. -- Best Regards, Artem Bityutskiy (???????? ?????) From nsekhar at ti.com Thu Dec 2 00:49:13 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 2 Dec 2010 12:19:13 +0530 Subject: [PATCH v9 6/6] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: <1291231949-23835-7-git-send-email-vm.rod25@gmail.com> References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> <1291231949-23835-7-git-send-email-vm.rod25@gmail.com> Message-ID: On Thu, Dec 02, 2010 at 01:02:29, vm.rod25 at gmail.com wrote: > +static int hawk_usb_ocic_notify(da8xx_ocic_handler_t handler) > +{ > + int irq = gpio_to_irq(DA850_USB1_OC_PIN); > + int error = 0; > + > + if (handler != NULL) { > + hawk_usb_ocic_handler = handler; > + > + error = request_irq(irq, omapl138_hawk_usb_ocic_irq, > + IRQF_DISABLED | IRQF_TRIGGER_RISING | > + IRQF_TRIGGER_FALLING, > + "OHCI over-current indicator", NULL); > + if (error) > + pr_err(KERN_ERR "%s: could not request IRQ to watch " > + "over-current indicator changes\n", __func__); pr_err adds a KERN_ERR already. > +static __init void omapl138_hawk_usb_init(void) > +{ > + int ret; > + u32 cfgchip2; > + > + ret = davinci_cfg_reg_list(da850_hawk_usb11_pins); > + if (ret) { > + pr_warning("%s: USB 1.1 PinMux setup failed: %d\n", > + __func__, ret); > + return; > + } > + > + /* Setup the Ref. clock frequency for the HAWK at 24 MHz. */ > + > + cfgchip2 = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); > + cfgchip2 &= ~CFGCHIP2_REFFREQ; > + cfgchip2 |= CFGCHIP2_REFFREQ_24MHZ; > + __raw_writel(cfgchip2, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); > + > + ret = gpio_request_one(DA850_USB1_VBUS_PIN, > + GPIOF_DIR_OUT, "USB1 VBUS"); > + if (ret < 0) { > + pr_err(KERN_ERR "%s: failed to request GPIO for USB 1.1 port " > + "power control: %d\n", __func__, ret); > + return; > + } > + > + ret = gpio_request_one(DA850_USB1_OC_PIN, > + GPIOF_DIR_IN, "USB1 OC"); > + if (ret < 0) { > + pr_err(KERN_ERR "%s: failed to request GPIO for USB 1.1 port " > + "over-current indicator: %d\n", __func__, ret); > + return; > + } Should free the gpio DA850_USB1_VBUS_PIN in this error path. This is also valid for MMC/SD gpio acquisition in patch 4/6. Thanks, Sekhar From nsekhar at ti.com Thu Dec 2 06:27:01 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Thu, 2 Dec 2010 17:57:01 +0530 Subject: [PATCH v3 1/2] davinci: am18x/da850/omap-l138: add support for higher speed grades Message-ID: <1291292822-20232-1-git-send-email-nsekhar@ti.com> AM18x/DA850/OMAP-L138 SoCs have variants that can operate at a maximum of 456 MHz at 1.3V operating point. Also the 1.2V operating point has a variant that can support a maximum of 375 MHz. This patch adds three new OPPs (456 MHz, 408 MHz and 372 MHz) to the list of DA850 OPPs. Not all silicon is qualified to run at higher speeds and unfortunately the maximum speed the chip can support can only be determined from the label on the package (not software readable). Because of this, we depend on the maximum speed grade information to be provided to us in some board specific way. The board informs the maximum speed grade information to the SoC by calling the da850_set_max_speed() function. Signed-off-by: Sekhar Nori --- Since v2, fixed comments from Sergei here: https://patchwork.kernel.org/patch/366501/ arch/arm/mach-davinci/da850.c | 72 +++++++++++++++++++++++----- arch/arm/mach-davinci/include/mach/da8xx.h | 7 +++ 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 63916b9..a343225 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -842,6 +842,33 @@ struct da850_opp { unsigned int cvdd_max; /* in uV */ }; +static const struct da850_opp da850_opp_456 = { + .freq = 456000, + .prediv = 1, + .mult = 19, + .postdiv = 1, + .cvdd_min = 1300000, + .cvdd_max = 1350000, +}; + +static const struct da850_opp da850_opp_408 = { + .freq = 408000, + .prediv = 1, + .mult = 17, + .postdiv = 1, + .cvdd_min = 1300000, + .cvdd_max = 1350000, +}; + +static const struct da850_opp da850_opp_372 = { + .freq = 372000, + .prediv = 1, + .mult = 31, + .postdiv = 2, + .cvdd_min = 1200000, + .cvdd_max = 1320000, +}; + static const struct da850_opp da850_opp_300 = { .freq = 300000, .prediv = 1, @@ -876,6 +903,9 @@ static const struct da850_opp da850_opp_96 = { } static struct cpufreq_frequency_table da850_freq_table[] = { + OPP(456), + OPP(408), + OPP(372), OPP(300), OPP(200), OPP(96), @@ -886,6 +916,19 @@ static struct cpufreq_frequency_table da850_freq_table[] = { }; #ifdef CONFIG_REGULATOR +static int da850_set_voltage(unsigned int index); +static int da850_regulator_init(void); +#endif + +static struct davinci_cpufreq_config cpufreq_info = { + .freq_table = da850_freq_table, +#ifdef CONFIG_REGULATOR + .init = da850_regulator_init, + .set_voltage = da850_set_voltage, +#endif +}; + +#ifdef CONFIG_REGULATOR static struct regulator *cvdd; static int da850_set_voltage(unsigned int index) @@ -895,7 +938,7 @@ static int da850_set_voltage(unsigned int index) if (!cvdd) return -ENODEV; - opp = (struct da850_opp *) da850_freq_table[index].index; + opp = (struct da850_opp *) cpufreq_info.freq_table[index].index; return regulator_set_voltage(cvdd, opp->cvdd_min, opp->cvdd_max); } @@ -912,14 +955,6 @@ static int da850_regulator_init(void) } #endif -static struct davinci_cpufreq_config cpufreq_info = { - .freq_table = &da850_freq_table[0], -#ifdef CONFIG_REGULATOR - .init = da850_regulator_init, - .set_voltage = da850_set_voltage, -#endif -}; - static struct platform_device da850_cpufreq_device = { .name = "cpufreq-davinci", .dev = { @@ -928,12 +963,22 @@ static struct platform_device da850_cpufreq_device = { .id = -1, }; +unsigned int da850_max_speed = 300000; + int __init da850_register_cpufreq(char *async_clk) { + int i; + /* cpufreq driver can help keep an "async" clock constant */ if (async_clk) clk_add_alias("async", da850_cpufreq_device.name, async_clk, NULL); + for (i = 0; i < ARRAY_SIZE(da850_freq_table); i++) { + if (da850_freq_table[i].frequency <= da850_max_speed) { + cpufreq_info.freq_table = &da850_freq_table[i]; + break; + } + } return platform_device_register(&da850_cpufreq_device); } @@ -942,17 +987,18 @@ static int da850_round_armrate(struct clk *clk, unsigned long rate) { int i, ret = 0, diff; unsigned int best = (unsigned int) -1; + struct cpufreq_frequency_table *table = cpufreq_info.freq_table; rate /= 1000; /* convert to kHz */ - for (i = 0; da850_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { - diff = da850_freq_table[i].frequency - rate; + for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { + diff = table[i].frequency - rate; if (diff < 0) diff = -diff; if (diff < best) { best = diff; - ret = da850_freq_table[i].frequency; + ret = table[i].frequency; } } @@ -973,7 +1019,7 @@ static int da850_set_pll0rate(struct clk *clk, unsigned long index) struct pll_data *pll = clk->pll_data; int ret; - opp = (struct da850_opp *) da850_freq_table[index].index; + opp = (struct da850_opp *) cpufreq_info.freq_table[index].index; prediv = opp->prediv; mult = opp->mult; postdiv = opp->postdiv; diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index 4247b3f..e7f9520 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -28,6 +28,13 @@ extern void __iomem *da8xx_syscfg0_base; extern void __iomem *da8xx_syscfg1_base; /* + * If the DA850/OMAP-L138/AM18x SoC on board is of a higher speed grade + * (than the regular 300Mhz variant), the board code should set this up + * with the supported speed before calling da850_register_cpufreq(). + */ +extern unsigned int da850_max_speed; + +/* * The cp_intc interrupt controller for the da8xx isn't in the same * chunk of physical memory space as the other registers (like it is * on the davincis) so it needs to be mapped separately. It will be -- 1.7.3.2 From nsekhar at ti.com Thu Dec 2 06:27:02 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Thu, 2 Dec 2010 17:57:02 +0530 Subject: [PATCH v3 2/2] davinci: am18x/da850/omap-l138 evm: add support for higher speed grades In-Reply-To: <1291292822-20232-1-git-send-email-nsekhar@ti.com> References: <1291292822-20232-1-git-send-email-nsekhar@ti.com> Message-ID: <1291292822-20232-2-git-send-email-nsekhar@ti.com> Apart from the regular AM18x/DA850/OMAP-L138 SoC operating at 300MHz, these SoCs have variants that can operate at a maximum of 456MHz. Variants at 408Mhz and 375 Mhz are available as well. Not all silicon is qualified to run at higher speeds and unfortunately the maximum speed the chip can support can only be determined from the label on the package (not software readable). The EVM hardware for all these variants is the same (except for the actual SoC populated). U-Boot on the EVM sets up ATAG_REVISION to inform the OS regarding the speed grade supported by the silicon. We use this information to pass on the speed grade information to the SoC code. Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/board-da850-evm.c | 25 +++++++++++++++++++++++-- 1 files changed, 23 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index f89b0b7..893d9be 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -540,7 +540,7 @@ static struct regulator_init_data tps65070_regulator_data[] = { { .constraints = { .min_uV = 950000, - .max_uV = 1320000, + .max_uV = 1350000, .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS), .boot_on = 1, @@ -736,6 +736,27 @@ static struct edma_rsv_info *da850_edma_rsv[2] = { &da850_edma_cc1_rsv, }; +#ifdef CONFIG_CPU_FREQ +static __init int da850_evm_init_cpufreq(void) +{ + switch (system_rev & 0xF) { + case 3: + da850_max_speed = 456000; + break; + case 2: + da850_max_speed = 408000; + break; + case 1: + da850_max_speed = 372000; + break; + } + + return da850_register_cpufreq("pll0_sysclk3"); +} +#else +static __init int da850_evm_init_cpufreq(void) { return 0; } +#endif + static __init void da850_evm_init(void) { int ret; @@ -836,7 +857,7 @@ static __init void da850_evm_init(void) if (ret) pr_warning("da850_evm_init: rtc setup failed: %d\n", ret); - ret = da850_register_cpufreq("pll0_sysclk3"); + ret = da850_evm_init_cpufreq(); if (ret) pr_warning("da850_evm_init: cpufreq registration failed: %d\n", ret); -- 1.7.3.2 From manjunath.hadli at ti.com Thu Dec 2 06:37:34 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 2 Dec 2010 18:07:34 +0530 Subject: [PATCH v3 0/6] davinci vpbe: DM6446 V4l2 display driver Message-ID: <1291293454-1783-1-git-send-email-manjunath.hadli@ti.com> V4L2 Display driver for Davici vpbe IP. Dtails in the readme.txt update on 6th patch. version3 : addressed Sekhar,Hans's and Murali's comments on: 1. removing the unwanted if_config 2. using core-assisted locking 3. simplifying the code by reducing the length 4. introdcuing spinlock for shared access 5. removing longisg error prints 6. reduced dereferncing levels to max 3 7. Intriduced the txt file with driver description 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: platform specific additions davinci vpbe: Build infrastructure for VPBE driver arch/arm/mach-davinci/board-dm644x-evm.c | 79 +- arch/arm/mach-davinci/dm644x.c | 164 ++- arch/arm/mach-davinci/include/mach/dm644x.h | 4 + drivers/media/video/davinci/Kconfig | 22 + drivers/media/video/davinci/Makefile | 2 + .../media/video/davinci/davinci_vpbe_readme.txt | 100 + drivers/media/video/davinci/vpbe.c | 847 ++++++++ drivers/media/video/davinci/vpbe_display.c | 2103 ++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd.c | 1210 +++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 389 ++++ drivers/media/video/davinci/vpbe_venc.c | 574 ++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 189 ++ include/media/davinci/vpbe.h | 186 ++ include/media/davinci/vpbe_display.h | 146 ++ include/media/davinci/vpbe_osd.h | 397 ++++ include/media/davinci/vpbe_types.h | 93 + include/media/davinci/vpbe_venc.h | 38 + 17 files changed, 6524 insertions(+), 19 deletions(-) create mode 100644 drivers/media/video/davinci/davinci_vpbe_readme.txt 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 Thu Dec 2 06:38:05 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 2 Dec 2010 18:08:05 +0530 Subject: [PATCH v3 1/6] davinci vpbe: V4L2 display driver for DM644X SoC Message-ID: <1291293485-2009-1-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 is 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 Signed-off-by: Muralidharan Karicheri --- drivers/media/video/davinci/vpbe_display.c | 2103 ++++++++++++++++++++++++++++ include/media/davinci/vpbe_display.h | 146 ++ include/media/davinci/vpbe_types.h | 93 ++ 3 files changed, 2342 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..7a2d447 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_display.c @@ -0,0 +1,2103 @@ +/* + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#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 +#include "vpbe_venc_regs.h" + + +#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" + +static int debug; +static u32 video2_numbuffers = 3; +static u32 video3_numbuffers = 3; + +#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) +#define VPBE_DEFAULT_NUM_BUFS 3 + +static u32 video2_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; +static u32 video3_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; + +module_param(video2_numbuffers, uint, S_IRUGO); +module_param(video3_numbuffers, uint, S_IRUGO); +module_param(video2_bufsize, uint, S_IRUGO); +module_param(video3_bufsize, uint, S_IRUGO); +module_param(debug, int, 0644); + +static struct buf_config_params display_buf_config_params = { + .min_numbuffers = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[0] = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[1] = VPBE_DEFAULT_NUM_BUFS, + .min_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .min_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, +}; + +static struct vpbe_device *vpbe_dev; +static struct osd_state *osd_device; +static int vpbe_display_nr[] = { 2, 3 }; + +static struct v4l2_capability vpbe_display_videocap = { + .driver = VPBE_DISPLAY_DRIVER, + .bus_info = "platform", + .version = VPBE_DISPLAY_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, +}; + +static u8 layer_first_int[VPBE_DISPLAY_MAX_DEVICES]; + +__iomem void *reg_base_venc; + +static int venc_is_second_field() +{ + u32 val; + val = __raw_readl(reg_base_venc + VENC_VSTAT); + return ((val & VENC_VSTAT_FIDST) + == VENC_VSTAT_FIDST); +} + +/* + * vpbe_display_isr() + * ISR function. It changes status of the displayed buffer, takes next buffer + * from the queue and sets its address in VPBE registers + */ +static void vpbe_display_isr(unsigned int event, void *disp_obj) +{ + unsigned long jiffies_time = get_jiffies_64(); + struct timeval timevalue; + int i, fid; + unsigned long addr = 0; + struct vpbe_display_obj *layer = NULL; + struct vpbe_display *disp_dev = (struct vpbe_display *)disp_obj; + + /* Convert time represention from jiffies to timeval */ + jiffies_to_timeval(jiffies_time, &timevalue); + + 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; + /* Check the field format */ + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && + (event & OSD_END_OF_FRAME)) { + /* Progressive mode */ + if (layer_first_int[i]) + layer_first_int[i] = 0; + continue; + /* + * Mark status of the cur_frm to + * done and unlock semaphore on it + */ + + if (layer->cur_frm != layer->next_frm) { + layer->cur_frm->ts = timevalue; + 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; + } + /* Get the next buffer from buffer queue */ + spin_lock(&disp_dev->dma_queue_lock); + if (!list_empty(&layer->dma_queue)) { + layer->next_frm = + list_entry(layer->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove that buffer from the buffer queue */ + list_del(&layer->next_frm->queue); + /* Mark status of the buffer as 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_dev->cbcr_ofst); + } + spin_unlock(&disp_dev->dma_queue_lock); + } else { + /* + * Interlaced mode + * If it is first interrupt, ignore it + */ + if (layer_first_int[i]) { + layer_first_int[i] = 0; + return; + } + + layer->field_id ^= 1; + if (event & OSD_FIRST_FIELD) + fid = 0; + else if (event & OSD_SECOND_FIELD) + fid = 1; + else + return; + + /* + * If field id does not match with stored + * field id + */ + if (fid != layer->field_id) { + /* Make them in sync */ + if (0 == fid) + layer->field_id = fid; + + return; + } + /* + * device field id and local field id are + * in sync. If this is even field + */ + if (0 == fid) { + if (layer->cur_frm == layer->next_frm) + continue; + /* + * one frame is displayed If next frame is + * available, release cur_frm and move on + * copy frame display time + */ + layer->cur_frm->ts = timevalue; + /* Change status of the cur_frm */ + layer->cur_frm->state = VIDEOBUF_DONE; + /* unlock semaphore on cur_frm */ + wake_up_interruptible(&layer->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + layer->cur_frm = layer->next_frm; + } else if (1 == fid) { /* odd field */ + + if (list_empty(&layer->dma_queue) + || (layer->cur_frm != layer->next_frm)) + continue; + + /* + * one field is displayed configure + * the next frame if it is available + * otherwise hold on current frame + * Get next from the buffer queue + */ + spin_lock(&disp_dev->dma_queue_lock); + 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); + + /* 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_dev->cbcr_ofst); + spin_unlock(&disp_dev->dma_queue_lock); + } + } + } +} + +/* interrupt service routine */ +static irqreturn_t venc_isr(int irq, void *arg) +{ + static unsigned last_event; + unsigned event = 0; + + if (venc_is_second_field()) + 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; + + vpbe_display_isr(event, arg); + 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_display_obj *layer = fh->layer; + unsigned long addr; + int ret = 0; + + 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_display_obj *layer = fh->layer; + int buf_size; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); + + *size = layer->pix_fmt.sizeimage; + buf_size = + display_buf_config_params.layer_bufsize[layer->device_id]; + /* + * For MMAP, limit the memory allocation as per bootarg + * configured buffer size + */ + if (V4L2_MEMORY_MMAP == layer->memory) + if (*size > buf_size) + *size = buf_size; + + /* Store number of buffers allocated in numbuffer member */ + if (*count < display_buf_config_params.min_numbuffers) + *count = layer->numbuffers = + display_buf_config_params.numbuffers[layer->device_id]; + + 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_display_obj *layer = fh->layer; + struct vpbe_display *disp = fh->disp_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_display_obj *layer = fh->layer; + + 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_display_obj* +_vpbe_display_get_other_win(struct vpbe_display *disp_dev, + struct vpbe_display_obj *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_video_display_params(struct vpbe_display *disp_dev, + struct vpbe_display_obj *layer) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + unsigned long addr; + int ret = 0; + + 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_display_obj *otherlayer = + _vpbe_display_get_other_win(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_display_obj *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; + int h_scale = 0, v_scale = 0, h_exp = 0, v_exp = 0, temp; + 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)) { + temp = (cfg->xsize * + VPBE_DISPLAY_H_EXP_RATIO_N) / + VPBE_DISPLAY_H_EXP_RATIO_D; + if (temp <= expected_xsize) { + h_exp = 1; + cfg->xsize = temp; + } + } + } + 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)) { + temp = (cfg->ysize * + VPBE_DISPLAY_V_EXP_RATIO_N) / + VPBE_DISPLAY_V_EXP_RATIO_D; + if (temp <= expected_ysize) { + v_exp = 1; + cfg->ysize = temp; + } + } + } + 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_display_obj *layer, + int top, int left) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + cfg->xpos = cfg->ypos = 0; + if (left + cfg->xsize <= vpbe_dev->current_timings.xres) + cfg->xpos = left; + if (top + cfg->ysize <= vpbe_dev->current_timings.yres) + cfg->ypos = top; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "new xpos = %d, ypos = %d\n", + cfg->xpos, cfg->ypos); +} + +static int vpbe_disp_check_window_params(struct vpbe_display *disp_dev, + struct v4l2_rect *c) +{ + if ((c->width == 0) + || ((c->width + c->left) > vpbe_dev->current_timings.xres) + || (c->height == 0) + || ((c->height + c->top) > vpbe_dev->current_timings.yres)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid crop values\n"); + return -1; + } + if ((c->height & 0x1) && (vpbe_dev->current_timings.interlaced)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "window height must be even for interlaced display\n"); + return -1; + } + return 0; +} + +/** + * 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. If application likes to add pads at the end of each line and + * end of the buffer , it can set bytesperline to line size and sizeimage to + * bytesperline * height of the buffer. If driver fills zero for active + * video width and height, and has requested user bytesperline and sizeimage, + * width and height is adjusted to maximum display limit or buffer width + * height which ever is lower + */ +static int vpbe_try_format(struct vpbe_display *disp_dev, + struct v4l2_pix_format *pixfmt, int check) +{ + int min_sizeimage, bpp, min_height = 1, min_width = 32, + max_width, max_height, user_info = 0; + + 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_ANY) { + 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->bytesperline) { + v4l2_err(&vpbe_dev->v4l2_dev, "bytesperline and width" + " cannot be zero\n"); + return -EINVAL; + } + + /* if user provided bytesperline, it must provide sizeimage as well */ + if (pixfmt->bytesperline && !pixfmt->sizeimage) { + v4l2_err(&vpbe_dev->v4l2_dev, + "sizeimage must be non zero, when user" + " provides bytesperline\n"); + return -EINVAL; + } + + /* adjust bytesperline as per hardware - multiple of 32 */ + if (!pixfmt->width) + pixfmt->width = pixfmt->bytesperline / bpp; + + if (!pixfmt->bytesperline) + pixfmt->bytesperline = pixfmt->width * bpp; + else + user_info = 1; + pixfmt->bytesperline = ((pixfmt->bytesperline + 31) & ~31); + + if (pixfmt->width < min_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is less than minimum," + "input width = %d, min_width = %d\n", + pixfmt->width, min_width); + return -EINVAL; + } + pixfmt->width = min_width; + } + + if (pixfmt->width > max_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is more than maximum," + "input width = %d, max_width = %d\n", + pixfmt->width, max_width); + return -EINVAL; + } + pixfmt->width = max_width; + } + + /* + * If height is zero, then atleast we need to have sizeimage + * to calculate height + */ + if (!pixfmt->height) { + if (user_info) { + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) { + /* + * for NV12 format, sizeimage is y-plane size + * + CbCr plane which is half of y-plane + */ + pixfmt->height = pixfmt->sizeimage / + (pixfmt->bytesperline + + (pixfmt->bytesperline >> 1)); + } else + pixfmt->height = pixfmt->sizeimage/ + pixfmt->bytesperline; + } + } + + if (pixfmt->height > max_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is more than maximum," + "input height = %d, max_height = %d\n", + pixfmt->height, max_height); + return -EINVAL; + } + pixfmt->height = max_height; + } + + if (pixfmt->height < min_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is less than minimum," + "input height = %d, min_height = %d\n", + pixfmt->height, min_height); + return -EINVAL; + } + pixfmt->height = min_width; + } + + /* if user has not provided bytesperline calculate it based on width */ + if (!user_info) + pixfmt->bytesperline = (((pixfmt->width * bpp) + 31) & ~31); + + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + min_sizeimage = pixfmt->bytesperline * pixfmt->height + + (pixfmt->bytesperline * pixfmt->height >> 1); + else + min_sizeimage = pixfmt->bytesperline * pixfmt->height; + + if (pixfmt->sizeimage < min_sizeimage) { + if (check && user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, "sizeimage is less, %d\n", + min_sizeimage); + return -EINVAL; + } + pixfmt->sizeimage = min_sizeimage; + } + 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_display_obj *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_display_obj *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_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id); + *cap = vpbe_display_videocap; + + return 0; +} + +static int vpbe_display_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct vpbe_display *disp_dev = video_drvdata(file); + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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) { + struct v4l2_rect *rect = &crop->c; + + if (rect->top < 0 || rect->left < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + + if (vpbe_disp_check_window_params(disp_dev, rect)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + 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); + + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + return ret; +} + +static int vpbe_display_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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) { + struct v4l2_rect *rect = &crop->c; + if (ret) + return ret; + 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; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + ret = -EINVAL; + } + + return ret; +} + +static int vpbe_display_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + + 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_display_obj *layer = fh->layer; + + 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) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Fill in the information about format */ + *pixfmt = layer->pix_fmt; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + + 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_display_obj *layer = fh->layer; + 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 > 0) { + 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 if (index == 1) { + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* 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; + + /* Change of the default pixel format for both video windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { + struct vpbe_display_obj *otherlayer; + cfg->pixfmt = PIXFMT_NV12; + otherlayer = _vpbe_display_get_other_win(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); + + /* verify if readback values are as expected */ + if (layer->pix_fmt.width != cfg->xsize || + layer->pix_fmt.height != cfg->ysize || + layer->pix_fmt.bytesperline != layer->layer_info. + config.line_length || + (cfg->interlaced + && layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || + (!cfg->interlaced && layer->pix_fmt.field + != V4L2_FIELD_NONE)) { + + v4l2_err(&vpbe_dev->v4l2_dev, "mismatch:layer conf params:\n"); + return -EINVAL; + } + + } else { + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + + return 0; +} + +static int vpbe_display_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Check for valid field format */ + return vpbe_try_format(disp_dev, pixfmt, 0); + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + + return 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_display_obj *layer = fh->layer; + int ret = 0; + + 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; + } + } + return ret; +} + +/** + * 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) +{ + 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) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_outputs) { + 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 ret; +} + +/** + * 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_display_obj *layer = fh->layer; + int ret = 0; + + 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) { + 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 ret; +} + +/** + * 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) +{ + 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) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_dv_presets) { + 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 ret; +} + +/** + * 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_display_obj *layer = fh->layer; + int ret = 0; + + 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) { + 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 ret; +} + +/** + * 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) +{ + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + + 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_video_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_first_int[layer->device_id] = 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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_display_obj *layer = fh->layer; + + 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) +{ + unsigned int err = 0; + struct vpbe_fh *fh = filep->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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; +} + +static int vpbe_display_cfg_layer_default(enum vpbe_display_device_id id, + struct vpbe_display *disp_dev) +{ + int err = 0; + struct osd_layer_config *layer_config; + struct vpbe_display_obj *layer = disp_dev->dev[id]; + struct osd_layer_config *cfg = &layer->layer_info.config; + + /* 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"); + return -EBUSY; + } + + layer_config = cfg; + /* Set the default image and crop values */ + layer_config->pixfmt = PIXFMT_YCbCrI; + layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY; + layer->pix_fmt.bytesperline = layer_config->line_length = + vpbe_dev->current_timings.xres * 2; + + layer->pix_fmt.width = layer_config->xsize = + vpbe_dev->current_timings.xres; + layer->pix_fmt.height = layer_config->ysize = + vpbe_dev->current_timings.yres; + layer->pix_fmt.sizeimage = + layer->pix_fmt.bytesperline * layer->pix_fmt.height; + layer_config->xpos = 0; + layer_config->ypos = 0; + layer_config->interlaced = vpbe_dev->current_timings.interlaced; + + /* + * turn off ping-pong buffer and field inversion to fix + * the image shaking problem in 1080I mode + */ + + if (cfg->interlaced) + layer->pix_fmt.field = V4L2_FIELD_INTERLACED; + else + layer->pix_fmt.field = V4L2_FIELD_NONE; + + err = osd_device->ops.set_layer_config(osd_device, + layer->layer_info.id, + layer_config); + if (err < 0) { + /* Couldn't set layer */ + v4l2_err(&vpbe_dev->v4l2_dev, + "Display Manager failed to set osd layer\n"); + return -EBUSY; + } + + return 0; +} + +/* + * 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) +{ + int minor = iminor(file->f_path.dentry->d_inode); + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer; + struct vpbe_fh *fh = NULL; + int found = -1; + int i = 0; + + /* Check for valid minor number */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + layer = disp_dev->dev[i]; + if (minor == layer->video_dev->minor) { + found = i; + break; + } + } + + /* If not found, return error no device */ + if (0 > found) { + v4l2_err(&vpbe_dev->v4l2_dev, "device not found\n"); + return -ENODEV; + } + + /* 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) { + /* Configure the default values for the layer */ + if (vpbe_display_cfg_layer_default(layer->device_id, + disp_dev)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to configure video layer" + " for id = %d\n", layer->device_id); + 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_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); + /* If this is doing IO and other layer are not closed */ + if ((layer->usrs != 1) && fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "Close other instances\n"); + return -EAGAIN; + } + + /* 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_display_obj *otherlayer; + otherlayer = + _vpbe_display_get_other_win(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); + + if (strcmp("vpbe_controller", pdev->name) == 0) + vpbe_dev = platform_get_drvdata(pdev); + + if (strcmp("vpbe-osd", pdev->name) == 0) + osd_device = platform_get_drvdata(pdev); + + return 0; +} + +/*Configure the channels, buffer size */ +static int init_vpbe_layer_objects(int i) +{ + int free_buffer_index; + + /* Default number of buffers should be 3 */ + if ((video2_numbuffers > 0) && + (video2_numbuffers < display_buf_config_params.min_numbuffers)) + video2_numbuffers = display_buf_config_params.min_numbuffers; + if ((video3_numbuffers > 0) && + (video3_numbuffers < display_buf_config_params.min_numbuffers)) + video3_numbuffers = display_buf_config_params.min_numbuffers; + + /* + * Set buffer size to min buffers size if invalid + * buffer size is given + */ + if (video2_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]) + video2_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]; + + if (video3_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]) + video3_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]; + + /* set number of buffers, they could come from boot/args */ + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_0] = + video2_numbuffers; + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_1] = + video3_numbuffers; + + if (display_buf_config_params.numbuffers[0] == 0) + printk(KERN_ERR "no vid2 buffer allocated\n"); + if (display_buf_config_params.numbuffers[1] == 0) + printk(KERN_ERR "no vid3 buffer allocated\n"); + free_buffer_index = display_buf_config_params.numbuffers[i - 1]; + + 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 __init int vpbe_display_probe(struct platform_device *pdev) +{ + int i, j = 0, k, err = 0; + struct vpbe_display *disp_dev; + struct video_device *vbd = NULL; + struct vpbe_display_obj *vpbe_display_layer = NULL; + struct resource *res; + 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; + } + + /* Allocate memory for four plane display objects */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + disp_dev->dev[i] = + kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL); + /* If memory allocation fails, return error */ + if (!disp_dev->dev[i]) { + printk(KERN_ERR "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + spin_lock_init(&disp_dev->dev[i]->irqlock); + mutex_init(&disp_dev->dev[i]->opslock); + } + spin_lock_init(&disp_dev->dma_queue_lock); + + err = init_vpbe_layer_objects(i); + if (err) { + printk(KERN_ERR "Error initializing vpbe display\n"); + return err; + } + + /* + * 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, NULL, + vpbe_device_get); + if (err < 0) + return err; + + /* Initialize the vpbe display controller */ + if (NULL != vpbe_dev->ops.initialize) { + err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev); + if (err) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error initing vpbe\n"); + err = -ENOMEM; + goto probe_out; + } + } + + /* check the name of davinci device */ + if (vpbe_dev->cfg->module_name != NULL) + strcpy(vpbe_display_videocap.card, + vpbe_dev->cfg->module_name); + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[i]; + /* Allocate memory for video device */ + vbd = video_device_alloc(); + if (vbd == NULL) { + for (j = 0; j < i; j++) { + video_device_release( + disp_dev->dev[j]->video_dev); + } + v4l2_err(&vpbe_dev->v4l2_dev, "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + /* Initialize field of video device */ + vbd->release = video_device_release; + vbd->fops = &vpbe_fops; + vbd->ioctl_ops = &vpbe_ioctl_ops; + vbd->minor = -1; + vbd->v4l2_dev = &vpbe_dev->v4l2_dev; + vbd->lock = &vpbe_display_layer->opslock; + + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { + vbd->tvnorms = (V4L2_STD_NTSC | V4L2_STD_PAL); + vbd->current_norm = + 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); + + /* Set video_dev to the video device */ + vpbe_display_layer->video_dev = vbd; + vpbe_display_layer->device_id = i; + + vpbe_display_layer->layer_info.id = + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); + if (display_buf_config_params.numbuffers[i] == 0) + vpbe_display_layer->memory = V4L2_MEMORY_USERPTR; + else + vpbe_display_layer->memory = V4L2_MEMORY_MMAP; + + /* Initialize field of the display layer objects */ + vpbe_display_layer->usrs = 0; + vpbe_display_layer->io_usrs = 0; + vpbe_display_layer->started = 0; + + /* Initialize prio member of layer object */ + v4l2_prio_init(&vpbe_display_layer->prio); + + /* Register video device */ + v4l2_info(&vpbe_dev->v4l2_dev, + "Trying to register VPBE display device.\n"); + v4l2_info(&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, + vpbe_display_nr[i]); + if (err) + goto probe_out; + /* set the driver data in platform device */ + platform_set_drvdata(pdev, disp_dev); + video_set_drvdata(vpbe_display_layer->video_dev, disp_dev); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to get VENC register address map\n"); + err = -ENODEV; + goto probe_out; + } + + reg_base_venc = ioremap_nocache(res->start, resource_size(res)); + if (!reg_base_venc) { + v4l2_err(&vpbe_dev->v4l2_dev, "Unable to map VENC IO space\n"); + err = -ENODEV; + goto probe_out; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + v4l2_err(&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(&vpbe_dev->v4l2_dev, "Unable to request interrupt\n"); + err = -ENODEV; + goto probe_out; + } + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); + return 0; +probe_out: + kfree(disp_dev); + + for (k = 0; k < j; k++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[k]; + /* Unregister video device */ + video_unregister_device(vpbe_display_layer->video_dev); + /* Release video device */ + video_device_release(vpbe_display_layer->video_dev); + vpbe_display_layer->video_dev = NULL; + } + return err; +} + +/* + * vpbe_display_remove() + * It un-register hardware layer from V4L2 driver + */ +static int vpbe_display_remove(struct platform_device *pdev) +{ + int i; + struct vpbe_display_obj *vpbe_display_layer; + struct vpbe_display *disp_dev = platform_get_drvdata(pdev); + struct resource *res; + + 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); + + vpbe_display_layer->video_dev = NULL; + } + 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 __init int vpbe_display_init(void) +{ + int err = 0; + + 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 DMXXX 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..90ad066 --- /dev/null +++ b/include/media/davinci/vpbe_display.h @@ -0,0 +1,146 @@ +/* + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef VPBE_DISPLAY_H +#define VPBE_DISPLAY_H + +#ifdef __KERNEL__ + +/* 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_display_obj { + /* number of buffers in fbuffers */ + unsigned int numbuffers; + /* 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; +}; + +/* 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_display_obj *dev[VPBE_DISPLAY_MAX_DEVICES]; +}; + +/* File handle structure */ +struct vpbe_fh { + /* vpbe device structure */ + struct vpbe_display *disp_dev; + /* pointer to layer object for opened device */ + struct vpbe_display_obj *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]; +}; + +static int venc_is_second_field(void); +#endif /* end of __KERNEL__ */ +#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..a4b8585 --- /dev/null +++ b/include/media/davinci/vpbe_types.h @@ -0,0 +1,93 @@ +/* + * 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; either version 2 of the License. + * + * 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_types { + DM644X_VPBE = 1, + DM355_VPBE, + DM365_VPBE, +}; + +/* 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 Thu Dec 2 06:38:36 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 2 Dec 2010 18:08:36 +0530 Subject: [PATCH v3 2/6] davinci vpbe: VPBE display driver Message-ID: <1291293516-2185-1-git-send-email-manjunath.hadli@ti.com> This patch implements the coe functionality of the dislay driver, mainly controlling the VENC and other encoders, and acting as the one point interface for the man V4L2 driver.This implements the cre of each of the V4L2 IOCTLs. Signed-off-by: Manjunath Hadli Signed-off-by: Muralidharan Karicheri --- drivers/media/video/davinci/vpbe.c | 847 ++++++++++++++++++++++++++++++++++++ include/media/davinci/vpbe.h | 186 ++++++++ 2 files changed, 1033 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..96c0eea --- /dev/null +++ b/drivers/media/video/davinci/vpbe.c @@ -0,0 +1,847 @@ +/* + * 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; either version 2 of the License. + * + * 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 + + +#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 struct osd_state *osd_device; +static struct venc_platform_data *venc_device; +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(ef_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_display_config *vpbe_config = vpbe_dev->cfg; + int index = vpbe_dev->current_sd_index; + return ((index == 0) ? &vpbe_config->venc : + &vpbe_config->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_display_config *vpbe_config, + int index) +{ + char *encoder_name = vpbe_config->outputs[index].subdev_name; + int i; + + /* Venc is always first */ + if (!strcmp(encoder_name, vpbe_config->venc.module_name)) + return 0; + + for (i = 0; i < vpbe_config->num_ext_encoders; i++) { + if (!strcmp(encoder_name, + vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; + int temp_index = output->index; + + if (temp_index >= vpbe_config->num_outputs) + return -EINVAL; + + *output = vpbe_config->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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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 vpbe_display_config *vpbe_config = vpbe_dev->cfg; + struct encoder_config_info *curr_enc_info = + vpbe_current_encoder_info(vpbe_dev); + int ret = 0, enc_out_index = 0, sd_index; + + if (index >= vpbe_config->num_outputs) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpbe_dev->lock); + if (ret) + return ret; + + sd_index = vpbe_dev->current_sd_index; + enc_out_index = vpbe_config->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, + vpbe_config->outputs[index].subdev_name)) { + /* Need to switch the encoder at the output */ + sd_index = vpbe_find_encoder_sd_index(vpbe_config, 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, + vpbe_config->outputs[index].default_mode); + if (!ret) { + 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); + } + if (!ret) { + 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_display_config *vpbe_config = vpbe_dev->cfg; + int i, ret = 0; + + for (i = 0; i < vpbe_config->num_outputs; i++) { + if (!strcmp(def_output, + vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; + int sd_index = vpbe_dev->current_sd_index; + int out_index = vpbe_dev->current_out_index, ret; + + + if (!(vpbe_config->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; + + ret = mutex_lock_interruptible(&vpbe_dev->lock); + if (ret) + return ret; + + + 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) { + 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_display_config *vpbe_config = vpbe_dev->cfg; + int out_index = vpbe_dev->current_out_index; + struct vpbe_output *output = &vpbe_config->outputs[out_index]; + int i, j = 0; + + 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_display_config *vpbe_config = vpbe_dev->cfg; + int sd_index = vpbe_dev->current_sd_index, out_index = + vpbe_dev->current_out_index, ret; + + if (!(vpbe_config->outputs[out_index].output.capabilities & + V4L2_OUT_CAP_STD)) + return -EINVAL; + + ret = vpbe_get_std_info(vpbe_dev, *std_id); + if (ret) + return ret; + + ret = mutex_lock_interruptible(&vpbe_dev->lock); + if (ret) + return ret; + + 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) { + 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_display_config *vpbe_config = vpbe_dev->cfg; + int out_index = vpbe_dev->current_out_index, ret = 0, i; + struct vpbe_enc_mode_info *preset_mode = NULL; + struct v4l2_dv_preset dv_preset; + + if ((NULL == mode_info) || (NULL == mode_info->name)) + return -EINVAL; + + for (i = 0; i < vpbe_config->outputs[out_index].num_modes; i++) { + if (!strcmp(mode_info->name, + vpbe_config->outputs[out_index].modes[i].name)) { + preset_mode = &vpbe_config->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) { + ret = vpbe_s_std(vpbe_dev, + &preset_mode->timings.std_id); + return ret; + } else if (preset_mode->timings_type & + VPBE_ENC_DV_PRESET) { + dv_preset.preset = + preset_mode->timings.dv_preset; + ret = vpbe_s_dv_preset(vpbe_dev, &dv_preset); + return ret; + } + } + } + + /* Only custom timing should reach here */ + if (preset_mode == NULL) + return -EINVAL; + + ret = mutex_lock_interruptible(&vpbe_dev->lock); + if (ret) + return ret; + + if (!ret) { + 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); + if (strcmp("vpbe-osd", pdev->name) == 0) + osd_device = platform_get_drvdata(pdev); + if (strcmp("vpbe-venc", pdev->name) == 0) + venc_device = dev_get_platdata(&pdev->dev); + + 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; + int i, ret = 0, num_encoders; + struct i2c_adapter *i2c_adap; + int output_index; + int err; + + /* + * 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, NULL, + 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 */ + 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->module_name, + &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. + */ +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; + /* disaable 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 __init int vpbe_probe(struct platform_device *pdev) +{ + struct vpbe_display_config *vpbe_config; + struct vpbe_device *vpbe_dev; + + int ret = -EINVAL; + + if (NULL == pdev->dev.platform_data) { + v4l2_err(pdev->dev.driver, "Unable to get vpbe config\n"); + return -ENODEV; + } + + if (pdev->dev.platform_data == NULL) { + v4l2_err(pdev->dev.driver, "No platform data\n"); + return -ENODEV; + } + vpbe_config = pdev->dev.platform_data; + + if (!vpbe_config->module_name[0] || + !vpbe_config->osd.module_name[0] || + !vpbe_config->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 = vpbe_config; + vpbe_dev->ops = vpbe_dev_ops; + vpbe_dev->pdev = &pdev->dev; + + if (vpbe_config->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..c8853f2 --- /dev/null +++ b/include/media/davinci/vpbe.h @@ -0,0 +1,186 @@ +/* + * 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; either version 2 of the License. + * + * 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 + +/* 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_display_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_display_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; + + /* + * 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; +}; + +/* 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 Thu Dec 2 06:38:52 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 2 Dec 2010 18:08:52 +0530 Subject: [PATCH v3 3/6] davinci vpbe: OSD(On Screen Display) block Message-ID: <1291293532-2350-1-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 Signed-off-by: Muralidharan Karicheri --- drivers/media/video/davinci/vpbe_osd.c | 1210 +++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++++++ include/media/davinci/vpbe_osd.h | 397 +++++++++ 3 files changed, 1996 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..6abe529 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd.c @@ -0,0 +1,1210 @@ +/* + * 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; 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 + * + */ +#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 __raw_readl(osd->osd_base + offset); +} + +static inline u32 osd_write(struct osd_state *sd, u32 val, u32 offset) +{ + struct osd_state *osd = sd; + __raw_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 = __raw_readl(addr) | mask; + + __raw_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 = __raw_readl(addr) & ~mask; + + __raw_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 = (__raw_readl(addr) & ~mask) | (val & mask); + __raw_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) +{ + +#ifdef CONFIG_DM6446_FIELD_INV_WORKAROUND + 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; + } +#endif + 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) +{ + int bmp_reg, bmp_offset, bmp_mask, bmp_shift; + static const int map_1bpp[] = { 0, 15 }; + static const int map_2bpp[] = { 0, 5, 10, 15 }; + + 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]; + unsigned long flags; + struct osd_layer_config *cfg = &win->lconfig; + + 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]; + unsigned long flags; + struct osd_layer_config *cfg = &win->lconfig; + + 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 = 0; + /* 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; + int reject_config; + unsigned long flags; + + 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_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; + osd->vpbe_type = (enum vpbe_types)pdev->dev.platform_data; + 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) +{ + /* Register the driver */ + 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..9cd3839 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd_regs.h @@ -0,0 +1,389 @@ +/* + * 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; either version 2 of the License. + * + * 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 + +enum vpbe_soc_type { + DM644x = 0, + DM35x, + DM36x, +}; + +struct vpbe_osd_platform_data { + enum vpbe_soc_type soc; +}; + +#define DM644X_OSD_REG_BASE 0x01C72600 +#define DM644X_VPBE_REG_BASE 0x01C72780 + +#define DM355_VPSSCLK_REG_BASE 0x01C70000 +#define DM355_OSD_REG_BASE 0x01C70200 + +#define DM365_OSD_REG_BASE 0x01C71C00 +#define DM365_ISP5_REG_BASE 0x01C70000 + +#define OSD_REG_SIZE 0x00000100 + +/* SYS register addresses */ +#define SYS_VPSS_CLKCTL 0x01C40044 + +/* 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..9549f3e --- /dev/null +++ b/include/media/davinci/vpbe_osd.h @@ -0,0 +1,397 @@ +/* + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef _OSD_H +#define _OSD_H + +#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; +}; +/* OSD events. Should match with that in vpbe_venc.h */ +#define OSD_END_OF_FRAME 1 +#define OSD_FIRST_FIELD 2 +#define OSD_SECOND_FIELD 4 + +/* 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; + +/* TBD. Some of these operations here are to be removed and implemented + * as a ioctl call on the osd sub device node. Right now just trying to + * make this work. + */ +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_types 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; +}; + + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Dec 2 06:39:07 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 2 Dec 2010 18:09:07 +0530 Subject: [PATCH 4/6] davinci vpbe: VENC( Video Encoder) implementation Message-ID: <1291293547-2473-1-git-send-email-manjunath.hadli@ti.com> This patch adds the VENC or the Video encoder, whichis responsible for the blending of al 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 extenal and internal encoders of other kind to plug-in. Signed-off-by: Manjunath Hadli Signed-off-by: Muralidharan Karicheri --- drivers/media/video/davinci/vpbe_venc.c | 574 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++++++++ include/media/davinci/vpbe_venc.h | 38 ++ 3 files changed, 801 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..bf30332 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc.c @@ -0,0 +1,574 @@ +/* + * 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; 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 + */ +#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 __raw_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); + __raw_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); + + __raw_writel(val, venc->vdaccfg_reg); + + val = __raw_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) +{ + int ret = 0; + + 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: + ret = -EINVAL; + } + return ret; +} + +static void enabledigitaloutput(struct v4l2_subdev *sd, int benable) +{ + v4l2_dbg(debug, 2, sd, "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; + + 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; + + 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; + + 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; + + 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) +{ + int ret = 0; + + v4l2_dbg(debug, 1, sd, "venc_s_std_output\n"); + + if (norm & V4L2_STD_NTSC) + ret = venc_set_ntsc(sd); + else if (norm & V4L2_STD_PAL) + ret = venc_set_pal(sd); + else + ret = -EINVAL; + return ret; +} + +static int venc_s_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *dv_preset) +{ + int ret = -EINVAL; + + 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 ret; +} + +static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct venc_state *venc = to_state(sd); + int max_output, lcd_out_index, ret = 0; + + v4l2_dbg(debug, 1, sd, "venc_s_routing\n"); + + max_output = 2 + venc->pdata->num_lcd_outputs; + lcd_out_index = 3; + + if (output >= max_output) + return -EINVAL; + + if (output < lcd_out_index) + ret = venc_set_dac(sd, output); + if (!ret) + venc->output = output; + return ret; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int venc_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + __iomem void *reg_base; + + if (reg->match.type == 2) + reg->val = venc_read(sd, reg->reg); + else if (reg->match.type == 3) { + reg_base = ioremap_nocache(0x01c70800, 0x80); + if (reg_base == NULL) + return -EINVAL; + reg->val = __raw_readl(reg_base + reg->reg); + iounmap(reg_base); + } else { + reg_base = ioremap_nocache(0x01c70000, 0x80); + if (reg_base == NULL) + return -1; + reg->val = __raw_readl(reg_base + reg->reg); + iounmap(reg_base); + } + return 0; +} +#endif + +static const struct v4l2_subdev_core_ops venc_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = venc_g_register, +#endif +}; + +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 = 0; + + /* Set default to output to composite and std to NTSC */ + venc->output = 0; + venc->std = V4L2_STD_NTSC; + + 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) +{ + /* Register the driver */ + 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..38e4818 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc_regs.h @@ -0,0 +1,189 @@ +/* + * 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; either version 2 of the License. + * + * 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 register base addresses */ +#define DM644X_VENC_REG_BASE 0x01C72400 +#define DM644X_VPBE_REG_BASE 0x01C72780 + +#define DM355_VENC_REG_BASE 0x01C70400 + +#define DM365_VENC_REG_BASE 0x01C71E00 +#define DM365_ISP5_REG_BASE 0x01C70000 + +#define DM3XX_VDAC_CONFIG 0x01C4002C +#define DM355_USB_PHY_CTRL 0x01c40034 + +/* 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..47a977c --- /dev/null +++ b/include/media/davinci/vpbe_venc.h @@ -0,0 +1,38 @@ +/* + * 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; either version 2 of the License. + * + * 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 + +#define VPBE_VENC_SUBDEV_NAME "vpbe-venc" + +/* venc events */ +#define VENC_END_OF_FRAME 1 +#define VENC_FIRST_FIELD 2 +#define VENC_SECOND_FIELD 4 + +struct venc_platform_data { + enum vpbe_types venc_type; + int (*setup_clock)(enum vpbe_enc_timings_type type, + __u64 mode); + /* Number of LCD outputs supported */ + int num_lcd_outputs; +}; + + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Dec 2 06:39:27 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 2 Dec 2010 18:09:27 +0530 Subject: [PATCH v3 5/6] davinci vpbe: platform specific additions Message-ID: <1291293567-2676-1-git-send-email-manjunath.hadli@ti.com> This patch implements the overall device creation for the Video display driver, and addition of tables for the mode and output list Signed-off-by: Manjunath Hadli Signed-off-by: Muralidharan Karicheri --- arch/arm/mach-davinci/board-dm644x-evm.c | 79 +++++++++++-- arch/arm/mach-davinci/dm644x.c | 164 ++++++++++++++++++++++++++- arch/arm/mach-davinci/include/mach/dm644x.h | 4 + 3 files changed, 228 insertions(+), 19 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 34c8b41..e9b1243 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -166,18 +166,6 @@ static struct platform_device davinci_evm_nandflash_device = { .resource = davinci_evm_nandflash_resource, }; -static u64 davinci_fb_dma_mask = DMA_BIT_MASK(32); - -static struct platform_device davinci_fb_device = { - .name = "davincifb", - .id = -1, - .dev = { - .dma_mask = &davinci_fb_dma_mask, - .coherent_dma_mask = DMA_BIT_MASK(32), - }, - .num_resources = 0, -}; - static struct tvp514x_platform_data tvp5146_pdata = { .clk_polarity = 0, .hs_polarity = 1, @@ -606,8 +594,71 @@ static void __init evm_init_i2c(void) i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info)); } +#define VENC_STD_ALL (V4L2_STD_NTSC | V4L2_STD_PAL) +/* venc standards timings */ +static struct vpbe_enc_mode_info vbpe_enc_std_timings[] = { + {"ntsc", VPBE_ENC_STD, {V4L2_STD_525_60}, 1, 720, 480, + {11, 10}, {30000, 1001}, 0x79, 0, 0x10, 0, 0, 0, 0}, + {"pal", VPBE_ENC_STD, {V4L2_STD_625_50}, 1, 720, 576, + {54, 59}, {25, 1}, 0x7E, 0, 0x16, 0, 0, 0, 0}, +}; + +/* venc dv preset timings */ +static struct vpbe_enc_mode_info vbpe_enc_preset_timings[] = { + {"480p59_94", VPBE_ENC_DV_PRESET, {V4L2_DV_480P59_94}, 0, 720, 480, + {1, 1}, {5994, 100}, 0x80, 0, 0x20, 0, 0, 0, 0}, + {"576p50", VPBE_ENC_DV_PRESET, {V4L2_DV_576P50}, 0, 720, 576, + {1, 1}, {50, 1}, 0x7E, 0, 0x30, 0, 0, 0, 0}, +}; + +/* + * The outputs available from VPBE + ecnoders. Keep the + * the order same as that of encoders. First that from venc followed by that + * from encoders. Index in the output refers to index on a particular encoder. + * Driver uses this index to pass it to encoder when it supports more than + * one output. Application uses index of the array to set an output. + */ +static struct vpbe_output dm644x_vpbe_outputs[] = { + { + .output = { + .index = 0, + .name = "Composite", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .std = VENC_STD_ALL, + .capabilities = V4L2_OUT_CAP_STD, + }, + .subdev_name = VPBE_VENC_SUBDEV_NAME, + .default_mode = "ntsc", + .num_modes = ARRAY_SIZE(vbpe_enc_std_timings), + .modes = vbpe_enc_std_timings, + }, + { + .output = { + .index = 1, + .name = "Component", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .capabilities = V4L2_OUT_CAP_PRESETS, + }, + .subdev_name = VPBE_VENC_SUBDEV_NAME, + .default_mode = "480p59_94", + .num_modes = ARRAY_SIZE(vbpe_enc_preset_timings), + .modes = vbpe_enc_preset_timings, + }, +}; + +static struct vpbe_display_config vpbe_display_cfg = { + .module_name = "dm644x-vpbe-display", + .i2c_adapter_id = 1, + .osd = { + .module_name = VPBE_OSD_SUBDEV_NAME, + }, + .venc = { + .module_name = VPBE_VENC_SUBDEV_NAME, + }, + .num_outputs = ARRAY_SIZE(dm644x_vpbe_outputs), + .outputs = dm644x_vpbe_outputs, +}; static struct platform_device *davinci_evm_devices[] __initdata = { - &davinci_fb_device, &rtc_dev, }; @@ -620,6 +671,8 @@ davinci_evm_map_io(void) { /* setup input configuration for VPFE input devices */ dm644x_set_vpfe_config(&vpfe_cfg); + /* setup configuration for vpbe devices */ + dm644x_set_vpbe_display_config(&vpbe_display_cfg); dm644x_init(); } diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 5e5b0a7..e8b8e94 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -640,6 +640,142 @@ void dm644x_set_vpfe_config(struct vpfe_config *cfg) vpfe_capture_dev.dev.platform_data = cfg; } +static struct resource dm644x_osd_resources[] = { + { + .start = 0x01C72600, + .end = 0x01C72600 + 0x200, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 dm644x_osd_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device dm644x_osd_dev = { + .name = VPBE_OSD_SUBDEV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_osd_resources), + .resource = dm644x_osd_resources, + .dev = { + .dma_mask = &dm644x_osd_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = (void *)DM644X_VPBE, + }, +}; + +static struct resource dm644x_venc_resources[] = { + /* venc registers io space */ + { + .start = 0x01C72400, + .end = 0x01C72400 + 0x180, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 dm644x_venc_dma_mask = DMA_BIT_MASK(32); + +#define VPSS_CLKCTL 0x01C40044 +static void __iomem *vpss_clkctl_reg; + +/* TBD. Check what VENC_CLOCK_SEL settings for HDTV and EDTV */ +static int dm644x_venc_setup_clock(enum vpbe_enc_timings_type type, __u64 mode) +{ + int ret = 0; + + if (NULL == vpss_clkctl_reg) + return -EINVAL; + if (type == VPBE_ENC_STD) { + __raw_writel(0x18, vpss_clkctl_reg); + } else if (type == VPBE_ENC_DV_PRESET) { + switch ((unsigned int)mode) { + case V4L2_DV_480P59_94: + case V4L2_DV_576P50: + __raw_writel(0x19, vpss_clkctl_reg); + break; + case V4L2_DV_720P60: + case V4L2_DV_1080I60: + case V4L2_DV_1080P30: + /* + * For HD, use external clock source since HD requires higher + * clock rate + */ + __raw_writel(0xa, vpss_clkctl_reg); + break; + default: + ret = -EINVAL; + break; + } + } else + ret = -EINVAL; + + return ret; +} + + +static inline u32 dm644x_reg_modify(void *reg, u32 val, u32 mask) +{ + u32 new_val = (__raw_readl(reg) & ~mask) | (val & mask); + __raw_writel(new_val, reg); + return new_val; +} + +static u64 vpbe_display_dma_mask = DMA_BIT_MASK(32); + +static struct resource dm644x_v4l2_disp_resources[] = { + { + .start = IRQ_VENCINT, + .end = IRQ_VENCINT, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0x01C72400, + .end = 0x01C72400 + 0x180, + .flags = IORESOURCE_MEM, + }, + +}; +static struct platform_device vpbe_v4l2_display = { + .name = "vpbe-v4l2", + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_v4l2_disp_resources), + .resource = dm644x_v4l2_disp_resources, + .dev = { + .dma_mask = &vpbe_display_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; +struct venc_platform_data dm644x_venc_pdata = { + .venc_type = DM644X_VPBE, + .setup_clock = dm644x_venc_setup_clock, +}; + +static struct platform_device dm644x_venc_dev = { + .name = VPBE_VENC_SUBDEV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_venc_resources), + .resource = dm644x_venc_resources, + .dev = { + .dma_mask = &dm644x_venc_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = (void *)&dm644x_venc_pdata, + }, +}; + +static u64 dm644x_vpbe_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device dm644x_vpbe_dev = { + .name = "vpbe_controller", + .id = -1, + .dev = { + .dma_mask = &dm644x_vpbe_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +void dm644x_set_vpbe_display_config(struct vpbe_display_config *cfg) +{ + dm644x_vpbe_dev.dev.platform_data = cfg; +} + /*----------------------------------------------------------------------*/ static struct map_desc dm644x_io_desc[] = { @@ -767,20 +903,36 @@ void __init dm644x_init(void) davinci_common_init(&davinci_soc_info_dm644x); } +static struct platform_device *dm644x_video_devices[] __initdata = { + &dm644x_vpss_device, + &dm644x_ccdc_dev, + &vpfe_capture_dev, + &dm644x_osd_dev, + &dm644x_venc_dev, + &dm644x_vpbe_dev, + &vpbe_v4l2_display, +}; + +static int __init dm644x_init_video(void) +{ + /* Add ccdc clock aliases */ + clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); + clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); + vpss_clkctl_reg = ioremap_nocache(VPSS_CLKCTL, 4); + platform_add_devices(dm644x_video_devices, + ARRAY_SIZE(dm644x_video_devices)); + return 0; +} + static int __init dm644x_init_devices(void) { if (!cpu_is_davinci_dm644x()) return 0; /* Add ccdc clock aliases */ - clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); - clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); platform_device_register(&dm644x_edma_device); platform_device_register(&dm644x_emac_device); - platform_device_register(&dm644x_vpss_device); - platform_device_register(&dm644x_ccdc_dev); - platform_device_register(&vpfe_capture_dev); - + dm644x_init_video(); return 0; } postcore_initcall(dm644x_init_devices); diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h index 6fca568..bf7adcd 100644 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ b/arch/arm/mach-davinci/include/mach/dm644x.h @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #define DM644X_EMAC_BASE (0x01C80000) #define DM644X_EMAC_CNTRL_OFFSET (0x0000) @@ -43,5 +46,6 @@ void __init dm644x_init(void); void __init dm644x_init_asp(struct snd_platform_data *pdata); void dm644x_set_vpfe_config(struct vpfe_config *cfg); +void dm644x_set_vpbe_display_config(struct vpbe_display_config *cfg); #endif /* __ASM_ARCH_DM644X_H */ -- 1.6.2.4 From manjunath.hadli at ti.com Thu Dec 2 06:39:43 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 2 Dec 2010 18:09:43 +0530 Subject: [PATCH v3 6/6] davinci vpbe: Build infrastructure for VPBE driver Message-ID: <1291293583-2810-1-git-send-email-manjunath.hadli@ti.com> This patch adds the build infra-structure for Davinci VPBE dislay driver Signed-off-by: Manjunath Hadli Signed-off-by: Muralidharan Karicheri --- drivers/media/video/davinci/Kconfig | 22 +++++ drivers/media/video/davinci/Makefile | 2 + .../media/video/davinci/davinci_vpbe_readme.txt | 100 ++++++++++++++++++++ 3 files changed, 124 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/davinci_vpbe_readme.txt diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig index 6b19540..dab32d5 100644 --- a/drivers/media/video/davinci/Kconfig +++ b/drivers/media/video/davinci/Kconfig @@ -91,3 +91,25 @@ 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" + 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" + select VIDEO_DM644X_VPBE + default y + help + Enables VPBE V4L2 Display driver on a DMXXX 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 diff --git a/drivers/media/video/davinci/davinci_vpbe_readme.txt b/drivers/media/video/davinci/davinci_vpbe_readme.txt new file mode 100644 index 0000000..e7aabba --- /dev/null +++ b/drivers/media/video/davinci/davinci_vpbe_readme.txt @@ -0,0 +1,100 @@ + + 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 following (in the same order as the list under file + partitioning):- + + 1. V4L2 display driver + Implements 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 sub device + 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 connetected 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 allowes + 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 ans setting of the external + encoders is not present, and would be a part of the next patch series. + + 3. Venc sub device + Responsible for setting outputs provides 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 cab be maintained in the board specific setup file to support + various LCD displays. + + 4. osd sub device + Osd sub device implements all osd layer management and hardware specific + features. In the legacy drivers (LSPxxx), the hardware specific features + are configured through proprietary IOCTLs at the fb device interface. Since + sub devices are going to support device nodes, application will be able + to configure the hardware feayture directly by opening the osd sub device + node and by calling the related IOCTL. So these proprietary IOCTLs are + to be removed from the FB Device driver when doing up port of these drivers to + mainline kernel. The V4L2 and FB device nodes supports only IOCTLS as per + the associated spec. The rest of the IOCTLs are to be moved to osd and + venc sub devices. + + Current status:- + + A build tested version of vpbe controller is available. + + Following are TBDs. + + vpbe display controller + - review and modify the handling of 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. + + v4l2 driver + - A version is already developed which is to be cleaned up and unit tested + + FB drivers + - Add support for fbdev drivers.- Ready and part of subsequent patches. \ No newline at end of file -- 1.6.2.4 From michael.williamson at criticallink.com Thu Dec 2 06:48:47 2010 From: michael.williamson at criticallink.com (Michael Williamson) Date: Thu, 02 Dec 2010 07:48:47 -0500 Subject: [PATCH v3 1/2] davinci: am18x/da850/omap-l138: add support for higher speed grades In-Reply-To: <1291292822-20232-1-git-send-email-nsekhar@ti.com> References: <1291292822-20232-1-git-send-email-nsekhar@ti.com> Message-ID: <4CF795AF.4070905@criticallink.com> Hello Sekhar, Excited to see the significant performance boost on these parts! On 12/2/2010 7:27 AM, Sekhar Nori wrote: > AM18x/DA850/OMAP-L138 SoCs have variants that can operate > at a maximum of 456 MHz at 1.3V operating point. Also the > 1.2V operating point has a variant that can support a maximum > of 375 MHz. > > This patch adds three new OPPs (456 MHz, 408 MHz and 372 MHz) > to the list of DA850 OPPs. > > Not all silicon is qualified to run at higher speeds and > unfortunately the maximum speed the chip can support can only > be determined from the label on the package (not software > readable). > > Because of this, we depend on the maximum speed grade information > to be provided to us in some board specific way. The board informs > the maximum speed grade information to the SoC by calling the > da850_set_max_speed() function. > > Signed-off-by: Sekhar Nori > --- > Since v2, fixed comments from Sergei here: > https://patchwork.kernel.org/patch/366501/ > > arch/arm/mach-davinci/da850.c | 72 +++++++++++++++++++++++----- > arch/arm/mach-davinci/include/mach/da8xx.h | 7 +++ > 2 files changed, 66 insertions(+), 13 deletions(-) > > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 63916b9..a343225 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -842,6 +842,33 @@ struct da850_opp { > unsigned int cvdd_max; /* in uV */ > }; > > +static const struct da850_opp da850_opp_456 = { > + .freq = 456000, > + .prediv = 1, > + .mult = 19, > + .postdiv = 1, > + .cvdd_min = 1300000, > + .cvdd_max = 1350000, > +}; > + > +static const struct da850_opp da850_opp_408 = { > + .freq = 408000, > + .prediv = 1, > + .mult = 17, > + .postdiv = 1, > + .cvdd_min = 1300000, > + .cvdd_max = 1350000, > +}; > + > +static const struct da850_opp da850_opp_372 = { > + .freq = 372000, > + .prediv = 1, > + .mult = 31, > + .postdiv = 2, > + .cvdd_min = 1200000, > + .cvdd_max = 1320000, > +}; > + Table 6-4 of the OMAP-L138 (rev B) spec indicates a maximum PLLOUT of 600 MHz. PLLOUT is defined in the table as the PLL Output frequency, it's not clear if that's before or after the post divider (seems like before given the block diagram in Figure 6-9 and a minimum value of 300 MHz). This OPP(372) results in a PLL of 744 MHz prior to the post divider. Is this a problem? Or am I reading the spec wrong? Thanks. > static const struct da850_opp da850_opp_300 = { > .freq = 300000, > .prediv = 1, > @@ -876,6 +903,9 @@ static const struct da850_opp da850_opp_96 = { > } > > static struct cpufreq_frequency_table da850_freq_table[] = { > + OPP(456), > + OPP(408), > + OPP(372), > OPP(300), > OPP(200), > OPP(96), > @@ -886,6 +916,19 @@ static struct cpufreq_frequency_table da850_freq_table[] = { > }; > > #ifdef CONFIG_REGULATOR > +static int da850_set_voltage(unsigned int index); > +static int da850_regulator_init(void); > +#endif > + > +static struct davinci_cpufreq_config cpufreq_info = { > + .freq_table = da850_freq_table, > +#ifdef CONFIG_REGULATOR > + .init = da850_regulator_init, > + .set_voltage = da850_set_voltage, > +#endif > +}; > + > +#ifdef CONFIG_REGULATOR > static struct regulator *cvdd; > > static int da850_set_voltage(unsigned int index) > @@ -895,7 +938,7 @@ static int da850_set_voltage(unsigned int index) > if (!cvdd) > return -ENODEV; > > - opp = (struct da850_opp *) da850_freq_table[index].index; > + opp = (struct da850_opp *) cpufreq_info.freq_table[index].index; > > return regulator_set_voltage(cvdd, opp->cvdd_min, opp->cvdd_max); > } > @@ -912,14 +955,6 @@ static int da850_regulator_init(void) > } > #endif > > -static struct davinci_cpufreq_config cpufreq_info = { > - .freq_table = &da850_freq_table[0], > -#ifdef CONFIG_REGULATOR > - .init = da850_regulator_init, > - .set_voltage = da850_set_voltage, > -#endif > -}; > - > static struct platform_device da850_cpufreq_device = { > .name = "cpufreq-davinci", > .dev = { > @@ -928,12 +963,22 @@ static struct platform_device da850_cpufreq_device = { > .id = -1, > }; > > +unsigned int da850_max_speed = 300000; > + > int __init da850_register_cpufreq(char *async_clk) > { > + int i; > + > /* cpufreq driver can help keep an "async" clock constant */ > if (async_clk) > clk_add_alias("async", da850_cpufreq_device.name, > async_clk, NULL); > + for (i = 0; i < ARRAY_SIZE(da850_freq_table); i++) { > + if (da850_freq_table[i].frequency <= da850_max_speed) { > + cpufreq_info.freq_table = &da850_freq_table[i]; > + break; > + } > + } > > return platform_device_register(&da850_cpufreq_device); > } > @@ -942,17 +987,18 @@ static int da850_round_armrate(struct clk *clk, unsigned long rate) > { > int i, ret = 0, diff; > unsigned int best = (unsigned int) -1; > + struct cpufreq_frequency_table *table = cpufreq_info.freq_table; > > rate /= 1000; /* convert to kHz */ > > - for (i = 0; da850_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { > - diff = da850_freq_table[i].frequency - rate; > + for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { > + diff = table[i].frequency - rate; > if (diff < 0) > diff = -diff; > > if (diff < best) { > best = diff; > - ret = da850_freq_table[i].frequency; > + ret = table[i].frequency; > } > } > > @@ -973,7 +1019,7 @@ static int da850_set_pll0rate(struct clk *clk, unsigned long index) > struct pll_data *pll = clk->pll_data; > int ret; > > - opp = (struct da850_opp *) da850_freq_table[index].index; > + opp = (struct da850_opp *) cpufreq_info.freq_table[index].index; > prediv = opp->prediv; > mult = opp->mult; > postdiv = opp->postdiv; > diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h > index 4247b3f..e7f9520 100644 > --- a/arch/arm/mach-davinci/include/mach/da8xx.h > +++ b/arch/arm/mach-davinci/include/mach/da8xx.h > @@ -28,6 +28,13 @@ extern void __iomem *da8xx_syscfg0_base; > extern void __iomem *da8xx_syscfg1_base; > > /* > + * If the DA850/OMAP-L138/AM18x SoC on board is of a higher speed grade > + * (than the regular 300Mhz variant), the board code should set this up > + * with the supported speed before calling da850_register_cpufreq(). > + */ > +extern unsigned int da850_max_speed; > + > +/* > * The cp_intc interrupt controller for the da8xx isn't in the same > * chunk of physical memory space as the other registers (like it is > * on the davincis) so it needs to be mapped separately. It will be From m-karicheri2 at ti.com Thu Dec 2 08:41:31 2010 From: m-karicheri2 at ti.com (Karicheri, Muralidharan) Date: Thu, 2 Dec 2010 08:41:31 -0600 Subject: [PATCH v3 1/6] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: <1291293485-2009-1-git-send-email-manjunath.hadli@ti.com> References: <1291293485-2009-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Manjunath I think you need to replace all of the v4l2_err() calls that are used in the path of user API invocation with v4l2_dbg(). You don't want to see any error logs thrown to console when a user application is running. If debugging is required, one can set the debug bootargs to see the error log. Please include my ack with this change. Murali Karicheri Software Design Engineer Texas Instruments Inc. Germantown, MD 20874 >-----Original Message----- >From: Hadli, Manjunath >Sent: Thursday, December 02, 2010 7:38 AM >To: LMML >Cc: dlos; Mauro Carvalho Chehab; Hans Verkuil; Hadli, Manjunath; Karicheri, >Muralidharan >Subject: [PATCH v3 1/6] davinci vpbe: V4L2 display driver for DM644X SoC > >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 is 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 >Signed-off-by: Muralidharan Karicheri >--- > drivers/media/video/davinci/vpbe_display.c | 2103 >++++++++++++++++++++++++++++ > include/media/davinci/vpbe_display.h | 146 ++ > include/media/davinci/vpbe_types.h | 93 ++ > 3 files changed, 2342 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..7a2d447 >--- /dev/null >+++ b/drivers/media/video/davinci/vpbe_display.c >@@ -0,0 +1,2103 @@ >+/* >+ * 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 "as is" WITHOUT ANY WARRANTY of any >+ * kind, whether express or implied; without even the implied warranty >+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ */ >+#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 >+#include "vpbe_venc_regs.h" >+ >+ >+#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" >+ >+static int debug; >+static u32 video2_numbuffers = 3; >+static u32 video3_numbuffers = 3; >+ >+#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) >+#define VPBE_DEFAULT_NUM_BUFS 3 >+ >+static u32 video2_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; >+static u32 video3_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; >+ >+module_param(video2_numbuffers, uint, S_IRUGO); >+module_param(video3_numbuffers, uint, S_IRUGO); >+module_param(video2_bufsize, uint, S_IRUGO); >+module_param(video3_bufsize, uint, S_IRUGO); >+module_param(debug, int, 0644); >+ >+static struct buf_config_params display_buf_config_params = { >+ .min_numbuffers = VPBE_DEFAULT_NUM_BUFS, >+ .numbuffers[0] = VPBE_DEFAULT_NUM_BUFS, >+ .numbuffers[1] = VPBE_DEFAULT_NUM_BUFS, >+ .min_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, >+ .min_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, >+ .layer_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, >+ .layer_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, >+}; >+ >+static struct vpbe_device *vpbe_dev; >+static struct osd_state *osd_device; >+static int vpbe_display_nr[] = { 2, 3 }; >+ >+static struct v4l2_capability vpbe_display_videocap = { >+ .driver = VPBE_DISPLAY_DRIVER, >+ .bus_info = "platform", >+ .version = VPBE_DISPLAY_VERSION_CODE, >+ .capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, >+}; >+ >+static u8 layer_first_int[VPBE_DISPLAY_MAX_DEVICES]; >+ >+__iomem void *reg_base_venc; >+ >+static int venc_is_second_field() >+{ >+ u32 val; >+ val = __raw_readl(reg_base_venc + VENC_VSTAT); >+ return ((val & VENC_VSTAT_FIDST) >+ == VENC_VSTAT_FIDST); >+} >+ >+/* >+ * vpbe_display_isr() >+ * ISR function. It changes status of the displayed buffer, takes next >buffer >+ * from the queue and sets its address in VPBE registers >+ */ >+static void vpbe_display_isr(unsigned int event, void *disp_obj) >+{ >+ unsigned long jiffies_time = get_jiffies_64(); >+ struct timeval timevalue; >+ int i, fid; >+ unsigned long addr = 0; >+ struct vpbe_display_obj *layer = NULL; >+ struct vpbe_display *disp_dev = (struct vpbe_display *)disp_obj; >+ >+ /* Convert time represention from jiffies to timeval */ >+ jiffies_to_timeval(jiffies_time, &timevalue); >+ >+ 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; >+ /* Check the field format */ >+ if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && >+ (event & OSD_END_OF_FRAME)) { >+ /* Progressive mode */ >+ if (layer_first_int[i]) >+ layer_first_int[i] = 0; >+ continue; >+ /* >+ * Mark status of the cur_frm to >+ * done and unlock semaphore on it >+ */ >+ >+ if (layer->cur_frm != layer->next_frm) { >+ layer->cur_frm->ts = timevalue; >+ 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; >+ } >+ /* Get the next buffer from buffer queue */ >+ spin_lock(&disp_dev->dma_queue_lock); >+ if (!list_empty(&layer->dma_queue)) { >+ layer->next_frm = >+ list_entry(layer->dma_queue.next, >+ struct videobuf_buffer, queue); >+ /* Remove that buffer from the buffer queue */ >+ list_del(&layer->next_frm->queue); >+ /* Mark status of the buffer as 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_dev->cbcr_ofst); >+ } >+ spin_unlock(&disp_dev->dma_queue_lock); >+ } else { >+ /* >+ * Interlaced mode >+ * If it is first interrupt, ignore it >+ */ >+ if (layer_first_int[i]) { >+ layer_first_int[i] = 0; >+ return; >+ } >+ >+ layer->field_id ^= 1; >+ if (event & OSD_FIRST_FIELD) >+ fid = 0; >+ else if (event & OSD_SECOND_FIELD) >+ fid = 1; >+ else >+ return; >+ >+ /* >+ * If field id does not match with stored >+ * field id >+ */ >+ if (fid != layer->field_id) { >+ /* Make them in sync */ >+ if (0 == fid) >+ layer->field_id = fid; >+ >+ return; >+ } >+ /* >+ * device field id and local field id are >+ * in sync. If this is even field >+ */ >+ if (0 == fid) { >+ if (layer->cur_frm == layer->next_frm) >+ continue; >+ /* >+ * one frame is displayed If next frame is >+ * available, release cur_frm and move on >+ * copy frame display time >+ */ >+ layer->cur_frm->ts = timevalue; >+ /* Change status of the cur_frm */ >+ layer->cur_frm->state = VIDEOBUF_DONE; >+ /* unlock semaphore on cur_frm */ >+ wake_up_interruptible(&layer->cur_frm->done); >+ /* Make cur_frm pointing to next_frm */ >+ layer->cur_frm = layer->next_frm; >+ } else if (1 == fid) { /* odd field */ >+ >+ if (list_empty(&layer->dma_queue) >+ || (layer->cur_frm != layer->next_frm)) >+ continue; >+ >+ /* >+ * one field is displayed configure >+ * the next frame if it is available >+ * otherwise hold on current frame >+ * Get next from the buffer queue >+ */ >+ spin_lock(&disp_dev->dma_queue_lock); >+ 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); >+ >+ /* 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_dev->cbcr_ofst); >+ spin_unlock(&disp_dev->dma_queue_lock); >+ } >+ } >+ } >+} >+ >+/* interrupt service routine */ >+static irqreturn_t venc_isr(int irq, void *arg) >+{ >+ static unsigned last_event; >+ unsigned event = 0; >+ >+ if (venc_is_second_field()) >+ 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; >+ >+ vpbe_display_isr(event, arg); >+ 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_display_obj *layer = fh->layer; >+ unsigned long addr; >+ int ret = 0; >+ >+ 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_display_obj *layer = fh->layer; >+ int buf_size; >+ >+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); >+ >+ *size = layer->pix_fmt.sizeimage; >+ buf_size = >+ display_buf_config_params.layer_bufsize[layer->device_id]; >+ /* >+ * For MMAP, limit the memory allocation as per bootarg >+ * configured buffer size >+ */ >+ if (V4L2_MEMORY_MMAP == layer->memory) >+ if (*size > buf_size) >+ *size = buf_size; >+ >+ /* Store number of buffers allocated in numbuffer member */ >+ if (*count < display_buf_config_params.min_numbuffers) >+ *count = layer->numbuffers = >+ display_buf_config_params.numbuffers[layer->device_id]; >+ >+ 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_display_obj *layer = fh->layer; >+ struct vpbe_display *disp = fh->disp_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_display_obj *layer = fh->layer; >+ >+ 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_display_obj* >+_vpbe_display_get_other_win(struct vpbe_display *disp_dev, >+ struct vpbe_display_obj *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_video_display_params(struct vpbe_display *disp_dev, >+ struct vpbe_display_obj *layer) >+{ >+ struct osd_layer_config *cfg = &layer->layer_info.config; >+ unsigned long addr; >+ int ret = 0; >+ >+ 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_display_obj *otherlayer = >+ _vpbe_display_get_other_win(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_display_obj *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; >+ int h_scale = 0, v_scale = 0, h_exp = 0, v_exp = 0, temp; >+ 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)) { >+ temp = (cfg->xsize * >+ VPBE_DISPLAY_H_EXP_RATIO_N) / >+ VPBE_DISPLAY_H_EXP_RATIO_D; >+ if (temp <= expected_xsize) { >+ h_exp = 1; >+ cfg->xsize = temp; >+ } >+ } >+ } >+ 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)) { >+ temp = (cfg->ysize * >+ VPBE_DISPLAY_V_EXP_RATIO_N) / >+ VPBE_DISPLAY_V_EXP_RATIO_D; >+ if (temp <= expected_ysize) { >+ v_exp = 1; >+ cfg->ysize = temp; >+ } >+ } >+ } >+ 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_display_obj *layer, >+ int top, int left) >+{ >+ struct osd_layer_config *cfg = &layer->layer_info.config; >+ cfg->xpos = cfg->ypos = 0; >+ if (left + cfg->xsize <= vpbe_dev->current_timings.xres) >+ cfg->xpos = left; >+ if (top + cfg->ysize <= vpbe_dev->current_timings.yres) >+ cfg->ypos = top; >+ >+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, >+ "new xpos = %d, ypos = %d\n", >+ cfg->xpos, cfg->ypos); >+} >+ >+static int vpbe_disp_check_window_params(struct vpbe_display *disp_dev, >+ struct v4l2_rect *c) >+{ >+ if ((c->width == 0) >+ || ((c->width + c->left) > vpbe_dev->current_timings.xres) >+ || (c->height == 0) >+ || ((c->height + c->top) > vpbe_dev->current_timings.yres)) { >+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid crop values\n"); >+ return -1; >+ } >+ if ((c->height & 0x1) && (vpbe_dev->current_timings.interlaced)) { >+ v4l2_err(&vpbe_dev->v4l2_dev, >+ "window height must be even for interlaced display\n"); >+ return -1; >+ } >+ return 0; >+} >+ >+/** >+ * 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. If application likes to add pads at the end of each line and >+ * end of the buffer , it can set bytesperline to line size and sizeimage >to >+ * bytesperline * height of the buffer. If driver fills zero for active >+ * video width and height, and has requested user bytesperline and >sizeimage, >+ * width and height is adjusted to maximum display limit or buffer width >+ * height which ever is lower >+ */ >+static int vpbe_try_format(struct vpbe_display *disp_dev, >+ struct v4l2_pix_format *pixfmt, int check) >+{ >+ int min_sizeimage, bpp, min_height = 1, min_width = 32, >+ max_width, max_height, user_info = 0; >+ >+ 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_ANY) { >+ 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->bytesperline) { >+ v4l2_err(&vpbe_dev->v4l2_dev, "bytesperline and width" >+ " cannot be zero\n"); >+ return -EINVAL; >+ } >+ >+ /* if user provided bytesperline, it must provide sizeimage as well >*/ >+ if (pixfmt->bytesperline && !pixfmt->sizeimage) { >+ v4l2_err(&vpbe_dev->v4l2_dev, >+ "sizeimage must be non zero, when user" >+ " provides bytesperline\n"); >+ return -EINVAL; >+ } >+ >+ /* adjust bytesperline as per hardware - multiple of 32 */ >+ if (!pixfmt->width) >+ pixfmt->width = pixfmt->bytesperline / bpp; >+ >+ if (!pixfmt->bytesperline) >+ pixfmt->bytesperline = pixfmt->width * bpp; >+ else >+ user_info = 1; >+ pixfmt->bytesperline = ((pixfmt->bytesperline + 31) & ~31); >+ >+ if (pixfmt->width < min_width) { >+ if (check) { >+ v4l2_err(&vpbe_dev->v4l2_dev, >+ "height is less than minimum," >+ "input width = %d, min_width = %d\n", >+ pixfmt->width, min_width); >+ return -EINVAL; >+ } >+ pixfmt->width = min_width; >+ } >+ >+ if (pixfmt->width > max_width) { >+ if (check) { >+ v4l2_err(&vpbe_dev->v4l2_dev, >+ "width is more than maximum," >+ "input width = %d, max_width = %d\n", >+ pixfmt->width, max_width); >+ return -EINVAL; >+ } >+ pixfmt->width = max_width; >+ } >+ >+ /* >+ * If height is zero, then atleast we need to have sizeimage >+ * to calculate height >+ */ >+ if (!pixfmt->height) { >+ if (user_info) { >+ if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) { >+ /* >+ * for NV12 format, sizeimage is y-plane size >+ * + CbCr plane which is half of y-plane >+ */ >+ pixfmt->height = pixfmt->sizeimage / >+ (pixfmt->bytesperline + >+ (pixfmt->bytesperline >> 1)); >+ } else >+ pixfmt->height = pixfmt->sizeimage/ >+ pixfmt->bytesperline; >+ } >+ } >+ >+ if (pixfmt->height > max_height) { >+ if (check && !user_info) { >+ v4l2_err(&vpbe_dev->v4l2_dev, >+ "height is more than maximum," >+ "input height = %d, max_height = %d\n", >+ pixfmt->height, max_height); >+ return -EINVAL; >+ } >+ pixfmt->height = max_height; >+ } >+ >+ if (pixfmt->height < min_height) { >+ if (check && !user_info) { >+ v4l2_err(&vpbe_dev->v4l2_dev, >+ "width is less than minimum," >+ "input height = %d, min_height = %d\n", >+ pixfmt->height, min_height); >+ return -EINVAL; >+ } >+ pixfmt->height = min_width; >+ } >+ >+ /* if user has not provided bytesperline calculate it based on width >*/ >+ if (!user_info) >+ pixfmt->bytesperline = (((pixfmt->width * bpp) + 31) & ~31); >+ >+ if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) >+ min_sizeimage = pixfmt->bytesperline * pixfmt->height + >+ (pixfmt->bytesperline * pixfmt->height >> 1); >+ else >+ min_sizeimage = pixfmt->bytesperline * pixfmt->height; >+ >+ if (pixfmt->sizeimage < min_sizeimage) { >+ if (check && user_info) { >+ v4l2_err(&vpbe_dev->v4l2_dev, "sizeimage is less, %d\n", >+ min_sizeimage); >+ return -EINVAL; >+ } >+ pixfmt->sizeimage = min_sizeimage; >+ } >+ 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_display_obj *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_display_obj *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_display_obj *layer = fh->layer; >+ >+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, >+ "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id); >+ *cap = vpbe_display_videocap; >+ >+ return 0; >+} >+ >+static int vpbe_display_s_crop(struct file *file, void *priv, >+ struct v4l2_crop *crop) >+{ >+ int ret = 0; >+ struct vpbe_fh *fh = file->private_data; >+ struct vpbe_display_obj *layer = fh->layer; >+ struct vpbe_display *disp_dev = video_drvdata(file); >+ struct osd_layer_config *cfg = &layer->layer_info.config; >+ >+ 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) { >+ struct v4l2_rect *rect = &crop->c; >+ >+ if (rect->top < 0 || rect->left < 0) { >+ v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); >+ return -EINVAL; >+ } >+ >+ if (vpbe_disp_check_window_params(disp_dev, rect)) { >+ v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); >+ return -EINVAL; >+ } >+ 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); >+ >+ } else { >+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); >+ return -EINVAL; >+ } >+ >+ return ret; >+} >+ >+static int vpbe_display_g_crop(struct file *file, void *priv, >+ struct v4l2_crop *crop) >+{ >+ int ret = 0; >+ struct vpbe_fh *fh = file->private_data; >+ struct vpbe_display_obj *layer = fh->layer; >+ struct osd_layer_config *cfg = &layer->layer_info.config; >+ >+ 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) { >+ struct v4l2_rect *rect = &crop->c; >+ if (ret) >+ return ret; >+ 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; >+ } else { >+ v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); >+ ret = -EINVAL; >+ } >+ >+ return ret; >+} >+ >+static int vpbe_display_cropcap(struct file *file, void *priv, >+ struct v4l2_cropcap *cropcap) >+{ >+ >+ 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_display_obj *layer = fh->layer; >+ >+ 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) { >+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; >+ /* Fill in the information about format */ >+ *pixfmt = layer->pix_fmt; >+ } else { >+ v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); >+ return -EINVAL; >+ } >+ >+ 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_display_obj *layer = fh->layer; >+ 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 > 0) { >+ 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 if (index == 1) { >+ 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) >+{ >+ int ret = 0; >+ struct vpbe_fh *fh = file->private_data; >+ struct vpbe_display *disp_dev = video_drvdata(file); >+ struct vpbe_display_obj *layer = fh->layer; >+ struct osd_layer_config *cfg = &layer->layer_info.config; >+ >+ 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) { >+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; >+ /* 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; >+ >+ /* Change of the default pixel format for both video windows */ >+ if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { >+ struct vpbe_display_obj *otherlayer; >+ cfg->pixfmt = PIXFMT_NV12; >+ otherlayer = _vpbe_display_get_other_win(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); >+ >+ /* verify if readback values are as expected */ >+ if (layer->pix_fmt.width != cfg->xsize || >+ layer->pix_fmt.height != cfg->ysize || >+ layer->pix_fmt.bytesperline != layer->layer_info. >+ config.line_length || >+ (cfg->interlaced >+ && layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || >+ (!cfg->interlaced && layer->pix_fmt.field >+ != V4L2_FIELD_NONE)) { >+ >+ v4l2_err(&vpbe_dev->v4l2_dev, "mismatch:layer conf params:\n"); >+ return -EINVAL; >+ } >+ >+ } else { >+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n"); >+ return -EINVAL; >+ } >+ >+ return 0; >+} >+ >+static int vpbe_display_try_fmt(struct file *file, void *priv, >+ struct v4l2_format *fmt) >+{ >+ struct vpbe_display *disp_dev = video_drvdata(file); >+ >+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); >+ >+ if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { >+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; >+ /* Check for valid field format */ >+ return vpbe_try_format(disp_dev, pixfmt, 0); >+ } else { >+ v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); >+ return -EINVAL; >+ } >+ >+ return 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_display_obj *layer = fh->layer; >+ int ret = 0; >+ >+ 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; >+ } >+ } >+ return ret; >+} >+ >+/** >+ * 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) >+{ >+ 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) >+{ >+ int ret = 0; >+ >+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); >+ >+ /* Enumerate outputs */ >+ >+ if (NULL != vpbe_dev->ops.enum_outputs) { >+ 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 ret; >+} >+ >+/** >+ * 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_display_obj *layer = fh->layer; >+ int ret = 0; >+ >+ 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) { >+ 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 ret; >+} >+ >+/** >+ * 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) >+{ >+ 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) >+{ >+ int ret = 0; >+ >+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n"); >+ >+ /* Enumerate outputs */ >+ >+ if (NULL != vpbe_dev->ops.enum_dv_presets) { >+ 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 ret; >+} >+ >+/** >+ * 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_display_obj *layer = fh->layer; >+ int ret = 0; >+ >+ 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) { >+ 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 ret; >+} >+ >+/** >+ * 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) >+{ >+ 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) >+{ >+ int ret = 0; >+ struct vpbe_fh *fh = file->private_data; >+ struct vpbe_display_obj *layer = fh->layer; >+ >+ 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) >+{ >+ int ret = 0; >+ struct vpbe_fh *fh = file->private_data; >+ struct vpbe_display *disp_dev = video_drvdata(file); >+ struct vpbe_display_obj *layer = fh->layer; >+ >+ 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_video_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_first_int[layer->device_id] = 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) >+{ >+ int ret = 0; >+ struct vpbe_fh *fh = file->private_data; >+ struct vpbe_display_obj *layer = fh->layer; >+ >+ 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_display_obj *layer = fh->layer; >+ >+ 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) >+{ >+ int ret = 0; >+ struct vpbe_fh *fh = file->private_data; >+ struct vpbe_display_obj *layer = fh->layer; >+ >+ 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) >+{ >+ int ret = 0; >+ struct vpbe_fh *fh = file->private_data; >+ struct vpbe_display_obj *layer = fh->layer; >+ >+ 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_display_obj *layer = fh->layer; >+ >+ 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) >+{ >+ unsigned int err = 0; >+ struct vpbe_fh *fh = filep->private_data; >+ struct vpbe_display_obj *layer = fh->layer; >+ >+ 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; >+} >+ >+static int vpbe_display_cfg_layer_default(enum vpbe_display_device_id id, >+ struct vpbe_display *disp_dev) >+{ >+ int err = 0; >+ struct osd_layer_config *layer_config; >+ struct vpbe_display_obj *layer = disp_dev->dev[id]; >+ struct osd_layer_config *cfg = &layer->layer_info.config; >+ >+ /* 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"); >+ return -EBUSY; >+ } >+ >+ layer_config = cfg; >+ /* Set the default image and crop values */ >+ layer_config->pixfmt = PIXFMT_YCbCrI; >+ layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY; >+ layer->pix_fmt.bytesperline = layer_config->line_length = >+ vpbe_dev->current_timings.xres * 2; >+ >+ layer->pix_fmt.width = layer_config->xsize = >+ vpbe_dev->current_timings.xres; >+ layer->pix_fmt.height = layer_config->ysize = >+ vpbe_dev->current_timings.yres; >+ layer->pix_fmt.sizeimage = >+ layer->pix_fmt.bytesperline * layer->pix_fmt.height; >+ layer_config->xpos = 0; >+ layer_config->ypos = 0; >+ layer_config->interlaced = vpbe_dev->current_timings.interlaced; >+ >+ /* >+ * turn off ping-pong buffer and field inversion to fix >+ * the image shaking problem in 1080I mode >+ */ >+ >+ if (cfg->interlaced) >+ layer->pix_fmt.field = V4L2_FIELD_INTERLACED; >+ else >+ layer->pix_fmt.field = V4L2_FIELD_NONE; >+ >+ err = osd_device->ops.set_layer_config(osd_device, >+ layer->layer_info.id, >+ layer_config); >+ if (err < 0) { >+ /* Couldn't set layer */ >+ v4l2_err(&vpbe_dev->v4l2_dev, >+ "Display Manager failed to set osd layer\n"); >+ return -EBUSY; >+ } >+ >+ return 0; >+} >+ >+/* >+ * 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) >+{ >+ int minor = iminor(file->f_path.dentry->d_inode); >+ struct vpbe_display *disp_dev = video_drvdata(file); >+ struct vpbe_display_obj *layer; >+ struct vpbe_fh *fh = NULL; >+ int found = -1; >+ int i = 0; >+ >+ /* Check for valid minor number */ >+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { >+ /* Get the pointer to the layer object */ >+ layer = disp_dev->dev[i]; >+ if (minor == layer->video_dev->minor) { >+ found = i; >+ break; >+ } >+ } >+ >+ /* If not found, return error no device */ >+ if (0 > found) { >+ v4l2_err(&vpbe_dev->v4l2_dev, "device not found\n"); >+ return -ENODEV; >+ } >+ >+ /* 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) { >+ /* Configure the default values for the layer */ >+ if (vpbe_display_cfg_layer_default(layer->device_id, >+ disp_dev)) { >+ v4l2_err(&vpbe_dev->v4l2_dev, >+ "Unable to configure video layer" >+ " for id = %d\n", layer->device_id); >+ 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_display_obj *layer = fh->layer; >+ struct osd_layer_config *cfg = &layer->layer_info.config; >+ struct vpbe_display *disp_dev = video_drvdata(file); >+ >+ v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); >+ /* If this is doing IO and other layer are not closed */ >+ if ((layer->usrs != 1) && fh->io_allowed) { >+ v4l2_err(&vpbe_dev->v4l2_dev, "Close other instances\n"); >+ return -EAGAIN; >+ } >+ >+ /* 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_display_obj *otherlayer; >+ otherlayer = >+ _vpbe_display_get_other_win(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); >+ >+ if (strcmp("vpbe_controller", pdev->name) == 0) >+ vpbe_dev = platform_get_drvdata(pdev); >+ >+ if (strcmp("vpbe-osd", pdev->name) == 0) >+ osd_device = platform_get_drvdata(pdev); >+ >+ return 0; >+} >+ >+/*Configure the channels, buffer size */ >+static int init_vpbe_layer_objects(int i) >+{ >+ int free_buffer_index; >+ >+ /* Default number of buffers should be 3 */ >+ if ((video2_numbuffers > 0) && >+ (video2_numbuffers < display_buf_config_params.min_numbuffers)) >+ video2_numbuffers = display_buf_config_params.min_numbuffers; >+ if ((video3_numbuffers > 0) && >+ (video3_numbuffers < display_buf_config_params.min_numbuffers)) >+ video3_numbuffers = display_buf_config_params.min_numbuffers; >+ >+ /* >+ * Set buffer size to min buffers size if invalid >+ * buffer size is given >+ */ >+ if (video2_bufsize < >+ display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]) >+ video2_bufsize = >+ display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]; >+ >+ if (video3_bufsize < >+ display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]) >+ video3_bufsize = >+ display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]; >+ >+ /* set number of buffers, they could come from boot/args */ >+ display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_0] = >+ video2_numbuffers; >+ display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_1] = >+ video3_numbuffers; >+ >+ if (display_buf_config_params.numbuffers[0] == 0) >+ printk(KERN_ERR "no vid2 buffer allocated\n"); >+ if (display_buf_config_params.numbuffers[1] == 0) >+ printk(KERN_ERR "no vid3 buffer allocated\n"); >+ free_buffer_index = display_buf_config_params.numbuffers[i - 1]; >+ >+ 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 __init int vpbe_display_probe(struct platform_device *pdev) >+{ >+ int i, j = 0, k, err = 0; >+ struct vpbe_display *disp_dev; >+ struct video_device *vbd = NULL; >+ struct vpbe_display_obj *vpbe_display_layer = NULL; >+ struct resource *res; >+ 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; >+ } >+ >+ /* Allocate memory for four plane display objects */ >+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { >+ disp_dev->dev[i] = >+ kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL); >+ /* If memory allocation fails, return error */ >+ if (!disp_dev->dev[i]) { >+ printk(KERN_ERR "ran out of memory\n"); >+ err = -ENOMEM; >+ goto probe_out; >+ } >+ spin_lock_init(&disp_dev->dev[i]->irqlock); >+ mutex_init(&disp_dev->dev[i]->opslock); >+ } >+ spin_lock_init(&disp_dev->dma_queue_lock); >+ >+ err = init_vpbe_layer_objects(i); >+ if (err) { >+ printk(KERN_ERR "Error initializing vpbe display\n"); >+ return err; >+ } >+ >+ /* >+ * 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, NULL, >+ vpbe_device_get); >+ if (err < 0) >+ return err; >+ >+ /* Initialize the vpbe display controller */ >+ if (NULL != vpbe_dev->ops.initialize) { >+ err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev); >+ if (err) { >+ v4l2_err(&vpbe_dev->v4l2_dev, "Error initing vpbe\n"); >+ err = -ENOMEM; >+ goto probe_out; >+ } >+ } >+ >+ /* check the name of davinci device */ >+ if (vpbe_dev->cfg->module_name != NULL) >+ strcpy(vpbe_display_videocap.card, >+ vpbe_dev->cfg->module_name); >+ >+ for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { >+ /* Get the pointer to the layer object */ >+ vpbe_display_layer = disp_dev->dev[i]; >+ /* Allocate memory for video device */ >+ vbd = video_device_alloc(); >+ if (vbd == NULL) { >+ for (j = 0; j < i; j++) { >+ video_device_release( >+ disp_dev->dev[j]->video_dev); >+ } >+ v4l2_err(&vpbe_dev->v4l2_dev, "ran out of memory\n"); >+ err = -ENOMEM; >+ goto probe_out; >+ } >+ /* Initialize field of video device */ >+ vbd->release = video_device_release; >+ vbd->fops = &vpbe_fops; >+ vbd->ioctl_ops = &vpbe_ioctl_ops; >+ vbd->minor = -1; >+ vbd->v4l2_dev = &vpbe_dev->v4l2_dev; >+ vbd->lock = &vpbe_display_layer->opslock; >+ >+ if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { >+ vbd->tvnorms = (V4L2_STD_NTSC | V4L2_STD_PAL); >+ vbd->current_norm = >+ 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); >+ >+ /* Set video_dev to the video device */ >+ vpbe_display_layer->video_dev = vbd; >+ vpbe_display_layer->device_id = i; >+ >+ vpbe_display_layer->layer_info.id = >+ ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); >+ if (display_buf_config_params.numbuffers[i] == 0) >+ vpbe_display_layer->memory = V4L2_MEMORY_USERPTR; >+ else >+ vpbe_display_layer->memory = V4L2_MEMORY_MMAP; >+ >+ /* Initialize field of the display layer objects */ >+ vpbe_display_layer->usrs = 0; >+ vpbe_display_layer->io_usrs = 0; >+ vpbe_display_layer->started = 0; >+ >+ /* Initialize prio member of layer object */ >+ v4l2_prio_init(&vpbe_display_layer->prio); >+ >+ /* Register video device */ >+ v4l2_info(&vpbe_dev->v4l2_dev, >+ "Trying to register VPBE display device.\n"); >+ v4l2_info(&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, >+ vpbe_display_nr[i]); >+ if (err) >+ goto probe_out; >+ /* set the driver data in platform device */ >+ platform_set_drvdata(pdev, disp_dev); >+ video_set_drvdata(vpbe_display_layer->video_dev, disp_dev); >+ } >+ >+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >+ if (!res) { >+ v4l2_err(&vpbe_dev->v4l2_dev, >+ "Unable to get VENC register address map\n"); >+ err = -ENODEV; >+ goto probe_out; >+ } >+ >+ reg_base_venc = ioremap_nocache(res->start, resource_size(res)); >+ if (!reg_base_venc) { >+ v4l2_err(&vpbe_dev->v4l2_dev, "Unable to map VENC IO space\n"); >+ err = -ENODEV; >+ goto probe_out; >+ } >+ >+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); >+ if (!res) { >+ v4l2_err(&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(&vpbe_dev->v4l2_dev, "Unable to request interrupt\n"); >+ err = -ENODEV; >+ goto probe_out; >+ } >+ printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 >device\n"); >+ return 0; >+probe_out: >+ kfree(disp_dev); >+ >+ for (k = 0; k < j; k++) { >+ /* Get the pointer to the layer object */ >+ vpbe_display_layer = disp_dev->dev[k]; >+ /* Unregister video device */ >+ video_unregister_device(vpbe_display_layer->video_dev); >+ /* Release video device */ >+ video_device_release(vpbe_display_layer->video_dev); >+ vpbe_display_layer->video_dev = NULL; >+ } >+ return err; >+} >+ >+/* >+ * vpbe_display_remove() >+ * It un-register hardware layer from V4L2 driver >+ */ >+static int vpbe_display_remove(struct platform_device *pdev) >+{ >+ int i; >+ struct vpbe_display_obj *vpbe_display_layer; >+ struct vpbe_display *disp_dev = platform_get_drvdata(pdev); >+ struct resource *res; >+ >+ 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); >+ >+ vpbe_display_layer->video_dev = NULL; >+ } >+ 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 __init int vpbe_display_init(void) >+{ >+ int err = 0; >+ >+ 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 DMXXX 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..90ad066 >--- /dev/null >+++ b/include/media/davinci/vpbe_display.h >@@ -0,0 +1,146 @@ >+/* >+ * 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 "as is" WITHOUT ANY WARRANTY of any >+ * kind, whether express or implied; without even the implied warranty >+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ */ >+#ifndef VPBE_DISPLAY_H >+#define VPBE_DISPLAY_H >+ >+#ifdef __KERNEL__ >+ >+/* 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_display_obj { >+ /* number of buffers in fbuffers */ >+ unsigned int numbuffers; >+ /* 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; >+}; >+ >+/* 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_display_obj *dev[VPBE_DISPLAY_MAX_DEVICES]; >+}; >+ >+/* File handle structure */ >+struct vpbe_fh { >+ /* vpbe device structure */ >+ struct vpbe_display *disp_dev; >+ /* pointer to layer object for opened device */ >+ struct vpbe_display_obj *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]; >+}; >+ >+static int venc_is_second_field(void); >+#endif /* end of __KERNEL__ */ >+#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..a4b8585 >--- /dev/null >+++ b/include/media/davinci/vpbe_types.h >@@ -0,0 +1,93 @@ >+/* >+ * 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; either version 2 of the License. >+ * >+ * 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_types { >+ DM644X_VPBE = 1, >+ DM355_VPBE, >+ DM365_VPBE, >+}; >+ >+/* 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 m-karicheri2 at ti.com Thu Dec 2 08:43:09 2010 From: m-karicheri2 at ti.com (Karicheri, Muralidharan) Date: Thu, 2 Dec 2010 08:43:09 -0600 Subject: [PATCH v3 2/6] davinci vpbe: VPBE display driver In-Reply-To: <1291293516-2185-1-git-send-email-manjunath.hadli@ti.com> References: <1291293516-2185-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Acked-by Muralidharan Karicheri Murali Karicheri Software Design Engineer Texas Instruments Inc. Germantown, MD 20874 phone: 301-407-9583 email: m-karicheri2 at ti.com >-----Original Message----- >From: Hadli, Manjunath >Sent: Thursday, December 02, 2010 7:39 AM >To: LMML >Cc: dlos; Mauro Carvalho Chehab; Hans Verkuil; Hadli, Manjunath; Karicheri, >Muralidharan >Subject: [PATCH v3 2/6] davinci vpbe: VPBE display driver > >This patch implements the coe functionality of the dislay driver, >mainly controlling the VENC and other encoders, and acting as >the one point interface for the man V4L2 driver.This implements >the cre of each of the V4L2 IOCTLs. > >Signed-off-by: Manjunath Hadli >Signed-off-by: Muralidharan Karicheri >--- > drivers/media/video/davinci/vpbe.c | 847 >++++++++++++++++++++++++++++++++++++ > include/media/davinci/vpbe.h | 186 ++++++++ > 2 files changed, 1033 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..96c0eea >--- /dev/null >+++ b/drivers/media/video/davinci/vpbe.c >@@ -0,0 +1,847 @@ >+/* >+ * 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; either version 2 of the License. >+ * >+ * 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 >+ >+ >+#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 struct osd_state *osd_device; >+static struct venc_platform_data *venc_device; >+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(ef_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_display_config *vpbe_config = vpbe_dev->cfg; >+ int index = vpbe_dev->current_sd_index; >+ return ((index == 0) ? &vpbe_config->venc : >+ &vpbe_config->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_display_config >*vpbe_config, >+ int index) >+{ >+ char *encoder_name = vpbe_config->outputs[index].subdev_name; >+ int i; >+ >+ /* Venc is always first */ >+ if (!strcmp(encoder_name, vpbe_config->venc.module_name)) >+ return 0; >+ >+ for (i = 0; i < vpbe_config->num_ext_encoders; i++) { >+ if (!strcmp(encoder_name, >+ vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; >+ int temp_index = output->index; >+ >+ if (temp_index >= vpbe_config->num_outputs) >+ return -EINVAL; >+ >+ *output = vpbe_config->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_display_config *cfg = vpbe_dev->cfg; >+ struct vpbe_enc_mode_info var; >+ int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; >+ struct vpbe_enc_mode_info var; >+ int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; >+ struct vpbe_enc_mode_info var; >+ int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; >+ struct vpbe_enc_mode_info var; >+ int curr_output = vpbe_dev->current_out_index, 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 vpbe_display_config *vpbe_config = vpbe_dev->cfg; >+ struct encoder_config_info *curr_enc_info = >+ vpbe_current_encoder_info(vpbe_dev); >+ int ret = 0, enc_out_index = 0, sd_index; >+ >+ if (index >= vpbe_config->num_outputs) >+ return -EINVAL; >+ >+ ret = mutex_lock_interruptible(&vpbe_dev->lock); >+ if (ret) >+ return ret; >+ >+ sd_index = vpbe_dev->current_sd_index; >+ enc_out_index = vpbe_config->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, >+ vpbe_config->outputs[index].subdev_name)) { >+ /* Need to switch the encoder at the output */ >+ sd_index = vpbe_find_encoder_sd_index(vpbe_config, 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, >+ vpbe_config->outputs[index].default_mode); >+ if (!ret) { >+ 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); >+ } >+ if (!ret) { >+ 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_display_config *vpbe_config = vpbe_dev->cfg; >+ int i, ret = 0; >+ >+ for (i = 0; i < vpbe_config->num_outputs; i++) { >+ if (!strcmp(def_output, >+ vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; >+ int sd_index = vpbe_dev->current_sd_index; >+ int out_index = vpbe_dev->current_out_index, ret; >+ >+ >+ if (!(vpbe_config->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; >+ >+ ret = mutex_lock_interruptible(&vpbe_dev->lock); >+ if (ret) >+ return ret; >+ >+ >+ 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) { >+ 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_display_config *vpbe_config = vpbe_dev->cfg; >+ int out_index = vpbe_dev->current_out_index; >+ struct vpbe_output *output = &vpbe_config->outputs[out_index]; >+ int i, j = 0; >+ >+ 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_display_config *vpbe_config = vpbe_dev->cfg; >+ int sd_index = vpbe_dev->current_sd_index, out_index = >+ vpbe_dev->current_out_index, ret; >+ >+ if (!(vpbe_config->outputs[out_index].output.capabilities & >+ V4L2_OUT_CAP_STD)) >+ return -EINVAL; >+ >+ ret = vpbe_get_std_info(vpbe_dev, *std_id); >+ if (ret) >+ return ret; >+ >+ ret = mutex_lock_interruptible(&vpbe_dev->lock); >+ if (ret) >+ return ret; >+ >+ 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) { >+ 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_display_config *vpbe_config = vpbe_dev->cfg; >+ int out_index = vpbe_dev->current_out_index, ret = 0, i; >+ struct vpbe_enc_mode_info *preset_mode = NULL; >+ struct v4l2_dv_preset dv_preset; >+ >+ if ((NULL == mode_info) || (NULL == mode_info->name)) >+ return -EINVAL; >+ >+ for (i = 0; i < vpbe_config->outputs[out_index].num_modes; i++) { >+ if (!strcmp(mode_info->name, >+ vpbe_config->outputs[out_index].modes[i].name)) { >+ preset_mode = &vpbe_config->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) { >+ ret = vpbe_s_std(vpbe_dev, >+ &preset_mode->timings.std_id); >+ return ret; >+ } else if (preset_mode->timings_type & >+ VPBE_ENC_DV_PRESET) { >+ dv_preset.preset = >+ preset_mode->timings.dv_preset; >+ ret = vpbe_s_dv_preset(vpbe_dev, &dv_preset); >+ return ret; >+ } >+ } >+ } >+ >+ /* Only custom timing should reach here */ >+ if (preset_mode == NULL) >+ return -EINVAL; >+ >+ ret = mutex_lock_interruptible(&vpbe_dev->lock); >+ if (ret) >+ return ret; >+ >+ if (!ret) { >+ 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); >+ if (strcmp("vpbe-osd", pdev->name) == 0) >+ osd_device = platform_get_drvdata(pdev); >+ if (strcmp("vpbe-venc", pdev->name) == 0) >+ venc_device = dev_get_platdata(&pdev->dev); >+ >+ 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; >+ int i, ret = 0, num_encoders; >+ struct i2c_adapter *i2c_adap; >+ int output_index; >+ int err; >+ >+ /* >+ * 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, NULL, >+ 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 */ >+ 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->module_name, >+ &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. >+ */ >+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; >+ /* disaable 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 __init int vpbe_probe(struct platform_device *pdev) >+{ >+ struct vpbe_display_config *vpbe_config; >+ struct vpbe_device *vpbe_dev; >+ >+ int ret = -EINVAL; >+ >+ if (NULL == pdev->dev.platform_data) { >+ v4l2_err(pdev->dev.driver, "Unable to get vpbe config\n"); >+ return -ENODEV; >+ } >+ >+ if (pdev->dev.platform_data == NULL) { >+ v4l2_err(pdev->dev.driver, "No platform data\n"); >+ return -ENODEV; >+ } >+ vpbe_config = pdev->dev.platform_data; >+ >+ if (!vpbe_config->module_name[0] || >+ !vpbe_config->osd.module_name[0] || >+ !vpbe_config->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 = vpbe_config; >+ vpbe_dev->ops = vpbe_dev_ops; >+ vpbe_dev->pdev = &pdev->dev; >+ >+ if (vpbe_config->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..c8853f2 >--- /dev/null >+++ b/include/media/davinci/vpbe.h >@@ -0,0 +1,186 @@ >+/* >+ * 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; either version 2 of the License. >+ * >+ * 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 >+ >+/* 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_display_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_display_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; >+ >+ /* >+ * 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; >+}; >+ >+/* exported functions */ >+struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev, >+ const char *venc_name); >+#endif >-- >1.6.2.4 From m-karicheri2 at ti.com Thu Dec 2 08:43:58 2010 From: m-karicheri2 at ti.com (Karicheri, Muralidharan) Date: Thu, 2 Dec 2010 08:43:58 -0600 Subject: [PATCH v3 3/6] davinci vpbe: OSD(On Screen Display) block In-Reply-To: <1291293532-2350-1-git-send-email-manjunath.hadli@ti.com> References: <1291293532-2350-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Acked-by Muralidharan Karicheri Murali Karicheri Software Design Engineer Texas Instruments Inc. Germantown, MD 20874 phone: 301-407-9583 email: m-karicheri2 at ti.com >-----Original Message----- >From: Hadli, Manjunath >Sent: Thursday, December 02, 2010 7:39 AM >To: LMML >Cc: dlos; Mauro Carvalho Chehab; Hans Verkuil; Hadli, Manjunath; Karicheri, >Muralidharan >Subject: [PATCH v3 3/6] davinci vpbe: OSD(On Screen Display) block > >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 >Signed-off-by: Muralidharan Karicheri >--- > drivers/media/video/davinci/vpbe_osd.c | 1210 >+++++++++++++++++++++++++++ > drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++++++ > include/media/davinci/vpbe_osd.h | 397 +++++++++ > 3 files changed, 1996 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..6abe529 >--- /dev/null >+++ b/drivers/media/video/davinci/vpbe_osd.c >@@ -0,0 +1,1210 @@ >+/* >+ * 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; 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 >+ * >+ */ >+#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 __raw_readl(osd->osd_base + offset); >+} >+ >+static inline u32 osd_write(struct osd_state *sd, u32 val, u32 offset) >+{ >+ struct osd_state *osd = sd; >+ __raw_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 = __raw_readl(addr) | mask; >+ >+ __raw_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 = __raw_readl(addr) & ~mask; >+ >+ __raw_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 = (__raw_readl(addr) & ~mask) | (val & mask); >+ __raw_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) >+{ >+ >+#ifdef CONFIG_DM6446_FIELD_INV_WORKAROUND >+ 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; >+ } >+#endif >+ 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) >+{ >+ int bmp_reg, bmp_offset, bmp_mask, bmp_shift; >+ static const int map_1bpp[] = { 0, 15 }; >+ static const int map_2bpp[] = { 0, 5, 10, 15 }; >+ >+ 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]; >+ unsigned long flags; >+ struct osd_layer_config *cfg = &win->lconfig; >+ >+ 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]; >+ unsigned long flags; >+ struct osd_layer_config *cfg = &win->lconfig; >+ >+ 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 = 0; >+ /* 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; >+ int reject_config; >+ unsigned long flags; >+ >+ 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_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; >+ osd->vpbe_type = (enum vpbe_types)pdev->dev.platform_data; >+ 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) >+{ >+ /* Register the driver */ >+ 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..9cd3839 >--- /dev/null >+++ b/drivers/media/video/davinci/vpbe_osd_regs.h >@@ -0,0 +1,389 @@ >+/* >+ * 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; either version 2 of the License. >+ * >+ * 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 >+ >+enum vpbe_soc_type { >+ DM644x = 0, >+ DM35x, >+ DM36x, >+}; >+ >+struct vpbe_osd_platform_data { >+ enum vpbe_soc_type soc; >+}; >+ >+#define DM644X_OSD_REG_BASE 0x01C72600 >+#define DM644X_VPBE_REG_BASE 0x01C72780 >+ >+#define DM355_VPSSCLK_REG_BASE 0x01C70000 >+#define DM355_OSD_REG_BASE 0x01C70200 >+ >+#define DM365_OSD_REG_BASE 0x01C71C00 >+#define DM365_ISP5_REG_BASE 0x01C70000 >+ >+#define OSD_REG_SIZE 0x00000100 >+ >+/* SYS register addresses */ >+#define SYS_VPSS_CLKCTL 0x01C40044 >+ >+/* 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..9549f3e >--- /dev/null >+++ b/include/media/davinci/vpbe_osd.h >@@ -0,0 +1,397 @@ >+/* >+ * 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; either version 2 of the License, or >+ * (at your option) any later version. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >+ * GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 >USA >+ * >+ */ >+#ifndef _OSD_H >+#define _OSD_H >+ >+#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; >+}; >+/* OSD events. Should match with that in vpbe_venc.h */ >+#define OSD_END_OF_FRAME 1 >+#define OSD_FIRST_FIELD 2 >+#define OSD_SECOND_FIELD 4 >+ >+/* 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; >+ >+/* TBD. Some of these operations here are to be removed and implemented >+ * as a ioctl call on the osd sub device node. Right now just trying to >+ * make this work. >+ */ >+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_types 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; >+}; >+ >+ >+#endif >-- >1.6.2.4 From m-karicheri2 at ti.com Thu Dec 2 08:44:54 2010 From: m-karicheri2 at ti.com (Karicheri, Muralidharan) Date: Thu, 2 Dec 2010 08:44:54 -0600 Subject: [PATCH 4/6] davinci vpbe: VENC( Video Encoder) implementation In-Reply-To: <1291293547-2473-1-git-send-email-manjunath.hadli@ti.com> References: <1291293547-2473-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Acked-by Muralidharan Karicheri Murali Karicheri Software Design Engineer Texas Instruments Inc. Germantown, MD 20874 phone: 301-407-9583 email: m-karicheri2 at ti.com >-----Original Message----- >From: Hadli, Manjunath >Sent: Thursday, December 02, 2010 7:39 AM >To: LMML >Cc: dlos; Mauro Carvalho Chehab; Hans Verkuil; Hadli, Manjunath; Karicheri, >Muralidharan >Subject: [PATCH 4/6] davinci vpbe: VENC( Video Encoder) implementation > >This patch adds the VENC or the Video encoder, whichis responsible >for the blending of al 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 extenal and internal encoders >of other kind to plug-in. > >Signed-off-by: Manjunath Hadli >Signed-off-by: Muralidharan Karicheri >--- > drivers/media/video/davinci/vpbe_venc.c | 574 >++++++++++++++++++++++++++ > drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++++++++ > include/media/davinci/vpbe_venc.h | 38 ++ > 3 files changed, 801 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..bf30332 >--- /dev/null >+++ b/drivers/media/video/davinci/vpbe_venc.c >@@ -0,0 +1,574 @@ >+/* >+ * 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; 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 >+ */ >+#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 __raw_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); >+ __raw_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); >+ >+ __raw_writel(val, venc->vdaccfg_reg); >+ >+ val = __raw_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) >+{ >+ int ret = 0; >+ >+ 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: >+ ret = -EINVAL; >+ } >+ return ret; >+} >+ >+static void enabledigitaloutput(struct v4l2_subdev *sd, int benable) >+{ >+ v4l2_dbg(debug, 2, sd, "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; >+ >+ 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; >+ >+ 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; >+ >+ 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; >+ >+ 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) >+{ >+ int ret = 0; >+ >+ v4l2_dbg(debug, 1, sd, "venc_s_std_output\n"); >+ >+ if (norm & V4L2_STD_NTSC) >+ ret = venc_set_ntsc(sd); >+ else if (norm & V4L2_STD_PAL) >+ ret = venc_set_pal(sd); >+ else >+ ret = -EINVAL; >+ return ret; >+} >+ >+static int venc_s_dv_preset(struct v4l2_subdev *sd, >+ struct v4l2_dv_preset *dv_preset) >+{ >+ int ret = -EINVAL; >+ >+ 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 ret; >+} >+ >+static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, >+ u32 config) >+{ >+ struct venc_state *venc = to_state(sd); >+ int max_output, lcd_out_index, ret = 0; >+ >+ v4l2_dbg(debug, 1, sd, "venc_s_routing\n"); >+ >+ max_output = 2 + venc->pdata->num_lcd_outputs; >+ lcd_out_index = 3; >+ >+ if (output >= max_output) >+ return -EINVAL; >+ >+ if (output < lcd_out_index) >+ ret = venc_set_dac(sd, output); >+ if (!ret) >+ venc->output = output; >+ return ret; >+} >+ >+#ifdef CONFIG_VIDEO_ADV_DEBUG >+static int venc_g_register(struct v4l2_subdev *sd, >+ struct v4l2_dbg_register *reg) >+{ >+ __iomem void *reg_base; >+ >+ if (reg->match.type == 2) >+ reg->val = venc_read(sd, reg->reg); >+ else if (reg->match.type == 3) { >+ reg_base = ioremap_nocache(0x01c70800, 0x80); >+ if (reg_base == NULL) >+ return -EINVAL; >+ reg->val = __raw_readl(reg_base + reg->reg); >+ iounmap(reg_base); >+ } else { >+ reg_base = ioremap_nocache(0x01c70000, 0x80); >+ if (reg_base == NULL) >+ return -1; >+ reg->val = __raw_readl(reg_base + reg->reg); >+ iounmap(reg_base); >+ } >+ return 0; >+} >+#endif >+ >+static const struct v4l2_subdev_core_ops venc_core_ops = { >+#ifdef CONFIG_VIDEO_ADV_DEBUG >+ .g_register = venc_g_register, >+#endif >+}; >+ >+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 = 0; >+ >+ /* Set default to output to composite and std to NTSC */ >+ venc->output = 0; >+ venc->std = V4L2_STD_NTSC; >+ >+ 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) >+{ >+ /* Register the driver */ >+ 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..38e4818 >--- /dev/null >+++ b/drivers/media/video/davinci/vpbe_venc_regs.h >@@ -0,0 +1,189 @@ >+/* >+ * 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; either version 2 of the License. >+ * >+ * 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 register base addresses */ >+#define DM644X_VENC_REG_BASE 0x01C72400 >+#define DM644X_VPBE_REG_BASE 0x01C72780 >+ >+#define DM355_VENC_REG_BASE 0x01C70400 >+ >+#define DM365_VENC_REG_BASE 0x01C71E00 >+#define DM365_ISP5_REG_BASE 0x01C70000 >+ >+#define DM3XX_VDAC_CONFIG 0x01C4002C >+#define DM355_USB_PHY_CTRL 0x01c40034 >+ >+/* 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..47a977c >--- /dev/null >+++ b/include/media/davinci/vpbe_venc.h >@@ -0,0 +1,38 @@ >+/* >+ * 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; either version 2 of the License. >+ * >+ * 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 >+ >+#define VPBE_VENC_SUBDEV_NAME "vpbe-venc" >+ >+/* venc events */ >+#define VENC_END_OF_FRAME 1 >+#define VENC_FIRST_FIELD 2 >+#define VENC_SECOND_FIELD 4 >+ >+struct venc_platform_data { >+ enum vpbe_types venc_type; >+ int (*setup_clock)(enum vpbe_enc_timings_type type, >+ __u64 mode); >+ /* Number of LCD outputs supported */ >+ int num_lcd_outputs; >+}; >+ >+ >+#endif >-- >1.6.2.4 From m-karicheri2 at ti.com Thu Dec 2 08:48:18 2010 From: m-karicheri2 at ti.com (Karicheri, Muralidharan) Date: Thu, 2 Dec 2010 08:48:18 -0600 Subject: [PATCH v3 5/6] davinci vpbe: platform specific additions In-Reply-To: <1291293567-2676-1-git-send-email-manjunath.hadli@ti.com> References: <1291293567-2676-1-git-send-email-manjunath.hadli@ti.com> Message-ID: >-----Original Message----- >From: Hadli, Manjunath >Sent: Thursday, December 02, 2010 7:39 AM >To: LMML >Cc: dlos; Mauro Carvalho Chehab; Hans Verkuil; Hadli, Manjunath; Karicheri, >Muralidharan >Subject: [PATCH v3 5/6] davinci vpbe: platform specific additions > >This patch implements the overall device creation for the Video >display driver, and addition of tables for the mode and output list > >Signed-off-by: Manjunath Hadli >Signed-off-by: Muralidharan Karicheri >--- > arch/arm/mach-davinci/board-dm644x-evm.c | 79 +++++++++++-- > arch/arm/mach-davinci/dm644x.c | 164 >++++++++++++++++++++++++++- > arch/arm/mach-davinci/include/mach/dm644x.h | 4 + > 3 files changed, 228 insertions(+), 19 deletions(-) > >diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach- >davinci/board-dm644x-evm.c >index 34c8b41..e9b1243 100644 >--- a/arch/arm/mach-davinci/board-dm644x-evm.c >+++ b/arch/arm/mach-davinci/board-dm644x-evm.c >@@ -166,18 +166,6 @@ static struct platform_device >davinci_evm_nandflash_device = { > .resource = davinci_evm_nandflash_resource, > }; > >-static u64 davinci_fb_dma_mask = DMA_BIT_MASK(32); >- >-static struct platform_device davinci_fb_device = { >- .name = "davincifb", >- .id = -1, >- .dev = { >- .dma_mask = &davinci_fb_dma_mask, >- .coherent_dma_mask = DMA_BIT_MASK(32), >- }, >- .num_resources = 0, >-}; >- > static struct tvp514x_platform_data tvp5146_pdata = { > .clk_polarity = 0, > .hs_polarity = 1, >@@ -606,8 +594,71 @@ static void __init evm_init_i2c(void) > i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info)); > } > >+#define VENC_STD_ALL (V4L2_STD_NTSC | V4L2_STD_PAL) >+/* venc standards timings */ >+static struct vpbe_enc_mode_info vbpe_enc_std_timings[] = { >+ {"ntsc", VPBE_ENC_STD, {V4L2_STD_525_60}, 1, 720, 480, >+ {11, 10}, {30000, 1001}, 0x79, 0, 0x10, 0, 0, 0, 0}, >+ {"pal", VPBE_ENC_STD, {V4L2_STD_625_50}, 1, 720, 576, >+ {54, 59}, {25, 1}, 0x7E, 0, 0x16, 0, 0, 0, 0}, >+}; >+ >+/* venc dv preset timings */ >+static struct vpbe_enc_mode_info vbpe_enc_preset_timings[] = { >+ {"480p59_94", VPBE_ENC_DV_PRESET, {V4L2_DV_480P59_94}, 0, 720, 480, >+ {1, 1}, {5994, 100}, 0x80, 0, 0x20, 0, 0, 0, 0}, >+ {"576p50", VPBE_ENC_DV_PRESET, {V4L2_DV_576P50}, 0, 720, 576, >+ {1, 1}, {50, 1}, 0x7E, 0, 0x30, 0, 0, 0, 0}, >+}; >+ >+/* >+ * The outputs available from VPBE + ecnoders. Keep the >+ * the order same as that of encoders. First that from venc followed by >that >+ * from encoders. Index in the output refers to index on a particular >encoder. >+ * Driver uses this index to pass it to encoder when it supports more than >+ * one output. Application uses index of the array to set an output. >+ */ >+static struct vpbe_output dm644x_vpbe_outputs[] = { >+ { >+ .output = { >+ .index = 0, >+ .name = "Composite", >+ .type = V4L2_OUTPUT_TYPE_ANALOG, >+ .std = VENC_STD_ALL, >+ .capabilities = V4L2_OUT_CAP_STD, >+ }, >+ .subdev_name = VPBE_VENC_SUBDEV_NAME, >+ .default_mode = "ntsc", >+ .num_modes = ARRAY_SIZE(vbpe_enc_std_timings), >+ .modes = vbpe_enc_std_timings, >+ }, >+ { >+ .output = { >+ .index = 1, >+ .name = "Component", >+ .type = V4L2_OUTPUT_TYPE_ANALOG, >+ .capabilities = V4L2_OUT_CAP_PRESETS, >+ }, >+ .subdev_name = VPBE_VENC_SUBDEV_NAME, >+ .default_mode = "480p59_94", >+ .num_modes = ARRAY_SIZE(vbpe_enc_preset_timings), >+ .modes = vbpe_enc_preset_timings, >+ }, >+}; >+ >+static struct vpbe_display_config vpbe_display_cfg = { >+ .module_name = "dm644x-vpbe-display", >+ .i2c_adapter_id = 1, >+ .osd = { >+ .module_name = VPBE_OSD_SUBDEV_NAME, >+ }, >+ .venc = { >+ .module_name = VPBE_VENC_SUBDEV_NAME, >+ }, >+ .num_outputs = ARRAY_SIZE(dm644x_vpbe_outputs), >+ .outputs = dm644x_vpbe_outputs, >+}; > static struct platform_device *davinci_evm_devices[] __initdata = { >- &davinci_fb_device, > &rtc_dev, > }; > >@@ -620,6 +671,8 @@ davinci_evm_map_io(void) > { > /* setup input configuration for VPFE input devices */ > dm644x_set_vpfe_config(&vpfe_cfg); >+ /* setup configuration for vpbe devices */ >+ dm644x_set_vpbe_display_config(&vpbe_display_cfg); > dm644x_init(); > } > >diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach- >davinci/dm644x.c >index 5e5b0a7..e8b8e94 100644 >--- a/arch/arm/mach-davinci/dm644x.c >+++ b/arch/arm/mach-davinci/dm644x.c >@@ -640,6 +640,142 @@ void dm644x_set_vpfe_config(struct vpfe_config *cfg) > vpfe_capture_dev.dev.platform_data = cfg; > } > >+static struct resource dm644x_osd_resources[] = { >+ { >+ .start = 0x01C72600, >+ .end = 0x01C72600 + 0x200, >+ .flags = IORESOURCE_MEM, >+ }, >+}; >+ >+static u64 dm644x_osd_dma_mask = DMA_BIT_MASK(32); >+ >+static struct platform_device dm644x_osd_dev = { >+ .name = VPBE_OSD_SUBDEV_NAME, >+ .id = -1, >+ .num_resources = ARRAY_SIZE(dm644x_osd_resources), >+ .resource = dm644x_osd_resources, >+ .dev = { >+ .dma_mask = &dm644x_osd_dma_mask, >+ .coherent_dma_mask = DMA_BIT_MASK(32), >+ .platform_data = (void *)DM644X_VPBE, >+ }, >+}; >+ >+static struct resource dm644x_venc_resources[] = { >+ /* venc registers io space */ >+ { >+ .start = 0x01C72400, >+ .end = 0x01C72400 + 0x180, >+ .flags = IORESOURCE_MEM, >+ }, >+}; >+ >+static u64 dm644x_venc_dma_mask = DMA_BIT_MASK(32); >+ >+#define VPSS_CLKCTL 0x01C40044 >+static void __iomem *vpss_clkctl_reg; >+ >+/* TBD. Check what VENC_CLOCK_SEL settings for HDTV and EDTV */ Please remove this TBD comment. This is already addressed in the code. Add my Ack after removing this. >+static int dm644x_venc_setup_clock(enum vpbe_enc_timings_type type, __u64 >mode) >+{ >+ int ret = 0; >+ >+ if (NULL == vpss_clkctl_reg) >+ return -EINVAL; >+ if (type == VPBE_ENC_STD) { >+ __raw_writel(0x18, vpss_clkctl_reg); >+ } else if (type == VPBE_ENC_DV_PRESET) { >+ switch ((unsigned int)mode) { >+ case V4L2_DV_480P59_94: >+ case V4L2_DV_576P50: >+ __raw_writel(0x19, vpss_clkctl_reg); >+ break; >+ case V4L2_DV_720P60: >+ case V4L2_DV_1080I60: >+ case V4L2_DV_1080P30: >+ /* >+ * For HD, use external clock source since HD requires higher >+ * clock rate >+ */ >+ __raw_writel(0xa, vpss_clkctl_reg); >+ break; >+ default: >+ ret = -EINVAL; >+ break; >+ } >+ } else >+ ret = -EINVAL; >+ >+ return ret; >+} >+ >+ >+static inline u32 dm644x_reg_modify(void *reg, u32 val, u32 mask) >+{ >+ u32 new_val = (__raw_readl(reg) & ~mask) | (val & mask); >+ __raw_writel(new_val, reg); >+ return new_val; >+} >+ >+static u64 vpbe_display_dma_mask = DMA_BIT_MASK(32); >+ >+static struct resource dm644x_v4l2_disp_resources[] = { >+ { >+ .start = IRQ_VENCINT, >+ .end = IRQ_VENCINT, >+ .flags = IORESOURCE_IRQ, >+ }, >+ { >+ .start = 0x01C72400, >+ .end = 0x01C72400 + 0x180, >+ .flags = IORESOURCE_MEM, >+ }, >+ >+}; >+static struct platform_device vpbe_v4l2_display = { >+ .name = "vpbe-v4l2", >+ .id = -1, >+ .num_resources = ARRAY_SIZE(dm644x_v4l2_disp_resources), >+ .resource = dm644x_v4l2_disp_resources, >+ .dev = { >+ .dma_mask = &vpbe_display_dma_mask, >+ .coherent_dma_mask = DMA_BIT_MASK(32), >+ }, >+}; >+struct venc_platform_data dm644x_venc_pdata = { >+ .venc_type = DM644X_VPBE, >+ .setup_clock = dm644x_venc_setup_clock, >+}; >+ >+static struct platform_device dm644x_venc_dev = { >+ .name = VPBE_VENC_SUBDEV_NAME, >+ .id = -1, >+ .num_resources = ARRAY_SIZE(dm644x_venc_resources), >+ .resource = dm644x_venc_resources, >+ .dev = { >+ .dma_mask = &dm644x_venc_dma_mask, >+ .coherent_dma_mask = DMA_BIT_MASK(32), >+ .platform_data = (void *)&dm644x_venc_pdata, >+ }, >+}; >+ >+static u64 dm644x_vpbe_dma_mask = DMA_BIT_MASK(32); >+ >+static struct platform_device dm644x_vpbe_dev = { >+ .name = "vpbe_controller", >+ .id = -1, >+ .dev = { >+ .dma_mask = &dm644x_vpbe_dma_mask, >+ .coherent_dma_mask = DMA_BIT_MASK(32), >+ }, >+}; >+ >+void dm644x_set_vpbe_display_config(struct vpbe_display_config *cfg) >+{ >+ dm644x_vpbe_dev.dev.platform_data = cfg; >+} >+ > /*----------------------------------------------------------------------*/ > > static struct map_desc dm644x_io_desc[] = { >@@ -767,20 +903,36 @@ void __init dm644x_init(void) > davinci_common_init(&davinci_soc_info_dm644x); > } > >+static struct platform_device *dm644x_video_devices[] __initdata = { >+ &dm644x_vpss_device, >+ &dm644x_ccdc_dev, >+ &vpfe_capture_dev, >+ &dm644x_osd_dev, >+ &dm644x_venc_dev, >+ &dm644x_vpbe_dev, >+ &vpbe_v4l2_display, >+}; >+ >+static int __init dm644x_init_video(void) >+{ >+ /* Add ccdc clock aliases */ >+ clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); >+ clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); >+ vpss_clkctl_reg = ioremap_nocache(VPSS_CLKCTL, 4); >+ platform_add_devices(dm644x_video_devices, >+ ARRAY_SIZE(dm644x_video_devices)); >+ return 0; >+} >+ > static int __init dm644x_init_devices(void) > { > if (!cpu_is_davinci_dm644x()) > return 0; > > /* Add ccdc clock aliases */ >- clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); >- clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); > platform_device_register(&dm644x_edma_device); > platform_device_register(&dm644x_emac_device); >- platform_device_register(&dm644x_vpss_device); >- platform_device_register(&dm644x_ccdc_dev); >- platform_device_register(&vpfe_capture_dev); >- >+ dm644x_init_video(); > return 0; > } > postcore_initcall(dm644x_init_devices); >diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach- >davinci/include/mach/dm644x.h >index 6fca568..bf7adcd 100644 >--- a/arch/arm/mach-davinci/include/mach/dm644x.h >+++ b/arch/arm/mach-davinci/include/mach/dm644x.h >@@ -26,6 +26,9 @@ > #include > #include > #include >+#include >+#include >+#include > > #define DM644X_EMAC_BASE (0x01C80000) > #define DM644X_EMAC_CNTRL_OFFSET (0x0000) >@@ -43,5 +46,6 @@ > void __init dm644x_init(void); > void __init dm644x_init_asp(struct snd_platform_data *pdata); > void dm644x_set_vpfe_config(struct vpfe_config *cfg); >+void dm644x_set_vpbe_display_config(struct vpbe_display_config *cfg); > > #endif /* __ASM_ARCH_DM644X_H */ >-- >1.6.2.4 From m-karicheri2 at ti.com Thu Dec 2 08:50:48 2010 From: m-karicheri2 at ti.com (Karicheri, Muralidharan) Date: Thu, 2 Dec 2010 08:50:48 -0600 Subject: [PATCH v3 6/6] davinci vpbe: Build infrastructure for VPBE driver In-Reply-To: <1291293583-2810-1-git-send-email-manjunath.hadli@ti.com> References: <1291293583-2810-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Murali Karicheri Software Design Engineer Texas Instruments Inc. Germantown, MD 20874 phone: 301-407-9583 email: m-karicheri2 at ti.com >-----Original Message----- >From: Hadli, Manjunath >Sent: Thursday, December 02, 2010 7:40 AM >To: LMML >Cc: dlos; Mauro Carvalho Chehab; Hans Verkuil; Hadli, Manjunath; Karicheri, >Muralidharan >Subject: [PATCH v3 6/6] davinci vpbe: Build infrastructure for VPBE driver > >This patch adds the build infra-structure for Davinci >VPBE dislay driver > >Signed-off-by: Manjunath Hadli >Signed-off-by: Muralidharan Karicheri >--- > drivers/media/video/davinci/Kconfig | 22 +++++ > drivers/media/video/davinci/Makefile | 2 + > .../media/video/davinci/davinci_vpbe_readme.txt | 100 >++++++++++++++++++++ > 3 files changed, 124 insertions(+), 0 deletions(-) > create mode 100644 drivers/media/video/davinci/davinci_vpbe_readme.txt > >diff --git a/drivers/media/video/davinci/Kconfig >b/drivers/media/video/davinci/Kconfig >index 6b19540..dab32d5 100644 >--- a/drivers/media/video/davinci/Kconfig >+++ b/drivers/media/video/davinci/Kconfig >@@ -91,3 +91,25 @@ 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" >+ 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" >+ select VIDEO_DM644X_VPBE >+ default y >+ help >+ Enables VPBE V4L2 Display driver on a DMXXX 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 >diff --git a/drivers/media/video/davinci/davinci_vpbe_readme.txt >b/drivers/media/video/davinci/davinci_vpbe_readme.txt >new file mode 100644 >index 0000000..e7aabba >--- /dev/null >+++ b/drivers/media/video/davinci/davinci_vpbe_readme.txt >@@ -0,0 +1,100 @@ >+ I think this doesn't belong to davinci folder. Consider moving this to Documentation folder where v4l2 documentations are available. >+ 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 following (in the same order as the list under file >+ partitioning):- >+ >+ 1. V4L2 display driver >+ Implements 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 sub device >+ 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 connetected 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 allowes >+ 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 ans setting of the >external >+ encoders is not present, and would be a part of the next patch series. >+ >+ 3. Venc sub device >+ Responsible for setting outputs provides 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 cab be maintained in the board specific setup file to >support >+ various LCD displays. >+ >+ 4. osd sub device >+ Osd sub device implements all osd layer management and hardware >specific >+ features. In the legacy drivers (LSPxxx), the hardware specific >features >+ are configured through proprietary IOCTLs at the fb device interface. >Since >+ sub devices are going to support device nodes, application will be >able >+ to configure the hardware feayture directly by opening the osd sub >device >+ node and by calling the related IOCTL. So these proprietary IOCTLs are >+ to be removed from the FB Device driver when doing up port of these >drivers to >+ mainline kernel. The V4L2 and FB device nodes supports only IOCTLS as >per >+ the associated spec. The rest of the IOCTLs are to be moved to osd and >+ venc sub devices. >+ >+ Current status:- >+ >+ A build tested version of vpbe controller is available. >+ >+ Following are TBDs. >+ >+ vpbe display controller >+ - review and modify the handling of 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. >+ >+ v4l2 driver >+ - A version is already developed which is to be cleaned up and unit >tested >+ >+ FB drivers >+ - Add support for fbdev drivers.- Ready and part of subsequent patches. >\ No newline at end of file >-- >1.6.2.4 From vm.rod25 at gmail.com Thu Dec 2 18:44:39 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Thu, 2 Dec 2010 18:44:39 -0600 Subject: [PATCH v9 6/6] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> <1291231949-23835-7-git-send-email-vm.rod25@gmail.com> Message-ID: On Thu, Dec 2, 2010 at 12:49 AM, Nori, Sekhar wrote: > On Thu, Dec 02, 2010 at 01:02:29, vm.rod25 at gmail.com wrote: >> +static int hawk_usb_ocic_notify(da8xx_ocic_handler_t handler) >> +{ >> + ? ? int irq ? ? ? ? = gpio_to_irq(DA850_USB1_OC_PIN); >> + ? ? int error ? ? ? = 0; >> + >> + ? ? if (handler != NULL) { >> + ? ? ? ? ? ? hawk_usb_ocic_handler = handler; >> + >> + ? ? ? ? ? ? error = request_irq(irq, omapl138_hawk_usb_ocic_irq, >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? IRQF_DISABLED | IRQF_TRIGGER_RISING | >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? IRQF_TRIGGER_FALLING, >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "OHCI over-current indicator", NULL); >> + ? ? ? ? ? ? if (error) >> + ? ? ? ? ? ? ? ? ? ? pr_err(KERN_ERR "%s: could not request IRQ to watch " >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? "over-current indicator changes\n", __func__); > > pr_err adds a KERN_ERR already. Changed to this static int hawk_usb_ocic_notify(da8xx_ocic_handler_t handler) { int irq = gpio_to_irq(DA850_USB1_OC_PIN); int error = 0; if (handler != NULL) { hawk_usb_ocic_handler = handler; error = request_irq(irq, omapl138_hawk_usb_ocic_irq, IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "OHCI over-current indicator", NULL); if (error) pr_err("%s: could not request IRQ to watch " "over-current indicator changes\n", __func__); } else { free_irq(irq, NULL); } return error; } >> +static __init void omapl138_hawk_usb_init(void) >> +{ >> + ? ? int ret; >> + ? ? u32 cfgchip2; >> + >> + ? ? ret = davinci_cfg_reg_list(da850_hawk_usb11_pins); >> + ? ? if (ret) { >> + ? ? ? ? ? ? pr_warning("%s: USB 1.1 PinMux setup failed: %d\n", >> + ? ? ? ? ? ? ? ? ? ? __func__, ret); >> + ? ? ? ? ? ? return; >> + ? ? } >> + >> + ? ? /* Setup the Ref. clock frequency for the HAWK at 24 MHz. */ >> + >> + ? ? cfgchip2 = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); >> + ? ? cfgchip2 &= ~CFGCHIP2_REFFREQ; >> + ? ? cfgchip2 |= ?CFGCHIP2_REFFREQ_24MHZ; >> + ? ? __raw_writel(cfgchip2, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); >> + >> + ? ? ret = gpio_request_one(DA850_USB1_VBUS_PIN, >> + ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_OUT, "USB1 VBUS"); >> + ? ? if (ret < 0) { >> + ? ? ? ? ? ? pr_err(KERN_ERR "%s: failed to request GPIO for USB 1.1 port " >> + ? ? ? ? ? ? ? ? ? ? "power control: %d\n", __func__, ret); >> + ? ? ? ? ? ? return; >> + ? ? } >> + >> + ? ? ret = gpio_request_one(DA850_USB1_OC_PIN, >> + ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "USB1 OC"); >> + ? ? if (ret < 0) { >> + ? ? ? ? ? ? pr_err(KERN_ERR "%s: failed to request GPIO for USB 1.1 port " >> + ? ? ? ? ? ? ? ? ? ? "over-current indicator: %d\n", __func__, ret); >> + ? ? ? ? ? ? return; >> + ? ? } > > Should free the gpio DA850_USB1_VBUS_PIN in this error > path. This is also valid for MMC/SD gpio acquisition in > patch 4/6. USB part Changed to this if (ret < 0) { pr_err("%s: failed to request GPIO for USB 1.1 port " "power control: %d\n", __func__, ret); gpio_free(DA850_USB1_VBUS_PIN); return; } ret = gpio_request_one(DA850_USB1_OC_PIN, GPIOF_DIR_IN, "USB1 OC"); if (ret < 0) { pr_err("%s: failed to request GPIO for USB 1.1 port " "over-current indicator: %d\n", __func__, ret); gpio_free(DA850_USB1_OC_PIN); return; } Patch 4/6 ret = gpio_request_one(DA850_HAWK_MMCSD_CD_PIN, GPIOF_DIR_IN, "MMC CD"); if (ret < 0) { pr_warning("%s: can not open GPIO %d\n", __func__, DA850_HAWK_MMCSD_CD_PIN); gpio_free(DA850_HAWK_MMCSD_CD_PIN); return; } ret = gpio_request_one(DA850_HAWK_MMCSD_WP_PIN, GPIOF_DIR_IN, "MMC WP"); if (ret < 0) { pr_warning("%s: can not open GPIO %d\n", __func__, DA850_HAWK_MMCSD_WP_PIN); gpio_free(DA850_HAWK_MMCSD_WP_PIN); return; } Is this ok ? or should I free the GPIO on the next section ? Same with USB ret = da8xx_register_mmcsd0(&da850_mmc_config); if (ret) pr_warning("%s: MMC/SD0 registration failed: %d\n", __func__, ret); Thanks for your comments Regards Victor Rodriguez > Thanks, > Sekhar > > From nsekhar at ti.com Fri Dec 3 03:01:22 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 3 Dec 2010 14:31:22 +0530 Subject: [PATCH v3 1/2] davinci: am18x/da850/omap-l138: add support for higher speed grades In-Reply-To: <4CF795AF.4070905@criticallink.com> References: <1291292822-20232-1-git-send-email-nsekhar@ti.com> <4CF795AF.4070905@criticallink.com> Message-ID: Hi Mike, On Thu, Dec 02, 2010 at 18:18:47, Michael Williamson wrote: > > +static const struct da850_opp da850_opp_456 = { > > + .freq = 456000, > > + .prediv = 1, > > + .mult = 19, > > + .postdiv = 1, > > + .cvdd_min = 1300000, > > + .cvdd_max = 1350000, > > +}; > > + > > +static const struct da850_opp da850_opp_408 = { > > + .freq = 408000, > > + .prediv = 1, > > + .mult = 17, > > + .postdiv = 1, > > + .cvdd_min = 1300000, > > + .cvdd_max = 1350000, > > +}; > > + > > +static const struct da850_opp da850_opp_372 = { > > + .freq = 372000, > > + .prediv = 1, > > + .mult = 31, > > + .postdiv = 2, > > + .cvdd_min = 1200000, > > + .cvdd_max = 1320000, > > +}; > > + > > > Table 6-4 of the OMAP-L138 (rev B) spec indicates a maximum PLLOUT of 600 MHz. Right. > PLLOUT is defined in the table as the PLL Output frequency, it's not clear > if that's before or after the post divider (seems like before given the block > diagram in Figure 6-9 and a minimum value of 300 MHz). Yup, PLLOUT is before the post divider. > > This OPP(372) results in a PLL of 744 MHz prior to the post divider. > > Is this a problem? Or am I reading the spec wrong? Good point, I had this question too. With the current spec, it is not possible to achieve any frequency between 300-400MHz on the ARM. However, 375 MHz parts are available. The values I used are according to the recommendations of the hardware team (around 8-9 months back) Since the spec is still in contradiction, I am checking internally on when the spec will be updated. Kevin, can you please keep this patch on hold till I re-confirm on what the final speced PLLOUT range will be and when the spec will be updated? Thanks, Sekhar From nsekhar at ti.com Fri Dec 3 03:14:22 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 3 Dec 2010 14:44:22 +0530 Subject: [PATCH v3 1/2] davinci: am18x/da850/omap-l138: add support for higher speed grades References: <1291292822-20232-1-git-send-email-nsekhar@ti.com> <4CF795AF.4070905@criticallink.com> Message-ID: On Fri, Dec 03, 2010 at 14:31:22, Nori, Sekhar wrote: > > PLLOUT is defined in the table as the PLL Output frequency, it's not clear > > if that's before or after the post divider (seems like before given the block > > diagram in Figure 6-9 and a minimum value of 300 MHz). > > Yup, PLLOUT is before the post divider. > > > > > This OPP(372) results in a PLL of 744 MHz prior to the post divider. > > > > Is this a problem? Or am I reading the spec wrong? > > Good point, I had this question too. With the current spec, it is not > possible to achieve any frequency between 300-400MHz on the ARM. However, > 375 MHz parts are available. Okay, the OMAP-L138 datasheet shows the range as 300-600MHz and AM1808 shows the range as 400-600MHz. Most likely the AM1808 spec will be updated and 372 MHz will be achieved using: 24 (crystal) / 2 (pre-div) * 31 (pllm) / 1 (post-div). I will confirm this with h/w folks and update the code (and comments). Thanks Mike for pointing out. Best Regards, Sekhar From nsekhar at ti.com Fri Dec 3 05:51:54 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 3 Dec 2010 17:21:54 +0530 Subject: [PATCH v9 6/6] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> <1291231949-23835-7-git-send-email-vm.rod25@gmail.com> Message-ID: On Fri, Dec 03, 2010 at 06:14:39, Victor Rodriguez wrote: > On Thu, Dec 2, 2010 at 12:49 AM, Nori, Sekhar wrote: > > On Thu, Dec 02, 2010 at 01:02:29, vm.rod25 at gmail.com wrote: > >> +static int hawk_usb_ocic_notify(da8xx_ocic_handler_t handler) > >> +{ > >> + int irq = gpio_to_irq(DA850_USB1_OC_PIN); > >> + int error = 0; > >> + > >> + if (handler != NULL) { > >> + hawk_usb_ocic_handler = handler; > >> + > >> + error = request_irq(irq, omapl138_hawk_usb_ocic_irq, > >> + IRQF_DISABLED | IRQF_TRIGGER_RISING | > >> + IRQF_TRIGGER_FALLING, > >> + "OHCI over-current indicator", NULL); > >> + if (error) > >> + pr_err(KERN_ERR "%s: could not request IRQ to watch " > >> + "over-current indicator changes\n", __func__); > > > > pr_err adds a KERN_ERR already. > > Changed to this > > static int hawk_usb_ocic_notify(da8xx_ocic_handler_t handler) > { > int irq = gpio_to_irq(DA850_USB1_OC_PIN); > int error = 0; > > if (handler != NULL) { > hawk_usb_ocic_handler = handler; > > error = request_irq(irq, omapl138_hawk_usb_ocic_irq, > IRQF_DISABLED | IRQF_TRIGGER_RISING | > IRQF_TRIGGER_FALLING, > "OHCI over-current indicator", NULL); > if (error) > pr_err("%s: could not request IRQ to watch " > "over-current indicator changes\n", __func__); Looks good. Thanks. The same error is present elsewhere in the patch and other patches too. Please fix those too. > > USB part > > Changed to this > > if (ret < 0) { > pr_err("%s: failed to request GPIO for USB 1.1 port " > "power control: %d\n", __func__, ret); > gpio_free(DA850_USB1_VBUS_PIN); > return; > } > > ret = gpio_request_one(DA850_USB1_OC_PIN, > GPIOF_DIR_IN, "USB1 OC"); > if (ret < 0) { > pr_err("%s: failed to request GPIO for USB 1.1 port " > "over-current indicator: %d\n", __func__, ret); > gpio_free(DA850_USB1_OC_PIN); > return; > } > > Patch 4/6 > > ret = gpio_request_one(DA850_HAWK_MMCSD_CD_PIN, > GPIOF_DIR_IN, "MMC CD"); > if (ret < 0) { > pr_warning("%s: can not open GPIO %d\n", > __func__, DA850_HAWK_MMCSD_CD_PIN); > gpio_free(DA850_HAWK_MMCSD_CD_PIN); > return; > } > > ret = gpio_request_one(DA850_HAWK_MMCSD_WP_PIN, > GPIOF_DIR_IN, "MMC WP"); > if (ret < 0) { > pr_warning("%s: can not open GPIO %d\n", > __func__, DA850_HAWK_MMCSD_WP_PIN); > gpio_free(DA850_HAWK_MMCSD_WP_PIN); > return; > } > > > Is this ok ? or should I free the GPIO on the next section ? Same with USB Not sure what you mean by "next section"? goto based error recovery is more commonly used and probably more preferred as it is easier to extend. You can look at function da850_evm_ui_expander_setup() of arch/arm/mach-davinci/board-da850-evm.c for an example. Thanks, Sekhar From tlzjhtlzjh at gmail.com Fri Dec 3 07:08:41 2010 From: tlzjhtlzjh at gmail.com (tlzjhtlzjh at gmail.com) Date: Fri, 3 Dec 2010 21:08:41 +0800 Subject: help : DM365 JPEG decoder error 0x8800 Message-ID: hi, I have encoutered a problem when I am using DM365 jpeg decoder. I capture a number of 720x576 yuv422 images, encode them and then decode them. encode process has no problem, decode process return error sometimes, and most of the time the result is correct. the extendedError of outArgs is 0x8800, which indicate a bit stream error. I also try to save the jpeg file and display with photoshop and acdsee, there is no error. I have try to capture 768x576 yuv422 images, I have the same result. Here is my jpeg codec params : static const IMGDEC1_Params jpgdec_fix_params_default = { .size = sizeof(IMGDEC1_Params), .maxWidth = 720, .maxHeight = 576, .maxScans = 0, .forceChromaFormat = XDM_YUV_422ILE, .dataEndianness = XDM_BYTE, }; static const IMGDEC1_DynamicParams jpgdec_var_params_default = { .size = sizeof(IMGDEC1_DynamicParams), .numAU = 0, .decodeHeader = XDM_DECODE_AU, .displayWidth = 0, }; static const IMGENC1_Params jpgenc_fix_params_default = { .size = sizeof(IMGENC1_Params), .maxWidth = 720, .maxHeight = 576, .maxScans = XDM_DEFAULT, .dataEndianness = XDM_BYTE, .forceChromaFormat = XDM_YUV_422P, }; static const IMGENC1_DynamicParams jpgenc_var_params_default = { .size = sizeof(IMGENC1_DynamicParams), .numAU = XDM_DEFAULT, .inputChromaFormat = XDM_YUV_422ILE, .inputWidth = 720, .inputHeight = 576, .captureWidth = 720, .generateHeader = XDM_ENCODE_AU, .qValue = 65, }; thanks Frank -------------- next part -------------- An HTML attachment was scrubbed... URL: From ghosh.subhasish at gmail.com Fri Dec 3 09:11:47 2010 From: ghosh.subhasish at gmail.com (Subhasish Ghosh) Date: Fri, 3 Dec 2010 20:41:47 +0530 Subject: [PATCH 1/2] da850: Support for TI's PRU SoftUART Emulation Message-ID: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> The patch adds support for emulated UART controllers on the programmable realtime unit (PRU) available on OMAPL138. This defines the system resource requirements such as pin mux, clock, iomem, interrupt etc and registers the platform device as per the Linux driver model. Signed-off-by: Subhasish Ghosh --- arch/arm/mach-davinci/da850.c | 16 +++++ arch/arm/mach-davinci/devices-da8xx.c | 95 ++++++++++++++++++++++++++- arch/arm/mach-davinci/include/mach/da8xx.h | 1 + arch/arm/mach-davinci/include/mach/memory.h | 1 + include/linux/serial_core.h | 3 + 5 files changed, 115 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 63916b9..85508c2 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -238,6 +238,12 @@ static struct clk tptc2_clk = { .flags = ALWAYS_ENABLED, }; +static struct clk pru_clk = { + .name = "pruss", + .parent = &pll0_sysclk2, + .lpsc = DA8XX_LPSC0_DMAX, +}; + static struct clk uart0_clk = { .name = "uart0", .parent = &pll0_sysclk2, @@ -318,6 +324,14 @@ static struct clk mcasp_clk = { .flags = DA850_CLK_ASYNC3, }; +static struct clk mcasp_pru_clk = { + .name = "mcasp_pru", + .parent = &pll0_sysclk2, + .lpsc = DA8XX_LPSC1_McASP0, + .gpsc = 1, + .flags = DA850_CLK_ASYNC3, +}; + static struct clk lcdc_clk = { .name = "lcdc", .parent = &pll0_sysclk2, @@ -373,6 +387,7 @@ static struct clk_lookup da850_clks[] = { CLK(NULL, "tpcc1", &tpcc1_clk), CLK(NULL, "tptc2", &tptc2_clk), CLK(NULL, "uart0", &uart0_clk), + CLK(NULL, "pruss", &pru_clk), CLK(NULL, "uart1", &uart1_clk), CLK(NULL, "uart2", &uart2_clk), CLK(NULL, "aintc", &aintc_clk), @@ -381,6 +396,7 @@ static struct clk_lookup da850_clks[] = { CLK(NULL, "emif3", &emif3_clk), CLK(NULL, "arm", &arm_clk), CLK(NULL, "rmii", &rmii_clk), + CLK(NULL, "mcasp_pru", &mcasp_pru_clk), CLK("davinci_emac.1", NULL, &emac_clk), CLK("davinci-mcasp.0", NULL, &mcasp_clk), CLK("da8xx_lcdc.0", NULL, &lcdc_clk), diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index 9eec630..25de36d 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -85,7 +85,100 @@ struct platform_device da8xx_serial_device = { }, }; -static const s8 da8xx_queue_tc_mapping[][2] = { + +#define OMAPL138_PRU_MEM_BASE 0x01C30000 + +#define OMAPL138_INT_PRU_SUART_1 IRQ_DA8XX_EVTOUT0 +#define OMAPL138_INT_PRU_SUART_2 IRQ_DA8XX_EVTOUT1 +#define OMAPL138_INT_PRU_SUART_3 IRQ_DA8XX_EVTOUT2 +#define OMAPL138_INT_PRU_SUART_4 IRQ_DA8XX_EVTOUT3 +#define OMAPL138_INT_PRU_SUART_5 IRQ_DA8XX_EVTOUT4 +#define OMAPL138_INT_PRU_SUART_6 IRQ_DA8XX_EVTOUT5 +#define OMAPL138_INT_PRU_SUART_7 IRQ_DA8XX_EVTOUT6 +#define OMAPL138_INT_PRU_SUART_8 IRQ_DA8XX_EVTOUT7 + +static struct resource omapl138_pru_suart_resources[] = { + { + .name = "omapl_pru_suart", + .start = OMAPL138_PRU_MEM_BASE, + .end = OMAPL138_PRU_MEM_BASE + 0xFFFF, + .flags = IORESOURCE_MEM, + }, + { + .start = DAVINCI_DA8XX_MCASP0_REG_BASE, + .end = DAVINCI_DA8XX_MCASP0_REG_BASE + (SZ_1K * 12) - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = DA8XX_PSC0_BASE, + .end = DA8XX_PSC0_BASE + (SZ_1K * 3) - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = DA8XX_PSC1_BASE, + .end = DA8XX_PSC1_BASE + (SZ_1K * 3) - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = DA8XX_SHARED_RAM_BASE, + .end = DA8XX_SHARED_RAM_BASE + (SZ_1K * 8) - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = OMAPL138_INT_PRU_SUART_1, + .end = OMAPL138_INT_PRU_SUART_1, + .flags = IORESOURCE_IRQ, + }, + { + .start = OMAPL138_INT_PRU_SUART_2, + .end = OMAPL138_INT_PRU_SUART_2, + .flags = IORESOURCE_IRQ, + }, + { + .start = OMAPL138_INT_PRU_SUART_3, + .end = OMAPL138_INT_PRU_SUART_3, + .flags = IORESOURCE_IRQ, + }, + { + .start = OMAPL138_INT_PRU_SUART_4, + .end = OMAPL138_INT_PRU_SUART_4, + .flags = IORESOURCE_IRQ, + }, + { + .start = OMAPL138_INT_PRU_SUART_5, + .end = OMAPL138_INT_PRU_SUART_5, + .flags = IORESOURCE_IRQ, + }, + { + .start = OMAPL138_INT_PRU_SUART_6, + .end = OMAPL138_INT_PRU_SUART_6, + .flags = IORESOURCE_IRQ, + }, + { + .start = OMAPL138_INT_PRU_SUART_7, + .end = OMAPL138_INT_PRU_SUART_7, + .flags = IORESOURCE_IRQ, + }, + { + .start = OMAPL138_INT_PRU_SUART_8, + .end = OMAPL138_INT_PRU_SUART_8, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device omapl_pru_suart_device = { + .name = "davinci_pru_suart", + .id = -1, + .num_resources = ARRAY_SIZE(omapl138_pru_suart_resources), + .resource = omapl138_pru_suart_resources, +}; + +int __init da8xx_register_pru_suart(void) +{ + return platform_device_register(&omapl_pru_suart_device); +} + +static const s8 da8xx_queue_tc_mapping[][2] = { /* {event queue no, TC no} */ {0, 0}, {1, 1}, diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index 4247b3f..940dbd6 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -74,6 +74,7 @@ int da8xx_register_watchdog(void); int da8xx_register_usb20(unsigned mA, unsigned potpgt); int da8xx_register_usb11(struct da8xx_ohci_root_hub *pdata); int da8xx_register_emac(void); +int da8xx_register_pru_suart(void); int da8xx_register_lcdc(struct da8xx_lcdc_platform_data *pdata); int da8xx_register_mmcsd0(struct davinci_mmc_config *config); int da850_register_mmcsd1(struct davinci_mmc_config *config); diff --git a/arch/arm/mach-davinci/include/mach/memory.h b/arch/arm/mach-davinci/include/mach/memory.h index 22eb97c..d3e48d9 100644 --- a/arch/arm/mach-davinci/include/mach/memory.h +++ b/arch/arm/mach-davinci/include/mach/memory.h @@ -22,6 +22,7 @@ **************************************************************************/ #define DAVINCI_DDR_BASE 0x80000000 #define DA8XX_DDR_BASE 0xc0000000 +#define DA8XX_SHARED_RAM_BASE 0x80000000 #if defined(CONFIG_ARCH_DAVINCI_DA8XX) && defined(CONFIG_ARCH_DAVINCI_DMx) #error Cannot enable DaVinci and DA8XX platforms concurrently diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 212eb4c..407624e 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -199,6 +199,9 @@ /* TI OMAP-UART */ #define PORT_OMAP 96 +/* omapl pru uart emulation */ +#define PORT_OMAPL_PRU_SUART 97 + #ifdef __KERNEL__ #include -- 1.7.2.3 From ghosh.subhasish at gmail.com Fri Dec 3 09:11:48 2010 From: ghosh.subhasish at gmail.com (Subhasish Ghosh) Date: Fri, 3 Dec 2010 20:41:48 +0530 Subject: [PATCH 2/2] da850: board file modifications for PRU SUART. In-Reply-To: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> References: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1291389108-25356-2-git-send-email-subhasish@mistralsolutions.com> Signed-off-by: Subhasish Ghosh --- arch/arm/mach-davinci/board-da850-evm.c | 28 ++++++++++++++++++++++++++++ 1 files changed, 28 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index f89b0b7..86a89b1 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -736,6 +736,34 @@ static struct edma_rsv_info *da850_edma_rsv[2] = { &da850_edma_cc1_rsv, }; +const short da850_evm_pru_suart_pins[] = { + DA850_AHCLKX, DA850_ACLKX, DA850_AFSX, + DA850_AHCLKR, DA850_ACLKR, DA850_AFSR, + DA850_AXR_13, DA850_AXR_9, DA850_AXR_7, + DA850_AXR_14, DA850_AXR_10, DA850_AXR_8, + -1 +}; + +static int __init da850_evm_setup_pru_suart(void) +{ + int ret; + + if (!machine_is_davinci_da850_evm()) + return 0; + + ret = davinci_cfg_reg_list(da850_evm_pru_suart_pins); + if (ret) + pr_warning("%s: da850_evm_pru_suart_pins " + "mux setup failed: %d\n", __func__, ret); + ret = da8xx_register_pru_suart(); + if (ret) + pr_warning("%s: pru suart registration " + "failed: %d\n", __func__, ret); + return ret; +} + +device_initcall(da850_evm_setup_pru_suart); + static __init void da850_evm_init(void) { int ret; -- 1.7.2.3 From ghosh.subhasish at gmail.com Fri Dec 3 09:14:51 2010 From: ghosh.subhasish at gmail.com (Subhasish Ghosh) Date: Fri, 3 Dec 2010 07:14:51 -0800 (PST) Subject: [PATCH 1/2] da850: Support for TI's PRU SoftUART Emulation In-Reply-To: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> References: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1291389291247-5800168.post@n2.nabble.com> I am still receiving the alignment problems with CLK and da8xx_register_pru_suart, but the actual patch does not have them, would really appreciate any help. Not sure if this is any problem with the mail client. Attached the original patch file for reference. The CAN and UART patches are not incremental. Hence, some common Macros like the base address of the PRU is available in both places. As and when any one of the patch is accepted, I am planning to resubmit the other incrementally. This way both the patches are reviewed simultaneously. Please let me know if any better way to do this. http://davinci-linux-open-source.1494791.n2.nabble.com/file/n5800168/0001-da850-Support-for-TI-s-PRU-SoftUART-Emulation.patch 0001-da850-Support-for-TI-s-PRU-SoftUART-Emulation.patch -- View this message in context: http://davinci-linux-open-source.1494791.n2.nabble.com/PATCH-1-2-da850-Support-for-TI-s-PRU-SoftUART-Emulation-tp5800118p5800168.html Sent from the davinci-linux-open-source mailing list archive at Nabble.com. From vm.rod25 at gmail.com Fri Dec 3 11:10:44 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Fri, 3 Dec 2010 11:10:44 -0600 Subject: [PATCH v9 6/6] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> <1291231949-23835-7-git-send-email-vm.rod25@gmail.com> Message-ID: On Fri, Dec 3, 2010 at 5:51 AM, Nori, Sekhar wrote: > On Fri, Dec 03, 2010 at 06:14:39, Victor Rodriguez wrote: >> On Thu, Dec 2, 2010 at 12:49 AM, Nori, Sekhar wrote: >> > On Thu, Dec 02, 2010 at 01:02:29, vm.rod25 at gmail.com wrote: >> >> +static int hawk_usb_ocic_notify(da8xx_ocic_handler_t handler) >> >> +{ >> >> + ? ? int irq ? ? ? ? = gpio_to_irq(DA850_USB1_OC_PIN); >> >> + ? ? int error ? ? ? = 0; >> >> + >> >> + ? ? if (handler != NULL) { >> >> + ? ? ? ? ? ? hawk_usb_ocic_handler = handler; >> >> + >> >> + ? ? ? ? ? ? error = request_irq(irq, omapl138_hawk_usb_ocic_irq, >> >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? IRQF_DISABLED | IRQF_TRIGGER_RISING | >> >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? IRQF_TRIGGER_FALLING, >> >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "OHCI over-current indicator", NULL); >> >> + ? ? ? ? ? ? if (error) >> >> + ? ? ? ? ? ? ? ? ? ? pr_err(KERN_ERR "%s: could not request IRQ to watch " >> >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? "over-current indicator changes\n", __func__); >> > >> > pr_err adds a KERN_ERR already. >> >> Changed to this >> >> static int hawk_usb_ocic_notify(da8xx_ocic_handler_t handler) >> { >> ? ? ? int irq ? ? ? ? = gpio_to_irq(DA850_USB1_OC_PIN); >> ? ? ? int error ? ? ? = 0; >> >> ? ? ? if (handler != NULL) { >> ? ? ? ? ? ? ? hawk_usb_ocic_handler = handler; >> >> ? ? ? ? ? ? ? error = request_irq(irq, omapl138_hawk_usb_ocic_irq, >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? IRQF_DISABLED | IRQF_TRIGGER_RISING | >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? IRQF_TRIGGER_FALLING, >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "OHCI over-current indicator", NULL); >> ? ? ? ? ? ? ? if (error) >> ? ? ? ? ? ? ? ? ? ? ? pr_err("%s: could not request IRQ to watch " >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "over-current indicator changes\n", __func__); > > Looks good. Thanks. The same error is present elsewhere in the > patch and other patches too. Please fix those too. Ok thanks for check it, I am sending the changes in a patch format , any way I will resend the series with the correction if it is ok >> >> USB part >> >> Changed to this >> >> ? ? ? if (ret < 0) { >> ? ? ? ? ? ? ? pr_err("%s: failed to request GPIO for USB 1.1 port " >> ? ? ? ? ? ? ? ? ? ? ? "power control: %d\n", __func__, ret); >> ? ? ? ? ? ? ? gpio_free(DA850_USB1_VBUS_PIN); >> ? ? ? ? ? ? ? return; >> ? ? ? } >> >> ? ? ? ret = gpio_request_one(DA850_USB1_OC_PIN, >> ? ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "USB1 OC"); >> ? ? ? if (ret < 0) { >> ? ? ? ? ? ? ? pr_err("%s: failed to request GPIO for USB 1.1 port " >> ? ? ? ? ? ? ? ? ? ? ? "over-current indicator: %d\n", __func__, ret); >> ? ? ? ? ? ? ? gpio_free(DA850_USB1_OC_PIN); >> ? ? ? ? ? ? ? return; >> ? ? ? } >> >> Patch 4/6 >> >> ? ? ? ret = gpio_request_one(DA850_HAWK_MMCSD_CD_PIN, >> ? ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "MMC CD"); >> ? ? ? if (ret < 0) { >> ? ? ? ? ? ? ? pr_warning("%s: can not open GPIO %d\n", >> ? ? ? ? ? ? ? ? ? ? ? __func__, DA850_HAWK_MMCSD_CD_PIN); >> ? ? ? ? ? ? ? gpio_free(DA850_HAWK_MMCSD_CD_PIN); >> ? ? ? ? ? ? ? return; >> ? ? ? } >> >> ? ? ? ret = gpio_request_one(DA850_HAWK_MMCSD_WP_PIN, >> ? ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "MMC WP"); >> ? ? ? if (ret < 0) { >> ? ? ? ? ? ? ? pr_warning("%s: can not open GPIO %d\n", >> ? ? ? ? ? ? ? ? ? ? ? __func__, DA850_HAWK_MMCSD_WP_PIN); >> ? ? ? ? ? ? ? gpio_free(DA850_HAWK_MMCSD_WP_PIN); >> ? ? ? ? ? ? ? return; >> ? ? ? } >> >> >> Is this ok ? or should I free the GPIO on the next section ? Same with USB > > Not sure what you mean by "next section"? goto based error recovery is > more commonly used and probably more preferred as it is easier to extend. I mean the next if ret = da8xx_register_usb11(&omapl138_hawk_usb11_pdata); if (ret) { pr_warning("%s: USB 1.1 registration failed: %d\n", __func__, ret); } > You can look at function da850_evm_ui_expander_setup() of > arch/arm/mach-davinci/board-da850-evm.c for an example. Thanks for the help I have free the two gpio on the last registration request error handler check it on the next patch --- arch/arm/mach-davinci/board-omapl138-hawk.c | 38 ++++++++++++++++++++------ 1 files changed, 29 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index da51136..8fc78f2 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -157,7 +157,7 @@ static __init void omapl138_hawk_mmc_init(void) if (ret < 0) { pr_warning("%s: can not open GPIO %d\n", __func__, DA850_HAWK_MMCSD_CD_PIN); - return; + goto exp_setup_cd_fail; } ret = gpio_request_one(DA850_HAWK_MMCSD_WP_PIN, @@ -165,13 +165,23 @@ static __init void omapl138_hawk_mmc_init(void) if (ret < 0) { pr_warning("%s: can not open GPIO %d\n", __func__, DA850_HAWK_MMCSD_WP_PIN); - return; + goto exp_setup_wp_fail; } ret = da8xx_register_mmcsd0(&da850_mmc_config); - if (ret) + if (ret) { pr_warning("%s: MMC/SD0 registration failed: %d\n", __func__, ret); + goto exp_setup_mmcsd_fail; + } + return; + +exp_setup_mmcsd_fail: + gpio_free(DA850_HAWK_MMCSD_WP_PIN); +exp_setup_wp_fail: + gpio_free(DA850_HAWK_MMCSD_CD_PIN); +exp_setup_cd_fail: + return; } static irqreturn_t omapl138_hawk_usb_ocic_irq(int irq, void *dev_id); @@ -211,7 +221,7 @@ static int hawk_usb_ocic_notify(da8xx_ocic_handler_t handler) IRQF_TRIGGER_FALLING, "OHCI over-current indicator", NULL); if (error) - pr_err(KERN_ERR "%s: could not request IRQ to watch " + pr_err("%s: could not request IRQ to watch " "over-current indicator changes\n", __func__); } else { free_irq(irq, NULL); @@ -256,23 +266,33 @@ static __init void omapl138_hawk_usb_init(void) ret = gpio_request_one(DA850_USB1_VBUS_PIN, GPIOF_DIR_OUT, "USB1 VBUS"); if (ret < 0) { - pr_err(KERN_ERR "%s: failed to request GPIO for USB 1.1 port " + pr_err("%s: failed to request GPIO for USB 1.1 port " "power control: %d\n", __func__, ret); - return; + goto exp_setup_vbus_fail; } ret = gpio_request_one(DA850_USB1_OC_PIN, GPIOF_DIR_IN, "USB1 OC"); if (ret < 0) { - pr_err(KERN_ERR "%s: failed to request GPIO for USB 1.1 port " + pr_err("%s: failed to request GPIO for USB 1.1 port " "over-current indicator: %d\n", __func__, ret); - return; + goto exp_setup_oc_fail; } ret = da8xx_register_usb11(&omapl138_hawk_usb11_pdata); - if (ret) + if (ret) { pr_warning("%s: USB 1.1 registration failed: %d\n", __func__, ret); + goto exp_setup_usb11_fail; + } + return; + +exp_setup_usb11_fail: + gpio_free(DA850_USB1_OC_PIN); +exp_setup_oc_fail: + gpio_free(DA850_USB1_VBUS_PIN); +exp_setup_vbus_fail: + return; } static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { -- 1.7.0.4 I have already compiled and tested. If it is ok for you I will resend the patches with the corrections Thanks a lot for your help Regards Victor Rodriguez > Thanks, > Sekhar > > From lamiaposta71 at gmail.com Sat Dec 4 23:32:29 2010 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Sun, 5 Dec 2010 06:32:29 +0100 Subject: IPIPE issue in DM365 In-Reply-To: <4C691E37.30301@einfochips.com> References: <4C68F454.6010700@einfochips.com> <4C691E37.30301@einfochips.com> Message-ID: Hi Albert, On Mon, Aug 16, 2010 at 1:17 PM, hitesh wrote: > Thanks Albert for your prompt response.. > > If my application want to allocate memory and I want to pass that memory > to IPIPE module then > what should be the parameter? > Do you have any sample application that only doing chroma conversion from > 422 to 420 without resizing of width and height? > My application getting kernel panic as below. > I need to do this because the PAL video in, YUV422 need to be converted to YUV420 before it can be compressed to H264 by HDVICP. I also need to integrate it in gstreamer. Can you point me to the right direction? Regards, Raffaele > > > Read from file done > hResizerHandle 0x2f6c0,srcOffset 0x8ba3e000,destOffset 0x8b87c000 > Again in Hell1 > Again in Hell22 > Unable to handle kernel NULL pointer dereference at virtual address > 00000000 > Resize operationpgd = c41f0000 > completed succe[00000000] *pgd=8770f031ssfully.Write to, *pte=00000000 > file done > cou, *ppte=00000000nt 13 > file read > > Read from fileInternal error: Oops: 817 [#1] > Modules linked in: dm365mmap edmak irqk cmemk > CPU: 0 > PC is at nfs_update_request+0x194/0x36c > LR is at radix_tree_node_alloc+0x24/0x5c > pc : [] lr : [] Not tainted > sp : c41dbc70 ip : c057c960 fp : c41dbcb4 > r10: c40f8a10 r9 : c40f8918 r8 : 00000000 > r7 : c40f89f0 r6 : c44863a0 r5 : ffffffef r4 : 00000000 > r3 : ffffffef r2 : 00000001 r1 : c33e7ee8 r0 : ffffffef > Flags: nZCv IRQs on FIQs on Mode SVC_32 Segment user > Control: 5317F > Table: 841F0000 DAC: 00000015 > Process resizer_test (pid: 830, stack limit = 0xc41da258) > Stack: (0xc41dbc70 to 0xc41dc000) > > -Hitesh > > > > > On 08/16/2010 03:47 PM, Albert Burbea wrote: > > Hi > seems strange to me that you use > convert.in_buff.index = 0; > convert.out_buff1.index = 0; > with the same index. > In the dvsdk for DaVinci (2.1 to the best of my knowledge) you should use > -1 for buffers that have not been allocated by the resizer itself. > I hope this did not confuse you > Albert > > On Mon, Aug 16, 2010 at 11:18 AM, hitesh wrote: > >> Hi All, >> >> I am facing memory leak issue in Chroma Conversion module of DM365 IPIPE. >> When I run the ioctl(resizer_fd, RSZ_RESIZE, &convert) ioctl, It increase >> my memory usage and it is gradually increasing it in >> every call of of ioctl. Do you have any solution? I want to do chroma >> conversion for encode my video with H264 encoder. >> >> My configuration for IPIPE is as below. >> INPUT_WIDTH =1280 >> INPUT_HEIGHT =720 >> >> rsz_ss_config.input.image_width = INPUT_WIDTH; >> rsz_ss_config.input.image_height = INPUT_HEIGHT; >> rsz_ss_config.input.ppln = rsz_ss_config.input.image_width + 8; >> rsz_ss_config.input.lpfr = rsz_ss_config.input.image_height + 10; >> rsz_ss_config.input.pix_fmt = IPIPE_UYVY; >> rsz_ss_config.output1.pix_fmt = IPIPE_YUV420SP; >> rsz_ss_config.output1.enable = 1; >> rsz_ss_config.output1.width = INPUT_WIDTH; >> rsz_ss_config.output1.height = INPUT_HEIGHT; >> rsz_ss_config.output2.enable = 0; >> >> rsz_chan_config.oper_mode = IMP_MODE_SINGLE_SHOT; >> rsz_chan_config.chain = 0; >> >> >> I am calling ioctl as below. >> >> convert.in_buff.buf_type = IMP_BUF_IN; >> convert.in_buff.index = 0; >> convert.in_buff.offset = buf_in[0].offset; >> convert.in_buff.size = buf_in[0].size; >> convert.out_buff1.buf_type = IMP_BUF_OUT1; >> convert.out_buff1.index = 0; >> convert.out_buff1.offset = buf_out1[0].offset; >> convert.out_buff1.size = buf_out1[0].size; >> if (ioctl(resizer_fd, RSZ_RESIZE, &convert) < 0) { >> perror("Error in doing preview\n"); >> munmap(input_buffer, buf_in[0].size); >> munmap(output_buffer, buf_out1[0].size); >> close(resizer_fd); >> fclose(inp_f); >> fclose(outp_f); >> exit(1); >> } >> >> >> >> Thanks >> Hitesh >> >> >> _______________________________________________ >> Davinci-linux-open-source mailing list >> Davinci-linux-open-source at linux.davincidsp.com >> http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source >> > > > > -- > Albert Burbea > Harishonim 8 > Ramat Gan 52502, Israel > Tel/Fax + 972-3-7526016 > Mobile: +972-52-3541842 > > > *Email Scanned for Virus & Dangerous Content by :* * > www.CleanMailGateway.com* > > > > _______________________________________________ > Davinci-linux-open-source mailing list > Davinci-linux-open-source at linux.davincidsp.com > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source > > -- www.opensurf.it -------------- next part -------------- An HTML attachment was scrubbed... URL: From tlzjhtlzjh at gmail.com Sun Dec 5 01:11:08 2010 From: tlzjhtlzjh at gmail.com (tlzjhtlzjh at gmail.com) Date: Sun, 5 Dec 2010 15:11:08 +0800 Subject: help : DM365 JPEG decoder error 0x8800 In-Reply-To: References: Message-ID: hi, everyone, I have resolved the problem, if I set the input buffer size of jpeg file length, the decoder will fail, so I add the input buffer size with 1024, the decoder will success then. it's, inbuf.descs[0].bufSize = jpg_len + 1024; But I don't know what's wrong with it. Best regards Frank -------------- next part -------------- An HTML attachment was scrubbed... URL: From lamiaposta71 at gmail.com Sun Dec 5 02:18:42 2010 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Sun, 5 Dec 2010 09:18:42 +0100 Subject: help : DM365 JPEG decoder error 0x8800 In-Reply-To: References: Message-ID: On Sun, Dec 5, 2010 at 8:11 AM, wrote: > hi, everyone, > I have resolved the problem, > if I set the input buffer size of jpeg file length, the decoder will fail, > so I add the input buffer size with 1024, the decoder will success then. > it's, > inbuf.descs[0].bufSize = jpg_len + 1024; > But I don't know what's wrong with it. > Best regards > Frank > _______________________________________________ > Davinci-linux-open-source mailing list > Davinci-linux-open-source at linux.davincidsp.com > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source Thank you for having posted also the solution. I'll work on encoding, but your information can be useful also for me! Bye, Raffaele From albertbu at gmail.com Sun Dec 5 02:57:50 2010 From: albertbu at gmail.com (Albert Burbea) Date: Sun, 5 Dec 2010 10:57:50 +0200 Subject: using SOC audio encoder Message-ID: Hi everybody I need an example about operating the audio encoder/decoder on the dm365. Also, how do Iuse it at the same time with the AIC33? Is it possble ? Thanks in advance Albert -------------- next part -------------- An HTML attachment was scrubbed... URL: From sshtylyov at mvista.com Sun Dec 5 07:56:10 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Sun, 05 Dec 2010 16:56:10 +0300 Subject: [PATCH 1/2] da850: Support for TI's PRU SoftUART Emulation In-Reply-To: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> References: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <4CFB99FA.10302@mvista.com> Hello. On 03-12-2010 18:11, Subhasish Ghosh wrote: > The patch adds support for emulated UART controllers > on the programmable realtime unit (PRU) available on OMAPL138. > This defines the system resource requirements such as pin mux, > clock, iomem, interrupt etc and registers the platform device > as per the Linux driver model. > Signed-off-by: Subhasish Ghosh [...] > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 63916b9..85508c2 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c [...] > +struct platform_device omapl_pru_suart_device = { > + .name = "davinci_pru_suart", > + .id = -1, > + .num_resources = ARRAY_SIZE(omapl138_pru_suart_resources), > + .resource = omapl138_pru_suart_resources, Please align the initilaizers. WBR, Sergei From nsekhar at ti.com Mon Dec 6 05:12:10 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 6 Dec 2010 16:42:10 +0530 Subject: [PATCH v9 6/6] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> <1291231949-23835-7-git-send-email-vm.rod25@gmail.com> Message-ID: On Fri, Dec 03, 2010 at 22:40:44, Victor Rodriguez wrote: > arch/arm/mach-davinci/board-omapl138-hawk.c | 38 ++++++++++++++++++++------ > 1 files changed, 29 insertions(+), 9 deletions(-) > > diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c > b/arch/arm/mach-davinci/board-omapl138-hawk.c > index da51136..8fc78f2 100644 > --- a/arch/arm/mach-davinci/board-omapl138-hawk.c > +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c > @@ -157,7 +157,7 @@ static __init void omapl138_hawk_mmc_init(void) > if (ret < 0) { > pr_warning("%s: can not open GPIO %d\n", > __func__, DA850_HAWK_MMCSD_CD_PIN); > - return; > + goto exp_setup_cd_fail; > } > > ret = gpio_request_one(DA850_HAWK_MMCSD_WP_PIN, > @@ -165,13 +165,23 @@ static __init void omapl138_hawk_mmc_init(void) > if (ret < 0) { > pr_warning("%s: can not open GPIO %d\n", > __func__, DA850_HAWK_MMCSD_WP_PIN); > - return; > + goto exp_setup_wp_fail; > } > > ret = da8xx_register_mmcsd0(&da850_mmc_config); > - if (ret) > + if (ret) { > pr_warning("%s: MMC/SD0 registration failed: %d\n", > __func__, ret); > + goto exp_setup_mmcsd_fail; > + } > + return; This return has extra indentation. > + > +exp_setup_mmcsd_fail: > + gpio_free(DA850_HAWK_MMCSD_WP_PIN); > +exp_setup_wp_fail: > + gpio_free(DA850_HAWK_MMCSD_CD_PIN); > +exp_setup_cd_fail: > + return; This one too. Other than that, it all looks good to me. Thanks, Sekhar From sshtylyov at mvista.com Mon Dec 6 05:43:18 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Mon, 06 Dec 2010 14:43:18 +0300 Subject: [PATCH v9 6/6] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> <1291231949-23835-7-git-send-email-vm.rod25@gmail.com> Message-ID: <4CFCCC56.3010700@mvista.com> Hello. On 06-12-2010 14:12, Nori, Sekhar wrote: >> arch/arm/mach-davinci/board-omapl138-hawk.c | 38 ++++++++++++++++++++------ >> 1 files changed, 29 insertions(+), 9 deletions(-) >> diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c >> b/arch/arm/mach-davinci/board-omapl138-hawk.c >> index da51136..8fc78f2 100644 >> --- a/arch/arm/mach-davinci/board-omapl138-hawk.c >> +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c [...] >> @@ -165,13 +165,23 @@ static __init void omapl138_hawk_mmc_init(void) >> if (ret< 0) { >> pr_warning("%s: can not open GPIO %d\n", >> __func__, DA850_HAWK_MMCSD_WP_PIN); >> - return; >> + goto exp_setup_wp_fail; >> } >> >> ret = da8xx_register_mmcsd0(&da850_mmc_config); >> - if (ret) >> + if (ret) { >> pr_warning("%s: MMC/SD0 registration failed: %d\n", >> __func__, ret); >> + goto exp_setup_mmcsd_fail; >> + } >> + return; > This return has extra indentation. >> + >> +exp_setup_mmcsd_fail: >> + gpio_free(DA850_HAWK_MMCSD_WP_PIN); >> +exp_setup_wp_fail: >> + gpio_free(DA850_HAWK_MMCSD_CD_PIN); >> +exp_setup_cd_fail: >> + return; > This one too. Moreover, it's not needed at all. > Other than that, it all looks good to me. Except those 'exp_setup_'prefixes which I'm not sure where are coming from... > Thanks, > Sekhar WBR, Sergei From hitesh.patel at einfochips.com Mon Dec 6 07:30:38 2010 From: hitesh.patel at einfochips.com (hitesh) Date: Mon, 06 Dec 2010 19:00:38 +0530 Subject: IPIPE issue in DM365 In-Reply-To: References: <4C68F454.6010700@einfochips.com> <4C691E37.30301@einfochips.com> Message-ID: <4CFCE57E.6030007@einfochips.com> Hi, You have to allocate CMEM memory in your application and take physical address of that memry and pass that physical address in IPIPE argument. Best Regards, Hitesh Patel Embedded Engineer e-Infochips Pvt. Ltd. Cubic Number - (KB-320) (O) +91 79 2656-3705/4971/0265 Extn. - 242 www.einfochips.com On 12/05/2010 11:02 AM, Raffaele Recalcati wrote: > Hi Albert, > > On Mon, Aug 16, 2010 at 1:17 PM, hitesh > wrote: > > Thanks Albert for your prompt response.. > > If my application want to allocate memory and I want to pass that > memory to IPIPE module then > what should be the parameter? > Do you have any sample application that only doing chroma > conversion from 422 to 420 without resizing of width and height? > My application getting kernel panic as below. > > > I need to do this because the PAL video in, YUV422 need to be > converted to YUV420 before it can be compressed to H264 by HDVICP. > I also need to integrate it in gstreamer. > Can you point me to the right direction? > > Regards, > Raffaele > > > > Read from file done > hResizerHandle 0x2f6c0,srcOffset 0x8ba3e000,destOffset 0x8b87c000 > Again in Hell1 > Again in Hell22 > Unable to handle kernel NULL pointer dereference at virtual > address 00000000 > Resize operationpgd = c41f0000 > completed succe[00000000] *pgd=8770f031ssfully.Write to, > *pte=00000000 file done > cou, *ppte=00000000nt 13 > file read > > Read from fileInternal error: Oops: 817 [#1] > Modules linked in: dm365mmap edmak irqk cmemk > CPU: 0 > PC is at nfs_update_request+0x194/0x36c > LR is at radix_tree_node_alloc+0x24/0x5c > pc : [] lr : [] Not tainted > sp : c41dbc70 ip : c057c960 fp : c41dbcb4 > r10: c40f8a10 r9 : c40f8918 r8 : 00000000 > r7 : c40f89f0 r6 : c44863a0 r5 : ffffffef r4 : 00000000 > r3 : ffffffef r2 : 00000001 r1 : c33e7ee8 r0 : ffffffef > Flags: nZCv IRQs on FIQs on Mode SVC_32 Segment user > Control: 5317F > Table: 841F0000 DAC: 00000015 > Process resizer_test (pid: 830, stack limit = 0xc41da258) > Stack: (0xc41dbc70 to 0xc41dc000) > > -Hitesh > > > > > On 08/16/2010 03:47 PM, Albert Burbea wrote: >> Hi >> seems strange to me that you use >> convert.in_buff.index = 0; >> convert.out_buff1.index = 0; >> with the same index. >> In the dvsdk for DaVinci (2.1 to the best of my knowledge) you >> should use -1 for buffers that have not been allocated by the >> resizer itself. >> I hope this did not confuse you >> Albert >> >> On Mon, Aug 16, 2010 at 11:18 AM, hitesh >> > > wrote: >> >> Hi All, >> >> I am facing memory leak issue in Chroma Conversion module of >> DM365 IPIPE. >> When I run the ioctl(resizer_fd, RSZ_RESIZE, &convert) ioctl, >> It increase my memory usage and it is gradually increasing it in >> every call of of ioctl. Do you have any solution? I want to >> do chroma conversion for encode my video with H264 encoder. >> >> My configuration for IPIPE is as below. >> INPUT_WIDTH =1280 >> INPUT_HEIGHT =720 >> >> rsz_ss_config.input.image_width = INPUT_WIDTH; >> rsz_ss_config.input.image_height = INPUT_HEIGHT; >> rsz_ss_config.input.ppln = >> rsz_ss_config.input.image_width + 8; >> rsz_ss_config.input.lpfr = >> rsz_ss_config.input.image_height + 10; >> rsz_ss_config.input.pix_fmt = IPIPE_UYVY; >> rsz_ss_config.output1.pix_fmt = IPIPE_YUV420SP; >> rsz_ss_config.output1.enable = 1; >> rsz_ss_config.output1.width = INPUT_WIDTH; >> rsz_ss_config.output1.height = INPUT_HEIGHT; >> rsz_ss_config.output2.enable = 0; >> >> rsz_chan_config.oper_mode = IMP_MODE_SINGLE_SHOT; >> rsz_chan_config.chain = 0; >> >> >> I am calling ioctl as below. >> >> convert.in_buff.buf_type = IMP_BUF_IN; >> convert.in_buff.index = 0; >> convert.in_buff.offset = buf_in[0].offset; >> convert.in_buff.size = buf_in[0].size; >> convert.out_buff1.buf_type = IMP_BUF_OUT1; >> convert.out_buff1.index = 0; >> convert.out_buff1.offset = buf_out1[0].offset; >> convert.out_buff1.size = buf_out1[0].size; >> if (ioctl(resizer_fd, RSZ_RESIZE, &convert) < 0) { >> perror("Error in doing preview\n"); >> munmap(input_buffer, buf_in[0].size); >> munmap(output_buffer, buf_out1[0].size); >> close(resizer_fd); >> fclose(inp_f); >> fclose(outp_f); >> exit(1); >> } >> >> >> >> Thanks >> Hitesh >> >> >> _______________________________________________ >> Davinci-linux-open-source mailing list >> Davinci-linux-open-source at linux.davincidsp.com >> >> http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source >> >> >> >> >> -- >> Albert Burbea >> Harishonim 8 >> Ramat Gan 52502, Israel >> Tel/Fax + 972-3-7526016 >> Mobile: +972-52-3541842 >> >> >> *Email Scanned for Virus & Dangerous Content by >> :**www.CleanMailGateway.com * >> > > > _______________________________________________ > Davinci-linux-open-source mailing list > Davinci-linux-open-source at linux.davincidsp.com > > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source > > > > > -- > www.opensurf.it > > > > *Email Scanned for Virus & Dangerous Content by > :**www.CleanMailGateway.com* > -- -------------- next part -------------- An HTML attachment was scrubbed... URL: From vm.rod25 at gmail.com Mon Dec 6 09:47:22 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Mon, 6 Dec 2010 09:47:22 -0600 Subject: [PATCH v9 6/6] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: <4CFCCC56.3010700@mvista.com> References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> <1291231949-23835-7-git-send-email-vm.rod25@gmail.com> <4CFCCC56.3010700@mvista.com> Message-ID: On Mon, Dec 6, 2010 at 5:43 AM, Sergei Shtylyov wrote: > Hello. > > On 06-12-2010 14:12, Nori, Sekhar wrote: > >>> ?arch/arm/mach-davinci/board-omapl138-hawk.c | ? 38 >>> ++++++++++++++++++++------ >>> ?1 files changed, 29 insertions(+), 9 deletions(-) > >>> diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c >>> b/arch/arm/mach-davinci/board-omapl138-hawk.c >>> index da51136..8fc78f2 100644 >>> --- a/arch/arm/mach-davinci/board-omapl138-hawk.c >>> +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c > > [...] >>> >>> @@ -165,13 +165,23 @@ static __init void omapl138_hawk_mmc_init(void) >>> ? ? ? if (ret< ?0) { >>> ? ? ? ? ? ? ? pr_warning("%s: can not open GPIO %d\n", >>> ? ? ? ? ? ? ? ? ? ? ? __func__, DA850_HAWK_MMCSD_WP_PIN); >>> - ? ? ? ? ? ? return; >>> + ? ? ? ? ? ? goto exp_setup_wp_fail; >>> ? ? ? } >>> >>> ? ? ? ret = da8xx_register_mmcsd0(&da850_mmc_config); >>> - ? ? if (ret) >>> + ? ? if (ret) { >>> ? ? ? ? ? ? ? pr_warning("%s: MMC/SD0 registration failed: %d\n", >>> ? ? ? ? ? ? ? ? ? ? ? __func__, ret); >>> + ? ? ? ? ? ? goto exp_setup_mmcsd_fail; >>> + ? ? } >>> + ? ? ? ? ? ? return; > >> This return has extra indentation. > >>> + >>> +exp_setup_mmcsd_fail: >>> + ? ? gpio_free(DA850_HAWK_MMCSD_WP_PIN); >>> +exp_setup_wp_fail: >>> + ? ? gpio_free(DA850_HAWK_MMCSD_CD_PIN); >>> +exp_setup_cd_fail: >>> + ? ? ? ? ? ? return; > >> This one too. > > ? Moreover, it's not needed at all. Ok >> Other than that, it all looks good to me. > > ? Except those 'exp_setup_'prefixes which I'm not sure where are coming > from... it comes from arch/arm/mach-davinci/board-da850-evm.c I took this as a template and I think that is better to keep this exp_setup_ as a template but if you have any other suggestion please tell me Thanks for check it helps me a lot Regards Victor Rodriguez >> Thanks, >> Sekhar > > WBR, Sergei > From bengardiner at nanometrics.ca Mon Dec 6 09:53:50 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Mon, 6 Dec 2010 10:53:50 -0500 Subject: [PATCH v9 6/6] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> <1291231949-23835-7-git-send-email-vm.rod25@gmail.com> <4CFCCC56.3010700@mvista.com> Message-ID: On Mon, Dec 6, 2010 at 10:47 AM, Victor Rodriguez wrote: > On Mon, Dec 6, 2010 at 5:43 AM, Sergei Shtylyov wrote: >> Hello. >> >> On 06-12-2010 14:12, Nori, Sekhar wrote: >> >>>> ?arch/arm/mach-davinci/board-omapl138-hawk.c | ? 38 >>>> ++++++++++++++++++++------ >>>> ?1 files changed, 29 insertions(+), 9 deletions(-) >> >>>> diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c >>>> b/arch/arm/mach-davinci/board-omapl138-hawk.c >>>> index da51136..8fc78f2 100644 >>>> --- a/arch/arm/mach-davinci/board-omapl138-hawk.c >>>> +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c >> >> [...] >>>> >>>> @@ -165,13 +165,23 @@ static __init void omapl138_hawk_mmc_init(void) >>>> ? ? ? if (ret< ?0) { >>>> ? ? ? ? ? ? ? pr_warning("%s: can not open GPIO %d\n", >>>> ? ? ? ? ? ? ? ? ? ? ? __func__, DA850_HAWK_MMCSD_WP_PIN); >>>> - ? ? ? ? ? ? return; >>>> + ? ? ? ? ? ? goto exp_setup_wp_fail; >>>> ? ? ? } >>>> >>>> ? ? ? ret = da8xx_register_mmcsd0(&da850_mmc_config); >>>> - ? ? if (ret) >>>> + ? ? if (ret) { >>>> ? ? ? ? ? ? ? pr_warning("%s: MMC/SD0 registration failed: %d\n", >>>> ? ? ? ? ? ? ? ? ? ? ? __func__, ret); >>>> + ? ? ? ? ? ? goto exp_setup_mmcsd_fail; >>>> + ? ? } >>>> + ? ? ? ? ? ? return; >> >>> This return has extra indentation. >> >>>> + >>>> +exp_setup_mmcsd_fail: >>>> + ? ? gpio_free(DA850_HAWK_MMCSD_WP_PIN); >>>> +exp_setup_wp_fail: >>>> + ? ? gpio_free(DA850_HAWK_MMCSD_CD_PIN); >>>> +exp_setup_cd_fail: >>>> + ? ? ? ? ? ? return; >> >>> This one too. >> >> ? Moreover, it's not needed at all. > > Ok >>> Other than that, it all looks good to me. >> >> ? Except those 'exp_setup_'prefixes which I'm not sure where are coming >> from... > > > it comes from > > arch/arm/mach-davinci/board-da850-evm.c > > I took this as a template and I think that is better to keep this > exp_setup_ ?as a template > > but if you have any other suggestion please tell me I think those are based on the error handling in da850_evm_ui_expander_setup() which means that the 'exp' in 'exp_setup_' stands-for IO-expander. Based on the function names where the your exp_setup_* labels were introduced I think that the label could be renamed 'mmc_setup_*' and 'usb11_setup_*'. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From vm.rod25 at gmail.com Mon Dec 6 09:56:17 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Mon, 6 Dec 2010 09:56:17 -0600 Subject: [PATCH v9 6/6] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> <1291231949-23835-7-git-send-email-vm.rod25@gmail.com> <4CFCCC56.3010700@mvista.com> Message-ID: On Mon, Dec 6, 2010 at 9:53 AM, Ben Gardiner wrote: > On Mon, Dec 6, 2010 at 10:47 AM, Victor Rodriguez wrote: >> On Mon, Dec 6, 2010 at 5:43 AM, Sergei Shtylyov wrote: >>> Hello. >>> >>> On 06-12-2010 14:12, Nori, Sekhar wrote: >>> >>>>> ?arch/arm/mach-davinci/board-omapl138-hawk.c | ? 38 >>>>> ++++++++++++++++++++------ >>>>> ?1 files changed, 29 insertions(+), 9 deletions(-) >>> >>>>> diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c >>>>> b/arch/arm/mach-davinci/board-omapl138-hawk.c >>>>> index da51136..8fc78f2 100644 >>>>> --- a/arch/arm/mach-davinci/board-omapl138-hawk.c >>>>> +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c >>> >>> [...] >>>>> >>>>> @@ -165,13 +165,23 @@ static __init void omapl138_hawk_mmc_init(void) >>>>> ? ? ? if (ret< ?0) { >>>>> ? ? ? ? ? ? ? pr_warning("%s: can not open GPIO %d\n", >>>>> ? ? ? ? ? ? ? ? ? ? ? __func__, DA850_HAWK_MMCSD_WP_PIN); >>>>> - ? ? ? ? ? ? return; >>>>> + ? ? ? ? ? ? goto exp_setup_wp_fail; >>>>> ? ? ? } >>>>> >>>>> ? ? ? ret = da8xx_register_mmcsd0(&da850_mmc_config); >>>>> - ? ? if (ret) >>>>> + ? ? if (ret) { >>>>> ? ? ? ? ? ? ? pr_warning("%s: MMC/SD0 registration failed: %d\n", >>>>> ? ? ? ? ? ? ? ? ? ? ? __func__, ret); >>>>> + ? ? ? ? ? ? goto exp_setup_mmcsd_fail; >>>>> + ? ? } >>>>> + ? ? ? ? ? ? return; >>> >>>> This return has extra indentation. >>> >>>>> + >>>>> +exp_setup_mmcsd_fail: >>>>> + ? ? gpio_free(DA850_HAWK_MMCSD_WP_PIN); >>>>> +exp_setup_wp_fail: >>>>> + ? ? gpio_free(DA850_HAWK_MMCSD_CD_PIN); >>>>> +exp_setup_cd_fail: >>>>> + ? ? ? ? ? ? return; >>> >>>> This one too. >>> >>> ? Moreover, it's not needed at all. >> >> Ok >>>> Other than that, it all looks good to me. >>> >>> ? Except those 'exp_setup_'prefixes which I'm not sure where are coming >>> from... >> >> >> it comes from >> >> arch/arm/mach-davinci/board-da850-evm.c >> >> I took this as a template and I think that is better to keep this >> exp_setup_ ?as a template >> >> but if you have any other suggestion please tell me > > I think those are based on the error handling in > da850_evm_ui_expander_setup() which means that the 'exp' in > 'exp_setup_' stands-for IO-expander. > > Based on the function names where the your exp_setup_* labels were > introduced I think that the label could be renamed 'mmc_setup_*' and > 'usb11_setup_*'. > I like it thanks I will submit the patches with that names. Sergei are you ok with this ? Regards Victor Rodriguez > Best Regards, > Ben Gardiner > > --- > Nanometrics Inc. > http://www.nanometrics.ca > From sshtylyov at mvista.com Mon Dec 6 11:48:08 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Mon, 06 Dec 2010 20:48:08 +0300 Subject: [PATCH v9 6/6] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: References: <1291231949-23835-1-git-send-email-vm.rod25@gmail.com> <1291231949-23835-7-git-send-email-vm.rod25@gmail.com> <4CFCCC56.3010700@mvista.com> Message-ID: <4CFD21D8.3040402@mvista.com> Hello. Victor Rodriguez wrote: >>>>>> arch/arm/mach-davinci/board-omapl138-hawk.c | 38 >>>>>> ++++++++++++++++++++------ >>>>>> 1 files changed, 29 insertions(+), 9 deletions(-) >>>>>> diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c >>>>>> b/arch/arm/mach-davinci/board-omapl138-hawk.c >>>>>> index da51136..8fc78f2 100644 >>>>>> --- a/arch/arm/mach-davinci/board-omapl138-hawk.c >>>>>> +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c >>>> [...] >>>>>> @@ -165,13 +165,23 @@ static __init void omapl138_hawk_mmc_init(void) >>>>>> if (ret< 0) { >>>>>> pr_warning("%s: can not open GPIO %d\n", >>>>>> __func__, DA850_HAWK_MMCSD_WP_PIN); >>>>>> - return; >>>>>> + goto exp_setup_wp_fail; >>>>>> } >>>>>> >>>>>> ret = da8xx_register_mmcsd0(&da850_mmc_config); >>>>>> - if (ret) >>>>>> + if (ret) { >>>>>> pr_warning("%s: MMC/SD0 registration failed: %d\n", >>>>>> __func__, ret); >>>>>> + goto exp_setup_mmcsd_fail; >>>>>> + } >>>>>> + return; >>>>> This return has extra indentation. >>>>>> + >>>>>> +exp_setup_mmcsd_fail: >>>>>> + gpio_free(DA850_HAWK_MMCSD_WP_PIN); >>>>>> +exp_setup_wp_fail: >>>>>> + gpio_free(DA850_HAWK_MMCSD_CD_PIN); >>>>>> +exp_setup_cd_fail: >>>>>> + return; >>>>> This one too. >>>> Moreover, it's not needed at all. >>> Ok ... except you can't probably put a label next to }. >>>>> Other than that, it all looks good to me. >>>> Except those 'exp_setup_'prefixes which I'm not sure where are coming >>>> from... >>> it comes from >>> arch/arm/mach-davinci/board-da850-evm.c >>> I took this as a template and I think that is better to keep this >>> exp_setup_ as a template >>> but if you have any other suggestion please tell me >> I think those are based on the error handling in >> da850_evm_ui_expander_setup() which means that the 'exp' in >> 'exp_setup_' stands-for IO-expander. >> Based on the function names where the your exp_setup_* labels were >> introduced I think that the label could be renamed 'mmc_setup_*' and >> 'usb11_setup_*'. > I like it thanks I will submit the patches with that names. Sergei are > you ok with this ? I'm not sure the prefixes are necessary at all, but won't have objection to those ones... > Regards > Victor Rodriguez WBR, Sergei From Wee-chang.Liew at leica-microsystems.com Mon Dec 6 14:03:10 2010 From: Wee-chang.Liew at leica-microsystems.com (Wee-chang.Liew at leica-microsystems.com) Date: Tue, 7 Dec 2010 04:03:10 +0800 Subject: AUTO: Wee-chang Liew is out of the office. Message-ID: I am out of the office from Tue 07.12.2010 until Tue 14.12.2010. I will respond to your message when I return. Note: This is an automated response to your message "Davinci-linux-open-source Digest, Vol 60, Issue 21" sent on 12/7/2010 2:00:00 AM. This is the only notification you will receive while this person is away. ______________________________________________________________________ This email has been scanned by the MessageLabs Email Security System. For more information please visit http://www.messagelabs.com/email ______________________________________________________________________ From hverkuil at xs4all.nl Mon Dec 6 14:21:36 2010 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Mon, 6 Dec 2010 21:21:36 +0100 Subject: [PATCH v3 1/6] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: <1291293485-2009-1-git-send-email-manjunath.hadli@ti.com> References: <1291293485-2009-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <201012062121.36287.hverkuil@xs4all.nl> A few minor comments: On Thursday, December 02, 2010 13:38:05 Manjunath Hadli wrote: > 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 is 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 > Signed-off-by: Muralidharan Karicheri > --- > drivers/media/video/davinci/vpbe_display.c | 2103 ++++++++++++++++++++++++++++ > include/media/davinci/vpbe_display.h | 146 ++ > include/media/davinci/vpbe_types.h | 93 ++ > 3 files changed, 2342 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..7a2d447 > --- /dev/null > +++ b/drivers/media/video/davinci/vpbe_display.c > +static void > +vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev, > + struct vpbe_display_obj *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; > + int h_scale = 0, v_scale = 0, h_exp = 0, v_exp = 0, temp; > + v4l2_std_id standard_id = vpbe_dev->current_timings.timings.std_id; Please add an empty line here for readability. > + /* > + * 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)) { Shouldn't this be '&' instead of '=='? Type v4l2_std_id is a bitmask, after all. It's a good idea to check other occurences of this. > + temp = (cfg->xsize * > + VPBE_DISPLAY_H_EXP_RATIO_N) / > + VPBE_DISPLAY_H_EXP_RATIO_D; > + if (temp <= expected_xsize) { > + h_exp = 1; > + cfg->xsize = temp; > + } > + } > + } > + 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)) { Should be '&'. > + temp = (cfg->ysize * > + VPBE_DISPLAY_V_EXP_RATIO_N) / > + VPBE_DISPLAY_V_EXP_RATIO_D; > + if (temp <= expected_ysize) { > + v_exp = 1; > + cfg->ysize = temp; > + } > + } > + } > + 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_display_obj *layer, > + int top, int left) > +{ > + struct osd_layer_config *cfg = &layer->layer_info.config; > + cfg->xpos = cfg->ypos = 0; > + if (left + cfg->xsize <= vpbe_dev->current_timings.xres) > + cfg->xpos = left; > + if (top + cfg->ysize <= vpbe_dev->current_timings.yres) > + cfg->ypos = top; > + > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, > + "new xpos = %d, ypos = %d\n", > + cfg->xpos, cfg->ypos); > +} > +static int vpbe_display_s_fmt(struct file *file, void *priv, > + struct v4l2_format *fmt) > +{ > + int ret = 0; > + struct vpbe_fh *fh = file->private_data; > + struct vpbe_display *disp_dev = video_drvdata(file); > + struct vpbe_display_obj *layer = fh->layer; > + struct osd_layer_config *cfg = &layer->layer_info.config; > + > + 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) { > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > + /* 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 */ The indentation of this line and the following lines seems to be off. Instead of having one huge 'if' block followed by a small 'else' block it is much better to handle the 'else' part first: if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n"); return -EINVAL; } Much more understandable. > + > + 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; > + > + /* Change of the default pixel format for both video windows */ > + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { > + struct vpbe_display_obj *otherlayer; > + cfg->pixfmt = PIXFMT_NV12; > + otherlayer = _vpbe_display_get_other_win(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); > + > + /* verify if readback values are as expected */ > + if (layer->pix_fmt.width != cfg->xsize || > + layer->pix_fmt.height != cfg->ysize || > + layer->pix_fmt.bytesperline != layer->layer_info. > + config.line_length || > + (cfg->interlaced > + && layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || > + (!cfg->interlaced && layer->pix_fmt.field > + != V4L2_FIELD_NONE)) { > + > + v4l2_err(&vpbe_dev->v4l2_dev, "mismatch:layer conf params:\n"); > + return -EINVAL; > + } > + > + } else { > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n"); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int vpbe_display_try_fmt(struct file *file, void *priv, > + struct v4l2_format *fmt) > +{ > + struct vpbe_display *disp_dev = video_drvdata(file); > + > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); > + > + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { > + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; > + /* Check for valid field format */ > + return vpbe_try_format(disp_dev, pixfmt, 0); > + } else { 'else' keyword is not needed since the 'if' will always return. > + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); > + return -EINVAL; > + } > + > + return 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_display_obj *layer = fh->layer; > + int ret = 0; > + > + 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; Shouldn't this be 'return ret;'? (or just do nothing actually). > + } > + } > + return ret; > +} > + > +/** > + * 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) > +{ > + 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) > +{ > + int ret = 0; > + > + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); > + > + /* Enumerate outputs */ > + > + if (NULL != vpbe_dev->ops.enum_outputs) { > + 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; Ditto. I actually see it more often in this code. > + } > + } > + return ret; > +} -- Hans Verkuil - video4linux developer - sponsored by Cisco From hverkuil at xs4all.nl Mon Dec 6 14:43:45 2010 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Mon, 6 Dec 2010 21:43:45 +0100 Subject: [PATCH v3 2/6] davinci vpbe: VPBE display driver In-Reply-To: <1291293516-2185-1-git-send-email-manjunath.hadli@ti.com> References: <1291293516-2185-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <201012062143.45910.hverkuil@xs4all.nl> Comments below... On Thursday, December 02, 2010 13:38:36 Manjunath Hadli wrote: > This patch implements the coe functionality of the dislay driver, > mainly controlling the VENC and other encoders, and acting as > the one point interface for the man V4L2 driver.This implements > the cre of each of the V4L2 IOCTLs. > > Signed-off-by: Manjunath Hadli > Signed-off-by: Muralidharan Karicheri > --- > drivers/media/video/davinci/vpbe.c | 847 ++++++++++++++++++++++++++++++++++++ > include/media/davinci/vpbe.h | 186 ++++++++ > 2 files changed, 1033 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..96c0eea > --- /dev/null > +++ b/drivers/media/video/davinci/vpbe.c > @@ -0,0 +1,847 @@ > +/* > + * 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; either version 2 of the License. > + * > + * 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 > + > + > +#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 struct osd_state *osd_device; > +static struct venc_platform_data *venc_device; > +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(ef_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_display_config *vpbe_config = vpbe_dev->cfg; > + int index = vpbe_dev->current_sd_index; > + return ((index == 0) ? &vpbe_config->venc : > + &vpbe_config->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_display_config *vpbe_config, > + int index) > +{ > + char *encoder_name = vpbe_config->outputs[index].subdev_name; > + int i; > + > + /* Venc is always first */ > + if (!strcmp(encoder_name, vpbe_config->venc.module_name)) > + return 0; > + > + for (i = 0; i < vpbe_config->num_ext_encoders; i++) { > + if (!strcmp(encoder_name, > + vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; > + int temp_index = output->index; > + > + if (temp_index >= vpbe_config->num_outputs) > + return -EINVAL; > + > + *output = vpbe_config->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_display_config *cfg = vpbe_dev->cfg; > + struct vpbe_enc_mode_info var; > + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; > + struct vpbe_enc_mode_info var; > + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; > + struct vpbe_enc_mode_info var; > + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; > + struct vpbe_enc_mode_info var; > + int curr_output = vpbe_dev->current_out_index, 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 vpbe_display_config *vpbe_config = vpbe_dev->cfg; > + struct encoder_config_info *curr_enc_info = > + vpbe_current_encoder_info(vpbe_dev); > + int ret = 0, enc_out_index = 0, sd_index; > + > + if (index >= vpbe_config->num_outputs) > + return -EINVAL; > + > + ret = mutex_lock_interruptible(&vpbe_dev->lock); > + if (ret) > + return ret; I am not sure about this mutex. Is it still needed now that the main driver is serialized via opslock? And if it is, does it make sense to use the _interruptible variant? That only makes sense if some of the critical sections can sleep or otherwise take a long time. And if mutex_lock_interruptible() is indeed valid, then you should return -ERESTARTSYS on error. This will cause the scheduler to handle the signal and call the system call again. That's probably what you want. > + > + sd_index = vpbe_dev->current_sd_index; > + enc_out_index = vpbe_config->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, > + vpbe_config->outputs[index].subdev_name)) { > + /* Need to switch the encoder at the output */ > + sd_index = vpbe_find_encoder_sd_index(vpbe_config, 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, > + vpbe_config->outputs[index].default_mode); > + if (!ret) { > + 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); > + } > + if (!ret) { Why not combine this in one 'if'? > + 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_display_config *vpbe_config = vpbe_dev->cfg; > + int i, ret = 0; > + > + for (i = 0; i < vpbe_config->num_outputs; i++) { > + if (!strcmp(def_output, > + vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; > + int sd_index = vpbe_dev->current_sd_index; > + int out_index = vpbe_dev->current_out_index, ret; > + > + > + if (!(vpbe_config->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; > + > + ret = mutex_lock_interruptible(&vpbe_dev->lock); > + if (ret) > + return ret; > + > + > + 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) { > + 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_display_config *vpbe_config = vpbe_dev->cfg; > + int out_index = vpbe_dev->current_out_index; > + struct vpbe_output *output = &vpbe_config->outputs[out_index]; > + int i, j = 0; > + > + 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_display_config *vpbe_config = vpbe_dev->cfg; > + int sd_index = vpbe_dev->current_sd_index, out_index = > + vpbe_dev->current_out_index, ret; > + > + if (!(vpbe_config->outputs[out_index].output.capabilities & > + V4L2_OUT_CAP_STD)) > + return -EINVAL; > + > + ret = vpbe_get_std_info(vpbe_dev, *std_id); > + if (ret) > + return ret; > + > + ret = mutex_lock_interruptible(&vpbe_dev->lock); > + if (ret) > + return ret; > + > + 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) { > + 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; Add empty line. I see this more often where the empty line separating the variable declarations from the code is missing. A quick scan through the code should fix this. > + 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_display_config *vpbe_config = vpbe_dev->cfg; > + int out_index = vpbe_dev->current_out_index, ret = 0, i; > + struct vpbe_enc_mode_info *preset_mode = NULL; > + struct v4l2_dv_preset dv_preset; > + > + if ((NULL == mode_info) || (NULL == mode_info->name)) > + return -EINVAL; > + > + for (i = 0; i < vpbe_config->outputs[out_index].num_modes; i++) { > + if (!strcmp(mode_info->name, > + vpbe_config->outputs[out_index].modes[i].name)) { > + preset_mode = &vpbe_config->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) { > + ret = vpbe_s_std(vpbe_dev, > + &preset_mode->timings.std_id); > + return ret; Why not just do 'return vpbe_s_std(...)'? Again, I see this construct a lot. > + } else if (preset_mode->timings_type & The 'else' keyword is not needed here. > + VPBE_ENC_DV_PRESET) { > + dv_preset.preset = > + preset_mode->timings.dv_preset; > + ret = vpbe_s_dv_preset(vpbe_dev, &dv_preset); > + return ret; > + } > + } > + } > + > + /* Only custom timing should reach here */ > + if (preset_mode == NULL) > + return -EINVAL; > + > + ret = mutex_lock_interruptible(&vpbe_dev->lock); > + if (ret) > + return ret; > + > + if (!ret) { > + 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); > + if (strcmp("vpbe-osd", pdev->name) == 0) > + osd_device = platform_get_drvdata(pdev); > + if (strcmp("vpbe-venc", pdev->name) == 0) > + venc_device = dev_get_platdata(&pdev->dev); > + > + 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; > + int i, ret = 0, num_encoders; > + struct i2c_adapter *i2c_adap; > + int output_index; > + int err; > + > + /* > + * 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, NULL, > + 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 */ > + 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->module_name, > + &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. > + */ > +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; > + /* disaable 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 __init int vpbe_probe(struct platform_device *pdev) > +{ > + struct vpbe_display_config *vpbe_config; > + struct vpbe_device *vpbe_dev; > + > + int ret = -EINVAL; > + > + if (NULL == pdev->dev.platform_data) { > + v4l2_err(pdev->dev.driver, "Unable to get vpbe config\n"); > + return -ENODEV; > + } > + > + if (pdev->dev.platform_data == NULL) { > + v4l2_err(pdev->dev.driver, "No platform data\n"); > + return -ENODEV; > + } > + vpbe_config = pdev->dev.platform_data; > + > + if (!vpbe_config->module_name[0] || > + !vpbe_config->osd.module_name[0] || > + !vpbe_config->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 = vpbe_config; > + vpbe_dev->ops = vpbe_dev_ops; > + vpbe_dev->pdev = &pdev->dev; > + > + if (vpbe_config->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..c8853f2 > --- /dev/null > +++ b/include/media/davinci/vpbe.h > @@ -0,0 +1,186 @@ > +/* > + * 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; either version 2 of the License. > + * > + * 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 > + > +/* 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_display_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_display_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; > + > + /* > + * 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; > +}; > + > +/* exported functions */ > +struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev, > + const char *venc_name); > +#endif > Regards, Hans -- Hans Verkuil - video4linux developer - sponsored by Cisco From hverkuil at xs4all.nl Mon Dec 6 14:48:47 2010 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Mon, 6 Dec 2010 21:48:47 +0100 Subject: [PATCH 4/6] davinci vpbe: VENC( Video Encoder) implementation In-Reply-To: <1291293547-2473-1-git-send-email-manjunath.hadli@ti.com> References: <1291293547-2473-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <201012062148.47555.hverkuil@xs4all.nl> On Thursday, December 02, 2010 13:39:07 Manjunath Hadli wrote: > This patch adds the VENC or the Video encoder, whichis responsible > for the blending of al 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 extenal and internal encoders > of other kind to plug-in. > > Signed-off-by: Manjunath Hadli > Signed-off-by: Muralidharan Karicheri > --- > drivers/media/video/davinci/vpbe_venc.c | 574 ++++++++++++++++++++++++++ > drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++++++++ > include/media/davinci/vpbe_venc.h | 38 ++ > 3 files changed, 801 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..bf30332 > --- /dev/null > +++ b/drivers/media/video/davinci/vpbe_venc.c > +static int venc_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm) > +{ > + int ret = 0; > + > + v4l2_dbg(debug, 1, sd, "venc_s_std_output\n"); > + > + if (norm & V4L2_STD_NTSC) > + ret = venc_set_ntsc(sd); > + else if (norm & V4L2_STD_PAL) Shouldn't this be V4L2_STD_525_60 and V4L2_STD_625_50 instead of NTSC/PAL? > + ret = venc_set_pal(sd); > + else > + ret = -EINVAL; > + return ret; > +} Regards, Hans -- Hans Verkuil - video4linux developer - sponsored by Cisco From paul_stuart at seektech.com Mon Dec 6 18:39:29 2010 From: paul_stuart at seektech.com (Paul Stuart) Date: Mon, 06 Dec 2010 16:39:29 -0800 Subject: [PATCH v2.6.37-rc3] USB: musb: Prevent Transmit Buffer Descriptor corruption Message-ID: <4CFD8241.7030306@seektech.com> cppi_next_tx_segment is not checking for Transmit Buffer Descriptor ownership before modifying parameters. Transmit Buffer Descriptor ram is shared between the host processor and the DMA. The "Ownership" bit is set by the host processor to give the DMA ownership of the Transmit Buffer Descriptor, and the bit is cleared by the DMA to return ownership to the host processor. On USB Tx, when the system is heavily loaded, cppi_next_tx_segment can overwrite a Transmit Buffer Descriptor that is still owned by the DMA, causing DMA truncation error to fire, resulting in a channel abort. This proposed fix adds a check for host processor ownership of the bd and does not proceed to program it until the DMA has ended ownership. Condition rarely occurs, so USB write speed is not negatively impacted. Tested on DM365 Signed-off-by: Paul Stuart *---* --- a/drivers/usb/musb/cppi_dma.c 2010-12-06 16:35:37.000000000 -0800 +++ b/drivers/usb/musb/cppi_dma.c 2010-12-06 16:36:15.000000000 -0800 @@ -625,6 +625,9 @@ cppi_next_tx_segment(struct musb *musb, * size; for RNDIS there _is_ only that last packet. */ for (i = 0; i < n_bds; ) { + /* wait for DMA to release ownership of this bd */ + while(bd->hw_options & CPPI_OWN_SET)cpu_relax(); + if (++i < n_bds && bd->next) bd->hw_next = bd->next->dma; else From cyril at ti.com Tue Dec 7 08:51:53 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 7 Dec 2010 09:51:53 -0500 Subject: [PATCH v7 03/12] davinci: add ssp config for tnetv107x evm board In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <1291733522-3626-4-git-send-email-cyril@ti.com> This patch adds SSP configuration and pin muxing info for tnetv107x evm boards. Signed-off-by: Cyril Chemparathy --- arch/arm/mach-davinci/board-tnetv107x-evm.c | 14 ++++++++++++++ 1 files changed, 14 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-tnetv107x-evm.c b/arch/arm/mach-davinci/board-tnetv107x-evm.c index a6db854..ef526b1 100644 --- a/arch/arm/mach-davinci/board-tnetv107x-evm.c +++ b/arch/arm/mach-davinci/board-tnetv107x-evm.c @@ -99,6 +99,12 @@ static const short uart1_pins[] __initdata = { -1 }; +static const short ssp_pins[] __initdata = { + TNETV107X_SSP0_0, TNETV107X_SSP0_1, TNETV107X_SSP0_2, + TNETV107X_SSP1_0, TNETV107X_SSP1_1, TNETV107X_SSP1_2, + TNETV107X_SSP1_3, -1 +}; + static struct mtd_partition nand_partitions[] = { /* bootloader (U-Boot, etc) in first 12 sectors */ { @@ -196,17 +202,25 @@ static struct matrix_keypad_platform_data keypad_config = { .no_autorepeat = 0, }; +static struct ti_ssp_data ssp_config = { + .out_clock = 250 * 1000, + .dev_data = { + }, +}; + static struct tnetv107x_device_info evm_device_info __initconst = { .serial_config = &serial_config, .mmc_config[1] = &mmc_config, /* controller 1 */ .nand_config[0] = &nand_config, /* chip select 0 */ .keypad_config = &keypad_config, + .ssp_config = &ssp_config, }; static __init void tnetv107x_evm_board_init(void) { davinci_cfg_reg_list(sdio1_pins); davinci_cfg_reg_list(uart1_pins); + davinci_cfg_reg_list(ssp_pins); tnetv107x_devices_init(&evm_device_info); } -- 1.7.1 From cyril at ti.com Tue Dec 7 08:51:50 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 7 Dec 2010 09:51:50 -0500 Subject: [PATCH v7 00/12] tnetv107x ssp drivers Message-ID: <1291733522-3626-1-git-send-email-cyril@ti.com> TI's sequencer serial port (TI-SSP) is a jack-of-all-trades type of serial port device. It has a built-in programmable execution engine that can be programmed to operate as almost any serial bus (I2C, SPI, EasyScale, and others). This patch series implements a driver stack that looks like the following: +--------+ | eeprom | . . . +--------+ +-----------+ +--------------+ +---------+ | regulator | . . . | i2c-gpio | | 1-wire | . . . +-----------+ +--------------+ +---------+ +----------------------+ +--------------------------------+ | ssp-spi | | ssp-gpio | +----------------------+ +--------------------------------+ +----------------------------------------------------------+ | ssp | +----------------------------------------------------------+ Changes between v7 and v6 if this series: - Workaround for iosel2 register not reading back set bits. - Update backlight status once probe succeeds. Changes between v6 and v5 of this series: - Changed initcalls to module_init() across all drivers. This series now uses a late_initcall() in the board to delay initialization of gpio and regulator dependent devices. Changes between v5 and v4 of this series: - Moved drivers from misc/gpio/spi to mfd - Removed implicit init-time iosel setup - Minor cleanups in backlight driver Changes between v3 and v4 of this series: - Replaced polled wait for sequence termination with interrupt - Improved locking within SSP driver - Other minor cleanups Changes between v2 and v3 of this series: - Minor cleanups in Kconfig and Makefile ordering Changes between v1 and v2 of this series: - Replaced open()/close() semantics with dynamic platform_device registration on SSP probe. - Removed user-land interface to regulator registers - More sensible regulator constraints - Other minor cleanups Cyril Chemparathy (12): misc: add driver for sequencer serial port davinci: add tnetv107x ssp platform device davinci: add ssp config for tnetv107x evm board spi: add ti-ssp spi master driver davinci: add spi devices on tnetv107x evm regulator: add driver for tps6524x regulator davinci: add tnetv107x evm regulators gpio: add ti-ssp gpio driver davinci: add tnetv107x evm ti-ssp gpio device backlight: add support for tps6116x controller davinci: add tnetv107x evm backlight device davinci: add tnetv107x evm i2c eeprom device arch/arm/mach-davinci/board-tnetv107x-evm.c | 197 +++++++ arch/arm/mach-davinci/devices-tnetv107x.c | 25 + arch/arm/mach-davinci/include/mach/tnetv107x.h | 2 + arch/arm/mach-davinci/tnetv107x.c | 2 +- drivers/gpio/Kconfig | 10 + drivers/gpio/Makefile | 1 + drivers/gpio/ti-ssp-gpio.c | 206 +++++++ drivers/mfd/Kconfig | 11 + drivers/mfd/Makefile | 1 + drivers/mfd/ti-ssp.c | 476 ++++++++++++++++ drivers/regulator/Kconfig | 10 + drivers/regulator/Makefile | 1 + drivers/regulator/tps6524x-regulator.c | 692 ++++++++++++++++++++++++ drivers/spi/Kconfig | 10 + drivers/spi/Makefile | 1 + drivers/spi/ti-ssp-spi.c | 402 ++++++++++++++ drivers/video/backlight/Kconfig | 7 + drivers/video/backlight/Makefile | 2 +- drivers/video/backlight/tps6116x.c | 299 ++++++++++ include/linux/mfd/ti_ssp.h | 97 ++++ 20 files changed, 2450 insertions(+), 2 deletions(-) create mode 100644 drivers/gpio/ti-ssp-gpio.c create mode 100644 drivers/mfd/ti-ssp.c create mode 100644 drivers/regulator/tps6524x-regulator.c create mode 100644 drivers/spi/ti-ssp-spi.c create mode 100644 drivers/video/backlight/tps6116x.c create mode 100644 include/linux/mfd/ti_ssp.h From cyril at ti.com Tue Dec 7 08:51:51 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 7 Dec 2010 09:51:51 -0500 Subject: [PATCH v7 01/12] misc: add driver for sequencer serial port In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <1291733522-3626-2-git-send-email-cyril@ti.com> TI's sequencer serial port (TI-SSP) is a jack-of-all-trades type of serial port device. It has a built-in programmable execution engine that can be programmed to operate as almost any serial bus (I2C, SPI, EasyScale, and others). This patch adds a driver for this controller device. The driver does not expose a user-land interface. Protocol drivers built on top of this layer are expected to remain in-kernel. Signed-off-by: Cyril Chemparathy --- drivers/mfd/Kconfig | 11 + drivers/mfd/Makefile | 1 + drivers/mfd/ti-ssp.c | 476 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/ti_ssp.h | 87 ++++++++ 4 files changed, 575 insertions(+), 0 deletions(-) create mode 100644 drivers/mfd/ti-ssp.c create mode 100644 include/linux/mfd/ti_ssp.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3a1493b..a4b4b70 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -81,6 +81,17 @@ config MFD_DM355EVM_MSP boards. MSP430 firmware manages resets and power sequencing, inputs from buttons and the IR remote, LEDs, an RTC, and more. +config MFD_TI_SSP + tristate "TI Sequencer Serial Port support" + depends on ARCH_DAVINCI_TNETV107X + select MFD_CORE + ---help--- + Say Y here if you want support for the Sequencer Serial Port + in a Texas Instruments TNETV107X SoC. + + To compile this driver as a module, choose M here: the + module will be called ti-ssp. + config HTC_EGPIO bool "HTC EGPIO support" depends on GENERIC_HARDIRQS && GPIOLIB && ARM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f54b365..f64cf13 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o +obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o obj-$(CONFIG_MFD_STMPE) += stmpe.o obj-$(CONFIG_MFD_TC35892) += tc35892.o diff --git a/drivers/mfd/ti-ssp.c b/drivers/mfd/ti-ssp.c new file mode 100644 index 0000000..af9ab0e --- /dev/null +++ b/drivers/mfd/ti-ssp.c @@ -0,0 +1,476 @@ +/* + * Sequencer Serial Port (SSP) driver for Texas Instruments' SoCs + * + * 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; 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register Offsets */ +#define REG_REV 0x00 +#define REG_IOSEL_1 0x04 +#define REG_IOSEL_2 0x08 +#define REG_PREDIV 0x0c +#define REG_INTR_ST 0x10 +#define REG_INTR_EN 0x14 +#define REG_TEST_CTRL 0x18 + +/* Per port registers */ +#define PORT_CFG_2 0x00 +#define PORT_ADDR 0x04 +#define PORT_DATA 0x08 +#define PORT_CFG_1 0x0c +#define PORT_STATE 0x10 + +#define SSP_PORT_CONFIG_MASK (SSP_EARLY_DIN | SSP_DELAY_DOUT) +#define SSP_PORT_CLKRATE_MASK 0x0f + +#define SSP_SEQRAM_WR_EN BIT(4) +#define SSP_SEQRAM_RD_EN BIT(5) +#define SSP_START BIT(15) +#define SSP_BUSY BIT(10) +#define SSP_PORT_ASL BIT(7) +#define SSP_PORT_CFO1 BIT(6) + +#define SSP_PORT_SEQRAM_SIZE 32 + +static const int ssp_port_base[] = {0x040, 0x080}; +static const int ssp_port_seqram[] = {0x100, 0x180}; + +struct ti_ssp { + struct resource *res; + struct device *dev; + void __iomem *regs; + spinlock_t lock; + struct clk *clk; + int irq; + wait_queue_head_t wqh; + + /* + * Some of the iosel2 register bits always read-back as 0, we need to + * remember these values so that we don't clobber previously set + * values. + */ + u32 iosel2; +}; + +static inline struct ti_ssp *dev_to_ssp(struct device *dev) +{ + return dev_get_drvdata(dev->parent); +} + +static inline int dev_to_port(struct device *dev) +{ + return to_platform_device(dev)->id; +} + +/* Register Access Helpers, rmw() functions need to run locked */ +static inline u32 ssp_read(struct ti_ssp *ssp, int reg) +{ + return __raw_readl(ssp->regs + reg); +} + +static inline void ssp_write(struct ti_ssp *ssp, int reg, u32 val) +{ + __raw_writel(val, ssp->regs + reg); +} + +static inline void ssp_rmw(struct ti_ssp *ssp, int reg, u32 mask, u32 bits) +{ + ssp_write(ssp, reg, (ssp_read(ssp, reg) & ~mask) | bits); +} + +static inline u32 ssp_port_read(struct ti_ssp *ssp, int port, int reg) +{ + return ssp_read(ssp, ssp_port_base[port] + reg); +} + +static inline void ssp_port_write(struct ti_ssp *ssp, int port, int reg, + u32 val) +{ + ssp_write(ssp, ssp_port_base[port] + reg, val); +} + +static inline void ssp_port_rmw(struct ti_ssp *ssp, int port, int reg, + u32 mask, u32 bits) +{ + ssp_rmw(ssp, ssp_port_base[port] + reg, mask, bits); +} + +static inline void ssp_port_clr_bits(struct ti_ssp *ssp, int port, int reg, + u32 bits) +{ + ssp_port_rmw(ssp, port, reg, bits, 0); +} + +static inline void ssp_port_set_bits(struct ti_ssp *ssp, int port, int reg, + u32 bits) +{ + ssp_port_rmw(ssp, port, reg, 0, bits); +} + +/* Called to setup port clock mode, caller must hold ssp->lock */ +static int __set_mode(struct ti_ssp *ssp, int port, int mode) +{ + mode &= SSP_PORT_CONFIG_MASK; + ssp_port_rmw(ssp, port, PORT_CFG_1, SSP_PORT_CONFIG_MASK, mode); + + return 0; +} + +int ti_ssp_set_mode(struct device *dev, int mode) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + int ret; + + spin_lock(&ssp->lock); + ret = __set_mode(ssp, port, mode); + spin_unlock(&ssp->lock); + + return ret; +} +EXPORT_SYMBOL(ti_ssp_set_mode); + +/* Called to setup iosel2, caller must hold ssp->lock */ +static void __set_iosel2(struct ti_ssp *ssp, u32 mask, u32 val) +{ + ssp->iosel2 = (ssp->iosel2 & ~mask) | val; + ssp_write(ssp, REG_IOSEL_2, ssp->iosel2); +} + +/* Called to setup port iosel, caller must hold ssp->lock */ +static void __set_iosel(struct ti_ssp *ssp, int port, u32 iosel) +{ + unsigned val, shift = port ? 16 : 0; + + /* IOSEL1 gets the least significant 16 bits */ + val = ssp_read(ssp, REG_IOSEL_1); + val &= 0xffff << (port ? 0 : 16); + val |= (iosel & 0xffff) << (port ? 16 : 0); + ssp_write(ssp, REG_IOSEL_1, val); + + /* IOSEL2 gets the most significant 16 bits */ + val = (iosel >> 16) & 0x7; + __set_iosel2(ssp, 0x7 << shift, val << shift); +} + +int ti_ssp_set_iosel(struct device *dev, u32 iosel) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + + spin_lock(&ssp->lock); + __set_iosel(ssp, port, iosel); + spin_unlock(&ssp->lock); + + return 0; +} +EXPORT_SYMBOL(ti_ssp_set_iosel); + +int ti_ssp_load(struct device *dev, int offs, u32* prog, int len) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + int i; + + if (len > SSP_PORT_SEQRAM_SIZE) + return -ENOSPC; + + spin_lock(&ssp->lock); + + /* Enable SeqRAM access */ + ssp_port_set_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN); + + /* Copy code */ + for (i = 0; i < len; i++) { + __raw_writel(prog[i], ssp->regs + offs + 4*i + + ssp_port_seqram[port]); + } + + /* Disable SeqRAM access */ + ssp_port_clr_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN); + + spin_unlock(&ssp->lock); + + return 0; +} +EXPORT_SYMBOL(ti_ssp_load); + +int ti_ssp_raw_read(struct device *dev) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + int shift = port ? 27 : 11; + + return (ssp_read(ssp, REG_IOSEL_2) >> shift) & 0xf; +} +EXPORT_SYMBOL(ti_ssp_raw_read); + +int ti_ssp_raw_write(struct device *dev, u32 val) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev), shift; + + spin_lock(&ssp->lock); + + shift = port ? 22 : 6; + val &= 0xf; + __set_iosel2(ssp, 0xf << shift, val << shift); + + spin_unlock(&ssp->lock); + + return 0; +} +EXPORT_SYMBOL(ti_ssp_raw_write); + +static inline int __xfer_done(struct ti_ssp *ssp, int port) +{ + return !(ssp_port_read(ssp, port, PORT_CFG_1) & SSP_BUSY); +} + +int ti_ssp_run(struct device *dev, u32 pc, u32 input, u32 *output) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + int ret; + + if (pc & ~(0x3f)) + return -EINVAL; + + /* Grab ssp->lock to serialize rmw on ssp registers */ + spin_lock(&ssp->lock); + + ssp_port_write(ssp, port, PORT_ADDR, input >> 16); + ssp_port_write(ssp, port, PORT_DATA, input & 0xffff); + ssp_port_rmw(ssp, port, PORT_CFG_1, 0x3f, pc); + + /* grab wait queue head lock to avoid race with the isr */ + spin_lock_irq(&ssp->wqh.lock); + + /* kick off sequence execution in hardware */ + ssp_port_set_bits(ssp, port, PORT_CFG_1, SSP_START); + + /* drop ssp lock; no register writes beyond this */ + spin_unlock(&ssp->lock); + + ret = wait_event_interruptible_locked_irq(ssp->wqh, + __xfer_done(ssp, port)); + spin_unlock_irq(&ssp->wqh.lock); + + if (ret < 0) + return ret; + + if (output) { + *output = (ssp_port_read(ssp, port, PORT_ADDR) << 16) | + (ssp_port_read(ssp, port, PORT_DATA) & 0xffff); + } + + ret = ssp_port_read(ssp, port, PORT_STATE) & 0x3f; /* stop address */ + + return ret; +} +EXPORT_SYMBOL(ti_ssp_run); + +static irqreturn_t ti_ssp_interrupt(int irq, void *dev_data) +{ + struct ti_ssp *ssp = dev_data; + + spin_lock(&ssp->wqh.lock); + + ssp_write(ssp, REG_INTR_ST, 0x3); + wake_up_locked(&ssp->wqh); + + spin_unlock(&ssp->wqh.lock); + + return IRQ_HANDLED; +} + +static int __devinit ti_ssp_probe(struct platform_device *pdev) +{ + static struct ti_ssp *ssp; + const struct ti_ssp_data *pdata = pdev->dev.platform_data; + int error = 0, prediv = 0xff, id; + unsigned long sysclk; + struct device *dev = &pdev->dev; + struct mfd_cell cells[2]; + + ssp = kzalloc(sizeof(*ssp), GFP_KERNEL); + if (!ssp) { + dev_err(dev, "cannot allocate device info\n"); + return -ENOMEM; + } + + ssp->dev = dev; + dev_set_drvdata(dev, ssp); + + ssp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!ssp->res) { + error = -ENODEV; + dev_err(dev, "cannot determine register area\n"); + goto error_res; + } + + if (!request_mem_region(ssp->res->start, resource_size(ssp->res), + pdev->name)) { + error = -ENOMEM; + dev_err(dev, "cannot claim register memory\n"); + goto error_res; + } + + ssp->regs = ioremap(ssp->res->start, resource_size(ssp->res)); + if (!ssp->regs) { + error = -ENOMEM; + dev_err(dev, "cannot map register memory\n"); + goto error_map; + } + + ssp->clk = clk_get(dev, NULL); + if (IS_ERR(ssp->clk)) { + error = PTR_ERR(ssp->clk); + dev_err(dev, "cannot claim device clock\n"); + goto error_clk; + } + + ssp->irq = platform_get_irq(pdev, 0); + if (ssp->irq < 0) { + error = -ENODEV; + dev_err(dev, "unknown irq\n"); + goto error_irq; + } + + error = request_threaded_irq(ssp->irq, NULL, ti_ssp_interrupt, 0, + dev_name(dev), ssp); + if (error < 0) { + dev_err(dev, "cannot acquire irq\n"); + goto error_irq; + } + + spin_lock_init(&ssp->lock); + init_waitqueue_head(&ssp->wqh); + + /* Power on and initialize SSP */ + error = clk_enable(ssp->clk); + if (error) { + dev_err(dev, "cannot enable device clock\n"); + goto error_enable; + } + + /* Reset registers to a sensible known state */ + ssp_write(ssp, REG_IOSEL_1, 0); + ssp_write(ssp, REG_IOSEL_2, 0); + ssp_write(ssp, REG_INTR_EN, 0x3); + ssp_write(ssp, REG_INTR_ST, 0x3); + ssp_write(ssp, REG_TEST_CTRL, 0); + ssp_port_write(ssp, 0, PORT_CFG_1, SSP_PORT_ASL); + ssp_port_write(ssp, 1, PORT_CFG_1, SSP_PORT_ASL); + ssp_port_write(ssp, 0, PORT_CFG_2, SSP_PORT_CFO1); + ssp_port_write(ssp, 1, PORT_CFG_2, SSP_PORT_CFO1); + + sysclk = clk_get_rate(ssp->clk); + if (pdata && pdata->out_clock) + prediv = (sysclk / pdata->out_clock) - 1; + prediv = clamp(prediv, 0, 0xff); + ssp_rmw(ssp, REG_PREDIV, 0xff, prediv); + + memset(cells, 0, sizeof(cells)); + for (id = 0; id < 2; id++) { + const struct ti_ssp_dev_data *data = &pdata->dev_data[id]; + + cells[id].id = id; + cells[id].name = data->dev_name; + cells[id].platform_data = data->pdata; + cells[id].data_size = data->pdata_size; + } + + error = mfd_add_devices(dev, 0, cells, 2, NULL, 0); + if (error < 0) { + dev_err(dev, "cannot add mfd cells\n"); + goto error_enable; + } + + return 0; + +error_enable: + free_irq(ssp->irq, ssp); +error_irq: + clk_put(ssp->clk); +error_clk: + iounmap(ssp->regs); +error_map: + release_mem_region(ssp->res->start, resource_size(ssp->res)); +error_res: + kfree(ssp); + return error; +} + +static int __devexit ti_ssp_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ti_ssp *ssp = dev_get_drvdata(dev); + + mfd_remove_devices(dev); + clk_disable(ssp->clk); + free_irq(ssp->irq, ssp); + clk_put(ssp->clk); + iounmap(ssp->regs); + release_mem_region(ssp->res->start, resource_size(ssp->res)); + kfree(ssp); + dev_set_drvdata(dev, NULL); + return 0; +} + +static struct platform_driver ti_ssp_driver = { + .probe = ti_ssp_probe, + .remove = __devexit_p(ti_ssp_remove), + .driver = { + .name = "ti-ssp", + .owner = THIS_MODULE, + } +}; + +static int __init ti_ssp_init(void) +{ + return platform_driver_register(&ti_ssp_driver); +} +module_init(ti_ssp_init); + +static void __exit ti_ssp_exit(void) +{ + platform_driver_unregister(&ti_ssp_driver); +} +module_exit(ti_ssp_exit); + +MODULE_DESCRIPTION("Sequencer Serial Port (SSP) Driver"); +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ti-ssp"); diff --git a/include/linux/mfd/ti_ssp.h b/include/linux/mfd/ti_ssp.h new file mode 100644 index 0000000..021fe09 --- /dev/null +++ b/include/linux/mfd/ti_ssp.h @@ -0,0 +1,87 @@ +/* + * Sequencer Serial Port (SSP) driver for Texas Instruments' SoCs + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __TI_SSP_H__ +#define __TI_SSP_H__ + +struct ti_ssp_dev_data { + const char *dev_name; + void *pdata; + size_t pdata_size; +}; + +struct ti_ssp_data { + unsigned long out_clock; + struct ti_ssp_dev_data dev_data[2]; +}; + +/* + * Sequencer port IO pin configuration bits. These do not correlate 1-1 with + * the hardware. The iosel field in the port data combines iosel1 and iosel2, + * and is therefore not a direct map to register space. It is best to use the + * macros below to construct iosel values. + * + * least significant 16 bits --> iosel1 + * most significant 16 bits --> iosel2 + */ + +#define SSP_IN 0x0000 +#define SSP_DATA 0x0001 +#define SSP_CLOCK 0x0002 +#define SSP_CHIPSEL 0x0003 +#define SSP_OUT 0x0004 +#define SSP_PIN_SEL(pin, v) ((v) << ((pin) * 3)) +#define SSP_PIN_MASK(pin) SSP_PIN_SEL(pin, 0x7) +#define SSP_INPUT_SEL(pin) ((pin) << 16) + +/* Sequencer port config bits */ +#define SSP_EARLY_DIN BIT(8) +#define SSP_DELAY_DOUT BIT(9) + +/* Sequence map definitions */ +#define SSP_CLK_HIGH BIT(0) +#define SSP_CLK_LOW 0 +#define SSP_DATA_HIGH BIT(1) +#define SSP_DATA_LOW 0 +#define SSP_CS_HIGH BIT(2) +#define SSP_CS_LOW 0 +#define SSP_OUT_MODE BIT(3) +#define SSP_IN_MODE 0 +#define SSP_DATA_REG BIT(4) +#define SSP_ADDR_REG 0 + +#define SSP_OPCODE_DIRECT ((0x0) << 5) +#define SSP_OPCODE_TOGGLE ((0x1) << 5) +#define SSP_OPCODE_SHIFT ((0x2) << 5) +#define SSP_OPCODE_BRANCH0 ((0x4) << 5) +#define SSP_OPCODE_BRANCH1 ((0x5) << 5) +#define SSP_OPCODE_BRANCH ((0x6) << 5) +#define SSP_OPCODE_STOP ((0x7) << 5) +#define SSP_BRANCH(addr) ((addr) << 8) +#define SSP_COUNT(cycles) ((cycles) << 8) + +int ti_ssp_raw_read(struct device *dev); +int ti_ssp_raw_write(struct device *dev, u32 val); +int ti_ssp_load(struct device *dev, int offs, u32* prog, int len); +int ti_ssp_run(struct device *dev, u32 pc, u32 input, u32 *output); +int ti_ssp_set_mode(struct device *dev, int mode); +int ti_ssp_set_iosel(struct device *dev, u32 iosel); + +#endif /* __TI_SSP_H__ */ -- 1.7.1 From cyril at ti.com Tue Dec 7 08:51:52 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 7 Dec 2010 09:51:52 -0500 Subject: [PATCH v7 02/12] davinci: add tnetv107x ssp platform device In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <1291733522-3626-3-git-send-email-cyril@ti.com> This patch adds an SSP platform device definition for the tnetv107x soc family. The clock lookup entry has also been updated to match. Signed-off-by: Cyril Chemparathy --- arch/arm/mach-davinci/devices-tnetv107x.c | 25 ++++++++++++++++++++++++ arch/arm/mach-davinci/include/mach/tnetv107x.h | 2 + arch/arm/mach-davinci/tnetv107x.c | 2 +- 3 files changed, 28 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/devices-tnetv107x.c b/arch/arm/mach-davinci/devices-tnetv107x.c index 85503de..6162cae 100644 --- a/arch/arm/mach-davinci/devices-tnetv107x.c +++ b/arch/arm/mach-davinci/devices-tnetv107x.c @@ -35,6 +35,7 @@ #define TNETV107X_SDIO0_BASE 0x08088700 #define TNETV107X_SDIO1_BASE 0x08088800 #define TNETV107X_KEYPAD_BASE 0x08088a00 +#define TNETV107X_SSP_BASE 0x08088c00 #define TNETV107X_ASYNC_EMIF_CNTRL_BASE 0x08200000 #define TNETV107X_ASYNC_EMIF_DATA_CE0_BASE 0x30000000 #define TNETV107X_ASYNC_EMIF_DATA_CE1_BASE 0x40000000 @@ -342,6 +343,25 @@ static struct platform_device tsc_device = { .resource = tsc_resources, }; +static struct resource ssp_resources[] = { + { + .start = TNETV107X_SSP_BASE, + .end = TNETV107X_SSP_BASE + 0x1ff, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TNETV107X_SSP, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device ssp_device = { + .name = "ti-ssp", + .id = -1, + .num_resources = ARRAY_SIZE(ssp_resources), + .resource = ssp_resources, +}; + void __init tnetv107x_devices_init(struct tnetv107x_device_info *info) { int i, error; @@ -380,4 +400,9 @@ void __init tnetv107x_devices_init(struct tnetv107x_device_info *info) keypad_device.dev.platform_data = info->keypad_config; platform_device_register(&keypad_device); } + + if (info->ssp_config) { + ssp_device.dev.platform_data = info->ssp_config; + platform_device_register(&ssp_device); + } } diff --git a/arch/arm/mach-davinci/include/mach/tnetv107x.h b/arch/arm/mach-davinci/include/mach/tnetv107x.h index 5a681d8..89c1fdc 100644 --- a/arch/arm/mach-davinci/include/mach/tnetv107x.h +++ b/arch/arm/mach-davinci/include/mach/tnetv107x.h @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -44,6 +45,7 @@ struct tnetv107x_device_info { struct davinci_mmc_config *mmc_config[2]; /* 2 controllers */ struct davinci_nand_pdata *nand_config[4]; /* 4 chipsels */ struct matrix_keypad_platform_data *keypad_config; + struct ti_ssp_data *ssp_config; }; extern struct platform_device tnetv107x_wdt_device; diff --git a/arch/arm/mach-davinci/tnetv107x.c b/arch/arm/mach-davinci/tnetv107x.c index 6fcdece..1b28fdd 100644 --- a/arch/arm/mach-davinci/tnetv107x.c +++ b/arch/arm/mach-davinci/tnetv107x.c @@ -278,7 +278,7 @@ static struct clk_lookup clks[] = { CLK(NULL, "timer1", &clk_timer1), CLK("tnetv107x_wdt.0", NULL, &clk_wdt_arm), CLK(NULL, "clk_wdt_dsp", &clk_wdt_dsp), - CLK("ti-ssp.0", NULL, &clk_ssp), + CLK("ti-ssp", NULL, &clk_ssp), CLK(NULL, "clk_tdm0", &clk_tdm0), CLK(NULL, "clk_vlynq", &clk_vlynq), CLK(NULL, "clk_mcdma", &clk_mcdma), -- 1.7.1 From cyril at ti.com Tue Dec 7 08:51:56 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 7 Dec 2010 09:51:56 -0500 Subject: [PATCH v7 06/12] regulator: add driver for tps6524x regulator In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <1291733522-3626-7-git-send-email-cyril@ti.com> TPS6524X provides three step-down converters and two general-purpose LDO voltage regulators. This device is interfaced using SPI. Acked-by: Mark Brown Signed-off-by: Cyril Chemparathy --- drivers/regulator/Kconfig | 10 + drivers/regulator/Makefile | 1 + drivers/regulator/tps6524x-regulator.c | 692 ++++++++++++++++++++++++++++++++ 3 files changed, 703 insertions(+), 0 deletions(-) create mode 100644 drivers/regulator/tps6524x-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index dd30e88..da34981 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -250,5 +250,15 @@ config REGULATOR_TPS6586X help This driver supports TPS6586X voltage regulator chips. +config REGULATOR_TPS6524X + tristate "TI TPS6524X Power regulators" + depends on SPI + help + This driver supports TPS6524X voltage regulator chips. TPS6524X + provides three step-down converters and two general-purpose LDO + voltage regulators. This device is interfaced using a customized + serial interface currently supported on the sequencer serial + port controller. + endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index bff8157..cf71df7 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o +obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c new file mode 100644 index 0000000..dea1260 --- /dev/null +++ b/drivers/regulator/tps6524x-regulator.c @@ -0,0 +1,692 @@ +/* + * Regulator driver for TPS6524x PMIC + * + * Copyright (C) 2010 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_LDO_SET 0x0 +#define LDO_ILIM_MASK 1 /* 0 = 400-800, 1 = 900-1500 */ +#define LDO_VSEL_MASK 0x0f +#define LDO2_ILIM_SHIFT 12 +#define LDO2_VSEL_SHIFT 4 +#define LDO1_ILIM_SHIFT 8 +#define LDO1_VSEL_SHIFT 0 + +#define REG_BLOCK_EN 0x1 +#define BLOCK_MASK 1 +#define BLOCK_LDO1_SHIFT 0 +#define BLOCK_LDO2_SHIFT 1 +#define BLOCK_LCD_SHIFT 2 +#define BLOCK_USB_SHIFT 3 + +#define REG_DCDC_SET 0x2 +#define DCDC_VDCDC_MASK 0x1f +#define DCDC_VDCDC1_SHIFT 0 +#define DCDC_VDCDC2_SHIFT 5 +#define DCDC_VDCDC3_SHIFT 10 + +#define REG_DCDC_EN 0x3 +#define DCDCDCDC_EN_MASK 0x1 +#define DCDCDCDC1_EN_SHIFT 0 +#define DCDCDCDC1_PG_MSK BIT(1) +#define DCDCDCDC2_EN_SHIFT 2 +#define DCDCDCDC2_PG_MSK BIT(3) +#define DCDCDCDC3_EN_SHIFT 4 +#define DCDCDCDC3_PG_MSK BIT(5) + +#define REG_USB 0x4 +#define USB_ILIM_SHIFT 0 +#define USB_ILIM_MASK 0x3 +#define USB_TSD_SHIFT 2 +#define USB_TSD_MASK 0x3 +#define USB_TWARN_SHIFT 4 +#define USB_TWARN_MASK 0x3 +#define USB_IWARN_SD BIT(6) +#define USB_FAST_LOOP BIT(7) + +#define REG_ALARM 0x5 +#define ALARM_LDO1 BIT(0) +#define ALARM_DCDC1 BIT(1) +#define ALARM_DCDC2 BIT(2) +#define ALARM_DCDC3 BIT(3) +#define ALARM_LDO2 BIT(4) +#define ALARM_USB_WARN BIT(5) +#define ALARM_USB_ALARM BIT(6) +#define ALARM_LCD BIT(9) +#define ALARM_TEMP_WARM BIT(10) +#define ALARM_TEMP_HOT BIT(11) +#define ALARM_NRST BIT(14) +#define ALARM_POWERUP BIT(15) + +#define REG_INT_ENABLE 0x6 +#define INT_LDO1 BIT(0) +#define INT_DCDC1 BIT(1) +#define INT_DCDC2 BIT(2) +#define INT_DCDC3 BIT(3) +#define INT_LDO2 BIT(4) +#define INT_USB_WARN BIT(5) +#define INT_USB_ALARM BIT(6) +#define INT_LCD BIT(9) +#define INT_TEMP_WARM BIT(10) +#define INT_TEMP_HOT BIT(11) +#define INT_GLOBAL_EN BIT(15) + +#define REG_INT_STATUS 0x7 +#define STATUS_LDO1 BIT(0) +#define STATUS_DCDC1 BIT(1) +#define STATUS_DCDC2 BIT(2) +#define STATUS_DCDC3 BIT(3) +#define STATUS_LDO2 BIT(4) +#define STATUS_USB_WARN BIT(5) +#define STATUS_USB_ALARM BIT(6) +#define STATUS_LCD BIT(9) +#define STATUS_TEMP_WARM BIT(10) +#define STATUS_TEMP_HOT BIT(11) + +#define REG_SOFTWARE_RESET 0xb +#define REG_WRITE_ENABLE 0xd +#define REG_REV_ID 0xf + +#define N_DCDC 3 +#define N_LDO 2 +#define N_SWITCH 2 +#define N_REGULATORS (3 /* DCDC */ + \ + 2 /* LDO */ + \ + 2 /* switch */) + +#define FIXED_ILIMSEL BIT(0) +#define FIXED_VOLTAGE BIT(1) + +#define CMD_READ(reg) ((reg) << 6) +#define CMD_WRITE(reg) (BIT(5) | (reg) << 6) +#define STAT_CLK BIT(3) +#define STAT_WRITE BIT(2) +#define STAT_INVALID BIT(1) +#define STAT_WP BIT(0) + +struct field { + int reg; + int shift; + int mask; +}; + +struct supply_info { + const char *name; + int n_voltages; + const int *voltages; + int fixed_voltage; + int n_ilimsels; + const int *ilimsels; + int fixed_ilimsel; + int flags; + struct field enable, voltage, ilimsel; +}; + +struct tps6524x { + struct device *dev; + struct spi_device *spi; + struct mutex lock; + struct regulator_desc desc[N_REGULATORS]; + struct regulator_dev *rdev[N_REGULATORS]; +}; + +static int __read_reg(struct tps6524x *hw, int reg) +{ + int error = 0; + u16 cmd = CMD_READ(reg), in; + u8 status; + struct spi_message m; + struct spi_transfer t[3]; + + spi_message_init(&m); + memset(t, 0, sizeof(t)); + + t[0].tx_buf = &cmd; + t[0].len = 2; + t[0].bits_per_word = 12; + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = ∈ + t[1].len = 2; + t[1].bits_per_word = 16; + spi_message_add_tail(&t[1], &m); + + t[2].rx_buf = &status; + t[2].len = 1; + t[2].bits_per_word = 4; + spi_message_add_tail(&t[2], &m); + + error = spi_sync(hw->spi, &m); + if (error < 0) + return error; + + dev_dbg(hw->dev, "read reg %d, data %x, status %x\n", + reg, in, status); + + if (!(status & STAT_CLK) || (status & STAT_WRITE)) + return -EIO; + + if (status & STAT_INVALID) + return -EINVAL; + + return in; +} + +static int read_reg(struct tps6524x *hw, int reg) +{ + int ret; + + mutex_lock(&hw->lock); + ret = __read_reg(hw, reg); + mutex_unlock(&hw->lock); + + return ret; +} + +static int __write_reg(struct tps6524x *hw, int reg, int val) +{ + int error = 0; + u16 cmd = CMD_WRITE(reg), out = val; + u8 status; + struct spi_message m; + struct spi_transfer t[3]; + + spi_message_init(&m); + memset(t, 0, sizeof(t)); + + t[0].tx_buf = &cmd; + t[0].len = 2; + t[0].bits_per_word = 12; + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = &out; + t[1].len = 2; + t[1].bits_per_word = 16; + spi_message_add_tail(&t[1], &m); + + t[2].rx_buf = &status; + t[2].len = 1; + t[2].bits_per_word = 4; + spi_message_add_tail(&t[2], &m); + + error = spi_sync(hw->spi, &m); + if (error < 0) + return error; + + dev_dbg(hw->dev, "wrote reg %d, data %x, status %x\n", + reg, out, status); + + if (!(status & STAT_CLK) || !(status & STAT_WRITE)) + return -EIO; + + if (status & (STAT_INVALID | STAT_WP)) + return -EINVAL; + + return error; +} + +static int __rmw_reg(struct tps6524x *hw, int reg, int mask, int val) +{ + int ret; + + ret = __read_reg(hw, reg); + if (ret < 0) + return ret; + + ret &= ~mask; + ret |= val; + + ret = __write_reg(hw, reg, ret); + + return (ret < 0) ? ret : 0; +} + +static int rmw_protect(struct tps6524x *hw, int reg, int mask, int val) +{ + int ret; + + mutex_lock(&hw->lock); + + ret = __write_reg(hw, REG_WRITE_ENABLE, 1); + if (ret) { + dev_err(hw->dev, "failed to set write enable\n"); + goto error; + } + + ret = __rmw_reg(hw, reg, mask, val); + if (ret) + dev_err(hw->dev, "failed to rmw register %d\n", reg); + + ret = __write_reg(hw, REG_WRITE_ENABLE, 0); + if (ret) { + dev_err(hw->dev, "failed to clear write enable\n"); + goto error; + } + +error: + mutex_unlock(&hw->lock); + + return ret; +} + +static int read_field(struct tps6524x *hw, const struct field *field) +{ + int tmp; + + tmp = read_reg(hw, field->reg); + if (tmp < 0) + return tmp; + + return (tmp >> field->shift) & field->mask; +} + +static int write_field(struct tps6524x *hw, const struct field *field, + int val) +{ + if (val & ~field->mask) + return -EOVERFLOW; + + return rmw_protect(hw, field->reg, + field->mask << field->shift, + val << field->shift); +} + +static const int dcdc1_voltages[] = { + 800000, 825000, 850000, 875000, + 900000, 925000, 950000, 975000, + 1000000, 1025000, 1050000, 1075000, + 1100000, 1125000, 1150000, 1175000, + 1200000, 1225000, 1250000, 1275000, + 1300000, 1325000, 1350000, 1375000, + 1400000, 1425000, 1450000, 1475000, + 1500000, 1525000, 1550000, 1575000, +}; + +static const int dcdc2_voltages[] = { + 1400000, 1450000, 1500000, 1550000, + 1600000, 1650000, 1700000, 1750000, + 1800000, 1850000, 1900000, 1950000, + 2000000, 2050000, 2100000, 2150000, + 2200000, 2250000, 2300000, 2350000, + 2400000, 2450000, 2500000, 2550000, + 2600000, 2650000, 2700000, 2750000, + 2800000, 2850000, 2900000, 2950000, +}; + +static const int dcdc3_voltages[] = { + 2400000, 2450000, 2500000, 2550000, 2600000, + 2650000, 2700000, 2750000, 2800000, 2850000, + 2900000, 2950000, 3000000, 3050000, 3100000, + 3150000, 3200000, 3250000, 3300000, 3350000, + 3400000, 3450000, 3500000, 3550000, 3600000, +}; + +static const int ldo1_voltages[] = { + 4300000, 4350000, 4400000, 4450000, + 4500000, 4550000, 4600000, 4650000, + 4700000, 4750000, 4800000, 4850000, + 4900000, 4950000, 5000000, 5050000, +}; + +static const int ldo2_voltages[] = { + 1100000, 1150000, 1200000, 1250000, + 1300000, 1700000, 1750000, 1800000, + 1850000, 1900000, 3150000, 3200000, + 3250000, 3300000, 3350000, 3400000, +}; + +static const int ldo_ilimsel[] = { + 400000, 1500000 +}; + +static const int usb_ilimsel[] = { + 200000, 400000, 800000, 1000000 +}; + +#define __MK_FIELD(_reg, _mask, _shift) \ + { .reg = (_reg), .mask = (_mask), .shift = (_shift), } + +static const struct supply_info supply_info[N_REGULATORS] = { + { + .name = "DCDC1", + .flags = FIXED_ILIMSEL, + .n_voltages = ARRAY_SIZE(dcdc1_voltages), + .voltages = dcdc1_voltages, + .fixed_ilimsel = 2400000, + .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, + DCDCDCDC1_EN_SHIFT), + .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, + DCDC_VDCDC1_SHIFT), + }, + { + .name = "DCDC2", + .flags = FIXED_ILIMSEL, + .n_voltages = ARRAY_SIZE(dcdc2_voltages), + .voltages = dcdc2_voltages, + .fixed_ilimsel = 1200000, + .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, + DCDCDCDC2_EN_SHIFT), + .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, + DCDC_VDCDC2_SHIFT), + }, + { + .name = "DCDC3", + .flags = FIXED_ILIMSEL, + .n_voltages = ARRAY_SIZE(dcdc3_voltages), + .voltages = dcdc3_voltages, + .fixed_ilimsel = 1200000, + .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, + DCDCDCDC3_EN_SHIFT), + .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, + DCDC_VDCDC3_SHIFT), + }, + { + .name = "LDO1", + .n_voltages = ARRAY_SIZE(ldo1_voltages), + .voltages = ldo1_voltages, + .n_ilimsels = ARRAY_SIZE(ldo_ilimsel), + .ilimsels = ldo_ilimsel, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_LDO1_SHIFT), + .voltage = __MK_FIELD(REG_LDO_SET, LDO_VSEL_MASK, + LDO1_VSEL_SHIFT), + .ilimsel = __MK_FIELD(REG_LDO_SET, LDO_ILIM_MASK, + LDO1_ILIM_SHIFT), + }, + { + .name = "LDO2", + .n_voltages = ARRAY_SIZE(ldo2_voltages), + .voltages = ldo2_voltages, + .n_ilimsels = ARRAY_SIZE(ldo_ilimsel), + .ilimsels = ldo_ilimsel, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_LDO2_SHIFT), + .voltage = __MK_FIELD(REG_LDO_SET, LDO_VSEL_MASK, + LDO2_VSEL_SHIFT), + .ilimsel = __MK_FIELD(REG_LDO_SET, LDO_ILIM_MASK, + LDO2_ILIM_SHIFT), + }, + { + .name = "USB", + .flags = FIXED_VOLTAGE, + .fixed_voltage = 5000000, + .n_ilimsels = ARRAY_SIZE(usb_ilimsel), + .ilimsels = usb_ilimsel, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_USB_SHIFT), + .ilimsel = __MK_FIELD(REG_USB, USB_ILIM_MASK, + USB_ILIM_SHIFT), + }, + { + .name = "LCD", + .flags = FIXED_VOLTAGE | FIXED_ILIMSEL, + .fixed_voltage = 5000000, + .fixed_ilimsel = 400000, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_LCD_SHIFT), + }, +}; + +static int list_voltage(struct regulator_dev *rdev, unsigned selector) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->flags & FIXED_VOLTAGE) + return selector ? -EINVAL : info->fixed_voltage; + + if (selector < 0 || selector >= info->n_voltages) + return -EINVAL; + + return info->voltages[selector]; +} + +static int set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) +{ + const struct supply_info *info; + struct tps6524x *hw; + int i; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->flags & FIXED_VOLTAGE) + return -EINVAL; + + for (i = 0; i < info->n_voltages; i++) + if (min_uV <= info->voltages[i] && + max_uV >= info->voltages[i]) + break; + + if (i >= info->n_voltages) + return -EINVAL; + + return write_field(hw, &info->voltage, i); +} + +static int get_voltage(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + int ret; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->flags & FIXED_VOLTAGE) + return info->fixed_voltage; + + ret = read_field(hw, &info->voltage); + if (ret < 0) + return ret; + if (WARN_ON(ret >= info->n_voltages)) + return -EIO; + + return info->voltages[ret]; +} + +static int set_current_limit(struct regulator_dev *rdev, int min_uA, + int max_uA) +{ + const struct supply_info *info; + struct tps6524x *hw; + int i; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->flags & FIXED_ILIMSEL) + return -EINVAL; + + for (i = 0; i < info->n_ilimsels; i++) + if (min_uA <= info->ilimsels[i] && + max_uA >= info->ilimsels[i]) + break; + + if (i >= info->n_ilimsels) + return -EINVAL; + + return write_field(hw, &info->ilimsel, i); +} + +static int get_current_limit(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + int ret; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->flags & FIXED_ILIMSEL) + return info->fixed_ilimsel; + + ret = read_field(hw, &info->ilimsel); + if (ret < 0) + return ret; + if (WARN_ON(ret >= info->n_ilimsels)) + return -EIO; + + return info->ilimsels[ret]; +} + +static int enable_supply(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + return write_field(hw, &info->enable, 1); +} + +static int disable_supply(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + return write_field(hw, &info->enable, 0); +} + +static int is_supply_enabled(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + return read_field(hw, &info->enable); +} + +static struct regulator_ops regulator_ops = { + .is_enabled = is_supply_enabled, + .enable = enable_supply, + .disable = disable_supply, + .get_voltage = get_voltage, + .set_voltage = set_voltage, + .list_voltage = list_voltage, + .set_current_limit = set_current_limit, + .get_current_limit = get_current_limit, +}; + +static int __devexit pmic_remove(struct spi_device *spi) +{ + struct tps6524x *hw = spi_get_drvdata(spi); + int i; + + if (!hw) + return 0; + for (i = 0; i < N_REGULATORS; i++) { + if (hw->rdev[i]) + regulator_unregister(hw->rdev[i]); + hw->rdev[i] = NULL; + } + spi_set_drvdata(spi, NULL); + kfree(hw); + return 0; +} + +static int __devinit pmic_probe(struct spi_device *spi) +{ + struct tps6524x *hw; + struct device *dev = &spi->dev; + const struct supply_info *info = supply_info; + struct regulator_init_data *init_data; + int ret = 0, i; + + init_data = dev->platform_data; + if (!init_data) { + dev_err(dev, "could not find regulator platform data\n"); + return -EINVAL; + } + + hw = kzalloc(sizeof(struct tps6524x), GFP_KERNEL); + if (!hw) { + dev_err(dev, "cannot allocate regulator private data\n"); + return -ENOMEM; + } + spi_set_drvdata(spi, hw); + + memset(hw, 0, sizeof(struct tps6524x)); + hw->dev = dev; + hw->spi = spi_dev_get(spi); + mutex_init(&hw->lock); + + for (i = 0; i < N_REGULATORS; i++, info++, init_data++) { + hw->desc[i].name = info->name; + hw->desc[i].id = i; + hw->desc[i].n_voltages = info->n_voltages; + hw->desc[i].ops = ®ulator_ops; + hw->desc[i].type = REGULATOR_VOLTAGE; + hw->desc[i].owner = THIS_MODULE; + + if (info->flags & FIXED_VOLTAGE) + hw->desc[i].n_voltages = 1; + + hw->rdev[i] = regulator_register(&hw->desc[i], dev, + init_data, hw); + if (IS_ERR(hw->rdev[i])) { + ret = PTR_ERR(hw->rdev[i]); + hw->rdev[i] = NULL; + goto fail; + } + } + + return 0; + +fail: + pmic_remove(spi); + return ret; +} + +static struct spi_driver pmic_driver = { + .probe = pmic_probe, + .remove = __devexit_p(pmic_remove), + .driver = { + .name = "tps6524x", + .owner = THIS_MODULE, + }, +}; + +static int __init pmic_driver_init(void) +{ + return spi_register_driver(&pmic_driver); +} +module_init(pmic_driver_init); + +static void __exit pmic_driver_exit(void) +{ + spi_unregister_driver(&pmic_driver); +} +module_exit(pmic_driver_exit); + +MODULE_DESCRIPTION("TPS6524X PMIC Driver"); +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:tps6524x"); -- 1.7.1 From cyril at ti.com Tue Dec 7 08:51:58 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 7 Dec 2010 09:51:58 -0500 Subject: [PATCH v7 08/12] gpio: add ti-ssp gpio driver In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <1291733522-3626-9-git-send-email-cyril@ti.com> TI's SSP controller pins can be directly read and written to behave like a GPIO. This patch adds a GPIO driver that exposes such functionality. Signed-off-by: Cyril Chemparathy --- drivers/gpio/Kconfig | 10 ++ drivers/gpio/Makefile | 1 + drivers/gpio/ti-ssp-gpio.c | 206 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/ti_ssp.h | 4 + 4 files changed, 221 insertions(+), 0 deletions(-) create mode 100644 drivers/gpio/ti-ssp-gpio.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3143ac7..05bbe4c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -128,6 +128,16 @@ config GPIO_VX855 additional drivers must be enabled in order to use the functionality of the device. +config GPIO_TI_SSP + tristate "TI Sequencer Serial Port - GPIO Support" + depends on MFD_TI_SSP + help + Say yes here to support a GPIO interface on TI SSP port pins. + Each SSP port translates into 4 GPIOs. + + This driver can also be built as a module. If so, the module + will be called ti-ssp-gpio. + comment "I2C GPIO expanders:" config GPIO_MAX7300 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index bdf3dde..0e2a844 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_GPIO_PL061) += pl061.o obj-$(CONFIG_GPIO_STMPE) += stmpe-gpio.o obj-$(CONFIG_GPIO_TC35892) += tc35892-gpio.o obj-$(CONFIG_GPIO_TIMBERDALE) += timbgpio.o +obj-$(CONFIG_GPIO_TI_SSP) += ti-ssp-gpio.o obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o obj-$(CONFIG_GPIO_UCB1400) += ucb1400_gpio.o obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o diff --git a/drivers/gpio/ti-ssp-gpio.c b/drivers/gpio/ti-ssp-gpio.c new file mode 100644 index 0000000..387970e --- /dev/null +++ b/drivers/gpio/ti-ssp-gpio.c @@ -0,0 +1,206 @@ +/* + * Sequencer Serial Port (SSP) based virtual GPIO driver + * + * 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; 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct ti_ssp_gpio_chip { + struct gpio_chip chip; + struct device *dev; + spinlock_t lock; + u8 out; + u32 iosel; +}; + +#define to_ssp_gpio_chip(c) container_of(c, struct ti_ssp_gpio_chip, chip) + +static int direction_in(struct gpio_chip *chip, unsigned gpio_num) +{ + struct ti_ssp_gpio_chip *gpio = to_ssp_gpio_chip(chip); + int error = 0; + + spin_lock(&gpio->lock); + + gpio->iosel &= ~SSP_PIN_MASK(gpio_num); + gpio->iosel |= SSP_PIN_SEL(gpio_num, SSP_IN); + + error = ti_ssp_set_iosel(gpio->dev, gpio->iosel); + + spin_unlock(&gpio->lock); + + return error; +} + +static int direction_out(struct gpio_chip *chip, unsigned gpio_num, int val) +{ + struct ti_ssp_gpio_chip *gpio = to_ssp_gpio_chip(chip); + int error; + + spin_lock(&gpio->lock); + + gpio->iosel &= ~SSP_PIN_MASK(gpio_num); + gpio->iosel |= SSP_PIN_SEL(gpio_num, SSP_OUT); + + error = ti_ssp_set_iosel(gpio->dev, gpio->iosel); + + if (error < 0) + goto error; + + if (val) + gpio->out |= BIT(gpio_num); + else + gpio->out &= ~BIT(gpio_num); + + error = ti_ssp_raw_write(gpio->dev, gpio->out); + +error: + spin_unlock(&gpio->lock); + return error; +} + +static int value_get(struct gpio_chip *chip, unsigned gpio_num) +{ + struct ti_ssp_gpio_chip *gpio = to_ssp_gpio_chip(chip); + int ret; + + spin_lock(&gpio->lock); + + ret = ti_ssp_raw_read(gpio->dev); + if (ret >= 0) + ret = !!(ret & BIT(gpio_num)); + + spin_unlock(&gpio->lock); + return ret; +} + +static void value_set(struct gpio_chip *chip, unsigned gpio_num, int val) +{ + struct ti_ssp_gpio_chip *gpio = to_ssp_gpio_chip(chip); + + spin_lock(&gpio->lock); + + if (val) + gpio->out |= BIT(gpio_num); + else + gpio->out &= ~BIT(gpio_num); + + ti_ssp_raw_write(gpio->dev, gpio->out); + + spin_unlock(&gpio->lock); +} + +static int __devinit ti_ssp_gpio_probe(struct platform_device *pdev) +{ + const struct ti_ssp_gpio_data *pdata = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct ti_ssp_gpio_chip *gpio; + int error; + + if (!pdata) { + dev_err(dev, "platform data not found\n"); + return -EINVAL; + } + + gpio = kzalloc(sizeof(*gpio), GFP_KERNEL); + if (!gpio) { + dev_err(dev, "cannot allocate driver data\n"); + return -ENOMEM; + } + + gpio->dev = dev; + gpio->iosel = SSP_PIN_SEL(0, SSP_IN) | SSP_PIN_SEL(1, SSP_IN) | + SSP_PIN_SEL(2, SSP_IN) | SSP_PIN_SEL(3, SSP_IN); + error = ti_ssp_set_iosel(gpio->dev, gpio->iosel); + if (error < 0) { + dev_err(dev, "gpio io setup failed (%d)\n", error); + goto error; + } + + spin_lock_init(&gpio->lock); + platform_set_drvdata(pdev, gpio); + + gpio->chip.base = pdata->start; + gpio->chip.ngpio = 4; + gpio->chip.dev = &pdev->dev; + gpio->chip.label = "ti_ssp_gpio"; + gpio->chip.owner = THIS_MODULE; + gpio->chip.get = value_get; + gpio->chip.set = value_set; + gpio->chip.direction_input = direction_in; + gpio->chip.direction_output = direction_out; + + error = gpiochip_add(&gpio->chip); + if (error < 0) { + dev_err(dev, "gpio chip registration failed (%d)\n", error); + goto error; + } + + dev_info(dev, "ssp gpio interface registered\n"); + return 0; + +error: + kfree(gpio); + return error; +} + +static int __devexit ti_ssp_gpio_remove(struct platform_device *pdev) +{ + struct ti_ssp_gpio_chip *gpio = platform_get_drvdata(pdev); + int error; + + error = gpiochip_remove(&gpio->chip); + if (error < 0) + return error; + kfree(gpio); + return 0; +} + +static struct platform_driver ti_ssp_gpio_driver = { + .probe = ti_ssp_gpio_probe, + .remove = __devexit_p(ti_ssp_gpio_remove), + .driver = { + .name = "ti-ssp-gpio", + .owner = THIS_MODULE, + }, +}; + +static int __init ti_ssp_gpio_init(void) +{ + return platform_driver_register(&ti_ssp_gpio_driver); +} +module_init(ti_ssp_gpio_init); + +static void __exit ti_ssp_gpio_exit(void) +{ + platform_driver_unregister(&ti_ssp_gpio_driver); +} +module_exit(ti_ssp_gpio_exit); + +MODULE_DESCRIPTION("GPIO interface for TI-SSP"); +MODULE_AUTHOR("Cyril Chemparathy "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ti-ssp-gpio"); diff --git a/include/linux/mfd/ti_ssp.h b/include/linux/mfd/ti_ssp.h index dbb4b43..10c65bb 100644 --- a/include/linux/mfd/ti_ssp.h +++ b/include/linux/mfd/ti_ssp.h @@ -38,6 +38,10 @@ struct ti_ssp_spi_data { void (*select)(int cs); }; +struct ti_ssp_gpio_data { + int start; +}; + /* * Sequencer port IO pin configuration bits. These do not correlate 1-1 with * the hardware. The iosel field in the port data combines iosel1 and iosel2, -- 1.7.1 From cyril at ti.com Tue Dec 7 08:51:54 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 7 Dec 2010 09:51:54 -0500 Subject: [PATCH v7 04/12] spi: add ti-ssp spi master driver In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <1291733522-3626-5-git-send-email-cyril@ti.com> This patch adds an SPI master implementation that operates on top of an underlying TI-SSP port. Signed-off-by: Cyril Chemparathy --- drivers/spi/Kconfig | 10 + drivers/spi/Makefile | 1 + drivers/spi/ti-ssp-spi.c | 402 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/ti_ssp.h | 6 + 4 files changed, 419 insertions(+), 0 deletions(-) create mode 100644 drivers/spi/ti-ssp-spi.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 78f9fd0..7f0ed2a 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -336,6 +336,16 @@ config SPI_TEGRA help SPI driver for NVidia Tegra SoCs +config SPI_TI_SSP + tristate "TI Sequencer Serial Port - SPI Support" + depends on MFD_TI_SSP + help + This selects an SPI master implementation using a TI sequencer + serial port. + + To compile this driver as a module, choose M here: the + module will be called ti-ssp-spi. + config SPI_TOPCLIFF_PCH tristate "Topcliff PCH SPI Controller" depends on PCI diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 8bc1a5a..595e5b8 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o obj-$(CONFIG_SPI_TEGRA) += spi_tegra.o +obj-$(CONFIG_SPI_TI_SSP) += ti-ssp-spi.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o diff --git a/drivers/spi/ti-ssp-spi.c b/drivers/spi/ti-ssp-spi.c new file mode 100644 index 0000000..30fc0e2 --- /dev/null +++ b/drivers/spi/ti-ssp-spi.c @@ -0,0 +1,402 @@ +/* + * Sequencer Serial Port (SSP) based SPI master driver + * + * 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; 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 + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MODE_BITS (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH) + +struct ti_ssp_spi { + const struct ti_ssp_spi_data *pdata; + struct spi_master *master; + struct device *dev; + spinlock_t lock; + struct list_head msg_queue; + struct completion complete; + int shutdown:1; + struct workqueue_struct *workqueue; + struct work_struct work; + u8 mode, bpw; + int cs_active; + u32 pc_en, pc_dis, pc_wr, pc_rd; +}; + +static u32 do_read_data(struct ti_ssp_spi *hw) +{ + u32 ret; + + ti_ssp_run(hw->dev, hw->pc_rd, 0, &ret); + return ret; +} + +static void do_write_data(struct ti_ssp_spi *hw, u32 data) +{ + ti_ssp_run(hw->dev, hw->pc_wr, data << (32 - hw->bpw), NULL); +} + +static int do_transfer(struct ti_ssp_spi *hw, struct spi_message *msg, + struct spi_transfer *t) +{ + int count; + + if (hw->bpw <= 8) { + u8 *rx = t->rx_buf; + const u8 *tx = t->tx_buf; + + for (count = 0; count < t->len; count += 1) { + if (t->tx_buf) + do_write_data(hw, *tx++); + if (t->rx_buf) + *rx++ = do_read_data(hw); + } + } else if (hw->bpw <= 16) { + u16 *rx = t->rx_buf; + const u16 *tx = t->tx_buf; + + for (count = 0; count < t->len; count += 2) { + if (t->tx_buf) + do_write_data(hw, *tx++); + if (t->rx_buf) + *rx++ = do_read_data(hw); + } + } else { + u32 *rx = t->rx_buf; + const u32 *tx = t->tx_buf; + + for (count = 0; count < t->len; count += 4) { + if (t->tx_buf) + do_write_data(hw, *tx++); + if (t->rx_buf) + *rx++ = do_read_data(hw); + } + } + + msg->actual_length += count; /* bytes transferred */ + + dev_dbg(&msg->spi->dev, "xfer %s%s, %d bytes, %d bpw, count %d%s\n", + t->tx_buf ? "tx" : "", t->rx_buf ? "rx" : "", t->len, + hw->bpw, count, (count < t->len) ? " (under)" : ""); + + return (count < t->len) ? -EIO : 0; /* left over data */ +} + +static void chip_select(struct ti_ssp_spi *hw, int cs_active) +{ + cs_active = !!cs_active; + if (cs_active == hw->cs_active) + return; + ti_ssp_run(hw->dev, cs_active ? hw->pc_en : hw->pc_dis, 0, NULL); + hw->cs_active = cs_active; +} + +#define __SHIFT_OUT(bits) (SSP_OPCODE_SHIFT | SSP_OUT_MODE | \ + cs_en | clk | SSP_COUNT((bits) * 2 - 1)) +#define __SHIFT_IN(bits) (SSP_OPCODE_SHIFT | SSP_IN_MODE | \ + cs_en | clk | SSP_COUNT((bits) * 2 - 1)) + +static int setup_xfer(struct ti_ssp_spi *hw, u8 bpw, u8 mode) +{ + int error, idx = 0; + u32 seqram[16]; + u32 cs_en, cs_dis, clk; + u32 topbits, botbits; + + mode &= MODE_BITS; + if (mode == hw->mode && bpw == hw->bpw) + return 0; + + cs_en = (mode & SPI_CS_HIGH) ? SSP_CS_HIGH : SSP_CS_LOW; + cs_dis = (mode & SPI_CS_HIGH) ? SSP_CS_LOW : SSP_CS_HIGH; + clk = (mode & SPI_CPOL) ? SSP_CLK_HIGH : SSP_CLK_LOW; + + /* Construct instructions */ + + /* Disable Chip Select */ + hw->pc_dis = idx; + seqram[idx++] = SSP_OPCODE_DIRECT | SSP_OUT_MODE | cs_dis | clk; + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_dis | clk; + + /* Enable Chip Select */ + hw->pc_en = idx; + seqram[idx++] = SSP_OPCODE_DIRECT | SSP_OUT_MODE | cs_en | clk; + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk; + + /* Reads and writes need to be split for bpw > 16 */ + topbits = (bpw > 16) ? 16 : bpw; + botbits = bpw - topbits; + + /* Write */ + hw->pc_wr = idx; + seqram[idx++] = __SHIFT_OUT(topbits) | SSP_ADDR_REG; + if (botbits) + seqram[idx++] = __SHIFT_OUT(botbits) | SSP_DATA_REG; + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk; + + /* Read */ + hw->pc_rd = idx; + if (botbits) + seqram[idx++] = __SHIFT_IN(botbits) | SSP_ADDR_REG; + seqram[idx++] = __SHIFT_IN(topbits) | SSP_DATA_REG; + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk; + + error = ti_ssp_load(hw->dev, 0, seqram, idx); + if (error < 0) + return error; + + error = ti_ssp_set_mode(hw->dev, ((mode & SPI_CPHA) ? + 0 : SSP_EARLY_DIN)); + if (error < 0) + return error; + + hw->bpw = bpw; + hw->mode = mode; + + return error; +} + +static void ti_ssp_spi_work(struct work_struct *work) +{ + struct ti_ssp_spi *hw = container_of(work, struct ti_ssp_spi, work); + + spin_lock(&hw->lock); + + while (!list_empty(&hw->msg_queue)) { + struct spi_message *m; + struct spi_device *spi; + struct spi_transfer *t = NULL; + int status = 0; + + m = container_of(hw->msg_queue.next, struct spi_message, + queue); + + list_del_init(&m->queue); + + spin_unlock(&hw->lock); + + spi = m->spi; + + if (hw->pdata->select) + hw->pdata->select(spi->chip_select); + + list_for_each_entry(t, &m->transfers, transfer_list) { + int bpw = spi->bits_per_word; + int xfer_status; + + if (t->bits_per_word) + bpw = t->bits_per_word; + + if (setup_xfer(hw, bpw, spi->mode) < 0) + break; + + chip_select(hw, 1); + + xfer_status = do_transfer(hw, m, t); + if (xfer_status < 0) + status = xfer_status; + + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (t->cs_change) + chip_select(hw, 0); + } + + chip_select(hw, 0); + m->status = status; + m->complete(m->context); + + spin_lock(&hw->lock); + } + + if (hw->shutdown) + complete(&hw->complete); + + spin_unlock(&hw->lock); +} + +static int ti_ssp_spi_setup(struct spi_device *spi) +{ + if (spi->bits_per_word > 32) + return -EINVAL; + + return 0; +} + +static int ti_ssp_spi_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct ti_ssp_spi *hw; + struct spi_transfer *t; + int error = 0; + + m->actual_length = 0; + m->status = -EINPROGRESS; + + hw = spi_master_get_devdata(spi->master); + + if (list_empty(&m->transfers) || !m->complete) + return -EINVAL; + + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->len && !(t->rx_buf || t->tx_buf)) { + dev_err(&spi->dev, "invalid xfer, no buffer\n"); + return -EINVAL; + } + + if (t->len && t->rx_buf && t->tx_buf) { + dev_err(&spi->dev, "invalid xfer, full duplex\n"); + return -EINVAL; + } + + if (t->bits_per_word > 32) { + dev_err(&spi->dev, "invalid xfer width %d\n", + t->bits_per_word); + return -EINVAL; + } + } + + spin_lock(&hw->lock); + if (hw->shutdown) { + error = -ESHUTDOWN; + goto error_unlock; + } + list_add_tail(&m->queue, &hw->msg_queue); + queue_work(hw->workqueue, &hw->work); +error_unlock: + spin_unlock(&hw->lock); + return error; +} + +static int __devinit ti_ssp_spi_probe(struct platform_device *pdev) +{ + const struct ti_ssp_spi_data *pdata; + struct ti_ssp_spi *hw; + struct spi_master *master; + struct device *dev = &pdev->dev; + int error = 0; + + pdata = dev->platform_data; + if (!pdata) { + dev_err(dev, "platform data not found\n"); + return -EINVAL; + } + + master = spi_alloc_master(dev, sizeof(struct ti_ssp_spi)); + if (!master) { + dev_err(dev, "cannot allocate SPI master\n"); + return -ENOMEM; + } + + hw = spi_master_get_devdata(master); + platform_set_drvdata(pdev, hw); + + hw->master = master; + hw->dev = dev; + hw->pdata = pdata; + + spin_lock_init(&hw->lock); + init_completion(&hw->complete); + INIT_LIST_HEAD(&hw->msg_queue); + INIT_WORK(&hw->work, ti_ssp_spi_work); + + hw->workqueue = create_singlethread_workqueue(dev_name(dev)); + if (!hw->workqueue) { + error = -ENOMEM; + dev_err(dev, "work queue creation failed\n"); + goto error_wq; + } + + error = ti_ssp_set_iosel(hw->dev, hw->pdata->iosel); + if (error < 0) { + dev_err(dev, "io setup failed\n"); + goto error_iosel; + } + + master->bus_num = pdev->id; + master->num_chipselect = hw->pdata->num_cs; + master->mode_bits = MODE_BITS; + master->flags = SPI_MASTER_HALF_DUPLEX; + master->setup = ti_ssp_spi_setup; + master->transfer = ti_ssp_spi_transfer; + + error = spi_register_master(master); + if (error) { + dev_err(dev, "master registration failed\n"); + goto error_reg; + } + + return 0; + +error_reg: +error_iosel: + destroy_workqueue(hw->workqueue); +error_wq: + spi_master_put(master); + return error; +} + +static int __devexit ti_ssp_spi_remove(struct platform_device *pdev) +{ + struct ti_ssp_spi *hw = platform_get_drvdata(pdev); + int error; + + hw->shutdown = 1; + while (!list_empty(&hw->msg_queue)) { + error = wait_for_completion_interruptible(&hw->complete); + if (error < 0) { + hw->shutdown = 0; + return error; + } + } + destroy_workqueue(hw->workqueue); + spi_unregister_master(hw->master); + + return 0; +} + +static struct platform_driver ti_ssp_spi_driver = { + .probe = ti_ssp_spi_probe, + .remove = __devexit_p(ti_ssp_spi_remove), + .driver = { + .name = "ti-ssp-spi", + .owner = THIS_MODULE, + }, +}; + +static int __init ti_ssp_spi_init(void) +{ + return platform_driver_register(&ti_ssp_spi_driver); +} +module_init(ti_ssp_spi_init); + +static void __exit ti_ssp_spi_exit(void) +{ + platform_driver_unregister(&ti_ssp_spi_driver); +} +module_exit(ti_ssp_spi_exit); + +MODULE_DESCRIPTION("SSP SPI Master"); +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ti-ssp-spi"); diff --git a/include/linux/mfd/ti_ssp.h b/include/linux/mfd/ti_ssp.h index 021fe09..dbb4b43 100644 --- a/include/linux/mfd/ti_ssp.h +++ b/include/linux/mfd/ti_ssp.h @@ -32,6 +32,12 @@ struct ti_ssp_data { struct ti_ssp_dev_data dev_data[2]; }; +struct ti_ssp_spi_data { + unsigned long iosel; + int num_cs; + void (*select)(int cs); +}; + /* * Sequencer port IO pin configuration bits. These do not correlate 1-1 with * the hardware. The iosel field in the port data combines iosel1 and iosel2, -- 1.7.1 From cyril at ti.com Tue Dec 7 08:51:55 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 7 Dec 2010 09:51:55 -0500 Subject: [PATCH v7 05/12] davinci: add spi devices on tnetv107x evm In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <1291733522-3626-6-git-send-email-cyril@ti.com> This patch adds definitions for spi devices on the tnetv107x evm platform. Signed-off-by: Cyril Chemparathy --- arch/arm/mach-davinci/board-tnetv107x-evm.c | 43 +++++++++++++++++++++++++++ 1 files changed, 43 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-tnetv107x-evm.c b/arch/arm/mach-davinci/board-tnetv107x-evm.c index ef526b1..1a656e8 100644 --- a/arch/arm/mach-davinci/board-tnetv107x-evm.c +++ b/arch/arm/mach-davinci/board-tnetv107x-evm.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -37,6 +38,7 @@ #define EVM_MMC_WP_GPIO 21 #define EVM_MMC_CD_GPIO 24 +#define EVM_SPI_CS_GPIO 54 static int initialize_gpio(int gpio, char *desc) { @@ -202,9 +204,45 @@ static struct matrix_keypad_platform_data keypad_config = { .no_autorepeat = 0, }; +static void spi_select_device(int cs) +{ + static int gpio; + + if (!gpio) { + int ret; + ret = gpio_request(EVM_SPI_CS_GPIO, "spi chipsel"); + if (ret < 0) { + pr_err("cannot open spi chipsel gpio\n"); + gpio = -ENOSYS; + return; + } else { + gpio = EVM_SPI_CS_GPIO; + gpio_direction_output(gpio, 0); + } + } + + if (gpio < 0) + return; + + return gpio_set_value(gpio, cs ? 1 : 0); +} + +static struct ti_ssp_spi_data spi_master_data = { + .num_cs = 2, + .select = spi_select_device, + .iosel = SSP_PIN_SEL(0, SSP_CLOCK) | SSP_PIN_SEL(1, SSP_DATA) | + SSP_PIN_SEL(2, SSP_CHIPSEL) | SSP_PIN_SEL(3, SSP_IN) | + SSP_INPUT_SEL(3), +}; + static struct ti_ssp_data ssp_config = { .out_clock = 250 * 1000, .dev_data = { + [1] = { + .dev_name = "ti-ssp-spi", + .pdata = &spi_master_data, + .pdata_size = sizeof(spi_master_data), + }, }, }; @@ -216,6 +254,9 @@ static struct tnetv107x_device_info evm_device_info __initconst = { .ssp_config = &ssp_config, }; +static struct spi_board_info spi_info[] __initconst = { +}; + static __init void tnetv107x_evm_board_init(void) { davinci_cfg_reg_list(sdio1_pins); @@ -223,6 +264,8 @@ static __init void tnetv107x_evm_board_init(void) davinci_cfg_reg_list(ssp_pins); tnetv107x_devices_init(&evm_device_info); + + spi_register_board_info(spi_info, ARRAY_SIZE(spi_info)); } #ifdef CONFIG_SERIAL_8250_CONSOLE -- 1.7.1 From cyril at ti.com Tue Dec 7 08:51:57 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 7 Dec 2010 09:51:57 -0500 Subject: [PATCH v7 07/12] davinci: add tnetv107x evm regulators In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <1291733522-3626-8-git-send-email-cyril@ti.com> This patch adds regulator and spi board info definitions for the tps6524x power management IC found on tnetv107x evm boards. Signed-off-by: Cyril Chemparathy --- arch/arm/mach-davinci/board-tnetv107x-evm.c | 85 +++++++++++++++++++++++++++ 1 files changed, 85 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-tnetv107x-evm.c b/arch/arm/mach-davinci/board-tnetv107x-evm.c index 1a656e8..ca23516 100644 --- a/arch/arm/mach-davinci/board-tnetv107x-evm.c +++ b/arch/arm/mach-davinci/board-tnetv107x-evm.c @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #include #include @@ -254,7 +257,89 @@ static struct tnetv107x_device_info evm_device_info __initconst = { .ssp_config = &ssp_config, }; +static struct regulator_consumer_supply usb_consumers[] = { + REGULATOR_SUPPLY("vbus", "musb_hdrc.1"), +}; + +static struct regulator_consumer_supply lcd_consumers[] = { + REGULATOR_SUPPLY("vlcd", "tps6116x"), +}; + +static struct regulator_init_data regulators[] = { + { + .constraints = { + .name = "DCDC1", + .min_uV = 1000000, + .max_uV = 1000000, + .always_on = 1, + .boot_on = 1, + }, + }, + { + .constraints = { + .name = "DCDC2", + .min_uV = 1800000, + .max_uV = 1800000, + .always_on = 1, + .boot_on = 1, + }, + }, + { + .constraints = { + .name = "DCDC3", + .min_uV = 3300000, + .max_uV = 3300000, + .always_on = 1, + .boot_on = 1, + }, + }, + { + .constraints = { + .name = "LDO1", + .min_uV = 4800000, + .max_uV = 4800000, + .always_on = 1, + .boot_on = 1, + }, + }, + { + .constraints = { + .name = "LDO1", + .min_uV = 3300000, + .max_uV = 3300000, + .always_on = 1, + .boot_on = 1, + }, + }, + { + .num_consumer_supplies = ARRAY_SIZE(usb_consumers), + .consumer_supplies = usb_consumers, + .constraints = { + .name = "USB", + .min_uA = 200000, + .max_uA = 1000000, + .valid_ops_mask = REGULATOR_CHANGE_CURRENT | + REGULATOR_CHANGE_STATUS, + }, + }, + { + .num_consumer_supplies = ARRAY_SIZE(lcd_consumers), + .consumer_supplies = lcd_consumers, + .constraints = { + .name = "LCD", + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + }, + }, +}; + static struct spi_board_info spi_info[] __initconst = { + { + .modalias = "tps6524x", + .bus_num = 1, + .chip_select = 0, + .mode = SPI_MODE_0, + .platform_data = regulators, + }, }; static __init void tnetv107x_evm_board_init(void) -- 1.7.1 From cyril at ti.com Tue Dec 7 08:52:00 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 7 Dec 2010 09:52:00 -0500 Subject: [PATCH v7 10/12] backlight: add support for tps6116x controller In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <1291733522-3626-11-git-send-email-cyril@ti.com> TPS6116x is an EasyScale backlight controller device. This driver supports TPS6116x devices connected on a single GPIO. Signed-off-by: Cyril Chemparathy --- drivers/video/backlight/Kconfig | 7 + drivers/video/backlight/Makefile | 2 +- drivers/video/backlight/tps6116x.c | 299 ++++++++++++++++++++++++++++++++++++ 3 files changed, 307 insertions(+), 1 deletions(-) create mode 100644 drivers/video/backlight/tps6116x.c diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index e54a337..06e868e 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -307,6 +307,13 @@ config BACKLIGHT_PCF50633 If you have a backlight driven by a NXP PCF50633 MFD, say Y here to enable its driver. +config BACKLIGHT_TPS6116X + tristate "TPS6116X LCD Backlight" + depends on GENERIC_GPIO + help + This driver controls the LCD backlight level for EasyScale capable + SSP connected backlight controllers. + endif # BACKLIGHT_CLASS_DEVICE endif # BACKLIGHT_LCD_SUPPORT diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 44c0f81..5d407c8 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -35,4 +35,4 @@ obj-$(CONFIG_BACKLIGHT_ADP5520) += adp5520_bl.o obj-$(CONFIG_BACKLIGHT_ADP8860) += adp8860_bl.o obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o - +obj-$(CONFIG_BACKLIGHT_TPS6116X)+= tps6116x.o diff --git a/drivers/video/backlight/tps6116x.c b/drivers/video/backlight/tps6116x.c new file mode 100644 index 0000000..7f846ab --- /dev/null +++ b/drivers/video/backlight/tps6116x.c @@ -0,0 +1,299 @@ +/* + * TPS6116X LCD Backlight Controller Driver + * + * Copyright (C) 2010 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TPS6116X_MAX_INTENSITY 31 +#define TPS6116X_DEFAULT_INTENSITY 10 + +/* Easyscale timing w/ margin (usecs) */ +#define T_POWER_SETTLE 2000 +#define T_ES_DELAY 120 +#define T_ES_DETECT 280 +#define T_ES_WINDOW (1000 - T_ES_DELAY - T_ES_DETECT) +#define T_START 3 +#define T_EOS 3 +#define T_INACTIVE 3 +#define T_ACTIVE (3 * T_INACTIVE) + +#define CMD_SET 0x72 + +struct tps6116x { + struct ti_ssp_device *handle; + struct device *dev; + int gpio; + struct mutex lock; + int intensity; + struct backlight_properties props; + struct backlight_device *bl; + struct regulator *regulator; + bool power; + bool suspended; +}; + +static int __set_power(struct tps6116x *hw, bool power) +{ + unsigned long flags; + int error; + + if (power == hw->power) + return 0; /* nothing to do */ + + /* disabling is simple... choke power */ + if (!power) { + error = regulator_disable(hw->regulator); + goto done; + } + + /* set ctrl pin init state for easyscale detection */ + gpio_set_value(hw->gpio, 0); + + error = regulator_enable(hw->regulator); + if (error < 0) + goto done; + + udelay(T_POWER_SETTLE); + + /* + * Now that the controller is powered up, we need to put it into 1-wire + * mode. This is a timing sensitive operation, hence the irq disable. + * Ideally, this should happen rarely, and mostly at init, so disabling + * interrupts for the duration should not be a problem. + */ + local_irq_save(flags); + + gpio_set_value(hw->gpio, 1); + udelay(T_ES_DELAY); + gpio_set_value(hw->gpio, 0); + udelay(T_ES_DETECT); + gpio_set_value(hw->gpio, 1); + + local_irq_restore(flags); + +done: + if (error >= 0) + hw->power = power; + + return error; +} + +static void __write_byte(struct tps6116x *hw, u8 data) +{ + int bit; + + gpio_set_value(hw->gpio, 1); + udelay(T_START); + + for (bit = 0; bit < 8; bit++, data <<= 1) { + int val = data & 0x80; + int t_lo = val ? T_INACTIVE : T_ACTIVE; + int t_hi = val ? T_ACTIVE : T_INACTIVE; + + gpio_set_value(hw->gpio, 0); + udelay(t_lo); + gpio_set_value(hw->gpio, 1); + udelay(t_hi); + } + + gpio_set_value(hw->gpio, 0); + udelay(T_EOS); + gpio_set_value(hw->gpio, 1); +} + +static void __set_intensity(struct tps6116x *hw, int intensity) +{ + unsigned long flags; + + intensity = clamp(intensity, 0, TPS6116X_MAX_INTENSITY); + + local_irq_save(flags); + __write_byte(hw, CMD_SET); + __write_byte(hw, intensity); + local_irq_restore(flags); +} + +static int set_intensity(struct tps6116x *hw, int intensity) +{ + int error = 0; + + if (intensity == hw->intensity) + return 0; + + mutex_lock(&hw->lock); + + error = __set_power(hw, intensity ? true : false); + if (error < 0) + goto error; + + if (intensity > 0) + __set_intensity(hw, intensity); + + hw->intensity = intensity; +error: + mutex_unlock(&hw->lock); + + return error; +} + +static int get_brightness(struct backlight_device *bl) +{ + struct tps6116x *hw = bl_get_data(bl); + + return hw->intensity; +} + +static int update_status(struct backlight_device *bl) +{ + struct tps6116x *hw = bl_get_data(bl); + int intensity = bl->props.brightness; + + if (hw->suspended || hw->props.state & BL_CORE_SUSPENDED) + intensity = 0; + if (bl->props.power != FB_BLANK_UNBLANK) + intensity = 0; + if (bl->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + + return set_intensity(hw, intensity); +} + +static const struct backlight_ops tps6116x_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, + .get_brightness = get_brightness, + .update_status = update_status, +}; + +static int __devinit tps6116x_probe(struct platform_device *pdev) +{ + struct tps6116x *hw; + struct device *dev = &pdev->dev; + struct backlight_properties props; + int error; + + hw = kzalloc(sizeof(struct tps6116x), GFP_KERNEL); + if (!hw) { + dev_err(dev, "cannot allocate driver data\n"); + return -ENOMEM; + } + platform_set_drvdata(pdev, hw); + + hw->gpio = (int)dev->platform_data; + hw->dev = dev; + + mutex_init(&hw->lock); + + hw->regulator = regulator_get(dev, "vlcd"); + if (IS_ERR(hw->regulator)) { + error = PTR_ERR(hw->regulator); + dev_err(dev, "cannot claim regulator\n"); + goto error_regulator; + } + + error = gpio_request_one(hw->gpio, GPIOF_DIR_OUT, dev_name(dev)); + if (error < 0) { + dev_err(dev, "cannot claim gpio\n"); + goto error_gpio; + } + + memset(&props, 0, sizeof(props)); + props.max_brightness = TPS6116X_MAX_INTENSITY; + props.brightness = TPS6116X_DEFAULT_INTENSITY; + props.power = FB_BLANK_UNBLANK; + + hw->bl = backlight_device_register("tps6116x", hw->dev, hw, + &tps6116x_backlight_ops, &props); + if (IS_ERR(hw->bl)) { + error = PTR_ERR(hw->bl); + dev_err(dev, "backlight registration failed\n"); + goto error_register; + } + + update_status(hw->bl); + dev_info(dev, "registered backlight controller\n"); + return 0; + +error_register: + gpio_free(hw->gpio); +error_gpio: + regulator_put(hw->regulator); +error_regulator: + kfree(hw); + platform_set_drvdata(pdev, NULL); + return error; +} + +static int __devexit tps6116x_remove(struct platform_device *pdev) +{ + struct tps6116x *hw = platform_get_drvdata(pdev); + + backlight_device_unregister(hw->bl); + regulator_disable(hw->regulator); + regulator_put(hw->regulator); + gpio_free(hw->gpio); + kfree(hw); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static int tps6116x_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct tps6116x *hw = platform_get_drvdata(pdev); + hw->suspended = true; + update_status(hw->bl); + return 0; +} + +static int tps6116x_resume(struct platform_device *pdev) +{ + struct tps6116x *hw = platform_get_drvdata(pdev); + hw->suspended = false; + update_status(hw->bl); + return 0; +} + +static struct platform_driver tps6116x_driver = { + .probe = tps6116x_probe, + .remove = __devexit_p(tps6116x_remove), + .suspend = tps6116x_suspend, + .resume = tps6116x_resume, + .driver = { + .name = "tps6116x", + .owner = THIS_MODULE, + }, +}; + +static int __init tps6116x_init(void) +{ + return platform_driver_register(&tps6116x_driver); +} +module_init(tps6116x_init); + +static void __exit tps6116x_exit(void) +{ + platform_driver_unregister(&tps6116x_driver); +} +module_exit(tps6116x_exit); + +MODULE_DESCRIPTION("SSP TPS6116X Driver"); +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:tps6116x"); -- 1.7.1 From cyril at ti.com Tue Dec 7 08:51:59 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 7 Dec 2010 09:51:59 -0500 Subject: [PATCH v7 09/12] davinci: add tnetv107x evm ti-ssp gpio device In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <1291733522-3626-10-git-send-email-cyril@ti.com> This patch adds definitions to hook up one of the ti-ssp ports to the SSP GPIO driver. Signed-off-by: Cyril Chemparathy --- arch/arm/mach-davinci/board-tnetv107x-evm.c | 11 +++++++++++ 1 files changed, 11 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-tnetv107x-evm.c b/arch/arm/mach-davinci/board-tnetv107x-evm.c index ca23516..e3863dd 100644 --- a/arch/arm/mach-davinci/board-tnetv107x-evm.c +++ b/arch/arm/mach-davinci/board-tnetv107x-evm.c @@ -39,6 +39,8 @@ #include #include +#define SSP_GPIO_START 128 + #define EVM_MMC_WP_GPIO 21 #define EVM_MMC_CD_GPIO 24 #define EVM_SPI_CS_GPIO 54 @@ -238,9 +240,18 @@ static struct ti_ssp_spi_data spi_master_data = { SSP_INPUT_SEL(3), }; +static struct ti_ssp_gpio_data ssp_gpio_data = { + .start = SSP_GPIO_START, +}; + static struct ti_ssp_data ssp_config = { .out_clock = 250 * 1000, .dev_data = { + [0] = { + .dev_name = "ti-ssp-gpio", + .pdata = &ssp_gpio_data, + .pdata_size = sizeof(ssp_gpio_data), + }, [1] = { .dev_name = "ti-ssp-spi", .pdata = &spi_master_data, -- 1.7.1 From cyril at ti.com Tue Dec 7 08:52:02 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 7 Dec 2010 09:52:02 -0500 Subject: [PATCH v7 12/12] davinci: add tnetv107x evm i2c eeprom device In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <1291733522-3626-13-git-send-email-cyril@ti.com> The tnetv107x evm board has an I2C device connected on one of the SSP ports. This patch adds board definitions for a GPIO based I2C master, as well as definitions for the eeprom device on these boards. Signed-off-by: Cyril Chemparathy --- arch/arm/mach-davinci/board-tnetv107x-evm.c | 30 +++++++++++++++++++++++++++ 1 files changed, 30 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-tnetv107x-evm.c b/arch/arm/mach-davinci/board-tnetv107x-evm.c index ac62de2..869af15 100644 --- a/arch/arm/mach-davinci/board-tnetv107x-evm.c +++ b/arch/arm/mach-davinci/board-tnetv107x-evm.c @@ -29,6 +29,9 @@ #include #include #include +#include +#include +#include #include #include @@ -44,6 +47,8 @@ #define EVM_MMC_WP_GPIO 21 #define EVM_MMC_CD_GPIO 24 #define EVM_SPI_CS_GPIO 54 +#define EVM_I2C_SDA_GPIO (SSP_GPIO_START + 0) +#define EVM_I2C_SCL_GPIO (SSP_GPIO_START + 1) #define EVM_BACKLIGHT_GPIO (SSP_GPIO_START + 2) static int initialize_gpio(int gpio, char *desc) @@ -360,6 +365,29 @@ static struct platform_device backlight_device = { .dev.platform_data = (void *)EVM_BACKLIGHT_GPIO, }; +struct i2c_gpio_platform_data i2c_data = { + .sda_pin = EVM_I2C_SDA_GPIO, + .scl_pin = EVM_I2C_SCL_GPIO, +}; + +static struct platform_device i2c_device = { + .name = "i2c-gpio", + .id = 0, + .dev.platform_data = &i2c_data, +}; + +static struct at24_platform_data at24_config = { + .byte_len = SZ_16K / 8, + .page_size = 16, +}; + +static struct i2c_board_info i2c_info[] __initconst = { + { + I2C_BOARD_INFO("24c16", 0x50), + .platform_data = &at24_config, + }, +}; + static __init void tnetv107x_evm_board_init(void) { davinci_cfg_reg_list(sdio1_pins); @@ -369,11 +397,13 @@ static __init void tnetv107x_evm_board_init(void) tnetv107x_devices_init(&evm_device_info); spi_register_board_info(spi_info, ARRAY_SIZE(spi_info)); + i2c_register_board_info(0, i2c_info, ARRAY_SIZE(i2c_info)); } static int __init tnetv107x_evm_late_init(void) { platform_device_register(&backlight_device); + platform_device_register(&i2c_device); return 0; } late_initcall(tnetv107x_evm_late_init); -- 1.7.1 From cyril at ti.com Tue Dec 7 08:52:01 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 7 Dec 2010 09:52:01 -0500 Subject: [PATCH v7 11/12] davinci: add tnetv107x evm backlight device In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <1291733522-3626-12-git-send-email-cyril@ti.com> The tnetv107x evm board has a backlight device that is connected on one of the SSP ports. This patch adds the board definitions necessary to plug the backlight driver to the GPIO corresponding to this SSP pin. Signed-off-by: Cyril Chemparathy --- arch/arm/mach-davinci/board-tnetv107x-evm.c | 14 ++++++++++++++ 1 files changed, 14 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-tnetv107x-evm.c b/arch/arm/mach-davinci/board-tnetv107x-evm.c index e3863dd..ac62de2 100644 --- a/arch/arm/mach-davinci/board-tnetv107x-evm.c +++ b/arch/arm/mach-davinci/board-tnetv107x-evm.c @@ -44,6 +44,7 @@ #define EVM_MMC_WP_GPIO 21 #define EVM_MMC_CD_GPIO 24 #define EVM_SPI_CS_GPIO 54 +#define EVM_BACKLIGHT_GPIO (SSP_GPIO_START + 2) static int initialize_gpio(int gpio, char *desc) { @@ -353,6 +354,12 @@ static struct spi_board_info spi_info[] __initconst = { }, }; +static struct platform_device backlight_device = { + .name = "tps6116x", + .id = -1, + .dev.platform_data = (void *)EVM_BACKLIGHT_GPIO, +}; + static __init void tnetv107x_evm_board_init(void) { davinci_cfg_reg_list(sdio1_pins); @@ -364,6 +371,13 @@ static __init void tnetv107x_evm_board_init(void) spi_register_board_info(spi_info, ARRAY_SIZE(spi_info)); } +static int __init tnetv107x_evm_late_init(void) +{ + platform_device_register(&backlight_device); + return 0; +} +late_initcall(tnetv107x_evm_late_init); + #ifdef CONFIG_SERIAL_8250_CONSOLE static int __init tnetv107x_evm_console_init(void) { -- 1.7.1 From broonie at opensource.wolfsonmicro.com Tue Dec 7 09:22:22 2010 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Tue, 7 Dec 2010 15:22:22 +0000 Subject: [PATCH v7 06/12] regulator: add driver for tps6524x regulator In-Reply-To: <1291733522-3626-7-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> <1291733522-3626-7-git-send-email-cyril@ti.com> Message-ID: <20101207152222.GD9689@rakim.wolfsonmicro.main> On Tue, Dec 07, 2010 at 09:51:56AM -0500, Cyril Chemparathy wrote: > +static int set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) > +{ Updates in the regulator tree mean that this will no longer build in -next. Given the difficulty that this series seems to be having perhaps it would make sense to split out drivers such as this which have nothing to do with SSP itself and get them into the next merge window? From cyril at ti.com Tue Dec 7 10:46:53 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 07 Dec 2010 11:46:53 -0500 Subject: [PATCH v7 06/12] regulator: add driver for tps6524x regulator In-Reply-To: <20101207152222.GD9689@rakim.wolfsonmicro.main> References: <1291733522-3626-1-git-send-email-cyril@ti.com> <1291733522-3626-7-git-send-email-cyril@ti.com> <20101207152222.GD9689@rakim.wolfsonmicro.main> Message-ID: <4CFE64FD.8000809@ti.com> On 12/07/2010 10:22 AM, Mark Brown wrote: > On Tue, Dec 07, 2010 at 09:51:56AM -0500, Cyril Chemparathy wrote: > >> +static int set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) >> +{ > > Updates in the regulator tree mean that this will no longer build in > -next. Given the difficulty that this series seems to be having > perhaps it would make sense to split out drivers such as this which have > nothing to do with SSP itself and get them into the next merge window? Agreed. I will split this series up and repost. From cyril at ti.com Tue Dec 7 11:04:11 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 7 Dec 2010 12:04:11 -0500 Subject: [PATCH] regulator: add driver for tps6524x regulator Message-ID: <1291741451-17549-1-git-send-email-cyril@ti.com> TPS6524X provides three step-down converters and two general-purpose LDO voltage regulators. This device is interfaced using SPI. Signed-off-by: Cyril Chemparathy --- This patch was tested against davinci/master merged with regulator/for-next, with a pile of tnetv107x specific patches on top. drivers/regulator/Kconfig | 10 + drivers/regulator/Makefile | 1 + drivers/regulator/tps6524x-regulator.c | 693 ++++++++++++++++++++++++++++++++ 3 files changed, 704 insertions(+), 0 deletions(-) create mode 100644 drivers/regulator/tps6524x-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index dd30e88..da34981 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -250,5 +250,15 @@ config REGULATOR_TPS6586X help This driver supports TPS6586X voltage regulator chips. +config REGULATOR_TPS6524X + tristate "TI TPS6524X Power regulators" + depends on SPI + help + This driver supports TPS6524X voltage regulator chips. TPS6524X + provides three step-down converters and two general-purpose LDO + voltage regulators. This device is interfaced using a customized + serial interface currently supported on the sequencer serial + port controller. + endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index bff8157..cf71df7 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o +obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c new file mode 100644 index 0000000..176a6be --- /dev/null +++ b/drivers/regulator/tps6524x-regulator.c @@ -0,0 +1,693 @@ +/* + * Regulator driver for TPS6524x PMIC + * + * Copyright (C) 2010 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_LDO_SET 0x0 +#define LDO_ILIM_MASK 1 /* 0 = 400-800, 1 = 900-1500 */ +#define LDO_VSEL_MASK 0x0f +#define LDO2_ILIM_SHIFT 12 +#define LDO2_VSEL_SHIFT 4 +#define LDO1_ILIM_SHIFT 8 +#define LDO1_VSEL_SHIFT 0 + +#define REG_BLOCK_EN 0x1 +#define BLOCK_MASK 1 +#define BLOCK_LDO1_SHIFT 0 +#define BLOCK_LDO2_SHIFT 1 +#define BLOCK_LCD_SHIFT 2 +#define BLOCK_USB_SHIFT 3 + +#define REG_DCDC_SET 0x2 +#define DCDC_VDCDC_MASK 0x1f +#define DCDC_VDCDC1_SHIFT 0 +#define DCDC_VDCDC2_SHIFT 5 +#define DCDC_VDCDC3_SHIFT 10 + +#define REG_DCDC_EN 0x3 +#define DCDCDCDC_EN_MASK 0x1 +#define DCDCDCDC1_EN_SHIFT 0 +#define DCDCDCDC1_PG_MSK BIT(1) +#define DCDCDCDC2_EN_SHIFT 2 +#define DCDCDCDC2_PG_MSK BIT(3) +#define DCDCDCDC3_EN_SHIFT 4 +#define DCDCDCDC3_PG_MSK BIT(5) + +#define REG_USB 0x4 +#define USB_ILIM_SHIFT 0 +#define USB_ILIM_MASK 0x3 +#define USB_TSD_SHIFT 2 +#define USB_TSD_MASK 0x3 +#define USB_TWARN_SHIFT 4 +#define USB_TWARN_MASK 0x3 +#define USB_IWARN_SD BIT(6) +#define USB_FAST_LOOP BIT(7) + +#define REG_ALARM 0x5 +#define ALARM_LDO1 BIT(0) +#define ALARM_DCDC1 BIT(1) +#define ALARM_DCDC2 BIT(2) +#define ALARM_DCDC3 BIT(3) +#define ALARM_LDO2 BIT(4) +#define ALARM_USB_WARN BIT(5) +#define ALARM_USB_ALARM BIT(6) +#define ALARM_LCD BIT(9) +#define ALARM_TEMP_WARM BIT(10) +#define ALARM_TEMP_HOT BIT(11) +#define ALARM_NRST BIT(14) +#define ALARM_POWERUP BIT(15) + +#define REG_INT_ENABLE 0x6 +#define INT_LDO1 BIT(0) +#define INT_DCDC1 BIT(1) +#define INT_DCDC2 BIT(2) +#define INT_DCDC3 BIT(3) +#define INT_LDO2 BIT(4) +#define INT_USB_WARN BIT(5) +#define INT_USB_ALARM BIT(6) +#define INT_LCD BIT(9) +#define INT_TEMP_WARM BIT(10) +#define INT_TEMP_HOT BIT(11) +#define INT_GLOBAL_EN BIT(15) + +#define REG_INT_STATUS 0x7 +#define STATUS_LDO1 BIT(0) +#define STATUS_DCDC1 BIT(1) +#define STATUS_DCDC2 BIT(2) +#define STATUS_DCDC3 BIT(3) +#define STATUS_LDO2 BIT(4) +#define STATUS_USB_WARN BIT(5) +#define STATUS_USB_ALARM BIT(6) +#define STATUS_LCD BIT(9) +#define STATUS_TEMP_WARM BIT(10) +#define STATUS_TEMP_HOT BIT(11) + +#define REG_SOFTWARE_RESET 0xb +#define REG_WRITE_ENABLE 0xd +#define REG_REV_ID 0xf + +#define N_DCDC 3 +#define N_LDO 2 +#define N_SWITCH 2 +#define N_REGULATORS (3 /* DCDC */ + \ + 2 /* LDO */ + \ + 2 /* switch */) + +#define FIXED_ILIMSEL BIT(0) +#define FIXED_VOLTAGE BIT(1) + +#define CMD_READ(reg) ((reg) << 6) +#define CMD_WRITE(reg) (BIT(5) | (reg) << 6) +#define STAT_CLK BIT(3) +#define STAT_WRITE BIT(2) +#define STAT_INVALID BIT(1) +#define STAT_WP BIT(0) + +struct field { + int reg; + int shift; + int mask; +}; + +struct supply_info { + const char *name; + int n_voltages; + const int *voltages; + int fixed_voltage; + int n_ilimsels; + const int *ilimsels; + int fixed_ilimsel; + int flags; + struct field enable, voltage, ilimsel; +}; + +struct tps6524x { + struct device *dev; + struct spi_device *spi; + struct mutex lock; + struct regulator_desc desc[N_REGULATORS]; + struct regulator_dev *rdev[N_REGULATORS]; +}; + +static int __read_reg(struct tps6524x *hw, int reg) +{ + int error = 0; + u16 cmd = CMD_READ(reg), in; + u8 status; + struct spi_message m; + struct spi_transfer t[3]; + + spi_message_init(&m); + memset(t, 0, sizeof(t)); + + t[0].tx_buf = &cmd; + t[0].len = 2; + t[0].bits_per_word = 12; + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = ∈ + t[1].len = 2; + t[1].bits_per_word = 16; + spi_message_add_tail(&t[1], &m); + + t[2].rx_buf = &status; + t[2].len = 1; + t[2].bits_per_word = 4; + spi_message_add_tail(&t[2], &m); + + error = spi_sync(hw->spi, &m); + if (error < 0) + return error; + + dev_dbg(hw->dev, "read reg %d, data %x, status %x\n", + reg, in, status); + + if (!(status & STAT_CLK) || (status & STAT_WRITE)) + return -EIO; + + if (status & STAT_INVALID) + return -EINVAL; + + return in; +} + +static int read_reg(struct tps6524x *hw, int reg) +{ + int ret; + + mutex_lock(&hw->lock); + ret = __read_reg(hw, reg); + mutex_unlock(&hw->lock); + + return ret; +} + +static int __write_reg(struct tps6524x *hw, int reg, int val) +{ + int error = 0; + u16 cmd = CMD_WRITE(reg), out = val; + u8 status; + struct spi_message m; + struct spi_transfer t[3]; + + spi_message_init(&m); + memset(t, 0, sizeof(t)); + + t[0].tx_buf = &cmd; + t[0].len = 2; + t[0].bits_per_word = 12; + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = &out; + t[1].len = 2; + t[1].bits_per_word = 16; + spi_message_add_tail(&t[1], &m); + + t[2].rx_buf = &status; + t[2].len = 1; + t[2].bits_per_word = 4; + spi_message_add_tail(&t[2], &m); + + error = spi_sync(hw->spi, &m); + if (error < 0) + return error; + + dev_dbg(hw->dev, "wrote reg %d, data %x, status %x\n", + reg, out, status); + + if (!(status & STAT_CLK) || !(status & STAT_WRITE)) + return -EIO; + + if (status & (STAT_INVALID | STAT_WP)) + return -EINVAL; + + return error; +} + +static int __rmw_reg(struct tps6524x *hw, int reg, int mask, int val) +{ + int ret; + + ret = __read_reg(hw, reg); + if (ret < 0) + return ret; + + ret &= ~mask; + ret |= val; + + ret = __write_reg(hw, reg, ret); + + return (ret < 0) ? ret : 0; +} + +static int rmw_protect(struct tps6524x *hw, int reg, int mask, int val) +{ + int ret; + + mutex_lock(&hw->lock); + + ret = __write_reg(hw, REG_WRITE_ENABLE, 1); + if (ret) { + dev_err(hw->dev, "failed to set write enable\n"); + goto error; + } + + ret = __rmw_reg(hw, reg, mask, val); + if (ret) + dev_err(hw->dev, "failed to rmw register %d\n", reg); + + ret = __write_reg(hw, REG_WRITE_ENABLE, 0); + if (ret) { + dev_err(hw->dev, "failed to clear write enable\n"); + goto error; + } + +error: + mutex_unlock(&hw->lock); + + return ret; +} + +static int read_field(struct tps6524x *hw, const struct field *field) +{ + int tmp; + + tmp = read_reg(hw, field->reg); + if (tmp < 0) + return tmp; + + return (tmp >> field->shift) & field->mask; +} + +static int write_field(struct tps6524x *hw, const struct field *field, + int val) +{ + if (val & ~field->mask) + return -EOVERFLOW; + + return rmw_protect(hw, field->reg, + field->mask << field->shift, + val << field->shift); +} + +static const int dcdc1_voltages[] = { + 800000, 825000, 850000, 875000, + 900000, 925000, 950000, 975000, + 1000000, 1025000, 1050000, 1075000, + 1100000, 1125000, 1150000, 1175000, + 1200000, 1225000, 1250000, 1275000, + 1300000, 1325000, 1350000, 1375000, + 1400000, 1425000, 1450000, 1475000, + 1500000, 1525000, 1550000, 1575000, +}; + +static const int dcdc2_voltages[] = { + 1400000, 1450000, 1500000, 1550000, + 1600000, 1650000, 1700000, 1750000, + 1800000, 1850000, 1900000, 1950000, + 2000000, 2050000, 2100000, 2150000, + 2200000, 2250000, 2300000, 2350000, + 2400000, 2450000, 2500000, 2550000, + 2600000, 2650000, 2700000, 2750000, + 2800000, 2850000, 2900000, 2950000, +}; + +static const int dcdc3_voltages[] = { + 2400000, 2450000, 2500000, 2550000, 2600000, + 2650000, 2700000, 2750000, 2800000, 2850000, + 2900000, 2950000, 3000000, 3050000, 3100000, + 3150000, 3200000, 3250000, 3300000, 3350000, + 3400000, 3450000, 3500000, 3550000, 3600000, +}; + +static const int ldo1_voltages[] = { + 4300000, 4350000, 4400000, 4450000, + 4500000, 4550000, 4600000, 4650000, + 4700000, 4750000, 4800000, 4850000, + 4900000, 4950000, 5000000, 5050000, +}; + +static const int ldo2_voltages[] = { + 1100000, 1150000, 1200000, 1250000, + 1300000, 1700000, 1750000, 1800000, + 1850000, 1900000, 3150000, 3200000, + 3250000, 3300000, 3350000, 3400000, +}; + +static const int ldo_ilimsel[] = { + 400000, 1500000 +}; + +static const int usb_ilimsel[] = { + 200000, 400000, 800000, 1000000 +}; + +#define __MK_FIELD(_reg, _mask, _shift) \ + { .reg = (_reg), .mask = (_mask), .shift = (_shift), } + +static const struct supply_info supply_info[N_REGULATORS] = { + { + .name = "DCDC1", + .flags = FIXED_ILIMSEL, + .n_voltages = ARRAY_SIZE(dcdc1_voltages), + .voltages = dcdc1_voltages, + .fixed_ilimsel = 2400000, + .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, + DCDCDCDC1_EN_SHIFT), + .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, + DCDC_VDCDC1_SHIFT), + }, + { + .name = "DCDC2", + .flags = FIXED_ILIMSEL, + .n_voltages = ARRAY_SIZE(dcdc2_voltages), + .voltages = dcdc2_voltages, + .fixed_ilimsel = 1200000, + .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, + DCDCDCDC2_EN_SHIFT), + .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, + DCDC_VDCDC2_SHIFT), + }, + { + .name = "DCDC3", + .flags = FIXED_ILIMSEL, + .n_voltages = ARRAY_SIZE(dcdc3_voltages), + .voltages = dcdc3_voltages, + .fixed_ilimsel = 1200000, + .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, + DCDCDCDC3_EN_SHIFT), + .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, + DCDC_VDCDC3_SHIFT), + }, + { + .name = "LDO1", + .n_voltages = ARRAY_SIZE(ldo1_voltages), + .voltages = ldo1_voltages, + .n_ilimsels = ARRAY_SIZE(ldo_ilimsel), + .ilimsels = ldo_ilimsel, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_LDO1_SHIFT), + .voltage = __MK_FIELD(REG_LDO_SET, LDO_VSEL_MASK, + LDO1_VSEL_SHIFT), + .ilimsel = __MK_FIELD(REG_LDO_SET, LDO_ILIM_MASK, + LDO1_ILIM_SHIFT), + }, + { + .name = "LDO2", + .n_voltages = ARRAY_SIZE(ldo2_voltages), + .voltages = ldo2_voltages, + .n_ilimsels = ARRAY_SIZE(ldo_ilimsel), + .ilimsels = ldo_ilimsel, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_LDO2_SHIFT), + .voltage = __MK_FIELD(REG_LDO_SET, LDO_VSEL_MASK, + LDO2_VSEL_SHIFT), + .ilimsel = __MK_FIELD(REG_LDO_SET, LDO_ILIM_MASK, + LDO2_ILIM_SHIFT), + }, + { + .name = "USB", + .flags = FIXED_VOLTAGE, + .fixed_voltage = 5000000, + .n_ilimsels = ARRAY_SIZE(usb_ilimsel), + .ilimsels = usb_ilimsel, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_USB_SHIFT), + .ilimsel = __MK_FIELD(REG_USB, USB_ILIM_MASK, + USB_ILIM_SHIFT), + }, + { + .name = "LCD", + .flags = FIXED_VOLTAGE | FIXED_ILIMSEL, + .fixed_voltage = 5000000, + .fixed_ilimsel = 400000, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_LCD_SHIFT), + }, +}; + +static int list_voltage(struct regulator_dev *rdev, unsigned selector) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->flags & FIXED_VOLTAGE) + return selector ? -EINVAL : info->fixed_voltage; + + return ((selector < info->n_voltages) ? + info->voltages[selector] : -EINVAL); +} + +static int set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, + unsigned *selector) +{ + const struct supply_info *info; + struct tps6524x *hw; + unsigned i; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->flags & FIXED_VOLTAGE) + return -EINVAL; + + for (i = 0; i < info->n_voltages; i++) + if (min_uV <= info->voltages[i] && + max_uV >= info->voltages[i]) + break; + + if (i >= info->n_voltages) + i = info->n_voltages - 1; + + *selector = info->voltages[i]; + + return write_field(hw, &info->voltage, i); +} + +static int get_voltage(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + int ret; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->flags & FIXED_VOLTAGE) + return info->fixed_voltage; + + ret = read_field(hw, &info->voltage); + if (ret < 0) + return ret; + if (WARN_ON(ret >= info->n_voltages)) + return -EIO; + + return info->voltages[ret]; +} + +static int set_current_limit(struct regulator_dev *rdev, int min_uA, + int max_uA) +{ + const struct supply_info *info; + struct tps6524x *hw; + int i; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->flags & FIXED_ILIMSEL) + return -EINVAL; + + for (i = 0; i < info->n_ilimsels; i++) + if (min_uA <= info->ilimsels[i] && + max_uA >= info->ilimsels[i]) + break; + + if (i >= info->n_ilimsels) + return -EINVAL; + + return write_field(hw, &info->ilimsel, i); +} + +static int get_current_limit(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + int ret; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->flags & FIXED_ILIMSEL) + return info->fixed_ilimsel; + + ret = read_field(hw, &info->ilimsel); + if (ret < 0) + return ret; + if (WARN_ON(ret >= info->n_ilimsels)) + return -EIO; + + return info->ilimsels[ret]; +} + +static int enable_supply(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + return write_field(hw, &info->enable, 1); +} + +static int disable_supply(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + return write_field(hw, &info->enable, 0); +} + +static int is_supply_enabled(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + return read_field(hw, &info->enable); +} + +static struct regulator_ops regulator_ops = { + .is_enabled = is_supply_enabled, + .enable = enable_supply, + .disable = disable_supply, + .get_voltage = get_voltage, + .set_voltage = set_voltage, + .list_voltage = list_voltage, + .set_current_limit = set_current_limit, + .get_current_limit = get_current_limit, +}; + +static int __devexit pmic_remove(struct spi_device *spi) +{ + struct tps6524x *hw = spi_get_drvdata(spi); + int i; + + if (!hw) + return 0; + for (i = 0; i < N_REGULATORS; i++) { + if (hw->rdev[i]) + regulator_unregister(hw->rdev[i]); + hw->rdev[i] = NULL; + } + spi_set_drvdata(spi, NULL); + kfree(hw); + return 0; +} + +static int __devinit pmic_probe(struct spi_device *spi) +{ + struct tps6524x *hw; + struct device *dev = &spi->dev; + const struct supply_info *info = supply_info; + struct regulator_init_data *init_data; + int ret = 0, i; + + init_data = dev->platform_data; + if (!init_data) { + dev_err(dev, "could not find regulator platform data\n"); + return -EINVAL; + } + + hw = kzalloc(sizeof(struct tps6524x), GFP_KERNEL); + if (!hw) { + dev_err(dev, "cannot allocate regulator private data\n"); + return -ENOMEM; + } + spi_set_drvdata(spi, hw); + + memset(hw, 0, sizeof(struct tps6524x)); + hw->dev = dev; + hw->spi = spi_dev_get(spi); + mutex_init(&hw->lock); + + for (i = 0; i < N_REGULATORS; i++, info++, init_data++) { + hw->desc[i].name = info->name; + hw->desc[i].id = i; + hw->desc[i].n_voltages = info->n_voltages; + hw->desc[i].ops = ®ulator_ops; + hw->desc[i].type = REGULATOR_VOLTAGE; + hw->desc[i].owner = THIS_MODULE; + + if (info->flags & FIXED_VOLTAGE) + hw->desc[i].n_voltages = 1; + + hw->rdev[i] = regulator_register(&hw->desc[i], dev, + init_data, hw); + if (IS_ERR(hw->rdev[i])) { + ret = PTR_ERR(hw->rdev[i]); + hw->rdev[i] = NULL; + goto fail; + } + } + + return 0; + +fail: + pmic_remove(spi); + return ret; +} + +static struct spi_driver pmic_driver = { + .probe = pmic_probe, + .remove = __devexit_p(pmic_remove), + .driver = { + .name = "tps6524x", + .owner = THIS_MODULE, + }, +}; + +static int __init pmic_driver_init(void) +{ + return spi_register_driver(&pmic_driver); +} +module_init(pmic_driver_init); + +static void __exit pmic_driver_exit(void) +{ + spi_unregister_driver(&pmic_driver); +} +module_exit(pmic_driver_exit); + +MODULE_DESCRIPTION("TPS6524X PMIC Driver"); +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:tps6524x"); -- 1.7.1 From broonie at opensource.wolfsonmicro.com Tue Dec 7 11:15:33 2010 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Tue, 7 Dec 2010 17:15:33 +0000 Subject: [PATCH] regulator: add driver for tps6524x regulator In-Reply-To: <1291741451-17549-1-git-send-email-cyril@ti.com> References: <1291741451-17549-1-git-send-email-cyril@ti.com> Message-ID: <20101207171533.GH9689@rakim.wolfsonmicro.main> On Tue, Dec 07, 2010 at 12:04:11PM -0500, Cyril Chemparathy wrote: > TPS6524X provides three step-down converters and two general-purpose LDO > voltage regulators. This device is interfaced using SPI. > > Signed-off-by: Cyril Chemparathy Acked-by: Mark Brown From cyril at ti.com Tue Dec 7 11:20:14 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 07 Dec 2010 12:20:14 -0500 Subject: [PATCH] regulator: add driver for tps6524x regulator In-Reply-To: <20101207171533.GH9689@rakim.wolfsonmicro.main> References: <1291741451-17549-1-git-send-email-cyril@ti.com> <20101207171533.GH9689@rakim.wolfsonmicro.main> Message-ID: <4CFE6CCE.5050301@ti.com> On 12/07/2010 12:15 PM, Mark Brown wrote: > On Tue, Dec 07, 2010 at 12:04:11PM -0500, Cyril Chemparathy wrote: >> TPS6524X provides three step-down converters and two general-purpose LDO >> voltage regulators. This device is interfaced using SPI. >> >> Signed-off-by: Cyril Chemparathy > > Acked-by: Mark Brown Is this to be merged via the davinci tree? From broonie at opensource.wolfsonmicro.com Tue Dec 7 11:55:50 2010 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Tue, 7 Dec 2010 17:55:50 +0000 Subject: [PATCH] regulator: add driver for tps6524x regulator In-Reply-To: <4CFE6CCE.5050301@ti.com> References: <1291741451-17549-1-git-send-email-cyril@ti.com> <20101207171533.GH9689@rakim.wolfsonmicro.main> <4CFE6CCE.5050301@ti.com> Message-ID: <20101207175550.GJ9689@rakim.wolfsonmicro.main> On Tue, Dec 07, 2010 at 12:20:14PM -0500, Cyril Chemparathy wrote: > On 12/07/2010 12:15 PM, Mark Brown wrote: > > Acked-by: Mark Brown > Is this to be merged via the davinci tree? Liam would need to merge it, or it'd need to wait until the regulator tree change to the signature of set_voltage() gets merged into the DaVinci tree. From sshtylyov at mvista.com Tue Dec 7 12:23:14 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Tue, 07 Dec 2010 21:23:14 +0300 Subject: [PATCH v2.6.37-rc3] USB: musb: Prevent Transmit Buffer Descriptor corruption In-Reply-To: <4CFD8241.7030306@seektech.com> References: <4CFD8241.7030306@seektech.com> Message-ID: <4CFE7B92.2020101@mvista.com> Hello. Paul Stuart wrote: You should post this to linux-usb at vger,kernel.org and CC Felipe Balbi , the MUSB maintainer. Kevin doesn't apply USB patches. > cppi_next_tx_segment is not checking for Transmit Buffer Descriptor > ownership before modifying parameters. > Transmit Buffer Descriptor ram is shared between the host processor and > the DMA. The "Ownership" bit is set by the host processor to give the > DMA ownership of the Transmit Buffer Descriptor, and the bit is cleared > by the DMA to return ownership to the host processor. > On USB Tx, when the system is heavily loaded, cppi_next_tx_segment can > overwrite a Transmit Buffer Descriptor that is still owned by the DMA, > causing DMA truncation error to fire, resulting in a channel abort. This > proposed fix adds a check for host processor ownership of the bd and > does not proceed to program it until the DMA has ended ownership. > Condition rarely occurs, so USB write speed is not negatively impacted. > Tested on DM365 > Signed-off-by: Paul Stuart > *---* The tearline is ---, without *'s. You don't need it anyway as the patch immediately follows, without diffstat or your remarks. > --- a/drivers/usb/musb/cppi_dma.c 2010-12-06 16:35:37.000000000 -0800 > +++ b/drivers/usb/musb/cppi_dma.c 2010-12-06 16:36:15.000000000 -0800 > @@ -625,6 +625,9 @@ cppi_next_tx_segment(struct musb *musb, > * size; for RNDIS there _is_ only that last packet. > */ Your patch is whitespace-mangled, i.e. has spaces instead of all tabs. > for (i = 0; i < n_bds; ) { > + /* wait for DMA to release ownership of this bd */ > + while(bd->hw_options & CPPI_OWN_SET)cpu_relax(); cpu_relax() call should be on a line of its own (properly indented with tab). > + > if (++i < n_bds && bd->next) > bd->hw_next = bd->next->dma; > else WBR, Sergei From sshtylyov at mvista.com Tue Dec 7 12:38:22 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Tue, 07 Dec 2010 21:38:22 +0300 Subject: [PATCH v2.6.37-rc3] USB: musb: Prevent Transmit Buffer Descriptor corruption In-Reply-To: <4CFE7B92.2020101@mvista.com> References: <4CFD8241.7030306@seektech.com> <4CFE7B92.2020101@mvista.com> Message-ID: <4CFE7F1E.40505@mvista.com> Hello, I wrote: >> cppi_next_tx_segment is not checking for Transmit Buffer Descriptor >> ownership before modifying parameters. >> Transmit Buffer Descriptor ram is shared between the host processor >> and the DMA. The "Ownership" bit is set by the host processor to give >> the DMA ownership of the Transmit Buffer Descriptor, and the bit is >> cleared by the DMA to return ownership to the host processor. >> On USB Tx, when the system is heavily loaded, cppi_next_tx_segment can >> overwrite a Transmit Buffer Descriptor that is still owned by the DMA, >> causing DMA truncation error to fire, resulting in a channel abort. >> This proposed fix adds a check for host processor ownership of the bd >> and does not proceed to program it until the DMA has ended ownership. >> Condition rarely occurs, so USB write speed is not negatively impacted. >> Tested on DM365 >> Signed-off-by: Paul Stuart >> for (i = 0; i < n_bds; ) { >> + /* wait for DMA to release ownership of this bd */ >> + while(bd->hw_options & CPPI_OWN_SET)cpu_relax(); > cpu_relax() call should be on a line of its own (properly indented > with tab). And don't forget space after *while*! :-) WBR, Sergei From paul_stuart at seektech.com Tue Dec 7 15:43:05 2010 From: paul_stuart at seektech.com (Paul Stuart) Date: Tue, 07 Dec 2010 13:43:05 -0800 Subject: [PATCH v2] USB: musb: Prevent Transmit Buffer Descriptor corruption Message-ID: <4CFEAA69.4030503@seektech.com> cppi_next_tx_segment is not checking for Transmit Buffer Descriptor ownership before modifying parameters. Transmit Buffer Descriptor ram is shared between the host processor and the DMA. The "Ownership" bit is set by the host processor to give the DMA ownership of the Transmit Buffer Descriptor, and the bit is cleared by the DMA to return ownership to the host processor. On USB Tx, when the system is heavily loaded, cppi_next_tx_segment can overwrite a Transmit Buffer Descriptor that is still owned by the DMA, causing DMA truncation error to fire, resulting in a channel abort. This proposed fix adds a check for host processor ownership of the bd and does not proceed to program it until the DMA has ended ownership. Condition rarely occurs, so USB write speed is not negatively impacted. Tested on DM365 Signed-off-by: Paul Stuart --- V2: Wrapped test in unlikely macro as this event _shouldn't_ occur often. Fixed formatting, per Sergei's advice. --- a/drivers/usb/musb/cppi_dma.c 2010-12-06 20:09:04.000000000 -0800 +++ b/drivers/usb/musb/cppi_dma.c 2010-12-07 11:22:04.000000000 -0800 @@ -625,6 +625,14 @@ cppi_next_tx_segment(struct musb *musb, * size; for RNDIS there _is_ only that last packet. */ for (i = 0; i < n_bds; ) { + + /* wait for DMA to release ownership of this bd */ + if (unlikely(bd->hw_options & CPPI_OWN_SET)) { + do { + cpu_relax(); + } while (bd->hw_options & CPPI_OWN_SET); + } + if (++i < n_bds && bd->next) bd->hw_next = bd->next->dma; else From vm.rod25 at gmail.com Tue Dec 7 16:14:43 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Tue, 7 Dec 2010 16:14:43 -0600 Subject: [PATCH v10 0/6] Add Omapl138-Hawkboard support Message-ID: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds EMAC, EDMA, MMC/SD and USB OHCI support for the Hawkboard-L138 system It is under the machine name "omapl138_hawkboard". This system is based on the da850 davinci CPU architecture. Victor Rodriguez (6): davinci: EMAC support for Omapl138-Hawkboard davinci: EDMA support for Omapl138-Hawkboard davinci: MMC/SD and USB-OHCI configuration for Omapl138-Hawkboard davinci: MMC/SD support for Omapl138-Hawkboard davinci: USB clocks for Omapl138-Hawkboard davinci: USB1.1 support for Omapl138-Hawkboard arch/arm/mach-davinci/board-omapl138-hawk.c | 286 +++++++++++++++++++++++++++ arch/arm/mach-davinci/da850.c | 20 ++ arch/arm/mach-davinci/include/mach/mux.h | 4 + 3 files changed, 310 insertions(+), 0 deletions(-) From vm.rod25 at gmail.com Tue Dec 7 16:14:44 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Tue, 7 Dec 2010 16:14:44 -0600 Subject: [PATCH v10 1/6] davinci: EMAC support for Omapl138-Hawkboard In-Reply-To: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> References: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1291760089-5818-2-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds EMAC support for the Hawkboard-L138 system Signed-off-by: Victor Rodriguez Tested-by: Rene Gonzalez --- arch/arm/mach-davinci/board-omapl138-hawk.c | 43 +++++++++++++++++++++++++++ 1 files changed, 43 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index 0b8dbdb..75b3fe2 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -19,6 +19,47 @@ #include #include +#include + +#define HAWKBOARD_PHY_ID "0:07" + +static short omapl138_hawk_mii_pins[] __initdata = { + DA850_MII_TXEN, DA850_MII_TXCLK, DA850_MII_COL, DA850_MII_TXD_3, + DA850_MII_TXD_2, DA850_MII_TXD_1, DA850_MII_TXD_0, DA850_MII_RXER, + DA850_MII_CRS, DA850_MII_RXCLK, DA850_MII_RXDV, DA850_MII_RXD_3, + DA850_MII_RXD_2, DA850_MII_RXD_1, DA850_MII_RXD_0, DA850_MDIO_CLK, + DA850_MDIO_D, + -1 +}; + +static __init void omapl138_hawk_config_emac(void) +{ + void __iomem *cfgchip3 = DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP3_REG); + int ret; + u32 val; + struct davinci_soc_info *soc_info = &davinci_soc_info; + + val = __raw_readl(cfgchip3); + val &= ~BIT(8); + ret = davinci_cfg_reg_list(omapl138_hawk_mii_pins); + if (ret) { + pr_warning("%s: cpgmac/mii mux setup failed: %d\n", + __func__, ret); + return; + } + + /* configure the CFGCHIP3 register for MII */ + __raw_writel(val, cfgchip3); + pr_info("EMAC: MII PHY configured\n"); + + soc_info->emac_pdata->phy_id = HAWKBOARD_PHY_ID; + + ret = da8xx_register_emac(); + if (ret) + pr_warning("%s: emac registration failed: %d\n", + __func__, ret); +} + static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { .enabled_uarts = 0x7, @@ -30,6 +71,8 @@ static __init void omapl138_hawk_init(void) davinci_serial_init(&omapl138_hawk_uart_config); + omapl138_hawk_config_emac(); + ret = da8xx_register_watchdog(); if (ret) pr_warning("omapl138_hawk_init: " -- 1.7.0.4 From vm.rod25 at gmail.com Tue Dec 7 16:14:45 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Tue, 7 Dec 2010 16:14:45 -0600 Subject: [PATCH v10 2/6] davinci: EDMA support for Omapl138-Hawkboard In-Reply-To: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> References: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1291760089-5818-3-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds EDMA support for the Hawkboard-L138 system Signed-off-by: Victor Rodriguez Tested-by: Rene Gonzalez --- arch/arm/mach-davinci/board-omapl138-hawk.c | 54 +++++++++++++++++++++++++++ 1 files changed, 54 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index 75b3fe2..cefff9b 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -60,6 +60,55 @@ static __init void omapl138_hawk_config_emac(void) __func__, ret); } +/* + * The following EDMA channels/slots are not being used by drivers (for + * example: Timer, GPIO, UART events etc) on da850/omap-l138 EVM/Hawkboard, + * hence they are being reserved for codecs on the DSP side. + */ +static const s16 da850_dma0_rsv_chans[][2] = { + /* (offset, number) */ + { 8, 6}, + {24, 4}, + {30, 2}, + {-1, -1} +}; + +static const s16 da850_dma0_rsv_slots[][2] = { + /* (offset, number) */ + { 8, 6}, + {24, 4}, + {30, 50}, + {-1, -1} +}; + +static const s16 da850_dma1_rsv_chans[][2] = { + /* (offset, number) */ + { 0, 28}, + {30, 2}, + {-1, -1} +}; + +static const s16 da850_dma1_rsv_slots[][2] = { + /* (offset, number) */ + { 0, 28}, + {30, 90}, + {-1, -1} +}; + +static struct edma_rsv_info da850_edma_cc0_rsv = { + .rsv_chans = da850_dma0_rsv_chans, + .rsv_slots = da850_dma0_rsv_slots, +}; + +static struct edma_rsv_info da850_edma_cc1_rsv = { + .rsv_chans = da850_dma1_rsv_chans, + .rsv_slots = da850_dma1_rsv_slots, +}; + +static struct edma_rsv_info *da850_edma_rsv[2] = { + &da850_edma_cc0_rsv, + &da850_edma_cc1_rsv, +}; static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { .enabled_uarts = 0x7, @@ -73,6 +122,11 @@ static __init void omapl138_hawk_init(void) omapl138_hawk_config_emac(); + ret = da850_register_edma(da850_edma_rsv); + if (ret) + pr_warning("%s: EDMA registration failed: %d\n", + __func__, ret); + ret = da8xx_register_watchdog(); if (ret) pr_warning("omapl138_hawk_init: " -- 1.7.0.4 From vm.rod25 at gmail.com Tue Dec 7 16:14:46 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Tue, 7 Dec 2010 16:14:46 -0600 Subject: [PATCH v10 3/6] davinci: MMC/SD and USB-OHCI configuration for Omapl138-Hawkboard In-Reply-To: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> References: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1291760089-5818-4-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch defines Pin Mux configuration to enable MMC/SD and USB-OHCI on the Hawkboard-L138 system Signed-off-by: Victor Rodriguez Tested-by: Rene Gonzalez --- arch/arm/mach-davinci/da850.c | 4 ++++ arch/arm/mach-davinci/include/mach/mux.h | 4 ++++ 2 files changed, 8 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 63916b9..80803fa 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -543,11 +543,15 @@ static const struct mux_config da850_pins[] = { MUX_CFG(DA850, EMA_WAIT_1, 6, 24, 15, 1, false) MUX_CFG(DA850, NEMA_CS_2, 7, 0, 15, 1, false) /* GPIO function */ + MUX_CFG(DA850, GPIO2_4, 6, 12, 15, 8, false) MUX_CFG(DA850, GPIO2_6, 6, 4, 15, 8, false) MUX_CFG(DA850, GPIO2_8, 5, 28, 15, 8, false) MUX_CFG(DA850, GPIO2_15, 5, 0, 15, 8, false) + MUX_CFG(DA850, GPIO3_12, 7, 12, 15, 8, false) + MUX_CFG(DA850, GPIO3_13, 7, 8, 15, 8, false) MUX_CFG(DA850, GPIO4_0, 10, 28, 15, 8, false) MUX_CFG(DA850, GPIO4_1, 10, 24, 15, 8, false) + MUX_CFG(DA850, GPIO6_13, 13, 8, 15, 8, false) MUX_CFG(DA850, RTC_ALARM, 0, 28, 15, 2, false) #endif }; diff --git a/arch/arm/mach-davinci/include/mach/mux.h b/arch/arm/mach-davinci/include/mach/mux.h index de11aac..5d4e0fe 100644 --- a/arch/arm/mach-davinci/include/mach/mux.h +++ b/arch/arm/mach-davinci/include/mach/mux.h @@ -908,11 +908,15 @@ enum davinci_da850_index { DA850_NEMA_CS_2, /* GPIO function */ + DA850_GPIO2_4, DA850_GPIO2_6, DA850_GPIO2_8, DA850_GPIO2_15, + DA850_GPIO3_12, + DA850_GPIO3_13, DA850_GPIO4_0, DA850_GPIO4_1, + DA850_GPIO6_13, DA850_RTC_ALARM, }; -- 1.7.0.4 From vm.rod25 at gmail.com Tue Dec 7 16:14:47 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Tue, 7 Dec 2010 16:14:47 -0600 Subject: [PATCH v10 4/6] davinci: MMC/SD support for Omapl138-Hawkboard In-Reply-To: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> References: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1291760089-5818-5-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds MMC/SD support for the Hawkboard-L138 system Signed-off-by: Victor Rodriguez Tested-by: Rene Gonzalez --- Notes: This patch works with da8xx_omapl_defconfig In order to test it select in menuconfig like insmodule MMC/SD/SDIO card support ---> MMC block device driver Use bounce buffer for simple hosts TI DAVINCI Multimedia Card Interface support arch/arm/mach-davinci/board-omapl138-hawk.c | 73 +++++++++++++++++++++++++++ 1 files changed, 73 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index cefff9b..df50637 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -22,6 +22,8 @@ #include #define HAWKBOARD_PHY_ID "0:07" +#define DA850_HAWK_MMCSD_CD_PIN GPIO_TO_PIN(3, 12) +#define DA850_HAWK_MMCSD_WP_PIN GPIO_TO_PIN(3, 13) static short omapl138_hawk_mii_pins[] __initdata = { DA850_MII_TXEN, DA850_MII_TXCLK, DA850_MII_COL, DA850_MII_TXD_3, @@ -110,6 +112,75 @@ static struct edma_rsv_info *da850_edma_rsv[2] = { &da850_edma_cc1_rsv, }; +static const short hawk_mmcsd0_pins[] = { + DA850_MMCSD0_DAT_0, DA850_MMCSD0_DAT_1, DA850_MMCSD0_DAT_2, + DA850_MMCSD0_DAT_3, DA850_MMCSD0_CLK, DA850_MMCSD0_CMD, + DA850_GPIO3_12, DA850_GPIO3_13, + -1 +}; + +static int da850_hawk_mmc_get_ro(int index) +{ + return gpio_get_value(DA850_HAWK_MMCSD_WP_PIN); +} + +static int da850_hawk_mmc_get_cd(int index) +{ + return !gpio_get_value(DA850_HAWK_MMCSD_CD_PIN); +} + +static struct davinci_mmc_config da850_mmc_config = { + .get_ro = da850_hawk_mmc_get_ro, + .get_cd = da850_hawk_mmc_get_cd, + .wires = 4, + .max_freq = 50000000, + .caps = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED, + .version = MMC_CTLR_VERSION_2, +}; + +static __init void omapl138_hawk_mmc_init(void) +{ + int ret; + + ret = davinci_cfg_reg_list(hawk_mmcsd0_pins); + if (ret) { + pr_warning("%s: MMC/SD0 mux setup failed: %d\n", + __func__, ret); + return; + } + + ret = gpio_request_one(DA850_HAWK_MMCSD_CD_PIN, + GPIOF_DIR_IN, "MMC CD"); + if (ret < 0) { + pr_warning("%s: can not open GPIO %d\n", + __func__, DA850_HAWK_MMCSD_CD_PIN); + goto mmc_setup_cd_fail; + } + + ret = gpio_request_one(DA850_HAWK_MMCSD_WP_PIN, + GPIOF_DIR_IN, "MMC WP"); + if (ret < 0) { + pr_warning("%s: can not open GPIO %d\n", + __func__, DA850_HAWK_MMCSD_WP_PIN); + goto mmc_setup_wp_fail; + } + + ret = da8xx_register_mmcsd0(&da850_mmc_config); + if (ret) { + pr_warning("%s: MMC/SD0 registration failed: %d\n", + __func__, ret); + goto mmc_setup_mmcsd_fail; + } + +mmc_setup_cd_fail: + return; + +mmc_setup_mmcsd_fail: + gpio_free(DA850_HAWK_MMCSD_WP_PIN); +mmc_setup_wp_fail: + gpio_free(DA850_HAWK_MMCSD_CD_PIN); +} + static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { .enabled_uarts = 0x7, }; @@ -127,6 +198,8 @@ static __init void omapl138_hawk_init(void) pr_warning("%s: EDMA registration failed: %d\n", __func__, ret); + omapl138_hawk_mmc_init(); + ret = da8xx_register_watchdog(); if (ret) pr_warning("omapl138_hawk_init: " -- 1.7.0.4 From vm.rod25 at gmail.com Tue Dec 7 16:14:48 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Tue, 7 Dec 2010 16:14:48 -0600 Subject: [PATCH v10 5/6] davinci: USB clocks for Omapl138-Hawkboard In-Reply-To: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> References: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1291760089-5818-6-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds USB1.1 and USB2.0 clocks for the Hawkboard-L138 system Signed-off-by: Victor Rodriguez Tested-by: Rene Gonzalez --- arch/arm/mach-davinci/da850.c | 16 ++++++++++++++++ 1 files changed, 16 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 80803fa..93a4df4 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -345,6 +345,20 @@ static struct clk aemif_clk = { .flags = ALWAYS_ENABLED, }; +static struct clk usb11_clk = { + .name = "usb11", + .parent = &pll0_sysclk4, + .lpsc = DA8XX_LPSC1_USB11, + .gpsc = 1, +}; + +static struct clk usb20_clk = { + .name = "usb20", + .parent = &pll0_sysclk2, + .lpsc = DA8XX_LPSC1_USB20, + .gpsc = 1, +}; + static struct clk_lookup da850_clks[] = { CLK(NULL, "ref", &ref_clk), CLK(NULL, "pll0", &pll0_clk), @@ -387,6 +401,8 @@ static struct clk_lookup da850_clks[] = { CLK("davinci_mmc.0", NULL, &mmcsd0_clk), CLK("davinci_mmc.1", NULL, &mmcsd1_clk), CLK(NULL, "aemif", &aemif_clk), + CLK(NULL, "usb11", &usb11_clk), + CLK(NULL, "usb20", &usb20_clk), CLK(NULL, NULL, NULL), }; -- 1.7.0.4 From vm.rod25 at gmail.com Tue Dec 7 16:14:49 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Tue, 7 Dec 2010 16:14:49 -0600 Subject: [PATCH v10 6/6] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> References: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1291760089-5818-7-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds USB1.1 support for the Hawkboard-L138 system Signed-off-by: Victor Rodriguez Tested-by: Rene Gonzalez --- Notes: This patch works with da8xx_omapl_defconfig In order to test it select in menuconfig like insmodule Device Drivers ---> SCSI device support ---> SCSI device support legacy /proc/scsi/ support SCSI disk support SCSI low-level drivers USB support ---> Support for Host-side USB OHCI HCD support (NEW) USB Mass Storage support (NEW) USB Gadget Support ---> USB Gadget Drivers (Ethernet Gadget\ (with CDC Ethernet support)) ---> NOP USB Transceiver Driver And you will be able to mount an USB pen drive In order to connect a keyboard or a mouse on a USB-hub select in menuconfig like insmodule HID Devices ---> Generic HID support USB Human Interface Device (full HID) support arch/arm/mach-davinci/board-omapl138-hawk.c | 116 +++++++++++++++++++++++++++ 1 files changed, 116 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index df50637..8646b74 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -25,6 +25,9 @@ #define DA850_HAWK_MMCSD_CD_PIN GPIO_TO_PIN(3, 12) #define DA850_HAWK_MMCSD_WP_PIN GPIO_TO_PIN(3, 13) +#define DA850_USB1_VBUS_PIN GPIO_TO_PIN(2, 4) +#define DA850_USB1_OC_PIN GPIO_TO_PIN(6, 13) + static short omapl138_hawk_mii_pins[] __initdata = { DA850_MII_TXEN, DA850_MII_TXCLK, DA850_MII_COL, DA850_MII_TXD_3, DA850_MII_TXD_2, DA850_MII_TXD_1, DA850_MII_TXD_0, DA850_MII_RXER, @@ -181,6 +184,117 @@ mmc_setup_wp_fail: gpio_free(DA850_HAWK_MMCSD_CD_PIN); } +static irqreturn_t omapl138_hawk_usb_ocic_irq(int irq, void *dev_id); +static da8xx_ocic_handler_t hawk_usb_ocic_handler; + +static const short da850_hawk_usb11_pins[] = { + DA850_GPIO2_4, DA850_GPIO6_13, + -1 +}; + +static int hawk_usb_set_power(unsigned port, int on) +{ + gpio_set_value(DA850_USB1_VBUS_PIN, on); + return 0; +} + +static int hawk_usb_get_power(unsigned port) +{ + return gpio_get_value(DA850_USB1_VBUS_PIN); +} + +static int hawk_usb_get_oci(unsigned port) +{ + return !gpio_get_value(DA850_USB1_OC_PIN); +} + +static int hawk_usb_ocic_notify(da8xx_ocic_handler_t handler) +{ + int irq = gpio_to_irq(DA850_USB1_OC_PIN); + int error = 0; + + if (handler != NULL) { + hawk_usb_ocic_handler = handler; + + error = request_irq(irq, omapl138_hawk_usb_ocic_irq, + IRQF_DISABLED | IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + "OHCI over-current indicator", NULL); + if (error) + pr_err("%s: could not request IRQ to watch " + "over-current indicator changes\n", __func__); + } else { + free_irq(irq, NULL); + } + return error; +} + +static struct da8xx_ohci_root_hub omapl138_hawk_usb11_pdata = { + .set_power = hawk_usb_set_power, + .get_power = hawk_usb_get_power, + .get_oci = hawk_usb_get_oci, + .ocic_notify = hawk_usb_ocic_notify, + /* TPS2087 switch @ 5V */ + .potpgt = (3 + 1) / 2, /* 3 ms max */ +}; + +static irqreturn_t omapl138_hawk_usb_ocic_irq(int irq, void *dev_id) +{ + hawk_usb_ocic_handler(&omapl138_hawk_usb11_pdata, 1); + return IRQ_HANDLED; +} + +static __init void omapl138_hawk_usb_init(void) +{ + int ret; + u32 cfgchip2; + + ret = davinci_cfg_reg_list(da850_hawk_usb11_pins); + if (ret) { + pr_warning("%s: USB 1.1 PinMux setup failed: %d\n", + __func__, ret); + return; + } + + /* Setup the Ref. clock frequency for the HAWK at 24 MHz. */ + + cfgchip2 = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); + cfgchip2 &= ~CFGCHIP2_REFFREQ; + cfgchip2 |= CFGCHIP2_REFFREQ_24MHZ; + __raw_writel(cfgchip2, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); + + ret = gpio_request_one(DA850_USB1_VBUS_PIN, + GPIOF_DIR_OUT, "USB1 VBUS"); + if (ret < 0) { + pr_err("%s: failed to request GPIO for USB 1.1 port " + "power control: %d\n", __func__, ret); + goto usb11_setup_vbus_fail; + } + + ret = gpio_request_one(DA850_USB1_OC_PIN, + GPIOF_DIR_IN, "USB1 OC"); + if (ret < 0) { + pr_err("%s: failed to request GPIO for USB 1.1 port " + "over-current indicator: %d\n", __func__, ret); + goto usb11_setup_oc_fail; + } + + ret = da8xx_register_usb11(&omapl138_hawk_usb11_pdata); + if (ret) { + pr_warning("%s: USB 1.1 registration failed: %d\n", + __func__, ret); + goto usb11_setup_fail; + } + +usb11_setup_vbus_fail: + return; + +usb11_setup_fail: + gpio_free(DA850_USB1_OC_PIN); +usb11_setup_oc_fail: + gpio_free(DA850_USB1_VBUS_PIN); +} + static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { .enabled_uarts = 0x7, }; @@ -200,6 +314,8 @@ static __init void omapl138_hawk_init(void) omapl138_hawk_mmc_init(); + omapl138_hawk_usb_init(); + ret = da8xx_register_watchdog(); if (ret) pr_warning("omapl138_hawk_init: " -- 1.7.0.4 From nsekhar at ti.com Tue Dec 7 23:20:19 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 8 Dec 2010 10:50:19 +0530 Subject: [PATCH v10 4/6] davinci: MMC/SD support for Omapl138-Hawkboard In-Reply-To: <1291760089-5818-5-git-send-email-vm.rod25@gmail.com> References: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> <1291760089-5818-5-git-send-email-vm.rod25@gmail.com> Message-ID: On Wed, Dec 08, 2010 at 03:44:47, vm.rod25 at gmail.com wrote: > +static __init void omapl138_hawk_mmc_init(void) > +{ > + int ret; > + > + ret = davinci_cfg_reg_list(hawk_mmcsd0_pins); > + if (ret) { > + pr_warning("%s: MMC/SD0 mux setup failed: %d\n", > + __func__, ret); > + return; > + } > + > + ret = gpio_request_one(DA850_HAWK_MMCSD_CD_PIN, > + GPIOF_DIR_IN, "MMC CD"); > + if (ret < 0) { > + pr_warning("%s: can not open GPIO %d\n", > + __func__, DA850_HAWK_MMCSD_CD_PIN); > + goto mmc_setup_cd_fail; > + } A goto is only used when there is some recovery to be done. In this case, you should simply return here. The USB patch needs the same correction. Thanks, Sekhar > + > + ret = gpio_request_one(DA850_HAWK_MMCSD_WP_PIN, > + GPIOF_DIR_IN, "MMC WP"); > + if (ret < 0) { > + pr_warning("%s: can not open GPIO %d\n", > + __func__, DA850_HAWK_MMCSD_WP_PIN); > + goto mmc_setup_wp_fail; > + } > + > + ret = da8xx_register_mmcsd0(&da850_mmc_config); > + if (ret) { > + pr_warning("%s: MMC/SD0 registration failed: %d\n", > + __func__, ret); > + goto mmc_setup_mmcsd_fail; > + } > + > +mmc_setup_cd_fail: > + return; > + > +mmc_setup_mmcsd_fail: > + gpio_free(DA850_HAWK_MMCSD_WP_PIN); > +mmc_setup_wp_fail: > + gpio_free(DA850_HAWK_MMCSD_CD_PIN); > +} > + > static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { > .enabled_uarts = 0x7, > }; > @@ -127,6 +198,8 @@ static __init void omapl138_hawk_init(void) > pr_warning("%s: EDMA registration failed: %d\n", > __func__, ret); > > + omapl138_hawk_mmc_init(); > + > ret = da8xx_register_watchdog(); > if (ret) > pr_warning("omapl138_hawk_init: " > -- > 1.7.0.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 vm.rod25 at gmail.com Wed Dec 8 09:55:25 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Wed, 8 Dec 2010 09:55:25 -0600 Subject: [PATCH v10 4/6] davinci: MMC/SD support for Omapl138-Hawkboard In-Reply-To: References: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> <1291760089-5818-5-git-send-email-vm.rod25@gmail.com> Message-ID: On Tue, Dec 7, 2010 at 11:20 PM, Nori, Sekhar wrote: > On Wed, Dec 08, 2010 at 03:44:47, vm.rod25 at gmail.com wrote: > >> +static __init void omapl138_hawk_mmc_init(void) >> +{ >> + ? ? int ret; >> + >> + ? ? ret = davinci_cfg_reg_list(hawk_mmcsd0_pins); >> + ? ? if (ret) { >> + ? ? ? ? ? ? pr_warning("%s: MMC/SD0 mux setup failed: %d\n", >> + ? ? ? ? ? ? ? ? ? ? __func__, ret); >> + ? ? ? ? ? ? return; >> + ? ? } >> + >> + ? ? ret = gpio_request_one(DA850_HAWK_MMCSD_CD_PIN, >> + ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "MMC CD"); >> + ? ? if (ret < 0) { >> + ? ? ? ? ? ? pr_warning("%s: can not open GPIO %d\n", >> + ? ? ? ? ? ? ? ? ? ? __func__, DA850_HAWK_MMCSD_CD_PIN); >> + ? ? ? ? ? ? goto mmc_setup_cd_fail; >> + ? ? } > > A goto is only used when there is some recovery > to be done. In this case, you should simply return > here. The USB patch needs the same correction. > > Thanks, > Sekhar Sorry for this my mistake It wil change to this +static __init void omapl138_hawk_mmc_init(void) +{ + int ret; + + ret = davinci_cfg_reg_list(hawk_mmcsd0_pins); + if (ret) { + pr_warning("%s: MMC/SD0 mux setup failed: %d\n", + __func__, ret); + return; + } + + ret = gpio_request_one(DA850_HAWK_MMCSD_CD_PIN, + GPIOF_DIR_IN, "MMC CD"); + if (ret < 0) { + pr_warning("%s: can not open GPIO %d\n", + __func__, DA850_HAWK_MMCSD_CD_PIN); + return; + } + + ret = gpio_request_one(DA850_HAWK_MMCSD_WP_PIN, + GPIOF_DIR_IN, "MMC WP"); + if (ret < 0) { + pr_warning("%s: can not open GPIO %d\n", + __func__, DA850_HAWK_MMCSD_WP_PIN); + goto mmc_setup_wp_fail; + } + + ret = da8xx_register_mmcsd0(&da850_mmc_config); + if (ret) { + pr_warning("%s: MMC/SD0 registration failed: %d\n", + __func__, ret); + goto mmc_setup_mmcsd_fail; + } + + return; + +mmc_setup_mmcsd_fail: + gpio_free(DA850_HAWK_MMCSD_WP_PIN); +mmc_setup_wp_fail: + gpio_free(DA850_HAWK_MMCSD_CD_PIN); +} + And USB will be this +static __init void omapl138_hawk_usb_init(void) +{ + int ret; + u32 cfgchip2; + + ret = davinci_cfg_reg_list(da850_hawk_usb11_pins); + if (ret) { + pr_warning("%s: USB 1.1 PinMux setup failed: %d\n", + __func__, ret); + return; + } + + /* Setup the Ref. clock frequency for the HAWK at 24 MHz. */ + + cfgchip2 = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); + cfgchip2 &= ~CFGCHIP2_REFFREQ; + cfgchip2 |= CFGCHIP2_REFFREQ_24MHZ; + __raw_writel(cfgchip2, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); + + ret = gpio_request_one(DA850_USB1_VBUS_PIN, + GPIOF_DIR_OUT, "USB1 VBUS"); + if (ret < 0) { + pr_err("%s: failed to request GPIO for USB 1.1 port " + "power control: %d\n", __func__, ret); + return; + } + + ret = gpio_request_one(DA850_USB1_OC_PIN, + GPIOF_DIR_IN, "USB1 OC"); + if (ret < 0) { + pr_err("%s: failed to request GPIO for USB 1.1 port " + "over-current indicator: %d\n", __func__, ret); + goto usb11_setup_oc_fail; + } + + ret = da8xx_register_usb11(&omapl138_hawk_usb11_pdata); + if (ret) { + pr_warning("%s: USB 1.1 registration failed: %d\n", + __func__, ret); + goto usb11_setup_fail; + } + + return; + +usb11_setup_fail: + gpio_free(DA850_USB1_OC_PIN); +usb11_setup_oc_fail: + gpio_free(DA850_USB1_VBUS_PIN); +} + Thanks a lot for the comments Regards Victor Rodriguez >> + >> + ? ? ret = gpio_request_one(DA850_HAWK_MMCSD_WP_PIN, >> + ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "MMC WP"); >> + ? ? if (ret < 0) { >> + ? ? ? ? ? ? pr_warning("%s: can not open GPIO %d\n", >> + ? ? ? ? ? ? ? ? ? ? __func__, DA850_HAWK_MMCSD_WP_PIN); >> + ? ? ? ? ? ? goto mmc_setup_wp_fail; >> + ? ? } >> + >> + ? ? ret = da8xx_register_mmcsd0(&da850_mmc_config); >> + ? ? if (ret) { >> + ? ? ? ? ? ? pr_warning("%s: MMC/SD0 registration failed: %d\n", >> + ? ? ? ? ? ? ? ? ? ? __func__, ret); >> + ? ? ? ? ? ? goto mmc_setup_mmcsd_fail; >> + ? ? } >> + >> +mmc_setup_cd_fail: >> + ? ? return; >> + >> +mmc_setup_mmcsd_fail: >> + ? ? gpio_free(DA850_HAWK_MMCSD_WP_PIN); >> +mmc_setup_wp_fail: >> + ? ? gpio_free(DA850_HAWK_MMCSD_CD_PIN); >> +} >> + >> ?static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { >> ? ? ? .enabled_uarts = 0x7, >> ?}; >> @@ -127,6 +198,8 @@ static __init void omapl138_hawk_init(void) >> ? ? ? ? ? ? ? pr_warning("%s: EDMA registration failed: %d\n", >> ? ? ? ? ? ? ? ? ? ? ? __func__, ret); >> >> + ? ? omapl138_hawk_mmc_init(); >> + >> ? ? ? ret = da8xx_register_watchdog(); >> ? ? ? if (ret) >> ? ? ? ? ? ? ? pr_warning("omapl138_hawk_init: " >> -- >> 1.7.0.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 manjunath.hadli at ti.com Wed Dec 8 10:00:26 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Wed, 8 Dec 2010 21:30:26 +0530 Subject: [PATCH v3 2/6] davinci vpbe: VPBE display driver In-Reply-To: <201012062143.45910.hverkuil@xs4all.nl> Message-ID: Hans, Thanks for the comments. Only one comment from me. Rest everything I have taken care. Thanks and Regards, -Manju On Tue, Dec 07, 2010 at 02:13:45, Hans Verkuil wrote: > Comments below... > > On Thursday, December 02, 2010 13:38:36 Manjunath Hadli wrote: > > This patch implements the coe functionality of the dislay driver, > > mainly controlling the VENC and other encoders, and acting as the one > > point interface for the man V4L2 driver.This implements the cre of > > each of the V4L2 IOCTLs. > > > > Signed-off-by: Manjunath Hadli > > Signed-off-by: Muralidharan Karicheri > > --- > > drivers/media/video/davinci/vpbe.c | 847 ++++++++++++++++++++++++++++++++++++ > > include/media/davinci/vpbe.h | 186 ++++++++ > > 2 files changed, 1033 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..96c0eea > > --- /dev/null > > +++ b/drivers/media/video/davinci/vpbe.c > > @@ -0,0 +1,847 @@ > > +/* > > + * 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; either version 2 of the License. > > + * > > + * 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 > > + > > + > > +#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 struct osd_state *osd_device; static > > +struct venc_platform_data *venc_device; 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(ef_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_display_config *vpbe_config = vpbe_dev->cfg; > > + int index = vpbe_dev->current_sd_index; > > + return ((index == 0) ? &vpbe_config->venc : > > + &vpbe_config->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_display_config *vpbe_config, > > + int index) > > +{ > > + char *encoder_name = vpbe_config->outputs[index].subdev_name; > > + int i; > > + > > + /* Venc is always first */ > > + if (!strcmp(encoder_name, vpbe_config->venc.module_name)) > > + return 0; > > + > > + for (i = 0; i < vpbe_config->num_ext_encoders; i++) { > > + if (!strcmp(encoder_name, > > + vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; > > + int temp_index = output->index; > > + > > + if (temp_index >= vpbe_config->num_outputs) > > + return -EINVAL; > > + > > + *output = vpbe_config->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_display_config *cfg = vpbe_dev->cfg; > > + struct vpbe_enc_mode_info var; > > + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; > > + struct vpbe_enc_mode_info var; > > + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; > > + struct vpbe_enc_mode_info var; > > + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; > > + struct vpbe_enc_mode_info var; > > + int curr_output = vpbe_dev->current_out_index, 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 vpbe_display_config *vpbe_config = vpbe_dev->cfg; > > + struct encoder_config_info *curr_enc_info = > > + vpbe_current_encoder_info(vpbe_dev); > > + int ret = 0, enc_out_index = 0, sd_index; > > + > > + if (index >= vpbe_config->num_outputs) > > + return -EINVAL; > > + > > + ret = mutex_lock_interruptible(&vpbe_dev->lock); > > + if (ret) > > + return ret; > > I am not sure about this mutex. Is it still needed now that the main driver is serialized via opslock? And if it is, does it make sense to use the _interruptible variant? That only makes sense if some of the critical sections can sleep or otherwise take a long time. > > And if mutex_lock_interruptible() is indeed valid, then you should return -ERESTARTSYS on error. This will cause the scheduler to handle the signal and call the system call again. That's probably what you want. This is needed as the function might be called from multiple v4l2 video Nodes. The ioctl is protected but it is for each node. I will add the - ERESTARTSYS as you suggest. > > > + > > + sd_index = vpbe_dev->current_sd_index; > > + enc_out_index = vpbe_config->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, > > + vpbe_config->outputs[index].subdev_name)) { > > + /* Need to switch the encoder at the output */ > > + sd_index = vpbe_find_encoder_sd_index(vpbe_config, 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, > > + vpbe_config->outputs[index].default_mode); > > + if (!ret) { > > + 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); > > + } > > + if (!ret) { > > Why not combine this in one 'if'? > > > + 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_display_config *vpbe_config = vpbe_dev->cfg; > > + int i, ret = 0; > > + > > + for (i = 0; i < vpbe_config->num_outputs; i++) { > > + if (!strcmp(def_output, > > + vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; > > + int sd_index = vpbe_dev->current_sd_index; > > + int out_index = vpbe_dev->current_out_index, ret; > > + > > + > > + if (!(vpbe_config->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; > > + > > + ret = mutex_lock_interruptible(&vpbe_dev->lock); > > + if (ret) > > + return ret; > > + > > + > > + 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) { > > + 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_display_config *vpbe_config = vpbe_dev->cfg; > > + int out_index = vpbe_dev->current_out_index; > > + struct vpbe_output *output = &vpbe_config->outputs[out_index]; > > + int i, j = 0; > > + > > + 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_display_config *vpbe_config = vpbe_dev->cfg; > > + int sd_index = vpbe_dev->current_sd_index, out_index = > > + vpbe_dev->current_out_index, ret; > > + > > + if (!(vpbe_config->outputs[out_index].output.capabilities & > > + V4L2_OUT_CAP_STD)) > > + return -EINVAL; > > + > > + ret = vpbe_get_std_info(vpbe_dev, *std_id); > > + if (ret) > > + return ret; > > + > > + ret = mutex_lock_interruptible(&vpbe_dev->lock); > > + if (ret) > > + return ret; > > + > > + 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) { > > + 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; > > Add empty line. I see this more often where the empty line separating the variable declarations from the code is missing. A quick scan through the code should fix this. > > > + 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_display_config *vpbe_config = vpbe_dev->cfg; > > + int out_index = vpbe_dev->current_out_index, ret = 0, i; > > + struct vpbe_enc_mode_info *preset_mode = NULL; > > + struct v4l2_dv_preset dv_preset; > > + > > + if ((NULL == mode_info) || (NULL == mode_info->name)) > > + return -EINVAL; > > + > > + for (i = 0; i < vpbe_config->outputs[out_index].num_modes; i++) { > > + if (!strcmp(mode_info->name, > > + vpbe_config->outputs[out_index].modes[i].name)) { > > + preset_mode = &vpbe_config->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) { > > + ret = vpbe_s_std(vpbe_dev, > > + &preset_mode->timings.std_id); > > + return ret; > > Why not just do 'return vpbe_s_std(...)'? Again, I see this construct a lot. > > > + } else if (preset_mode->timings_type & > > The 'else' keyword is not needed here. > > > + VPBE_ENC_DV_PRESET) { > > + dv_preset.preset = > > + preset_mode->timings.dv_preset; > > + ret = vpbe_s_dv_preset(vpbe_dev, &dv_preset); > > + return ret; > > + } > > + } > > + } > > + > > + /* Only custom timing should reach here */ > > + if (preset_mode == NULL) > > + return -EINVAL; > > + > > + ret = mutex_lock_interruptible(&vpbe_dev->lock); > > + if (ret) > > + return ret; > > + > > + if (!ret) { > > + 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); > > + if (strcmp("vpbe-osd", pdev->name) == 0) > > + osd_device = platform_get_drvdata(pdev); > > + if (strcmp("vpbe-venc", pdev->name) == 0) > > + venc_device = dev_get_platdata(&pdev->dev); > > + > > + 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; > > + int i, ret = 0, num_encoders; > > + struct i2c_adapter *i2c_adap; > > + int output_index; > > + int err; > > + > > + /* > > + * 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, NULL, > > + 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 */ > > + 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->module_name, > > + &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. > > + */ > > +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; > > + /* disaable 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 __init int vpbe_probe(struct platform_device *pdev) { > > + struct vpbe_display_config *vpbe_config; > > + struct vpbe_device *vpbe_dev; > > + > > + int ret = -EINVAL; > > + > > + if (NULL == pdev->dev.platform_data) { > > + v4l2_err(pdev->dev.driver, "Unable to get vpbe config\n"); > > + return -ENODEV; > > + } > > + > > + if (pdev->dev.platform_data == NULL) { > > + v4l2_err(pdev->dev.driver, "No platform data\n"); > > + return -ENODEV; > > + } > > + vpbe_config = pdev->dev.platform_data; > > + > > + if (!vpbe_config->module_name[0] || > > + !vpbe_config->osd.module_name[0] || > > + !vpbe_config->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 = vpbe_config; > > + vpbe_dev->ops = vpbe_dev_ops; > > + vpbe_dev->pdev = &pdev->dev; > > + > > + if (vpbe_config->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..c8853f2 > > --- /dev/null > > +++ b/include/media/davinci/vpbe.h > > @@ -0,0 +1,186 @@ > > +/* > > + * 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; either version 2 of the License. > > + * > > + * 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 > > + > > +/* 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_display_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_display_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; > > + > > + /* > > + * 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; > > +}; > > + > > +/* exported functions */ > > +struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev, > > + const char *venc_name); > > +#endif > > > > Regards, > > Hans > > -- > Hans Verkuil - video4linux developer - sponsored by Cisco > From hverkuil at xs4all.nl Wed Dec 8 10:10:54 2010 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Wed, 8 Dec 2010 17:10:54 +0100 Subject: [PATCH v3 2/6] davinci vpbe: VPBE display driver In-Reply-To: References: Message-ID: <1ce46142c86b6b1c51d58ea4650c4786.squirrel@webmail.xs4all.nl> > Hans, > Thanks for the comments. Only one comment from me. Rest everything I have > taken care. > > Thanks and Regards, > -Manju > > On Tue, Dec 07, 2010 at 02:13:45, Hans Verkuil wrote: >> Comments below... >> >> On Thursday, December 02, 2010 13:38:36 Manjunath Hadli wrote: >> > This patch implements the coe functionality of the dislay driver, >> > mainly controlling the VENC and other encoders, and acting as the one >> > point interface for the man V4L2 driver.This implements the cre of >> > each of the V4L2 IOCTLs. >> > >> > Signed-off-by: Manjunath Hadli >> > Signed-off-by: Muralidharan Karicheri >> > --- >> > drivers/media/video/davinci/vpbe.c | 847 >> ++++++++++++++++++++++++++++++++++++ >> > include/media/davinci/vpbe.h | 186 ++++++++ >> > 2 files changed, 1033 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..96c0eea >> > --- /dev/null >> > +++ b/drivers/media/video/davinci/vpbe.c >> > @@ -0,0 +1,847 @@ >> > +/* >> > + * 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; either version 2 of the License. >> > + * >> > + * 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 >> > + >> > + >> > +#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 struct osd_state *osd_device; static >> > +struct venc_platform_data *venc_device; 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(ef_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_display_config *vpbe_config = vpbe_dev->cfg; >> > + int index = vpbe_dev->current_sd_index; >> > + return ((index == 0) ? &vpbe_config->venc : >> > + &vpbe_config->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_display_config >> *vpbe_config, >> > + int index) >> > +{ >> > + char *encoder_name = vpbe_config->outputs[index].subdev_name; >> > + int i; >> > + >> > + /* Venc is always first */ >> > + if (!strcmp(encoder_name, vpbe_config->venc.module_name)) >> > + return 0; >> > + >> > + for (i = 0; i < vpbe_config->num_ext_encoders; i++) { >> > + if (!strcmp(encoder_name, >> > + vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; >> > + int temp_index = output->index; >> > + >> > + if (temp_index >= vpbe_config->num_outputs) >> > + return -EINVAL; >> > + >> > + *output = vpbe_config->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_display_config *cfg = vpbe_dev->cfg; >> > + struct vpbe_enc_mode_info var; >> > + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; >> > + struct vpbe_enc_mode_info var; >> > + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; >> > + struct vpbe_enc_mode_info var; >> > + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; >> > + struct vpbe_enc_mode_info var; >> > + int curr_output = vpbe_dev->current_out_index, 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 vpbe_display_config *vpbe_config = vpbe_dev->cfg; >> > + struct encoder_config_info *curr_enc_info = >> > + vpbe_current_encoder_info(vpbe_dev); >> > + int ret = 0, enc_out_index = 0, sd_index; >> > + >> > + if (index >= vpbe_config->num_outputs) >> > + return -EINVAL; >> > + >> > + ret = mutex_lock_interruptible(&vpbe_dev->lock); >> > + if (ret) >> > + return ret; >> >> I am not sure about this mutex. Is it still needed now that the main >> driver is serialized via opslock? And if it is, does it make sense to >> use the _interruptible variant? That only makes sense if some of the >> critical sections can sleep or otherwise take a long time. >> >> And if mutex_lock_interruptible() is indeed valid, then you should >> return -ERESTARTSYS on error. This will cause the scheduler to handle >> the signal and call the system call again. That's probably what you >> want. > > This is needed as the function might be called from multiple v4l2 video > Nodes. The ioctl is protected but it is for each node. I will add the > - ERESTARTSYS as you suggest. OK, clear. But isn't it better to just use mutex_lock here? As I mentioned, the interruptible variant is usually only relevant if you can expect substantial delays/sleeps in critical sections. All the checks against the return code of mutex_lock_interruptible add a lot of code, and if it is not necessary then it's better to use the normal mutex_lock. Regards, Hans -- Hans Verkuil - video4linux developer - sponsored by Cisco From nsekhar at ti.com Thu Dec 9 02:41:33 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Thu, 9 Dec 2010 14:11:33 +0530 Subject: [PATCH v4 1/2] davinci: am18x/da850/omap-l138: add support for higher speed grades Message-ID: <1291884094-19433-1-git-send-email-nsekhar@ti.com> AM18x/DA850/OMAP-L138 SoCs have variants that can operate at a maximum of 456 MHz at 1.3V operating point. Also the 1.2V operating point has a variant that can support a maximum of 375 MHz. This patch adds three new OPPs (456 MHz, 408 MHz and 372 MHz) to the list of DA850 OPPs. Not all silicon is qualified to run at higher speeds and unfortunately the maximum speed the chip can support can only be determined from the label on the package (not software readable). Because of this, we depend on the maximum speed grade information to be provided to us in some board specific way. The board informs the maximum speed grade information to the SoC by calling the da850_set_max_speed() function. Signed-off-by: Sekhar Nori --- Since v3: Fixed pre-div and post-div for 372MHz OPP based on PLLOUT range limitations documented in OMAP-L138 datasheet. AM1808 datasheet will be updated to match this. arch/arm/mach-davinci/da850.c | 75 ++++++++++++++++++++++------ arch/arm/mach-davinci/include/mach/da8xx.h | 7 +++ 2 files changed, 67 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 63916b9..78b5ae2 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -830,8 +830,7 @@ static void da850_set_async3_src(int pllnum) * According to the TRM, minimum PLLM results in maximum power savings. * The OPP definitions below should keep the PLLM as low as possible. * - * The output of the PLLM must be between 400 to 600 MHz. - * This rules out prediv of anything but divide-by-one for 24Mhz OSC input. + * The output of the PLLM must be between 300 to 600 MHz. */ struct da850_opp { unsigned int freq; /* in KHz */ @@ -842,6 +841,33 @@ struct da850_opp { unsigned int cvdd_max; /* in uV */ }; +static const struct da850_opp da850_opp_456 = { + .freq = 456000, + .prediv = 1, + .mult = 19, + .postdiv = 1, + .cvdd_min = 1300000, + .cvdd_max = 1350000, +}; + +static const struct da850_opp da850_opp_408 = { + .freq = 408000, + .prediv = 1, + .mult = 17, + .postdiv = 1, + .cvdd_min = 1300000, + .cvdd_max = 1350000, +}; + +static const struct da850_opp da850_opp_372 = { + .freq = 372000, + .prediv = 2, + .mult = 31, + .postdiv = 1, + .cvdd_min = 1200000, + .cvdd_max = 1320000, +}; + static const struct da850_opp da850_opp_300 = { .freq = 300000, .prediv = 1, @@ -876,6 +902,9 @@ static const struct da850_opp da850_opp_96 = { } static struct cpufreq_frequency_table da850_freq_table[] = { + OPP(456), + OPP(408), + OPP(372), OPP(300), OPP(200), OPP(96), @@ -886,6 +915,19 @@ static struct cpufreq_frequency_table da850_freq_table[] = { }; #ifdef CONFIG_REGULATOR +static int da850_set_voltage(unsigned int index); +static int da850_regulator_init(void); +#endif + +static struct davinci_cpufreq_config cpufreq_info = { + .freq_table = da850_freq_table, +#ifdef CONFIG_REGULATOR + .init = da850_regulator_init, + .set_voltage = da850_set_voltage, +#endif +}; + +#ifdef CONFIG_REGULATOR static struct regulator *cvdd; static int da850_set_voltage(unsigned int index) @@ -895,7 +937,7 @@ static int da850_set_voltage(unsigned int index) if (!cvdd) return -ENODEV; - opp = (struct da850_opp *) da850_freq_table[index].index; + opp = (struct da850_opp *) cpufreq_info.freq_table[index].index; return regulator_set_voltage(cvdd, opp->cvdd_min, opp->cvdd_max); } @@ -912,14 +954,6 @@ static int da850_regulator_init(void) } #endif -static struct davinci_cpufreq_config cpufreq_info = { - .freq_table = &da850_freq_table[0], -#ifdef CONFIG_REGULATOR - .init = da850_regulator_init, - .set_voltage = da850_set_voltage, -#endif -}; - static struct platform_device da850_cpufreq_device = { .name = "cpufreq-davinci", .dev = { @@ -928,12 +962,22 @@ static struct platform_device da850_cpufreq_device = { .id = -1, }; +unsigned int da850_max_speed = 300000; + int __init da850_register_cpufreq(char *async_clk) { + int i; + /* cpufreq driver can help keep an "async" clock constant */ if (async_clk) clk_add_alias("async", da850_cpufreq_device.name, async_clk, NULL); + for (i = 0; i < ARRAY_SIZE(da850_freq_table); i++) { + if (da850_freq_table[i].frequency <= da850_max_speed) { + cpufreq_info.freq_table = &da850_freq_table[i]; + break; + } + } return platform_device_register(&da850_cpufreq_device); } @@ -942,17 +986,18 @@ static int da850_round_armrate(struct clk *clk, unsigned long rate) { int i, ret = 0, diff; unsigned int best = (unsigned int) -1; + struct cpufreq_frequency_table *table = cpufreq_info.freq_table; rate /= 1000; /* convert to kHz */ - for (i = 0; da850_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { - diff = da850_freq_table[i].frequency - rate; + for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { + diff = table[i].frequency - rate; if (diff < 0) diff = -diff; if (diff < best) { best = diff; - ret = da850_freq_table[i].frequency; + ret = table[i].frequency; } } @@ -973,7 +1018,7 @@ static int da850_set_pll0rate(struct clk *clk, unsigned long index) struct pll_data *pll = clk->pll_data; int ret; - opp = (struct da850_opp *) da850_freq_table[index].index; + opp = (struct da850_opp *) cpufreq_info.freq_table[index].index; prediv = opp->prediv; mult = opp->mult; postdiv = opp->postdiv; diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index 4247b3f..e7f9520 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -28,6 +28,13 @@ extern void __iomem *da8xx_syscfg0_base; extern void __iomem *da8xx_syscfg1_base; /* + * If the DA850/OMAP-L138/AM18x SoC on board is of a higher speed grade + * (than the regular 300Mhz variant), the board code should set this up + * with the supported speed before calling da850_register_cpufreq(). + */ +extern unsigned int da850_max_speed; + +/* * The cp_intc interrupt controller for the da8xx isn't in the same * chunk of physical memory space as the other registers (like it is * on the davincis) so it needs to be mapped separately. It will be -- 1.7.3.2 From nsekhar at ti.com Thu Dec 9 02:41:34 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Thu, 9 Dec 2010 14:11:34 +0530 Subject: [PATCH v4 2/2] davinci: am18x/da850/omap-l138 evm: add support for higher speed grades In-Reply-To: <1291884094-19433-1-git-send-email-nsekhar@ti.com> References: <1291884094-19433-1-git-send-email-nsekhar@ti.com> Message-ID: <1291884094-19433-2-git-send-email-nsekhar@ti.com> Apart from the regular AM18x/DA850/OMAP-L138 SoC operating at 300MHz, these SoCs have variants that can operate at a maximum of 456MHz. Variants at 408Mhz and 375 Mhz are available as well. Not all silicon is qualified to run at higher speeds and unfortunately the maximum speed the chip can support can only be determined from the label on the package (not software readable). The EVM hardware for all these variants is the same (except for the actual SoC populated). U-Boot on the EVM sets up ATAG_REVISION to inform the OS regarding the speed grade supported by the silicon. We use this information to pass on the speed grade information to the SoC code. Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/board-da850-evm.c | 25 +++++++++++++++++++++++-- 1 files changed, 23 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index f89b0b7..893d9be 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -540,7 +540,7 @@ static struct regulator_init_data tps65070_regulator_data[] = { { .constraints = { .min_uV = 950000, - .max_uV = 1320000, + .max_uV = 1350000, .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS), .boot_on = 1, @@ -736,6 +736,27 @@ static struct edma_rsv_info *da850_edma_rsv[2] = { &da850_edma_cc1_rsv, }; +#ifdef CONFIG_CPU_FREQ +static __init int da850_evm_init_cpufreq(void) +{ + switch (system_rev & 0xF) { + case 3: + da850_max_speed = 456000; + break; + case 2: + da850_max_speed = 408000; + break; + case 1: + da850_max_speed = 372000; + break; + } + + return da850_register_cpufreq("pll0_sysclk3"); +} +#else +static __init int da850_evm_init_cpufreq(void) { return 0; } +#endif + static __init void da850_evm_init(void) { int ret; @@ -836,7 +857,7 @@ static __init void da850_evm_init(void) if (ret) pr_warning("da850_evm_init: rtc setup failed: %d\n", ret); - ret = da850_register_cpufreq("pll0_sysclk3"); + ret = da850_evm_init_cpufreq(); if (ret) pr_warning("da850_evm_init: cpufreq registration failed: %d\n", ret); -- 1.7.3.2 From nsekhar at ti.com Thu Dec 9 02:55:49 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 9 Dec 2010 14:25:49 +0530 Subject: [PATCH v7 11/12] davinci: add tnetv107x evm backlight device In-Reply-To: <1291733522-3626-12-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> <1291733522-3626-12-git-send-email-cyril@ti.com> Message-ID: Hi Cyril, On Tue, Dec 07, 2010 at 20:22:01, Chemparathy, Cyril wrote: > The tnetv107x evm board has a backlight device that is connected on one of the > SSP ports. This patch adds the board definitions necessary to plug the > backlight driver to the GPIO corresponding to this SSP pin. > > Signed-off-by: Cyril Chemparathy > --- > arch/arm/mach-davinci/board-tnetv107x-evm.c | 14 ++++++++++++++ > 1 files changed, 14 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-davinci/board-tnetv107x-evm.c b/arch/arm/mach-davinci/board-tnetv107x-evm.c > index e3863dd..ac62de2 100644 > --- a/arch/arm/mach-davinci/board-tnetv107x-evm.c > +++ b/arch/arm/mach-davinci/board-tnetv107x-evm.c > @@ -44,6 +44,7 @@ > #define EVM_MMC_WP_GPIO 21 > #define EVM_MMC_CD_GPIO 24 > #define EVM_SPI_CS_GPIO 54 > +#define EVM_BACKLIGHT_GPIO (SSP_GPIO_START + 2) > > static int initialize_gpio(int gpio, char *desc) > { > @@ -353,6 +354,12 @@ static struct spi_board_info spi_info[] __initconst = { > }, > }; > > +static struct platform_device backlight_device = { > + .name = "tps6116x", > + .id = -1, > + .dev.platform_data = (void *)EVM_BACKLIGHT_GPIO, > +}; > + > static __init void tnetv107x_evm_board_init(void) > { > davinci_cfg_reg_list(sdio1_pins); > @@ -364,6 +371,13 @@ static __init void tnetv107x_evm_board_init(void) > spi_register_board_info(spi_info, ARRAY_SIZE(spi_info)); > } > > +static int __init tnetv107x_evm_late_init(void) > +{ > + platform_device_register(&backlight_device); > + return 0; > +} > +late_initcall(tnetv107x_evm_late_init); This call should simply return if machine is not tnetv107x EVM. I didn't follow the entire series but wondering why platform device registration should be a late init call. Typically the driver probe can be made a late init call in case of init sequence dependencies. Thanks, Sekhar From manjunath.hadli at ti.com Thu Dec 9 04:45:43 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 9 Dec 2010 16:15:43 +0530 Subject: [PATCH v4 0/6] davinci vpbe: dm6446 v4l2 driver Message-ID: <1291891543-27156-1-git-send-email-manjunath.hadli@ti.com> version4 : addressed Hans's comments on: 1. replaced mutex_lock_interruptible() with mutex_lock() 2. replaced ntsc and pal macros with new equivalent macros 3. simplifying the code in the if-else condition 4. minor code corrections 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: platform specific additions davinci vpbe: Build infrastructure for VPBE driver arch/arm/mach-davinci/board-dm644x-evm.c | 79 +- arch/arm/mach-davinci/dm644x.c | 164 ++- arch/arm/mach-davinci/include/mach/dm644x.h | 4 + drivers/media/video/davinci/Kconfig | 22 + drivers/media/video/davinci/Makefile | 2 + .../media/video/davinci/davinci_vpbe_readme.txt | 100 + drivers/media/video/davinci/vpbe.c | 837 ++++++++ drivers/media/video/davinci/vpbe_display.c | 2099 ++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd.c | 1211 +++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 389 ++++ drivers/media/video/davinci/vpbe_venc.c | 574 ++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 189 ++ include/media/davinci/vpbe.h | 186 ++ include/media/davinci/vpbe_display.h | 146 ++ include/media/davinci/vpbe_osd.h | 397 ++++ include/media/davinci/vpbe_types.h | 93 + include/media/davinci/vpbe_venc.h | 38 + 17 files changed, 6511 insertions(+), 19 deletions(-) create mode 100644 drivers/media/video/davinci/davinci_vpbe_readme.txt 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 Thu Dec 9 04:46:08 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 9 Dec 2010 16:16:08 +0530 Subject: [PATCH v4 1/6] davinci vpbe: V4L2 display driver for DM644X SoC Message-ID: <1291891568-27325-1-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 is 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 Signed-off-by: Muralidharan Karicheri Acked-by: Muralidharan Karicheri --- drivers/media/video/davinci/vpbe_display.c | 2099 ++++++++++++++++++++++++++++ include/media/davinci/vpbe_display.h | 146 ++ include/media/davinci/vpbe_types.h | 93 ++ 3 files changed, 2338 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..a29a2a0 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_display.c @@ -0,0 +1,2099 @@ +/* + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#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 +#include "vpbe_venc_regs.h" + + +#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" + +static int debug; +static u32 video2_numbuffers = 3; +static u32 video3_numbuffers = 3; + +#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) +#define VPBE_DEFAULT_NUM_BUFS 3 + +static u32 video2_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; +static u32 video3_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; + +module_param(video2_numbuffers, uint, S_IRUGO); +module_param(video3_numbuffers, uint, S_IRUGO); +module_param(video2_bufsize, uint, S_IRUGO); +module_param(video3_bufsize, uint, S_IRUGO); +module_param(debug, int, 0644); + +static struct buf_config_params display_buf_config_params = { + .min_numbuffers = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[0] = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[1] = VPBE_DEFAULT_NUM_BUFS, + .min_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .min_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, +}; + +static struct vpbe_device *vpbe_dev; +static struct osd_state *osd_device; +static int vpbe_display_nr[] = { 2, 3 }; + +static struct v4l2_capability vpbe_display_videocap = { + .driver = VPBE_DISPLAY_DRIVER, + .bus_info = "platform", + .version = VPBE_DISPLAY_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, +}; + +static u8 layer_first_int[VPBE_DISPLAY_MAX_DEVICES]; + +__iomem void *reg_base_venc; + +static int venc_is_second_field() +{ + u32 val; + val = __raw_readl(reg_base_venc + VENC_VSTAT); + return ((val & VENC_VSTAT_FIDST) + == VENC_VSTAT_FIDST); +} + +/* + * vpbe_display_isr() + * ISR function. It changes status of the displayed buffer, takes next buffer + * from the queue and sets its address in VPBE registers + */ +static void vpbe_display_isr(unsigned int event, void *disp_obj) +{ + unsigned long jiffies_time = get_jiffies_64(); + struct timeval timevalue; + int i, fid; + unsigned long addr = 0; + struct vpbe_display_obj *layer = NULL; + struct vpbe_display *disp_dev = (struct vpbe_display *)disp_obj; + + /* Convert time represention from jiffies to timeval */ + jiffies_to_timeval(jiffies_time, &timevalue); + + 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; + /* Check the field format */ + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && + (event & OSD_END_OF_FRAME)) { + /* Progressive mode */ + if (layer_first_int[i]) + layer_first_int[i] = 0; + continue; + /* + * Mark status of the cur_frm to + * done and unlock semaphore on it + */ + + if (layer->cur_frm != layer->next_frm) { + layer->cur_frm->ts = timevalue; + 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; + } + /* Get the next buffer from buffer queue */ + spin_lock(&disp_dev->dma_queue_lock); + if (!list_empty(&layer->dma_queue)) { + layer->next_frm = + list_entry(layer->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove that buffer from the buffer queue */ + list_del(&layer->next_frm->queue); + /* Mark status of the buffer as 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_dev->cbcr_ofst); + } + spin_unlock(&disp_dev->dma_queue_lock); + } else { + /* + * Interlaced mode + * If it is first interrupt, ignore it + */ + if (layer_first_int[i]) { + layer_first_int[i] = 0; + return; + } + + layer->field_id ^= 1; + if (event & OSD_FIRST_FIELD) + fid = 0; + else if (event & OSD_SECOND_FIELD) + fid = 1; + else + return; + + /* + * If field id does not match with stored + * field id + */ + if (fid != layer->field_id) { + /* Make them in sync */ + if (0 == fid) + layer->field_id = fid; + + return; + } + /* + * device field id and local field id are + * in sync. If this is even field + */ + if (0 == fid) { + if (layer->cur_frm == layer->next_frm) + continue; + /* + * one frame is displayed If next frame is + * available, release cur_frm and move on + * copy frame display time + */ + layer->cur_frm->ts = timevalue; + /* Change status of the cur_frm */ + layer->cur_frm->state = VIDEOBUF_DONE; + /* unlock semaphore on cur_frm */ + wake_up_interruptible(&layer->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + layer->cur_frm = layer->next_frm; + } else if (1 == fid) { /* odd field */ + + if (list_empty(&layer->dma_queue) + || (layer->cur_frm != layer->next_frm)) + continue; + + /* + * one field is displayed configure + * the next frame if it is available + * otherwise hold on current frame + * Get next from the buffer queue + */ + spin_lock(&disp_dev->dma_queue_lock); + 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); + + /* 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_dev->cbcr_ofst); + spin_unlock(&disp_dev->dma_queue_lock); + } + } + } +} + +/* interrupt service routine */ +static irqreturn_t venc_isr(int irq, void *arg) +{ + static unsigned last_event; + unsigned event = 0; + + if (venc_is_second_field()) + 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; + + vpbe_display_isr(event, arg); + 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_display_obj *layer = fh->layer; + unsigned long addr; + int ret = 0; + + 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_display_obj *layer = fh->layer; + int buf_size; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); + + *size = layer->pix_fmt.sizeimage; + buf_size = + display_buf_config_params.layer_bufsize[layer->device_id]; + /* + * For MMAP, limit the memory allocation as per bootarg + * configured buffer size + */ + if (V4L2_MEMORY_MMAP == layer->memory) + if (*size > buf_size) + *size = buf_size; + + /* Store number of buffers allocated in numbuffer member */ + if (*count < display_buf_config_params.min_numbuffers) + *count = layer->numbuffers = + display_buf_config_params.numbuffers[layer->device_id]; + + 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_display_obj *layer = fh->layer; + struct vpbe_display *disp = fh->disp_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_display_obj *layer = fh->layer; + + 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_display_obj* +_vpbe_display_get_other_win(struct vpbe_display *disp_dev, + struct vpbe_display_obj *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_video_display_params(struct vpbe_display *disp_dev, + struct vpbe_display_obj *layer) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + unsigned long addr; + int ret = 0; + + 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_display_obj *otherlayer = + _vpbe_display_get_other_win(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_display_obj *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; + int h_scale = 0, v_scale = 0, h_exp = 0, v_exp = 0, temp; + 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)) { + temp = (cfg->xsize * + VPBE_DISPLAY_H_EXP_RATIO_N) / + VPBE_DISPLAY_H_EXP_RATIO_D; + if (temp <= expected_xsize) { + h_exp = 1; + cfg->xsize = temp; + } + } + } + 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)) { + temp = (cfg->ysize * + VPBE_DISPLAY_V_EXP_RATIO_N) / + VPBE_DISPLAY_V_EXP_RATIO_D; + if (temp <= expected_ysize) { + v_exp = 1; + cfg->ysize = temp; + } + } + } + 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_display_obj *layer, + int top, int left) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + + cfg->xpos = cfg->ypos = 0; + if (left + cfg->xsize <= vpbe_dev->current_timings.xres) + cfg->xpos = left; + if (top + cfg->ysize <= vpbe_dev->current_timings.yres) + cfg->ypos = top; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "new xpos = %d, ypos = %d\n", + cfg->xpos, cfg->ypos); +} + +static int vpbe_disp_check_window_params(struct vpbe_display *disp_dev, + struct v4l2_rect *c) +{ + if ((c->width == 0) + || ((c->width + c->left) > vpbe_dev->current_timings.xres) + || (c->height == 0) + || ((c->height + c->top) > vpbe_dev->current_timings.yres)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid crop values\n"); + return -1; + } + if ((c->height & 0x1) && (vpbe_dev->current_timings.interlaced)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "window height must be even for interlaced display\n"); + return -1; + } + return 0; +} + +/** + * 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. If application likes to add pads at the end of each line and + * end of the buffer , it can set bytesperline to line size and sizeimage to + * bytesperline * height of the buffer. If driver fills zero for active + * video width and height, and has requested user bytesperline and sizeimage, + * width and height is adjusted to maximum display limit or buffer width + * height which ever is lower + */ +static int vpbe_try_format(struct vpbe_display *disp_dev, + struct v4l2_pix_format *pixfmt, int check) +{ + int min_sizeimage, bpp, min_height = 1, min_width = 32, + max_width, max_height, user_info = 0; + + 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_ANY) { + 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->bytesperline) { + v4l2_err(&vpbe_dev->v4l2_dev, "bytesperline and width" + " cannot be zero\n"); + return -EINVAL; + } + + /* if user provided bytesperline, it must provide sizeimage as well */ + if (pixfmt->bytesperline && !pixfmt->sizeimage) { + v4l2_err(&vpbe_dev->v4l2_dev, + "sizeimage must be non zero, when user" + " provides bytesperline\n"); + return -EINVAL; + } + + /* adjust bytesperline as per hardware - multiple of 32 */ + if (!pixfmt->width) + pixfmt->width = pixfmt->bytesperline / bpp; + + if (!pixfmt->bytesperline) + pixfmt->bytesperline = pixfmt->width * bpp; + else + user_info = 1; + pixfmt->bytesperline = ((pixfmt->bytesperline + 31) & ~31); + + if (pixfmt->width < min_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is less than minimum," + "input width = %d, min_width = %d\n", + pixfmt->width, min_width); + return -EINVAL; + } + pixfmt->width = min_width; + } + + if (pixfmt->width > max_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is more than maximum," + "input width = %d, max_width = %d\n", + pixfmt->width, max_width); + return -EINVAL; + } + pixfmt->width = max_width; + } + + /* + * If height is zero, then atleast we need to have sizeimage + * to calculate height + */ + if (!pixfmt->height) { + if (user_info) { + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) { + /* + * for NV12 format, sizeimage is y-plane size + * + CbCr plane which is half of y-plane + */ + pixfmt->height = pixfmt->sizeimage / + (pixfmt->bytesperline + + (pixfmt->bytesperline >> 1)); + } else + pixfmt->height = pixfmt->sizeimage/ + pixfmt->bytesperline; + } + } + + if (pixfmt->height > max_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is more than maximum," + "input height = %d, max_height = %d\n", + pixfmt->height, max_height); + return -EINVAL; + } + pixfmt->height = max_height; + } + + if (pixfmt->height < min_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is less than minimum," + "input height = %d, min_height = %d\n", + pixfmt->height, min_height); + return -EINVAL; + } + pixfmt->height = min_width; + } + + /* if user has not provided bytesperline calculate it based on width */ + if (!user_info) + pixfmt->bytesperline = (((pixfmt->width * bpp) + 31) & ~31); + + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + min_sizeimage = pixfmt->bytesperline * pixfmt->height + + (pixfmt->bytesperline * pixfmt->height >> 1); + else + min_sizeimage = pixfmt->bytesperline * pixfmt->height; + + if (pixfmt->sizeimage < min_sizeimage) { + if (check && user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, "sizeimage is less, %d\n", + min_sizeimage); + return -EINVAL; + } + pixfmt->sizeimage = min_sizeimage; + } + 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_display_obj *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_display_obj *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_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id); + *cap = vpbe_display_videocap; + + return 0; +} + +static int vpbe_display_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct vpbe_display *disp_dev = video_drvdata(file); + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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) { + struct v4l2_rect *rect = &crop->c; + + if (rect->top < 0 || rect->left < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + + if (vpbe_disp_check_window_params(disp_dev, rect)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + 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); + + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + return ret; +} + +static int vpbe_display_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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) { + struct v4l2_rect *rect = &crop->c; + if (ret) + return ret; + 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; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + ret = -EINVAL; + } + + return ret; +} + +static int vpbe_display_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + + 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_display_obj *layer = fh->layer; + + 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) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Fill in the information about format */ + *pixfmt = layer->pix_fmt; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + + 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_display_obj *layer = fh->layer; + 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 > 0) { + 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 if (index == 1) { + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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; + } else { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* 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; + + /* Change of the default pixel format for both video windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { + struct vpbe_display_obj *otherlayer; + cfg->pixfmt = PIXFMT_NV12; + otherlayer = _vpbe_display_get_other_win(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); + + /* verify if readback values are as expected */ + if (layer->pix_fmt.width != cfg->xsize || + layer->pix_fmt.height != cfg->ysize || + layer->pix_fmt.bytesperline != layer->layer_info. + config.line_length || + (cfg->interlaced + && layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || + (!cfg->interlaced && layer->pix_fmt.field + != V4L2_FIELD_NONE)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "mismatch:layer conf params:\n"); + return -EINVAL; + } + } + + return 0; +} + +static int vpbe_display_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Check for valid field format */ + return vpbe_try_format(disp_dev, pixfmt, 0); + } + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; +} + +/** + * 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_display_obj *layer = fh->layer; + int ret = 0; + + 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; + } + } + 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) +{ + 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) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_outputs) { + 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_display_obj *layer = fh->layer; + int ret = 0; + + 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) { + 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 ret; +} + +/** + * 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) +{ + 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) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_dv_presets) { + 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 ret; +} + +/** + * 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_display_obj *layer = fh->layer; + int ret = 0; + + 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) { + 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 ret; +} + +/** + * 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) +{ + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + + 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_video_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_first_int[layer->device_id] = 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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_display_obj *layer = fh->layer; + + 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) +{ + unsigned int err = 0; + struct vpbe_fh *fh = filep->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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; +} + +static int vpbe_display_cfg_layer_default(enum vpbe_display_device_id id, + struct vpbe_display *disp_dev) +{ + int err = 0; + struct osd_layer_config *layer_config; + struct vpbe_display_obj *layer = disp_dev->dev[id]; + struct osd_layer_config *cfg = &layer->layer_info.config; + + /* 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"); + return -EBUSY; + } + + layer_config = cfg; + /* Set the default image and crop values */ + layer_config->pixfmt = PIXFMT_YCbCrI; + layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY; + layer->pix_fmt.bytesperline = layer_config->line_length = + vpbe_dev->current_timings.xres * 2; + + layer->pix_fmt.width = layer_config->xsize = + vpbe_dev->current_timings.xres; + layer->pix_fmt.height = layer_config->ysize = + vpbe_dev->current_timings.yres; + layer->pix_fmt.sizeimage = + layer->pix_fmt.bytesperline * layer->pix_fmt.height; + layer_config->xpos = 0; + layer_config->ypos = 0; + layer_config->interlaced = vpbe_dev->current_timings.interlaced; + + /* + * turn off ping-pong buffer and field inversion to fix + * the image shaking problem in 1080I mode + */ + + if (cfg->interlaced) + layer->pix_fmt.field = V4L2_FIELD_INTERLACED; + else + layer->pix_fmt.field = V4L2_FIELD_NONE; + + err = osd_device->ops.set_layer_config(osd_device, + layer->layer_info.id, + layer_config); + if (err < 0) { + /* Couldn't set layer */ + v4l2_err(&vpbe_dev->v4l2_dev, + "Display Manager failed to set osd layer\n"); + return -EBUSY; + } + + return 0; +} + +/* + * 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) +{ + int minor = iminor(file->f_path.dentry->d_inode); + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer; + struct vpbe_fh *fh = NULL; + int found = -1; + int i = 0; + + /* Check for valid minor number */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + layer = disp_dev->dev[i]; + if (minor == layer->video_dev->minor) { + found = i; + break; + } + } + + /* If not found, return error no device */ + if (0 > found) { + v4l2_err(&vpbe_dev->v4l2_dev, "device not found\n"); + return -ENODEV; + } + + /* 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) { + /* Configure the default values for the layer */ + if (vpbe_display_cfg_layer_default(layer->device_id, + disp_dev)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to configure video layer" + " for id = %d\n", layer->device_id); + 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_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); + /* If this is doing IO and other layer are not closed */ + if ((layer->usrs != 1) && fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "Close other instances\n"); + return -EAGAIN; + } + + /* 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_display_obj *otherlayer; + otherlayer = + _vpbe_display_get_other_win(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); + + if (strcmp("vpbe_controller", pdev->name) == 0) + vpbe_dev = platform_get_drvdata(pdev); + + if (strcmp("vpbe-osd", pdev->name) == 0) + osd_device = platform_get_drvdata(pdev); + + return 0; +} + +/*Configure the channels, buffer size */ +static int init_vpbe_layer_objects(int i) +{ + int free_buffer_index; + + /* Default number of buffers should be 3 */ + if ((video2_numbuffers > 0) && + (video2_numbuffers < display_buf_config_params.min_numbuffers)) + video2_numbuffers = display_buf_config_params.min_numbuffers; + if ((video3_numbuffers > 0) && + (video3_numbuffers < display_buf_config_params.min_numbuffers)) + video3_numbuffers = display_buf_config_params.min_numbuffers; + + /* + * Set buffer size to min buffers size if invalid + * buffer size is given + */ + if (video2_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]) + video2_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]; + + if (video3_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]) + video3_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]; + + /* set number of buffers, they could come from boot/args */ + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_0] = + video2_numbuffers; + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_1] = + video3_numbuffers; + + if (display_buf_config_params.numbuffers[0] == 0) + printk(KERN_ERR "no vid2 buffer allocated\n"); + if (display_buf_config_params.numbuffers[1] == 0) + printk(KERN_ERR "no vid3 buffer allocated\n"); + free_buffer_index = display_buf_config_params.numbuffers[i - 1]; + + 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 __init int vpbe_display_probe(struct platform_device *pdev) +{ + int i, j = 0, k, err = 0; + struct vpbe_display *disp_dev; + struct video_device *vbd = NULL; + struct vpbe_display_obj *vpbe_display_layer = NULL; + struct resource *res; + 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; + } + + /* Allocate memory for four plane display objects */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + disp_dev->dev[i] = + kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL); + /* If memory allocation fails, return error */ + if (!disp_dev->dev[i]) { + printk(KERN_ERR "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + spin_lock_init(&disp_dev->dev[i]->irqlock); + mutex_init(&disp_dev->dev[i]->opslock); + } + spin_lock_init(&disp_dev->dma_queue_lock); + + err = init_vpbe_layer_objects(i); + if (err) { + printk(KERN_ERR "Error initializing vpbe display\n"); + return err; + } + + /* + * 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, NULL, + vpbe_device_get); + if (err < 0) + return err; + + /* Initialize the vpbe display controller */ + if (NULL != vpbe_dev->ops.initialize) { + err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev); + if (err) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error initing vpbe\n"); + err = -ENOMEM; + goto probe_out; + } + } + + /* check the name of davinci device */ + if (vpbe_dev->cfg->module_name != NULL) + strcpy(vpbe_display_videocap.card, + vpbe_dev->cfg->module_name); + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[i]; + /* Allocate memory for video device */ + vbd = video_device_alloc(); + if (vbd == NULL) { + for (j = 0; j < i; j++) { + video_device_release( + disp_dev->dev[j]->video_dev); + } + v4l2_err(&vpbe_dev->v4l2_dev, "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + /* Initialize field of video device */ + vbd->release = video_device_release; + vbd->fops = &vpbe_fops; + vbd->ioctl_ops = &vpbe_ioctl_ops; + vbd->minor = -1; + vbd->v4l2_dev = &vpbe_dev->v4l2_dev; + vbd->lock = &vpbe_display_layer->opslock; + + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { + vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); + vbd->current_norm = + 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); + + /* Set video_dev to the video device */ + vpbe_display_layer->video_dev = vbd; + vpbe_display_layer->device_id = i; + + vpbe_display_layer->layer_info.id = + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); + if (display_buf_config_params.numbuffers[i] == 0) + vpbe_display_layer->memory = V4L2_MEMORY_USERPTR; + else + vpbe_display_layer->memory = V4L2_MEMORY_MMAP; + + /* Initialize field of the display layer objects */ + vpbe_display_layer->usrs = 0; + vpbe_display_layer->io_usrs = 0; + vpbe_display_layer->started = 0; + + /* Initialize prio member of layer object */ + v4l2_prio_init(&vpbe_display_layer->prio); + + /* Register video device */ + v4l2_info(&vpbe_dev->v4l2_dev, + "Trying to register VPBE display device.\n"); + v4l2_info(&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, + vpbe_display_nr[i]); + if (err) + goto probe_out; + /* set the driver data in platform device */ + platform_set_drvdata(pdev, disp_dev); + video_set_drvdata(vpbe_display_layer->video_dev, disp_dev); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to get VENC register address map\n"); + err = -ENODEV; + goto probe_out; + } + + reg_base_venc = ioremap_nocache(res->start, resource_size(res)); + if (!reg_base_venc) { + v4l2_err(&vpbe_dev->v4l2_dev, "Unable to map VENC IO space\n"); + err = -ENODEV; + goto probe_out; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + v4l2_err(&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(&vpbe_dev->v4l2_dev, "Unable to request interrupt\n"); + err = -ENODEV; + goto probe_out; + } + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); + return 0; +probe_out: + kfree(disp_dev); + + for (k = 0; k < j; k++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[k]; + /* Unregister video device */ + video_unregister_device(vpbe_display_layer->video_dev); + /* Release video device */ + video_device_release(vpbe_display_layer->video_dev); + vpbe_display_layer->video_dev = NULL; + } + return err; +} + +/* + * vpbe_display_remove() + * It un-register hardware layer from V4L2 driver + */ +static int vpbe_display_remove(struct platform_device *pdev) +{ + int i; + struct vpbe_display_obj *vpbe_display_layer; + struct vpbe_display *disp_dev = platform_get_drvdata(pdev); + struct resource *res; + + 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); + + vpbe_display_layer->video_dev = NULL; + } + 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 __init int vpbe_display_init(void) +{ + int err = 0; + + 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 DMXXX 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..90ad066 --- /dev/null +++ b/include/media/davinci/vpbe_display.h @@ -0,0 +1,146 @@ +/* + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef VPBE_DISPLAY_H +#define VPBE_DISPLAY_H + +#ifdef __KERNEL__ + +/* 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_display_obj { + /* number of buffers in fbuffers */ + unsigned int numbuffers; + /* 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; +}; + +/* 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_display_obj *dev[VPBE_DISPLAY_MAX_DEVICES]; +}; + +/* File handle structure */ +struct vpbe_fh { + /* vpbe device structure */ + struct vpbe_display *disp_dev; + /* pointer to layer object for opened device */ + struct vpbe_display_obj *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]; +}; + +static int venc_is_second_field(void); +#endif /* end of __KERNEL__ */ +#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..a4b8585 --- /dev/null +++ b/include/media/davinci/vpbe_types.h @@ -0,0 +1,93 @@ +/* + * 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; either version 2 of the License. + * + * 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_types { + DM644X_VPBE = 1, + DM355_VPBE, + DM365_VPBE, +}; + +/* 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 Thu Dec 9 04:46:42 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 9 Dec 2010 16:16:42 +0530 Subject: [PATCH v4 2/6] davinci vpbe: VPBE display driver Message-ID: <1291891602-27581-1-git-send-email-manjunath.hadli@ti.com> This patch implements the coe functionality of the dislay driver, mainly controlling the VENC and other encoders, and acting as the one point interface for the man V4L2 driver.This implements the cre of each of the V4L2 IOCTLs. Signed-off-by: Manjunath Hadli Signed-off-by: Muralidharan Karicheri Acked-by: Muralidharan Karicheri --- drivers/media/video/davinci/vpbe.c | 837 ++++++++++++++++++++++++++++++++++++ include/media/davinci/vpbe.h | 186 ++++++++ 2 files changed, 1023 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..751370f --- /dev/null +++ b/drivers/media/video/davinci/vpbe.c @@ -0,0 +1,837 @@ +/* + * 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; either version 2 of the License. + * + * 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 + + +#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 struct osd_state *osd_device; +static struct venc_platform_data *venc_device; +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(ef_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_display_config *vpbe_config = vpbe_dev->cfg; + int index = vpbe_dev->current_sd_index; + + return ((index == 0) ? &vpbe_config->venc : + &vpbe_config->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_display_config *vpbe_config, + int index) +{ + char *encoder_name = vpbe_config->outputs[index].subdev_name; + int i; + + /* Venc is always first */ + if (!strcmp(encoder_name, vpbe_config->venc.module_name)) + return 0; + + for (i = 0; i < vpbe_config->num_ext_encoders; i++) { + if (!strcmp(encoder_name, + vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; + int temp_index = output->index; + + if (temp_index >= vpbe_config->num_outputs) + return -EINVAL; + + *output = vpbe_config->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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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 vpbe_display_config *vpbe_config = vpbe_dev->cfg; + struct encoder_config_info *curr_enc_info = + vpbe_current_encoder_info(vpbe_dev); + int ret = 0, enc_out_index = 0, sd_index; + + if (index >= vpbe_config->num_outputs) + return -EINVAL; + + mutex_lock(&vpbe_dev->lock); + + sd_index = vpbe_dev->current_sd_index; + enc_out_index = vpbe_config->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, + vpbe_config->outputs[index].subdev_name)) { + /* Need to switch the encoder at the output */ + sd_index = vpbe_find_encoder_sd_index(vpbe_config, 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, + vpbe_config->outputs[index].default_mode); + if (!ret) { + 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_display_config *vpbe_config = vpbe_dev->cfg; + int i, ret = 0; + + for (i = 0; i < vpbe_config->num_outputs; i++) { + if (!strcmp(def_output, + vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; + int sd_index = vpbe_dev->current_sd_index; + int out_index = vpbe_dev->current_out_index, ret; + + + if (!(vpbe_config->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) { + 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_display_config *vpbe_config = vpbe_dev->cfg; + int out_index = vpbe_dev->current_out_index; + struct vpbe_output *output = &vpbe_config->outputs[out_index]; + int i, j = 0; + + 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_display_config *vpbe_config = vpbe_dev->cfg; + int sd_index = vpbe_dev->current_sd_index, out_index = + vpbe_dev->current_out_index, ret; + + if (!(vpbe_config->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) { + 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_display_config *vpbe_config = vpbe_dev->cfg; + int out_index = vpbe_dev->current_out_index, ret = 0, i; + struct vpbe_enc_mode_info *preset_mode = NULL; + struct v4l2_dv_preset dv_preset; + + if ((NULL == mode_info) || (NULL == mode_info->name)) + return -EINVAL; + + for (i = 0; i < vpbe_config->outputs[out_index].num_modes; i++) { + if (!strcmp(mode_info->name, + vpbe_config->outputs[out_index].modes[i].name)) { + preset_mode = &vpbe_config->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); + + if (!ret) { + 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); + if (strcmp("vpbe-osd", pdev->name) == 0) + osd_device = platform_get_drvdata(pdev); + if (strcmp("vpbe-venc", pdev->name) == 0) + venc_device = dev_get_platdata(&pdev->dev); + + 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; + int i, ret = 0, num_encoders; + struct i2c_adapter *i2c_adap; + int output_index; + int err; + + /* + * 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, NULL, + 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 */ + 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->module_name, + &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. + */ +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; + /* disaable 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 __init int vpbe_probe(struct platform_device *pdev) +{ + struct vpbe_display_config *vpbe_config; + struct vpbe_device *vpbe_dev; + + int ret = -EINVAL; + + if (NULL == pdev->dev.platform_data) { + v4l2_err(pdev->dev.driver, "Unable to get vpbe config\n"); + return -ENODEV; + } + + if (pdev->dev.platform_data == NULL) { + v4l2_err(pdev->dev.driver, "No platform data\n"); + return -ENODEV; + } + vpbe_config = pdev->dev.platform_data; + + if (!vpbe_config->module_name[0] || + !vpbe_config->osd.module_name[0] || + !vpbe_config->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 = vpbe_config; + vpbe_dev->ops = vpbe_dev_ops; + vpbe_dev->pdev = &pdev->dev; + + if (vpbe_config->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..c8853f2 --- /dev/null +++ b/include/media/davinci/vpbe.h @@ -0,0 +1,186 @@ +/* + * 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; either version 2 of the License. + * + * 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 + +/* 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_display_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_display_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; + + /* + * 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; +}; + +/* 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 Thu Dec 9 04:47:05 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 9 Dec 2010 16:17:05 +0530 Subject: [PATCH v4 3/6] davinci vpbe: OSD(On Screen Display) block Message-ID: <1291891625-27768-1-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 Signed-off-by: Muralidharan Karicheri Acked-by: Muralidharan Karicheri --- drivers/media/video/davinci/vpbe_osd.c | 1211 +++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++++++ include/media/davinci/vpbe_osd.h | 397 +++++++++ 3 files changed, 1997 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..2599c83 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd.c @@ -0,0 +1,1211 @@ +/* + * 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; 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 + * + */ +#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 __raw_readl(osd->osd_base + offset); +} + +static inline u32 osd_write(struct osd_state *sd, u32 val, u32 offset) +{ + struct osd_state *osd = sd; + __raw_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 = __raw_readl(addr) | mask; + + __raw_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 = __raw_readl(addr) & ~mask; + + __raw_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 = (__raw_readl(addr) & ~mask) | (val & mask); + __raw_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) +{ + +#ifdef CONFIG_DM6446_FIELD_INV_WORKAROUND + 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; + } +#endif + 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) +{ + int bmp_reg, bmp_offset, bmp_mask, bmp_shift; + static const int map_1bpp[] = { 0, 15 }; + static const int map_2bpp[] = { 0, 5, 10, 15 }; + + 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]; + unsigned long flags; + struct osd_layer_config *cfg = &win->lconfig; + + 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]; + unsigned long flags; + struct osd_layer_config *cfg = &win->lconfig; + + 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 = 0; + + /* 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; + int reject_config; + unsigned long flags; + + 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_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; + osd->vpbe_type = (enum vpbe_types)pdev->dev.platform_data; + 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) +{ + /* Register the driver */ + 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..9cd3839 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd_regs.h @@ -0,0 +1,389 @@ +/* + * 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; either version 2 of the License. + * + * 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 + +enum vpbe_soc_type { + DM644x = 0, + DM35x, + DM36x, +}; + +struct vpbe_osd_platform_data { + enum vpbe_soc_type soc; +}; + +#define DM644X_OSD_REG_BASE 0x01C72600 +#define DM644X_VPBE_REG_BASE 0x01C72780 + +#define DM355_VPSSCLK_REG_BASE 0x01C70000 +#define DM355_OSD_REG_BASE 0x01C70200 + +#define DM365_OSD_REG_BASE 0x01C71C00 +#define DM365_ISP5_REG_BASE 0x01C70000 + +#define OSD_REG_SIZE 0x00000100 + +/* SYS register addresses */ +#define SYS_VPSS_CLKCTL 0x01C40044 + +/* 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..9549f3e --- /dev/null +++ b/include/media/davinci/vpbe_osd.h @@ -0,0 +1,397 @@ +/* + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef _OSD_H +#define _OSD_H + +#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; +}; +/* OSD events. Should match with that in vpbe_venc.h */ +#define OSD_END_OF_FRAME 1 +#define OSD_FIRST_FIELD 2 +#define OSD_SECOND_FIELD 4 + +/* 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; + +/* TBD. Some of these operations here are to be removed and implemented + * as a ioctl call on the osd sub device node. Right now just trying to + * make this work. + */ +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_types 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; +}; + + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Dec 9 04:47:35 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 9 Dec 2010 16:17:35 +0530 Subject: [PATCH v4 4/6] davinci vpbe: VENC( Video Encoder) implementation Message-ID: <1291891655-28009-1-git-send-email-manjunath.hadli@ti.com> This patch adds the VENC or the Video encoder, whichis responsible for the blending of al 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 extenal and internal encoders of other kind to plug-in. Signed-off-by: Manjunath Hadli Signed-off-by: Muralidharan Karicheri Acked-by: Muralidharan Karicheri --- drivers/media/video/davinci/vpbe_venc.c | 574 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++++++++ include/media/davinci/vpbe_venc.h | 38 ++ 3 files changed, 801 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..fafb41a --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc.c @@ -0,0 +1,574 @@ +/* + * 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; 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 + */ +#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 __raw_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); + __raw_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); + + __raw_writel(val, venc->vdaccfg_reg); + + val = __raw_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) +{ + int ret = 0; + + 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: + ret = -EINVAL; + } + return ret; +} + +static void enabledigitaloutput(struct v4l2_subdev *sd, int benable) +{ + v4l2_dbg(debug, 2, sd, "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; + + 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; + + 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; + + 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; + + 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) +{ + int ret = 0; + + v4l2_dbg(debug, 1, sd, "venc_s_std_output\n"); + + if (norm & V4L2_STD_525_60) + ret = venc_set_ntsc(sd); + else if (norm & V4L2_STD_625_50) + ret = venc_set_pal(sd); + else + ret = -EINVAL; + return ret; +} + +static int venc_s_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *dv_preset) +{ + int ret = -EINVAL; + + 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 ret; +} + +static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct venc_state *venc = to_state(sd); + int max_output, lcd_out_index, ret = 0; + + v4l2_dbg(debug, 1, sd, "venc_s_routing\n"); + + max_output = 2 + venc->pdata->num_lcd_outputs; + lcd_out_index = 3; + + if (output >= max_output) + return -EINVAL; + + if (output < lcd_out_index) + ret = venc_set_dac(sd, output); + if (!ret) + venc->output = output; + return ret; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int venc_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + __iomem void *reg_base; + + if (reg->match.type == 2) + reg->val = venc_read(sd, reg->reg); + else if (reg->match.type == 3) { + reg_base = ioremap_nocache(0x01c70800, 0x80); + if (reg_base == NULL) + return -EINVAL; + reg->val = __raw_readl(reg_base + reg->reg); + iounmap(reg_base); + } else { + reg_base = ioremap_nocache(0x01c70000, 0x80); + if (reg_base == NULL) + return -1; + reg->val = __raw_readl(reg_base + reg->reg); + iounmap(reg_base); + } + return 0; +} +#endif + +static const struct v4l2_subdev_core_ops venc_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = venc_g_register, +#endif +}; + +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 = 0; + + /* 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) +{ + /* Register the driver */ + 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..38e4818 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc_regs.h @@ -0,0 +1,189 @@ +/* + * 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; either version 2 of the License. + * + * 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 register base addresses */ +#define DM644X_VENC_REG_BASE 0x01C72400 +#define DM644X_VPBE_REG_BASE 0x01C72780 + +#define DM355_VENC_REG_BASE 0x01C70400 + +#define DM365_VENC_REG_BASE 0x01C71E00 +#define DM365_ISP5_REG_BASE 0x01C70000 + +#define DM3XX_VDAC_CONFIG 0x01C4002C +#define DM355_USB_PHY_CTRL 0x01c40034 + +/* 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..47a977c --- /dev/null +++ b/include/media/davinci/vpbe_venc.h @@ -0,0 +1,38 @@ +/* + * 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; either version 2 of the License. + * + * 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 + +#define VPBE_VENC_SUBDEV_NAME "vpbe-venc" + +/* venc events */ +#define VENC_END_OF_FRAME 1 +#define VENC_FIRST_FIELD 2 +#define VENC_SECOND_FIELD 4 + +struct venc_platform_data { + enum vpbe_types venc_type; + int (*setup_clock)(enum vpbe_enc_timings_type type, + __u64 mode); + /* Number of LCD outputs supported */ + int num_lcd_outputs; +}; + + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Dec 9 04:48:06 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 9 Dec 2010 16:18:06 +0530 Subject: [PATCH v4 5/6] davinci vpbe: platform specific additions Message-ID: <1291891686-28206-1-git-send-email-manjunath.hadli@ti.com> This patch implements the overall device creation for the Video display driver, and addition of tables for the mode and output list. Signed-off-by: Manjunath Hadli Signed-off-by: Muralidharan Karicheri Acked-by: Muralidharan Karicheri --- arch/arm/mach-davinci/board-dm644x-evm.c | 79 +++++++++++-- arch/arm/mach-davinci/dm644x.c | 164 ++++++++++++++++++++++++++- arch/arm/mach-davinci/include/mach/dm644x.h | 4 + 3 files changed, 228 insertions(+), 19 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 34c8b41..e9b1243 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -166,18 +166,6 @@ static struct platform_device davinci_evm_nandflash_device = { .resource = davinci_evm_nandflash_resource, }; -static u64 davinci_fb_dma_mask = DMA_BIT_MASK(32); - -static struct platform_device davinci_fb_device = { - .name = "davincifb", - .id = -1, - .dev = { - .dma_mask = &davinci_fb_dma_mask, - .coherent_dma_mask = DMA_BIT_MASK(32), - }, - .num_resources = 0, -}; - static struct tvp514x_platform_data tvp5146_pdata = { .clk_polarity = 0, .hs_polarity = 1, @@ -606,8 +594,71 @@ static void __init evm_init_i2c(void) i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info)); } +#define VENC_STD_ALL (V4L2_STD_NTSC | V4L2_STD_PAL) +/* venc standards timings */ +static struct vpbe_enc_mode_info vbpe_enc_std_timings[] = { + {"ntsc", VPBE_ENC_STD, {V4L2_STD_525_60}, 1, 720, 480, + {11, 10}, {30000, 1001}, 0x79, 0, 0x10, 0, 0, 0, 0}, + {"pal", VPBE_ENC_STD, {V4L2_STD_625_50}, 1, 720, 576, + {54, 59}, {25, 1}, 0x7E, 0, 0x16, 0, 0, 0, 0}, +}; + +/* venc dv preset timings */ +static struct vpbe_enc_mode_info vbpe_enc_preset_timings[] = { + {"480p59_94", VPBE_ENC_DV_PRESET, {V4L2_DV_480P59_94}, 0, 720, 480, + {1, 1}, {5994, 100}, 0x80, 0, 0x20, 0, 0, 0, 0}, + {"576p50", VPBE_ENC_DV_PRESET, {V4L2_DV_576P50}, 0, 720, 576, + {1, 1}, {50, 1}, 0x7E, 0, 0x30, 0, 0, 0, 0}, +}; + +/* + * The outputs available from VPBE + ecnoders. Keep the + * the order same as that of encoders. First that from venc followed by that + * from encoders. Index in the output refers to index on a particular encoder. + * Driver uses this index to pass it to encoder when it supports more than + * one output. Application uses index of the array to set an output. + */ +static struct vpbe_output dm644x_vpbe_outputs[] = { + { + .output = { + .index = 0, + .name = "Composite", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .std = VENC_STD_ALL, + .capabilities = V4L2_OUT_CAP_STD, + }, + .subdev_name = VPBE_VENC_SUBDEV_NAME, + .default_mode = "ntsc", + .num_modes = ARRAY_SIZE(vbpe_enc_std_timings), + .modes = vbpe_enc_std_timings, + }, + { + .output = { + .index = 1, + .name = "Component", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .capabilities = V4L2_OUT_CAP_PRESETS, + }, + .subdev_name = VPBE_VENC_SUBDEV_NAME, + .default_mode = "480p59_94", + .num_modes = ARRAY_SIZE(vbpe_enc_preset_timings), + .modes = vbpe_enc_preset_timings, + }, +}; + +static struct vpbe_display_config vpbe_display_cfg = { + .module_name = "dm644x-vpbe-display", + .i2c_adapter_id = 1, + .osd = { + .module_name = VPBE_OSD_SUBDEV_NAME, + }, + .venc = { + .module_name = VPBE_VENC_SUBDEV_NAME, + }, + .num_outputs = ARRAY_SIZE(dm644x_vpbe_outputs), + .outputs = dm644x_vpbe_outputs, +}; static struct platform_device *davinci_evm_devices[] __initdata = { - &davinci_fb_device, &rtc_dev, }; @@ -620,6 +671,8 @@ davinci_evm_map_io(void) { /* setup input configuration for VPFE input devices */ dm644x_set_vpfe_config(&vpfe_cfg); + /* setup configuration for vpbe devices */ + dm644x_set_vpbe_display_config(&vpbe_display_cfg); dm644x_init(); } diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 5e5b0a7..e8b8e94 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -640,6 +640,142 @@ void dm644x_set_vpfe_config(struct vpfe_config *cfg) vpfe_capture_dev.dev.platform_data = cfg; } +static struct resource dm644x_osd_resources[] = { + { + .start = 0x01C72600, + .end = 0x01C72600 + 0x200, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 dm644x_osd_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device dm644x_osd_dev = { + .name = VPBE_OSD_SUBDEV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_osd_resources), + .resource = dm644x_osd_resources, + .dev = { + .dma_mask = &dm644x_osd_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = (void *)DM644X_VPBE, + }, +}; + +static struct resource dm644x_venc_resources[] = { + /* venc registers io space */ + { + .start = 0x01C72400, + .end = 0x01C72400 + 0x180, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 dm644x_venc_dma_mask = DMA_BIT_MASK(32); + +#define VPSS_CLKCTL 0x01C40044 +static void __iomem *vpss_clkctl_reg; + +/* TBD. Check what VENC_CLOCK_SEL settings for HDTV and EDTV */ +static int dm644x_venc_setup_clock(enum vpbe_enc_timings_type type, __u64 mode) +{ + int ret = 0; + + if (NULL == vpss_clkctl_reg) + return -EINVAL; + if (type == VPBE_ENC_STD) { + __raw_writel(0x18, vpss_clkctl_reg); + } else if (type == VPBE_ENC_DV_PRESET) { + switch ((unsigned int)mode) { + case V4L2_DV_480P59_94: + case V4L2_DV_576P50: + __raw_writel(0x19, vpss_clkctl_reg); + break; + case V4L2_DV_720P60: + case V4L2_DV_1080I60: + case V4L2_DV_1080P30: + /* + * For HD, use external clock source since HD requires higher + * clock rate + */ + __raw_writel(0xa, vpss_clkctl_reg); + break; + default: + ret = -EINVAL; + break; + } + } else + ret = -EINVAL; + + return ret; +} + + +static inline u32 dm644x_reg_modify(void *reg, u32 val, u32 mask) +{ + u32 new_val = (__raw_readl(reg) & ~mask) | (val & mask); + __raw_writel(new_val, reg); + return new_val; +} + +static u64 vpbe_display_dma_mask = DMA_BIT_MASK(32); + +static struct resource dm644x_v4l2_disp_resources[] = { + { + .start = IRQ_VENCINT, + .end = IRQ_VENCINT, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0x01C72400, + .end = 0x01C72400 + 0x180, + .flags = IORESOURCE_MEM, + }, + +}; +static struct platform_device vpbe_v4l2_display = { + .name = "vpbe-v4l2", + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_v4l2_disp_resources), + .resource = dm644x_v4l2_disp_resources, + .dev = { + .dma_mask = &vpbe_display_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; +struct venc_platform_data dm644x_venc_pdata = { + .venc_type = DM644X_VPBE, + .setup_clock = dm644x_venc_setup_clock, +}; + +static struct platform_device dm644x_venc_dev = { + .name = VPBE_VENC_SUBDEV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_venc_resources), + .resource = dm644x_venc_resources, + .dev = { + .dma_mask = &dm644x_venc_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = (void *)&dm644x_venc_pdata, + }, +}; + +static u64 dm644x_vpbe_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device dm644x_vpbe_dev = { + .name = "vpbe_controller", + .id = -1, + .dev = { + .dma_mask = &dm644x_vpbe_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +void dm644x_set_vpbe_display_config(struct vpbe_display_config *cfg) +{ + dm644x_vpbe_dev.dev.platform_data = cfg; +} + /*----------------------------------------------------------------------*/ static struct map_desc dm644x_io_desc[] = { @@ -767,20 +903,36 @@ void __init dm644x_init(void) davinci_common_init(&davinci_soc_info_dm644x); } +static struct platform_device *dm644x_video_devices[] __initdata = { + &dm644x_vpss_device, + &dm644x_ccdc_dev, + &vpfe_capture_dev, + &dm644x_osd_dev, + &dm644x_venc_dev, + &dm644x_vpbe_dev, + &vpbe_v4l2_display, +}; + +static int __init dm644x_init_video(void) +{ + /* Add ccdc clock aliases */ + clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); + clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); + vpss_clkctl_reg = ioremap_nocache(VPSS_CLKCTL, 4); + platform_add_devices(dm644x_video_devices, + ARRAY_SIZE(dm644x_video_devices)); + return 0; +} + static int __init dm644x_init_devices(void) { if (!cpu_is_davinci_dm644x()) return 0; /* Add ccdc clock aliases */ - clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); - clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); platform_device_register(&dm644x_edma_device); platform_device_register(&dm644x_emac_device); - platform_device_register(&dm644x_vpss_device); - platform_device_register(&dm644x_ccdc_dev); - platform_device_register(&vpfe_capture_dev); - + dm644x_init_video(); return 0; } postcore_initcall(dm644x_init_devices); diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h index 6fca568..bf7adcd 100644 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ b/arch/arm/mach-davinci/include/mach/dm644x.h @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #define DM644X_EMAC_BASE (0x01C80000) #define DM644X_EMAC_CNTRL_OFFSET (0x0000) @@ -43,5 +46,6 @@ void __init dm644x_init(void); void __init dm644x_init_asp(struct snd_platform_data *pdata); void dm644x_set_vpfe_config(struct vpfe_config *cfg); +void dm644x_set_vpbe_display_config(struct vpbe_display_config *cfg); #endif /* __ASM_ARCH_DM644X_H */ -- 1.6.2.4 From manjunath.hadli at ti.com Thu Dec 9 04:48:30 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 9 Dec 2010 16:18:30 +0530 Subject: [PATCH v4 6/6] davinci vpbe: Build infrastructure for VPBE driver Message-ID: <1291891710-28328-1-git-send-email-manjunath.hadli@ti.com> This patch adds the build infra-structure for Davinci VPBE dislay driver Signed-off-by: Manjunath Hadli Signed-off-by: Muralidharan Karicheri Acked-by: Muralidharan Karicheri --- drivers/media/video/davinci/Kconfig | 22 +++++ drivers/media/video/davinci/Makefile | 2 + .../media/video/davinci/davinci_vpbe_readme.txt | 100 ++++++++++++++++++++ 3 files changed, 124 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/davinci_vpbe_readme.txt diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig index 6b19540..dab32d5 100644 --- a/drivers/media/video/davinci/Kconfig +++ b/drivers/media/video/davinci/Kconfig @@ -91,3 +91,25 @@ 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" + 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" + select VIDEO_DM644X_VPBE + default y + help + Enables VPBE V4L2 Display driver on a DMXXX 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 diff --git a/drivers/media/video/davinci/davinci_vpbe_readme.txt b/drivers/media/video/davinci/davinci_vpbe_readme.txt new file mode 100644 index 0000000..e7aabba --- /dev/null +++ b/drivers/media/video/davinci/davinci_vpbe_readme.txt @@ -0,0 +1,100 @@ + + 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 following (in the same order as the list under file + partitioning):- + + 1. V4L2 display driver + Implements 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 sub device + 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 connetected 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 allowes + 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 ans setting of the external + encoders is not present, and would be a part of the next patch series. + + 3. Venc sub device + Responsible for setting outputs provides 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 cab be maintained in the board specific setup file to support + various LCD displays. + + 4. osd sub device + Osd sub device implements all osd layer management and hardware specific + features. In the legacy drivers (LSPxxx), the hardware specific features + are configured through proprietary IOCTLs at the fb device interface. Since + sub devices are going to support device nodes, application will be able + to configure the hardware feayture directly by opening the osd sub device + node and by calling the related IOCTL. So these proprietary IOCTLs are + to be removed from the FB Device driver when doing up port of these drivers to + mainline kernel. The V4L2 and FB device nodes supports only IOCTLS as per + the associated spec. The rest of the IOCTLs are to be moved to osd and + venc sub devices. + + Current status:- + + A build tested version of vpbe controller is available. + + Following are TBDs. + + vpbe display controller + - review and modify the handling of 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. + + v4l2 driver + - A version is already developed which is to be cleaned up and unit tested + + FB drivers + - Add support for fbdev drivers.- Ready and part of subsequent patches. \ No newline at end of file -- 1.6.2.4 From nsekhar at ti.com Thu Dec 9 05:00:28 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 9 Dec 2010 16:30:28 +0530 Subject: [PATCH v7 11/12] davinci: add tnetv107x evm backlight device In-Reply-To: References: <1291733522-3626-1-git-send-email-cyril@ti.com> <1291733522-3626-12-git-send-email-cyril@ti.com> Message-ID: On Thu, Dec 09, 2010 at 14:25:49, Nori, Sekhar wrote: > This call should simply return if machine is not tnetv107x EVM. > > I didn't follow the entire series but wondering why > platform device registration should be a late init call. > Typically the driver probe can be made a late init call > in case of init sequence dependencies. I meant driver init, not probe. Thanks, Sekhar From balbi at ti.com Thu Dec 9 05:20:19 2010 From: balbi at ti.com (Felipe Balbi) Date: Thu, 9 Dec 2010 13:20:19 +0200 Subject: [PATCH v2] USB: musb: Prevent Transmit Buffer Descriptor corruption In-Reply-To: <4CFEAA69.4030503@seektech.com> References: <4CFEAA69.4030503@seektech.com> Message-ID: <20101209112019.GG4797@legolas.emea.dhcp.ti.com> Hi, On Tue, Dec 07, 2010 at 01:43:05PM -0800, Paul Stuart wrote: >cppi_next_tx_segment is not checking for Transmit Buffer Descriptor >ownership before modifying parameters. > >Transmit Buffer Descriptor ram is shared between the host processor >and the DMA. The "Ownership" bit is set by the host processor to give >the DMA ownership of the Transmit Buffer Descriptor, and the bit is >cleared by the DMA to return ownership to the host processor. > >On USB Tx, when the system is heavily loaded, cppi_next_tx_segment can >overwrite a Transmit Buffer Descriptor that is still owned by the DMA, >causing DMA truncation error to fire, resulting in a channel abort. >This proposed fix adds a check for host processor ownership of the bd >and does not proceed to program it until the DMA has ended ownership. > > >Condition rarely occurs, so USB write speed is not negatively impacted. > >Tested on DM365 Sergei, do you have anything against this patch ? I'm asking because you have been more closely working on DaVinci. -- balbi From hverkuil at xs4all.nl Thu Dec 9 05:47:42 2010 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Thu, 9 Dec 2010 12:47:42 +0100 Subject: [PATCH v4 0/6] davinci vpbe: dm6446 v4l2 driver In-Reply-To: <1291891543-27156-1-git-send-email-manjunath.hadli@ti.com> References: <1291891543-27156-1-git-send-email-manjunath.hadli@ti.com> Message-ID: > version4 : addressed Hans's comments > on: > 1. replaced mutex_lock_interruptible() with mutex_lock() > 2. replaced ntsc and pal macros with new equivalent macros > 3. simplifying the code in the if-else condition > 4. minor code corrections For the whole patch series: Acked-by: Hans Verkuil Regards, Hans > > 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: platform specific additions > davinci vpbe: Build infrastructure for VPBE driver > > arch/arm/mach-davinci/board-dm644x-evm.c | 79 +- > arch/arm/mach-davinci/dm644x.c | 164 ++- > arch/arm/mach-davinci/include/mach/dm644x.h | 4 + > drivers/media/video/davinci/Kconfig | 22 + > drivers/media/video/davinci/Makefile | 2 + > .../media/video/davinci/davinci_vpbe_readme.txt | 100 + > drivers/media/video/davinci/vpbe.c | 837 ++++++++ > drivers/media/video/davinci/vpbe_display.c | 2099 > ++++++++++++++++++++ > drivers/media/video/davinci/vpbe_osd.c | 1211 +++++++++++ > drivers/media/video/davinci/vpbe_osd_regs.h | 389 ++++ > drivers/media/video/davinci/vpbe_venc.c | 574 ++++++ > drivers/media/video/davinci/vpbe_venc_regs.h | 189 ++ > include/media/davinci/vpbe.h | 186 ++ > include/media/davinci/vpbe_display.h | 146 ++ > include/media/davinci/vpbe_osd.h | 397 ++++ > include/media/davinci/vpbe_types.h | 93 + > include/media/davinci/vpbe_venc.h | 38 + > 17 files changed, 6511 insertions(+), 19 deletions(-) > create mode 100644 drivers/media/video/davinci/davinci_vpbe_readme.txt > 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 > > -- Hans Verkuil - video4linux developer - sponsored by Cisco From sshtylyov at mvista.com Thu Dec 9 06:12:00 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Thu, 09 Dec 2010 15:12:00 +0300 Subject: [PATCH v3 6/6] davinci vpbe: Build infrastructure for VPBE driver In-Reply-To: <1291293583-2810-1-git-send-email-manjunath.hadli@ti.com> References: <1291293583-2810-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <4D00C790.8030507@mvista.com> Hello. On 02-12-2010 15:39, Manjunath Hadli wrote: > This patch adds the build infra-structure for Davinci > VPBE dislay driver > Signed-off-by: Manjunath Hadli > Signed-off-by: Muralidharan Karicheri [...] > diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig > index 6b19540..dab32d5 100644 > --- a/drivers/media/video/davinci/Kconfig > +++ b/drivers/media/video/davinci/Kconfig > @@ -91,3 +91,25 @@ 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" > + select VIDEO_VPSS_SYSTEM > + select VIDEOBUF_DMA_CONTIG Please use tabs uniformly to indent. > diff --git a/drivers/media/video/davinci/davinci_vpbe_readme.txt b/drivers/media/video/davinci/davinci_vpbe_readme.txt > new file mode 100644 > index 0000000..e7aabba > --- /dev/null > +++ b/drivers/media/video/davinci/davinci_vpbe_readme.txt > @@ -0,0 +1,100 @@ > + > + 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 following (in the same order as the list under file ^ "the" missing. > + partitioning):- > + > + 1. V4L2 display driver > + Implements 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 sub device > + 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 connetected 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 allowes Only "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 Space missing after period. > + patch series, the interconnection and enabling ans setting of the external > + encoders is not present, and would be a part of the next patch series. > + > + 3. Venc sub device > + Responsible for setting outputs provides Provided? > through internal dacs and also DACs. > + 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" Space not needed after (. > + output name to board-xxx-evm.c). A table of timings for various LCDs > + supported cab Can? > be maintained in the board specific setup file to support > + various LCD displays. > + > + 4. osd sub device OSD? What doesn it mean BTW? > + Osd sub device implements all osd layer management and hardware specific > + features. In the legacy drivers (LSPxxx), the hardware specific features > + are configured through proprietary IOCTLs at the fb device interface. Since > + sub devices I think it's either "sub-devices" or "subdevices"... > are going to support device nodes, application will be able > + to configure the hardware feayture Only "feature". > directly by opening the osd sub device > + node and by calling the related IOCTL. So these proprietary IOCTLs are > + to be removed from the FB Device driver when doing up port of these drivers to > + mainline kernel. The V4L2 and FB device nodes supports only IOCTLS as per > + the associated spec. The rest of the IOCTLs are to be moved to osd and > + venc sub devices. > + > + Current status:- > + > + A build tested version of vpbe controller is available. > + > + Following are TBDs. > + > + vpbe display controller > + - review and modify the handling of 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. > + > + v4l2 driver > + - A version is already developed which is to be cleaned up and unit tested > + > + FB drivers > + - Add support for fbdev drivers.- Forgot newline here? > Ready and part of subsequent patches. > \ No newline at end of file Here as well. :-) WBR, Sergei From sshtylyov at mvista.com Thu Dec 9 07:07:59 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Thu, 09 Dec 2010 16:07:59 +0300 Subject: [PATCH v2] USB: musb: Prevent Transmit Buffer Descriptor corruption In-Reply-To: <20101209112019.GG4797@legolas.emea.dhcp.ti.com> References: <4CFEAA69.4030503@seektech.com> <20101209112019.GG4797@legolas.emea.dhcp.ti.com> Message-ID: <4D00D4AF.4060506@ru.mvista.com> Hello. On 09-12-2010 14:20, Felipe Balbi wrote: >> cppi_next_tx_segment is not checking for Transmit Buffer Descriptor >> ownership before modifying parameters. >> Transmit Buffer Descriptor ram is shared between the host processor and the >> DMA. The "Ownership" bit is set by the host processor to give the DMA >> ownership of the Transmit Buffer Descriptor, and the bit is cleared by the >> DMA to return ownership to the host processor. >> On USB Tx, when the system is heavily loaded, cppi_next_tx_segment can >> overwrite a Transmit Buffer Descriptor that is still owned by the DMA, >> causing DMA truncation error to fire, resulting in a channel abort. This >> proposed fix adds a check for host processor ownership of the bd and does >> not proceed to program it until the DMA has ended ownership. >> Condition rarely occurs, so USB write speed is not negatively impacted. >> Tested on DM365 > Sergei, do you have anything against this patch ? I'm asking because you > have been more closely working on DaVinci. Nothing, though I don't work with DaVinci much, mostly with DA830. You should probably ask Swaminathan Subbramanian. WBR, Sergei From balbi at ti.com Thu Dec 9 07:50:23 2010 From: balbi at ti.com (Felipe Balbi) Date: Thu, 9 Dec 2010 15:50:23 +0200 Subject: [PATCH v2] USB: musb: Prevent Transmit Buffer Descriptor corruption In-Reply-To: <4D00D4AF.4060506@ru.mvista.com> References: <4CFEAA69.4030503@seektech.com> <20101209112019.GG4797@legolas.emea.dhcp.ti.com> <4D00D4AF.4060506@ru.mvista.com> Message-ID: <20101209135023.GD2825@legolas.emea.dhcp.ti.com> On Thu, Dec 09, 2010 at 04:07:59PM +0300, Sergei Shtylyov wrote: >Hello. > >On 09-12-2010 14:20, Felipe Balbi wrote: > >>>cppi_next_tx_segment is not checking for Transmit Buffer Descriptor >>>ownership before modifying parameters. > >>>Transmit Buffer Descriptor ram is shared between the host processor and the >>>DMA. The "Ownership" bit is set by the host processor to give the DMA >>>ownership of the Transmit Buffer Descriptor, and the bit is cleared by the >>>DMA to return ownership to the host processor. > >>>On USB Tx, when the system is heavily loaded, cppi_next_tx_segment can >>>overwrite a Transmit Buffer Descriptor that is still owned by the DMA, >>>causing DMA truncation error to fire, resulting in a channel abort. This >>>proposed fix adds a check for host processor ownership of the bd and does >>>not proceed to program it until the DMA has ended ownership. > >>>Condition rarely occurs, so USB write speed is not negatively impacted. > >>>Tested on DM365 > >>Sergei, do you have anything against this patch ? I'm asking because you >>have been more closely working on DaVinci. > > Nothing, though I don't work with DaVinci much, mostly with DA830. >You should probably ask Swaminathan Subbramanian. Ok, I'll wait someone working more closely with DaVinci to give me at least a Tested-by before I apply this one :-) -- balbi From cyril at ti.com Thu Dec 9 08:58:41 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Thu, 09 Dec 2010 09:58:41 -0500 Subject: [PATCH v7 11/12] davinci: add tnetv107x evm backlight device In-Reply-To: References: <1291733522-3626-1-git-send-email-cyril@ti.com> <1291733522-3626-12-git-send-email-cyril@ti.com> Message-ID: <4D00EEA1.8070201@ti.com> On 12/09/2010 06:00 AM, Nori, Sekhar wrote: > On Thu, Dec 09, 2010 at 14:25:49, Nori, Sekhar wrote: > >> This call should simply return if machine is not tnetv107x EVM. >> >> I didn't follow the entire series but wondering why >> platform device registration should be a late init call. >> Typically the driver probe can be made a late init call >> in case of init sequence dependencies. > > I meant driver init, not probe. This was done to handle driver dependencies, and is meant to be temporary (until a real driver dependency framework comes in). Please see [1] for a bit more of background on changing drivers to use module_init(). Regards - Cyril. [1] http://linux.davincidsp.com/pipermail/davinci-linux-open-source/2010-November/020987.html From paul_stuart at seektech.com Thu Dec 9 09:58:26 2010 From: paul_stuart at seektech.com (Paul Stuart) Date: Thu, 09 Dec 2010 07:58:26 -0800 Subject: [PATCH v2] USB: musb: Prevent Transmit Buffer Descriptor corruption In-Reply-To: <20101209135023.GD2825@legolas.emea.dhcp.ti.com> References: <4CFEAA69.4030503@seektech.com> <20101209112019.GG4797@legolas.emea.dhcp.ti.com> <4D00D4AF.4060506@ru.mvista.com> <20101209135023.GD2825@legolas.emea.dhcp.ti.com> Message-ID: <4D00FCA2.1000702@seektech.com> Hi, For anyone interested in testing this patch, here is some info to reproduce the problem: Platform: DM365 dvsdk 3.10.00.19 Run dvsdk encode-decode demo in the background while dd'ing ~500 MB out to a thumb drive. After about 200 MB or so, cppi_channel_abort is called (because of truncation error mentioned below) and system will hang in the abort function's while loop waiting for a tx_complete. For us this was 100% repeatable. Interested to know if anyone tries it and _doesn't_ see the badness ensue. Thanks, Paul Felipe Balbi wrote: > On Thu, Dec 09, 2010 at 04:07:59PM +0300, Sergei Shtylyov wrote: > >> Hello. >> >> On 09-12-2010 14:20, Felipe Balbi wrote: >> >> >>>> cppi_next_tx_segment is not checking for Transmit Buffer Descriptor >>>> ownership before modifying parameters. >>>> >>>> Transmit Buffer Descriptor ram is shared between the host processor and the >>>> DMA. The "Ownership" bit is set by the host processor to give the DMA >>>> ownership of the Transmit Buffer Descriptor, and the bit is cleared by the >>>> DMA to return ownership to the host processor. >>>> >>>> On USB Tx, when the system is heavily loaded, cppi_next_tx_segment can >>>> overwrite a Transmit Buffer Descriptor that is still owned by the DMA, >>>> causing DMA truncation error to fire, resulting in a channel abort. This >>>> proposed fix adds a check for host processor ownership of the bd and does >>>> not proceed to program it until the DMA has ended ownership. >>>> >>>> Condition rarely occurs, so USB write speed is not negatively impacted. >>>> >>>> Tested on DM365 >>>> >>> Sergei, do you have anything against this patch ? I'm asking because you >>> have been more closely working on DaVinci. >>> >> Nothing, though I don't work with DaVinci much, mostly with DA830. >> You should probably ask Swaminathan Subbramanian. >> > > Ok, I'll wait someone working more closely with DaVinci to give me at > least a Tested-by before I apply this one :-) > > From bengardiner at nanometrics.ca Thu Dec 9 15:51:03 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Thu, 9 Dec 2010 16:51:03 -0500 Subject: [PATCH v6 1/5] Input: add input driver for polled GPIO buttons In-Reply-To: References: Message-ID: From: Gabor Juhos The existing gpio-keys driver can be usable only for GPIO lines with interrupt support. Several devices have buttons connected to a GPIO line which is not capable to generate interrupts. This patch adds a new input driver using the generic GPIO layer and the input-polldev to support such buttons. [Ben Gardiner Signed-off-by: Ben Gardiner Tested-by: Ben Gardiner Signed-off-by: Dmitry Torokhov (cherry picked from commit 0e7d0c860a0dee49dacb7bbb248d1eba637075ad) --- This a copy of the commit -- I included in the patch series since linux-davinci/master does not currently have it; but 2.6.37-rc5 does. --- drivers/input/keyboard/Kconfig | 16 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/gpio_keys_polled.c | 261 +++++++++++++++++++++++++++++ include/linux/gpio_keys.h | 2 + 4 files changed, 280 insertions(+), 0 deletions(-) create mode 100644 drivers/input/keyboard/gpio_keys_polled.c diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index b8c51b9..3a87f3b 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -179,6 +179,22 @@ config KEYBOARD_GPIO To compile this driver as a module, choose M here: the module will be called gpio_keys. +config KEYBOARD_GPIO_POLLED + tristate "Polled GPIO buttons" + depends on GENERIC_GPIO + select INPUT_POLLDEV + help + This driver implements support for buttons connected + to GPIO pins that are not capable of generating interrupts. + + Say Y here if your device has buttons connected + directly to such GPIO pins. Your board-specific + setup logic must also provide a platform device, + with configuration data saying which GPIOs are used. + + To compile this driver as a module, choose M here: the + module will be called gpio_keys_polled. + config KEYBOARD_TCA6416 tristate "TCA6416 Keypad Support" depends on I2C diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index a34452e..622de73 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o +obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c new file mode 100644 index 0000000..4c17aff --- /dev/null +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -0,0 +1,261 @@ +/* + * Driver for buttons on GPIO lines not capable of generating interrupts + * + * Copyright (C) 2007-2010 Gabor Juhos + * Copyright (C) 2010 Nuno Goncalves + * + * This file was based on: /drivers/input/misc/cobalt_btns.c + * Copyright (C) 2007 Yoichi Yuasa + * + * also was based on: /drivers/input/keyboard/gpio_keys.c + * Copyright 2005 Phil Blundell + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "gpio-keys-polled" + +struct gpio_keys_button_data { + int last_state; + int count; + int threshold; + int can_sleep; +}; + +struct gpio_keys_polled_dev { + struct input_polled_dev *poll_dev; + struct device *dev; + struct gpio_keys_platform_data *pdata; + struct gpio_keys_button_data data[0]; +}; + +static void gpio_keys_polled_check_state(struct input_dev *input, + struct gpio_keys_button *button, + struct gpio_keys_button_data *bdata) +{ + int state; + + if (bdata->can_sleep) + state = !!gpio_get_value_cansleep(button->gpio); + else + state = !!gpio_get_value(button->gpio); + + if (state != bdata->last_state) { + unsigned int type = button->type ?: EV_KEY; + + input_event(input, type, button->code, + !!(state ^ button->active_low)); + input_sync(input); + bdata->count = 0; + bdata->last_state = state; + } +} + +static void gpio_keys_polled_poll(struct input_polled_dev *dev) +{ + struct gpio_keys_polled_dev *bdev = dev->private; + struct gpio_keys_platform_data *pdata = bdev->pdata; + struct input_dev *input = dev->input; + int i; + + for (i = 0; i < bdev->pdata->nbuttons; i++) { + struct gpio_keys_button_data *bdata = &bdev->data[i]; + + if (bdata->count < bdata->threshold) + bdata->count++; + else + gpio_keys_polled_check_state(input, &pdata->buttons[i], + bdata); + } +} + +static void gpio_keys_polled_open(struct input_polled_dev *dev) +{ + struct gpio_keys_polled_dev *bdev = dev->private; + struct gpio_keys_platform_data *pdata = bdev->pdata; + + if (pdata->enable) + pdata->enable(bdev->dev); +} + +static void gpio_keys_polled_close(struct input_polled_dev *dev) +{ + struct gpio_keys_polled_dev *bdev = dev->private; + struct gpio_keys_platform_data *pdata = bdev->pdata; + + if (pdata->disable) + pdata->disable(bdev->dev); +} + +static int __devinit gpio_keys_polled_probe(struct platform_device *pdev) +{ + struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct gpio_keys_polled_dev *bdev; + struct input_polled_dev *poll_dev; + struct input_dev *input; + int error; + int i; + + if (!pdata || !pdata->poll_interval) + return -EINVAL; + + bdev = kzalloc(sizeof(struct gpio_keys_polled_dev) + + pdata->nbuttons * sizeof(struct gpio_keys_button_data), + GFP_KERNEL); + if (!bdev) { + dev_err(dev, "no memory for private data\n"); + return -ENOMEM; + } + + poll_dev = input_allocate_polled_device(); + if (!poll_dev) { + dev_err(dev, "no memory for polled device\n"); + error = -ENOMEM; + goto err_free_bdev; + } + + poll_dev->private = bdev; + poll_dev->poll = gpio_keys_polled_poll; + poll_dev->poll_interval = pdata->poll_interval; + poll_dev->open = gpio_keys_polled_open; + poll_dev->close = gpio_keys_polled_close; + + input = poll_dev->input; + + input->evbit[0] = BIT(EV_KEY); + input->name = pdev->name; + input->phys = DRV_NAME"/input0"; + input->dev.parent = &pdev->dev; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_keys_button *button = &pdata->buttons[i]; + struct gpio_keys_button_data *bdata = &bdev->data[i]; + unsigned int gpio = button->gpio; + unsigned int type = button->type ?: EV_KEY; + + if (button->wakeup) { + dev_err(dev, DRV_NAME " does not support wakeup\n"); + error = -EINVAL; + goto err_free_gpio; + } + + error = gpio_request(gpio, + button->desc ? button->desc : DRV_NAME); + if (error) { + dev_err(dev, "unable to claim gpio %u, err=%d\n", + gpio, error); + goto err_free_gpio; + } + + error = gpio_direction_input(gpio); + if (error) { + dev_err(dev, + "unable to set direction on gpio %u, err=%d\n", + gpio, error); + goto err_free_gpio; + } + + bdata->can_sleep = gpio_cansleep(gpio); + bdata->last_state = -1; + bdata->threshold = DIV_ROUND_UP(button->debounce_interval, + pdata->poll_interval); + + input_set_capability(input, type, button->code); + } + + bdev->poll_dev = poll_dev; + bdev->dev = dev; + bdev->pdata = pdata; + platform_set_drvdata(pdev, bdev); + + error = input_register_polled_device(poll_dev); + if (error) { + dev_err(dev, "unable to register polled device, err=%d\n", + error); + goto err_free_gpio; + } + + /* report initial state of the buttons */ + for (i = 0; i < pdata->nbuttons; i++) + gpio_keys_polled_check_state(input, &pdata->buttons[i], + &bdev->data[i]); + + return 0; + +err_free_gpio: + while (--i >= 0) + gpio_free(pdata->buttons[i].gpio); + + input_free_polled_device(poll_dev); + +err_free_bdev: + kfree(bdev); + + platform_set_drvdata(pdev, NULL); + return error; +} + +static int __devexit gpio_keys_polled_remove(struct platform_device *pdev) +{ + struct gpio_keys_polled_dev *bdev = platform_get_drvdata(pdev); + struct gpio_keys_platform_data *pdata = bdev->pdata; + int i; + + input_unregister_polled_device(bdev->poll_dev); + + for (i = 0; i < pdata->nbuttons; i++) + gpio_free(pdata->buttons[i].gpio); + + input_free_polled_device(bdev->poll_dev); + + kfree(bdev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver gpio_keys_polled_driver = { + .probe = gpio_keys_polled_probe, + .remove = __devexit_p(gpio_keys_polled_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init gpio_keys_polled_init(void) +{ + return platform_driver_register(&gpio_keys_polled_driver); +} + +static void __exit gpio_keys_polled_exit(void) +{ + platform_driver_unregister(&gpio_keys_polled_driver); +} + +module_init(gpio_keys_polled_init); +module_exit(gpio_keys_polled_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Gabor Juhos "); +MODULE_DESCRIPTION("Polled GPIO Buttons driver"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index ce73a30..dd1a56f 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -16,6 +16,8 @@ struct gpio_keys_button { struct gpio_keys_platform_data { struct gpio_keys_button *buttons; int nbuttons; + unsigned int poll_interval; /* polling interval in msecs - + for polling driver only */ unsigned int rep:1; /* enable input subsystem auto repeat */ int (*enable)(struct device *dev); void (*disable)(struct device *dev); -- 1.7.0.4 From bengardiner at nanometrics.ca Thu Dec 9 15:51:02 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Thu, 9 Dec 2010 16:51:02 -0500 Subject: [PATCH v6 0/5] da850-evm: add gpio-{keys, leds} for UI and BB expanders In-Reply-To: References: Message-ID: The da850-evm baseboard (BB) and its UI board both have tca6416 IO expanders. They are bootstrapped to different I2C addresses so they can be used concurrently. The expander on the UI board is currently used to enable/disable the peripherals that are available on the UI board. In addition to this functionality the expander is also connected to 8 pushbuttons. The expander on the baseboard is not currently used; it is connected to deep sleep enable, sw reset, a push button, some switches and LEDs. This proposed patch series enables the push buttons and switches on the UI and BB expanders using the gpio-keys polling mode patch by Gabor Juhos. Some work was performed to test irq-based gpio-keys support on the expanders (a WIP patch can be posted on request) but I believe that it is not possible to use irq-based gpio-keys on IO expanders for arm systems at this time. The attempt started when I noticed the patch of Alek Du and Alan Cox [1] which was recently committed [2]; a stab at integrating irq-based gpio-keys support based on that patch was attempted. I found that I either got a warning that the irq could not be mapped for the given gpio ; or, when N_IRQ was increased, a system freeze. >From what I have read (particularly the message by Grant Likely [3]) IRQs on IO expanders are not ready in ARM yet. I _think_ that the sparse IRQ rework by Thomas Gleixner [4] will resolve the blocker to irq-based gpio-keys support. In the meantime we have buttons and switches that we would like to excersise in our prototyping development. The patch to convert this series to irq-based gpio-keys will be straighforward once the support in arch/arm is there. There is an existing tca6416-keypad driver with polling support which I did not employ because it isn't possible to keep the gpio's used for peripheral enable/disable on the UI board or the LEDs on the baseboard registered while simultaneously registering the pushbuttons or switches as a tca6416-keypad instance. I tested this patch series using evtest on the resulting /dev/input/eventN devices and also on the event node of a non-polling gpio-keys instance to ensure that irq-based input handling is not broken by the introduction of the polling-mode gpio-keys patch. The non-polling instance creation and registration is not included in this series since it uses one of the boot-mode DIP switches and woult not (I think) be suitable for mainline. Disclaimer: I'm not an expert in irq's or gpio-keys; this is, in fact, my first proposed feature. Please feel free to correct me -- I welcome the chance to learn from your expertise. Ben Gardiner (4): da850-evm: add UI Expander pushbuttons da850-evm: extract defines for SEL{A,B,C} pins in UI expander da850-evm: add baseboard GPIO expander buttons, switches and LEDs da850-evm: KEYBOARD_GPIO_POLLED Kconfig conditional Gabor Juhos (1): Input: add input driver for polled GPIO buttons arch/arm/mach-davinci/Kconfig | 3 + arch/arm/mach-davinci/board-da850-evm.c | 306 +++++++++++++++++++++++++++-- drivers/input/keyboard/Kconfig | 16 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/gpio_keys_polled.c | 261 ++++++++++++++++++++++++ include/linux/gpio_keys.h | 2 + 6 files changed, 577 insertions(+), 12 deletions(-) create mode 100644 drivers/input/keyboard/gpio_keys_polled.c --- Changes since v5: * included the final polled gpio keys driver which is now in 2.6.37-rc5 at 0e7d0c860a0dee49dacb7bbb248d1eba637075ad * small changes to includes and structure names due to driver changes Changes since v4: * integrated Gabor Juhos' polling gpio button driver in place of the gpio-keys patch of Paul Mundt and Alex Clouter * dont' line break error messages (Sekhar Nori) * whitespace cleanup (Sekhar Nori) Changes since v3: * introduced patch 5 in the series by extracting the Kconfig changes proposed in patch 2 of v3. * not gpio_request()'ing the sw_rst and deep_sleep_en lines as requested (Sekhar Nori) Changes since v2: * register a single input device for switches and keys on the baseboard since there is no benefit to separate devices with different polling intervals (Dmitry Torokhov) * use static array intialization and range intialization for platform data structure to minimize the amount of runtime intialization needed: (Sekhar Nori) * Use the da850_evm variable name prefix for static symbols in board-da850-evm.c Changes since v1: * use locally defined functions that are no-ops/error checkers when INPUT_POLLDEV is not defined. * disable polling mode support when input-polldev is a module and gpio_keys is builtin * set INPUT_POLLDEV default for DA850_EVM machine, but don't select it unconditionally * adding note to description about why tca6416-keypad was not used From bengardiner at nanometrics.ca Thu Dec 9 15:51:06 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Thu, 9 Dec 2010 16:51:06 -0500 Subject: [PATCH v6 4/5] da850-evm: add baseboard GPIO expander buttons, switches and LEDs In-Reply-To: References: Message-ID: This patch adds a pca953x platform device for the tca6416 found on the evm baseboard. The tca6416 is a GPIO expander, also found on the UI board at a separate I2C address. The pins of the baseboard IO expander are connected to software reset, deep sleep enable, test points, a push button, DIP switches and LEDs. Add support for the push button, DIP switches and LEDs and test points (as free GPIOs). The reset and deep sleep enable connections are reserved by the setup routine so that userspace can't toggle those lines. The existing tca6416-keypad driver was not employed because there was no apararent way to register the LEDs connected to gpio's on the tca6416 while simultaneously registering the tca6416-keypad instance. Signed-off-by: Ben Gardiner Reviewed-by: Chris Cordahi CC: Govindarajan, Sriramakrishnan Reviewed-by: Sekhar Nori Reviewed-by: Dmitry Torokhov CC: Gabor Juhos --- Changes since v5: * use platform_data type from final polled gpio keys driver Changes since v4: * integrated the use of Gabor Juhos' polled gpio buttons driver * removed extra indent (Sekhar Nori) * don't line-break error messages (Sekhar Nori) * left-in the explicit static initialization of structure members Changes since v3: * don't request sw_rst and deep_sleep_en gpio pins -- let clients use them freely Changes since v2: * rebased to 083eae3e28643e0eefc5243719f8b1572cf98299 of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git * remove the "TODO : populate at runtime using" in 1/4 instead of this patch (Nori, Sekhar) * ui_expander_names was renamed to da850_evm_ui_exp * DA850_SW_POLL_MS definition moved to this patch from 3/4 * use indexed array initialization pattern introduced by Sekhar Nori in 3/4 * shorter names prefixed with da850_evm * static array range intializers * using only a single gpio-keys instance for the pushbutton and switches on baseboard since there is no advantage to separate device instances with different polling intervals (Dmitry Torokhov) Changes since v1: * adding note about why the tca6416-keypad driver was not used. * adding Govindarajan, Sriramakrishnan, the author of the tca6416-keypad driver --- arch/arm/mach-davinci/board-da850-evm.c | 187 +++++++++++++++++++++++++++++++ 1 files changed, 187 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index dede268..0de789b 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -430,6 +430,182 @@ static int da850_evm_ui_expander_teardown(struct i2c_client *client, return 0; } +/* assign the baseboard expander's GPIOs after the UI board's */ +#define DA850_UI_EXPANDER_N_GPIOS ARRAY_SIZE(da850_evm_ui_exp) +#define DA850_BB_EXPANDER_GPIO_BASE (DAVINCI_N_GPIO + DA850_UI_EXPANDER_N_GPIOS) + +enum da850_evm_bb_exp_pins { + DA850_EVM_BB_EXP_DEEP_SLEEP_EN = 0, + DA850_EVM_BB_EXP_SW_RST, + DA850_EVM_BB_EXP_TP_23, + DA850_EVM_BB_EXP_TP_22, + DA850_EVM_BB_EXP_TP_21, + DA850_EVM_BB_EXP_USER_PB1, + DA850_EVM_BB_EXP_USER_LED2, + DA850_EVM_BB_EXP_USER_LED1, + DA850_EVM_BB_EXP_USER_SW1, + DA850_EVM_BB_EXP_USER_SW2, + DA850_EVM_BB_EXP_USER_SW3, + DA850_EVM_BB_EXP_USER_SW4, + DA850_EVM_BB_EXP_USER_SW5, + DA850_EVM_BB_EXP_USER_SW6, + DA850_EVM_BB_EXP_USER_SW7, + DA850_EVM_BB_EXP_USER_SW8 +}; + +static const char const *da850_evm_bb_exp[] = { + [DA850_EVM_BB_EXP_DEEP_SLEEP_EN] = "deep_sleep_en", + [DA850_EVM_BB_EXP_SW_RST] = "sw_rst", + [DA850_EVM_BB_EXP_TP_23] = "tp_23", + [DA850_EVM_BB_EXP_TP_22] = "tp_22", + [DA850_EVM_BB_EXP_TP_21] = "tp_21", + [DA850_EVM_BB_EXP_USER_PB1] = "user_pb1", + [DA850_EVM_BB_EXP_USER_LED2] = "user_led2", + [DA850_EVM_BB_EXP_USER_LED1] = "user_led1", + [DA850_EVM_BB_EXP_USER_SW1] = "user_sw1", + [DA850_EVM_BB_EXP_USER_SW2] = "user_sw2", + [DA850_EVM_BB_EXP_USER_SW3] = "user_sw3", + [DA850_EVM_BB_EXP_USER_SW4] = "user_sw4", + [DA850_EVM_BB_EXP_USER_SW5] = "user_sw5", + [DA850_EVM_BB_EXP_USER_SW6] = "user_sw6", + [DA850_EVM_BB_EXP_USER_SW7] = "user_sw7", + [DA850_EVM_BB_EXP_USER_SW8] = "user_sw8", +}; + +#define DA850_N_BB_USER_SW 8 + +static struct gpio_keys_button da850_evm_bb_keys[] = { + [0] = { + .type = EV_KEY, + .active_low = 1, + .wakeup = 0, + .debounce_interval = DA850_KEYS_DEBOUNCE_MS, + .code = KEY_PROG1, + .desc = NULL, /* assigned at runtime */ + .gpio = -1, /* assigned at runtime */ + }, + [1 ... DA850_N_BB_USER_SW] = { + .type = EV_SW, + .active_low = 1, + .wakeup = 0, + .debounce_interval = DA850_KEYS_DEBOUNCE_MS, + .code = -1, /* assigned at runtime */ + .desc = NULL, /* assigned at runtime */ + .gpio = -1, /* assigned at runtime */ + }, +}; + +static struct gpio_keys_platform_data da850_evm_bb_keys_pdata = { + .buttons = da850_evm_bb_keys, + .nbuttons = ARRAY_SIZE(da850_evm_bb_keys), + .poll_interval = DA850_GPIO_KEYS_POLL_MS, +}; + +static struct platform_device da850_evm_bb_keys_device = { + .name = "gpio-keys-polled", + .id = 1, + .dev = { + .platform_data = &da850_evm_bb_keys_pdata + }, +}; + +static void da850_evm_bb_keys_init(unsigned gpio) +{ + int i; + struct gpio_keys_button *button; + + button = &da850_evm_bb_keys[0]; + button->desc = (char *) + da850_evm_bb_exp[DA850_EVM_BB_EXP_USER_PB1]; + button->gpio = gpio + DA850_EVM_BB_EXP_USER_PB1; + + for (i = 0; i < DA850_N_BB_USER_SW; i++) { + button = &da850_evm_bb_keys[i + 1]; + button->code = SW_LID + i; + button->desc = (char *) + da850_evm_bb_exp[DA850_EVM_BB_EXP_USER_SW1 + i]; + button->gpio = gpio + DA850_EVM_BB_EXP_USER_SW1 + i; + } +} + +#define DA850_N_BB_USER_LED 2 + +static struct gpio_led da850_evm_bb_leds[] = { + [0 ... DA850_N_BB_USER_LED - 1] = { + .active_low = 1, + .gpio = -1, /* assigned at runtime */ + .name = NULL, /* assigned at runtime */ + }, +}; + +static struct gpio_led_platform_data da850_evm_bb_leds_pdata = { + .leds = da850_evm_bb_leds, + .num_leds = ARRAY_SIZE(da850_evm_bb_leds), +}; + +static struct platform_device da850_evm_bb_leds_device = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &da850_evm_bb_leds_pdata + } +}; + +static void da850_evm_bb_leds_init(unsigned gpio) +{ + int i; + struct gpio_led *led; + + for (i = 0; i < DA850_N_BB_USER_LED; i++) { + led = &da850_evm_bb_leds[i]; + + led->gpio = gpio + DA850_EVM_BB_EXP_USER_LED2 + i; + led->name = + da850_evm_bb_exp[DA850_EVM_BB_EXP_USER_LED2 + i]; + } +} + +static int da850_evm_bb_expander_setup(struct i2c_client *client, + unsigned gpio, unsigned ngpio, + void *c) +{ + int ret; + + /* + * Register the switches and pushbutton on the baseboard as a gpio-keys + * device. + */ + da850_evm_bb_keys_init(gpio); + ret = platform_device_register(&da850_evm_bb_keys_device); + if (ret) { + pr_warning("Could not register baseboard GPIO expander keys"); + goto io_exp_setup_sw_fail; + } + + da850_evm_bb_leds_init(gpio); + ret = platform_device_register(&da850_evm_bb_leds_device); + if (ret) { + pr_warning("Could not register baseboard GPIO expander LEDS"); + goto io_exp_setup_leds_fail; + } + + return 0; + +io_exp_setup_leds_fail: + platform_device_unregister(&da850_evm_bb_keys_device); +io_exp_setup_sw_fail: + return ret; +} + +static int da850_evm_bb_expander_teardown(struct i2c_client *client, + unsigned gpio, unsigned ngpio, void *c) +{ + platform_device_unregister(&da850_evm_bb_leds_device); + platform_device_unregister(&da850_evm_bb_keys_device); + + return 0; +} + static struct pca953x_platform_data da850_evm_ui_expander_info = { .gpio_base = DAVINCI_N_GPIO, .setup = da850_evm_ui_expander_setup, @@ -437,6 +613,13 @@ static struct pca953x_platform_data da850_evm_ui_expander_info = { .names = da850_evm_ui_exp, }; +static struct pca953x_platform_data da850_evm_bb_expander_info = { + .gpio_base = DA850_BB_EXPANDER_GPIO_BASE, + .setup = da850_evm_bb_expander_setup, + .teardown = da850_evm_bb_expander_teardown, + .names = da850_evm_bb_exp, +}; + static struct i2c_board_info __initdata da850_evm_i2c_devices[] = { { I2C_BOARD_INFO("tlv320aic3x", 0x18), @@ -445,6 +628,10 @@ static struct i2c_board_info __initdata da850_evm_i2c_devices[] = { I2C_BOARD_INFO("tca6416", 0x20), .platform_data = &da850_evm_ui_expander_info, }, + { + I2C_BOARD_INFO("tca6416", 0x21), + .platform_data = &da850_evm_bb_expander_info, + }, }; static struct davinci_i2c_platform_data da850_evm_i2c_0_pdata = { -- 1.7.0.4 From bengardiner at nanometrics.ca Thu Dec 9 15:51:05 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Thu, 9 Dec 2010 16:51:05 -0500 Subject: [PATCH v6 3/5] da850-evm: extract defines for SEL{A, B, C} pins in UI expander In-Reply-To: References: Message-ID: <9d143f9de4c8870d398c474e38cef4df05db1bea.1291931041.git.bengardiner@nanometrics.ca> The setup and teardown methods of the UI expander reference the SEL_{A,B,C} pins by 'magic number' in each function. This uses the common enum for their offsets in the expander setup and teardown functions. Signed-off-by: Ben Gardiner Reviewed-by: Chris Cordahi Reviewed-by: Sekhar Nori Signed-off-by: Sekhar Nori CC: Victor Rodriguez --- Changes since v5: * no changes in this patch of the series Changes since v4: * no changes in this patch of the series Changes since v3: * no changes in this patch of the series Changes since v2: * rebased to 083eae3e28643e0eefc5243719f8b1572cf98299 of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git * integrated the static array initialization patch provided by Sekhar Nori Changes since v1: * No changes since v1 --- arch/arm/mach-davinci/board-da850-evm.c | 24 ++++++++++++------------ 1 files changed, 12 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 4377679..dede268 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -360,23 +360,23 @@ static int da850_evm_ui_expander_setup(struct i2c_client *client, unsigned gpio, { int sel_a, sel_b, sel_c, ret; - sel_a = gpio + 7; - sel_b = gpio + 6; - sel_c = gpio + 5; + sel_a = gpio + DA850_EVM_UI_EXP_SEL_A; + sel_b = gpio + DA850_EVM_UI_EXP_SEL_B; + sel_c = gpio + DA850_EVM_UI_EXP_SEL_C; - ret = gpio_request(sel_a, "sel_a"); + ret = gpio_request(sel_a, da850_evm_ui_exp[DA850_EVM_UI_EXP_SEL_A]); if (ret) { pr_warning("Cannot open UI expander pin %d\n", sel_a); goto exp_setup_sela_fail; } - ret = gpio_request(sel_b, "sel_b"); + ret = gpio_request(sel_b, da850_evm_ui_exp[DA850_EVM_UI_EXP_SEL_B]); if (ret) { pr_warning("Cannot open UI expander pin %d\n", sel_b); goto exp_setup_selb_fail; } - ret = gpio_request(sel_c, "sel_c"); + ret = gpio_request(sel_c, da850_evm_ui_exp[DA850_EVM_UI_EXP_SEL_C]); if (ret) { pr_warning("Cannot open UI expander pin %d\n", sel_c); goto exp_setup_selc_fail; @@ -419,13 +419,13 @@ static int da850_evm_ui_expander_teardown(struct i2c_client *client, platform_device_unregister(&da850_evm_ui_keys_device); /* deselect all functionalities */ - gpio_set_value_cansleep(gpio + 5, 1); - gpio_set_value_cansleep(gpio + 6, 1); - gpio_set_value_cansleep(gpio + 7, 1); + gpio_set_value_cansleep(gpio + DA850_EVM_UI_EXP_SEL_C, 1); + gpio_set_value_cansleep(gpio + DA850_EVM_UI_EXP_SEL_B, 1); + gpio_set_value_cansleep(gpio + DA850_EVM_UI_EXP_SEL_A, 1); - gpio_free(gpio + 5); - gpio_free(gpio + 6); - gpio_free(gpio + 7); + gpio_free(gpio + DA850_EVM_UI_EXP_SEL_C); + gpio_free(gpio + DA850_EVM_UI_EXP_SEL_B); + gpio_free(gpio + DA850_EVM_UI_EXP_SEL_A); return 0; } -- 1.7.0.4 From bengardiner at nanometrics.ca Thu Dec 9 15:51:04 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Thu, 9 Dec 2010 16:51:04 -0500 Subject: [PATCH v6 2/5] da850-evm: add UI Expander pushbuttons In-Reply-To: References: Message-ID: <97475d95f5c1edad6c68f51b63c349760c2ff0f7.1291931041.git.bengardiner@nanometrics.ca> This patch adds EV_KEYs for each of the 8 pushbuttons on the UI board via a gpio-key device. The expander is a tca6416; it controls the SEL_{A,B,C} lines which enable and disable the peripherals found on the UI board in addition to the 8 pushbuttons mentioned above. The reason the existing tca6416-keypad driver is not employed is because there was no aparent way to keep the gpio lines used as SEL_{A,B,C} registered while simultaneously registering the pushbuttons as a tca6416-keypad instance. Some experimentation with the polling interval was performed; we were searching for the largest polling interval that did not affect the feel of the responsiveness of the buttons. It is very subjective but 200ms seems to be a good value that accepts firm pushes but rejects very light ones. The key values assigned to the buttons were arbitrarily chosen to be F1-F8. Signed-off-by: Ben Gardiner Reviewed-by: Chris Cordahi CC: Govindarajan, Sriramakrishnan Reviewed-by: Sekhar Nori Signed-off-by: Sekhar Nori CC: Kevin Hilman CC: Gabor Juhos --- Changes since v5: * use header and platform_data type from final polled gpio keys driver Changes since v4: * integrated the use of Gabor Juhos' polled gpio buttons driver * removed spurious whitespace change (Sekhar Nori) * don't linebreak error messages (Sekhar Nori) * kept the explicit static initialization of structure members in-place Changes since v3: * extracted Kconfig changes to patch 5/5 * fixed leading whitespace problem Changes since v2: * rebased to 083eae3e28643e0eefc5243719f8b1572cf98299 of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git * remove the "TODO : populate at runtime using" in this patch instead of 4/4 (Nori, Sekhar) * integrated the static array initialization patch of Sekhar Nori * use static array initialization ranges * rename DA850_PB_POLL_MS to DA850_GPIO_KEYS_POLL_MS * use shorter names prefixed with da850_evm Changes since v1: * set INPUT_POLLDEV default for DA850_EVM machine, but don't select it unconditionally * adding note to description about why tca6416-keypad was not used * adding Govindarajan, Sriramakrishnan, the author of the tca6416-keypad driver --- arch/arm/mach-davinci/board-da850-evm.c | 95 +++++++++++++++++++++++++++++++ 1 files changed, 95 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index f89b0b7..4377679 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -17,8 +17,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -272,6 +274,87 @@ static inline void da850_evm_setup_emac_rmii(int rmii_sel) static inline void da850_evm_setup_emac_rmii(int rmii_sel) { } #endif + +#define DA850_KEYS_DEBOUNCE_MS 10 +/* + * At 200ms polling interval it is possible to miss an + * event by tapping very lightly on the push button but most + * pushes do result in an event; longer intervals require the + * user to hold the button whereas shorter intervals require + * more CPU time for polling. + */ +#define DA850_GPIO_KEYS_POLL_MS 200 + +enum da850_evm_ui_exp_pins { + DA850_EVM_UI_EXP_SEL_C = 5, + DA850_EVM_UI_EXP_SEL_B, + DA850_EVM_UI_EXP_SEL_A, + DA850_EVM_UI_EXP_PB8, + DA850_EVM_UI_EXP_PB7, + DA850_EVM_UI_EXP_PB6, + DA850_EVM_UI_EXP_PB5, + DA850_EVM_UI_EXP_PB4, + DA850_EVM_UI_EXP_PB3, + DA850_EVM_UI_EXP_PB2, + DA850_EVM_UI_EXP_PB1, +}; + +static const char const *da850_evm_ui_exp[] = { + [DA850_EVM_UI_EXP_SEL_C] = "sel_c", + [DA850_EVM_UI_EXP_SEL_B] = "sel_b", + [DA850_EVM_UI_EXP_SEL_A] = "sel_a", + [DA850_EVM_UI_EXP_PB8] = "pb8", + [DA850_EVM_UI_EXP_PB7] = "pb7", + [DA850_EVM_UI_EXP_PB6] = "pb6", + [DA850_EVM_UI_EXP_PB5] = "pb5", + [DA850_EVM_UI_EXP_PB4] = "pb4", + [DA850_EVM_UI_EXP_PB3] = "pb3", + [DA850_EVM_UI_EXP_PB2] = "pb2", + [DA850_EVM_UI_EXP_PB1] = "pb1", +}; + +#define DA850_N_UI_PB 8 + +static struct gpio_keys_button da850_evm_ui_keys[] = { + [0 ... DA850_N_UI_PB - 1] = { + .type = EV_KEY, + .active_low = 1, + .wakeup = 0, + .debounce_interval = DA850_KEYS_DEBOUNCE_MS, + .code = -1, /* assigned at runtime */ + .gpio = -1, /* assigned at runtime */ + .desc = NULL, /* assigned at runtime */ + }, +}; + +static struct gpio_keys_platform_data da850_evm_ui_keys_pdata = { + .buttons = da850_evm_ui_keys, + .nbuttons = ARRAY_SIZE(da850_evm_ui_keys), + .poll_interval = DA850_GPIO_KEYS_POLL_MS, +}; + +static struct platform_device da850_evm_ui_keys_device = { + .name = "gpio-keys-polled", + .id = 0, + .dev = { + .platform_data = &da850_evm_ui_keys_pdata + }, +}; + +static void da850_evm_ui_keys_init(unsigned gpio) +{ + int i; + struct gpio_keys_button *button; + + for (i = 0; i < DA850_N_UI_PB; i++) { + button = &da850_evm_ui_keys[i]; + button->code = KEY_F8 - i; + button->desc = (char *) + da850_evm_ui_exp[DA850_EVM_UI_EXP_PB8 + i]; + button->gpio = gpio + DA850_EVM_UI_EXP_PB8 + i; + } +} + static int da850_evm_ui_expander_setup(struct i2c_client *client, unsigned gpio, unsigned ngpio, void *c) { @@ -304,6 +387,13 @@ static int da850_evm_ui_expander_setup(struct i2c_client *client, unsigned gpio, gpio_direction_output(sel_b, 1); gpio_direction_output(sel_c, 1); + da850_evm_ui_keys_init(gpio); + ret = platform_device_register(&da850_evm_ui_keys_device); + if (ret) { + pr_warning("Could not register UI GPIO expander push-buttons"); + goto exp_setup_keys_fail; + } + ui_card_detected = 1; pr_info("DA850/OMAP-L138 EVM UI card detected\n"); @@ -313,6 +403,8 @@ static int da850_evm_ui_expander_setup(struct i2c_client *client, unsigned gpio, return 0; +exp_setup_keys_fail: + gpio_free(sel_c); exp_setup_selc_fail: gpio_free(sel_b); exp_setup_selb_fail: @@ -324,6 +416,8 @@ exp_setup_sela_fail: static int da850_evm_ui_expander_teardown(struct i2c_client *client, unsigned gpio, unsigned ngpio, void *c) { + platform_device_unregister(&da850_evm_ui_keys_device); + /* deselect all functionalities */ gpio_set_value_cansleep(gpio + 5, 1); gpio_set_value_cansleep(gpio + 6, 1); @@ -340,6 +434,7 @@ static struct pca953x_platform_data da850_evm_ui_expander_info = { .gpio_base = DAVINCI_N_GPIO, .setup = da850_evm_ui_expander_setup, .teardown = da850_evm_ui_expander_teardown, + .names = da850_evm_ui_exp, }; static struct i2c_board_info __initdata da850_evm_i2c_devices[] = { -- 1.7.0.4 From bengardiner at nanometrics.ca Thu Dec 9 15:51:07 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Thu, 9 Dec 2010 16:51:07 -0500 Subject: [PATCH v6 5/5] da850-evm: KEYBOARD_GPIO_POLLED Kconfig conditional In-Reply-To: References: Message-ID: <939e12134ad83efb7a8706d16afec7c12663079e.1291931041.git.bengardiner@nanometrics.ca> Use the mach-davinci/Kconfig to enable gpio-keys-polled as default when da850-evm machine is enabled. Signed-off-by: Ben Gardiner CC: Kevin Hilman CC: "Nori, Sekhar" CC: Gabor Juhos --- Changes since v5: * no changes in this patch of the series Changes since v4: * integrated the use of Gabor Juhos' polled gpio buttons driver Changes since v3: * no changes in this patch of the series / this patch was introduced in v4 of the series --- arch/arm/mach-davinci/Kconfig | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index 84066e8..b93c327 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -180,6 +180,9 @@ endchoice config GPIO_PCA953X default MACH_DAVINCI_DA850_EVM +config KEYBOARD_GPIO_POLLED + default MACH_DAVINCI_DA850_EVM + config MACH_TNETV107X bool "TI TNETV107X Reference Platform" default ARCH_DAVINCI_TNETV107X -- 1.7.0.4 From manjunath.hadli at ti.com Fri Dec 10 08:15:01 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Fri, 10 Dec 2010 19:45:01 +0530 Subject: [PATCH v3 6/6] davinci vpbe: Build infrastructure for VPBE driver In-Reply-To: <4D00C790.8030507@mvista.com> Message-ID: Sergei, Thank you for your comments. Will send a corrected pacth. -Manju On Thu, Dec 09, 2010 at 17:42:00, Sergei Shtylyov wrote: > Hello. > > On 02-12-2010 15:39, Manjunath Hadli wrote: > > > This patch adds the build infra-structure for Davinci VPBE dislay > > driver > > > Signed-off-by: Manjunath Hadli > > Signed-off-by: Muralidharan Karicheri > [...] > > > diff --git a/drivers/media/video/davinci/Kconfig > > b/drivers/media/video/davinci/Kconfig > > index 6b19540..dab32d5 100644 > > --- a/drivers/media/video/davinci/Kconfig > > +++ b/drivers/media/video/davinci/Kconfig > > @@ -91,3 +91,25 @@ 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" > > + select VIDEO_VPSS_SYSTEM > > + select VIDEOBUF_DMA_CONTIG > > Please use tabs uniformly to indent. > > > diff --git a/drivers/media/video/davinci/davinci_vpbe_readme.txt > > b/drivers/media/video/davinci/davinci_vpbe_readme.txt > > new file mode 100644 > > index 0000000..e7aabba > > --- /dev/null > > +++ b/drivers/media/video/davinci/davinci_vpbe_readme.txt > > @@ -0,0 +1,100 @@ > > + > > + 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 following (in the same order as the list under file > ^ "the" missing. > > > + partitioning):- > > + > > + 1. V4L2 display driver > > + Implements 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 sub device > > + 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 connetected 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 > > + allowes > > Only "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 > > Space missing after period. > > > + patch series, the interconnection and enabling ans setting of the external > > + encoders is not present, and would be a part of the next patch series. > > + > > + 3. Venc sub device > > + Responsible for setting outputs provides > > Provided? > > > through internal dacs and also > > DACs. > > > + 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" > > Space not needed after (. > > > + output name to board-xxx-evm.c). A table of timings for various LCDs > > + supported cab > > Can? > > > be maintained in the board specific setup file to support > > + various LCD displays. > > + > > + 4. osd sub device > > OSD? What doesn it mean BTW? > > > + Osd sub device implements all osd layer management and hardware specific > > + features. In the legacy drivers (LSPxxx), the hardware specific features > > + are configured through proprietary IOCTLs at the fb device interface. Since > > + sub devices > > I think it's either "sub-devices" or "subdevices"... > > > are going to support device nodes, application will be able > > + to configure the hardware feayture > > Only "feature". > > > directly by opening the osd sub device > > + node and by calling the related IOCTL. So these proprietary IOCTLs are > > + to be removed from the FB Device driver when doing up port of these drivers to > > + mainline kernel. The V4L2 and FB device nodes supports only IOCTLS as per > > + the associated spec. The rest of the IOCTLs are to be moved to osd and > > + venc sub devices. > > + > > + Current status:- > > + > > + A build tested version of vpbe controller is available. > > + > > + Following are TBDs. > > + > > + vpbe display controller > > + - review and modify the handling of 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. > > + > > + v4l2 driver > > + - A version is already developed which is to be cleaned up and > > + unit tested > > + > > + FB drivers > > + - Add support for fbdev drivers.- > > Forgot newline here? > > > Ready and part of subsequent patches. > > \ No newline at end of file > > Here as well. :-) > > WBR, Sergei > From khilman at deeprootsystems.com Fri Dec 10 09:50:18 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 10 Dec 2010 07:50:18 -0800 Subject: [PATCH v6 1/5] Input: add input driver for polled GPIO buttons In-Reply-To: (Ben Gardiner's message of "Thu, 9 Dec 2010 16:51:03 -0500") References: Message-ID: <87hbeleif9.fsf@deeprootsystems.com> Ben Gardiner writes: > From: Gabor Juhos > > The existing gpio-keys driver can be usable only for GPIO lines with > interrupt support. Several devices have buttons connected to a GPIO > line which is not capable to generate interrupts. This patch adds a > new input driver using the generic GPIO layer and the input-polldev > to support such buttons. > > [Ben Gardiner of the original gpio_keys infrastructure; cleanups and other > improvements.] > > Signed-off-by: Gabor Juhos > Signed-off-by: Ben Gardiner > Tested-by: Ben Gardiner > Signed-off-by: Dmitry Torokhov > (cherry picked from commit 0e7d0c860a0dee49dacb7bbb248d1eba637075ad) > > --- > > This a copy of the commit -- I included in the patch series since > linux-davinci/master does not currently have it; but 2.6.37-rc5 does. I'll be updating davinci master to .37-rc5 today, so will drop this patch. Kevin From nsekhar at ti.com Fri Dec 10 10:01:59 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 10 Dec 2010 21:31:59 +0530 Subject: [PATCH v7 11/12] davinci: add tnetv107x evm backlight device In-Reply-To: <4D00EEA1.8070201@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> <1291733522-3626-12-git-send-email-cyril@ti.com> <4D00EEA1.8070201@ti.com> Message-ID: On Thu, Dec 09, 2010 at 20:28:41, Chemparathy, Cyril wrote: > On 12/09/2010 06:00 AM, Nori, Sekhar wrote: > > On Thu, Dec 09, 2010 at 14:25:49, Nori, Sekhar wrote: > > > >> This call should simply return if machine is not tnetv107x EVM. > >> > >> I didn't follow the entire series but wondering why > >> platform device registration should be a late init call. > >> Typically the driver probe can be made a late init call > >> in case of init sequence dependencies. > > > > I meant driver init, not probe. > > This was done to handle driver dependencies, and is meant to be > temporary (until a real driver dependency framework comes in). > > Please see [1] for a bit more of background on changing drivers to use > module_init(). Hmm, okay. Please include a check for machine in that case. Thanks, Sekhar > > Regards > - Cyril. > > [1] > http://linux.davincidsp.com/pipermail/davinci-linux-open-source/2010-November/020987.html > From khilman at deeprootsystems.com Fri Dec 10 10:16:43 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 10 Dec 2010 08:16:43 -0800 Subject: [PATCH v6 0/5] da850-evm: add gpio-{keys, leds} for UI and BB expanders In-Reply-To: (Ben Gardiner's message of "Thu, 9 Dec 2010 16:51:02 -0500") References: Message-ID: <87tyild2ms.fsf@deeprootsystems.com> Hi Ben, Ben Gardiner writes: > The da850-evm baseboard (BB) and its UI board both have tca6416 IO expanders. > They are bootstrapped to different I2C addresses so they can be used > concurrently. > > The expander on the UI board is currently used to enable/disable the > peripherals that are available on the UI board. In addition to this > functionality the expander is also connected to 8 pushbuttons. The expander > on the baseboard is not currently used; it is connected to deep sleep enable, > sw reset, a push button, some switches and LEDs. > > This proposed patch series enables the push buttons and switches on the UI and > BB expanders using the gpio-keys polling mode patch by Gabor Juhos. Some > work was performed to test irq-based gpio-keys support on the expanders (a WIP > patch can be posted on request) but I believe that it is not possible to use > irq-based gpio-keys on IO expanders for arm systems at this time. Thanks for your patience and persistence on this series, and thanks for working closely with the input folks to get the issues worked through. This series looks good to me, so I'll be queuing it in davinci-next for 2.6.38. It should show up in davinci git shortly. Please validate things are working as expected there. Were there any changes needed to the defaults in da8xx_omapl_defconfig to enable these features by default? or does the Kconfig change in PATCH 5/5 cover it? Also, I really appreciate the thorough patch descriptions and history information. This greatly eases the work of maintainers. Thanks! One minor question: the series has a couple of Signed-off-by tags from Sekhar Nori. The s-o-b tag is for folks on the delivery path, and based on what I saw, these should probably be Acked-by tags from Sekhar, since he certainly helped on the review/test/validate side, but AFAICT, was not an author or on the delivery path. If I'm wrong on this (e.g., if Sekhar actually did author some of those patches) let me know, otherwise I'll change the s-o-b to Acked-by for Sekhar. Thanks, Kevin From bengardiner at nanometrics.ca Fri Dec 10 10:33:41 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 10 Dec 2010 11:33:41 -0500 Subject: [PATCH v6 0/5] da850-evm: add gpio-{keys, leds} for UI and BB expanders In-Reply-To: <87tyild2ms.fsf@deeprootsystems.com> References: <87tyild2ms.fsf@deeprootsystems.com> Message-ID: Hi Kevin, On Fri, Dec 10, 2010 at 11:16 AM, Kevin Hilman wrote: > Hi Ben, > > Ben Gardiner writes: > >> The da850-evm baseboard (BB) and its UI board both have tca6416 IO expanders. >> They are bootstrapped to different I2C addresses so they can be used >> concurrently. >> >> The expander on the UI board is currently used to enable/disable the >> peripherals that are available on the UI board. In addition to this >> functionality the expander is also connected to 8 pushbuttons. The expander >> on the baseboard is not currently used; it is connected to deep sleep enable, >> sw reset, a push button, some switches and LEDs. >> >> This proposed patch series enables the push buttons and switches on the UI and >> BB expanders using the gpio-keys polling mode patch by Gabor Juhos. Some >> work was performed to test irq-based gpio-keys support on the expanders (a WIP >> patch can be posted on request) but I believe that it is not possible to use >> irq-based gpio-keys on IO expanders for arm systems at this time. > > Thanks for your patience and persistence on this series, and thanks for > working closely with the input folks to get the issues worked through. It is my pleasure. > This series looks good to me, so I'll be queuing it in davinci-next for > 2.6.38. ?It should show up in davinci git shortly. ?Please validate > things are working as expected there. ?Were there any changes needed to > the defaults in da8xx_omapl_defconfig to enable these features by > default? ?or does the Kconfig change in PATCH 5/5 cover it? Thank you very much, Kevin. I will check linux-davinci/master on monday. Yes, the 5/5 covers the necessary Kconfig changes; it makes the polled gpio keys default-on for da850evm. > Also, I really appreciate the thorough patch descriptions and history > information. ? This greatly eases the work of maintainers. ?Thanks! Again, you are most welcome. > One minor question: the series has a couple of Signed-off-by tags from > Sekhar Nori. ?The s-o-b tag is for folks on the delivery path, and based > on what I saw, these should probably be Acked-by tags from Sekhar, since > he certainly helped on the review/test/validate side, but AFAICT, was > not an author or on the delivery path. ?If I'm wrong on this (e.g., if > Sekhar actually did author some of those patches) let me know, otherwise > I'll change the s-o-b to Acked-by for Sekhar. The s-o-b 's for Sehkar are because I folded-in suggested changes submitted in review by Sehkar in the form of a patch. I 'think' this qualifies as authorship. I'll leave it to your good judgement. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From khilman at deeprootsystems.com Fri Dec 10 10:42:53 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 10 Dec 2010 08:42:53 -0800 Subject: [PATCH] regulator: add driver for tps6524x regulator In-Reply-To: <20101207175550.GJ9689@rakim.wolfsonmicro.main> (Mark Brown's message of "Tue, 7 Dec 2010 17:55:50 +0000") References: <1291741451-17549-1-git-send-email-cyril@ti.com> <20101207171533.GH9689@rakim.wolfsonmicro.main> <4CFE6CCE.5050301@ti.com> <20101207175550.GJ9689@rakim.wolfsonmicro.main> Message-ID: <87oc8td1f6.fsf@deeprootsystems.com> Mark Brown writes: > On Tue, Dec 07, 2010 at 12:20:14PM -0500, Cyril Chemparathy wrote: >> On 12/07/2010 12:15 PM, Mark Brown wrote: > >> > Acked-by: Mark Brown > >> Is this to be merged via the davinci tree? > > Liam would need to merge it, or it'd need to wait until the regulator > tree change to the signature of set_voltage() gets merged into the > DaVinci tree. I suggest Liam merge this, as there shouldn't be any dependencies in the davinci tree. Kevin From khilman at deeprootsystems.com Fri Dec 10 10:56:28 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 10 Dec 2010 08:56:28 -0800 Subject: [PATCH v7 11/12] davinci: add tnetv107x evm backlight device In-Reply-To: (Sekhar Nori's message of "Fri, 10 Dec 2010 21:31:59 +0530") References: <1291733522-3626-1-git-send-email-cyril@ti.com> <1291733522-3626-12-git-send-email-cyril@ti.com> <4D00EEA1.8070201@ti.com> Message-ID: <87aakdd0sj.fsf@deeprootsystems.com> "Nori, Sekhar" writes: > On Thu, Dec 09, 2010 at 20:28:41, Chemparathy, Cyril wrote: >> On 12/09/2010 06:00 AM, Nori, Sekhar wrote: >> > On Thu, Dec 09, 2010 at 14:25:49, Nori, Sekhar wrote: >> > >> >> This call should simply return if machine is not tnetv107x EVM. >> >> >> >> I didn't follow the entire series but wondering why >> >> platform device registration should be a late init call. >> >> Typically the driver probe can be made a late init call >> >> in case of init sequence dependencies. >> > >> > I meant driver init, not probe. >> >> This was done to handle driver dependencies, and is meant to be >> temporary (until a real driver dependency framework comes in). >> >> Please see [1] for a bit more of background on changing drivers to use >> module_init(). > > Hmm, okay. Please include a check for machine in that case. > Sekhar is right. Today, we don't (can't) build this machine with others for a couple reasons, but we're moving towards removing those restrictions, so when this machine is included in a binary with others, it needs to check. Thanks, Kevin From khilman at deeprootsystems.com Fri Dec 10 11:16:14 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 10 Dec 2010 09:16:14 -0800 Subject: [PATCH v4 0/6] davinci vpbe: dm6446 v4l2 driver In-Reply-To: (Hans Verkuil's message of "Thu, 9 Dec 2010 12:47:42 +0100") References: <1291891543-27156-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <87wrnhblb5.fsf@deeprootsystems.com> "Hans Verkuil" writes: >> version4 : addressed Hans's comments >> on: >> 1. replaced mutex_lock_interruptible() with mutex_lock() >> 2. replaced ntsc and pal macros with new equivalent macros >> 3. simplifying the code in the if-else condition >> 4. minor code corrections > > For the whole patch series: > > Acked-by: Hans Verkuil > Hans, can you take patches 1-4 and 6 through the linux-media tree? I will queue the patch 5 (the only mach-davinci change) in davinci git for 2.6.38. Manjunath, can rebase patch 5 on top of current davinci-next (or davinci master) as this patch doesn't currently apply there. Thanks, Kevin > >> >> 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: platform specific additions >> davinci vpbe: Build infrastructure for VPBE driver >> >> arch/arm/mach-davinci/board-dm644x-evm.c | 79 +- >> arch/arm/mach-davinci/dm644x.c | 164 ++- >> arch/arm/mach-davinci/include/mach/dm644x.h | 4 + >> drivers/media/video/davinci/Kconfig | 22 + >> drivers/media/video/davinci/Makefile | 2 + >> .../media/video/davinci/davinci_vpbe_readme.txt | 100 + >> drivers/media/video/davinci/vpbe.c | 837 ++++++++ >> drivers/media/video/davinci/vpbe_display.c | 2099 >> ++++++++++++++++++++ >> drivers/media/video/davinci/vpbe_osd.c | 1211 +++++++++++ >> drivers/media/video/davinci/vpbe_osd_regs.h | 389 ++++ >> drivers/media/video/davinci/vpbe_venc.c | 574 ++++++ >> drivers/media/video/davinci/vpbe_venc_regs.h | 189 ++ >> include/media/davinci/vpbe.h | 186 ++ >> include/media/davinci/vpbe_display.h | 146 ++ >> include/media/davinci/vpbe_osd.h | 397 ++++ >> include/media/davinci/vpbe_types.h | 93 + >> include/media/davinci/vpbe_venc.h | 38 + >> 17 files changed, 6511 insertions(+), 19 deletions(-) >> create mode 100644 drivers/media/video/davinci/davinci_vpbe_readme.txt >> 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 lrg at slimlogic.co.uk Fri Dec 10 12:12:09 2010 From: lrg at slimlogic.co.uk (Liam Girdwood) Date: Fri, 10 Dec 2010 18:12:09 +0000 Subject: [PATCH] regulator: add driver for tps6524x regulator In-Reply-To: <87oc8td1f6.fsf@deeprootsystems.com> References: <1291741451-17549-1-git-send-email-cyril@ti.com> <20101207171533.GH9689@rakim.wolfsonmicro.main> <4CFE6CCE.5050301@ti.com> <20101207175550.GJ9689@rakim.wolfsonmicro.main> <87oc8td1f6.fsf@deeprootsystems.com> Message-ID: <1292004729.3422.9.camel@odin> On Fri, 2010-12-10 at 08:42 -0800, Kevin Hilman wrote: > Mark Brown writes: > > > On Tue, Dec 07, 2010 at 12:20:14PM -0500, Cyril Chemparathy wrote: > >> On 12/07/2010 12:15 PM, Mark Brown wrote: > > > >> > Acked-by: Mark Brown > > > >> Is this to be merged via the davinci tree? > > > > Liam would need to merge it, or it'd need to wait until the regulator > > tree change to the signature of set_voltage() gets merged into the > > DaVinci tree. > > I suggest Liam merge this, as there shouldn't be any dependencies in the > davinci tree. Will do. I'll get around to this at the weekend. Thanks Liam -- Freelance Developer, SlimLogic Ltd ASoC and Voltage Regulator Maintainer. http://www.slimlogic.co.uk From lamiaposta71 at gmail.com Fri Dec 10 13:32:00 2010 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Fri, 10 Dec 2010 20:32:00 +0100 Subject: Thx for mmc in u-boot Message-ID: I was thinking to get crazy for having a v4.4 mmc working with dm365, instead with latest u-boot in ti tree mmc works out of the box. Thank you! -------------- next part -------------- An HTML attachment was scrubbed... URL: From vm.rod25 at gmail.com Fri Dec 10 15:02:35 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Fri, 10 Dec 2010 15:02:35 -0600 Subject: [PATCH v10 4/6] davinci: MMC/SD support for Omapl138-Hawkboard In-Reply-To: References: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> <1291760089-5818-5-git-send-email-vm.rod25@gmail.com> Message-ID: On Wed, Dec 8, 2010 at 9:55 AM, Victor Rodriguez wrote: > On Tue, Dec 7, 2010 at 11:20 PM, Nori, Sekhar wrote: >> On Wed, Dec 08, 2010 at 03:44:47, vm.rod25 at gmail.com wrote: >> >>> +static __init void omapl138_hawk_mmc_init(void) >>> +{ >>> + ? ? int ret; >>> + >>> + ? ? ret = davinci_cfg_reg_list(hawk_mmcsd0_pins); >>> + ? ? if (ret) { >>> + ? ? ? ? ? ? pr_warning("%s: MMC/SD0 mux setup failed: %d\n", >>> + ? ? ? ? ? ? ? ? ? ? __func__, ret); >>> + ? ? ? ? ? ? return; >>> + ? ? } >>> + >>> + ? ? ret = gpio_request_one(DA850_HAWK_MMCSD_CD_PIN, >>> + ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "MMC CD"); >>> + ? ? if (ret < 0) { >>> + ? ? ? ? ? ? pr_warning("%s: can not open GPIO %d\n", >>> + ? ? ? ? ? ? ? ? ? ? __func__, DA850_HAWK_MMCSD_CD_PIN); >>> + ? ? ? ? ? ? goto mmc_setup_cd_fail; >>> + ? ? } >> >> A goto is only used when there is some recovery >> to be done. In this case, you should simply return >> here. The USB patch needs the same correction. >> >> Thanks, >> Sekhar > > Sorry for this my mistake > > It wil change to this > > +static __init void omapl138_hawk_mmc_init(void) > +{ > + ? ? ? int ret; > + > + ? ? ? ret = davinci_cfg_reg_list(hawk_mmcsd0_pins); > + ? ? ? if (ret) { > + ? ? ? ? ? ? ? pr_warning("%s: MMC/SD0 mux setup failed: %d\n", > + ? ? ? ? ? ? ? ? ? ? ? __func__, ret); > + ? ? ? ? ? ? ? return; > + ? ? ? } > + > + ? ? ? ret = gpio_request_one(DA850_HAWK_MMCSD_CD_PIN, > + ? ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "MMC CD"); > + ? ? ? if (ret < 0) { > + ? ? ? ? ? ? ? pr_warning("%s: can not open GPIO %d\n", > + ? ? ? ? ? ? ? ? ? ? ? __func__, DA850_HAWK_MMCSD_CD_PIN); > + ? ? ? ? ? ? ? return; > + ? ? ? } > + > + ? ? ? ret = gpio_request_one(DA850_HAWK_MMCSD_WP_PIN, > + ? ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "MMC WP"); > + ? ? ? if (ret < 0) { > + ? ? ? ? ? ? ? pr_warning("%s: can not open GPIO %d\n", > + ? ? ? ? ? ? ? ? ? ? ? __func__, DA850_HAWK_MMCSD_WP_PIN); > + ? ? ? ? ? ? ? goto mmc_setup_wp_fail; > + ? ? ? } > + > + ? ? ? ret = da8xx_register_mmcsd0(&da850_mmc_config); > + ? ? ? if (ret) { > + ? ? ? ? ? ? ? pr_warning("%s: MMC/SD0 registration failed: %d\n", > + ? ? ? ? ? ? ? ? ? ? ? __func__, ret); > + ? ? ? ? ? ? ? goto mmc_setup_mmcsd_fail; > + ? ? ? } > + > + ? ? ? return; > + > +mmc_setup_mmcsd_fail: > + ? ? ? gpio_free(DA850_HAWK_MMCSD_WP_PIN); > +mmc_setup_wp_fail: > + ? ? ? gpio_free(DA850_HAWK_MMCSD_CD_PIN); > +} > + > > And USB will be this > > +static __init void omapl138_hawk_usb_init(void) > +{ > + ? ? ? int ret; > + ? ? ? u32 cfgchip2; > + > + ? ? ? ret = davinci_cfg_reg_list(da850_hawk_usb11_pins); > + ? ? ? if (ret) { > + ? ? ? ? ? ? ? pr_warning("%s: USB 1.1 PinMux setup failed: %d\n", > + ? ? ? ? ? ? ? ? ? ? ? __func__, ret); > + ? ? ? ? ? ? ? return; > + ? ? ? } > + > + ? ? ? /* Setup the Ref. clock frequency for the HAWK at 24 MHz. */ > + > + ? ? ? cfgchip2 = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); > + ? ? ? cfgchip2 &= ~CFGCHIP2_REFFREQ; > + ? ? ? cfgchip2 |= ?CFGCHIP2_REFFREQ_24MHZ; > + ? ? ? __raw_writel(cfgchip2, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); > + > + ? ? ? ret = gpio_request_one(DA850_USB1_VBUS_PIN, > + ? ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_OUT, "USB1 VBUS"); > + ? ? ? if (ret < 0) { > + ? ? ? ? ? ? ? pr_err("%s: failed to request GPIO for USB 1.1 port " > + ? ? ? ? ? ? ? ? ? ? ? "power control: %d\n", __func__, ret); > + ? ? ? ? ? ? ? return; > + ? ? ? } > + > + ? ? ? ret = gpio_request_one(DA850_USB1_OC_PIN, > + ? ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "USB1 OC"); > + ? ? ? if (ret < 0) { > + ? ? ? ? ? ? ? pr_err("%s: failed to request GPIO for USB 1.1 port " > + ? ? ? ? ? ? ? ? ? ? ? "over-current indicator: %d\n", __func__, ret); > + ? ? ? ? ? ? ? goto usb11_setup_oc_fail; > + ? ? ? } > + > + ? ? ? ret = da8xx_register_usb11(&omapl138_hawk_usb11_pdata); > + ? ? ? if (ret) { > + ? ? ? ? ? ? ? pr_warning("%s: USB 1.1 registration failed: %d\n", > + ? ? ? ? ? ? ? ? ? ? ? __func__, ret); > + ? ? ? ? ? ? ? goto usb11_setup_fail; > + ? ? ? } > + > + ? ? ? return; > + > +usb11_setup_fail: > + ? ? ? gpio_free(DA850_USB1_OC_PIN); > +usb11_setup_oc_fail: > + ? ? ? gpio_free(DA850_USB1_VBUS_PIN); > +} > + > > Thanks a lot for the comments > > Regards > > Victor Rodriguez Any update of this is Ok to resend the series with this small change? Regards Victor Rodriguez > >>> + >>> + ? ? ret = gpio_request_one(DA850_HAWK_MMCSD_WP_PIN, >>> + ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "MMC WP"); >>> + ? ? if (ret < 0) { >>> + ? ? ? ? ? ? pr_warning("%s: can not open GPIO %d\n", >>> + ? ? ? ? ? ? ? ? ? ? __func__, DA850_HAWK_MMCSD_WP_PIN); >>> + ? ? ? ? ? ? goto mmc_setup_wp_fail; >>> + ? ? } >>> + >>> + ? ? ret = da8xx_register_mmcsd0(&da850_mmc_config); >>> + ? ? if (ret) { >>> + ? ? ? ? ? ? pr_warning("%s: MMC/SD0 registration failed: %d\n", >>> + ? ? ? ? ? ? ? ? ? ? __func__, ret); >>> + ? ? ? ? ? ? goto mmc_setup_mmcsd_fail; >>> + ? ? } >>> + >>> +mmc_setup_cd_fail: >>> + ? ? return; >>> + >>> +mmc_setup_mmcsd_fail: >>> + ? ? gpio_free(DA850_HAWK_MMCSD_WP_PIN); >>> +mmc_setup_wp_fail: >>> + ? ? gpio_free(DA850_HAWK_MMCSD_CD_PIN); >>> +} >>> + >>> ?static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { >>> ? ? ? .enabled_uarts = 0x7, >>> ?}; >>> @@ -127,6 +198,8 @@ static __init void omapl138_hawk_init(void) >>> ? ? ? ? ? ? ? pr_warning("%s: EDMA registration failed: %d\n", >>> ? ? ? ? ? ? ? ? ? ? ? __func__, ret); >>> >>> + ? ? omapl138_hawk_mmc_init(); >>> + >>> ? ? ? ret = da8xx_register_watchdog(); >>> ? ? ? if (ret) >>> ? ? ? ? ? ? ? pr_warning("omapl138_hawk_init: " >>> -- >>> 1.7.0.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 lrg at slimlogic.co.uk Fri Dec 10 16:15:15 2010 From: lrg at slimlogic.co.uk (Liam Girdwood) Date: Fri, 10 Dec 2010 22:15:15 +0000 Subject: [PATCH] regulator: add driver for tps6524x regulator In-Reply-To: <87oc8td1f6.fsf@deeprootsystems.com> References: <1291741451-17549-1-git-send-email-cyril@ti.com> <20101207171533.GH9689@rakim.wolfsonmicro.main> <4CFE6CCE.5050301@ti.com> <20101207175550.GJ9689@rakim.wolfsonmicro.main> <87oc8td1f6.fsf@deeprootsystems.com> Message-ID: <1292019315.3422.15.camel@odin> On Fri, 2010-12-10 at 08:42 -0800, Kevin Hilman wrote: > Mark Brown writes: > > > On Tue, Dec 07, 2010 at 12:20:14PM -0500, Cyril Chemparathy wrote: > >> On 12/07/2010 12:15 PM, Mark Brown wrote: > > > >> > Acked-by: Mark Brown > > > >> Is this to be merged via the davinci tree? > > > > Liam would need to merge it, or it'd need to wait until the regulator > > tree change to the signature of set_voltage() gets merged into the > > DaVinci tree. > > I suggest Liam merge this, as there shouldn't be any dependencies in the > davinci tree. And now applied. Thanks Liam -- Freelance Developer, SlimLogic Ltd ASoC and Voltage Regulator Maintainer. http://www.slimlogic.co.uk From khilman at deeprootsystems.com Fri Dec 10 17:58:40 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 10 Dec 2010 15:58:40 -0800 Subject: [PATCH v4 1/2] davinci: am18x/da850/omap-l138: add support for higher speed grades In-Reply-To: <1291884094-19433-1-git-send-email-nsekhar@ti.com> (Sekhar Nori's message of "Thu, 9 Dec 2010 14:11:33 +0530") References: <1291884094-19433-1-git-send-email-nsekhar@ti.com> Message-ID: <87sjy589jj.fsf@deeprootsystems.com> Sekhar Nori writes: > AM18x/DA850/OMAP-L138 SoCs have variants that can operate > at a maximum of 456 MHz at 1.3V operating point. Also the > 1.2V operating point has a variant that can support a maximum > of 375 MHz. > > This patch adds three new OPPs (456 MHz, 408 MHz and 372 MHz) > to the list of DA850 OPPs. > > Not all silicon is qualified to run at higher speeds and > unfortunately the maximum speed the chip can support can only > be determined from the label on the package (not software > readable). > > Because of this, we depend on the maximum speed grade information > to be provided to us in some board specific way. The board informs > the maximum speed grade information to the SoC by calling the > da850_set_max_speed() function. > > Signed-off-by: Sekhar Nori > --- > Since v3: > Fixed pre-div and post-div for 372MHz OPP based on PLLOUT range > limitations documented in OMAP-L138 datasheet. AM1808 datasheet > will be updated to match this. This series looks good, but can you (re)post one more time and Cc the linux-arm-kernel list please? Thanks, Kevin > arch/arm/mach-davinci/da850.c | 75 ++++++++++++++++++++++------ > arch/arm/mach-davinci/include/mach/da8xx.h | 7 +++ > 2 files changed, 67 insertions(+), 15 deletions(-) > > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 63916b9..78b5ae2 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -830,8 +830,7 @@ static void da850_set_async3_src(int pllnum) > * According to the TRM, minimum PLLM results in maximum power savings. > * The OPP definitions below should keep the PLLM as low as possible. > * > - * The output of the PLLM must be between 400 to 600 MHz. > - * This rules out prediv of anything but divide-by-one for 24Mhz OSC input. > + * The output of the PLLM must be between 300 to 600 MHz. > */ > struct da850_opp { > unsigned int freq; /* in KHz */ > @@ -842,6 +841,33 @@ struct da850_opp { > unsigned int cvdd_max; /* in uV */ > }; > > +static const struct da850_opp da850_opp_456 = { > + .freq = 456000, > + .prediv = 1, > + .mult = 19, > + .postdiv = 1, > + .cvdd_min = 1300000, > + .cvdd_max = 1350000, > +}; > + > +static const struct da850_opp da850_opp_408 = { > + .freq = 408000, > + .prediv = 1, > + .mult = 17, > + .postdiv = 1, > + .cvdd_min = 1300000, > + .cvdd_max = 1350000, > +}; > + > +static const struct da850_opp da850_opp_372 = { > + .freq = 372000, > + .prediv = 2, > + .mult = 31, > + .postdiv = 1, > + .cvdd_min = 1200000, > + .cvdd_max = 1320000, > +}; > + > static const struct da850_opp da850_opp_300 = { > .freq = 300000, > .prediv = 1, > @@ -876,6 +902,9 @@ static const struct da850_opp da850_opp_96 = { > } > > static struct cpufreq_frequency_table da850_freq_table[] = { > + OPP(456), > + OPP(408), > + OPP(372), > OPP(300), > OPP(200), > OPP(96), > @@ -886,6 +915,19 @@ static struct cpufreq_frequency_table da850_freq_table[] = { > }; > > #ifdef CONFIG_REGULATOR > +static int da850_set_voltage(unsigned int index); > +static int da850_regulator_init(void); > +#endif > + > +static struct davinci_cpufreq_config cpufreq_info = { > + .freq_table = da850_freq_table, > +#ifdef CONFIG_REGULATOR > + .init = da850_regulator_init, > + .set_voltage = da850_set_voltage, > +#endif > +}; > + > +#ifdef CONFIG_REGULATOR > static struct regulator *cvdd; > > static int da850_set_voltage(unsigned int index) > @@ -895,7 +937,7 @@ static int da850_set_voltage(unsigned int index) > if (!cvdd) > return -ENODEV; > > - opp = (struct da850_opp *) da850_freq_table[index].index; > + opp = (struct da850_opp *) cpufreq_info.freq_table[index].index; > > return regulator_set_voltage(cvdd, opp->cvdd_min, opp->cvdd_max); > } > @@ -912,14 +954,6 @@ static int da850_regulator_init(void) > } > #endif > > -static struct davinci_cpufreq_config cpufreq_info = { > - .freq_table = &da850_freq_table[0], > -#ifdef CONFIG_REGULATOR > - .init = da850_regulator_init, > - .set_voltage = da850_set_voltage, > -#endif > -}; > - > static struct platform_device da850_cpufreq_device = { > .name = "cpufreq-davinci", > .dev = { > @@ -928,12 +962,22 @@ static struct platform_device da850_cpufreq_device = { > .id = -1, > }; > > +unsigned int da850_max_speed = 300000; > + > int __init da850_register_cpufreq(char *async_clk) > { > + int i; > + > /* cpufreq driver can help keep an "async" clock constant */ > if (async_clk) > clk_add_alias("async", da850_cpufreq_device.name, > async_clk, NULL); > + for (i = 0; i < ARRAY_SIZE(da850_freq_table); i++) { > + if (da850_freq_table[i].frequency <= da850_max_speed) { > + cpufreq_info.freq_table = &da850_freq_table[i]; > + break; > + } > + } > > return platform_device_register(&da850_cpufreq_device); > } > @@ -942,17 +986,18 @@ static int da850_round_armrate(struct clk *clk, unsigned long rate) > { > int i, ret = 0, diff; > unsigned int best = (unsigned int) -1; > + struct cpufreq_frequency_table *table = cpufreq_info.freq_table; > > rate /= 1000; /* convert to kHz */ > > - for (i = 0; da850_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { > - diff = da850_freq_table[i].frequency - rate; > + for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { > + diff = table[i].frequency - rate; > if (diff < 0) > diff = -diff; > > if (diff < best) { > best = diff; > - ret = da850_freq_table[i].frequency; > + ret = table[i].frequency; > } > } > > @@ -973,7 +1018,7 @@ static int da850_set_pll0rate(struct clk *clk, unsigned long index) > struct pll_data *pll = clk->pll_data; > int ret; > > - opp = (struct da850_opp *) da850_freq_table[index].index; > + opp = (struct da850_opp *) cpufreq_info.freq_table[index].index; > prediv = opp->prediv; > mult = opp->mult; > postdiv = opp->postdiv; > diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h > index 4247b3f..e7f9520 100644 > --- a/arch/arm/mach-davinci/include/mach/da8xx.h > +++ b/arch/arm/mach-davinci/include/mach/da8xx.h > @@ -28,6 +28,13 @@ extern void __iomem *da8xx_syscfg0_base; > extern void __iomem *da8xx_syscfg1_base; > > /* > + * If the DA850/OMAP-L138/AM18x SoC on board is of a higher speed grade > + * (than the regular 300Mhz variant), the board code should set this up > + * with the supported speed before calling da850_register_cpufreq(). > + */ > +extern unsigned int da850_max_speed; > + > +/* > * The cp_intc interrupt controller for the da8xx isn't in the same > * chunk of physical memory space as the other registers (like it is > * on the davincis) so it needs to be mapped separately. It will be From khilman at deeprootsystems.com Fri Dec 10 18:04:32 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 10 Dec 2010 16:04:32 -0800 Subject: [PATCH v6 0/5] da850-evm: add gpio-{keys, leds} for UI and BB expanders In-Reply-To: (Ben Gardiner's message of "Fri, 10 Dec 2010 11:33:41 -0500") References: <87tyild2ms.fsf@deeprootsystems.com> Message-ID: <87lj3x899r.fsf@deeprootsystems.com> Hi Ben, Ben Gardiner writes: [...] >> One minor question: the series has a couple of Signed-off-by tags from >> Sekhar Nori. ?The s-o-b tag is for folks on the delivery path, and based >> on what I saw, these should probably be Acked-by tags from Sekhar, since >> he certainly helped on the review/test/validate side, but AFAICT, was >> not an author or on the delivery path. ?If I'm wrong on this (e.g., if >> Sekhar actually did author some of those patches) let me know, otherwise >> I'll change the s-o-b to Acked-by for Sekhar. > > The s-o-b 's for Sehkar are because I folded-in suggested changes > submitted in review by Sehkar in the form of a patch. I 'think' this > qualifies as authorship. I'll leave it to your good judgement. OK, thanks for clarification. I'll leave the s-o-b tags as is. Kevin From khilman at deeprootsystems.com Fri Dec 10 18:13:58 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 10 Dec 2010 16:13:58 -0800 Subject: [PATCH v7 01/12] misc: add driver for sequencer serial port In-Reply-To: <1291733522-3626-2-git-send-email-cyril@ti.com> (Cyril Chemparathy's message of "Tue, 7 Dec 2010 09:51:51 -0500") References: <1291733522-3626-1-git-send-email-cyril@ti.com> <1291733522-3626-2-git-send-email-cyril@ti.com> Message-ID: <878vzx88u1.fsf@deeprootsystems.com> Cyril Chemparathy writes: > TI's sequencer serial port (TI-SSP) is a jack-of-all-trades type of serial port > device. It has a built-in programmable execution engine that can be programmed > to operate as almost any serial bus (I2C, SPI, EasyScale, and others). > > This patch adds a driver for this controller device. The driver does not > expose a user-land interface. Protocol drivers built on top of this layer are > expected to remain in-kernel. > > Signed-off-by: Cyril Chemparathy Minor nit: subject still says 'misc', but this has now been moved to mfd. Kevin > --- > drivers/mfd/Kconfig | 11 + > drivers/mfd/Makefile | 1 + > drivers/mfd/ti-ssp.c | 476 ++++++++++++++++++++++++++++++++++++++++++++ > include/linux/mfd/ti_ssp.h | 87 ++++++++ > 4 files changed, 575 insertions(+), 0 deletions(-) > create mode 100644 drivers/mfd/ti-ssp.c > create mode 100644 include/linux/mfd/ti_ssp.h > > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig > index 3a1493b..a4b4b70 100644 > --- a/drivers/mfd/Kconfig > +++ b/drivers/mfd/Kconfig > @@ -81,6 +81,17 @@ config MFD_DM355EVM_MSP > boards. MSP430 firmware manages resets and power sequencing, > inputs from buttons and the IR remote, LEDs, an RTC, and more. > > +config MFD_TI_SSP > + tristate "TI Sequencer Serial Port support" > + depends on ARCH_DAVINCI_TNETV107X > + select MFD_CORE > + ---help--- > + Say Y here if you want support for the Sequencer Serial Port > + in a Texas Instruments TNETV107X SoC. > + > + To compile this driver as a module, choose M here: the > + module will be called ti-ssp. > + > config HTC_EGPIO > bool "HTC EGPIO support" > depends on GENERIC_HARDIRQS && GPIOLIB && ARM > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile > index f54b365..f64cf13 100644 > --- a/drivers/mfd/Makefile > +++ b/drivers/mfd/Makefile > @@ -14,6 +14,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o > > obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o > obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o > +obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o > > obj-$(CONFIG_MFD_STMPE) += stmpe.o > obj-$(CONFIG_MFD_TC35892) += tc35892.o > diff --git a/drivers/mfd/ti-ssp.c b/drivers/mfd/ti-ssp.c > new file mode 100644 > index 0000000..af9ab0e > --- /dev/null > +++ b/drivers/mfd/ti-ssp.c > @@ -0,0 +1,476 @@ > +/* > + * Sequencer Serial Port (SSP) driver for Texas Instruments' SoCs > + * > + * 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; 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 > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* Register Offsets */ > +#define REG_REV 0x00 > +#define REG_IOSEL_1 0x04 > +#define REG_IOSEL_2 0x08 > +#define REG_PREDIV 0x0c > +#define REG_INTR_ST 0x10 > +#define REG_INTR_EN 0x14 > +#define REG_TEST_CTRL 0x18 > + > +/* Per port registers */ > +#define PORT_CFG_2 0x00 > +#define PORT_ADDR 0x04 > +#define PORT_DATA 0x08 > +#define PORT_CFG_1 0x0c > +#define PORT_STATE 0x10 > + > +#define SSP_PORT_CONFIG_MASK (SSP_EARLY_DIN | SSP_DELAY_DOUT) > +#define SSP_PORT_CLKRATE_MASK 0x0f > + > +#define SSP_SEQRAM_WR_EN BIT(4) > +#define SSP_SEQRAM_RD_EN BIT(5) > +#define SSP_START BIT(15) > +#define SSP_BUSY BIT(10) > +#define SSP_PORT_ASL BIT(7) > +#define SSP_PORT_CFO1 BIT(6) > + > +#define SSP_PORT_SEQRAM_SIZE 32 > + > +static const int ssp_port_base[] = {0x040, 0x080}; > +static const int ssp_port_seqram[] = {0x100, 0x180}; > + > +struct ti_ssp { > + struct resource *res; > + struct device *dev; > + void __iomem *regs; > + spinlock_t lock; > + struct clk *clk; > + int irq; > + wait_queue_head_t wqh; > + > + /* > + * Some of the iosel2 register bits always read-back as 0, we need to > + * remember these values so that we don't clobber previously set > + * values. > + */ > + u32 iosel2; > +}; > + > +static inline struct ti_ssp *dev_to_ssp(struct device *dev) > +{ > + return dev_get_drvdata(dev->parent); > +} > + > +static inline int dev_to_port(struct device *dev) > +{ > + return to_platform_device(dev)->id; > +} > + > +/* Register Access Helpers, rmw() functions need to run locked */ > +static inline u32 ssp_read(struct ti_ssp *ssp, int reg) > +{ > + return __raw_readl(ssp->regs + reg); > +} > + > +static inline void ssp_write(struct ti_ssp *ssp, int reg, u32 val) > +{ > + __raw_writel(val, ssp->regs + reg); > +} > + > +static inline void ssp_rmw(struct ti_ssp *ssp, int reg, u32 mask, u32 bits) > +{ > + ssp_write(ssp, reg, (ssp_read(ssp, reg) & ~mask) | bits); > +} > + > +static inline u32 ssp_port_read(struct ti_ssp *ssp, int port, int reg) > +{ > + return ssp_read(ssp, ssp_port_base[port] + reg); > +} > + > +static inline void ssp_port_write(struct ti_ssp *ssp, int port, int reg, > + u32 val) > +{ > + ssp_write(ssp, ssp_port_base[port] + reg, val); > +} > + > +static inline void ssp_port_rmw(struct ti_ssp *ssp, int port, int reg, > + u32 mask, u32 bits) > +{ > + ssp_rmw(ssp, ssp_port_base[port] + reg, mask, bits); > +} > + > +static inline void ssp_port_clr_bits(struct ti_ssp *ssp, int port, int reg, > + u32 bits) > +{ > + ssp_port_rmw(ssp, port, reg, bits, 0); > +} > + > +static inline void ssp_port_set_bits(struct ti_ssp *ssp, int port, int reg, > + u32 bits) > +{ > + ssp_port_rmw(ssp, port, reg, 0, bits); > +} > + > +/* Called to setup port clock mode, caller must hold ssp->lock */ > +static int __set_mode(struct ti_ssp *ssp, int port, int mode) > +{ > + mode &= SSP_PORT_CONFIG_MASK; > + ssp_port_rmw(ssp, port, PORT_CFG_1, SSP_PORT_CONFIG_MASK, mode); > + > + return 0; > +} > + > +int ti_ssp_set_mode(struct device *dev, int mode) > +{ > + struct ti_ssp *ssp = dev_to_ssp(dev); > + int port = dev_to_port(dev); > + int ret; > + > + spin_lock(&ssp->lock); > + ret = __set_mode(ssp, port, mode); > + spin_unlock(&ssp->lock); > + > + return ret; > +} > +EXPORT_SYMBOL(ti_ssp_set_mode); > + > +/* Called to setup iosel2, caller must hold ssp->lock */ > +static void __set_iosel2(struct ti_ssp *ssp, u32 mask, u32 val) > +{ > + ssp->iosel2 = (ssp->iosel2 & ~mask) | val; > + ssp_write(ssp, REG_IOSEL_2, ssp->iosel2); > +} > + > +/* Called to setup port iosel, caller must hold ssp->lock */ > +static void __set_iosel(struct ti_ssp *ssp, int port, u32 iosel) > +{ > + unsigned val, shift = port ? 16 : 0; > + > + /* IOSEL1 gets the least significant 16 bits */ > + val = ssp_read(ssp, REG_IOSEL_1); > + val &= 0xffff << (port ? 0 : 16); > + val |= (iosel & 0xffff) << (port ? 16 : 0); > + ssp_write(ssp, REG_IOSEL_1, val); > + > + /* IOSEL2 gets the most significant 16 bits */ > + val = (iosel >> 16) & 0x7; > + __set_iosel2(ssp, 0x7 << shift, val << shift); > +} > + > +int ti_ssp_set_iosel(struct device *dev, u32 iosel) > +{ > + struct ti_ssp *ssp = dev_to_ssp(dev); > + int port = dev_to_port(dev); > + > + spin_lock(&ssp->lock); > + __set_iosel(ssp, port, iosel); > + spin_unlock(&ssp->lock); > + > + return 0; > +} > +EXPORT_SYMBOL(ti_ssp_set_iosel); > + > +int ti_ssp_load(struct device *dev, int offs, u32* prog, int len) > +{ > + struct ti_ssp *ssp = dev_to_ssp(dev); > + int port = dev_to_port(dev); > + int i; > + > + if (len > SSP_PORT_SEQRAM_SIZE) > + return -ENOSPC; > + > + spin_lock(&ssp->lock); > + > + /* Enable SeqRAM access */ > + ssp_port_set_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN); > + > + /* Copy code */ > + for (i = 0; i < len; i++) { > + __raw_writel(prog[i], ssp->regs + offs + 4*i + > + ssp_port_seqram[port]); > + } > + > + /* Disable SeqRAM access */ > + ssp_port_clr_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN); > + > + spin_unlock(&ssp->lock); > + > + return 0; > +} > +EXPORT_SYMBOL(ti_ssp_load); > + > +int ti_ssp_raw_read(struct device *dev) > +{ > + struct ti_ssp *ssp = dev_to_ssp(dev); > + int port = dev_to_port(dev); > + int shift = port ? 27 : 11; > + > + return (ssp_read(ssp, REG_IOSEL_2) >> shift) & 0xf; > +} > +EXPORT_SYMBOL(ti_ssp_raw_read); > + > +int ti_ssp_raw_write(struct device *dev, u32 val) > +{ > + struct ti_ssp *ssp = dev_to_ssp(dev); > + int port = dev_to_port(dev), shift; > + > + spin_lock(&ssp->lock); > + > + shift = port ? 22 : 6; > + val &= 0xf; > + __set_iosel2(ssp, 0xf << shift, val << shift); > + > + spin_unlock(&ssp->lock); > + > + return 0; > +} > +EXPORT_SYMBOL(ti_ssp_raw_write); > + > +static inline int __xfer_done(struct ti_ssp *ssp, int port) > +{ > + return !(ssp_port_read(ssp, port, PORT_CFG_1) & SSP_BUSY); > +} > + > +int ti_ssp_run(struct device *dev, u32 pc, u32 input, u32 *output) > +{ > + struct ti_ssp *ssp = dev_to_ssp(dev); > + int port = dev_to_port(dev); > + int ret; > + > + if (pc & ~(0x3f)) > + return -EINVAL; > + > + /* Grab ssp->lock to serialize rmw on ssp registers */ > + spin_lock(&ssp->lock); > + > + ssp_port_write(ssp, port, PORT_ADDR, input >> 16); > + ssp_port_write(ssp, port, PORT_DATA, input & 0xffff); > + ssp_port_rmw(ssp, port, PORT_CFG_1, 0x3f, pc); > + > + /* grab wait queue head lock to avoid race with the isr */ > + spin_lock_irq(&ssp->wqh.lock); > + > + /* kick off sequence execution in hardware */ > + ssp_port_set_bits(ssp, port, PORT_CFG_1, SSP_START); > + > + /* drop ssp lock; no register writes beyond this */ > + spin_unlock(&ssp->lock); > + > + ret = wait_event_interruptible_locked_irq(ssp->wqh, > + __xfer_done(ssp, port)); > + spin_unlock_irq(&ssp->wqh.lock); > + > + if (ret < 0) > + return ret; > + > + if (output) { > + *output = (ssp_port_read(ssp, port, PORT_ADDR) << 16) | > + (ssp_port_read(ssp, port, PORT_DATA) & 0xffff); > + } > + > + ret = ssp_port_read(ssp, port, PORT_STATE) & 0x3f; /* stop address */ > + > + return ret; > +} > +EXPORT_SYMBOL(ti_ssp_run); > + > +static irqreturn_t ti_ssp_interrupt(int irq, void *dev_data) > +{ > + struct ti_ssp *ssp = dev_data; > + > + spin_lock(&ssp->wqh.lock); > + > + ssp_write(ssp, REG_INTR_ST, 0x3); > + wake_up_locked(&ssp->wqh); > + > + spin_unlock(&ssp->wqh.lock); > + > + return IRQ_HANDLED; > +} > + > +static int __devinit ti_ssp_probe(struct platform_device *pdev) > +{ > + static struct ti_ssp *ssp; > + const struct ti_ssp_data *pdata = pdev->dev.platform_data; > + int error = 0, prediv = 0xff, id; > + unsigned long sysclk; > + struct device *dev = &pdev->dev; > + struct mfd_cell cells[2]; > + > + ssp = kzalloc(sizeof(*ssp), GFP_KERNEL); > + if (!ssp) { > + dev_err(dev, "cannot allocate device info\n"); > + return -ENOMEM; > + } > + > + ssp->dev = dev; > + dev_set_drvdata(dev, ssp); > + > + ssp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!ssp->res) { > + error = -ENODEV; > + dev_err(dev, "cannot determine register area\n"); > + goto error_res; > + } > + > + if (!request_mem_region(ssp->res->start, resource_size(ssp->res), > + pdev->name)) { > + error = -ENOMEM; > + dev_err(dev, "cannot claim register memory\n"); > + goto error_res; > + } > + > + ssp->regs = ioremap(ssp->res->start, resource_size(ssp->res)); > + if (!ssp->regs) { > + error = -ENOMEM; > + dev_err(dev, "cannot map register memory\n"); > + goto error_map; > + } > + > + ssp->clk = clk_get(dev, NULL); > + if (IS_ERR(ssp->clk)) { > + error = PTR_ERR(ssp->clk); > + dev_err(dev, "cannot claim device clock\n"); > + goto error_clk; > + } > + > + ssp->irq = platform_get_irq(pdev, 0); > + if (ssp->irq < 0) { > + error = -ENODEV; > + dev_err(dev, "unknown irq\n"); > + goto error_irq; > + } > + > + error = request_threaded_irq(ssp->irq, NULL, ti_ssp_interrupt, 0, > + dev_name(dev), ssp); > + if (error < 0) { > + dev_err(dev, "cannot acquire irq\n"); > + goto error_irq; > + } > + > + spin_lock_init(&ssp->lock); > + init_waitqueue_head(&ssp->wqh); > + > + /* Power on and initialize SSP */ > + error = clk_enable(ssp->clk); > + if (error) { > + dev_err(dev, "cannot enable device clock\n"); > + goto error_enable; > + } > + > + /* Reset registers to a sensible known state */ > + ssp_write(ssp, REG_IOSEL_1, 0); > + ssp_write(ssp, REG_IOSEL_2, 0); > + ssp_write(ssp, REG_INTR_EN, 0x3); > + ssp_write(ssp, REG_INTR_ST, 0x3); > + ssp_write(ssp, REG_TEST_CTRL, 0); > + ssp_port_write(ssp, 0, PORT_CFG_1, SSP_PORT_ASL); > + ssp_port_write(ssp, 1, PORT_CFG_1, SSP_PORT_ASL); > + ssp_port_write(ssp, 0, PORT_CFG_2, SSP_PORT_CFO1); > + ssp_port_write(ssp, 1, PORT_CFG_2, SSP_PORT_CFO1); > + > + sysclk = clk_get_rate(ssp->clk); > + if (pdata && pdata->out_clock) > + prediv = (sysclk / pdata->out_clock) - 1; > + prediv = clamp(prediv, 0, 0xff); > + ssp_rmw(ssp, REG_PREDIV, 0xff, prediv); > + > + memset(cells, 0, sizeof(cells)); > + for (id = 0; id < 2; id++) { > + const struct ti_ssp_dev_data *data = &pdata->dev_data[id]; > + > + cells[id].id = id; > + cells[id].name = data->dev_name; > + cells[id].platform_data = data->pdata; > + cells[id].data_size = data->pdata_size; > + } > + > + error = mfd_add_devices(dev, 0, cells, 2, NULL, 0); > + if (error < 0) { > + dev_err(dev, "cannot add mfd cells\n"); > + goto error_enable; > + } > + > + return 0; > + > +error_enable: > + free_irq(ssp->irq, ssp); > +error_irq: > + clk_put(ssp->clk); > +error_clk: > + iounmap(ssp->regs); > +error_map: > + release_mem_region(ssp->res->start, resource_size(ssp->res)); > +error_res: > + kfree(ssp); > + return error; > +} > + > +static int __devexit ti_ssp_remove(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct ti_ssp *ssp = dev_get_drvdata(dev); > + > + mfd_remove_devices(dev); > + clk_disable(ssp->clk); > + free_irq(ssp->irq, ssp); > + clk_put(ssp->clk); > + iounmap(ssp->regs); > + release_mem_region(ssp->res->start, resource_size(ssp->res)); > + kfree(ssp); > + dev_set_drvdata(dev, NULL); > + return 0; > +} > + > +static struct platform_driver ti_ssp_driver = { > + .probe = ti_ssp_probe, > + .remove = __devexit_p(ti_ssp_remove), > + .driver = { > + .name = "ti-ssp", > + .owner = THIS_MODULE, > + } > +}; > + > +static int __init ti_ssp_init(void) > +{ > + return platform_driver_register(&ti_ssp_driver); > +} > +module_init(ti_ssp_init); > + > +static void __exit ti_ssp_exit(void) > +{ > + platform_driver_unregister(&ti_ssp_driver); > +} > +module_exit(ti_ssp_exit); > + > +MODULE_DESCRIPTION("Sequencer Serial Port (SSP) Driver"); > +MODULE_AUTHOR("Cyril Chemparathy"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:ti-ssp"); > diff --git a/include/linux/mfd/ti_ssp.h b/include/linux/mfd/ti_ssp.h > new file mode 100644 > index 0000000..021fe09 > --- /dev/null > +++ b/include/linux/mfd/ti_ssp.h > @@ -0,0 +1,87 @@ > +/* > + * Sequencer Serial Port (SSP) driver for Texas Instruments' SoCs > + * > + * 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; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + */ > + > +#ifndef __TI_SSP_H__ > +#define __TI_SSP_H__ > + > +struct ti_ssp_dev_data { > + const char *dev_name; > + void *pdata; > + size_t pdata_size; > +}; > + > +struct ti_ssp_data { > + unsigned long out_clock; > + struct ti_ssp_dev_data dev_data[2]; > +}; > + > +/* > + * Sequencer port IO pin configuration bits. These do not correlate 1-1 with > + * the hardware. The iosel field in the port data combines iosel1 and iosel2, > + * and is therefore not a direct map to register space. It is best to use the > + * macros below to construct iosel values. > + * > + * least significant 16 bits --> iosel1 > + * most significant 16 bits --> iosel2 > + */ > + > +#define SSP_IN 0x0000 > +#define SSP_DATA 0x0001 > +#define SSP_CLOCK 0x0002 > +#define SSP_CHIPSEL 0x0003 > +#define SSP_OUT 0x0004 > +#define SSP_PIN_SEL(pin, v) ((v) << ((pin) * 3)) > +#define SSP_PIN_MASK(pin) SSP_PIN_SEL(pin, 0x7) > +#define SSP_INPUT_SEL(pin) ((pin) << 16) > + > +/* Sequencer port config bits */ > +#define SSP_EARLY_DIN BIT(8) > +#define SSP_DELAY_DOUT BIT(9) > + > +/* Sequence map definitions */ > +#define SSP_CLK_HIGH BIT(0) > +#define SSP_CLK_LOW 0 > +#define SSP_DATA_HIGH BIT(1) > +#define SSP_DATA_LOW 0 > +#define SSP_CS_HIGH BIT(2) > +#define SSP_CS_LOW 0 > +#define SSP_OUT_MODE BIT(3) > +#define SSP_IN_MODE 0 > +#define SSP_DATA_REG BIT(4) > +#define SSP_ADDR_REG 0 > + > +#define SSP_OPCODE_DIRECT ((0x0) << 5) > +#define SSP_OPCODE_TOGGLE ((0x1) << 5) > +#define SSP_OPCODE_SHIFT ((0x2) << 5) > +#define SSP_OPCODE_BRANCH0 ((0x4) << 5) > +#define SSP_OPCODE_BRANCH1 ((0x5) << 5) > +#define SSP_OPCODE_BRANCH ((0x6) << 5) > +#define SSP_OPCODE_STOP ((0x7) << 5) > +#define SSP_BRANCH(addr) ((addr) << 8) > +#define SSP_COUNT(cycles) ((cycles) << 8) > + > +int ti_ssp_raw_read(struct device *dev); > +int ti_ssp_raw_write(struct device *dev, u32 val); > +int ti_ssp_load(struct device *dev, int offs, u32* prog, int len); > +int ti_ssp_run(struct device *dev, u32 pc, u32 input, u32 *output); > +int ti_ssp_set_mode(struct device *dev, int mode); > +int ti_ssp_set_iosel(struct device *dev, u32 iosel); > + > +#endif /* __TI_SSP_H__ */ From khilman at deeprootsystems.com Fri Dec 10 18:19:48 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 10 Dec 2010 16:19:48 -0800 Subject: [PATCH v7 00/12] tnetv107x ssp drivers In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> (Cyril Chemparathy's message of "Tue, 7 Dec 2010 09:51:50 -0500") References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <87r5dp6tzv.fsf@deeprootsystems.com> Cyril Chemparathy writes: > TI's sequencer serial port (TI-SSP) is a jack-of-all-trades type of serial port > device. It has a built-in programmable execution engine that can be programmed > to operate as almost any serial bus (I2C, SPI, EasyScale, and others). OK, this series seems to be stabilizing. The question now is how to merge it. I'm happy to merge it as a single series via the davinci tree as long as I get some acks on the drivers. For me to merge this, I'm missing some acks for the following: [PATCH v7 04/12] spi: add ti-ssp spi master driver Grant Likely, although I thought he gave an ack earlier) [PATCH v7 10/12] backlight: add support for tps6116x controller Richard Purdie [PATCH v7 08/12] gpio: add ti-ssp gpio driver David Brownell? (MAINTAINERS isn't clear here) If I get acks soon, we can get this in for 2.6.38. Thanks, Kevin From khilman at deeprootsystems.com Fri Dec 10 18:31:19 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 10 Dec 2010 16:31:19 -0800 Subject: [PATCH 1/2] da850: Support for TI's PRU SoftUART Emulation In-Reply-To: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> (Subhasish Ghosh's message of "Fri, 3 Dec 2010 20:41:47 +0530") References: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <87ei9p6tgo.fsf@deeprootsystems.com> Subhasish Ghosh writes: > The patch adds support for emulated UART controllers > on the programmable realtime unit (PRU) available on OMAPL138. > This defines the system resource requirements such as pin mux, > clock, iomem, interrupt etc and registers the platform device > as per the Linux driver model. > > Signed-off-by: Subhasish Ghosh You might consider setting your git user to use your Mistral address, so according to git, the author and the sign-off are the same person. Otherwise, git stats will report your personal address as the author and your company address as the signoff. Also, please Cc linux-arm-kernel at lists.infradead.org on all davinci kernel patches. Otherwise, this patch is looking ok (after addressing alignment issue reported by Sergei.) [...] > diff --git a/arch/arm/mach-davinci/include/mach/memory.h b/arch/arm/mach-davinci/include/mach/memory.h > index 22eb97c..d3e48d9 100644 > --- a/arch/arm/mach-davinci/include/mach/memory.h > +++ b/arch/arm/mach-davinci/include/mach/memory.h > @@ -22,6 +22,7 @@ > **************************************************************************/ > #define DAVINCI_DDR_BASE 0x80000000 > #define DA8XX_DDR_BASE 0xc0000000 > +#define DA8XX_SHARED_RAM_BASE 0x80000000 please align with previous defines > #if defined(CONFIG_ARCH_DAVINCI_DA8XX) && defined(CONFIG_ARCH_DAVINCI_DMx) > #error Cannot enable DaVinci and DA8XX platforms concurrently > diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h > index 212eb4c..407624e 100644 > --- a/include/linux/serial_core.h > +++ b/include/linux/serial_core.h > @@ -199,6 +199,9 @@ > /* TI OMAP-UART */ > #define PORT_OMAP 96 > > +/* omapl pru uart emulation */ > +#define PORT_OMAPL_PRU_SUART 97 > + This define is not used in this series. Please add it when you add the driver which uses it. Kevin From khilman at deeprootsystems.com Fri Dec 10 18:31:42 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 10 Dec 2010 16:31:42 -0800 Subject: [PATCH 2/2] da850: board file modifications for PRU SUART. In-Reply-To: <1291389108-25356-2-git-send-email-subhasish@mistralsolutions.com> (Subhasish Ghosh's message of "Fri, 3 Dec 2010 20:41:48 +0530") References: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> <1291389108-25356-2-git-send-email-subhasish@mistralsolutions.com> Message-ID: <878vzx6tg1.fsf@deeprootsystems.com> Subhasish Ghosh writes: Missing descriptive changelog. Kevin > Signed-off-by: Subhasish Ghosh > --- > arch/arm/mach-davinci/board-da850-evm.c | 28 ++++++++++++++++++++++++++++ > 1 files changed, 28 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index f89b0b7..86a89b1 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -736,6 +736,34 @@ static struct edma_rsv_info *da850_edma_rsv[2] = { > &da850_edma_cc1_rsv, > }; > > +const short da850_evm_pru_suart_pins[] = { > + DA850_AHCLKX, DA850_ACLKX, DA850_AFSX, > + DA850_AHCLKR, DA850_ACLKR, DA850_AFSR, > + DA850_AXR_13, DA850_AXR_9, DA850_AXR_7, > + DA850_AXR_14, DA850_AXR_10, DA850_AXR_8, > + -1 > +}; > + > +static int __init da850_evm_setup_pru_suart(void) > +{ > + int ret; > + > + if (!machine_is_davinci_da850_evm()) > + return 0; > + > + ret = davinci_cfg_reg_list(da850_evm_pru_suart_pins); > + if (ret) > + pr_warning("%s: da850_evm_pru_suart_pins " > + "mux setup failed: %d\n", __func__, ret); > + ret = da8xx_register_pru_suart(); > + if (ret) > + pr_warning("%s: pru suart registration " > + "failed: %d\n", __func__, ret); > + return ret; > +} > + > +device_initcall(da850_evm_setup_pru_suart); > + > static __init void da850_evm_init(void) > { > int ret; From lamiaposta71 at gmail.com Sat Dec 11 00:48:16 2010 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Sat, 11 Dec 2010 07:48:16 +0100 Subject: dm365 ccdc and newer isif Message-ID: Differently from 2.6.32, the isif.c is setting pinmux in platform_device. This is very nice, because YIN4 pinmux setting was overwriting mine. I'll all this behaviour also to 2.6.32. From lamiaposta71 at gmail.com Sat Dec 11 00:59:56 2010 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Sat, 11 Dec 2010 07:59:56 +0100 Subject: dm365 ccdc and newer isif In-Reply-To: References: Message-ID: On Sat, Dec 11, 2010 at 7:48 AM, Raffaele Recalcati wrote: > Differently from 2.6.32, the isif.c is setting pinmux in platform_device. > This is very nice, because YIN4 pinmux setting was overwriting mine. > I'll all this behaviour also to 2.6.32. But I'd like to set static void dm365_isif_setup_pinmux(void) { 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); } in board file, due to 16bit or 8bit port selection In my case I'd like static void dm365_basi_isif_setup_pinmux(void) { davinci_cfg_reg(DM365_VIN_CAM_WEN); davinci_cfg_reg(DM365_VIN_CAM_VD); davinci_cfg_reg(DM365_VIN_CAM_HD); } Is there a clean way to do this? Thx, Raffaele From manjunath.hadli at ti.com Sat Dec 11 03:17:54 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 11 Dec 2010 14:47:54 +0530 Subject: [PATCH v5 0/6] davinci vpbe: dm6446 v4l2 driver Message-ID: <1292059074-668-1-git-send-email-manjunath.hadli@ti.com> version3 : addressed Sergei's and Murali's comments on: 1. Fixed Sergei's comments on language errors in davinci_vpbe_readme.txt 2. Fixed Murali's comments on moving davinci_vpbe_readme.txt to different patch 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: platform specific additions davinci vpbe: Build infrastructure for VPBE driver arch/arm/mach-davinci/board-dm644x-evm.c | 79 +- arch/arm/mach-davinci/dm644x.c | 164 ++- arch/arm/mach-davinci/include/mach/dm644x.h | 4 + drivers/media/video/davinci/Kconfig | 22 + drivers/media/video/davinci/Makefile | 2 + .../media/video/davinci/davinci_vpbe_readme.txt | 100 + drivers/media/video/davinci/vpbe.c | 837 ++++++++ drivers/media/video/davinci/vpbe_display.c | 2099 ++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd.c | 1211 +++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 389 ++++ drivers/media/video/davinci/vpbe_venc.c | 574 ++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 189 ++ include/media/davinci/vpbe.h | 186 ++ include/media/davinci/vpbe_display.h | 146 ++ include/media/davinci/vpbe_osd.h | 397 ++++ include/media/davinci/vpbe_types.h | 93 + include/media/davinci/vpbe_venc.h | 38 + 17 files changed, 6511 insertions(+), 19 deletions(-) create mode 100644 drivers/media/video/davinci/davinci_vpbe_readme.txt 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 Sat Dec 11 03:18:11 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 11 Dec 2010 14:48:11 +0530 Subject: [PATCH v5 1/6] davinci vpbe: V4L2 display driver for DM644X SoC Message-ID: <1292059091-839-1-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 is 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 --- .../media/video/davinci/davinci_vpbe_readme.txt | 100 + drivers/media/video/davinci/vpbe_display.c | 2099 ++++++++++++++++++++ include/media/davinci/vpbe_display.h | 146 ++ include/media/davinci/vpbe_types.h | 93 + 4 files changed, 2438 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/davinci_vpbe_readme.txt 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/davinci_vpbe_readme.txt b/drivers/media/video/davinci/davinci_vpbe_readme.txt new file mode 100644 index 0000000..3ff2dc3 --- /dev/null +++ b/drivers/media/video/davinci/davinci_vpbe_readme.txt @@ -0,0 +1,100 @@ + + 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 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 sub device + 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 connetected 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 ans setting of the external + encoders is not present, and would be a part of the next patch series. + + 3. Venc subdevice + 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. + + 4. osd subdevice + Osd subdevice implements all osd layer management and hardware specific + features. In the legacfy drivers (LSPxxx), the hardware specific features + are configured through proprietary IOCTLs at the fb device interface. Since + subdevices are going to support device nodes, application will be able + to configure the hardware feature directly by opening the osd subdevice + node and by calling the related IOCTL. So these proprietary IOCTLs are + to be removed from the FB Device driver when doing up port of these drivers to + mainline kernel. The V4L2 and FB device nodes supports only IOCTLS as per + the associated spec. The rest of the IOCTLs are to be moved to osd and + venc subdevices. + + Current status:- + + A build tested version of vpbe controller is available. + + Following are TBDs. + + vpbe display controller + - review and modify the handling of 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. + + v4l2 driver + - A version is already developed which is to be cleaned up and unit tested + + FB drivers + - Add support for fbdev drivers.- Ready and part of subsequent patches. diff --git a/drivers/media/video/davinci/vpbe_display.c b/drivers/media/video/davinci/vpbe_display.c new file mode 100644 index 0000000..a29a2a0 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_display.c @@ -0,0 +1,2099 @@ +/* + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#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 +#include "vpbe_venc_regs.h" + + +#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" + +static int debug; +static u32 video2_numbuffers = 3; +static u32 video3_numbuffers = 3; + +#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) +#define VPBE_DEFAULT_NUM_BUFS 3 + +static u32 video2_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; +static u32 video3_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; + +module_param(video2_numbuffers, uint, S_IRUGO); +module_param(video3_numbuffers, uint, S_IRUGO); +module_param(video2_bufsize, uint, S_IRUGO); +module_param(video3_bufsize, uint, S_IRUGO); +module_param(debug, int, 0644); + +static struct buf_config_params display_buf_config_params = { + .min_numbuffers = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[0] = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[1] = VPBE_DEFAULT_NUM_BUFS, + .min_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .min_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, +}; + +static struct vpbe_device *vpbe_dev; +static struct osd_state *osd_device; +static int vpbe_display_nr[] = { 2, 3 }; + +static struct v4l2_capability vpbe_display_videocap = { + .driver = VPBE_DISPLAY_DRIVER, + .bus_info = "platform", + .version = VPBE_DISPLAY_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, +}; + +static u8 layer_first_int[VPBE_DISPLAY_MAX_DEVICES]; + +__iomem void *reg_base_venc; + +static int venc_is_second_field() +{ + u32 val; + val = __raw_readl(reg_base_venc + VENC_VSTAT); + return ((val & VENC_VSTAT_FIDST) + == VENC_VSTAT_FIDST); +} + +/* + * vpbe_display_isr() + * ISR function. It changes status of the displayed buffer, takes next buffer + * from the queue and sets its address in VPBE registers + */ +static void vpbe_display_isr(unsigned int event, void *disp_obj) +{ + unsigned long jiffies_time = get_jiffies_64(); + struct timeval timevalue; + int i, fid; + unsigned long addr = 0; + struct vpbe_display_obj *layer = NULL; + struct vpbe_display *disp_dev = (struct vpbe_display *)disp_obj; + + /* Convert time represention from jiffies to timeval */ + jiffies_to_timeval(jiffies_time, &timevalue); + + 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; + /* Check the field format */ + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && + (event & OSD_END_OF_FRAME)) { + /* Progressive mode */ + if (layer_first_int[i]) + layer_first_int[i] = 0; + continue; + /* + * Mark status of the cur_frm to + * done and unlock semaphore on it + */ + + if (layer->cur_frm != layer->next_frm) { + layer->cur_frm->ts = timevalue; + 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; + } + /* Get the next buffer from buffer queue */ + spin_lock(&disp_dev->dma_queue_lock); + if (!list_empty(&layer->dma_queue)) { + layer->next_frm = + list_entry(layer->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove that buffer from the buffer queue */ + list_del(&layer->next_frm->queue); + /* Mark status of the buffer as 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_dev->cbcr_ofst); + } + spin_unlock(&disp_dev->dma_queue_lock); + } else { + /* + * Interlaced mode + * If it is first interrupt, ignore it + */ + if (layer_first_int[i]) { + layer_first_int[i] = 0; + return; + } + + layer->field_id ^= 1; + if (event & OSD_FIRST_FIELD) + fid = 0; + else if (event & OSD_SECOND_FIELD) + fid = 1; + else + return; + + /* + * If field id does not match with stored + * field id + */ + if (fid != layer->field_id) { + /* Make them in sync */ + if (0 == fid) + layer->field_id = fid; + + return; + } + /* + * device field id and local field id are + * in sync. If this is even field + */ + if (0 == fid) { + if (layer->cur_frm == layer->next_frm) + continue; + /* + * one frame is displayed If next frame is + * available, release cur_frm and move on + * copy frame display time + */ + layer->cur_frm->ts = timevalue; + /* Change status of the cur_frm */ + layer->cur_frm->state = VIDEOBUF_DONE; + /* unlock semaphore on cur_frm */ + wake_up_interruptible(&layer->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + layer->cur_frm = layer->next_frm; + } else if (1 == fid) { /* odd field */ + + if (list_empty(&layer->dma_queue) + || (layer->cur_frm != layer->next_frm)) + continue; + + /* + * one field is displayed configure + * the next frame if it is available + * otherwise hold on current frame + * Get next from the buffer queue + */ + spin_lock(&disp_dev->dma_queue_lock); + 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); + + /* 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_dev->cbcr_ofst); + spin_unlock(&disp_dev->dma_queue_lock); + } + } + } +} + +/* interrupt service routine */ +static irqreturn_t venc_isr(int irq, void *arg) +{ + static unsigned last_event; + unsigned event = 0; + + if (venc_is_second_field()) + 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; + + vpbe_display_isr(event, arg); + 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_display_obj *layer = fh->layer; + unsigned long addr; + int ret = 0; + + 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_display_obj *layer = fh->layer; + int buf_size; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); + + *size = layer->pix_fmt.sizeimage; + buf_size = + display_buf_config_params.layer_bufsize[layer->device_id]; + /* + * For MMAP, limit the memory allocation as per bootarg + * configured buffer size + */ + if (V4L2_MEMORY_MMAP == layer->memory) + if (*size > buf_size) + *size = buf_size; + + /* Store number of buffers allocated in numbuffer member */ + if (*count < display_buf_config_params.min_numbuffers) + *count = layer->numbuffers = + display_buf_config_params.numbuffers[layer->device_id]; + + 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_display_obj *layer = fh->layer; + struct vpbe_display *disp = fh->disp_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_display_obj *layer = fh->layer; + + 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_display_obj* +_vpbe_display_get_other_win(struct vpbe_display *disp_dev, + struct vpbe_display_obj *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_video_display_params(struct vpbe_display *disp_dev, + struct vpbe_display_obj *layer) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + unsigned long addr; + int ret = 0; + + 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_display_obj *otherlayer = + _vpbe_display_get_other_win(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_display_obj *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; + int h_scale = 0, v_scale = 0, h_exp = 0, v_exp = 0, temp; + 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)) { + temp = (cfg->xsize * + VPBE_DISPLAY_H_EXP_RATIO_N) / + VPBE_DISPLAY_H_EXP_RATIO_D; + if (temp <= expected_xsize) { + h_exp = 1; + cfg->xsize = temp; + } + } + } + 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)) { + temp = (cfg->ysize * + VPBE_DISPLAY_V_EXP_RATIO_N) / + VPBE_DISPLAY_V_EXP_RATIO_D; + if (temp <= expected_ysize) { + v_exp = 1; + cfg->ysize = temp; + } + } + } + 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_display_obj *layer, + int top, int left) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + + cfg->xpos = cfg->ypos = 0; + if (left + cfg->xsize <= vpbe_dev->current_timings.xres) + cfg->xpos = left; + if (top + cfg->ysize <= vpbe_dev->current_timings.yres) + cfg->ypos = top; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "new xpos = %d, ypos = %d\n", + cfg->xpos, cfg->ypos); +} + +static int vpbe_disp_check_window_params(struct vpbe_display *disp_dev, + struct v4l2_rect *c) +{ + if ((c->width == 0) + || ((c->width + c->left) > vpbe_dev->current_timings.xres) + || (c->height == 0) + || ((c->height + c->top) > vpbe_dev->current_timings.yres)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid crop values\n"); + return -1; + } + if ((c->height & 0x1) && (vpbe_dev->current_timings.interlaced)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "window height must be even for interlaced display\n"); + return -1; + } + return 0; +} + +/** + * 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. If application likes to add pads at the end of each line and + * end of the buffer , it can set bytesperline to line size and sizeimage to + * bytesperline * height of the buffer. If driver fills zero for active + * video width and height, and has requested user bytesperline and sizeimage, + * width and height is adjusted to maximum display limit or buffer width + * height which ever is lower + */ +static int vpbe_try_format(struct vpbe_display *disp_dev, + struct v4l2_pix_format *pixfmt, int check) +{ + int min_sizeimage, bpp, min_height = 1, min_width = 32, + max_width, max_height, user_info = 0; + + 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_ANY) { + 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->bytesperline) { + v4l2_err(&vpbe_dev->v4l2_dev, "bytesperline and width" + " cannot be zero\n"); + return -EINVAL; + } + + /* if user provided bytesperline, it must provide sizeimage as well */ + if (pixfmt->bytesperline && !pixfmt->sizeimage) { + v4l2_err(&vpbe_dev->v4l2_dev, + "sizeimage must be non zero, when user" + " provides bytesperline\n"); + return -EINVAL; + } + + /* adjust bytesperline as per hardware - multiple of 32 */ + if (!pixfmt->width) + pixfmt->width = pixfmt->bytesperline / bpp; + + if (!pixfmt->bytesperline) + pixfmt->bytesperline = pixfmt->width * bpp; + else + user_info = 1; + pixfmt->bytesperline = ((pixfmt->bytesperline + 31) & ~31); + + if (pixfmt->width < min_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is less than minimum," + "input width = %d, min_width = %d\n", + pixfmt->width, min_width); + return -EINVAL; + } + pixfmt->width = min_width; + } + + if (pixfmt->width > max_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is more than maximum," + "input width = %d, max_width = %d\n", + pixfmt->width, max_width); + return -EINVAL; + } + pixfmt->width = max_width; + } + + /* + * If height is zero, then atleast we need to have sizeimage + * to calculate height + */ + if (!pixfmt->height) { + if (user_info) { + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) { + /* + * for NV12 format, sizeimage is y-plane size + * + CbCr plane which is half of y-plane + */ + pixfmt->height = pixfmt->sizeimage / + (pixfmt->bytesperline + + (pixfmt->bytesperline >> 1)); + } else + pixfmt->height = pixfmt->sizeimage/ + pixfmt->bytesperline; + } + } + + if (pixfmt->height > max_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is more than maximum," + "input height = %d, max_height = %d\n", + pixfmt->height, max_height); + return -EINVAL; + } + pixfmt->height = max_height; + } + + if (pixfmt->height < min_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is less than minimum," + "input height = %d, min_height = %d\n", + pixfmt->height, min_height); + return -EINVAL; + } + pixfmt->height = min_width; + } + + /* if user has not provided bytesperline calculate it based on width */ + if (!user_info) + pixfmt->bytesperline = (((pixfmt->width * bpp) + 31) & ~31); + + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + min_sizeimage = pixfmt->bytesperline * pixfmt->height + + (pixfmt->bytesperline * pixfmt->height >> 1); + else + min_sizeimage = pixfmt->bytesperline * pixfmt->height; + + if (pixfmt->sizeimage < min_sizeimage) { + if (check && user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, "sizeimage is less, %d\n", + min_sizeimage); + return -EINVAL; + } + pixfmt->sizeimage = min_sizeimage; + } + 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_display_obj *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_display_obj *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_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id); + *cap = vpbe_display_videocap; + + return 0; +} + +static int vpbe_display_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct vpbe_display *disp_dev = video_drvdata(file); + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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) { + struct v4l2_rect *rect = &crop->c; + + if (rect->top < 0 || rect->left < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + + if (vpbe_disp_check_window_params(disp_dev, rect)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + 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); + + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + return ret; +} + +static int vpbe_display_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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) { + struct v4l2_rect *rect = &crop->c; + if (ret) + return ret; + 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; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + ret = -EINVAL; + } + + return ret; +} + +static int vpbe_display_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + + 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_display_obj *layer = fh->layer; + + 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) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Fill in the information about format */ + *pixfmt = layer->pix_fmt; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + + 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_display_obj *layer = fh->layer; + 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 > 0) { + 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 if (index == 1) { + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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; + } else { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* 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; + + /* Change of the default pixel format for both video windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { + struct vpbe_display_obj *otherlayer; + cfg->pixfmt = PIXFMT_NV12; + otherlayer = _vpbe_display_get_other_win(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); + + /* verify if readback values are as expected */ + if (layer->pix_fmt.width != cfg->xsize || + layer->pix_fmt.height != cfg->ysize || + layer->pix_fmt.bytesperline != layer->layer_info. + config.line_length || + (cfg->interlaced + && layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || + (!cfg->interlaced && layer->pix_fmt.field + != V4L2_FIELD_NONE)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "mismatch:layer conf params:\n"); + return -EINVAL; + } + } + + return 0; +} + +static int vpbe_display_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Check for valid field format */ + return vpbe_try_format(disp_dev, pixfmt, 0); + } + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; +} + +/** + * 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_display_obj *layer = fh->layer; + int ret = 0; + + 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; + } + } + 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) +{ + 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) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_outputs) { + 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_display_obj *layer = fh->layer; + int ret = 0; + + 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) { + 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 ret; +} + +/** + * 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) +{ + 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) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_dv_presets) { + 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 ret; +} + +/** + * 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_display_obj *layer = fh->layer; + int ret = 0; + + 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) { + 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 ret; +} + +/** + * 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) +{ + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + + 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_video_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_first_int[layer->device_id] = 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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_display_obj *layer = fh->layer; + + 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) +{ + unsigned int err = 0; + struct vpbe_fh *fh = filep->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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; +} + +static int vpbe_display_cfg_layer_default(enum vpbe_display_device_id id, + struct vpbe_display *disp_dev) +{ + int err = 0; + struct osd_layer_config *layer_config; + struct vpbe_display_obj *layer = disp_dev->dev[id]; + struct osd_layer_config *cfg = &layer->layer_info.config; + + /* 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"); + return -EBUSY; + } + + layer_config = cfg; + /* Set the default image and crop values */ + layer_config->pixfmt = PIXFMT_YCbCrI; + layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY; + layer->pix_fmt.bytesperline = layer_config->line_length = + vpbe_dev->current_timings.xres * 2; + + layer->pix_fmt.width = layer_config->xsize = + vpbe_dev->current_timings.xres; + layer->pix_fmt.height = layer_config->ysize = + vpbe_dev->current_timings.yres; + layer->pix_fmt.sizeimage = + layer->pix_fmt.bytesperline * layer->pix_fmt.height; + layer_config->xpos = 0; + layer_config->ypos = 0; + layer_config->interlaced = vpbe_dev->current_timings.interlaced; + + /* + * turn off ping-pong buffer and field inversion to fix + * the image shaking problem in 1080I mode + */ + + if (cfg->interlaced) + layer->pix_fmt.field = V4L2_FIELD_INTERLACED; + else + layer->pix_fmt.field = V4L2_FIELD_NONE; + + err = osd_device->ops.set_layer_config(osd_device, + layer->layer_info.id, + layer_config); + if (err < 0) { + /* Couldn't set layer */ + v4l2_err(&vpbe_dev->v4l2_dev, + "Display Manager failed to set osd layer\n"); + return -EBUSY; + } + + return 0; +} + +/* + * 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) +{ + int minor = iminor(file->f_path.dentry->d_inode); + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer; + struct vpbe_fh *fh = NULL; + int found = -1; + int i = 0; + + /* Check for valid minor number */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + layer = disp_dev->dev[i]; + if (minor == layer->video_dev->minor) { + found = i; + break; + } + } + + /* If not found, return error no device */ + if (0 > found) { + v4l2_err(&vpbe_dev->v4l2_dev, "device not found\n"); + return -ENODEV; + } + + /* 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) { + /* Configure the default values for the layer */ + if (vpbe_display_cfg_layer_default(layer->device_id, + disp_dev)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to configure video layer" + " for id = %d\n", layer->device_id); + 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_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); + /* If this is doing IO and other layer are not closed */ + if ((layer->usrs != 1) && fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "Close other instances\n"); + return -EAGAIN; + } + + /* 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_display_obj *otherlayer; + otherlayer = + _vpbe_display_get_other_win(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); + + if (strcmp("vpbe_controller", pdev->name) == 0) + vpbe_dev = platform_get_drvdata(pdev); + + if (strcmp("vpbe-osd", pdev->name) == 0) + osd_device = platform_get_drvdata(pdev); + + return 0; +} + +/*Configure the channels, buffer size */ +static int init_vpbe_layer_objects(int i) +{ + int free_buffer_index; + + /* Default number of buffers should be 3 */ + if ((video2_numbuffers > 0) && + (video2_numbuffers < display_buf_config_params.min_numbuffers)) + video2_numbuffers = display_buf_config_params.min_numbuffers; + if ((video3_numbuffers > 0) && + (video3_numbuffers < display_buf_config_params.min_numbuffers)) + video3_numbuffers = display_buf_config_params.min_numbuffers; + + /* + * Set buffer size to min buffers size if invalid + * buffer size is given + */ + if (video2_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]) + video2_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]; + + if (video3_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]) + video3_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]; + + /* set number of buffers, they could come from boot/args */ + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_0] = + video2_numbuffers; + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_1] = + video3_numbuffers; + + if (display_buf_config_params.numbuffers[0] == 0) + printk(KERN_ERR "no vid2 buffer allocated\n"); + if (display_buf_config_params.numbuffers[1] == 0) + printk(KERN_ERR "no vid3 buffer allocated\n"); + free_buffer_index = display_buf_config_params.numbuffers[i - 1]; + + 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 __init int vpbe_display_probe(struct platform_device *pdev) +{ + int i, j = 0, k, err = 0; + struct vpbe_display *disp_dev; + struct video_device *vbd = NULL; + struct vpbe_display_obj *vpbe_display_layer = NULL; + struct resource *res; + 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; + } + + /* Allocate memory for four plane display objects */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + disp_dev->dev[i] = + kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL); + /* If memory allocation fails, return error */ + if (!disp_dev->dev[i]) { + printk(KERN_ERR "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + spin_lock_init(&disp_dev->dev[i]->irqlock); + mutex_init(&disp_dev->dev[i]->opslock); + } + spin_lock_init(&disp_dev->dma_queue_lock); + + err = init_vpbe_layer_objects(i); + if (err) { + printk(KERN_ERR "Error initializing vpbe display\n"); + return err; + } + + /* + * 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, NULL, + vpbe_device_get); + if (err < 0) + return err; + + /* Initialize the vpbe display controller */ + if (NULL != vpbe_dev->ops.initialize) { + err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev); + if (err) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error initing vpbe\n"); + err = -ENOMEM; + goto probe_out; + } + } + + /* check the name of davinci device */ + if (vpbe_dev->cfg->module_name != NULL) + strcpy(vpbe_display_videocap.card, + vpbe_dev->cfg->module_name); + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[i]; + /* Allocate memory for video device */ + vbd = video_device_alloc(); + if (vbd == NULL) { + for (j = 0; j < i; j++) { + video_device_release( + disp_dev->dev[j]->video_dev); + } + v4l2_err(&vpbe_dev->v4l2_dev, "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + /* Initialize field of video device */ + vbd->release = video_device_release; + vbd->fops = &vpbe_fops; + vbd->ioctl_ops = &vpbe_ioctl_ops; + vbd->minor = -1; + vbd->v4l2_dev = &vpbe_dev->v4l2_dev; + vbd->lock = &vpbe_display_layer->opslock; + + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { + vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); + vbd->current_norm = + 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); + + /* Set video_dev to the video device */ + vpbe_display_layer->video_dev = vbd; + vpbe_display_layer->device_id = i; + + vpbe_display_layer->layer_info.id = + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); + if (display_buf_config_params.numbuffers[i] == 0) + vpbe_display_layer->memory = V4L2_MEMORY_USERPTR; + else + vpbe_display_layer->memory = V4L2_MEMORY_MMAP; + + /* Initialize field of the display layer objects */ + vpbe_display_layer->usrs = 0; + vpbe_display_layer->io_usrs = 0; + vpbe_display_layer->started = 0; + + /* Initialize prio member of layer object */ + v4l2_prio_init(&vpbe_display_layer->prio); + + /* Register video device */ + v4l2_info(&vpbe_dev->v4l2_dev, + "Trying to register VPBE display device.\n"); + v4l2_info(&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, + vpbe_display_nr[i]); + if (err) + goto probe_out; + /* set the driver data in platform device */ + platform_set_drvdata(pdev, disp_dev); + video_set_drvdata(vpbe_display_layer->video_dev, disp_dev); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to get VENC register address map\n"); + err = -ENODEV; + goto probe_out; + } + + reg_base_venc = ioremap_nocache(res->start, resource_size(res)); + if (!reg_base_venc) { + v4l2_err(&vpbe_dev->v4l2_dev, "Unable to map VENC IO space\n"); + err = -ENODEV; + goto probe_out; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + v4l2_err(&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(&vpbe_dev->v4l2_dev, "Unable to request interrupt\n"); + err = -ENODEV; + goto probe_out; + } + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); + return 0; +probe_out: + kfree(disp_dev); + + for (k = 0; k < j; k++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[k]; + /* Unregister video device */ + video_unregister_device(vpbe_display_layer->video_dev); + /* Release video device */ + video_device_release(vpbe_display_layer->video_dev); + vpbe_display_layer->video_dev = NULL; + } + return err; +} + +/* + * vpbe_display_remove() + * It un-register hardware layer from V4L2 driver + */ +static int vpbe_display_remove(struct platform_device *pdev) +{ + int i; + struct vpbe_display_obj *vpbe_display_layer; + struct vpbe_display *disp_dev = platform_get_drvdata(pdev); + struct resource *res; + + 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); + + vpbe_display_layer->video_dev = NULL; + } + 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 __init int vpbe_display_init(void) +{ + int err = 0; + + 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 DMXXX 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..90ad066 --- /dev/null +++ b/include/media/davinci/vpbe_display.h @@ -0,0 +1,146 @@ +/* + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef VPBE_DISPLAY_H +#define VPBE_DISPLAY_H + +#ifdef __KERNEL__ + +/* 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_display_obj { + /* number of buffers in fbuffers */ + unsigned int numbuffers; + /* 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; +}; + +/* 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_display_obj *dev[VPBE_DISPLAY_MAX_DEVICES]; +}; + +/* File handle structure */ +struct vpbe_fh { + /* vpbe device structure */ + struct vpbe_display *disp_dev; + /* pointer to layer object for opened device */ + struct vpbe_display_obj *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]; +}; + +static int venc_is_second_field(void); +#endif /* end of __KERNEL__ */ +#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..a4b8585 --- /dev/null +++ b/include/media/davinci/vpbe_types.h @@ -0,0 +1,93 @@ +/* + * 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; either version 2 of the License. + * + * 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_types { + DM644X_VPBE = 1, + DM355_VPBE, + DM365_VPBE, +}; + +/* 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 Sat Dec 11 03:18:45 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 11 Dec 2010 14:48:45 +0530 Subject: [PATCH v5 2/6] davinci vpbe: VPBE display driver Message-ID: <1292059125-1026-1-git-send-email-manjunath.hadli@ti.com> This patch implements the coe functionality of the dislay driver, mainly controlling the VENC and other encoders, and acting as the one point interface for the man V4L2 driver.This implements the cre 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 | 837 ++++++++++++++++++++++++++++++++++++ include/media/davinci/vpbe.h | 186 ++++++++ 2 files changed, 1023 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..751370f --- /dev/null +++ b/drivers/media/video/davinci/vpbe.c @@ -0,0 +1,837 @@ +/* + * 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; either version 2 of the License. + * + * 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 + + +#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 struct osd_state *osd_device; +static struct venc_platform_data *venc_device; +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(ef_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_display_config *vpbe_config = vpbe_dev->cfg; + int index = vpbe_dev->current_sd_index; + + return ((index == 0) ? &vpbe_config->venc : + &vpbe_config->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_display_config *vpbe_config, + int index) +{ + char *encoder_name = vpbe_config->outputs[index].subdev_name; + int i; + + /* Venc is always first */ + if (!strcmp(encoder_name, vpbe_config->venc.module_name)) + return 0; + + for (i = 0; i < vpbe_config->num_ext_encoders; i++) { + if (!strcmp(encoder_name, + vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; + int temp_index = output->index; + + if (temp_index >= vpbe_config->num_outputs) + return -EINVAL; + + *output = vpbe_config->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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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 vpbe_display_config *vpbe_config = vpbe_dev->cfg; + struct encoder_config_info *curr_enc_info = + vpbe_current_encoder_info(vpbe_dev); + int ret = 0, enc_out_index = 0, sd_index; + + if (index >= vpbe_config->num_outputs) + return -EINVAL; + + mutex_lock(&vpbe_dev->lock); + + sd_index = vpbe_dev->current_sd_index; + enc_out_index = vpbe_config->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, + vpbe_config->outputs[index].subdev_name)) { + /* Need to switch the encoder at the output */ + sd_index = vpbe_find_encoder_sd_index(vpbe_config, 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, + vpbe_config->outputs[index].default_mode); + if (!ret) { + 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_display_config *vpbe_config = vpbe_dev->cfg; + int i, ret = 0; + + for (i = 0; i < vpbe_config->num_outputs; i++) { + if (!strcmp(def_output, + vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; + int sd_index = vpbe_dev->current_sd_index; + int out_index = vpbe_dev->current_out_index, ret; + + + if (!(vpbe_config->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) { + 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_display_config *vpbe_config = vpbe_dev->cfg; + int out_index = vpbe_dev->current_out_index; + struct vpbe_output *output = &vpbe_config->outputs[out_index]; + int i, j = 0; + + 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_display_config *vpbe_config = vpbe_dev->cfg; + int sd_index = vpbe_dev->current_sd_index, out_index = + vpbe_dev->current_out_index, ret; + + if (!(vpbe_config->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) { + 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_display_config *vpbe_config = vpbe_dev->cfg; + int out_index = vpbe_dev->current_out_index, ret = 0, i; + struct vpbe_enc_mode_info *preset_mode = NULL; + struct v4l2_dv_preset dv_preset; + + if ((NULL == mode_info) || (NULL == mode_info->name)) + return -EINVAL; + + for (i = 0; i < vpbe_config->outputs[out_index].num_modes; i++) { + if (!strcmp(mode_info->name, + vpbe_config->outputs[out_index].modes[i].name)) { + preset_mode = &vpbe_config->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); + + if (!ret) { + 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); + if (strcmp("vpbe-osd", pdev->name) == 0) + osd_device = platform_get_drvdata(pdev); + if (strcmp("vpbe-venc", pdev->name) == 0) + venc_device = dev_get_platdata(&pdev->dev); + + 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; + int i, ret = 0, num_encoders; + struct i2c_adapter *i2c_adap; + int output_index; + int err; + + /* + * 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, NULL, + 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 */ + 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->module_name, + &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. + */ +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; + /* disaable 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 __init int vpbe_probe(struct platform_device *pdev) +{ + struct vpbe_display_config *vpbe_config; + struct vpbe_device *vpbe_dev; + + int ret = -EINVAL; + + if (NULL == pdev->dev.platform_data) { + v4l2_err(pdev->dev.driver, "Unable to get vpbe config\n"); + return -ENODEV; + } + + if (pdev->dev.platform_data == NULL) { + v4l2_err(pdev->dev.driver, "No platform data\n"); + return -ENODEV; + } + vpbe_config = pdev->dev.platform_data; + + if (!vpbe_config->module_name[0] || + !vpbe_config->osd.module_name[0] || + !vpbe_config->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 = vpbe_config; + vpbe_dev->ops = vpbe_dev_ops; + vpbe_dev->pdev = &pdev->dev; + + if (vpbe_config->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..c8853f2 --- /dev/null +++ b/include/media/davinci/vpbe.h @@ -0,0 +1,186 @@ +/* + * 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; either version 2 of the License. + * + * 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 + +/* 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_display_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_display_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; + + /* + * 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; +}; + +/* 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 Sat Dec 11 03:19:08 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 11 Dec 2010 14:49:08 +0530 Subject: [PATCH v5 3/6] davinci vpbe: OSD(On Screen Display) block Message-ID: <1292059148-1174-1-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 | 1211 +++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++++++ include/media/davinci/vpbe_osd.h | 397 +++++++++ 3 files changed, 1997 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..2599c83 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd.c @@ -0,0 +1,1211 @@ +/* + * 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; 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 + * + */ +#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 __raw_readl(osd->osd_base + offset); +} + +static inline u32 osd_write(struct osd_state *sd, u32 val, u32 offset) +{ + struct osd_state *osd = sd; + __raw_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 = __raw_readl(addr) | mask; + + __raw_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 = __raw_readl(addr) & ~mask; + + __raw_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 = (__raw_readl(addr) & ~mask) | (val & mask); + __raw_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) +{ + +#ifdef CONFIG_DM6446_FIELD_INV_WORKAROUND + 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; + } +#endif + 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) +{ + int bmp_reg, bmp_offset, bmp_mask, bmp_shift; + static const int map_1bpp[] = { 0, 15 }; + static const int map_2bpp[] = { 0, 5, 10, 15 }; + + 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]; + unsigned long flags; + struct osd_layer_config *cfg = &win->lconfig; + + 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]; + unsigned long flags; + struct osd_layer_config *cfg = &win->lconfig; + + 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 = 0; + + /* 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; + int reject_config; + unsigned long flags; + + 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_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; + osd->vpbe_type = (enum vpbe_types)pdev->dev.platform_data; + 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) +{ + /* Register the driver */ + 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..9cd3839 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd_regs.h @@ -0,0 +1,389 @@ +/* + * 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; either version 2 of the License. + * + * 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 + +enum vpbe_soc_type { + DM644x = 0, + DM35x, + DM36x, +}; + +struct vpbe_osd_platform_data { + enum vpbe_soc_type soc; +}; + +#define DM644X_OSD_REG_BASE 0x01C72600 +#define DM644X_VPBE_REG_BASE 0x01C72780 + +#define DM355_VPSSCLK_REG_BASE 0x01C70000 +#define DM355_OSD_REG_BASE 0x01C70200 + +#define DM365_OSD_REG_BASE 0x01C71C00 +#define DM365_ISP5_REG_BASE 0x01C70000 + +#define OSD_REG_SIZE 0x00000100 + +/* SYS register addresses */ +#define SYS_VPSS_CLKCTL 0x01C40044 + +/* 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..9549f3e --- /dev/null +++ b/include/media/davinci/vpbe_osd.h @@ -0,0 +1,397 @@ +/* + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef _OSD_H +#define _OSD_H + +#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; +}; +/* OSD events. Should match with that in vpbe_venc.h */ +#define OSD_END_OF_FRAME 1 +#define OSD_FIRST_FIELD 2 +#define OSD_SECOND_FIELD 4 + +/* 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; + +/* TBD. Some of these operations here are to be removed and implemented + * as a ioctl call on the osd sub device node. Right now just trying to + * make this work. + */ +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_types 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; +}; + + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Sat Dec 11 03:19:29 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 11 Dec 2010 14:49:29 +0530 Subject: [PATCH v5 4/6] davinci vpbe: VENC( Video Encoder) implementation Message-ID: <1292059169-1317-1-git-send-email-manjunath.hadli@ti.com> This patch adds the VENC or the Video encoder, whichis responsible for the blending of al 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 extenal 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 | 574 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++++++++ include/media/davinci/vpbe_venc.h | 38 ++ 3 files changed, 801 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..fafb41a --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc.c @@ -0,0 +1,574 @@ +/* + * 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; 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 + */ +#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 __raw_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); + __raw_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); + + __raw_writel(val, venc->vdaccfg_reg); + + val = __raw_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) +{ + int ret = 0; + + 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: + ret = -EINVAL; + } + return ret; +} + +static void enabledigitaloutput(struct v4l2_subdev *sd, int benable) +{ + v4l2_dbg(debug, 2, sd, "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; + + 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; + + 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; + + 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; + + 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) +{ + int ret = 0; + + v4l2_dbg(debug, 1, sd, "venc_s_std_output\n"); + + if (norm & V4L2_STD_525_60) + ret = venc_set_ntsc(sd); + else if (norm & V4L2_STD_625_50) + ret = venc_set_pal(sd); + else + ret = -EINVAL; + return ret; +} + +static int venc_s_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *dv_preset) +{ + int ret = -EINVAL; + + 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 ret; +} + +static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct venc_state *venc = to_state(sd); + int max_output, lcd_out_index, ret = 0; + + v4l2_dbg(debug, 1, sd, "venc_s_routing\n"); + + max_output = 2 + venc->pdata->num_lcd_outputs; + lcd_out_index = 3; + + if (output >= max_output) + return -EINVAL; + + if (output < lcd_out_index) + ret = venc_set_dac(sd, output); + if (!ret) + venc->output = output; + return ret; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int venc_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + __iomem void *reg_base; + + if (reg->match.type == 2) + reg->val = venc_read(sd, reg->reg); + else if (reg->match.type == 3) { + reg_base = ioremap_nocache(0x01c70800, 0x80); + if (reg_base == NULL) + return -EINVAL; + reg->val = __raw_readl(reg_base + reg->reg); + iounmap(reg_base); + } else { + reg_base = ioremap_nocache(0x01c70000, 0x80); + if (reg_base == NULL) + return -1; + reg->val = __raw_readl(reg_base + reg->reg); + iounmap(reg_base); + } + return 0; +} +#endif + +static const struct v4l2_subdev_core_ops venc_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = venc_g_register, +#endif +}; + +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 = 0; + + /* 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) +{ + /* Register the driver */ + 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..38e4818 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc_regs.h @@ -0,0 +1,189 @@ +/* + * 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; either version 2 of the License. + * + * 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 register base addresses */ +#define DM644X_VENC_REG_BASE 0x01C72400 +#define DM644X_VPBE_REG_BASE 0x01C72780 + +#define DM355_VENC_REG_BASE 0x01C70400 + +#define DM365_VENC_REG_BASE 0x01C71E00 +#define DM365_ISP5_REG_BASE 0x01C70000 + +#define DM3XX_VDAC_CONFIG 0x01C4002C +#define DM355_USB_PHY_CTRL 0x01c40034 + +/* 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..47a977c --- /dev/null +++ b/include/media/davinci/vpbe_venc.h @@ -0,0 +1,38 @@ +/* + * 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; either version 2 of the License. + * + * 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 + +#define VPBE_VENC_SUBDEV_NAME "vpbe-venc" + +/* venc events */ +#define VENC_END_OF_FRAME 1 +#define VENC_FIRST_FIELD 2 +#define VENC_SECOND_FIELD 4 + +struct venc_platform_data { + enum vpbe_types venc_type; + int (*setup_clock)(enum vpbe_enc_timings_type type, + __u64 mode); + /* Number of LCD outputs supported */ + int num_lcd_outputs; +}; + + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Sat Dec 11 03:19:56 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 11 Dec 2010 14:49:56 +0530 Subject: [PATCH v5 5/6] davinci vpbe: platform specific additions Message-ID: <1292059196-1439-1-git-send-email-manjunath.hadli@ti.com> This patch implements the overall device creation for the Video display driver, and addition of tables for the mode and output list. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- arch/arm/mach-davinci/board-dm644x-evm.c | 79 +++++++++++-- arch/arm/mach-davinci/dm644x.c | 164 ++++++++++++++++++++++++++- arch/arm/mach-davinci/include/mach/dm644x.h | 4 + 3 files changed, 228 insertions(+), 19 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 34c8b41..e9b1243 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -166,18 +166,6 @@ static struct platform_device davinci_evm_nandflash_device = { .resource = davinci_evm_nandflash_resource, }; -static u64 davinci_fb_dma_mask = DMA_BIT_MASK(32); - -static struct platform_device davinci_fb_device = { - .name = "davincifb", - .id = -1, - .dev = { - .dma_mask = &davinci_fb_dma_mask, - .coherent_dma_mask = DMA_BIT_MASK(32), - }, - .num_resources = 0, -}; - static struct tvp514x_platform_data tvp5146_pdata = { .clk_polarity = 0, .hs_polarity = 1, @@ -606,8 +594,71 @@ static void __init evm_init_i2c(void) i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info)); } +#define VENC_STD_ALL (V4L2_STD_NTSC | V4L2_STD_PAL) +/* venc standards timings */ +static struct vpbe_enc_mode_info vbpe_enc_std_timings[] = { + {"ntsc", VPBE_ENC_STD, {V4L2_STD_525_60}, 1, 720, 480, + {11, 10}, {30000, 1001}, 0x79, 0, 0x10, 0, 0, 0, 0}, + {"pal", VPBE_ENC_STD, {V4L2_STD_625_50}, 1, 720, 576, + {54, 59}, {25, 1}, 0x7E, 0, 0x16, 0, 0, 0, 0}, +}; + +/* venc dv preset timings */ +static struct vpbe_enc_mode_info vbpe_enc_preset_timings[] = { + {"480p59_94", VPBE_ENC_DV_PRESET, {V4L2_DV_480P59_94}, 0, 720, 480, + {1, 1}, {5994, 100}, 0x80, 0, 0x20, 0, 0, 0, 0}, + {"576p50", VPBE_ENC_DV_PRESET, {V4L2_DV_576P50}, 0, 720, 576, + {1, 1}, {50, 1}, 0x7E, 0, 0x30, 0, 0, 0, 0}, +}; + +/* + * The outputs available from VPBE + ecnoders. Keep the + * the order same as that of encoders. First that from venc followed by that + * from encoders. Index in the output refers to index on a particular encoder. + * Driver uses this index to pass it to encoder when it supports more than + * one output. Application uses index of the array to set an output. + */ +static struct vpbe_output dm644x_vpbe_outputs[] = { + { + .output = { + .index = 0, + .name = "Composite", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .std = VENC_STD_ALL, + .capabilities = V4L2_OUT_CAP_STD, + }, + .subdev_name = VPBE_VENC_SUBDEV_NAME, + .default_mode = "ntsc", + .num_modes = ARRAY_SIZE(vbpe_enc_std_timings), + .modes = vbpe_enc_std_timings, + }, + { + .output = { + .index = 1, + .name = "Component", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .capabilities = V4L2_OUT_CAP_PRESETS, + }, + .subdev_name = VPBE_VENC_SUBDEV_NAME, + .default_mode = "480p59_94", + .num_modes = ARRAY_SIZE(vbpe_enc_preset_timings), + .modes = vbpe_enc_preset_timings, + }, +}; + +static struct vpbe_display_config vpbe_display_cfg = { + .module_name = "dm644x-vpbe-display", + .i2c_adapter_id = 1, + .osd = { + .module_name = VPBE_OSD_SUBDEV_NAME, + }, + .venc = { + .module_name = VPBE_VENC_SUBDEV_NAME, + }, + .num_outputs = ARRAY_SIZE(dm644x_vpbe_outputs), + .outputs = dm644x_vpbe_outputs, +}; static struct platform_device *davinci_evm_devices[] __initdata = { - &davinci_fb_device, &rtc_dev, }; @@ -620,6 +671,8 @@ davinci_evm_map_io(void) { /* setup input configuration for VPFE input devices */ dm644x_set_vpfe_config(&vpfe_cfg); + /* setup configuration for vpbe devices */ + dm644x_set_vpbe_display_config(&vpbe_display_cfg); dm644x_init(); } diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 5e5b0a7..e8b8e94 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -640,6 +640,142 @@ void dm644x_set_vpfe_config(struct vpfe_config *cfg) vpfe_capture_dev.dev.platform_data = cfg; } +static struct resource dm644x_osd_resources[] = { + { + .start = 0x01C72600, + .end = 0x01C72600 + 0x200, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 dm644x_osd_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device dm644x_osd_dev = { + .name = VPBE_OSD_SUBDEV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_osd_resources), + .resource = dm644x_osd_resources, + .dev = { + .dma_mask = &dm644x_osd_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = (void *)DM644X_VPBE, + }, +}; + +static struct resource dm644x_venc_resources[] = { + /* venc registers io space */ + { + .start = 0x01C72400, + .end = 0x01C72400 + 0x180, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 dm644x_venc_dma_mask = DMA_BIT_MASK(32); + +#define VPSS_CLKCTL 0x01C40044 +static void __iomem *vpss_clkctl_reg; + +/* TBD. Check what VENC_CLOCK_SEL settings for HDTV and EDTV */ +static int dm644x_venc_setup_clock(enum vpbe_enc_timings_type type, __u64 mode) +{ + int ret = 0; + + if (NULL == vpss_clkctl_reg) + return -EINVAL; + if (type == VPBE_ENC_STD) { + __raw_writel(0x18, vpss_clkctl_reg); + } else if (type == VPBE_ENC_DV_PRESET) { + switch ((unsigned int)mode) { + case V4L2_DV_480P59_94: + case V4L2_DV_576P50: + __raw_writel(0x19, vpss_clkctl_reg); + break; + case V4L2_DV_720P60: + case V4L2_DV_1080I60: + case V4L2_DV_1080P30: + /* + * For HD, use external clock source since HD requires higher + * clock rate + */ + __raw_writel(0xa, vpss_clkctl_reg); + break; + default: + ret = -EINVAL; + break; + } + } else + ret = -EINVAL; + + return ret; +} + + +static inline u32 dm644x_reg_modify(void *reg, u32 val, u32 mask) +{ + u32 new_val = (__raw_readl(reg) & ~mask) | (val & mask); + __raw_writel(new_val, reg); + return new_val; +} + +static u64 vpbe_display_dma_mask = DMA_BIT_MASK(32); + +static struct resource dm644x_v4l2_disp_resources[] = { + { + .start = IRQ_VENCINT, + .end = IRQ_VENCINT, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0x01C72400, + .end = 0x01C72400 + 0x180, + .flags = IORESOURCE_MEM, + }, + +}; +static struct platform_device vpbe_v4l2_display = { + .name = "vpbe-v4l2", + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_v4l2_disp_resources), + .resource = dm644x_v4l2_disp_resources, + .dev = { + .dma_mask = &vpbe_display_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; +struct venc_platform_data dm644x_venc_pdata = { + .venc_type = DM644X_VPBE, + .setup_clock = dm644x_venc_setup_clock, +}; + +static struct platform_device dm644x_venc_dev = { + .name = VPBE_VENC_SUBDEV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_venc_resources), + .resource = dm644x_venc_resources, + .dev = { + .dma_mask = &dm644x_venc_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = (void *)&dm644x_venc_pdata, + }, +}; + +static u64 dm644x_vpbe_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device dm644x_vpbe_dev = { + .name = "vpbe_controller", + .id = -1, + .dev = { + .dma_mask = &dm644x_vpbe_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +void dm644x_set_vpbe_display_config(struct vpbe_display_config *cfg) +{ + dm644x_vpbe_dev.dev.platform_data = cfg; +} + /*----------------------------------------------------------------------*/ static struct map_desc dm644x_io_desc[] = { @@ -767,20 +903,36 @@ void __init dm644x_init(void) davinci_common_init(&davinci_soc_info_dm644x); } +static struct platform_device *dm644x_video_devices[] __initdata = { + &dm644x_vpss_device, + &dm644x_ccdc_dev, + &vpfe_capture_dev, + &dm644x_osd_dev, + &dm644x_venc_dev, + &dm644x_vpbe_dev, + &vpbe_v4l2_display, +}; + +static int __init dm644x_init_video(void) +{ + /* Add ccdc clock aliases */ + clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); + clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); + vpss_clkctl_reg = ioremap_nocache(VPSS_CLKCTL, 4); + platform_add_devices(dm644x_video_devices, + ARRAY_SIZE(dm644x_video_devices)); + return 0; +} + static int __init dm644x_init_devices(void) { if (!cpu_is_davinci_dm644x()) return 0; /* Add ccdc clock aliases */ - clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); - clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); platform_device_register(&dm644x_edma_device); platform_device_register(&dm644x_emac_device); - platform_device_register(&dm644x_vpss_device); - platform_device_register(&dm644x_ccdc_dev); - platform_device_register(&vpfe_capture_dev); - + dm644x_init_video(); return 0; } postcore_initcall(dm644x_init_devices); diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h index 6fca568..bf7adcd 100644 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ b/arch/arm/mach-davinci/include/mach/dm644x.h @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #define DM644X_EMAC_BASE (0x01C80000) #define DM644X_EMAC_CNTRL_OFFSET (0x0000) @@ -43,5 +46,6 @@ void __init dm644x_init(void); void __init dm644x_init_asp(struct snd_platform_data *pdata); void dm644x_set_vpfe_config(struct vpfe_config *cfg); +void dm644x_set_vpbe_display_config(struct vpbe_display_config *cfg); #endif /* __ASM_ARCH_DM644X_H */ -- 1.6.2.4 From manjunath.hadli at ti.com Sat Dec 11 03:20:26 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Sat, 11 Dec 2010 14:50:26 +0530 Subject: [PATCH v5 6/6] davinci vpbe: Build infrastructure for VPBE driver Message-ID: <1292059226-1563-1-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 | 22 ++++++++++++++++++++++ drivers/media/video/davinci/Makefile | 2 ++ 2 files changed, 24 insertions(+), 0 deletions(-) diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig index 6b19540..dab32d5 100644 --- a/drivers/media/video/davinci/Kconfig +++ b/drivers/media/video/davinci/Kconfig @@ -91,3 +91,25 @@ 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" + 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" + select VIDEO_DM644X_VPBE + default y + help + Enables VPBE V4L2 Display driver on a DMXXX 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 broonie at opensource.wolfsonmicro.com Sat Dec 11 06:42:42 2010 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Sat, 11 Dec 2010 12:42:42 +0000 Subject: [PATCH v7 00/12] tnetv107x ssp drivers In-Reply-To: <87r5dp6tzv.fsf@deeprootsystems.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> <87r5dp6tzv.fsf@deeprootsystems.com> Message-ID: <20101211124242.GE9380@opensource.wolfsonmicro.com> On Fri, Dec 10, 2010 at 04:19:48PM -0800, Kevin Hilman wrote: > [PATCH v7 08/12] gpio: add ti-ssp gpio driver > David Brownell? (MAINTAINERS isn't clear here) It's mostly abandoned at the minute. Andrew Morton has been taking patches. Grant Likely said he might take it over so sending stuff his way might prod him into action. From manjunath.hadli at ti.com Sat Dec 11 08:19:41 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Sat, 11 Dec 2010 19:49:41 +0530 Subject: [PATCH v4 0/6] davinci vpbe: dm6446 v4l2 driver In-Reply-To: <87wrnhblb5.fsf@deeprootsystems.com> Message-ID: Sure Kevin. Will send you a patch. -Manju On Fri, Dec 10, 2010 at 22:46:14, Kevin Hilman wrote: > "Hans Verkuil" writes: > > >> version4 : addressed Hans's comments > >> on: > >> 1. replaced mutex_lock_interruptible() with mutex_lock() 2. replaced > >> ntsc and pal macros with new equivalent macros 3. simplifying the > >> code in the if-else condition 4. minor code corrections > > > > For the whole patch series: > > > > Acked-by: Hans Verkuil > > > > Hans, can you take patches 1-4 and 6 through the linux-media tree? > I will queue the patch 5 (the only mach-davinci change) in davinci git for 2.6.38. > > Manjunath, can rebase patch 5 on top of current davinci-next (or davinci > master) as this patch doesn't currently apply there. > > Thanks, > > Kevin > > > > >> > >> 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: platform specific additions > >> davinci vpbe: Build infrastructure for VPBE driver > >> > >> arch/arm/mach-davinci/board-dm644x-evm.c | 79 +- > >> arch/arm/mach-davinci/dm644x.c | 164 ++- > >> arch/arm/mach-davinci/include/mach/dm644x.h | 4 + > >> drivers/media/video/davinci/Kconfig | 22 + > >> drivers/media/video/davinci/Makefile | 2 + > >> .../media/video/davinci/davinci_vpbe_readme.txt | 100 + > >> drivers/media/video/davinci/vpbe.c | 837 ++++++++ > >> drivers/media/video/davinci/vpbe_display.c | 2099 > >> ++++++++++++++++++++ > >> drivers/media/video/davinci/vpbe_osd.c | 1211 +++++++++++ > >> drivers/media/video/davinci/vpbe_osd_regs.h | 389 ++++ > >> drivers/media/video/davinci/vpbe_venc.c | 574 ++++++ > >> drivers/media/video/davinci/vpbe_venc_regs.h | 189 ++ > >> include/media/davinci/vpbe.h | 186 ++ > >> include/media/davinci/vpbe_display.h | 146 ++ > >> include/media/davinci/vpbe_osd.h | 397 ++++ > >> include/media/davinci/vpbe_types.h | 93 + > >> include/media/davinci/vpbe_venc.h | 38 + > >> 17 files changed, 6511 insertions(+), 19 deletions(-) create mode > >> 100644 drivers/media/video/davinci/davinci_vpbe_readme.txt > >> 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 sshtylyov at mvista.com Sat Dec 11 08:20:50 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Sat, 11 Dec 2010 17:20:50 +0300 Subject: [PATCH v5 6/6] davinci vpbe: Build infrastructure for VPBE driver In-Reply-To: <1292059226-1563-1-git-send-email-manjunath.hadli@ti.com> References: <1292059226-1563-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <4D0388C2.3090804@mvista.com> Hello. On 11-12-2010 12:20, Manjunath Hadli wrote: > 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 [...] > diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig > index 6b19540..dab32d5 100644 > --- a/drivers/media/video/davinci/Kconfig > +++ b/drivers/media/video/davinci/Kconfig > @@ -91,3 +91,25 @@ 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" > + select VIDEO_VPSS_SYSTEM > + select VIDEOBUF_DMA_CONTIG I remember I've asked you to align using a tab everywhere. WBR, Sergei From michael.williamson at criticallink.com Sat Dec 11 09:34:03 2010 From: michael.williamson at criticallink.com (Michael Williamson) Date: Sat, 11 Dec 2010 10:34:03 -0500 Subject: [PATCH 00/49] spi: davinci: re-write existing driver In-Reply-To: References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <20101117152426.GA5757@angua.secretlab.ca> Message-ID: <4D0399EB.6070204@criticallink.com> On 11/18/2010 08:14 AM, Nori, Sekhar wrote: > Hi Grant, > > On Thu, Nov 18, 2010 at 17:01:24, Nori, Sekhar wrote: >> On Thu, Nov 18, 2010 at 12:23:24, Nori, Sekhar wrote: >>> >>> Here is the pull request: >>> >>> The following changes since commit 0a5b871ea4c6bfb2723ac2ffc7ef5c32452abb89: >>> Linus Torvalds (1): >>> hardirq.h: remove now-empty #ifdef/#endif pair >>> >>> are available in the git repository at: >>> >>> git://arago-project.org/git/projects/linux-davinci.git for-grant >>> >>> >> >> Oops, I just realized that because my sign-off is added when >> git-format-patch -s is run, the patches in the pull request >> do not have my sign-off. I will recreate the branch with my >> sign-off added to these patches. > > I fixed this now. You can go ahead and do the pull. > > Thanks, > Sekhar > I was wondering is this has been / will be queued or if there were issues holding it off? If it has, what version? I have some patches that are dependent on this series that I have some time coming up to work on submission. Apologies for the nag, Grant, if it's in process. Thanks. -Mike From hverkuil at xs4all.nl Sat Dec 11 09:54:12 2010 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Sat, 11 Dec 2010 16:54:12 +0100 Subject: [PATCH v4 0/6] davinci vpbe: dm6446 v4l2 driver In-Reply-To: <87wrnhblb5.fsf@deeprootsystems.com> References: <1291891543-27156-1-git-send-email-manjunath.hadli@ti.com> <87wrnhblb5.fsf@deeprootsystems.com> Message-ID: <201012111654.12789.hverkuil@xs4all.nl> On Friday, December 10, 2010 18:16:14 Kevin Hilman wrote: > "Hans Verkuil" writes: > > >> version4 : addressed Hans's comments > >> on: > >> 1. replaced mutex_lock_interruptible() with mutex_lock() > >> 2. replaced ntsc and pal macros with new equivalent macros > >> 3. simplifying the code in the if-else condition > >> 4. minor code corrections > > > > For the whole patch series: > > > > Acked-by: Hans Verkuil > > > > Hans, can you take patches 1-4 and 6 through the linux-media tree? > I will queue the patch 5 (the only mach-davinci change) in davinci git > for 2.6.38. Mauro is the linux-media maintainer, so Mauro: it's all yours :-) Regards, Hans > Manjunath, can rebase patch 5 on top of current davinci-next (or davinci > master) as this patch doesn't currently apply there. > > Thanks, > > Kevin > > > > >> > >> 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: platform specific additions > >> davinci vpbe: Build infrastructure for VPBE driver > >> > >> arch/arm/mach-davinci/board-dm644x-evm.c | 79 +- > >> arch/arm/mach-davinci/dm644x.c | 164 ++- > >> arch/arm/mach-davinci/include/mach/dm644x.h | 4 + > >> drivers/media/video/davinci/Kconfig | 22 + > >> drivers/media/video/davinci/Makefile | 2 + > >> .../media/video/davinci/davinci_vpbe_readme.txt | 100 + > >> drivers/media/video/davinci/vpbe.c | 837 ++++++++ > >> drivers/media/video/davinci/vpbe_display.c | 2099 > >> ++++++++++++++++++++ > >> drivers/media/video/davinci/vpbe_osd.c | 1211 +++++++++++ > >> drivers/media/video/davinci/vpbe_osd_regs.h | 389 ++++ > >> drivers/media/video/davinci/vpbe_venc.c | 574 ++++++ > >> drivers/media/video/davinci/vpbe_venc_regs.h | 189 ++ > >> include/media/davinci/vpbe.h | 186 ++ > >> include/media/davinci/vpbe_display.h | 146 ++ > >> include/media/davinci/vpbe_osd.h | 397 ++++ > >> include/media/davinci/vpbe_types.h | 93 + > >> include/media/davinci/vpbe_venc.h | 38 + > >> 17 files changed, 6511 insertions(+), 19 deletions(-) > >> create mode 100644 drivers/media/video/davinci/davinci_vpbe_readme.txt > >> 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 > >> > >> > -- Hans Verkuil - video4linux developer - sponsored by Cisco From grant.likely at secretlab.ca Sat Dec 11 14:54:45 2010 From: grant.likely at secretlab.ca (Grant Likely) Date: Sat, 11 Dec 2010 13:54:45 -0700 Subject: [PATCH 00/49] spi: davinci: re-write existing driver In-Reply-To: <4D0399EB.6070204@criticallink.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <20101117152426.GA5757@angua.secretlab.ca> <4D0399EB.6070204@criticallink.com> Message-ID: On Sat, Dec 11, 2010 at 8:34 AM, Michael Williamson wrote: > On 11/18/2010 08:14 AM, Nori, Sekhar wrote: >> Hi Grant, >> >> On Thu, Nov 18, 2010 at 17:01:24, Nori, Sekhar wrote: >>> On Thu, Nov 18, 2010 at 12:23:24, Nori, Sekhar wrote: >>>> >>>> Here is the pull request: >>>> >>>> The following changes since commit 0a5b871ea4c6bfb2723ac2ffc7ef5c32452abb89: >>>> ? Linus Torvalds (1): >>>> ? ? ? ? hardirq.h: remove now-empty #ifdef/#endif pair >>>> >>>> are available in the git repository at: >>>> >>>> ? git://arago-project.org/git/projects/linux-davinci.git for-grant >>>> >>>> >>> >>> Oops, I just realized that because my sign-off is added when >>> git-format-patch -s is run, the patches in the pull request >>> do not have my sign-off. I will recreate the branch with my >>> sign-off added to these patches. >> >> I fixed this now. You can go ahead and do the pull. >> >> Thanks, >> Sekhar >> > > I was wondering is this has been / will be queued or if there were issues holding > it off? ?If it has, what version? > > I have some patches that are dependent on this series that I have some time coming > up to work on submission. ?Apologies for the nag, Grant, if it's in process. Yes, stuff is in progress. I hit a personal scalability issue which has delayed getting a lot of my maintainership backlog sorted out. Mostly due to the fact that it takes a lot of work to test my tree before pushing it out. I'm in the process of bringing up autotest so that I have to manually test every platform before pushing my tree into linux-next. Patches should be sorted out next week since I've dedicated the week to doing maintainership tasks. g. From m-karicheri2 at ti.com Mon Dec 13 08:49:35 2010 From: m-karicheri2 at ti.com (Karicheri, Muralidharan) Date: Mon, 13 Dec 2010 08:49:35 -0600 Subject: [PATCH v5 0/6] davinci vpbe: dm6446 v4l2 driver In-Reply-To: <1292059074-668-1-git-send-email-manjunath.hadli@ti.com> References: <1292059074-668-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Manju, >2. Fixed Murali's comments on moving davinci_vpbe_readme.txt to different >patch different patch or path? My comment was to move the documentation to Documentation folder. But it is still in it's original path :( > >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: platform specific additions > davinci vpbe: Build infrastructure for VPBE driver > > arch/arm/mach-davinci/board-dm644x-evm.c | 79 +- > arch/arm/mach-davinci/dm644x.c | 164 ++- > arch/arm/mach-davinci/include/mach/dm644x.h | 4 + > drivers/media/video/davinci/Kconfig | 22 + > drivers/media/video/davinci/Makefile | 2 + > .../media/video/davinci/davinci_vpbe_readme.txt | 100 + > drivers/media/video/davinci/vpbe.c | 837 ++++++++ > drivers/media/video/davinci/vpbe_display.c | 2099 >++++++++++++++++++++ > drivers/media/video/davinci/vpbe_osd.c | 1211 +++++++++++ > drivers/media/video/davinci/vpbe_osd_regs.h | 389 ++++ > drivers/media/video/davinci/vpbe_venc.c | 574 ++++++ > drivers/media/video/davinci/vpbe_venc_regs.h | 189 ++ > include/media/davinci/vpbe.h | 186 ++ > include/media/davinci/vpbe_display.h | 146 ++ > include/media/davinci/vpbe_osd.h | 397 ++++ > include/media/davinci/vpbe_types.h | 93 + > include/media/davinci/vpbe_venc.h | 38 + > 17 files changed, 6511 insertions(+), 19 deletions(-) > create mode 100644 drivers/media/video/davinci/davinci_vpbe_readme.txt > 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 > >_______________________________________________ >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 bengardiner at nanometrics.ca Mon Dec 13 11:02:32 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Mon, 13 Dec 2010 12:02:32 -0500 Subject: [PATCH v6 0/5] da850-evm: add gpio-{keys, leds} for UI and BB expanders In-Reply-To: References: <87tyild2ms.fsf@deeprootsystems.com> Message-ID: Hi Kevin, On Fri, Dec 10, 2010 at 11:33 AM, Ben Gardiner wrote: > On Fri, Dec 10, 2010 at 11:16 AM, Kevin Hilman >> [...] >> This series looks good to me, so I'll be queuing it in davinci-next for >> 2.6.38. ?It should show up in davinci git shortly. [...] > > Thank you very much, Kevin. > > I will check linux-davinci/master on monday. I looked at git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git#davinci-next ; HEAD at the time was commit 3004ce0d3a44525de63e18b01f7734bc8d64f2c5 Author: Ben Gardiner Date: Thu Dec 9 16:51:07 2010 -0500 da850-evm: KEYBOARD_GPIO_POLLED Kconfig conditional Use the mach-davinci/Kconfig to enable gpio-keys-polled as default when da850-evm machine is enabled. Signed-off-by: Ben Gardiner CC: Kevin Hilman CC: "Nori, Sekhar" CC: Gabor Juhos Signed-off-by: Kevin Hilman Everything seems to be in order there; I tested the resulting kernel with evtest and the expected output was observed. Note that davinci-next still contains the cherry-pick of the upstream commit of the polled gpio keys driver: commit 03b79201321d53acc56b43cdd9d43e869f62021c Author: Gabor Juhos Date: Thu Dec 9 16:51:03 2010 -0500 Input: add input driver for polled GPIO buttons The existing gpio-keys driver can be usable only for GPIO lines with interrupt support. Several devices have buttons connected to a GPIO line which is not capable to generate interrupts. This patch adds a new input driver using the generic GPIO layer and the input-polldev to support such buttons. [Ben Gardiner Signed-off-by: Ben Gardiner Tested-by: Ben Gardiner Signed-off-by: Dmitry Torokhov (cherry picked from commit 0e7d0c860a0dee49dacb7bbb248d1eba637075ad) Signed-off-by: Kevin Hilman I also looked at git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git#master ; HEAD at the time was commit 4bfbdddc0655a284a98304bc343c6bcaf81b8e4d Merge: 771215c 7507fd3 Author: Kevin Hilman Date: Fri Dec 10 16:25:27 2010 -0800 rebuild linux-davinci from branches There appears to be some double commits of the patch series. I tested it with evtest anyways and also observed the expected output. The following command and its output hopefully demonstrate what I am seeing as double commits. $git log --format="%s -- %Cgreen%an <%aE> %Cred%ai %Creset%h" khilman/master rebuild linux-davinci from branches -- Kevin Hilman 2010-12-10 16:25:27 -0800 4bfbddd Merge branch 'davinci-orphaned' into davinci-reset -- Kevin Hilman 2010-12-10 16:25:22 -0800 771215c da850-evm: KEYBOARD_GPIO_POLLED Kconfig conditional -- Ben Gardiner 2010-12-09 16:51:07 -0500 3004ce0 da850-evm: add baseboard GPIO expander buttons, switches and LEDs -- Ben Gardiner 2010-12-09 16:51:06 -0500 61eaa3a da850-evm: extract defines for SEL{A,B,C} pins in UI expander -- Ben Gardiner 2010-12-09 16:51:05 -0500 e594a12 da850-evm: add UI Expander pushbuttons -- Ben Gardiner 2010-12-09 16:51:04 -0500 260660f davinci: am18x/da850/omap-l138 evm: add support for higher speed grades -- Sekhar Nori 2010-12-09 14:11:34 +0530 3c812e7 davinci: am18x/da850/omap-l138: add support for higher speed grades -- Sekhar Nori 2010-12-09 14:11:33 +0530 d2e9976 rebuild linux-davinci from branches -- Kevin Hilman 2010-12-10 08:13:18 -0800 7507fd3 Merge branch 'davinci-orphaned' into davinci-reset -- Kevin Hilman 2010-12-10 08:13:13 -0800 66a897a da850-evm: KEYBOARD_GPIO_POLLED Kconfig conditional -- Ben Gardiner 2010-12-09 16:51:07 -0500 bc1c63f da850-evm: add baseboard GPIO expander buttons, switches and LEDs -- Ben Gardiner 2010-12-09 16:51:06 -0500 c6c519c da850-evm: extract defines for SEL{A,B,C} pins in UI expander -- Ben Gardiner 2010-12-09 16:51:05 -0500 22693bc da850-evm: add UI Expander pushbuttons -- Ben Gardiner 2010-12-09 16:51:04 -0500 b481719 Input: add input driver for polled GPIO buttons -- Gabor Juhos 2010-12-09 16:51:03 -0500 03b7920 ARM: Add writethrough dcache support for ARM926EJS processor -- Mark A. Greer 2009-05-11 15:36:54 -0700 ecc2acc fb: davinci: DMx driver -- Kevin Hilman 2009-10-12 14:05:30 -0700 577b52a ASoC: davinci: SFFSDR board updates -- Kevin Hilman 2009-10-12 14:03:47 -0700 cdbc657 davinci: dm365evm_keys driver -- David Brownell 2009-06-25 17:03:21 -0700 cf4e892 davinci: kconfig: select at24 eeprom for selected boards -- Kevin Hilman 2010-11-19 07:25:30 -0800 22ca466 da850-evm, trivial: use da850_evm prefix for consistency -- Ben Gardiner 2010-11-19 16:43:04 -0500 3506f27 da850-evm: allow pca953x module build -- Ben Gardiner 2010-11-19 09:17:35 -0500 d5539ca davinci: da850-evm: UI expander gpio_set_value can sleep, use _cansleep -- Ben Gardiner 2010-11-15 09:42:52 -0500 47e7cb1 davinci: aemif: signedness bug in davinci_aemif_setup_timing() -- Nicolas Kaiser 2010-11-15 19:40:28 +0100 12cdd3d davinci: psc: simplify if-statement -- Nicolas Kaiser 2010-10-25 14:41:18 +0200 1a07bfb davinci: minor tnetv107x clock tree fixes -- Cyril Chemparathy 2010-10-20 17:49:57 -0400 ced9862 davinci: use divide ratio limits from pll_data -- Cyril Chemparathy 2010-10-20 17:49:56 -0400 b1d05be davinci: Implement sched_clock() -- Andreas Gaeer 2010-10-06 10:38:55 +0200 6d1c57c Linux 2.6.37-rc5 -- Linus Torvalds 2010-12-06 20:09:04 -0800 cf7d7e5 [...] Note that the cherry pick of the upstream commit of the polled gpio keys driver is here in 'master' also. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From khilman at deeprootsystems.com Mon Dec 13 15:53:44 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Mon, 13 Dec 2010 13:53:44 -0800 Subject: [PATCH v6 0/5] da850-evm: add gpio-{keys, leds} for UI and BB expanders In-Reply-To: (Ben Gardiner's message of "Mon, 13 Dec 2010 12:02:32 -0500") References: <87tyild2ms.fsf@deeprootsystems.com> Message-ID: <8762ux49w7.fsf@deeprootsystems.com> Ben Gardiner writes: > Hi Kevin, > > On Fri, Dec 10, 2010 at 11:33 AM, Ben Gardiner > wrote: >> On Fri, Dec 10, 2010 at 11:16 AM, Kevin Hilman >>> [...] >>> This series looks good to me, so I'll be queuing it in davinci-next for >>> 2.6.38. ?It should show up in davinci git shortly. [...] >> >> Thank you very much, Kevin. >> >> I will check linux-davinci/master on monday. > > I looked at git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git#davinci-next > ; HEAD at the time was > > commit 3004ce0d3a44525de63e18b01f7734bc8d64f2c5 > Author: Ben Gardiner > Date: Thu Dec 9 16:51:07 2010 -0500 > > da850-evm: KEYBOARD_GPIO_POLLED Kconfig conditional > > Use the mach-davinci/Kconfig to enable gpio-keys-polled as default when > da850-evm machine is enabled. > > Signed-off-by: Ben Gardiner > CC: Kevin Hilman > CC: "Nori, Sekhar" > CC: Gabor Juhos > Signed-off-by: Kevin Hilman > > Everything seems to be in order there; I tested the resulting kernel > with evtest and the expected output was observed. Note that > davinci-next still contains the cherry-pick of the upstream commit of > the polled gpio keys driver: oops... I've now removed that, since it is part of v2.6.36-rc5 already. Thanks for checking. [...] > I also looked at > git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git#master > ; HEAD at the time was > > commit 4bfbdddc0655a284a98304bc343c6bcaf81b8e4d > Merge: 771215c 7507fd3 > Author: Kevin Hilman > Date: Fri Dec 10 16:25:27 2010 -0800 > > rebuild linux-davinci from branches > > There appears to be some double commits of the patch series. I tested > it with evtest anyways and also observed the expected output. > > The following command and its output hopefully demonstrate what I am > seeing as double commits. Yes, there will be double commits in master, because of the way I manage master using 'git merge -ours'. But there shouldn't be double commits between my "rebuild from braches" merges. It can be confusing, but if you look at the history with a graphical tool like 'gitk', it might shed some light on what is going on. I know it's confusing, but the davinci-next branch is the only important branch in this tree for upstream purposes. [...] > Note that the cherry pick of the upstream commit of the polled gpio > keys driver is here in 'master' also. Yeah, that came from davinc-next. Now that it's removed from there, it should be ok. I just pushed an updated davinci-next and master branch. It sometimes takes a bit to propagate to all the kernel.org mirrors, but the update should be there shortly. Thanks, Kevin From bengardiner at nanometrics.ca Mon Dec 13 22:31:09 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Mon, 13 Dec 2010 23:31:09 -0500 Subject: [PATCH v6 0/5] da850-evm: add gpio-{keys, leds} for UI and BB expanders In-Reply-To: <8762ux49w7.fsf@deeprootsystems.com> References: <87tyild2ms.fsf@deeprootsystems.com> <8762ux49w7.fsf@deeprootsystems.com> Message-ID: On Mon, Dec 13, 2010 at 4:53 PM, Kevin Hilman wrote: > Ben Gardiner writes: > [...] > Yes, there will be double commits in master, because of the way I manage > master using 'git merge -ours'. ?But there shouldn't be double commits > between my "rebuild from braches" merges. ?It can be confusing, but if > you look at the history with a graphical tool like 'gitk', it might shed > some light on what is going on. Ok. I'll consider a graphical investigation. Thanks for confirming the state of master is as-designed. > I know it's confusing, but the davinci-next branch is the only important > branch in this tree for upstream purposes. Understood. Thanks for the clarification. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From vm.rod25 at gmail.com Tue Dec 14 09:39:55 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Tue, 14 Dec 2010 09:39:55 -0600 Subject: [PATCH v10 4/6] davinci: MMC/SD support for Omapl138-Hawkboard In-Reply-To: References: <1291760089-5818-1-git-send-email-vm.rod25@gmail.com> <1291760089-5818-5-git-send-email-vm.rod25@gmail.com> Message-ID: On Fri, Dec 10, 2010 at 3:02 PM, Victor Rodriguez wrote: > On Wed, Dec 8, 2010 at 9:55 AM, Victor Rodriguez wrote: >> On Tue, Dec 7, 2010 at 11:20 PM, Nori, Sekhar wrote: >>> On Wed, Dec 08, 2010 at 03:44:47, vm.rod25 at gmail.com wrote: >>> >>>> +static __init void omapl138_hawk_mmc_init(void) >>>> +{ >>>> + ? ? int ret; >>>> + >>>> + ? ? ret = davinci_cfg_reg_list(hawk_mmcsd0_pins); >>>> + ? ? if (ret) { >>>> + ? ? ? ? ? ? pr_warning("%s: MMC/SD0 mux setup failed: %d\n", >>>> + ? ? ? ? ? ? ? ? ? ? __func__, ret); >>>> + ? ? ? ? ? ? return; >>>> + ? ? } >>>> + >>>> + ? ? ret = gpio_request_one(DA850_HAWK_MMCSD_CD_PIN, >>>> + ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "MMC CD"); >>>> + ? ? if (ret < 0) { >>>> + ? ? ? ? ? ? pr_warning("%s: can not open GPIO %d\n", >>>> + ? ? ? ? ? ? ? ? ? ? __func__, DA850_HAWK_MMCSD_CD_PIN); >>>> + ? ? ? ? ? ? goto mmc_setup_cd_fail; >>>> + ? ? } >>> >>> A goto is only used when there is some recovery >>> to be done. In this case, you should simply return >>> here. The USB patch needs the same correction. >>> >>> Thanks, >>> Sekhar >> >> Sorry for this my mistake >> >> It wil change to this >> >> +static __init void omapl138_hawk_mmc_init(void) >> +{ >> + ? ? ? int ret; >> + >> + ? ? ? ret = davinci_cfg_reg_list(hawk_mmcsd0_pins); >> + ? ? ? if (ret) { >> + ? ? ? ? ? ? ? pr_warning("%s: MMC/SD0 mux setup failed: %d\n", >> + ? ? ? ? ? ? ? ? ? ? ? __func__, ret); >> + ? ? ? ? ? ? ? return; >> + ? ? ? } >> + >> + ? ? ? ret = gpio_request_one(DA850_HAWK_MMCSD_CD_PIN, >> + ? ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "MMC CD"); >> + ? ? ? if (ret < 0) { >> + ? ? ? ? ? ? ? pr_warning("%s: can not open GPIO %d\n", >> + ? ? ? ? ? ? ? ? ? ? ? __func__, DA850_HAWK_MMCSD_CD_PIN); >> + ? ? ? ? ? ? ? return; >> + ? ? ? } >> + >> + ? ? ? ret = gpio_request_one(DA850_HAWK_MMCSD_WP_PIN, >> + ? ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "MMC WP"); >> + ? ? ? if (ret < 0) { >> + ? ? ? ? ? ? ? pr_warning("%s: can not open GPIO %d\n", >> + ? ? ? ? ? ? ? ? ? ? ? __func__, DA850_HAWK_MMCSD_WP_PIN); >> + ? ? ? ? ? ? ? goto mmc_setup_wp_fail; >> + ? ? ? } >> + >> + ? ? ? ret = da8xx_register_mmcsd0(&da850_mmc_config); >> + ? ? ? if (ret) { >> + ? ? ? ? ? ? ? pr_warning("%s: MMC/SD0 registration failed: %d\n", >> + ? ? ? ? ? ? ? ? ? ? ? __func__, ret); >> + ? ? ? ? ? ? ? goto mmc_setup_mmcsd_fail; >> + ? ? ? } >> + >> + ? ? ? return; >> + >> +mmc_setup_mmcsd_fail: >> + ? ? ? gpio_free(DA850_HAWK_MMCSD_WP_PIN); >> +mmc_setup_wp_fail: >> + ? ? ? gpio_free(DA850_HAWK_MMCSD_CD_PIN); >> +} >> + >> >> And USB will be this >> >> +static __init void omapl138_hawk_usb_init(void) >> +{ >> + ? ? ? int ret; >> + ? ? ? u32 cfgchip2; >> + >> + ? ? ? ret = davinci_cfg_reg_list(da850_hawk_usb11_pins); >> + ? ? ? if (ret) { >> + ? ? ? ? ? ? ? pr_warning("%s: USB 1.1 PinMux setup failed: %d\n", >> + ? ? ? ? ? ? ? ? ? ? ? __func__, ret); >> + ? ? ? ? ? ? ? return; >> + ? ? ? } >> + >> + ? ? ? /* Setup the Ref. clock frequency for the HAWK at 24 MHz. */ >> + >> + ? ? ? cfgchip2 = __raw_readl(DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); >> + ? ? ? cfgchip2 &= ~CFGCHIP2_REFFREQ; >> + ? ? ? cfgchip2 |= ?CFGCHIP2_REFFREQ_24MHZ; >> + ? ? ? __raw_writel(cfgchip2, DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG)); >> + >> + ? ? ? ret = gpio_request_one(DA850_USB1_VBUS_PIN, >> + ? ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_OUT, "USB1 VBUS"); >> + ? ? ? if (ret < 0) { >> + ? ? ? ? ? ? ? pr_err("%s: failed to request GPIO for USB 1.1 port " >> + ? ? ? ? ? ? ? ? ? ? ? "power control: %d\n", __func__, ret); >> + ? ? ? ? ? ? ? return; >> + ? ? ? } >> + >> + ? ? ? ret = gpio_request_one(DA850_USB1_OC_PIN, >> + ? ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "USB1 OC"); >> + ? ? ? if (ret < 0) { >> + ? ? ? ? ? ? ? pr_err("%s: failed to request GPIO for USB 1.1 port " >> + ? ? ? ? ? ? ? ? ? ? ? "over-current indicator: %d\n", __func__, ret); >> + ? ? ? ? ? ? ? goto usb11_setup_oc_fail; >> + ? ? ? } >> + >> + ? ? ? ret = da8xx_register_usb11(&omapl138_hawk_usb11_pdata); >> + ? ? ? if (ret) { >> + ? ? ? ? ? ? ? pr_warning("%s: USB 1.1 registration failed: %d\n", >> + ? ? ? ? ? ? ? ? ? ? ? __func__, ret); >> + ? ? ? ? ? ? ? goto usb11_setup_fail; >> + ? ? ? } >> + >> + ? ? ? return; >> + >> +usb11_setup_fail: >> + ? ? ? gpio_free(DA850_USB1_OC_PIN); >> +usb11_setup_oc_fail: >> + ? ? ? gpio_free(DA850_USB1_VBUS_PIN); >> +} >> + >> >> Thanks a lot for the comments >> >> Regards >> >> Victor Rodriguez > > > Any update of this is Ok to resend the series with this small change? > > > Regards > > Victor Rodriguez Hi Sekhar and Kevin I will resend the patches again with this changes is ok? Thanks and regards Victor Rodriguez > >> >>>> + >>>> + ? ? ret = gpio_request_one(DA850_HAWK_MMCSD_WP_PIN, >>>> + ? ? ? ? ? ? ? ? ? ? GPIOF_DIR_IN, "MMC WP"); >>>> + ? ? if (ret < 0) { >>>> + ? ? ? ? ? ? pr_warning("%s: can not open GPIO %d\n", >>>> + ? ? ? ? ? ? ? ? ? ? __func__, DA850_HAWK_MMCSD_WP_PIN); >>>> + ? ? ? ? ? ? goto mmc_setup_wp_fail; >>>> + ? ? } >>>> + >>>> + ? ? ret = da8xx_register_mmcsd0(&da850_mmc_config); >>>> + ? ? if (ret) { >>>> + ? ? ? ? ? ? pr_warning("%s: MMC/SD0 registration failed: %d\n", >>>> + ? ? ? ? ? ? ? ? ? ? __func__, ret); >>>> + ? ? ? ? ? ? goto mmc_setup_mmcsd_fail; >>>> + ? ? } >>>> + >>>> +mmc_setup_cd_fail: >>>> + ? ? return; >>>> + >>>> +mmc_setup_mmcsd_fail: >>>> + ? ? gpio_free(DA850_HAWK_MMCSD_WP_PIN); >>>> +mmc_setup_wp_fail: >>>> + ? ? gpio_free(DA850_HAWK_MMCSD_CD_PIN); >>>> +} >>>> + >>>> ?static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { >>>> ? ? ? .enabled_uarts = 0x7, >>>> ?}; >>>> @@ -127,6 +198,8 @@ static __init void omapl138_hawk_init(void) >>>> ? ? ? ? ? ? ? pr_warning("%s: EDMA registration failed: %d\n", >>>> ? ? ? ? ? ? ? ? ? ? ? __func__, ret); >>>> >>>> + ? ? omapl138_hawk_mmc_init(); >>>> + >>>> ? ? ? ret = da8xx_register_watchdog(); >>>> ? ? ? if (ret) >>>> ? ? ? ? ? ? ? pr_warning("omapl138_hawk_init: " >>>> -- >>>> 1.7.0.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 bengardiner at nanometrics.ca Tue Dec 14 10:17:14 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 14 Dec 2010 11:17:14 -0500 Subject: [PATCH v6 0/5] da850-evm: add gpio-{keys, leds} for UI and BB expanders In-Reply-To: <8762ux49w7.fsf@deeprootsystems.com> References: <87tyild2ms.fsf@deeprootsystems.com> <8762ux49w7.fsf@deeprootsystems.com> Message-ID: On Mon, Dec 13, 2010 at 4:53 PM, Kevin Hilman wrote: > Ben Gardiner writes: >> [...] >> Everything seems to be in order there; I tested the resulting kernel >> with evtest and the expected output was observed. Note that >> davinci-next still contains the cherry-pick of the upstream commit of >> the polled gpio keys driver: > > oops... I've now removed that, since it is part of v2.6.36-rc5 already. > Thanks for checking. Oops on my part... I looked at git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git#davinci-next again ; HEAD at the time was: commit 5682cfebad5e68736f4c54a268b5b371698f5262 Author: Ben Gardiner Date: Thu Dec 9 16:51:07 2010 -0500 da850-evm: KEYBOARD_GPIO_POLLED Kconfig conditional Use the mach-davinci/Kconfig to enable gpio-keys-polled as default when da850-evm machine is enabled. Signed-off-by: Ben Gardiner CC: Kevin Hilman CC: "Nori, Sekhar" CC: Gabor Juhos Signed-off-by: Kevin Hilman and I was unable to build a kernel from da8xx_omapl_defconfig b/c: arch/arm/mach-davinci/board-da850-evm.c:333: error: unknown field 'poll_interval' specified in initializer It appears that dropping the cherry-pick caused the build failure. The commit that introduces the polled gpio keys driver (which was included in the series as a cherry pick) is commit 0e7d0c860a0dee49dacb7bbb248d1eba637075ad which is in git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git#master _after_ the tag v2.6.37-rc5. That's my fault. I incorrectly thought that commit 0e7d0c860a0dee49dacb7bbb248d1eba637075ad was _in_ 2.6.37-rc5 and stated this is previous emails. I'm sorry for the confusion; I think I jumped the gun there due to my excitement at getting this prerequisite driver committed. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From khilman at deeprootsystems.com Tue Dec 14 11:10:19 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Tue, 14 Dec 2010 09:10:19 -0800 Subject: [PATCH v6 0/5] da850-evm: add gpio-{keys, leds} for UI and BB expanders In-Reply-To: (Ben Gardiner's message of "Tue, 14 Dec 2010 11:17:14 -0500") References: <87tyild2ms.fsf@deeprootsystems.com> <8762ux49w7.fsf@deeprootsystems.com> Message-ID: <87ei9kwa9w.fsf@deeprootsystems.com> Ben Gardiner writes: > On Mon, Dec 13, 2010 at 4:53 PM, Kevin Hilman > wrote: >> Ben Gardiner writes: >>> [...] >>> Everything seems to be in order there; I tested the resulting kernel >>> with evtest and the expected output was observed. Note that >>> davinci-next still contains the cherry-pick of the upstream commit of >>> the polled gpio keys driver: >> >> oops... I've now removed that, since it is part of v2.6.36-rc5 already. >> Thanks for checking. > > Oops on my part... > [...] > It appears that dropping the cherry-pick caused the build failure. > > The commit that introduces the polled gpio keys driver (which was > included in the series as a cherry pick) is commit > 0e7d0c860a0dee49dacb7bbb248d1eba637075ad which is in > git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git#master > _after_ the tag v2.6.37-rc5. > > That's my fault. I incorrectly thought that commit > 0e7d0c860a0dee49dacb7bbb248d1eba637075ad was _in_ 2.6.37-rc5 and > stated this is previous emails. I'm sorry for the confusion; I think I > jumped the gun there due to my excitement at getting this prerequisite > driver committed. OK, while waiting for it to arrive upstream, I've added it to my 'davinci-backports' branch, which is also merged into the master branch of davinci git (but not in davinci-next, since it will go upstream via another subsystem.) Just pushed an updated version, and this time, I actually build tested for davinci_all_defconfig and da8xx_omapl_defconfig. :) Sorry for the churn, Kevin From manjunath.hadli at ti.com Wed Dec 15 03:09:22 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 15 Dec 2010 14:39:22 +0530 Subject: [PATCH v6 0/7] davinci vpbe: dm6446 v4l2 driver Message-ID: <1292404162-11734-1-git-send-email-manjunath.hadli@ti.com> version6 : addressed Sergei's and Murali's comments on: 1. Fixed Murali's comments on moving README.davinci-vpbe to Documentation directory. 2. Fixed Sergei's comments on indentation. Manjunath Hadli (7): 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: platform specific additions davinci vpbe: Build infrastructure for VPBE driver davinci vpbe: Readme text for Dm6446 vpbe Documentation/video4linux/README.davinci-vpbe | 100 ++ arch/arm/mach-davinci/board-dm644x-evm.c | 79 +- arch/arm/mach-davinci/dm644x.c | 164 ++- arch/arm/mach-davinci/include/mach/dm644x.h | 4 + drivers/media/video/davinci/Kconfig | 22 + drivers/media/video/davinci/Makefile | 2 + drivers/media/video/davinci/vpbe.c | 837 ++++++++++ drivers/media/video/davinci/vpbe_display.c | 2099 +++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd.c | 1211 ++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++ drivers/media/video/davinci/vpbe_venc.c | 574 +++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++ include/media/davinci/vpbe.h | 186 +++ include/media/davinci/vpbe_display.h | 146 ++ include/media/davinci/vpbe_osd.h | 397 +++++ include/media/davinci/vpbe_types.h | 93 ++ include/media/davinci/vpbe_venc.h | 38 + 17 files changed, 6511 insertions(+), 19 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 Wed Dec 15 03:09:40 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 15 Dec 2010 14:39:40 +0530 Subject: [PATCH v6 1/7] davinci vpbe: V4L2 display driver for DM644X SoC Message-ID: <1292404180-11846-1-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 is 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 | 2099 ++++++++++++++++++++++++++++ include/media/davinci/vpbe_display.h | 146 ++ include/media/davinci/vpbe_types.h | 93 ++ 3 files changed, 2338 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..a29a2a0 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_display.c @@ -0,0 +1,2099 @@ +/* + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#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 +#include "vpbe_venc_regs.h" + + +#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" + +static int debug; +static u32 video2_numbuffers = 3; +static u32 video3_numbuffers = 3; + +#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) +#define VPBE_DEFAULT_NUM_BUFS 3 + +static u32 video2_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; +static u32 video3_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; + +module_param(video2_numbuffers, uint, S_IRUGO); +module_param(video3_numbuffers, uint, S_IRUGO); +module_param(video2_bufsize, uint, S_IRUGO); +module_param(video3_bufsize, uint, S_IRUGO); +module_param(debug, int, 0644); + +static struct buf_config_params display_buf_config_params = { + .min_numbuffers = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[0] = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[1] = VPBE_DEFAULT_NUM_BUFS, + .min_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .min_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, +}; + +static struct vpbe_device *vpbe_dev; +static struct osd_state *osd_device; +static int vpbe_display_nr[] = { 2, 3 }; + +static struct v4l2_capability vpbe_display_videocap = { + .driver = VPBE_DISPLAY_DRIVER, + .bus_info = "platform", + .version = VPBE_DISPLAY_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, +}; + +static u8 layer_first_int[VPBE_DISPLAY_MAX_DEVICES]; + +__iomem void *reg_base_venc; + +static int venc_is_second_field() +{ + u32 val; + val = __raw_readl(reg_base_venc + VENC_VSTAT); + return ((val & VENC_VSTAT_FIDST) + == VENC_VSTAT_FIDST); +} + +/* + * vpbe_display_isr() + * ISR function. It changes status of the displayed buffer, takes next buffer + * from the queue and sets its address in VPBE registers + */ +static void vpbe_display_isr(unsigned int event, void *disp_obj) +{ + unsigned long jiffies_time = get_jiffies_64(); + struct timeval timevalue; + int i, fid; + unsigned long addr = 0; + struct vpbe_display_obj *layer = NULL; + struct vpbe_display *disp_dev = (struct vpbe_display *)disp_obj; + + /* Convert time represention from jiffies to timeval */ + jiffies_to_timeval(jiffies_time, &timevalue); + + 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; + /* Check the field format */ + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && + (event & OSD_END_OF_FRAME)) { + /* Progressive mode */ + if (layer_first_int[i]) + layer_first_int[i] = 0; + continue; + /* + * Mark status of the cur_frm to + * done and unlock semaphore on it + */ + + if (layer->cur_frm != layer->next_frm) { + layer->cur_frm->ts = timevalue; + 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; + } + /* Get the next buffer from buffer queue */ + spin_lock(&disp_dev->dma_queue_lock); + if (!list_empty(&layer->dma_queue)) { + layer->next_frm = + list_entry(layer->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove that buffer from the buffer queue */ + list_del(&layer->next_frm->queue); + /* Mark status of the buffer as 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_dev->cbcr_ofst); + } + spin_unlock(&disp_dev->dma_queue_lock); + } else { + /* + * Interlaced mode + * If it is first interrupt, ignore it + */ + if (layer_first_int[i]) { + layer_first_int[i] = 0; + return; + } + + layer->field_id ^= 1; + if (event & OSD_FIRST_FIELD) + fid = 0; + else if (event & OSD_SECOND_FIELD) + fid = 1; + else + return; + + /* + * If field id does not match with stored + * field id + */ + if (fid != layer->field_id) { + /* Make them in sync */ + if (0 == fid) + layer->field_id = fid; + + return; + } + /* + * device field id and local field id are + * in sync. If this is even field + */ + if (0 == fid) { + if (layer->cur_frm == layer->next_frm) + continue; + /* + * one frame is displayed If next frame is + * available, release cur_frm and move on + * copy frame display time + */ + layer->cur_frm->ts = timevalue; + /* Change status of the cur_frm */ + layer->cur_frm->state = VIDEOBUF_DONE; + /* unlock semaphore on cur_frm */ + wake_up_interruptible(&layer->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + layer->cur_frm = layer->next_frm; + } else if (1 == fid) { /* odd field */ + + if (list_empty(&layer->dma_queue) + || (layer->cur_frm != layer->next_frm)) + continue; + + /* + * one field is displayed configure + * the next frame if it is available + * otherwise hold on current frame + * Get next from the buffer queue + */ + spin_lock(&disp_dev->dma_queue_lock); + 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); + + /* 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_dev->cbcr_ofst); + spin_unlock(&disp_dev->dma_queue_lock); + } + } + } +} + +/* interrupt service routine */ +static irqreturn_t venc_isr(int irq, void *arg) +{ + static unsigned last_event; + unsigned event = 0; + + if (venc_is_second_field()) + 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; + + vpbe_display_isr(event, arg); + 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_display_obj *layer = fh->layer; + unsigned long addr; + int ret = 0; + + 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_display_obj *layer = fh->layer; + int buf_size; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); + + *size = layer->pix_fmt.sizeimage; + buf_size = + display_buf_config_params.layer_bufsize[layer->device_id]; + /* + * For MMAP, limit the memory allocation as per bootarg + * configured buffer size + */ + if (V4L2_MEMORY_MMAP == layer->memory) + if (*size > buf_size) + *size = buf_size; + + /* Store number of buffers allocated in numbuffer member */ + if (*count < display_buf_config_params.min_numbuffers) + *count = layer->numbuffers = + display_buf_config_params.numbuffers[layer->device_id]; + + 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_display_obj *layer = fh->layer; + struct vpbe_display *disp = fh->disp_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_display_obj *layer = fh->layer; + + 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_display_obj* +_vpbe_display_get_other_win(struct vpbe_display *disp_dev, + struct vpbe_display_obj *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_video_display_params(struct vpbe_display *disp_dev, + struct vpbe_display_obj *layer) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + unsigned long addr; + int ret = 0; + + 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_display_obj *otherlayer = + _vpbe_display_get_other_win(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_display_obj *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; + int h_scale = 0, v_scale = 0, h_exp = 0, v_exp = 0, temp; + 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)) { + temp = (cfg->xsize * + VPBE_DISPLAY_H_EXP_RATIO_N) / + VPBE_DISPLAY_H_EXP_RATIO_D; + if (temp <= expected_xsize) { + h_exp = 1; + cfg->xsize = temp; + } + } + } + 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)) { + temp = (cfg->ysize * + VPBE_DISPLAY_V_EXP_RATIO_N) / + VPBE_DISPLAY_V_EXP_RATIO_D; + if (temp <= expected_ysize) { + v_exp = 1; + cfg->ysize = temp; + } + } + } + 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_display_obj *layer, + int top, int left) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + + cfg->xpos = cfg->ypos = 0; + if (left + cfg->xsize <= vpbe_dev->current_timings.xres) + cfg->xpos = left; + if (top + cfg->ysize <= vpbe_dev->current_timings.yres) + cfg->ypos = top; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "new xpos = %d, ypos = %d\n", + cfg->xpos, cfg->ypos); +} + +static int vpbe_disp_check_window_params(struct vpbe_display *disp_dev, + struct v4l2_rect *c) +{ + if ((c->width == 0) + || ((c->width + c->left) > vpbe_dev->current_timings.xres) + || (c->height == 0) + || ((c->height + c->top) > vpbe_dev->current_timings.yres)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid crop values\n"); + return -1; + } + if ((c->height & 0x1) && (vpbe_dev->current_timings.interlaced)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "window height must be even for interlaced display\n"); + return -1; + } + return 0; +} + +/** + * 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. If application likes to add pads at the end of each line and + * end of the buffer , it can set bytesperline to line size and sizeimage to + * bytesperline * height of the buffer. If driver fills zero for active + * video width and height, and has requested user bytesperline and sizeimage, + * width and height is adjusted to maximum display limit or buffer width + * height which ever is lower + */ +static int vpbe_try_format(struct vpbe_display *disp_dev, + struct v4l2_pix_format *pixfmt, int check) +{ + int min_sizeimage, bpp, min_height = 1, min_width = 32, + max_width, max_height, user_info = 0; + + 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_ANY) { + 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->bytesperline) { + v4l2_err(&vpbe_dev->v4l2_dev, "bytesperline and width" + " cannot be zero\n"); + return -EINVAL; + } + + /* if user provided bytesperline, it must provide sizeimage as well */ + if (pixfmt->bytesperline && !pixfmt->sizeimage) { + v4l2_err(&vpbe_dev->v4l2_dev, + "sizeimage must be non zero, when user" + " provides bytesperline\n"); + return -EINVAL; + } + + /* adjust bytesperline as per hardware - multiple of 32 */ + if (!pixfmt->width) + pixfmt->width = pixfmt->bytesperline / bpp; + + if (!pixfmt->bytesperline) + pixfmt->bytesperline = pixfmt->width * bpp; + else + user_info = 1; + pixfmt->bytesperline = ((pixfmt->bytesperline + 31) & ~31); + + if (pixfmt->width < min_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is less than minimum," + "input width = %d, min_width = %d\n", + pixfmt->width, min_width); + return -EINVAL; + } + pixfmt->width = min_width; + } + + if (pixfmt->width > max_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is more than maximum," + "input width = %d, max_width = %d\n", + pixfmt->width, max_width); + return -EINVAL; + } + pixfmt->width = max_width; + } + + /* + * If height is zero, then atleast we need to have sizeimage + * to calculate height + */ + if (!pixfmt->height) { + if (user_info) { + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) { + /* + * for NV12 format, sizeimage is y-plane size + * + CbCr plane which is half of y-plane + */ + pixfmt->height = pixfmt->sizeimage / + (pixfmt->bytesperline + + (pixfmt->bytesperline >> 1)); + } else + pixfmt->height = pixfmt->sizeimage/ + pixfmt->bytesperline; + } + } + + if (pixfmt->height > max_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is more than maximum," + "input height = %d, max_height = %d\n", + pixfmt->height, max_height); + return -EINVAL; + } + pixfmt->height = max_height; + } + + if (pixfmt->height < min_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is less than minimum," + "input height = %d, min_height = %d\n", + pixfmt->height, min_height); + return -EINVAL; + } + pixfmt->height = min_width; + } + + /* if user has not provided bytesperline calculate it based on width */ + if (!user_info) + pixfmt->bytesperline = (((pixfmt->width * bpp) + 31) & ~31); + + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + min_sizeimage = pixfmt->bytesperline * pixfmt->height + + (pixfmt->bytesperline * pixfmt->height >> 1); + else + min_sizeimage = pixfmt->bytesperline * pixfmt->height; + + if (pixfmt->sizeimage < min_sizeimage) { + if (check && user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, "sizeimage is less, %d\n", + min_sizeimage); + return -EINVAL; + } + pixfmt->sizeimage = min_sizeimage; + } + 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_display_obj *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_display_obj *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_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id); + *cap = vpbe_display_videocap; + + return 0; +} + +static int vpbe_display_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct vpbe_display *disp_dev = video_drvdata(file); + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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) { + struct v4l2_rect *rect = &crop->c; + + if (rect->top < 0 || rect->left < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + + if (vpbe_disp_check_window_params(disp_dev, rect)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + 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); + + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + return ret; +} + +static int vpbe_display_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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) { + struct v4l2_rect *rect = &crop->c; + if (ret) + return ret; + 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; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + ret = -EINVAL; + } + + return ret; +} + +static int vpbe_display_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + + 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_display_obj *layer = fh->layer; + + 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) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Fill in the information about format */ + *pixfmt = layer->pix_fmt; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + + 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_display_obj *layer = fh->layer; + 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 > 0) { + 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 if (index == 1) { + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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; + } else { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* 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; + + /* Change of the default pixel format for both video windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { + struct vpbe_display_obj *otherlayer; + cfg->pixfmt = PIXFMT_NV12; + otherlayer = _vpbe_display_get_other_win(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); + + /* verify if readback values are as expected */ + if (layer->pix_fmt.width != cfg->xsize || + layer->pix_fmt.height != cfg->ysize || + layer->pix_fmt.bytesperline != layer->layer_info. + config.line_length || + (cfg->interlaced + && layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || + (!cfg->interlaced && layer->pix_fmt.field + != V4L2_FIELD_NONE)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "mismatch:layer conf params:\n"); + return -EINVAL; + } + } + + return 0; +} + +static int vpbe_display_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Check for valid field format */ + return vpbe_try_format(disp_dev, pixfmt, 0); + } + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; +} + +/** + * 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_display_obj *layer = fh->layer; + int ret = 0; + + 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; + } + } + 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) +{ + 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) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_outputs) { + 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_display_obj *layer = fh->layer; + int ret = 0; + + 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) { + 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 ret; +} + +/** + * 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) +{ + 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) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_dv_presets) { + 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 ret; +} + +/** + * 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_display_obj *layer = fh->layer; + int ret = 0; + + 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) { + 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 ret; +} + +/** + * 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) +{ + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + + 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_video_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_first_int[layer->device_id] = 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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_display_obj *layer = fh->layer; + + 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) +{ + unsigned int err = 0; + struct vpbe_fh *fh = filep->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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; +} + +static int vpbe_display_cfg_layer_default(enum vpbe_display_device_id id, + struct vpbe_display *disp_dev) +{ + int err = 0; + struct osd_layer_config *layer_config; + struct vpbe_display_obj *layer = disp_dev->dev[id]; + struct osd_layer_config *cfg = &layer->layer_info.config; + + /* 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"); + return -EBUSY; + } + + layer_config = cfg; + /* Set the default image and crop values */ + layer_config->pixfmt = PIXFMT_YCbCrI; + layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY; + layer->pix_fmt.bytesperline = layer_config->line_length = + vpbe_dev->current_timings.xres * 2; + + layer->pix_fmt.width = layer_config->xsize = + vpbe_dev->current_timings.xres; + layer->pix_fmt.height = layer_config->ysize = + vpbe_dev->current_timings.yres; + layer->pix_fmt.sizeimage = + layer->pix_fmt.bytesperline * layer->pix_fmt.height; + layer_config->xpos = 0; + layer_config->ypos = 0; + layer_config->interlaced = vpbe_dev->current_timings.interlaced; + + /* + * turn off ping-pong buffer and field inversion to fix + * the image shaking problem in 1080I mode + */ + + if (cfg->interlaced) + layer->pix_fmt.field = V4L2_FIELD_INTERLACED; + else + layer->pix_fmt.field = V4L2_FIELD_NONE; + + err = osd_device->ops.set_layer_config(osd_device, + layer->layer_info.id, + layer_config); + if (err < 0) { + /* Couldn't set layer */ + v4l2_err(&vpbe_dev->v4l2_dev, + "Display Manager failed to set osd layer\n"); + return -EBUSY; + } + + return 0; +} + +/* + * 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) +{ + int minor = iminor(file->f_path.dentry->d_inode); + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer; + struct vpbe_fh *fh = NULL; + int found = -1; + int i = 0; + + /* Check for valid minor number */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + layer = disp_dev->dev[i]; + if (minor == layer->video_dev->minor) { + found = i; + break; + } + } + + /* If not found, return error no device */ + if (0 > found) { + v4l2_err(&vpbe_dev->v4l2_dev, "device not found\n"); + return -ENODEV; + } + + /* 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) { + /* Configure the default values for the layer */ + if (vpbe_display_cfg_layer_default(layer->device_id, + disp_dev)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to configure video layer" + " for id = %d\n", layer->device_id); + 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_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); + /* If this is doing IO and other layer are not closed */ + if ((layer->usrs != 1) && fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "Close other instances\n"); + return -EAGAIN; + } + + /* 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_display_obj *otherlayer; + otherlayer = + _vpbe_display_get_other_win(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); + + if (strcmp("vpbe_controller", pdev->name) == 0) + vpbe_dev = platform_get_drvdata(pdev); + + if (strcmp("vpbe-osd", pdev->name) == 0) + osd_device = platform_get_drvdata(pdev); + + return 0; +} + +/*Configure the channels, buffer size */ +static int init_vpbe_layer_objects(int i) +{ + int free_buffer_index; + + /* Default number of buffers should be 3 */ + if ((video2_numbuffers > 0) && + (video2_numbuffers < display_buf_config_params.min_numbuffers)) + video2_numbuffers = display_buf_config_params.min_numbuffers; + if ((video3_numbuffers > 0) && + (video3_numbuffers < display_buf_config_params.min_numbuffers)) + video3_numbuffers = display_buf_config_params.min_numbuffers; + + /* + * Set buffer size to min buffers size if invalid + * buffer size is given + */ + if (video2_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]) + video2_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]; + + if (video3_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]) + video3_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]; + + /* set number of buffers, they could come from boot/args */ + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_0] = + video2_numbuffers; + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_1] = + video3_numbuffers; + + if (display_buf_config_params.numbuffers[0] == 0) + printk(KERN_ERR "no vid2 buffer allocated\n"); + if (display_buf_config_params.numbuffers[1] == 0) + printk(KERN_ERR "no vid3 buffer allocated\n"); + free_buffer_index = display_buf_config_params.numbuffers[i - 1]; + + 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 __init int vpbe_display_probe(struct platform_device *pdev) +{ + int i, j = 0, k, err = 0; + struct vpbe_display *disp_dev; + struct video_device *vbd = NULL; + struct vpbe_display_obj *vpbe_display_layer = NULL; + struct resource *res; + 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; + } + + /* Allocate memory for four plane display objects */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + disp_dev->dev[i] = + kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL); + /* If memory allocation fails, return error */ + if (!disp_dev->dev[i]) { + printk(KERN_ERR "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + spin_lock_init(&disp_dev->dev[i]->irqlock); + mutex_init(&disp_dev->dev[i]->opslock); + } + spin_lock_init(&disp_dev->dma_queue_lock); + + err = init_vpbe_layer_objects(i); + if (err) { + printk(KERN_ERR "Error initializing vpbe display\n"); + return err; + } + + /* + * 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, NULL, + vpbe_device_get); + if (err < 0) + return err; + + /* Initialize the vpbe display controller */ + if (NULL != vpbe_dev->ops.initialize) { + err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev); + if (err) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error initing vpbe\n"); + err = -ENOMEM; + goto probe_out; + } + } + + /* check the name of davinci device */ + if (vpbe_dev->cfg->module_name != NULL) + strcpy(vpbe_display_videocap.card, + vpbe_dev->cfg->module_name); + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[i]; + /* Allocate memory for video device */ + vbd = video_device_alloc(); + if (vbd == NULL) { + for (j = 0; j < i; j++) { + video_device_release( + disp_dev->dev[j]->video_dev); + } + v4l2_err(&vpbe_dev->v4l2_dev, "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + /* Initialize field of video device */ + vbd->release = video_device_release; + vbd->fops = &vpbe_fops; + vbd->ioctl_ops = &vpbe_ioctl_ops; + vbd->minor = -1; + vbd->v4l2_dev = &vpbe_dev->v4l2_dev; + vbd->lock = &vpbe_display_layer->opslock; + + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { + vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); + vbd->current_norm = + 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); + + /* Set video_dev to the video device */ + vpbe_display_layer->video_dev = vbd; + vpbe_display_layer->device_id = i; + + vpbe_display_layer->layer_info.id = + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); + if (display_buf_config_params.numbuffers[i] == 0) + vpbe_display_layer->memory = V4L2_MEMORY_USERPTR; + else + vpbe_display_layer->memory = V4L2_MEMORY_MMAP; + + /* Initialize field of the display layer objects */ + vpbe_display_layer->usrs = 0; + vpbe_display_layer->io_usrs = 0; + vpbe_display_layer->started = 0; + + /* Initialize prio member of layer object */ + v4l2_prio_init(&vpbe_display_layer->prio); + + /* Register video device */ + v4l2_info(&vpbe_dev->v4l2_dev, + "Trying to register VPBE display device.\n"); + v4l2_info(&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, + vpbe_display_nr[i]); + if (err) + goto probe_out; + /* set the driver data in platform device */ + platform_set_drvdata(pdev, disp_dev); + video_set_drvdata(vpbe_display_layer->video_dev, disp_dev); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to get VENC register address map\n"); + err = -ENODEV; + goto probe_out; + } + + reg_base_venc = ioremap_nocache(res->start, resource_size(res)); + if (!reg_base_venc) { + v4l2_err(&vpbe_dev->v4l2_dev, "Unable to map VENC IO space\n"); + err = -ENODEV; + goto probe_out; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + v4l2_err(&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(&vpbe_dev->v4l2_dev, "Unable to request interrupt\n"); + err = -ENODEV; + goto probe_out; + } + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); + return 0; +probe_out: + kfree(disp_dev); + + for (k = 0; k < j; k++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[k]; + /* Unregister video device */ + video_unregister_device(vpbe_display_layer->video_dev); + /* Release video device */ + video_device_release(vpbe_display_layer->video_dev); + vpbe_display_layer->video_dev = NULL; + } + return err; +} + +/* + * vpbe_display_remove() + * It un-register hardware layer from V4L2 driver + */ +static int vpbe_display_remove(struct platform_device *pdev) +{ + int i; + struct vpbe_display_obj *vpbe_display_layer; + struct vpbe_display *disp_dev = platform_get_drvdata(pdev); + struct resource *res; + + 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); + + vpbe_display_layer->video_dev = NULL; + } + 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 __init int vpbe_display_init(void) +{ + int err = 0; + + 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 DMXXX 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..90ad066 --- /dev/null +++ b/include/media/davinci/vpbe_display.h @@ -0,0 +1,146 @@ +/* + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef VPBE_DISPLAY_H +#define VPBE_DISPLAY_H + +#ifdef __KERNEL__ + +/* 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_display_obj { + /* number of buffers in fbuffers */ + unsigned int numbuffers; + /* 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; +}; + +/* 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_display_obj *dev[VPBE_DISPLAY_MAX_DEVICES]; +}; + +/* File handle structure */ +struct vpbe_fh { + /* vpbe device structure */ + struct vpbe_display *disp_dev; + /* pointer to layer object for opened device */ + struct vpbe_display_obj *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]; +}; + +static int venc_is_second_field(void); +#endif /* end of __KERNEL__ */ +#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..a4b8585 --- /dev/null +++ b/include/media/davinci/vpbe_types.h @@ -0,0 +1,93 @@ +/* + * 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; either version 2 of the License. + * + * 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_types { + DM644X_VPBE = 1, + DM355_VPBE, + DM365_VPBE, +}; + +/* 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 Wed Dec 15 03:10:04 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 15 Dec 2010 14:40:04 +0530 Subject: [PATCH v6 2/7] davinci vpbe: VPBE display driver Message-ID: <1292404204-11989-1-git-send-email-manjunath.hadli@ti.com> This patch implements the coe functionality of the dislay driver, mainly controlling the VENC and other encoders, and acting as the one point interface for the man V4L2 driver.This implements the cre 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 | 837 ++++++++++++++++++++++++++++++++++++ include/media/davinci/vpbe.h | 186 ++++++++ 2 files changed, 1023 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..751370f --- /dev/null +++ b/drivers/media/video/davinci/vpbe.c @@ -0,0 +1,837 @@ +/* + * 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; either version 2 of the License. + * + * 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 + + +#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 struct osd_state *osd_device; +static struct venc_platform_data *venc_device; +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(ef_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_display_config *vpbe_config = vpbe_dev->cfg; + int index = vpbe_dev->current_sd_index; + + return ((index == 0) ? &vpbe_config->venc : + &vpbe_config->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_display_config *vpbe_config, + int index) +{ + char *encoder_name = vpbe_config->outputs[index].subdev_name; + int i; + + /* Venc is always first */ + if (!strcmp(encoder_name, vpbe_config->venc.module_name)) + return 0; + + for (i = 0; i < vpbe_config->num_ext_encoders; i++) { + if (!strcmp(encoder_name, + vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; + int temp_index = output->index; + + if (temp_index >= vpbe_config->num_outputs) + return -EINVAL; + + *output = vpbe_config->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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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 vpbe_display_config *vpbe_config = vpbe_dev->cfg; + struct encoder_config_info *curr_enc_info = + vpbe_current_encoder_info(vpbe_dev); + int ret = 0, enc_out_index = 0, sd_index; + + if (index >= vpbe_config->num_outputs) + return -EINVAL; + + mutex_lock(&vpbe_dev->lock); + + sd_index = vpbe_dev->current_sd_index; + enc_out_index = vpbe_config->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, + vpbe_config->outputs[index].subdev_name)) { + /* Need to switch the encoder at the output */ + sd_index = vpbe_find_encoder_sd_index(vpbe_config, 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, + vpbe_config->outputs[index].default_mode); + if (!ret) { + 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_display_config *vpbe_config = vpbe_dev->cfg; + int i, ret = 0; + + for (i = 0; i < vpbe_config->num_outputs; i++) { + if (!strcmp(def_output, + vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; + int sd_index = vpbe_dev->current_sd_index; + int out_index = vpbe_dev->current_out_index, ret; + + + if (!(vpbe_config->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) { + 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_display_config *vpbe_config = vpbe_dev->cfg; + int out_index = vpbe_dev->current_out_index; + struct vpbe_output *output = &vpbe_config->outputs[out_index]; + int i, j = 0; + + 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_display_config *vpbe_config = vpbe_dev->cfg; + int sd_index = vpbe_dev->current_sd_index, out_index = + vpbe_dev->current_out_index, ret; + + if (!(vpbe_config->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) { + 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_display_config *vpbe_config = vpbe_dev->cfg; + int out_index = vpbe_dev->current_out_index, ret = 0, i; + struct vpbe_enc_mode_info *preset_mode = NULL; + struct v4l2_dv_preset dv_preset; + + if ((NULL == mode_info) || (NULL == mode_info->name)) + return -EINVAL; + + for (i = 0; i < vpbe_config->outputs[out_index].num_modes; i++) { + if (!strcmp(mode_info->name, + vpbe_config->outputs[out_index].modes[i].name)) { + preset_mode = &vpbe_config->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); + + if (!ret) { + 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); + if (strcmp("vpbe-osd", pdev->name) == 0) + osd_device = platform_get_drvdata(pdev); + if (strcmp("vpbe-venc", pdev->name) == 0) + venc_device = dev_get_platdata(&pdev->dev); + + 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; + int i, ret = 0, num_encoders; + struct i2c_adapter *i2c_adap; + int output_index; + int err; + + /* + * 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, NULL, + 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 */ + 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->module_name, + &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. + */ +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; + /* disaable 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 __init int vpbe_probe(struct platform_device *pdev) +{ + struct vpbe_display_config *vpbe_config; + struct vpbe_device *vpbe_dev; + + int ret = -EINVAL; + + if (NULL == pdev->dev.platform_data) { + v4l2_err(pdev->dev.driver, "Unable to get vpbe config\n"); + return -ENODEV; + } + + if (pdev->dev.platform_data == NULL) { + v4l2_err(pdev->dev.driver, "No platform data\n"); + return -ENODEV; + } + vpbe_config = pdev->dev.platform_data; + + if (!vpbe_config->module_name[0] || + !vpbe_config->osd.module_name[0] || + !vpbe_config->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 = vpbe_config; + vpbe_dev->ops = vpbe_dev_ops; + vpbe_dev->pdev = &pdev->dev; + + if (vpbe_config->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..c8853f2 --- /dev/null +++ b/include/media/davinci/vpbe.h @@ -0,0 +1,186 @@ +/* + * 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; either version 2 of the License. + * + * 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 + +/* 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_display_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_display_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; + + /* + * 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; +}; + +/* 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 Wed Dec 15 03:10:30 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 15 Dec 2010 14:40:30 +0530 Subject: [PATCH v6 3/7] davinci vpbe: OSD(On Screen Display) block Message-ID: <1292404230-12211-1-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 | 1211 +++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++++++ include/media/davinci/vpbe_osd.h | 397 +++++++++ 3 files changed, 1997 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..2599c83 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd.c @@ -0,0 +1,1211 @@ +/* + * 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; 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 + * + */ +#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 __raw_readl(osd->osd_base + offset); +} + +static inline u32 osd_write(struct osd_state *sd, u32 val, u32 offset) +{ + struct osd_state *osd = sd; + __raw_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 = __raw_readl(addr) | mask; + + __raw_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 = __raw_readl(addr) & ~mask; + + __raw_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 = (__raw_readl(addr) & ~mask) | (val & mask); + __raw_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) +{ + +#ifdef CONFIG_DM6446_FIELD_INV_WORKAROUND + 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; + } +#endif + 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) +{ + int bmp_reg, bmp_offset, bmp_mask, bmp_shift; + static const int map_1bpp[] = { 0, 15 }; + static const int map_2bpp[] = { 0, 5, 10, 15 }; + + 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]; + unsigned long flags; + struct osd_layer_config *cfg = &win->lconfig; + + 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]; + unsigned long flags; + struct osd_layer_config *cfg = &win->lconfig; + + 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 = 0; + + /* 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; + int reject_config; + unsigned long flags; + + 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_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; + osd->vpbe_type = (enum vpbe_types)pdev->dev.platform_data; + 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) +{ + /* Register the driver */ + 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..9cd3839 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd_regs.h @@ -0,0 +1,389 @@ +/* + * 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; either version 2 of the License. + * + * 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 + +enum vpbe_soc_type { + DM644x = 0, + DM35x, + DM36x, +}; + +struct vpbe_osd_platform_data { + enum vpbe_soc_type soc; +}; + +#define DM644X_OSD_REG_BASE 0x01C72600 +#define DM644X_VPBE_REG_BASE 0x01C72780 + +#define DM355_VPSSCLK_REG_BASE 0x01C70000 +#define DM355_OSD_REG_BASE 0x01C70200 + +#define DM365_OSD_REG_BASE 0x01C71C00 +#define DM365_ISP5_REG_BASE 0x01C70000 + +#define OSD_REG_SIZE 0x00000100 + +/* SYS register addresses */ +#define SYS_VPSS_CLKCTL 0x01C40044 + +/* 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..9549f3e --- /dev/null +++ b/include/media/davinci/vpbe_osd.h @@ -0,0 +1,397 @@ +/* + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef _OSD_H +#define _OSD_H + +#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; +}; +/* OSD events. Should match with that in vpbe_venc.h */ +#define OSD_END_OF_FRAME 1 +#define OSD_FIRST_FIELD 2 +#define OSD_SECOND_FIELD 4 + +/* 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; + +/* TBD. Some of these operations here are to be removed and implemented + * as a ioctl call on the osd sub device node. Right now just trying to + * make this work. + */ +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_types 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; +}; + + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Wed Dec 15 03:10:49 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 15 Dec 2010 14:40:49 +0530 Subject: [PATCH v6 4/7] davinci vpbe: VENC( Video Encoder) implementation Message-ID: <1292404249-12370-1-git-send-email-manjunath.hadli@ti.com> This patch adds the VENC or the Video encoder, whichis responsible for the blending of al 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 extenal 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 | 574 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++++++++ include/media/davinci/vpbe_venc.h | 38 ++ 3 files changed, 801 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..fafb41a --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc.c @@ -0,0 +1,574 @@ +/* + * 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; 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 + */ +#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 __raw_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); + __raw_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); + + __raw_writel(val, venc->vdaccfg_reg); + + val = __raw_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) +{ + int ret = 0; + + 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: + ret = -EINVAL; + } + return ret; +} + +static void enabledigitaloutput(struct v4l2_subdev *sd, int benable) +{ + v4l2_dbg(debug, 2, sd, "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; + + 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; + + 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; + + 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; + + 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) +{ + int ret = 0; + + v4l2_dbg(debug, 1, sd, "venc_s_std_output\n"); + + if (norm & V4L2_STD_525_60) + ret = venc_set_ntsc(sd); + else if (norm & V4L2_STD_625_50) + ret = venc_set_pal(sd); + else + ret = -EINVAL; + return ret; +} + +static int venc_s_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *dv_preset) +{ + int ret = -EINVAL; + + 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 ret; +} + +static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct venc_state *venc = to_state(sd); + int max_output, lcd_out_index, ret = 0; + + v4l2_dbg(debug, 1, sd, "venc_s_routing\n"); + + max_output = 2 + venc->pdata->num_lcd_outputs; + lcd_out_index = 3; + + if (output >= max_output) + return -EINVAL; + + if (output < lcd_out_index) + ret = venc_set_dac(sd, output); + if (!ret) + venc->output = output; + return ret; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int venc_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + __iomem void *reg_base; + + if (reg->match.type == 2) + reg->val = venc_read(sd, reg->reg); + else if (reg->match.type == 3) { + reg_base = ioremap_nocache(0x01c70800, 0x80); + if (reg_base == NULL) + return -EINVAL; + reg->val = __raw_readl(reg_base + reg->reg); + iounmap(reg_base); + } else { + reg_base = ioremap_nocache(0x01c70000, 0x80); + if (reg_base == NULL) + return -1; + reg->val = __raw_readl(reg_base + reg->reg); + iounmap(reg_base); + } + return 0; +} +#endif + +static const struct v4l2_subdev_core_ops venc_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = venc_g_register, +#endif +}; + +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 = 0; + + /* 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) +{ + /* Register the driver */ + 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..38e4818 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc_regs.h @@ -0,0 +1,189 @@ +/* + * 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; either version 2 of the License. + * + * 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 register base addresses */ +#define DM644X_VENC_REG_BASE 0x01C72400 +#define DM644X_VPBE_REG_BASE 0x01C72780 + +#define DM355_VENC_REG_BASE 0x01C70400 + +#define DM365_VENC_REG_BASE 0x01C71E00 +#define DM365_ISP5_REG_BASE 0x01C70000 + +#define DM3XX_VDAC_CONFIG 0x01C4002C +#define DM355_USB_PHY_CTRL 0x01c40034 + +/* 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..47a977c --- /dev/null +++ b/include/media/davinci/vpbe_venc.h @@ -0,0 +1,38 @@ +/* + * 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; either version 2 of the License. + * + * 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 + +#define VPBE_VENC_SUBDEV_NAME "vpbe-venc" + +/* venc events */ +#define VENC_END_OF_FRAME 1 +#define VENC_FIRST_FIELD 2 +#define VENC_SECOND_FIELD 4 + +struct venc_platform_data { + enum vpbe_types venc_type; + int (*setup_clock)(enum vpbe_enc_timings_type type, + __u64 mode); + /* Number of LCD outputs supported */ + int num_lcd_outputs; +}; + + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Wed Dec 15 03:11:08 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 15 Dec 2010 14:41:08 +0530 Subject: [PATCH v6 5/7] davinci vpbe: platform specific additions Message-ID: <1292404268-12517-1-git-send-email-manjunath.hadli@ti.com> This patch implements the overall device creation for the Video display driver, and addition of tables for the mode and output list. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- arch/arm/mach-davinci/board-dm644x-evm.c | 79 +++++++++++-- arch/arm/mach-davinci/dm644x.c | 164 ++++++++++++++++++++++++++- arch/arm/mach-davinci/include/mach/dm644x.h | 4 + 3 files changed, 228 insertions(+), 19 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 34c8b41..e9b1243 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -166,18 +166,6 @@ static struct platform_device davinci_evm_nandflash_device = { .resource = davinci_evm_nandflash_resource, }; -static u64 davinci_fb_dma_mask = DMA_BIT_MASK(32); - -static struct platform_device davinci_fb_device = { - .name = "davincifb", - .id = -1, - .dev = { - .dma_mask = &davinci_fb_dma_mask, - .coherent_dma_mask = DMA_BIT_MASK(32), - }, - .num_resources = 0, -}; - static struct tvp514x_platform_data tvp5146_pdata = { .clk_polarity = 0, .hs_polarity = 1, @@ -606,8 +594,71 @@ static void __init evm_init_i2c(void) i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info)); } +#define VENC_STD_ALL (V4L2_STD_NTSC | V4L2_STD_PAL) +/* venc standards timings */ +static struct vpbe_enc_mode_info vbpe_enc_std_timings[] = { + {"ntsc", VPBE_ENC_STD, {V4L2_STD_525_60}, 1, 720, 480, + {11, 10}, {30000, 1001}, 0x79, 0, 0x10, 0, 0, 0, 0}, + {"pal", VPBE_ENC_STD, {V4L2_STD_625_50}, 1, 720, 576, + {54, 59}, {25, 1}, 0x7E, 0, 0x16, 0, 0, 0, 0}, +}; + +/* venc dv preset timings */ +static struct vpbe_enc_mode_info vbpe_enc_preset_timings[] = { + {"480p59_94", VPBE_ENC_DV_PRESET, {V4L2_DV_480P59_94}, 0, 720, 480, + {1, 1}, {5994, 100}, 0x80, 0, 0x20, 0, 0, 0, 0}, + {"576p50", VPBE_ENC_DV_PRESET, {V4L2_DV_576P50}, 0, 720, 576, + {1, 1}, {50, 1}, 0x7E, 0, 0x30, 0, 0, 0, 0}, +}; + +/* + * The outputs available from VPBE + ecnoders. Keep the + * the order same as that of encoders. First that from venc followed by that + * from encoders. Index in the output refers to index on a particular encoder. + * Driver uses this index to pass it to encoder when it supports more than + * one output. Application uses index of the array to set an output. + */ +static struct vpbe_output dm644x_vpbe_outputs[] = { + { + .output = { + .index = 0, + .name = "Composite", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .std = VENC_STD_ALL, + .capabilities = V4L2_OUT_CAP_STD, + }, + .subdev_name = VPBE_VENC_SUBDEV_NAME, + .default_mode = "ntsc", + .num_modes = ARRAY_SIZE(vbpe_enc_std_timings), + .modes = vbpe_enc_std_timings, + }, + { + .output = { + .index = 1, + .name = "Component", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .capabilities = V4L2_OUT_CAP_PRESETS, + }, + .subdev_name = VPBE_VENC_SUBDEV_NAME, + .default_mode = "480p59_94", + .num_modes = ARRAY_SIZE(vbpe_enc_preset_timings), + .modes = vbpe_enc_preset_timings, + }, +}; + +static struct vpbe_display_config vpbe_display_cfg = { + .module_name = "dm644x-vpbe-display", + .i2c_adapter_id = 1, + .osd = { + .module_name = VPBE_OSD_SUBDEV_NAME, + }, + .venc = { + .module_name = VPBE_VENC_SUBDEV_NAME, + }, + .num_outputs = ARRAY_SIZE(dm644x_vpbe_outputs), + .outputs = dm644x_vpbe_outputs, +}; static struct platform_device *davinci_evm_devices[] __initdata = { - &davinci_fb_device, &rtc_dev, }; @@ -620,6 +671,8 @@ davinci_evm_map_io(void) { /* setup input configuration for VPFE input devices */ dm644x_set_vpfe_config(&vpfe_cfg); + /* setup configuration for vpbe devices */ + dm644x_set_vpbe_display_config(&vpbe_display_cfg); dm644x_init(); } diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 5e5b0a7..e8b8e94 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -640,6 +640,142 @@ void dm644x_set_vpfe_config(struct vpfe_config *cfg) vpfe_capture_dev.dev.platform_data = cfg; } +static struct resource dm644x_osd_resources[] = { + { + .start = 0x01C72600, + .end = 0x01C72600 + 0x200, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 dm644x_osd_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device dm644x_osd_dev = { + .name = VPBE_OSD_SUBDEV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_osd_resources), + .resource = dm644x_osd_resources, + .dev = { + .dma_mask = &dm644x_osd_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = (void *)DM644X_VPBE, + }, +}; + +static struct resource dm644x_venc_resources[] = { + /* venc registers io space */ + { + .start = 0x01C72400, + .end = 0x01C72400 + 0x180, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 dm644x_venc_dma_mask = DMA_BIT_MASK(32); + +#define VPSS_CLKCTL 0x01C40044 +static void __iomem *vpss_clkctl_reg; + +/* TBD. Check what VENC_CLOCK_SEL settings for HDTV and EDTV */ +static int dm644x_venc_setup_clock(enum vpbe_enc_timings_type type, __u64 mode) +{ + int ret = 0; + + if (NULL == vpss_clkctl_reg) + return -EINVAL; + if (type == VPBE_ENC_STD) { + __raw_writel(0x18, vpss_clkctl_reg); + } else if (type == VPBE_ENC_DV_PRESET) { + switch ((unsigned int)mode) { + case V4L2_DV_480P59_94: + case V4L2_DV_576P50: + __raw_writel(0x19, vpss_clkctl_reg); + break; + case V4L2_DV_720P60: + case V4L2_DV_1080I60: + case V4L2_DV_1080P30: + /* + * For HD, use external clock source since HD requires higher + * clock rate + */ + __raw_writel(0xa, vpss_clkctl_reg); + break; + default: + ret = -EINVAL; + break; + } + } else + ret = -EINVAL; + + return ret; +} + + +static inline u32 dm644x_reg_modify(void *reg, u32 val, u32 mask) +{ + u32 new_val = (__raw_readl(reg) & ~mask) | (val & mask); + __raw_writel(new_val, reg); + return new_val; +} + +static u64 vpbe_display_dma_mask = DMA_BIT_MASK(32); + +static struct resource dm644x_v4l2_disp_resources[] = { + { + .start = IRQ_VENCINT, + .end = IRQ_VENCINT, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0x01C72400, + .end = 0x01C72400 + 0x180, + .flags = IORESOURCE_MEM, + }, + +}; +static struct platform_device vpbe_v4l2_display = { + .name = "vpbe-v4l2", + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_v4l2_disp_resources), + .resource = dm644x_v4l2_disp_resources, + .dev = { + .dma_mask = &vpbe_display_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; +struct venc_platform_data dm644x_venc_pdata = { + .venc_type = DM644X_VPBE, + .setup_clock = dm644x_venc_setup_clock, +}; + +static struct platform_device dm644x_venc_dev = { + .name = VPBE_VENC_SUBDEV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_venc_resources), + .resource = dm644x_venc_resources, + .dev = { + .dma_mask = &dm644x_venc_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = (void *)&dm644x_venc_pdata, + }, +}; + +static u64 dm644x_vpbe_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device dm644x_vpbe_dev = { + .name = "vpbe_controller", + .id = -1, + .dev = { + .dma_mask = &dm644x_vpbe_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +void dm644x_set_vpbe_display_config(struct vpbe_display_config *cfg) +{ + dm644x_vpbe_dev.dev.platform_data = cfg; +} + /*----------------------------------------------------------------------*/ static struct map_desc dm644x_io_desc[] = { @@ -767,20 +903,36 @@ void __init dm644x_init(void) davinci_common_init(&davinci_soc_info_dm644x); } +static struct platform_device *dm644x_video_devices[] __initdata = { + &dm644x_vpss_device, + &dm644x_ccdc_dev, + &vpfe_capture_dev, + &dm644x_osd_dev, + &dm644x_venc_dev, + &dm644x_vpbe_dev, + &vpbe_v4l2_display, +}; + +static int __init dm644x_init_video(void) +{ + /* Add ccdc clock aliases */ + clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); + clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); + vpss_clkctl_reg = ioremap_nocache(VPSS_CLKCTL, 4); + platform_add_devices(dm644x_video_devices, + ARRAY_SIZE(dm644x_video_devices)); + return 0; +} + static int __init dm644x_init_devices(void) { if (!cpu_is_davinci_dm644x()) return 0; /* Add ccdc clock aliases */ - clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); - clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); platform_device_register(&dm644x_edma_device); platform_device_register(&dm644x_emac_device); - platform_device_register(&dm644x_vpss_device); - platform_device_register(&dm644x_ccdc_dev); - platform_device_register(&vpfe_capture_dev); - + dm644x_init_video(); return 0; } postcore_initcall(dm644x_init_devices); diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h index 6fca568..bf7adcd 100644 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ b/arch/arm/mach-davinci/include/mach/dm644x.h @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #define DM644X_EMAC_BASE (0x01C80000) #define DM644X_EMAC_CNTRL_OFFSET (0x0000) @@ -43,5 +46,6 @@ void __init dm644x_init(void); void __init dm644x_init_asp(struct snd_platform_data *pdata); void dm644x_set_vpfe_config(struct vpfe_config *cfg); +void dm644x_set_vpbe_display_config(struct vpbe_display_config *cfg); #endif /* __ASM_ARCH_DM644X_H */ -- 1.6.2.4 From manjunath.hadli at ti.com Wed Dec 15 03:11:29 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 15 Dec 2010 14:41:29 +0530 Subject: [PATCH v6 6/7] davinci vpbe: Build infrastructure for VPBE driver Message-ID: <1292404289-12655-1-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 | 22 ++++++++++++++++++++++ drivers/media/video/davinci/Makefile | 2 ++ 2 files changed, 24 insertions(+), 0 deletions(-) diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig index 6b19540..a7f11e7 100644 --- a/drivers/media/video/davinci/Kconfig +++ b/drivers/media/video/davinci/Kconfig @@ -91,3 +91,25 @@ 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" + 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" + select VIDEO_DM644X_VPBE + default y + help + Enables VPBE V4L2 Display driver on a DMXXX 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 Wed Dec 15 03:11:49 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 15 Dec 2010 14:41:49 +0530 Subject: [PATCH v6 7/7] davinci vpbe: Readme text for Dm6446 vpbe Message-ID: <1292404309-12791-1-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 | 100 +++++++++++++++++++++++++ 1 files changed, 100 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..3ff2dc3 --- /dev/null +++ b/Documentation/video4linux/README.davinci-vpbe @@ -0,0 +1,100 @@ + + 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 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 sub device + 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 connetected 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 ans setting of the external + encoders is not present, and would be a part of the next patch series. + + 3. Venc subdevice + 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. + + 4. osd subdevice + Osd subdevice implements all osd layer management and hardware specific + features. In the legacfy drivers (LSPxxx), the hardware specific features + are configured through proprietary IOCTLs at the fb device interface. Since + subdevices are going to support device nodes, application will be able + to configure the hardware feature directly by opening the osd subdevice + node and by calling the related IOCTL. So these proprietary IOCTLs are + to be removed from the FB Device driver when doing up port of these drivers to + mainline kernel. The V4L2 and FB device nodes supports only IOCTLS as per + the associated spec. The rest of the IOCTLs are to be moved to osd and + venc subdevices. + + Current status:- + + A build tested version of vpbe controller is available. + + Following are TBDs. + + vpbe display controller + - review and modify the handling of 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. + + v4l2 driver + - A version is already developed which is to be cleaned up and unit tested + + FB drivers + - Add support for fbdev drivers.- Ready and part of subsequent patches. -- 1.6.2.4 From manjunath.hadli at ti.com Wed Dec 15 03:28:51 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 15 Dec 2010 14:58:51 +0530 Subject: [PATCH 5/7] davinci vpbe: platform specific additions KHilman Message-ID: <1292405331-18416-1-git-send-email-manjunath.hadli@ti.com> This patch implements the overall device creation for the Video display driver, and addition of tables for the mode and output list. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- arch/arm/mach-davinci/board-dm644x-evm.c | 79 +++++++++++-- arch/arm/mach-davinci/dm644x.c | 165 +++++++++++++++++++++++++- arch/arm/mach-davinci/include/mach/dm644x.h | 4 + 3 files changed, 228 insertions(+), 20 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 0ca90b8..81d52c1 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -176,18 +176,6 @@ static struct platform_device davinci_evm_nandflash_device = { .resource = davinci_evm_nandflash_resource, }; -static u64 davinci_fb_dma_mask = DMA_BIT_MASK(32); - -static struct platform_device davinci_fb_device = { - .name = "davincifb", - .id = -1, - .dev = { - .dma_mask = &davinci_fb_dma_mask, - .coherent_dma_mask = DMA_BIT_MASK(32), - }, - .num_resources = 0, -}; - static struct tvp514x_platform_data tvp5146_pdata = { .clk_polarity = 0, .hs_polarity = 1, @@ -616,8 +604,71 @@ static void __init evm_init_i2c(void) i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info)); } +#define VENC_STD_ALL (V4L2_STD_NTSC | V4L2_STD_PAL) +/* venc standards timings */ +static struct vpbe_enc_mode_info vbpe_enc_std_timings[] = { + {"ntsc", VPBE_ENC_STD, {V4L2_STD_525_60}, 1, 720, 480, + {11, 10}, {30000, 1001}, 0x79, 0, 0x10, 0, 0, 0, 0}, + {"pal", VPBE_ENC_STD, {V4L2_STD_625_50}, 1, 720, 576, + {54, 59}, {25, 1}, 0x7E, 0, 0x16, 0, 0, 0, 0}, +}; + +/* venc dv preset timings */ +static struct vpbe_enc_mode_info vbpe_enc_preset_timings[] = { + {"480p59_94", VPBE_ENC_DV_PRESET, {V4L2_DV_480P59_94}, 0, 720, 480, + {1, 1}, {5994, 100}, 0x80, 0, 0x20, 0, 0, 0, 0}, + {"576p50", VPBE_ENC_DV_PRESET, {V4L2_DV_576P50}, 0, 720, 576, + {1, 1}, {50, 1}, 0x7E, 0, 0x30, 0, 0, 0, 0}, +}; + +/* + * The outputs available from VPBE + ecnoders. Keep the + * the order same as that of encoders. First that from venc followed by that + * from encoders. Index in the output refers to index on a particular encoder. + * Driver uses this index to pass it to encoder when it supports more than + * one output. Application uses index of the array to set an output. + */ +static struct vpbe_output dm644x_vpbe_outputs[] = { + { + .output = { + .index = 0, + .name = "Composite", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .std = VENC_STD_ALL, + .capabilities = V4L2_OUT_CAP_STD, + }, + .subdev_name = VPBE_VENC_SUBDEV_NAME, + .default_mode = "ntsc", + .num_modes = ARRAY_SIZE(vbpe_enc_std_timings), + .modes = vbpe_enc_std_timings, + }, + { + .output = { + .index = 1, + .name = "Component", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .capabilities = V4L2_OUT_CAP_PRESETS, + }, + .subdev_name = VPBE_VENC_SUBDEV_NAME, + .default_mode = "480p59_94", + .num_modes = ARRAY_SIZE(vbpe_enc_preset_timings), + .modes = vbpe_enc_preset_timings, + }, +}; + +static struct vpbe_display_config vpbe_display_cfg = { + .module_name = "dm644x-vpbe-display", + .i2c_adapter_id = 1, + .osd = { + .module_name = VPBE_OSD_SUBDEV_NAME, + }, + .venc = { + .module_name = VPBE_VENC_SUBDEV_NAME, + }, + .num_outputs = ARRAY_SIZE(dm644x_vpbe_outputs), + .outputs = dm644x_vpbe_outputs, +}; static struct platform_device *davinci_evm_devices[] __initdata = { - &davinci_fb_device, &rtc_dev, }; @@ -630,6 +681,8 @@ davinci_evm_map_io(void) { /* setup input configuration for VPFE input devices */ dm644x_set_vpfe_config(&vpfe_cfg); + /* setup configuration for vpbe devices */ + dm644x_set_vpbe_display_config(&vpbe_display_cfg); dm644x_init(); } diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 9a2376b..9db2b53 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -654,8 +654,165 @@ void dm644x_set_vpfe_config(struct vpfe_config *cfg) vpfe_capture_dev.dev.platform_data = cfg; } +static struct resource dm644x_osd_resources[] = { + { + .start = 0x01C72600, + .end = 0x01C72600 + 0x200, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 dm644x_osd_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device dm644x_osd_dev = { + .name = VPBE_OSD_SUBDEV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_osd_resources), + .resource = dm644x_osd_resources, + .dev = { + .dma_mask = &dm644x_osd_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = (void *)DM644X_VPBE, + }, +}; + +static struct resource dm644x_venc_resources[] = { + /* venc registers io space */ + { + .start = 0x01C72400, + .end = 0x01C72400 + 0x180, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 dm644x_venc_dma_mask = DMA_BIT_MASK(32); + +#define VPSS_CLKCTL 0x01C40044 +static void __iomem *vpss_clkctl_reg; + +/* TBD. Check what VENC_CLOCK_SEL settings for HDTV and EDTV */ +static int dm644x_venc_setup_clock(enum vpbe_enc_timings_type type, __u64 mode) +{ + int ret = 0; + + if (NULL == vpss_clkctl_reg) + return -EINVAL; + if (type == VPBE_ENC_STD) { + __raw_writel(0x18, vpss_clkctl_reg); + } else if (type == VPBE_ENC_DV_PRESET) { + switch ((unsigned int)mode) { + case V4L2_DV_480P59_94: + case V4L2_DV_576P50: + __raw_writel(0x19, vpss_clkctl_reg); + break; + case V4L2_DV_720P60: + case V4L2_DV_1080I60: + case V4L2_DV_1080P30: + /* + * For HD, use external clock source since HD requires higher + * clock rate + */ + __raw_writel(0xa, vpss_clkctl_reg); + break; + default: + ret = -EINVAL; + break; + } + } else + ret = -EINVAL; + + return ret; +} + + +static inline u32 dm644x_reg_modify(void *reg, u32 val, u32 mask) +{ + u32 new_val = (__raw_readl(reg) & ~mask) | (val & mask); + __raw_writel(new_val, reg); + return new_val; +} + +static u64 vpbe_display_dma_mask = DMA_BIT_MASK(32); + +static struct resource dm644x_v4l2_disp_resources[] = { + { + .start = IRQ_VENCINT, + .end = IRQ_VENCINT, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0x01C72400, + .end = 0x01C72400 + 0x180, + .flags = IORESOURCE_MEM, + }, + +}; +static struct platform_device vpbe_v4l2_display = { + .name = "vpbe-v4l2", + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_v4l2_disp_resources), + .resource = dm644x_v4l2_disp_resources, + .dev = { + .dma_mask = &vpbe_display_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; +struct venc_platform_data dm644x_venc_pdata = { + .venc_type = DM644X_VPBE, + .setup_clock = dm644x_venc_setup_clock, +}; + +static struct platform_device dm644x_venc_dev = { + .name = VPBE_VENC_SUBDEV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_venc_resources), + .resource = dm644x_venc_resources, + .dev = { + .dma_mask = &dm644x_venc_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = (void *)&dm644x_venc_pdata, + }, +}; + +static u64 dm644x_vpbe_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device dm644x_vpbe_dev = { + .name = "vpbe_controller", + .id = -1, + .dev = { + .dma_mask = &dm644x_vpbe_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +void dm644x_set_vpbe_display_config(struct vpbe_display_config *cfg) +{ + dm644x_vpbe_dev.dev.platform_data = cfg; +} + /*----------------------------------------------------------------------*/ +static struct platform_device *dm644x_video_devices[] __initdata = { + &dm644x_vpss_device, + &dm644x_ccdc_dev, + &vpfe_capture_dev, + &dm644x_osd_dev, + &dm644x_venc_dev, + &dm644x_vpbe_dev, + &vpbe_v4l2_display, +}; + +static int __init dm644x_init_video(void) +{ + /* Add ccdc clock aliases */ + clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); + clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); + vpss_clkctl_reg = ioremap_nocache(VPSS_CLKCTL, 4); + platform_add_devices(dm644x_video_devices, + ARRAY_SIZE(dm644x_video_devices)); + return 0; +} + static struct map_desc dm644x_io_desc[] = { { .virtual = IO_VIRT, @@ -787,19 +944,13 @@ static int __init dm644x_init_devices(void) return 0; /* Add ccdc clock aliases */ - clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); - clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); platform_device_register(&dm644x_edma_device); - platform_device_register(&dm644x_mdio_device); platform_device_register(&dm644x_emac_device); clk_add_alias(NULL, dev_name(&dm644x_mdio_device.dev), NULL, &dm644x_emac_device.dev); - platform_device_register(&dm644x_vpss_device); - platform_device_register(&dm644x_ccdc_dev); - platform_device_register(&vpfe_capture_dev); - + dm644x_init_video(); return 0; } postcore_initcall(dm644x_init_devices); diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h index 5a1b26d..8c0fad2 100644 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ b/arch/arm/mach-davinci/include/mach/dm644x.h @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #define DM644X_EMAC_BASE (0x01C80000) #define DM644X_EMAC_MDIO_BASE (DM644X_EMAC_BASE + 0x4000) @@ -43,5 +46,6 @@ void __init dm644x_init(void); void __init dm644x_init_asp(struct snd_platform_data *pdata); void dm644x_set_vpfe_config(struct vpfe_config *cfg); +void dm644x_set_vpbe_display_config(struct vpbe_display_config *cfg); #endif /* __ASM_ARCH_DM644X_H */ -- 1.7.0.4 From nsekhar at ti.com Wed Dec 15 05:13:37 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 15 Dec 2010 16:43:37 +0530 Subject: [PATCH 2/2] da850: board file modifications for PRU SUART. In-Reply-To: <1291389108-25356-2-git-send-email-subhasish@mistralsolutions.com> References: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> <1291389108-25356-2-git-send-email-subhasish@mistralsolutions.com> Message-ID: Hi Subhasish, On Fri, Dec 03, 2010 at 20:41:48, Subhasish Ghosh wrote: > > Signed-off-by: Subhasish Ghosh > --- > arch/arm/mach-davinci/board-da850-evm.c | 28 ++++++++++++++++++++++++++++ > 1 files changed, 28 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index f89b0b7..86a89b1 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -736,6 +736,34 @@ static struct edma_rsv_info *da850_edma_rsv[2] = { > &da850_edma_cc1_rsv, > }; > > +const short da850_evm_pru_suart_pins[] = { > + DA850_AHCLKX, DA850_ACLKX, DA850_AFSX, > + DA850_AHCLKR, DA850_ACLKR, DA850_AFSR, > + DA850_AXR_13, DA850_AXR_9, DA850_AXR_7, > + DA850_AXR_14, DA850_AXR_10, DA850_AXR_8, > + -1 > +}; > + > +static int __init da850_evm_setup_pru_suart(void) > +{ > + int ret; > + > + if (!machine_is_davinci_da850_evm()) > + return 0; > + > + ret = davinci_cfg_reg_list(da850_evm_pru_suart_pins); > + if (ret) > + pr_warning("%s: da850_evm_pru_suart_pins " > + "mux setup failed: %d\n", __func__, ret); > + ret = da8xx_register_pru_suart(); > + if (ret) > + pr_warning("%s: pru suart registration " > + "failed: %d\n", __func__, ret); > + return ret; > +} > + > +device_initcall(da850_evm_setup_pru_suart); Why is this a device_initcall()? Thanks, Sekhar From nsekhar at ti.com Wed Dec 15 05:18:37 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 15 Dec 2010 16:48:37 +0530 Subject: [PATCH 1/2] da850: Support for TI's PRU SoftUART Emulation In-Reply-To: <87ei9p6tgo.fsf@deeprootsystems.com> References: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> <87ei9p6tgo.fsf@deeprootsystems.com> Message-ID: Hi Kevin, On Sat, Dec 11, 2010 at 06:01:19, Kevin Hilman wrote: > Subhasish Ghosh writes: > > > The patch adds support for emulated UART controllers > > on the programmable realtime unit (PRU) available on OMAPL138. > > This defines the system resource requirements such as pin mux, > > clock, iomem, interrupt etc and registers the platform device > > as per the Linux driver model. > > > > Signed-off-by: Subhasish Ghosh > > You might consider setting your git user to use your Mistral address, so > according to git, the author and the sign-off are the same person. > Otherwise, git stats will report your personal address as the author and > your company address as the signoff. > > Also, please Cc linux-arm-kernel at lists.infradead.org on all davinci > kernel patches. > > Otherwise, this patch is looking ok (after addressing alignment issue > reported by Sergei.) Is it OK to merge the platform data before the driver is merged? Thanks, Sekhar > > [...] > > > diff --git a/arch/arm/mach-davinci/include/mach/memory.h b/arch/arm/mach-davinci/include/mach/memory.h > > index 22eb97c..d3e48d9 100644 > > --- a/arch/arm/mach-davinci/include/mach/memory.h > > +++ b/arch/arm/mach-davinci/include/mach/memory.h > > @@ -22,6 +22,7 @@ > > **************************************************************************/ > > #define DAVINCI_DDR_BASE 0x80000000 > > #define DA8XX_DDR_BASE 0xc0000000 > > +#define DA8XX_SHARED_RAM_BASE 0x80000000 > > please align with previous defines > > > #if defined(CONFIG_ARCH_DAVINCI_DA8XX) && defined(CONFIG_ARCH_DAVINCI_DMx) > > #error Cannot enable DaVinci and DA8XX platforms concurrently > > diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h > > index 212eb4c..407624e 100644 > > --- a/include/linux/serial_core.h > > +++ b/include/linux/serial_core.h > > @@ -199,6 +199,9 @@ > > /* TI OMAP-UART */ > > #define PORT_OMAP 96 > > > > +/* omapl pru uart emulation */ > > +#define PORT_OMAPL_PRU_SUART 97 > > + > > This define is not used in this series. Please add it when you add the > driver which uses it. > > Kevin > _______________________________________________ > 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 Wed Dec 15 05:20:21 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Wed, 15 Dec 2010 14:20:21 +0300 Subject: [PATCH v6 5/7] davinci vpbe: platform specific additions In-Reply-To: <1292404268-12517-1-git-send-email-manjunath.hadli@ti.com> References: <1292404268-12517-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <4D08A475.6080703@mvista.com> Hello. On 15-12-2010 12:11, Manjunath Hadli wrote: > This patch implements the overall device creation for the Video > display driver, and addition of tables for the mode and output list. > Signed-off-by: Manjunath Hadli > Acked-by: Muralidharan Karicheri > Acked-by: Hans Verkuil [...] > diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c > index 34c8b41..e9b1243 100644 > --- a/arch/arm/mach-davinci/board-dm644x-evm.c > +++ b/arch/arm/mach-davinci/board-dm644x-evm.c I think the DM644x EVM board changes should be in a separate patch. WBR, Sergei From mahidadigvijay at gmail.com Wed Dec 15 05:32:43 2010 From: mahidadigvijay at gmail.com (Digvijay Mahida) Date: Wed, 15 Dec 2010 17:02:43 +0530 Subject: No subject Message-ID: Hi, I am interfacing 320x240 LCD with DM355 and usinf MONAVISTA 5.0. The maximum !)MHz of DCLK frequency is supporting by LCD. I know that i have to reduce my DCLK frequency and i also know that i can get it by PLLDIV but i am not getting that in which file i have to make change.. So please tell me the file location in which i have to make change to get required DClk frequency using PLLDIV. -------------- next part -------------- An HTML attachment was scrubbed... URL: From nsekhar at ti.com Wed Dec 15 06:11:26 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 15 Dec 2010 17:41:26 +0530 Subject: [PATCH 1/2] da850: Support for TI's PRU SoftUART Emulation In-Reply-To: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> References: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: On Fri, Dec 03, 2010 at 20:41:47, Subhasish Ghosh wrote: > The patch adds support for emulated UART controllers > on the programmable realtime unit (PRU) available on OMAPL138. > This defines the system resource requirements such as pin mux, > clock, iomem, interrupt etc and registers the platform device > as per the Linux driver model. > > Signed-off-by: Subhasish Ghosh > --- > arch/arm/mach-davinci/da850.c | 16 +++++ > arch/arm/mach-davinci/devices-da8xx.c | 95 ++++++++++++++++++++++++++- > arch/arm/mach-davinci/include/mach/da8xx.h | 1 + > arch/arm/mach-davinci/include/mach/memory.h | 1 + > include/linux/serial_core.h | 3 + > 5 files changed, 115 insertions(+), 1 deletions(-) > > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 63916b9..85508c2 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -238,6 +238,12 @@ static struct clk tptc2_clk = { > .flags = ALWAYS_ENABLED, > }; > > +static struct clk pru_clk = { > + .name = "pruss", > + .parent = &pll0_sysclk2, > + .lpsc = DA8XX_LPSC0_DMAX, > +}; > + > static struct clk uart0_clk = { > .name = "uart0", > .parent = &pll0_sysclk2, > @@ -318,6 +324,14 @@ static struct clk mcasp_clk = { > .flags = DA850_CLK_ASYNC3, > }; > > +static struct clk mcasp_pru_clk = { > + .name = "mcasp_pru", > + .parent = &pll0_sysclk2, > + .lpsc = DA8XX_LPSC1_McASP0, > + .gpsc = 1, > + .flags = DA850_CLK_ASYNC3, > +}; There is already a mcasp clock defined. Why not use it instead of replicating it with a different name? Not doing so will cause a mess with reference counting. > + > static struct clk lcdc_clk = { > .name = "lcdc", > .parent = &pll0_sysclk2, > @@ -373,6 +387,7 @@ static struct clk_lookup da850_clks[] = { > CLK(NULL, "tpcc1", &tpcc1_clk), > CLK(NULL, "tptc2", &tptc2_clk), > CLK(NULL, "uart0", &uart0_clk), > + CLK(NULL, "pruss", &pru_clk), > CLK(NULL, "uart1", &uart1_clk), > CLK(NULL, "uart2", &uart2_clk), > CLK(NULL, "aintc", &aintc_clk), > @@ -381,6 +396,7 @@ static struct clk_lookup da850_clks[] = { > CLK(NULL, "emif3", &emif3_clk), > CLK(NULL, "arm", &arm_clk), > CLK(NULL, "rmii", &rmii_clk), > + CLK(NULL, "mcasp_pru", &mcasp_pru_clk), > CLK("davinci_emac.1", NULL, &emac_clk), > CLK("davinci-mcasp.0", NULL, &mcasp_clk), > CLK("da8xx_lcdc.0", NULL, &lcdc_clk), > diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c > index 9eec630..25de36d 100644 > --- a/arch/arm/mach-davinci/devices-da8xx.c > +++ b/arch/arm/mach-davinci/devices-da8xx.c > @@ -85,7 +85,100 @@ struct platform_device da8xx_serial_device = { > }, > }; > > -static const s8 da8xx_queue_tc_mapping[][2] = { > + > +#define OMAPL138_PRU_MEM_BASE 0x01C30000 > + > +#define OMAPL138_INT_PRU_SUART_1 IRQ_DA8XX_EVTOUT0 > +#define OMAPL138_INT_PRU_SUART_2 IRQ_DA8XX_EVTOUT1 > +#define OMAPL138_INT_PRU_SUART_3 IRQ_DA8XX_EVTOUT2 > +#define OMAPL138_INT_PRU_SUART_4 IRQ_DA8XX_EVTOUT3 > +#define OMAPL138_INT_PRU_SUART_5 IRQ_DA8XX_EVTOUT4 > +#define OMAPL138_INT_PRU_SUART_6 IRQ_DA8XX_EVTOUT5 > +#define OMAPL138_INT_PRU_SUART_7 IRQ_DA8XX_EVTOUT6 > +#define OMAPL138_INT_PRU_SUART_8 IRQ_DA8XX_EVTOUT7 Can you please use DA850/DA8XX prefix like is done in other DA850 code? This will help reading and not make this code stand out. > + > +static struct resource omapl138_pru_suart_resources[] = { > + { > + .name = "omapl_pru_suart", > + .start = OMAPL138_PRU_MEM_BASE, > + .end = OMAPL138_PRU_MEM_BASE + 0xFFFF, > + .flags = IORESOURCE_MEM, > + }, > + { > + .start = DAVINCI_DA8XX_MCASP0_REG_BASE, > + .end = DAVINCI_DA8XX_MCASP0_REG_BASE + (SZ_1K * 12) - 1, > + .flags = IORESOURCE_MEM, > + }, > + { > + .start = DA8XX_PSC0_BASE, > + .end = DA8XX_PSC0_BASE + (SZ_1K * 3) - 1, > + .flags = IORESOURCE_MEM, > + }, > + { > + .start = DA8XX_PSC1_BASE, > + .end = DA8XX_PSC1_BASE + (SZ_1K * 3) - 1, > + .flags = IORESOURCE_MEM, > + }, I thought you are going to remove these since PSCs should only be worked using the clock API. Also, can you please add a version number after the word "PATCH" in the subject so it is easy for reviewers to locate your latest patches? > + { > + .start = DA8XX_SHARED_RAM_BASE, > + .end = DA8XX_SHARED_RAM_BASE + (SZ_1K * 8) - 1, > + .flags = IORESOURCE_MEM, > + }, Shared RAM can be used by other drivers (like audio). So, allocation of this RAM should happen using sram_alloc(). > + { > + .start = OMAPL138_INT_PRU_SUART_1, > + .end = OMAPL138_INT_PRU_SUART_1, > + .flags = IORESOURCE_IRQ, > + }, > + { > + .start = OMAPL138_INT_PRU_SUART_2, > + .end = OMAPL138_INT_PRU_SUART_2, > + .flags = IORESOURCE_IRQ, > + }, > + { > + .start = OMAPL138_INT_PRU_SUART_3, > + .end = OMAPL138_INT_PRU_SUART_3, > + .flags = IORESOURCE_IRQ, > + }, > + { > + .start = OMAPL138_INT_PRU_SUART_4, > + .end = OMAPL138_INT_PRU_SUART_4, > + .flags = IORESOURCE_IRQ, > + }, > + { > + .start = OMAPL138_INT_PRU_SUART_5, > + .end = OMAPL138_INT_PRU_SUART_5, > + .flags = IORESOURCE_IRQ, > + }, > + { > + .start = OMAPL138_INT_PRU_SUART_6, > + .end = OMAPL138_INT_PRU_SUART_6, > + .flags = IORESOURCE_IRQ, > + }, > + { > + .start = OMAPL138_INT_PRU_SUART_7, > + .end = OMAPL138_INT_PRU_SUART_7, > + .flags = IORESOURCE_IRQ, > + }, > + { > + .start = OMAPL138_INT_PRU_SUART_8, > + .end = OMAPL138_INT_PRU_SUART_8, > + .flags = IORESOURCE_IRQ, > + }, > +}; > + > +struct platform_device omapl_pru_suart_device = { > + .name = "davinci_pru_suart", > + .id = -1, > + .num_resources = ARRAY_SIZE(omapl138_pru_suart_resources), > + .resource = omapl138_pru_suart_resources, > +}; > + > +int __init da8xx_register_pru_suart(void) > +{ > + return platform_device_register(&omapl_pru_suart_device); > +} > + > +static const s8 da8xx_queue_tc_mapping[][2] = { > /* {event queue no, TC no} */ > {0, 0}, > {1, 1}, > diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h > index 4247b3f..940dbd6 100644 > --- a/arch/arm/mach-davinci/include/mach/da8xx.h > +++ b/arch/arm/mach-davinci/include/mach/da8xx.h > @@ -74,6 +74,7 @@ int da8xx_register_watchdog(void); > int da8xx_register_usb20(unsigned mA, unsigned potpgt); > int da8xx_register_usb11(struct da8xx_ohci_root_hub *pdata); > int da8xx_register_emac(void); > +int da8xx_register_pru_suart(void); > int da8xx_register_lcdc(struct da8xx_lcdc_platform_data *pdata); > int da8xx_register_mmcsd0(struct davinci_mmc_config *config); > int da850_register_mmcsd1(struct davinci_mmc_config *config); > diff --git a/arch/arm/mach-davinci/include/mach/memory.h b/arch/arm/mach-davinci/include/mach/memory.h > index 22eb97c..d3e48d9 100644 > --- a/arch/arm/mach-davinci/include/mach/memory.h > +++ b/arch/arm/mach-davinci/include/mach/memory.h > @@ -22,6 +22,7 @@ > **************************************************************************/ > #define DAVINCI_DDR_BASE 0x80000000 > #define DA8XX_DDR_BASE 0xc0000000 > +#define DA8XX_SHARED_RAM_BASE 0x80000000 Please add it in arch/arm/mach-davinci/include/mach/da8xx.h along with rest of da8xx specific base address defines. Thanks, Sekhar From nsekhar at ti.com Wed Dec 15 06:13:06 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 15 Dec 2010 17:43:06 +0530 Subject: [PATCH v4 1/2] davinci: am18x/da850/omap-l138: add support for higher speed grades In-Reply-To: <87sjy589jj.fsf@deeprootsystems.com> References: <1291884094-19433-1-git-send-email-nsekhar@ti.com> <87sjy589jj.fsf@deeprootsystems.com> Message-ID: On Sat, Dec 11, 2010 at 05:28:40, Kevin Hilman wrote: > Sekhar Nori writes: > > > AM18x/DA850/OMAP-L138 SoCs have variants that can operate > > at a maximum of 456 MHz at 1.3V operating point. Also the > > 1.2V operating point has a variant that can support a maximum > > of 375 MHz. > > > > This patch adds three new OPPs (456 MHz, 408 MHz and 372 MHz) > > to the list of DA850 OPPs. > > > > Not all silicon is qualified to run at higher speeds and > > unfortunately the maximum speed the chip can support can only > > be determined from the label on the package (not software > > readable). > > > > Because of this, we depend on the maximum speed grade information > > to be provided to us in some board specific way. The board informs > > the maximum speed grade information to the SoC by calling the > > da850_set_max_speed() function. > > > > Signed-off-by: Sekhar Nori > > --- > > Since v3: > > Fixed pre-div and post-div for 372MHz OPP based on PLLOUT range > > limitations documented in OMAP-L138 datasheet. AM1808 datasheet > > will be updated to match this. > > This series looks good, but can you (re)post one more time and Cc > the linux-arm-kernel list please? Sure. Will do. Thanks, Sekhar From sshtylyov at mvista.com Wed Dec 15 07:01:43 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Wed, 15 Dec 2010 16:01:43 +0300 Subject: [PATCH 1/2] da850: Support for TI's PRU SoftUART Emulation In-Reply-To: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> References: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <4D08BC37.7060703@mvista.com> Hello. On 03-12-2010 18:11, Subhasish Ghosh wrote: > The patch adds support for emulated UART controllers > on the programmable realtime unit (PRU) available on OMAPL138. > This defines the system resource requirements such as pin mux, > clock, iomem, interrupt etc and registers the platform device > as per the Linux driver model. > Signed-off-by: Subhasish Ghosh [...] > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 63916b9..85508c2 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -238,6 +238,12 @@ static struct clk tptc2_clk = { > .flags = ALWAYS_ENABLED, > }; > > +static struct clk pru_clk = { > + .name = "pruss", > + .parent = &pll0_sysclk2, > + .lpsc = DA8XX_LPSC0_DMAX, > +}; > + Shouldn't it be called dmax_clk. And please align the intializer like it's done for other clocks. > static struct clk uart0_clk = { > .name = "uart0", > .parent =&pll0_sysclk2, > @@ -318,6 +324,14 @@ static struct clk mcasp_clk = { > .flags = DA850_CLK_ASYNC3, > }; > > +static struct clk mcasp_pru_clk = { > + .name = "mcasp_pru", > + .parent = &pll0_sysclk2, > + .lpsc = DA8XX_LPSC1_McASP0, > + .gpsc = 1, > + .flags = DA850_CLK_ASYNC3, > +}; > + We already have McASP clock defined, this is a duplicate one. > static struct clk lcdc_clk = { > .name = "lcdc", > .parent =&pll0_sysclk2, > diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h > index 212eb4c..407624e 100644 > --- a/include/linux/serial_core.h > +++ b/include/linux/serial_core.h > @@ -199,6 +199,9 @@ > /* TI OMAP-UART */ > #define PORT_OMAP 96 > > +/* omapl pru uart emulation */ > +#define PORT_OMAPL_PRU_SUART 97 > + This is not used in the patch... WBR, Sergei From nsekhar at ti.com Wed Dec 15 08:23:14 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 15 Dec 2010 19:53:14 +0530 Subject: [PATCH 1/2] da850: Support for TI's PRU SoftUART Emulation In-Reply-To: <4D08BC37.7060703@mvista.com> References: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> <4D08BC37.7060703@mvista.com> Message-ID: On Wed, Dec 15, 2010 at 18:31:43, Sergei Shtylyov wrote: > Hello. > > On 03-12-2010 18:11, Subhasish Ghosh wrote: > > > The patch adds support for emulated UART controllers > > on the programmable realtime unit (PRU) available on OMAPL138. > > This defines the system resource requirements such as pin mux, > > clock, iomem, interrupt etc and registers the platform device > > as per the Linux driver model. > > > Signed-off-by: Subhasish Ghosh > [...] > > > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > > index 63916b9..85508c2 100644 > > --- a/arch/arm/mach-davinci/da850.c > > +++ b/arch/arm/mach-davinci/da850.c > > @@ -238,6 +238,12 @@ static struct clk tptc2_clk = { > > .flags = ALWAYS_ENABLED, > > }; > > > > +static struct clk pru_clk = { > > + .name = "pruss", > > + .parent = &pll0_sysclk2, > > + .lpsc = DA8XX_LPSC0_DMAX, > > +}; > > + > > Shouldn't it be called dmax_clk. And please align the intializer like it's > done for other clocks. This module is known as PRU (Programmable Realtime Unit) in TI documentation So, the LPSC define is better corrected. Thanks, Sekhar From m-karicheri2 at ti.com Wed Dec 15 09:19:00 2010 From: m-karicheri2 at ti.com (Karicheri, Muralidharan) Date: Wed, 15 Dec 2010 09:19:00 -0600 Subject: [PATCH v6 7/7] davinci vpbe: Readme text for Dm6446 vpbe In-Reply-To: <1292404309-12791-1-git-send-email-manjunath.hadli@ti.com> References: <1292404309-12791-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Manju, Could you review the Document? I think it is not updated to reflect the latest status: >+ Current status:- >+ >+ A build tested version of vpbe controller is available. I guess you have already tested this using the v4l2 driver. >+ v4l2 driver >+ - A version is already developed which is to be cleaned up and unit >tested Ditto. v4l2 driver is already tested, right? Murali Karicheri Software Design Engineer Texas Instruments Inc. Germantown, MD 20874 >-----Original Message----- >From: linux-media-owner at vger.kernel.org [mailto:linux-media- >owner at vger.kernel.org] On Behalf Of Manjunath Hadli >Sent: Wednesday, December 15, 2010 4:12 AM >To: LMML >Cc: dlos; Mauro Carvalho Chehab; Hans Verkuil; Hadli, Manjunath >Subject: [PATCH v6 7/7] davinci vpbe: Readme text for Dm6446 vpbe > >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 | 100 >+++++++++++++++++++++++++ > 1 files changed, 100 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..3ff2dc3 >--- /dev/null >+++ b/Documentation/video4linux/README.davinci-vpbe >@@ -0,0 +1,100 @@ >+ >+ 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 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 sub device >+ 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 connetected 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 ans setting of the >external >+ encoders is not present, and would be a part of the next patch series. >+ >+ 3. Venc subdevice >+ 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. >+ >+ 4. osd subdevice >+ Osd subdevice implements all osd layer management and hardware >specific >+ features. In the legacfy drivers (LSPxxx), the hardware specific >features >+ are configured through proprietary IOCTLs at the fb device interface. >Since >+ subdevices are going to support device nodes, application will be able >+ to configure the hardware feature directly by opening the osd >subdevice >+ node and by calling the related IOCTL. So these proprietary IOCTLs are >+ to be removed from the FB Device driver when doing up port of these >drivers to >+ mainline kernel. The V4L2 and FB device nodes supports only IOCTLS as >per >+ the associated spec. The rest of the IOCTLs are to be moved to osd and >+ venc subdevices. >+ >+ Current status:- >+ >+ A build tested version of vpbe controller is available. >+ >+ Following are TBDs. >+ >+ vpbe display controller >+ - review and modify the handling of 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. >+ >+ v4l2 driver >+ - A version is already developed which is to be cleaned up and unit >tested >+ >+ FB drivers >+ - Add support for fbdev drivers.- Ready and part of subsequent patches. >-- >1.6.2.4 > >-- >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 m-karicheri2 at ti.com Wed Dec 15 09:54:44 2010 From: m-karicheri2 at ti.com (Karicheri, Muralidharan) Date: Wed, 15 Dec 2010 09:54:44 -0600 Subject: [PATCH v6 5/7] davinci vpbe: platform specific additions In-Reply-To: <4D08A475.6080703@mvista.com> References: <1292404268-12517-1-git-send-email-manjunath.hadli@ti.com> <4D08A475.6080703@mvista.com> Message-ID: Sergei, >I think the DM644x EVM board changes should be in a separate patch. Any reason? Murali Karicheri Software Design Engineer Texas Instruments Inc. Germantown, MD 20874 >-----Original Message----- >From: davinci-linux-open-source-bounces at linux.davincidsp.com >[mailto:davinci-linux-open-source-bounces at linux.davincidsp.com] On Behalf >Of Sergei Shtylyov >Sent: Wednesday, December 15, 2010 6:20 AM >To: Hadli, Manjunath >Cc: dlos; Mauro Carvalho Chehab; LMML >Subject: Re: [PATCH v6 5/7] davinci vpbe: platform specific additions > >Hello. > >On 15-12-2010 12:11, Manjunath Hadli wrote: > >> This patch implements the overall device creation for the Video >> display driver, and addition of tables for the mode and output list. > >> Signed-off-by: Manjunath Hadli >> Acked-by: Muralidharan Karicheri >> Acked-by: Hans Verkuil >[...] > >> diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach- >davinci/board-dm644x-evm.c >> index 34c8b41..e9b1243 100644 >> --- a/arch/arm/mach-davinci/board-dm644x-evm.c >> +++ b/arch/arm/mach-davinci/board-dm644x-evm.c > > I think the DM644x EVM board changes should be in a separate patch. > >WBR, Sergei >_______________________________________________ >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 Thu Dec 16 00:50:35 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 16 Dec 2010 12:20:35 +0530 Subject: Thx for mmc in u-boot In-Reply-To: References: Message-ID: Hi Raffaele, On Sat, Dec 11, 2010 at 01:02:00, Raffaele Recalcati wrote: > I was thinking to get crazy for having a v4.4 mmc working with dm365, > instead with latest u-boot in ti tree mmc works out of the box. > Thank you! Glad that you found it useful. Thanks, Sekhar From vaibhav.bedia at ti.com Thu Dec 16 03:16:58 2010 From: vaibhav.bedia at ti.com (Vaibhav Bedia) Date: Thu, 16 Dec 2010 14:46:58 +0530 Subject: [PATCH] ASoC: Davinci: Replace use of IO_ADDRESS with ioremap() Message-ID: <1292491018-24491-1-git-send-email-vaibhav.bedia@ti.com> This patch modifies the Davinci i2s and mcasp drivers to use ioremap() instead of IO_ADDRESS Signed-off-by: Vaibhav Bedia --- sound/soc/davinci/davinci-i2s.c | 15 +++++++++++---- sound/soc/davinci/davinci-mcasp.c | 16 ++++++++++++---- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 9e0e565..5395fad 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -694,7 +694,12 @@ static int davinci_i2s_probe(struct platform_device *pdev) } clk_enable(dev->clk); - dev->base = (void __iomem *)IO_ADDRESS(mem->start); + dev->base = ioremap(mem->start, resource_size(mem)); + if (!dev->base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_ioremap; + } dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr = (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG); @@ -707,7 +712,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENXIO; - goto err_free_mem; + goto err_ioremap; } dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel = res->start; @@ -715,7 +720,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENXIO; - goto err_free_mem; + goto err_ioremap; } dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start; dev->dev = &pdev->dev; @@ -724,10 +729,12 @@ static int davinci_i2s_probe(struct platform_device *pdev) ret = snd_soc_register_dai(&pdev->dev, &davinci_i2s_dai); if (ret != 0) - goto err_free_mem; + goto err_ioremap; return 0; +err_ioremap: + iounmap(dev->base); err_free_mem: kfree(dev); err_release_region: diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index fb55d2c..de61b41 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -885,7 +885,13 @@ static int davinci_mcasp_probe(struct platform_device *pdev) clk_enable(dev->clk); dev->clk_active = 1; - dev->base = (void __iomem *)IO_ADDRESS(mem->start); + dev->base = ioremap(mem->start, resource_size(mem)); + if (!dev->base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_ioremap; + } + dev->op_mode = pdata->op_mode; dev->tdm_slots = pdata->tdm_slots; dev->num_serializer = pdata->num_serializer; @@ -906,7 +912,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENODEV; - goto err_release_region; + goto err_ioremap; } dma_data->channel = res->start; @@ -921,7 +927,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) if (!res) { dev_err(&pdev->dev, "no DMA resource\n"); ret = -ENODEV; - goto err_release_region; + goto err_ioremap; } dma_data->channel = res->start; @@ -929,9 +935,11 @@ static int davinci_mcasp_probe(struct platform_device *pdev) ret = snd_soc_register_dai(&pdev->dev, &davinci_mcasp_dai[pdata->op_mode]); if (ret != 0) - goto err_release_region; + goto err_ioremap; return 0; +err_ioremap: + iounmap(dev->base); err_release_region: release_mem_region(mem->start, (mem->end - mem->start) + 1); err_release_data: -- 1.6.2.4 From sshtylyov at mvista.com Thu Dec 16 05:12:57 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Thu, 16 Dec 2010 14:12:57 +0300 Subject: [PATCH] ASoC: Davinci: Replace use of IO_ADDRESS with ioremap() In-Reply-To: <1292491018-24491-1-git-send-email-vaibhav.bedia@ti.com> References: <1292491018-24491-1-git-send-email-vaibhav.bedia@ti.com> Message-ID: <4D09F439.9010404@mvista.com> Hello. On 16-12-2010 12:16, Vaibhav Bedia wrote: > This patch modifies the Davinci i2s and mcasp drivers to use ioremap() > instead of IO_ADDRESS > Signed-off-by: Vaibhav Bedia [...] > diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c > index 9e0e565..5395fad 100644 > --- a/sound/soc/davinci/davinci-i2s.c > +++ b/sound/soc/davinci/davinci-i2s.c > @@ -694,7 +694,12 @@ static int davinci_i2s_probe(struct platform_device *pdev) > } > clk_enable(dev->clk); > > - dev->base = (void __iomem *)IO_ADDRESS(mem->start); > + dev->base = ioremap(mem->start, resource_size(mem)); > + if (!dev->base) { > + dev_err(&pdev->dev, "ioremap failed\n"); > + ret = -ENOMEM; > + goto err_ioremap; Why iounmap() what you've just failed to ioremap()? > @@ -724,10 +729,12 @@ static int davinci_i2s_probe(struct platform_device *pdev) [...] > +err_ioremap: > + iounmap(dev->base); > err_free_mem: Don't you forget to call clk_disable()/clk_put()? > kfree(dev); > err_release_region: > diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c > index fb55d2c..de61b41 100644 > --- a/sound/soc/davinci/davinci-mcasp.c > +++ b/sound/soc/davinci/davinci-mcasp.c > @@ -885,7 +885,13 @@ static int davinci_mcasp_probe(struct platform_device *pdev) > clk_enable(dev->clk); > dev->clk_active = 1; > > - dev->base = (void __iomem *)IO_ADDRESS(mem->start); > + dev->base = ioremap(mem->start, resource_size(mem)); > + if (!dev->base) { > + dev_err(&pdev->dev, "ioremap failed\n"); > + ret = -ENOMEM; > + goto err_ioremap; Why iounmap() what you've just failed to ioremap()? > @@ -929,9 +935,11 @@ static int davinci_mcasp_probe(struct platform_device *pdev) [...] > +err_ioremap: > + iounmap(dev->base); > err_release_region: Don't you forget to call clk_disable()/clk_put()? > release_mem_region(mem->start, (mem->end - mem->start) + 1); > err_release_data: WBR, Sergei From sshtylyov at mvista.com Thu Dec 16 05:14:15 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Thu, 16 Dec 2010 14:14:15 +0300 Subject: [PATCH v6 5/7] davinci vpbe: platform specific additions In-Reply-To: References: <1292404268-12517-1-git-send-email-manjunath.hadli@ti.com> <4D08A475.6080703@mvista.com> Message-ID: <4D09F487.4010906@mvista.com> Hello. On 15-12-2010 18:54, Karicheri, Muralidharan wrote: >> I think the DM644x EVM board changes should be in a separate patch. > Any reason? The resaon is simple: you shouldn't mix SoC-level and board-level changes. > Murali Karicheri > Software Design Engineer > Texas Instruments Inc. > Germantown, MD 20874 WBR, Sergei From vaibhav.bedia at ti.com Thu Dec 16 05:41:09 2010 From: vaibhav.bedia at ti.com (Bedia, Vaibhav) Date: Thu, 16 Dec 2010 17:11:09 +0530 Subject: [PATCH] ASoC: Davinci: Replace use of IO_ADDRESS with ioremap() In-Reply-To: <4D09F439.9010404@mvista.com> References: <1292491018-24491-1-git-send-email-vaibhav.bedia@ti.com> <4D09F439.9010404@mvista.com> Message-ID: On Thursday, December 16, 2010 4:43 PM, Sergei Shtylyov wrote: [...] > > Why iounmap() what you've just failed to ioremap()? > Will fix this. [...] >> err_free_mem: > > Don't you forget to call clk_disable()/clk_put()? > Will fix this issue also as part of the patch. > release_mem_region(mem->start, (mem->end - mem->start) + 1); > err_release_data: Regards, Vaibhav From manjunath.hadli at ti.com Thu Dec 16 06:40:20 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Thu, 16 Dec 2010 18:10:20 +0530 Subject: [PATCH v6 7/7] davinci vpbe: Readme text for Dm6446 vpbe In-Reply-To: Message-ID: Murali, Will update the doc. Thank you, -Manju On Wed, Dec 15, 2010 at 20:49:00, Karicheri, Muralidharan wrote: > Manju, > > Could you review the Document? I think it is not updated to reflect the latest status: > > >+ Current status:- > >+ > >+ A build tested version of vpbe controller is available. > > I guess you have already tested this using the v4l2 driver. > > >+ v4l2 driver > >+ - A version is already developed which is to be cleaned up and > >+ unit > >tested > > Ditto. v4l2 driver is already tested, right? > > > Murali Karicheri > Software Design Engineer > Texas Instruments Inc. > Germantown, MD 20874 > > >-----Original Message----- > >From: linux-media-owner at vger.kernel.org [mailto:linux-media- > >owner at vger.kernel.org] On Behalf Of Manjunath Hadli > >Sent: Wednesday, December 15, 2010 4:12 AM > >To: LMML > >Cc: dlos; Mauro Carvalho Chehab; Hans Verkuil; Hadli, Manjunath > >Subject: [PATCH v6 7/7] davinci vpbe: Readme text for Dm6446 vpbe > > > >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 | 100 > >+++++++++++++++++++++++++ > > 1 files changed, 100 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..3ff2dc3 > >--- /dev/null > >+++ b/Documentation/video4linux/README.davinci-vpbe > >@@ -0,0 +1,100 @@ > >+ > >+ 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 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 sub device > >+ 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 connetected 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 ans setting of the > >external > >+ encoders is not present, and would be a part of the next patch series. > >+ > >+ 3. Venc subdevice > >+ 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. > >+ > >+ 4. osd subdevice > >+ Osd subdevice implements all osd layer management and hardware > >specific > >+ features. In the legacfy drivers (LSPxxx), the hardware specific > >features > >+ are configured through proprietary IOCTLs at the fb device interface. > >Since > >+ subdevices are going to support device nodes, application will be able > >+ to configure the hardware feature directly by opening the osd > >subdevice > >+ node and by calling the related IOCTL. So these proprietary IOCTLs are > >+ to be removed from the FB Device driver when doing up port of > >+ these > >drivers to > >+ mainline kernel. The V4L2 and FB device nodes supports only IOCTLS > >+ as > >per > >+ the associated spec. The rest of the IOCTLs are to be moved to osd and > >+ venc subdevices. > >+ > >+ Current status:- > >+ > >+ A build tested version of vpbe controller is available. > >+ > >+ Following are TBDs. > >+ > >+ vpbe display controller > >+ - review and modify the handling of 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. > >+ > >+ v4l2 driver > >+ - A version is already developed which is to be cleaned up and > >+ unit > >tested > >+ > >+ FB drivers > >+ - Add support for fbdev drivers.- Ready and part of subsequent patches. > >-- > >1.6.2.4 > > > >-- > >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 sshtylyov at mvista.com Thu Dec 16 06:49:19 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Thu, 16 Dec 2010 15:49:19 +0300 Subject: [PATCH] ASoC: Davinci: Replace use of IO_ADDRESS with ioremap() In-Reply-To: References: <1292491018-24491-1-git-send-email-vaibhav.bedia@ti.com> <4D09F439.9010404@mvista.com> Message-ID: <4D0A0ACF.2010901@mvista.com> Hello. On 16-12-2010 14:41, Bedia, Vaibhav wrote: >>> err_free_mem: >> Don't you forget to call clk_disable()/clk_put()? > Will fix this issue also as part of the patch. No, this is different issue -- please submit another patch. >> release_mem_region(mem->start, (mem->end - mem->start) + 1); >> err_release_data: > Regards, > Vaibhav WBR, Sergei From manjunath.hadli at ti.com Thu Dec 16 07:54:16 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 16 Dec 2010 19:24:16 +0530 Subject: [PATCH v7 0/8] davinci vpbe: dm6446 v4l2 driver Message-ID: <1292507656-30143-1-git-send-email-manjunath.hadli@ti.com> version7 : addressed Murali's and Sergei's comments on: 1. Readme cleanup. 2. Seperate patch for platform dependent and board specific files. Manjunath Hadli (8): 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: board specific additions davinci vpbe: platform specific additions davinci vpbe: Build infrastructure for VPBE driver davinci vpbe: Readme text for Dm6446 vpbe Documentation/video4linux/README.davinci-vpbe | 93 ++ arch/arm/mach-davinci/board-dm644x-evm.c | 79 +- arch/arm/mach-davinci/dm644x.c | 164 ++- arch/arm/mach-davinci/include/mach/dm644x.h | 4 + drivers/media/video/davinci/Kconfig | 22 + drivers/media/video/davinci/Makefile | 2 + drivers/media/video/davinci/vpbe.c | 837 ++++++++++ drivers/media/video/davinci/vpbe_display.c | 2099 +++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd.c | 1211 ++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++ drivers/media/video/davinci/vpbe_venc.c | 574 +++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++ include/media/davinci/vpbe.h | 186 +++ include/media/davinci/vpbe_display.h | 146 ++ include/media/davinci/vpbe_osd.h | 397 +++++ include/media/davinci/vpbe_types.h | 93 ++ include/media/davinci/vpbe_venc.h | 38 + 17 files changed, 6504 insertions(+), 19 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 Thu Dec 16 07:54:31 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 16 Dec 2010 19:24:31 +0530 Subject: [PATCH v7 1/8] davinci vpbe: V4L2 display driver for DM644X SoC Message-ID: <1292507671-30604-1-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 is 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 | 2099 ++++++++++++++++++++++++++++ include/media/davinci/vpbe_display.h | 146 ++ include/media/davinci/vpbe_types.h | 93 ++ 3 files changed, 2338 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..a29a2a0 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_display.c @@ -0,0 +1,2099 @@ +/* + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#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 +#include "vpbe_venc_regs.h" + + +#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" + +static int debug; +static u32 video2_numbuffers = 3; +static u32 video3_numbuffers = 3; + +#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) +#define VPBE_DEFAULT_NUM_BUFS 3 + +static u32 video2_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; +static u32 video3_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; + +module_param(video2_numbuffers, uint, S_IRUGO); +module_param(video3_numbuffers, uint, S_IRUGO); +module_param(video2_bufsize, uint, S_IRUGO); +module_param(video3_bufsize, uint, S_IRUGO); +module_param(debug, int, 0644); + +static struct buf_config_params display_buf_config_params = { + .min_numbuffers = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[0] = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[1] = VPBE_DEFAULT_NUM_BUFS, + .min_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .min_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, +}; + +static struct vpbe_device *vpbe_dev; +static struct osd_state *osd_device; +static int vpbe_display_nr[] = { 2, 3 }; + +static struct v4l2_capability vpbe_display_videocap = { + .driver = VPBE_DISPLAY_DRIVER, + .bus_info = "platform", + .version = VPBE_DISPLAY_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, +}; + +static u8 layer_first_int[VPBE_DISPLAY_MAX_DEVICES]; + +__iomem void *reg_base_venc; + +static int venc_is_second_field() +{ + u32 val; + val = __raw_readl(reg_base_venc + VENC_VSTAT); + return ((val & VENC_VSTAT_FIDST) + == VENC_VSTAT_FIDST); +} + +/* + * vpbe_display_isr() + * ISR function. It changes status of the displayed buffer, takes next buffer + * from the queue and sets its address in VPBE registers + */ +static void vpbe_display_isr(unsigned int event, void *disp_obj) +{ + unsigned long jiffies_time = get_jiffies_64(); + struct timeval timevalue; + int i, fid; + unsigned long addr = 0; + struct vpbe_display_obj *layer = NULL; + struct vpbe_display *disp_dev = (struct vpbe_display *)disp_obj; + + /* Convert time represention from jiffies to timeval */ + jiffies_to_timeval(jiffies_time, &timevalue); + + 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; + /* Check the field format */ + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && + (event & OSD_END_OF_FRAME)) { + /* Progressive mode */ + if (layer_first_int[i]) + layer_first_int[i] = 0; + continue; + /* + * Mark status of the cur_frm to + * done and unlock semaphore on it + */ + + if (layer->cur_frm != layer->next_frm) { + layer->cur_frm->ts = timevalue; + 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; + } + /* Get the next buffer from buffer queue */ + spin_lock(&disp_dev->dma_queue_lock); + if (!list_empty(&layer->dma_queue)) { + layer->next_frm = + list_entry(layer->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove that buffer from the buffer queue */ + list_del(&layer->next_frm->queue); + /* Mark status of the buffer as 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_dev->cbcr_ofst); + } + spin_unlock(&disp_dev->dma_queue_lock); + } else { + /* + * Interlaced mode + * If it is first interrupt, ignore it + */ + if (layer_first_int[i]) { + layer_first_int[i] = 0; + return; + } + + layer->field_id ^= 1; + if (event & OSD_FIRST_FIELD) + fid = 0; + else if (event & OSD_SECOND_FIELD) + fid = 1; + else + return; + + /* + * If field id does not match with stored + * field id + */ + if (fid != layer->field_id) { + /* Make them in sync */ + if (0 == fid) + layer->field_id = fid; + + return; + } + /* + * device field id and local field id are + * in sync. If this is even field + */ + if (0 == fid) { + if (layer->cur_frm == layer->next_frm) + continue; + /* + * one frame is displayed If next frame is + * available, release cur_frm and move on + * copy frame display time + */ + layer->cur_frm->ts = timevalue; + /* Change status of the cur_frm */ + layer->cur_frm->state = VIDEOBUF_DONE; + /* unlock semaphore on cur_frm */ + wake_up_interruptible(&layer->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + layer->cur_frm = layer->next_frm; + } else if (1 == fid) { /* odd field */ + + if (list_empty(&layer->dma_queue) + || (layer->cur_frm != layer->next_frm)) + continue; + + /* + * one field is displayed configure + * the next frame if it is available + * otherwise hold on current frame + * Get next from the buffer queue + */ + spin_lock(&disp_dev->dma_queue_lock); + 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); + + /* 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_dev->cbcr_ofst); + spin_unlock(&disp_dev->dma_queue_lock); + } + } + } +} + +/* interrupt service routine */ +static irqreturn_t venc_isr(int irq, void *arg) +{ + static unsigned last_event; + unsigned event = 0; + + if (venc_is_second_field()) + 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; + + vpbe_display_isr(event, arg); + 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_display_obj *layer = fh->layer; + unsigned long addr; + int ret = 0; + + 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_display_obj *layer = fh->layer; + int buf_size; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); + + *size = layer->pix_fmt.sizeimage; + buf_size = + display_buf_config_params.layer_bufsize[layer->device_id]; + /* + * For MMAP, limit the memory allocation as per bootarg + * configured buffer size + */ + if (V4L2_MEMORY_MMAP == layer->memory) + if (*size > buf_size) + *size = buf_size; + + /* Store number of buffers allocated in numbuffer member */ + if (*count < display_buf_config_params.min_numbuffers) + *count = layer->numbuffers = + display_buf_config_params.numbuffers[layer->device_id]; + + 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_display_obj *layer = fh->layer; + struct vpbe_display *disp = fh->disp_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_display_obj *layer = fh->layer; + + 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_display_obj* +_vpbe_display_get_other_win(struct vpbe_display *disp_dev, + struct vpbe_display_obj *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_video_display_params(struct vpbe_display *disp_dev, + struct vpbe_display_obj *layer) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + unsigned long addr; + int ret = 0; + + 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_display_obj *otherlayer = + _vpbe_display_get_other_win(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_display_obj *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; + int h_scale = 0, v_scale = 0, h_exp = 0, v_exp = 0, temp; + 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)) { + temp = (cfg->xsize * + VPBE_DISPLAY_H_EXP_RATIO_N) / + VPBE_DISPLAY_H_EXP_RATIO_D; + if (temp <= expected_xsize) { + h_exp = 1; + cfg->xsize = temp; + } + } + } + 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)) { + temp = (cfg->ysize * + VPBE_DISPLAY_V_EXP_RATIO_N) / + VPBE_DISPLAY_V_EXP_RATIO_D; + if (temp <= expected_ysize) { + v_exp = 1; + cfg->ysize = temp; + } + } + } + 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_display_obj *layer, + int top, int left) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + + cfg->xpos = cfg->ypos = 0; + if (left + cfg->xsize <= vpbe_dev->current_timings.xres) + cfg->xpos = left; + if (top + cfg->ysize <= vpbe_dev->current_timings.yres) + cfg->ypos = top; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "new xpos = %d, ypos = %d\n", + cfg->xpos, cfg->ypos); +} + +static int vpbe_disp_check_window_params(struct vpbe_display *disp_dev, + struct v4l2_rect *c) +{ + if ((c->width == 0) + || ((c->width + c->left) > vpbe_dev->current_timings.xres) + || (c->height == 0) + || ((c->height + c->top) > vpbe_dev->current_timings.yres)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid crop values\n"); + return -1; + } + if ((c->height & 0x1) && (vpbe_dev->current_timings.interlaced)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "window height must be even for interlaced display\n"); + return -1; + } + return 0; +} + +/** + * 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. If application likes to add pads at the end of each line and + * end of the buffer , it can set bytesperline to line size and sizeimage to + * bytesperline * height of the buffer. If driver fills zero for active + * video width and height, and has requested user bytesperline and sizeimage, + * width and height is adjusted to maximum display limit or buffer width + * height which ever is lower + */ +static int vpbe_try_format(struct vpbe_display *disp_dev, + struct v4l2_pix_format *pixfmt, int check) +{ + int min_sizeimage, bpp, min_height = 1, min_width = 32, + max_width, max_height, user_info = 0; + + 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_ANY) { + 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->bytesperline) { + v4l2_err(&vpbe_dev->v4l2_dev, "bytesperline and width" + " cannot be zero\n"); + return -EINVAL; + } + + /* if user provided bytesperline, it must provide sizeimage as well */ + if (pixfmt->bytesperline && !pixfmt->sizeimage) { + v4l2_err(&vpbe_dev->v4l2_dev, + "sizeimage must be non zero, when user" + " provides bytesperline\n"); + return -EINVAL; + } + + /* adjust bytesperline as per hardware - multiple of 32 */ + if (!pixfmt->width) + pixfmt->width = pixfmt->bytesperline / bpp; + + if (!pixfmt->bytesperline) + pixfmt->bytesperline = pixfmt->width * bpp; + else + user_info = 1; + pixfmt->bytesperline = ((pixfmt->bytesperline + 31) & ~31); + + if (pixfmt->width < min_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is less than minimum," + "input width = %d, min_width = %d\n", + pixfmt->width, min_width); + return -EINVAL; + } + pixfmt->width = min_width; + } + + if (pixfmt->width > max_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is more than maximum," + "input width = %d, max_width = %d\n", + pixfmt->width, max_width); + return -EINVAL; + } + pixfmt->width = max_width; + } + + /* + * If height is zero, then atleast we need to have sizeimage + * to calculate height + */ + if (!pixfmt->height) { + if (user_info) { + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) { + /* + * for NV12 format, sizeimage is y-plane size + * + CbCr plane which is half of y-plane + */ + pixfmt->height = pixfmt->sizeimage / + (pixfmt->bytesperline + + (pixfmt->bytesperline >> 1)); + } else + pixfmt->height = pixfmt->sizeimage/ + pixfmt->bytesperline; + } + } + + if (pixfmt->height > max_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is more than maximum," + "input height = %d, max_height = %d\n", + pixfmt->height, max_height); + return -EINVAL; + } + pixfmt->height = max_height; + } + + if (pixfmt->height < min_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is less than minimum," + "input height = %d, min_height = %d\n", + pixfmt->height, min_height); + return -EINVAL; + } + pixfmt->height = min_width; + } + + /* if user has not provided bytesperline calculate it based on width */ + if (!user_info) + pixfmt->bytesperline = (((pixfmt->width * bpp) + 31) & ~31); + + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + min_sizeimage = pixfmt->bytesperline * pixfmt->height + + (pixfmt->bytesperline * pixfmt->height >> 1); + else + min_sizeimage = pixfmt->bytesperline * pixfmt->height; + + if (pixfmt->sizeimage < min_sizeimage) { + if (check && user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, "sizeimage is less, %d\n", + min_sizeimage); + return -EINVAL; + } + pixfmt->sizeimage = min_sizeimage; + } + 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_display_obj *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_display_obj *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_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id); + *cap = vpbe_display_videocap; + + return 0; +} + +static int vpbe_display_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct vpbe_display *disp_dev = video_drvdata(file); + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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) { + struct v4l2_rect *rect = &crop->c; + + if (rect->top < 0 || rect->left < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + + if (vpbe_disp_check_window_params(disp_dev, rect)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + 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); + + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + return ret; +} + +static int vpbe_display_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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) { + struct v4l2_rect *rect = &crop->c; + if (ret) + return ret; + 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; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + ret = -EINVAL; + } + + return ret; +} + +static int vpbe_display_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + + 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_display_obj *layer = fh->layer; + + 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) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Fill in the information about format */ + *pixfmt = layer->pix_fmt; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + + 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_display_obj *layer = fh->layer; + 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 > 0) { + 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 if (index == 1) { + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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; + } else { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* 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; + + /* Change of the default pixel format for both video windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { + struct vpbe_display_obj *otherlayer; + cfg->pixfmt = PIXFMT_NV12; + otherlayer = _vpbe_display_get_other_win(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); + + /* verify if readback values are as expected */ + if (layer->pix_fmt.width != cfg->xsize || + layer->pix_fmt.height != cfg->ysize || + layer->pix_fmt.bytesperline != layer->layer_info. + config.line_length || + (cfg->interlaced + && layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || + (!cfg->interlaced && layer->pix_fmt.field + != V4L2_FIELD_NONE)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "mismatch:layer conf params:\n"); + return -EINVAL; + } + } + + return 0; +} + +static int vpbe_display_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Check for valid field format */ + return vpbe_try_format(disp_dev, pixfmt, 0); + } + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; +} + +/** + * 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_display_obj *layer = fh->layer; + int ret = 0; + + 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; + } + } + 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) +{ + 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) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_outputs) { + 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_display_obj *layer = fh->layer; + int ret = 0; + + 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) { + 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 ret; +} + +/** + * 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) +{ + 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) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_dv_presets) { + 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 ret; +} + +/** + * 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_display_obj *layer = fh->layer; + int ret = 0; + + 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) { + 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 ret; +} + +/** + * 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) +{ + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + + 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_video_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_first_int[layer->device_id] = 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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_display_obj *layer = fh->layer; + + 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) +{ + unsigned int err = 0; + struct vpbe_fh *fh = filep->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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; +} + +static int vpbe_display_cfg_layer_default(enum vpbe_display_device_id id, + struct vpbe_display *disp_dev) +{ + int err = 0; + struct osd_layer_config *layer_config; + struct vpbe_display_obj *layer = disp_dev->dev[id]; + struct osd_layer_config *cfg = &layer->layer_info.config; + + /* 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"); + return -EBUSY; + } + + layer_config = cfg; + /* Set the default image and crop values */ + layer_config->pixfmt = PIXFMT_YCbCrI; + layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY; + layer->pix_fmt.bytesperline = layer_config->line_length = + vpbe_dev->current_timings.xres * 2; + + layer->pix_fmt.width = layer_config->xsize = + vpbe_dev->current_timings.xres; + layer->pix_fmt.height = layer_config->ysize = + vpbe_dev->current_timings.yres; + layer->pix_fmt.sizeimage = + layer->pix_fmt.bytesperline * layer->pix_fmt.height; + layer_config->xpos = 0; + layer_config->ypos = 0; + layer_config->interlaced = vpbe_dev->current_timings.interlaced; + + /* + * turn off ping-pong buffer and field inversion to fix + * the image shaking problem in 1080I mode + */ + + if (cfg->interlaced) + layer->pix_fmt.field = V4L2_FIELD_INTERLACED; + else + layer->pix_fmt.field = V4L2_FIELD_NONE; + + err = osd_device->ops.set_layer_config(osd_device, + layer->layer_info.id, + layer_config); + if (err < 0) { + /* Couldn't set layer */ + v4l2_err(&vpbe_dev->v4l2_dev, + "Display Manager failed to set osd layer\n"); + return -EBUSY; + } + + return 0; +} + +/* + * 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) +{ + int minor = iminor(file->f_path.dentry->d_inode); + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer; + struct vpbe_fh *fh = NULL; + int found = -1; + int i = 0; + + /* Check for valid minor number */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + layer = disp_dev->dev[i]; + if (minor == layer->video_dev->minor) { + found = i; + break; + } + } + + /* If not found, return error no device */ + if (0 > found) { + v4l2_err(&vpbe_dev->v4l2_dev, "device not found\n"); + return -ENODEV; + } + + /* 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) { + /* Configure the default values for the layer */ + if (vpbe_display_cfg_layer_default(layer->device_id, + disp_dev)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to configure video layer" + " for id = %d\n", layer->device_id); + 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_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); + /* If this is doing IO and other layer are not closed */ + if ((layer->usrs != 1) && fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "Close other instances\n"); + return -EAGAIN; + } + + /* 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_display_obj *otherlayer; + otherlayer = + _vpbe_display_get_other_win(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); + + if (strcmp("vpbe_controller", pdev->name) == 0) + vpbe_dev = platform_get_drvdata(pdev); + + if (strcmp("vpbe-osd", pdev->name) == 0) + osd_device = platform_get_drvdata(pdev); + + return 0; +} + +/*Configure the channels, buffer size */ +static int init_vpbe_layer_objects(int i) +{ + int free_buffer_index; + + /* Default number of buffers should be 3 */ + if ((video2_numbuffers > 0) && + (video2_numbuffers < display_buf_config_params.min_numbuffers)) + video2_numbuffers = display_buf_config_params.min_numbuffers; + if ((video3_numbuffers > 0) && + (video3_numbuffers < display_buf_config_params.min_numbuffers)) + video3_numbuffers = display_buf_config_params.min_numbuffers; + + /* + * Set buffer size to min buffers size if invalid + * buffer size is given + */ + if (video2_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]) + video2_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]; + + if (video3_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]) + video3_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]; + + /* set number of buffers, they could come from boot/args */ + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_0] = + video2_numbuffers; + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_1] = + video3_numbuffers; + + if (display_buf_config_params.numbuffers[0] == 0) + printk(KERN_ERR "no vid2 buffer allocated\n"); + if (display_buf_config_params.numbuffers[1] == 0) + printk(KERN_ERR "no vid3 buffer allocated\n"); + free_buffer_index = display_buf_config_params.numbuffers[i - 1]; + + 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 __init int vpbe_display_probe(struct platform_device *pdev) +{ + int i, j = 0, k, err = 0; + struct vpbe_display *disp_dev; + struct video_device *vbd = NULL; + struct vpbe_display_obj *vpbe_display_layer = NULL; + struct resource *res; + 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; + } + + /* Allocate memory for four plane display objects */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + disp_dev->dev[i] = + kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL); + /* If memory allocation fails, return error */ + if (!disp_dev->dev[i]) { + printk(KERN_ERR "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + spin_lock_init(&disp_dev->dev[i]->irqlock); + mutex_init(&disp_dev->dev[i]->opslock); + } + spin_lock_init(&disp_dev->dma_queue_lock); + + err = init_vpbe_layer_objects(i); + if (err) { + printk(KERN_ERR "Error initializing vpbe display\n"); + return err; + } + + /* + * 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, NULL, + vpbe_device_get); + if (err < 0) + return err; + + /* Initialize the vpbe display controller */ + if (NULL != vpbe_dev->ops.initialize) { + err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev); + if (err) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error initing vpbe\n"); + err = -ENOMEM; + goto probe_out; + } + } + + /* check the name of davinci device */ + if (vpbe_dev->cfg->module_name != NULL) + strcpy(vpbe_display_videocap.card, + vpbe_dev->cfg->module_name); + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[i]; + /* Allocate memory for video device */ + vbd = video_device_alloc(); + if (vbd == NULL) { + for (j = 0; j < i; j++) { + video_device_release( + disp_dev->dev[j]->video_dev); + } + v4l2_err(&vpbe_dev->v4l2_dev, "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + /* Initialize field of video device */ + vbd->release = video_device_release; + vbd->fops = &vpbe_fops; + vbd->ioctl_ops = &vpbe_ioctl_ops; + vbd->minor = -1; + vbd->v4l2_dev = &vpbe_dev->v4l2_dev; + vbd->lock = &vpbe_display_layer->opslock; + + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { + vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); + vbd->current_norm = + 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); + + /* Set video_dev to the video device */ + vpbe_display_layer->video_dev = vbd; + vpbe_display_layer->device_id = i; + + vpbe_display_layer->layer_info.id = + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); + if (display_buf_config_params.numbuffers[i] == 0) + vpbe_display_layer->memory = V4L2_MEMORY_USERPTR; + else + vpbe_display_layer->memory = V4L2_MEMORY_MMAP; + + /* Initialize field of the display layer objects */ + vpbe_display_layer->usrs = 0; + vpbe_display_layer->io_usrs = 0; + vpbe_display_layer->started = 0; + + /* Initialize prio member of layer object */ + v4l2_prio_init(&vpbe_display_layer->prio); + + /* Register video device */ + v4l2_info(&vpbe_dev->v4l2_dev, + "Trying to register VPBE display device.\n"); + v4l2_info(&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, + vpbe_display_nr[i]); + if (err) + goto probe_out; + /* set the driver data in platform device */ + platform_set_drvdata(pdev, disp_dev); + video_set_drvdata(vpbe_display_layer->video_dev, disp_dev); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to get VENC register address map\n"); + err = -ENODEV; + goto probe_out; + } + + reg_base_venc = ioremap_nocache(res->start, resource_size(res)); + if (!reg_base_venc) { + v4l2_err(&vpbe_dev->v4l2_dev, "Unable to map VENC IO space\n"); + err = -ENODEV; + goto probe_out; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + v4l2_err(&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(&vpbe_dev->v4l2_dev, "Unable to request interrupt\n"); + err = -ENODEV; + goto probe_out; + } + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); + return 0; +probe_out: + kfree(disp_dev); + + for (k = 0; k < j; k++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[k]; + /* Unregister video device */ + video_unregister_device(vpbe_display_layer->video_dev); + /* Release video device */ + video_device_release(vpbe_display_layer->video_dev); + vpbe_display_layer->video_dev = NULL; + } + return err; +} + +/* + * vpbe_display_remove() + * It un-register hardware layer from V4L2 driver + */ +static int vpbe_display_remove(struct platform_device *pdev) +{ + int i; + struct vpbe_display_obj *vpbe_display_layer; + struct vpbe_display *disp_dev = platform_get_drvdata(pdev); + struct resource *res; + + 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); + + vpbe_display_layer->video_dev = NULL; + } + 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 __init int vpbe_display_init(void) +{ + int err = 0; + + 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 DMXXX 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..90ad066 --- /dev/null +++ b/include/media/davinci/vpbe_display.h @@ -0,0 +1,146 @@ +/* + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#ifndef VPBE_DISPLAY_H +#define VPBE_DISPLAY_H + +#ifdef __KERNEL__ + +/* 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_display_obj { + /* number of buffers in fbuffers */ + unsigned int numbuffers; + /* 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; +}; + +/* 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_display_obj *dev[VPBE_DISPLAY_MAX_DEVICES]; +}; + +/* File handle structure */ +struct vpbe_fh { + /* vpbe device structure */ + struct vpbe_display *disp_dev; + /* pointer to layer object for opened device */ + struct vpbe_display_obj *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]; +}; + +static int venc_is_second_field(void); +#endif /* end of __KERNEL__ */ +#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..a4b8585 --- /dev/null +++ b/include/media/davinci/vpbe_types.h @@ -0,0 +1,93 @@ +/* + * 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; either version 2 of the License. + * + * 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_types { + DM644X_VPBE = 1, + DM355_VPBE, + DM365_VPBE, +}; + +/* 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 Thu Dec 16 07:54:49 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 16 Dec 2010 19:24:49 +0530 Subject: [PATCH v7 2/8] davinci vpbe: VPBE display driver Message-ID: <1292507689-31220-1-git-send-email-manjunath.hadli@ti.com> This patch implements the coe functionality of the dislay driver, mainly controlling the VENC and other encoders, and acting as the one point interface for the man V4L2 driver.This implements the cre 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 | 837 ++++++++++++++++++++++++++++++++++++ include/media/davinci/vpbe.h | 186 ++++++++ 2 files changed, 1023 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..751370f --- /dev/null +++ b/drivers/media/video/davinci/vpbe.c @@ -0,0 +1,837 @@ +/* + * 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; either version 2 of the License. + * + * 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 + + +#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 struct osd_state *osd_device; +static struct venc_platform_data *venc_device; +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(ef_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_display_config *vpbe_config = vpbe_dev->cfg; + int index = vpbe_dev->current_sd_index; + + return ((index == 0) ? &vpbe_config->venc : + &vpbe_config->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_display_config *vpbe_config, + int index) +{ + char *encoder_name = vpbe_config->outputs[index].subdev_name; + int i; + + /* Venc is always first */ + if (!strcmp(encoder_name, vpbe_config->venc.module_name)) + return 0; + + for (i = 0; i < vpbe_config->num_ext_encoders; i++) { + if (!strcmp(encoder_name, + vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; + int temp_index = output->index; + + if (temp_index >= vpbe_config->num_outputs) + return -EINVAL; + + *output = vpbe_config->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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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_display_config *cfg = vpbe_dev->cfg; + struct vpbe_enc_mode_info var; + int curr_output = vpbe_dev->current_out_index, 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 vpbe_display_config *vpbe_config = vpbe_dev->cfg; + struct encoder_config_info *curr_enc_info = + vpbe_current_encoder_info(vpbe_dev); + int ret = 0, enc_out_index = 0, sd_index; + + if (index >= vpbe_config->num_outputs) + return -EINVAL; + + mutex_lock(&vpbe_dev->lock); + + sd_index = vpbe_dev->current_sd_index; + enc_out_index = vpbe_config->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, + vpbe_config->outputs[index].subdev_name)) { + /* Need to switch the encoder at the output */ + sd_index = vpbe_find_encoder_sd_index(vpbe_config, 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, + vpbe_config->outputs[index].default_mode); + if (!ret) { + 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_display_config *vpbe_config = vpbe_dev->cfg; + int i, ret = 0; + + for (i = 0; i < vpbe_config->num_outputs; i++) { + if (!strcmp(def_output, + vpbe_config->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_display_config *vpbe_config = vpbe_dev->cfg; + int sd_index = vpbe_dev->current_sd_index; + int out_index = vpbe_dev->current_out_index, ret; + + + if (!(vpbe_config->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) { + 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_display_config *vpbe_config = vpbe_dev->cfg; + int out_index = vpbe_dev->current_out_index; + struct vpbe_output *output = &vpbe_config->outputs[out_index]; + int i, j = 0; + + 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_display_config *vpbe_config = vpbe_dev->cfg; + int sd_index = vpbe_dev->current_sd_index, out_index = + vpbe_dev->current_out_index, ret; + + if (!(vpbe_config->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) { + 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_display_config *vpbe_config = vpbe_dev->cfg; + int out_index = vpbe_dev->current_out_index, ret = 0, i; + struct vpbe_enc_mode_info *preset_mode = NULL; + struct v4l2_dv_preset dv_preset; + + if ((NULL == mode_info) || (NULL == mode_info->name)) + return -EINVAL; + + for (i = 0; i < vpbe_config->outputs[out_index].num_modes; i++) { + if (!strcmp(mode_info->name, + vpbe_config->outputs[out_index].modes[i].name)) { + preset_mode = &vpbe_config->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); + + if (!ret) { + 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); + if (strcmp("vpbe-osd", pdev->name) == 0) + osd_device = platform_get_drvdata(pdev); + if (strcmp("vpbe-venc", pdev->name) == 0) + venc_device = dev_get_platdata(&pdev->dev); + + 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; + int i, ret = 0, num_encoders; + struct i2c_adapter *i2c_adap; + int output_index; + int err; + + /* + * 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, NULL, + 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 */ + 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->module_name, + &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. + */ +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; + /* disaable 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 __init int vpbe_probe(struct platform_device *pdev) +{ + struct vpbe_display_config *vpbe_config; + struct vpbe_device *vpbe_dev; + + int ret = -EINVAL; + + if (NULL == pdev->dev.platform_data) { + v4l2_err(pdev->dev.driver, "Unable to get vpbe config\n"); + return -ENODEV; + } + + if (pdev->dev.platform_data == NULL) { + v4l2_err(pdev->dev.driver, "No platform data\n"); + return -ENODEV; + } + vpbe_config = pdev->dev.platform_data; + + if (!vpbe_config->module_name[0] || + !vpbe_config->osd.module_name[0] || + !vpbe_config->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 = vpbe_config; + vpbe_dev->ops = vpbe_dev_ops; + vpbe_dev->pdev = &pdev->dev; + + if (vpbe_config->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..c8853f2 --- /dev/null +++ b/include/media/davinci/vpbe.h @@ -0,0 +1,186 @@ +/* + * 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; either version 2 of the License. + * + * 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 + +/* 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_display_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_display_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; + + /* + * 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; +}; + +/* 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 Thu Dec 16 07:55:05 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 16 Dec 2010 19:25:05 +0530 Subject: [PATCH v7 3/8] davinci vpbe: OSD(On Screen Display) block Message-ID: <1292507705-31967-1-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 | 1211 +++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++++++ include/media/davinci/vpbe_osd.h | 397 +++++++++ 3 files changed, 1997 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..2599c83 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd.c @@ -0,0 +1,1211 @@ +/* + * 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; 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 + * + */ +#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 __raw_readl(osd->osd_base + offset); +} + +static inline u32 osd_write(struct osd_state *sd, u32 val, u32 offset) +{ + struct osd_state *osd = sd; + __raw_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 = __raw_readl(addr) | mask; + + __raw_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 = __raw_readl(addr) & ~mask; + + __raw_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 = (__raw_readl(addr) & ~mask) | (val & mask); + __raw_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) +{ + +#ifdef CONFIG_DM6446_FIELD_INV_WORKAROUND + 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; + } +#endif + 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) +{ + int bmp_reg, bmp_offset, bmp_mask, bmp_shift; + static const int map_1bpp[] = { 0, 15 }; + static const int map_2bpp[] = { 0, 5, 10, 15 }; + + 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]; + unsigned long flags; + struct osd_layer_config *cfg = &win->lconfig; + + 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]; + unsigned long flags; + struct osd_layer_config *cfg = &win->lconfig; + + 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 = 0; + + /* 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; + int reject_config; + unsigned long flags; + + 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_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; + osd->vpbe_type = (enum vpbe_types)pdev->dev.platform_data; + 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) +{ + /* Register the driver */ + 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..9cd3839 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd_regs.h @@ -0,0 +1,389 @@ +/* + * 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; either version 2 of the License. + * + * 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 + +enum vpbe_soc_type { + DM644x = 0, + DM35x, + DM36x, +}; + +struct vpbe_osd_platform_data { + enum vpbe_soc_type soc; +}; + +#define DM644X_OSD_REG_BASE 0x01C72600 +#define DM644X_VPBE_REG_BASE 0x01C72780 + +#define DM355_VPSSCLK_REG_BASE 0x01C70000 +#define DM355_OSD_REG_BASE 0x01C70200 + +#define DM365_OSD_REG_BASE 0x01C71C00 +#define DM365_ISP5_REG_BASE 0x01C70000 + +#define OSD_REG_SIZE 0x00000100 + +/* SYS register addresses */ +#define SYS_VPSS_CLKCTL 0x01C40044 + +/* 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..9549f3e --- /dev/null +++ b/include/media/davinci/vpbe_osd.h @@ -0,0 +1,397 @@ +/* + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef _OSD_H +#define _OSD_H + +#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; +}; +/* OSD events. Should match with that in vpbe_venc.h */ +#define OSD_END_OF_FRAME 1 +#define OSD_FIRST_FIELD 2 +#define OSD_SECOND_FIELD 4 + +/* 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; + +/* TBD. Some of these operations here are to be removed and implemented + * as a ioctl call on the osd sub device node. Right now just trying to + * make this work. + */ +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_types 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; +}; + + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Dec 16 07:55:20 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 16 Dec 2010 19:25:20 +0530 Subject: [PATCH v7 4/8] davinci vpbe: VENC( Video Encoder) implementation Message-ID: <1292507720-32340-1-git-send-email-manjunath.hadli@ti.com> This patch adds the VENC or the Video encoder, whichis responsible for the blending of al 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 extenal 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 | 574 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++++++++ include/media/davinci/vpbe_venc.h | 38 ++ 3 files changed, 801 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..fafb41a --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc.c @@ -0,0 +1,574 @@ +/* + * 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; 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 + */ +#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 __raw_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); + __raw_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); + + __raw_writel(val, venc->vdaccfg_reg); + + val = __raw_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) +{ + int ret = 0; + + 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: + ret = -EINVAL; + } + return ret; +} + +static void enabledigitaloutput(struct v4l2_subdev *sd, int benable) +{ + v4l2_dbg(debug, 2, sd, "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; + + 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; + + 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; + + 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; + + 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) +{ + int ret = 0; + + v4l2_dbg(debug, 1, sd, "venc_s_std_output\n"); + + if (norm & V4L2_STD_525_60) + ret = venc_set_ntsc(sd); + else if (norm & V4L2_STD_625_50) + ret = venc_set_pal(sd); + else + ret = -EINVAL; + return ret; +} + +static int venc_s_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *dv_preset) +{ + int ret = -EINVAL; + + 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 ret; +} + +static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct venc_state *venc = to_state(sd); + int max_output, lcd_out_index, ret = 0; + + v4l2_dbg(debug, 1, sd, "venc_s_routing\n"); + + max_output = 2 + venc->pdata->num_lcd_outputs; + lcd_out_index = 3; + + if (output >= max_output) + return -EINVAL; + + if (output < lcd_out_index) + ret = venc_set_dac(sd, output); + if (!ret) + venc->output = output; + return ret; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int venc_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + __iomem void *reg_base; + + if (reg->match.type == 2) + reg->val = venc_read(sd, reg->reg); + else if (reg->match.type == 3) { + reg_base = ioremap_nocache(0x01c70800, 0x80); + if (reg_base == NULL) + return -EINVAL; + reg->val = __raw_readl(reg_base + reg->reg); + iounmap(reg_base); + } else { + reg_base = ioremap_nocache(0x01c70000, 0x80); + if (reg_base == NULL) + return -1; + reg->val = __raw_readl(reg_base + reg->reg); + iounmap(reg_base); + } + return 0; +} +#endif + +static const struct v4l2_subdev_core_ops venc_core_ops = { +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = venc_g_register, +#endif +}; + +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 = 0; + + /* 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) +{ + /* Register the driver */ + 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..38e4818 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc_regs.h @@ -0,0 +1,189 @@ +/* + * 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; either version 2 of the License. + * + * 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 register base addresses */ +#define DM644X_VENC_REG_BASE 0x01C72400 +#define DM644X_VPBE_REG_BASE 0x01C72780 + +#define DM355_VENC_REG_BASE 0x01C70400 + +#define DM365_VENC_REG_BASE 0x01C71E00 +#define DM365_ISP5_REG_BASE 0x01C70000 + +#define DM3XX_VDAC_CONFIG 0x01C4002C +#define DM355_USB_PHY_CTRL 0x01c40034 + +/* 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..47a977c --- /dev/null +++ b/include/media/davinci/vpbe_venc.h @@ -0,0 +1,38 @@ +/* + * 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; either version 2 of the License. + * + * 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 + +#define VPBE_VENC_SUBDEV_NAME "vpbe-venc" + +/* venc events */ +#define VENC_END_OF_FRAME 1 +#define VENC_FIRST_FIELD 2 +#define VENC_SECOND_FIELD 4 + +struct venc_platform_data { + enum vpbe_types venc_type; + int (*setup_clock)(enum vpbe_enc_timings_type type, + __u64 mode); + /* Number of LCD outputs supported */ + int num_lcd_outputs; +}; + + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Thu Dec 16 07:55:37 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 16 Dec 2010 19:25:37 +0530 Subject: [PATCH v7 5/8] davinci vpbe: board specific additions Message-ID: <1292507737-32739-1-git-send-email-manjunath.hadli@ti.com> This patch implements tables for display timings,outputs and other board related functionalities. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- arch/arm/mach-davinci/board-dm644x-evm.c | 79 +++++++++++++++++++++++++----- 1 files changed, 66 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c index 34c8b41..e9b1243 100644 --- a/arch/arm/mach-davinci/board-dm644x-evm.c +++ b/arch/arm/mach-davinci/board-dm644x-evm.c @@ -166,18 +166,6 @@ static struct platform_device davinci_evm_nandflash_device = { .resource = davinci_evm_nandflash_resource, }; -static u64 davinci_fb_dma_mask = DMA_BIT_MASK(32); - -static struct platform_device davinci_fb_device = { - .name = "davincifb", - .id = -1, - .dev = { - .dma_mask = &davinci_fb_dma_mask, - .coherent_dma_mask = DMA_BIT_MASK(32), - }, - .num_resources = 0, -}; - static struct tvp514x_platform_data tvp5146_pdata = { .clk_polarity = 0, .hs_polarity = 1, @@ -606,8 +594,71 @@ static void __init evm_init_i2c(void) i2c_register_board_info(1, i2c_info, ARRAY_SIZE(i2c_info)); } +#define VENC_STD_ALL (V4L2_STD_NTSC | V4L2_STD_PAL) +/* venc standards timings */ +static struct vpbe_enc_mode_info vbpe_enc_std_timings[] = { + {"ntsc", VPBE_ENC_STD, {V4L2_STD_525_60}, 1, 720, 480, + {11, 10}, {30000, 1001}, 0x79, 0, 0x10, 0, 0, 0, 0}, + {"pal", VPBE_ENC_STD, {V4L2_STD_625_50}, 1, 720, 576, + {54, 59}, {25, 1}, 0x7E, 0, 0x16, 0, 0, 0, 0}, +}; + +/* venc dv preset timings */ +static struct vpbe_enc_mode_info vbpe_enc_preset_timings[] = { + {"480p59_94", VPBE_ENC_DV_PRESET, {V4L2_DV_480P59_94}, 0, 720, 480, + {1, 1}, {5994, 100}, 0x80, 0, 0x20, 0, 0, 0, 0}, + {"576p50", VPBE_ENC_DV_PRESET, {V4L2_DV_576P50}, 0, 720, 576, + {1, 1}, {50, 1}, 0x7E, 0, 0x30, 0, 0, 0, 0}, +}; + +/* + * The outputs available from VPBE + ecnoders. Keep the + * the order same as that of encoders. First that from venc followed by that + * from encoders. Index in the output refers to index on a particular encoder. + * Driver uses this index to pass it to encoder when it supports more than + * one output. Application uses index of the array to set an output. + */ +static struct vpbe_output dm644x_vpbe_outputs[] = { + { + .output = { + .index = 0, + .name = "Composite", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .std = VENC_STD_ALL, + .capabilities = V4L2_OUT_CAP_STD, + }, + .subdev_name = VPBE_VENC_SUBDEV_NAME, + .default_mode = "ntsc", + .num_modes = ARRAY_SIZE(vbpe_enc_std_timings), + .modes = vbpe_enc_std_timings, + }, + { + .output = { + .index = 1, + .name = "Component", + .type = V4L2_OUTPUT_TYPE_ANALOG, + .capabilities = V4L2_OUT_CAP_PRESETS, + }, + .subdev_name = VPBE_VENC_SUBDEV_NAME, + .default_mode = "480p59_94", + .num_modes = ARRAY_SIZE(vbpe_enc_preset_timings), + .modes = vbpe_enc_preset_timings, + }, +}; + +static struct vpbe_display_config vpbe_display_cfg = { + .module_name = "dm644x-vpbe-display", + .i2c_adapter_id = 1, + .osd = { + .module_name = VPBE_OSD_SUBDEV_NAME, + }, + .venc = { + .module_name = VPBE_VENC_SUBDEV_NAME, + }, + .num_outputs = ARRAY_SIZE(dm644x_vpbe_outputs), + .outputs = dm644x_vpbe_outputs, +}; static struct platform_device *davinci_evm_devices[] __initdata = { - &davinci_fb_device, &rtc_dev, }; @@ -620,6 +671,8 @@ davinci_evm_map_io(void) { /* setup input configuration for VPFE input devices */ dm644x_set_vpfe_config(&vpfe_cfg); + /* setup configuration for vpbe devices */ + dm644x_set_vpbe_display_config(&vpbe_display_cfg); dm644x_init(); } -- 1.6.2.4 From manjunath.hadli at ti.com Thu Dec 16 07:55:55 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 16 Dec 2010 19:25:55 +0530 Subject: [PATCH v7 6/8] davinci vpbe: platform specific additions Message-ID: <1292507755-664-1-git-send-email-manjunath.hadli@ti.com> This patch implements the overall device creation for the Video display driver Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- arch/arm/mach-davinci/dm644x.c | 164 ++++++++++++++++++++++++++- arch/arm/mach-davinci/include/mach/dm644x.h | 4 + 2 files changed, 162 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 5e5b0a7..e8b8e94 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -640,6 +640,142 @@ void dm644x_set_vpfe_config(struct vpfe_config *cfg) vpfe_capture_dev.dev.platform_data = cfg; } +static struct resource dm644x_osd_resources[] = { + { + .start = 0x01C72600, + .end = 0x01C72600 + 0x200, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 dm644x_osd_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device dm644x_osd_dev = { + .name = VPBE_OSD_SUBDEV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_osd_resources), + .resource = dm644x_osd_resources, + .dev = { + .dma_mask = &dm644x_osd_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = (void *)DM644X_VPBE, + }, +}; + +static struct resource dm644x_venc_resources[] = { + /* venc registers io space */ + { + .start = 0x01C72400, + .end = 0x01C72400 + 0x180, + .flags = IORESOURCE_MEM, + }, +}; + +static u64 dm644x_venc_dma_mask = DMA_BIT_MASK(32); + +#define VPSS_CLKCTL 0x01C40044 +static void __iomem *vpss_clkctl_reg; + +/* TBD. Check what VENC_CLOCK_SEL settings for HDTV and EDTV */ +static int dm644x_venc_setup_clock(enum vpbe_enc_timings_type type, __u64 mode) +{ + int ret = 0; + + if (NULL == vpss_clkctl_reg) + return -EINVAL; + if (type == VPBE_ENC_STD) { + __raw_writel(0x18, vpss_clkctl_reg); + } else if (type == VPBE_ENC_DV_PRESET) { + switch ((unsigned int)mode) { + case V4L2_DV_480P59_94: + case V4L2_DV_576P50: + __raw_writel(0x19, vpss_clkctl_reg); + break; + case V4L2_DV_720P60: + case V4L2_DV_1080I60: + case V4L2_DV_1080P30: + /* + * For HD, use external clock source since HD requires higher + * clock rate + */ + __raw_writel(0xa, vpss_clkctl_reg); + break; + default: + ret = -EINVAL; + break; + } + } else + ret = -EINVAL; + + return ret; +} + + +static inline u32 dm644x_reg_modify(void *reg, u32 val, u32 mask) +{ + u32 new_val = (__raw_readl(reg) & ~mask) | (val & mask); + __raw_writel(new_val, reg); + return new_val; +} + +static u64 vpbe_display_dma_mask = DMA_BIT_MASK(32); + +static struct resource dm644x_v4l2_disp_resources[] = { + { + .start = IRQ_VENCINT, + .end = IRQ_VENCINT, + .flags = IORESOURCE_IRQ, + }, + { + .start = 0x01C72400, + .end = 0x01C72400 + 0x180, + .flags = IORESOURCE_MEM, + }, + +}; +static struct platform_device vpbe_v4l2_display = { + .name = "vpbe-v4l2", + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_v4l2_disp_resources), + .resource = dm644x_v4l2_disp_resources, + .dev = { + .dma_mask = &vpbe_display_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; +struct venc_platform_data dm644x_venc_pdata = { + .venc_type = DM644X_VPBE, + .setup_clock = dm644x_venc_setup_clock, +}; + +static struct platform_device dm644x_venc_dev = { + .name = VPBE_VENC_SUBDEV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_venc_resources), + .resource = dm644x_venc_resources, + .dev = { + .dma_mask = &dm644x_venc_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = (void *)&dm644x_venc_pdata, + }, +}; + +static u64 dm644x_vpbe_dma_mask = DMA_BIT_MASK(32); + +static struct platform_device dm644x_vpbe_dev = { + .name = "vpbe_controller", + .id = -1, + .dev = { + .dma_mask = &dm644x_vpbe_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +void dm644x_set_vpbe_display_config(struct vpbe_display_config *cfg) +{ + dm644x_vpbe_dev.dev.platform_data = cfg; +} + /*----------------------------------------------------------------------*/ static struct map_desc dm644x_io_desc[] = { @@ -767,20 +903,36 @@ void __init dm644x_init(void) davinci_common_init(&davinci_soc_info_dm644x); } +static struct platform_device *dm644x_video_devices[] __initdata = { + &dm644x_vpss_device, + &dm644x_ccdc_dev, + &vpfe_capture_dev, + &dm644x_osd_dev, + &dm644x_venc_dev, + &dm644x_vpbe_dev, + &vpbe_v4l2_display, +}; + +static int __init dm644x_init_video(void) +{ + /* Add ccdc clock aliases */ + clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); + clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); + vpss_clkctl_reg = ioremap_nocache(VPSS_CLKCTL, 4); + platform_add_devices(dm644x_video_devices, + ARRAY_SIZE(dm644x_video_devices)); + return 0; +} + static int __init dm644x_init_devices(void) { if (!cpu_is_davinci_dm644x()) return 0; /* Add ccdc clock aliases */ - clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); - clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); platform_device_register(&dm644x_edma_device); platform_device_register(&dm644x_emac_device); - platform_device_register(&dm644x_vpss_device); - platform_device_register(&dm644x_ccdc_dev); - platform_device_register(&vpfe_capture_dev); - + dm644x_init_video(); return 0; } postcore_initcall(dm644x_init_devices); diff --git a/arch/arm/mach-davinci/include/mach/dm644x.h b/arch/arm/mach-davinci/include/mach/dm644x.h index 6fca568..bf7adcd 100644 --- a/arch/arm/mach-davinci/include/mach/dm644x.h +++ b/arch/arm/mach-davinci/include/mach/dm644x.h @@ -26,6 +26,9 @@ #include #include #include +#include +#include +#include #define DM644X_EMAC_BASE (0x01C80000) #define DM644X_EMAC_CNTRL_OFFSET (0x0000) @@ -43,5 +46,6 @@ void __init dm644x_init(void); void __init dm644x_init_asp(struct snd_platform_data *pdata); void dm644x_set_vpfe_config(struct vpfe_config *cfg); +void dm644x_set_vpbe_display_config(struct vpbe_display_config *cfg); #endif /* __ASM_ARCH_DM644X_H */ -- 1.6.2.4 From manjunath.hadli at ti.com Thu Dec 16 07:56:11 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 16 Dec 2010 19:26:11 +0530 Subject: [PATCH v7 7/8] davinci vpbe: Build infrastructure for VPBE driver Message-ID: <1292507771-992-1-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 | 22 ++++++++++++++++++++++ drivers/media/video/davinci/Makefile | 2 ++ 2 files changed, 24 insertions(+), 0 deletions(-) diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig index 6b19540..a7f11e7 100644 --- a/drivers/media/video/davinci/Kconfig +++ b/drivers/media/video/davinci/Kconfig @@ -91,3 +91,25 @@ 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" + 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" + select VIDEO_DM644X_VPBE + default y + help + Enables VPBE V4L2 Display driver on a DMXXX 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 Thu Dec 16 07:56:26 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Thu, 16 Dec 2010 19:26:26 +0530 Subject: [PATCH v7 8/8] davinci vpbe: Readme text for Dm6446 vpbe Message-ID: <1292507786-1270-1-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 vaibhav.bedia at ti.com Thu Dec 16 22:16:30 2010 From: vaibhav.bedia at ti.com (Bedia, Vaibhav) Date: Fri, 17 Dec 2010 09:46:30 +0530 Subject: [PATCH] ASoC: Davinci: Replace use of IO_ADDRESS with ioremap() In-Reply-To: <4D0A0ACF.2010901@mvista.com> References: <1292491018-24491-1-git-send-email-vaibhav.bedia@ti.com> <4D09F439.9010404@mvista.com> <4D0A0ACF.2010901@mvista.com> Message-ID: On Thursday, December 16, 2010 6:19 PM, Sergei Shtylyov wrote: > Hello. > > On 16-12-2010 14:41, Bedia, Vaibhav wrote: > >>>> err_free_mem: > >>> Don't you forget to call clk_disable()/clk_put()? > >> Will fix this issue also as part of the patch. > > No, this is different issue -- please submit another patch. > >>> release_mem_region(mem->start, (mem->end - mem->start) + 1); >>> err_release_data: > Seems like multiple issues can be fixed in the driver. Will send patches for those also. Regards, Vaibhav From bengardiner at nanometrics.ca Fri Dec 17 09:15:35 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 17 Dec 2010 10:15:35 -0500 Subject: [PATCH v6 0/5] da850-evm: add gpio-{keys, leds} for UI and BB expanders In-Reply-To: <87ei9kwa9w.fsf@deeprootsystems.com> References: <87tyild2ms.fsf@deeprootsystems.com> <8762ux49w7.fsf@deeprootsystems.com> <87ei9kwa9w.fsf@deeprootsystems.com> Message-ID: Hi Kevin, On Tue, Dec 14, 2010 at 12:10 PM, Kevin Hilman wrote: > Just pushed an updated version, and this time, I actually build tested for > davinci_all_defconfig and da8xx_omapl_defconfig. :) > > Sorry for the churn, I tested linux-davinci master; HEAD was c63d0f1df0284f2fcb28c0943b17848500d01515. Everything is working as expected. Thanks again for taking up this series along with the complications introduced by the cherry pick. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From sshtylyov at mvista.com Fri Dec 17 10:29:23 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 17 Dec 2010 19:29:23 +0300 Subject: [PATCH v7 5/8] davinci vpbe: board specific additions In-Reply-To: <1292507737-32739-1-git-send-email-manjunath.hadli@ti.com> References: <1292507737-32739-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <4D0B8FE3.3080502@mvista.com> Hello. Manjunath Hadli wrote: > This patch implements tables for display timings,outputs and > other board related functionalities. > Signed-off-by: Manjunath Hadli > Acked-by: Muralidharan Karicheri > Acked-by: Hans Verkuil [...] > diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c b/arch/arm/mach-davinci/board-dm644x-evm.c > index 34c8b41..e9b1243 100644 > --- a/arch/arm/mach-davinci/board-dm644x-evm.c > +++ b/arch/arm/mach-davinci/board-dm644x-evm.c [...] > @@ -620,6 +671,8 @@ davinci_evm_map_io(void) > { > /* setup input configuration for VPFE input devices */ > dm644x_set_vpfe_config(&vpfe_cfg); > + /* setup configuration for vpbe devices */ > + dm644x_set_vpbe_display_config(&vpbe_display_cfg); > dm644x_init(); > } This patch should *follow* the platform patch (where dm644x_set_vpbe_display_config() is defined), not precede it. WBR, Sergei From khilman at deeprootsystems.com Fri Dec 17 18:22:54 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 17 Dec 2010 16:22:54 -0800 Subject: [PATCH 1/2] da850: Support for TI's PRU SoftUART Emulation In-Reply-To: (Sekhar Nori's message of "Wed, 15 Dec 2010 16:48:37 +0530") References: <1291389108-25356-1-git-send-email-subhasish@mistralsolutions.com> <87ei9p6tgo.fsf@deeprootsystems.com> Message-ID: <87y67o53q9.fsf@deeprootsystems.com> "Nori, Sekhar" writes: > Hi Kevin, > > On Sat, Dec 11, 2010 at 06:01:19, Kevin Hilman wrote: >> Subhasish Ghosh writes: >> >> > The patch adds support for emulated UART controllers >> > on the programmable realtime unit (PRU) available on OMAPL138. >> > This defines the system resource requirements such as pin mux, >> > clock, iomem, interrupt etc and registers the platform device >> > as per the Linux driver model. >> > >> > Signed-off-by: Subhasish Ghosh >> >> You might consider setting your git user to use your Mistral address, so >> according to git, the author and the sign-off are the same person. >> Otherwise, git stats will report your personal address as the author and >> your company address as the signoff. >> >> Also, please Cc linux-arm-kernel at lists.infradead.org on all davinci >> kernel patches. >> >> Otherwise, this patch is looking ok (after addressing alignment issue >> reported by Sergei.) > > Is it OK to merge the platform data before the driver > is merged? Hmm, good point. It's probably not worth merging until we can also see the driver, since more than likely, the data between device code and driver will have to change before the driver merges. Let's wait on this to see the driver too. Kevin From nsekhar at ti.com Mon Dec 20 04:22:11 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Mon, 20 Dec 2010 15:52:11 +0530 Subject: [PATCH v4 1/2 (REPOST)] davinci: am18x/da850/omap-l138: add support for higher speed grades Message-ID: <1292840532-14303-1-git-send-email-nsekhar@ti.com> AM18x/DA850/OMAP-L138 SoCs have variants that can operate at a maximum of 456 MHz at 1.3V operating point. Also the 1.2V operating point has a variant that can support a maximum of 375 MHz. This patch adds three new OPPs (456 MHz, 408 MHz and 372 MHz) to the list of DA850 OPPs. Not all silicon is qualified to run at higher speeds and unfortunately the maximum speed the chip can support can only be determined from the label on the package (not software readable). Because of this, we depend on the maximum speed grade information to be provided to us in some board specific way. The board informs the maximum speed grade information to the SoC by calling the da850_set_max_speed() function. Signed-off-by: Sekhar Nori --- re-posting with linux-arm-kernel included in CC. arch/arm/mach-davinci/da850.c | 75 ++++++++++++++++++++++------ arch/arm/mach-davinci/include/mach/da8xx.h | 7 +++ 2 files changed, 67 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 63916b9..78b5ae2 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -830,8 +830,7 @@ static void da850_set_async3_src(int pllnum) * According to the TRM, minimum PLLM results in maximum power savings. * The OPP definitions below should keep the PLLM as low as possible. * - * The output of the PLLM must be between 400 to 600 MHz. - * This rules out prediv of anything but divide-by-one for 24Mhz OSC input. + * The output of the PLLM must be between 300 to 600 MHz. */ struct da850_opp { unsigned int freq; /* in KHz */ @@ -842,6 +841,33 @@ struct da850_opp { unsigned int cvdd_max; /* in uV */ }; +static const struct da850_opp da850_opp_456 = { + .freq = 456000, + .prediv = 1, + .mult = 19, + .postdiv = 1, + .cvdd_min = 1300000, + .cvdd_max = 1350000, +}; + +static const struct da850_opp da850_opp_408 = { + .freq = 408000, + .prediv = 1, + .mult = 17, + .postdiv = 1, + .cvdd_min = 1300000, + .cvdd_max = 1350000, +}; + +static const struct da850_opp da850_opp_372 = { + .freq = 372000, + .prediv = 2, + .mult = 31, + .postdiv = 1, + .cvdd_min = 1200000, + .cvdd_max = 1320000, +}; + static const struct da850_opp da850_opp_300 = { .freq = 300000, .prediv = 1, @@ -876,6 +902,9 @@ static const struct da850_opp da850_opp_96 = { } static struct cpufreq_frequency_table da850_freq_table[] = { + OPP(456), + OPP(408), + OPP(372), OPP(300), OPP(200), OPP(96), @@ -886,6 +915,19 @@ static struct cpufreq_frequency_table da850_freq_table[] = { }; #ifdef CONFIG_REGULATOR +static int da850_set_voltage(unsigned int index); +static int da850_regulator_init(void); +#endif + +static struct davinci_cpufreq_config cpufreq_info = { + .freq_table = da850_freq_table, +#ifdef CONFIG_REGULATOR + .init = da850_regulator_init, + .set_voltage = da850_set_voltage, +#endif +}; + +#ifdef CONFIG_REGULATOR static struct regulator *cvdd; static int da850_set_voltage(unsigned int index) @@ -895,7 +937,7 @@ static int da850_set_voltage(unsigned int index) if (!cvdd) return -ENODEV; - opp = (struct da850_opp *) da850_freq_table[index].index; + opp = (struct da850_opp *) cpufreq_info.freq_table[index].index; return regulator_set_voltage(cvdd, opp->cvdd_min, opp->cvdd_max); } @@ -912,14 +954,6 @@ static int da850_regulator_init(void) } #endif -static struct davinci_cpufreq_config cpufreq_info = { - .freq_table = &da850_freq_table[0], -#ifdef CONFIG_REGULATOR - .init = da850_regulator_init, - .set_voltage = da850_set_voltage, -#endif -}; - static struct platform_device da850_cpufreq_device = { .name = "cpufreq-davinci", .dev = { @@ -928,12 +962,22 @@ static struct platform_device da850_cpufreq_device = { .id = -1, }; +unsigned int da850_max_speed = 300000; + int __init da850_register_cpufreq(char *async_clk) { + int i; + /* cpufreq driver can help keep an "async" clock constant */ if (async_clk) clk_add_alias("async", da850_cpufreq_device.name, async_clk, NULL); + for (i = 0; i < ARRAY_SIZE(da850_freq_table); i++) { + if (da850_freq_table[i].frequency <= da850_max_speed) { + cpufreq_info.freq_table = &da850_freq_table[i]; + break; + } + } return platform_device_register(&da850_cpufreq_device); } @@ -942,17 +986,18 @@ static int da850_round_armrate(struct clk *clk, unsigned long rate) { int i, ret = 0, diff; unsigned int best = (unsigned int) -1; + struct cpufreq_frequency_table *table = cpufreq_info.freq_table; rate /= 1000; /* convert to kHz */ - for (i = 0; da850_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { - diff = da850_freq_table[i].frequency - rate; + for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { + diff = table[i].frequency - rate; if (diff < 0) diff = -diff; if (diff < best) { best = diff; - ret = da850_freq_table[i].frequency; + ret = table[i].frequency; } } @@ -973,7 +1018,7 @@ static int da850_set_pll0rate(struct clk *clk, unsigned long index) struct pll_data *pll = clk->pll_data; int ret; - opp = (struct da850_opp *) da850_freq_table[index].index; + opp = (struct da850_opp *) cpufreq_info.freq_table[index].index; prediv = opp->prediv; mult = opp->mult; postdiv = opp->postdiv; diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index 4247b3f..e7f9520 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -28,6 +28,13 @@ extern void __iomem *da8xx_syscfg0_base; extern void __iomem *da8xx_syscfg1_base; /* + * If the DA850/OMAP-L138/AM18x SoC on board is of a higher speed grade + * (than the regular 300Mhz variant), the board code should set this up + * with the supported speed before calling da850_register_cpufreq(). + */ +extern unsigned int da850_max_speed; + +/* * The cp_intc interrupt controller for the da8xx isn't in the same * chunk of physical memory space as the other registers (like it is * on the davincis) so it needs to be mapped separately. It will be -- 1.7.3.2 From nsekhar at ti.com Mon Dec 20 04:22:12 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Mon, 20 Dec 2010 15:52:12 +0530 Subject: [PATCH v4 2/2 (REPOST)] davinci: am18x/da850/omap-l138 evm: add support for higher speed grades In-Reply-To: <1292840532-14303-1-git-send-email-nsekhar@ti.com> References: <1292840532-14303-1-git-send-email-nsekhar@ti.com> Message-ID: <1292840532-14303-2-git-send-email-nsekhar@ti.com> Apart from the regular AM18x/DA850/OMAP-L138 SoC operating at 300MHz, these SoCs have variants that can operate at a maximum of 456MHz. Variants at 408Mhz and 375 Mhz are available as well. Not all silicon is qualified to run at higher speeds and unfortunately the maximum speed the chip can support can only be determined from the label on the package (not software readable). The EVM hardware for all these variants is the same (except for the actual SoC populated). U-Boot on the EVM sets up ATAG_REVISION to inform the OS regarding the speed grade supported by the silicon. We use this information to pass on the speed grade information to the SoC code. Signed-off-by: Sekhar Nori --- re-posting with linux-arm-kernel included in CC. arch/arm/mach-davinci/board-da850-evm.c | 25 +++++++++++++++++++++++-- 1 files changed, 23 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index f89b0b7..893d9be 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -540,7 +540,7 @@ static struct regulator_init_data tps65070_regulator_data[] = { { .constraints = { .min_uV = 950000, - .max_uV = 1320000, + .max_uV = 1350000, .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS), .boot_on = 1, @@ -736,6 +736,27 @@ static struct edma_rsv_info *da850_edma_rsv[2] = { &da850_edma_cc1_rsv, }; +#ifdef CONFIG_CPU_FREQ +static __init int da850_evm_init_cpufreq(void) +{ + switch (system_rev & 0xF) { + case 3: + da850_max_speed = 456000; + break; + case 2: + da850_max_speed = 408000; + break; + case 1: + da850_max_speed = 372000; + break; + } + + return da850_register_cpufreq("pll0_sysclk3"); +} +#else +static __init int da850_evm_init_cpufreq(void) { return 0; } +#endif + static __init void da850_evm_init(void) { int ret; @@ -836,7 +857,7 @@ static __init void da850_evm_init(void) if (ret) pr_warning("da850_evm_init: rtc setup failed: %d\n", ret); - ret = da850_register_cpufreq("pll0_sysclk3"); + ret = da850_evm_init_cpufreq(); if (ret) pr_warning("da850_evm_init: cpufreq registration failed: %d\n", ret); -- 1.7.3.2 From nsekhar at ti.com Mon Dec 20 04:28:30 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Mon, 20 Dec 2010 15:58:30 +0530 Subject: [PATCH v4 1/2 (REPOST)] davinci: am18x/da850/omap-l138: add support for higher speed grades Message-ID: <1292840911-16325-1-git-send-email-nsekhar@ti.com> AM18x/DA850/OMAP-L138 SoCs have variants that can operate at a maximum of 456 MHz at 1.3V operating point. Also the 1.2V operating point has a variant that can support a maximum of 375 MHz. This patch adds three new OPPs (456 MHz, 408 MHz and 372 MHz) to the list of DA850 OPPs. Not all silicon is qualified to run at higher speeds and unfortunately the maximum speed the chip can support can only be determined from the label on the package (not software readable). Because of this, we depend on the maximum speed grade information to be provided to us in some board specific way. The board informs the maximum speed grade information to the SoC by calling the da850_set_max_speed() function. Signed-off-by: Sekhar Nori --- re-posting with linux-arm-kernel included in CC. arch/arm/mach-davinci/da850.c | 75 ++++++++++++++++++++++------ arch/arm/mach-davinci/include/mach/da8xx.h | 7 +++ 2 files changed, 67 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 63916b9..78b5ae2 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -830,8 +830,7 @@ static void da850_set_async3_src(int pllnum) * According to the TRM, minimum PLLM results in maximum power savings. * The OPP definitions below should keep the PLLM as low as possible. * - * The output of the PLLM must be between 400 to 600 MHz. - * This rules out prediv of anything but divide-by-one for 24Mhz OSC input. + * The output of the PLLM must be between 300 to 600 MHz. */ struct da850_opp { unsigned int freq; /* in KHz */ @@ -842,6 +841,33 @@ struct da850_opp { unsigned int cvdd_max; /* in uV */ }; +static const struct da850_opp da850_opp_456 = { + .freq = 456000, + .prediv = 1, + .mult = 19, + .postdiv = 1, + .cvdd_min = 1300000, + .cvdd_max = 1350000, +}; + +static const struct da850_opp da850_opp_408 = { + .freq = 408000, + .prediv = 1, + .mult = 17, + .postdiv = 1, + .cvdd_min = 1300000, + .cvdd_max = 1350000, +}; + +static const struct da850_opp da850_opp_372 = { + .freq = 372000, + .prediv = 2, + .mult = 31, + .postdiv = 1, + .cvdd_min = 1200000, + .cvdd_max = 1320000, +}; + static const struct da850_opp da850_opp_300 = { .freq = 300000, .prediv = 1, @@ -876,6 +902,9 @@ static const struct da850_opp da850_opp_96 = { } static struct cpufreq_frequency_table da850_freq_table[] = { + OPP(456), + OPP(408), + OPP(372), OPP(300), OPP(200), OPP(96), @@ -886,6 +915,19 @@ static struct cpufreq_frequency_table da850_freq_table[] = { }; #ifdef CONFIG_REGULATOR +static int da850_set_voltage(unsigned int index); +static int da850_regulator_init(void); +#endif + +static struct davinci_cpufreq_config cpufreq_info = { + .freq_table = da850_freq_table, +#ifdef CONFIG_REGULATOR + .init = da850_regulator_init, + .set_voltage = da850_set_voltage, +#endif +}; + +#ifdef CONFIG_REGULATOR static struct regulator *cvdd; static int da850_set_voltage(unsigned int index) @@ -895,7 +937,7 @@ static int da850_set_voltage(unsigned int index) if (!cvdd) return -ENODEV; - opp = (struct da850_opp *) da850_freq_table[index].index; + opp = (struct da850_opp *) cpufreq_info.freq_table[index].index; return regulator_set_voltage(cvdd, opp->cvdd_min, opp->cvdd_max); } @@ -912,14 +954,6 @@ static int da850_regulator_init(void) } #endif -static struct davinci_cpufreq_config cpufreq_info = { - .freq_table = &da850_freq_table[0], -#ifdef CONFIG_REGULATOR - .init = da850_regulator_init, - .set_voltage = da850_set_voltage, -#endif -}; - static struct platform_device da850_cpufreq_device = { .name = "cpufreq-davinci", .dev = { @@ -928,12 +962,22 @@ static struct platform_device da850_cpufreq_device = { .id = -1, }; +unsigned int da850_max_speed = 300000; + int __init da850_register_cpufreq(char *async_clk) { + int i; + /* cpufreq driver can help keep an "async" clock constant */ if (async_clk) clk_add_alias("async", da850_cpufreq_device.name, async_clk, NULL); + for (i = 0; i < ARRAY_SIZE(da850_freq_table); i++) { + if (da850_freq_table[i].frequency <= da850_max_speed) { + cpufreq_info.freq_table = &da850_freq_table[i]; + break; + } + } return platform_device_register(&da850_cpufreq_device); } @@ -942,17 +986,18 @@ static int da850_round_armrate(struct clk *clk, unsigned long rate) { int i, ret = 0, diff; unsigned int best = (unsigned int) -1; + struct cpufreq_frequency_table *table = cpufreq_info.freq_table; rate /= 1000; /* convert to kHz */ - for (i = 0; da850_freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { - diff = da850_freq_table[i].frequency - rate; + for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) { + diff = table[i].frequency - rate; if (diff < 0) diff = -diff; if (diff < best) { best = diff; - ret = da850_freq_table[i].frequency; + ret = table[i].frequency; } } @@ -973,7 +1018,7 @@ static int da850_set_pll0rate(struct clk *clk, unsigned long index) struct pll_data *pll = clk->pll_data; int ret; - opp = (struct da850_opp *) da850_freq_table[index].index; + opp = (struct da850_opp *) cpufreq_info.freq_table[index].index; prediv = opp->prediv; mult = opp->mult; postdiv = opp->postdiv; diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index 4247b3f..e7f9520 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -28,6 +28,13 @@ extern void __iomem *da8xx_syscfg0_base; extern void __iomem *da8xx_syscfg1_base; /* + * If the DA850/OMAP-L138/AM18x SoC on board is of a higher speed grade + * (than the regular 300Mhz variant), the board code should set this up + * with the supported speed before calling da850_register_cpufreq(). + */ +extern unsigned int da850_max_speed; + +/* * The cp_intc interrupt controller for the da8xx isn't in the same * chunk of physical memory space as the other registers (like it is * on the davincis) so it needs to be mapped separately. It will be -- 1.7.3.2 From nsekhar at ti.com Mon Dec 20 04:28:31 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Mon, 20 Dec 2010 15:58:31 +0530 Subject: [PATCH v4 2/2 (REPOST)] davinci: am18x/da850/omap-l138 evm: add support for higher speed grades In-Reply-To: <1292840911-16325-1-git-send-email-nsekhar@ti.com> References: <1292840911-16325-1-git-send-email-nsekhar@ti.com> Message-ID: <1292840911-16325-2-git-send-email-nsekhar@ti.com> Apart from the regular AM18x/DA850/OMAP-L138 SoC operating at 300MHz, these SoCs have variants that can operate at a maximum of 456MHz. Variants at 408Mhz and 375 Mhz are available as well. Not all silicon is qualified to run at higher speeds and unfortunately the maximum speed the chip can support can only be determined from the label on the package (not software readable). The EVM hardware for all these variants is the same (except for the actual SoC populated). U-Boot on the EVM sets up ATAG_REVISION to inform the OS regarding the speed grade supported by the silicon. We use this information to pass on the speed grade information to the SoC code. Signed-off-by: Sekhar Nori --- re-posting with linux-arm-kernel included in CC. arch/arm/mach-davinci/board-da850-evm.c | 25 +++++++++++++++++++++++-- 1 files changed, 23 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index f89b0b7..893d9be 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -540,7 +540,7 @@ static struct regulator_init_data tps65070_regulator_data[] = { { .constraints = { .min_uV = 950000, - .max_uV = 1320000, + .max_uV = 1350000, .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS), .boot_on = 1, @@ -736,6 +736,27 @@ static struct edma_rsv_info *da850_edma_rsv[2] = { &da850_edma_cc1_rsv, }; +#ifdef CONFIG_CPU_FREQ +static __init int da850_evm_init_cpufreq(void) +{ + switch (system_rev & 0xF) { + case 3: + da850_max_speed = 456000; + break; + case 2: + da850_max_speed = 408000; + break; + case 1: + da850_max_speed = 372000; + break; + } + + return da850_register_cpufreq("pll0_sysclk3"); +} +#else +static __init int da850_evm_init_cpufreq(void) { return 0; } +#endif + static __init void da850_evm_init(void) { int ret; @@ -836,7 +857,7 @@ static __init void da850_evm_init(void) if (ret) pr_warning("da850_evm_init: rtc setup failed: %d\n", ret); - ret = da850_register_cpufreq("pll0_sysclk3"); + ret = da850_evm_init_cpufreq(); if (ret) pr_warning("da850_evm_init: cpufreq registration failed: %d\n", ret); -- 1.7.3.2 From sshtylyov at mvista.com Mon Dec 20 04:43:35 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Mon, 20 Dec 2010 13:43:35 +0300 Subject: [PATCH v4 1/2 (REPOST)] davinci: am18x/da850/omap-l138: add support for higher speed grades In-Reply-To: <1292840911-16325-1-git-send-email-nsekhar@ti.com> References: <1292840911-16325-1-git-send-email-nsekhar@ti.com> Message-ID: <4D0F3357.8060905@mvista.com> Hello. On 20-12-2010 13:28, Sekhar Nori wrote: > AM18x/DA850/OMAP-L138 SoCs have variants that can operate > at a maximum of 456 MHz at 1.3V operating point. Also the > 1.2V operating point has a variant that can support a maximum > of 375 MHz. > This patch adds three new OPPs (456 MHz, 408 MHz and 372 MHz) > to the list of DA850 OPPs. > Not all silicon is qualified to run at higher speeds and > unfortunately the maximum speed the chip can support can only > be determined from the label on the package (not software > readable). > Because of this, we depend on the maximum speed grade information > to be provided to us in some board specific way. The board informs > the maximum speed grade information to the SoC by calling the > da850_set_max_speed() function. This sentence no longer applies. You've reduced the function to a variable. > Signed-off-by: Sekhar Nori WBR, Sergei From nsekhar at ti.com Mon Dec 20 05:40:30 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 20 Dec 2010 17:10:30 +0530 Subject: [PATCH v4 1/2 (REPOST)] davinci: am18x/da850/omap-l138: add support for higher speed grades In-Reply-To: <4D0F3357.8060905@mvista.com> References: <1292840911-16325-1-git-send-email-nsekhar@ti.com> <4D0F3357.8060905@mvista.com> Message-ID: On Mon, Dec 20, 2010 at 16:13:35, Sergei Shtylyov wrote: > Hello. > > On 20-12-2010 13:28, Sekhar Nori wrote: > > > AM18x/DA850/OMAP-L138 SoCs have variants that can operate > > at a maximum of 456 MHz at 1.3V operating point. Also the > > 1.2V operating point has a variant that can support a maximum > > of 375 MHz. > > > This patch adds three new OPPs (456 MHz, 408 MHz and 372 MHz) > > to the list of DA850 OPPs. > > > Not all silicon is qualified to run at higher speeds and > > unfortunately the maximum speed the chip can support can only > > be determined from the label on the package (not software > > readable). > > > Because of this, we depend on the maximum speed grade information > > to be provided to us in some board specific way. The board informs > > the maximum speed grade information to the SoC by calling the > > da850_set_max_speed() function. > > This sentence no longer applies. You've reduced the function to a variable. Oops, missed updating this part. Thanks for pointing out. Regards, Sekhar From manjunath.hadli at ti.com Mon Dec 20 07:39:55 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Mon, 20 Dec 2010 19:09:55 +0530 Subject: [PATCH v7 5/8] davinci vpbe: board specific additions In-Reply-To: <4D0B8FE3.3080502@mvista.com> Message-ID: On Fri, Dec 17, 2010 at 21:59:23, Sergei Shtylyov wrote: > Hello. > > Manjunath Hadli wrote: > > > This patch implements tables for display timings,outputs and other > > board related functionalities. > > > Signed-off-by: Manjunath Hadli > > Acked-by: Muralidharan Karicheri > > Acked-by: Hans Verkuil > [...] > > > diff --git a/arch/arm/mach-davinci/board-dm644x-evm.c > > b/arch/arm/mach-davinci/board-dm644x-evm.c > > index 34c8b41..e9b1243 100644 > > --- a/arch/arm/mach-davinci/board-dm644x-evm.c > > +++ b/arch/arm/mach-davinci/board-dm644x-evm.c > [...] > > @@ -620,6 +671,8 @@ davinci_evm_map_io(void) { > > /* setup input configuration for VPFE input devices */ > > dm644x_set_vpfe_config(&vpfe_cfg); > > + /* setup configuration for vpbe devices */ > > + dm644x_set_vpbe_display_config(&vpbe_display_cfg); > > dm644x_init(); > > } > > This patch should *follow* the platform patch (where > dm644x_set_vpbe_display_config() is defined), not precede it. Thanks. Will update the patch series. -Manju > > WBR, Sergei > From manjunath.hadli at ti.com Mon Dec 20 07:53:36 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Mon, 20 Dec 2010 19:23:36 +0530 Subject: [PATCH v8 0/8] davinci vpbe: dm6446 v4l2 driver Message-ID: <1292853216-2216-1-git-send-email-manjunath.hadli@ti.com> version8 : addressed on Sergei's comments on: 1.Interchanged platform and board specific patches due to dependencies. Manjunath Hadli (8): 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: platform specific additions davinci vpbe: board specific additions davinci vpbe: Build infrastructure for VPBE driver davinci vpbe: Readme text for Dm6446 vpbe Documentation/video4linux/README.davinci-vpbe | 93 ++ arch/arm/mach-davinci/board-dm644x-evm.c | 79 +- arch/arm/mach-davinci/dm644x.c | 164 ++- arch/arm/mach-davinci/include/mach/dm644x.h | 4 + drivers/media/video/davinci/Kconfig | 22 + drivers/media/video/davinci/Makefile | 2 + drivers/media/video/davinci/vpbe.c | 837 ++++++++++ drivers/media/video/davinci/vpbe_display.c | 2099 +++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd.c | 1211 ++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++ drivers/media/video/davinci/vpbe_venc.c | 574 +++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++ include/media/davinci/vpbe.h | 186 +++ include/media/davinci/vpbe_display.h | 146 ++ include/media/davinci/vpbe_osd.h | 397 +++++ include/media/davinci/vpbe_types.h | 93 ++ include/media/davinci/vpbe_venc.h | 38 + 17 files changed, 6504 insertions(+), 19 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 Mon Dec 20 07:53:47 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Mon, 20 Dec 2010 19:23:47 +0530 Subject: [PATCH v8 1/8] davinci vpbe: V4L2 display driver for DM644X SoC Message-ID: <1292853227-2295-1-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 is 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 | 2099 ++++++++++++++++++++++++++++ include/media/davinci/vpbe_display.h | 146 ++ include/media/davinci/vpbe_types.h | 93 ++ 3 files changed, 2338 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..a29a2a0 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_display.c @@ -0,0 +1,2099 @@ +/* + * 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 "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#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 +#include "vpbe_venc_regs.h" + + +#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" + +static int debug; +static u32 video2_numbuffers = 3; +static u32 video3_numbuffers = 3; + +#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) +#define VPBE_DEFAULT_NUM_BUFS 3 + +static u32 video2_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; +static u32 video3_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; + +module_param(video2_numbuffers, uint, S_IRUGO); +module_param(video3_numbuffers, uint, S_IRUGO); +module_param(video2_bufsize, uint, S_IRUGO); +module_param(video3_bufsize, uint, S_IRUGO); +module_param(debug, int, 0644); + +static struct buf_config_params display_buf_config_params = { + .min_numbuffers = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[0] = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[1] = VPBE_DEFAULT_NUM_BUFS, + .min_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .min_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, +}; + +static struct vpbe_device *vpbe_dev; +static struct osd_state *osd_device; +static int vpbe_display_nr[] = { 2, 3 }; + +static struct v4l2_capability vpbe_display_videocap = { + .driver = VPBE_DISPLAY_DRIVER, + .bus_info = "platform", + .version = VPBE_DISPLAY_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, +}; + +static u8 layer_first_int[VPBE_DISPLAY_MAX_DEVICES]; + +__iomem void *reg_base_venc; + +static int venc_is_second_field() +{ + u32 val; + val = __raw_readl(reg_base_venc + VENC_VSTAT); + return ((val & VENC_VSTAT_FIDST) + == VENC_VSTAT_FIDST); +} + +/* + * vpbe_display_isr() + * ISR function. It changes status of the displayed buffer, takes next buffer + * from the queue and sets its address in VPBE registers + */ +static void vpbe_display_isr(unsigned int event, void *disp_obj) +{ + unsigned long jiffies_time = get_jiffies_64(); + struct timeval timevalue; + int i, fid; + unsigned long addr = 0; + struct vpbe_display_obj *layer = NULL; + struct vpbe_display *disp_dev = (struct vpbe_display *)disp_obj; + + /* Convert time represention from jiffies to timeval */ + jiffies_to_timeval(jiffies_time, &timevalue); + + 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; + /* Check the field format */ + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && + (event & OSD_END_OF_FRAME)) { + /* Progressive mode */ + if (layer_first_int[i]) + layer_first_int[i] = 0; + continue; + /* + * Mark status of the cur_frm to + * done and unlock semaphore on it + */ + + if (layer->cur_frm != layer->next_frm) { + layer->cur_frm->ts = timevalue; + 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; + } + /* Get the next buffer from buffer queue */ + spin_lock(&disp_dev->dma_queue_lock); + if (!list_empty(&layer->dma_queue)) { + layer->next_frm = + list_entry(layer->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove that buffer from the buffer queue */ + list_del(&layer->next_frm->queue); + /* Mark status of the buffer as 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_dev->cbcr_ofst); + } + spin_unlock(&disp_dev->dma_queue_lock); + } else { + /* + * Interlaced mode + * If it is first interrupt, ignore it + */ + if (layer_first_int[i]) { + layer_first_int[i] = 0; + return; + } + + layer->field_id ^= 1; + if (event & OSD_FIRST_FIELD) + fid = 0; + else if (event & OSD_SECOND_FIELD) + fid = 1; + else + return; + + /* + * If field id does not match with stored + * field id + */ + if (fid != layer->field_id) { + /* Make them in sync */ + if (0 == fid) + layer->field_id = fid; + + return; + } + /* + * device field id and local field id are + * in sync. If this is even field + */ + if (0 == fid) { + if (layer->cur_frm == layer->next_frm) + continue; + /* + * one frame is displayed If next frame is + * available, release cur_frm and move on + * copy frame display time + */ + layer->cur_frm->ts = timevalue; + /* Change status of the cur_frm */ + layer->cur_frm->state = VIDEOBUF_DONE; + /* unlock semaphore on cur_frm */ + wake_up_interruptible(&layer->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + layer->cur_frm = layer->next_frm; + } else if (1 == fid) { /* odd field */ + + if (list_empty(&layer->dma_queue) + || (layer->cur_frm != layer->next_frm)) + continue; + + /* + * one field is displayed configure + * the next frame if it is available + * otherwise hold on current frame + * Get next from the buffer queue + */ + spin_lock(&disp_dev->dma_queue_lock); + 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); + + /* 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_dev->cbcr_ofst); + spin_unlock(&disp_dev->dma_queue_lock); + } + } + } +} + +/* interrupt service routine */ +static irqreturn_t venc_isr(int irq, void *arg) +{ + static unsigned last_event; + unsigned event = 0; + + if (venc_is_second_field()) + 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; + + vpbe_display_isr(event, arg); + 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_display_obj *layer = fh->layer; + unsigned long addr; + int ret = 0; + + 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_display_obj *layer = fh->layer; + int buf_size; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); + + *size = layer->pix_fmt.sizeimage; + buf_size = + display_buf_config_params.layer_bufsize[layer->device_id]; + /* + * For MMAP, limit the memory allocation as per bootarg + * configured buffer size + */ + if (V4L2_MEMORY_MMAP == layer->memory) + if (*size > buf_size) + *size = buf_size; + + /* Store number of buffers allocated in numbuffer member */ + if (*count < display_buf_config_params.min_numbuffers) + *count = layer->numbuffers = + display_buf_config_params.numbuffers[layer->device_id]; + + 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_display_obj *layer = fh->layer; + struct vpbe_display *disp = fh->disp_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_display_obj *layer = fh->layer; + + 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_display_obj* +_vpbe_display_get_other_win(struct vpbe_display *disp_dev, + struct vpbe_display_obj *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_video_display_params(struct vpbe_display *disp_dev, + struct vpbe_display_obj *layer) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + unsigned long addr; + int ret = 0; + + 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_display_obj *otherlayer = + _vpbe_display_get_other_win(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_display_obj *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; + int h_scale = 0, v_scale = 0, h_exp = 0, v_exp = 0, temp; + 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)) { + temp = (cfg->xsize * + VPBE_DISPLAY_H_EXP_RATIO_N) / + VPBE_DISPLAY_H_EXP_RATIO_D; + if (temp <= expected_xsize) { + h_exp = 1; + cfg->xsize = temp; + } + } + } + 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)) { + temp = (cfg->ysize * + VPBE_DISPLAY_V_EXP_RATIO_N) / + VPBE_DISPLAY_V_EXP_RATIO_D; + if (temp <= expected_ysize) { + v_exp = 1; + cfg->ysize = temp; + } + } + } + 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_display_obj *layer, + int top, int left) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + + cfg->xpos = cfg->ypos = 0; + if (left + cfg->xsize <= vpbe_dev->current_timings.xres) + cfg->xpos = left; + if (top + cfg->ysize <= vpbe_dev->current_timings.yres) + cfg->ypos = top; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "new xpos = %d, ypos = %d\n", + cfg->xpos, cfg->ypos); +} + +static int vpbe_disp_check_window_params(struct vpbe_display *disp_dev, + struct v4l2_rect *c) +{ + if ((c->width == 0) + || ((c->width + c->left) > vpbe_dev->current_timings.xres) + || (c->height == 0) + || ((c->height + c->top) > vpbe_dev->current_timings.yres)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid crop values\n"); + return -1; + } + if ((c->height & 0x1) && (vpbe_dev->current_timings.interlaced)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "window height must be even for interlaced display\n"); + return -1; + } + return 0; +} + +/** + * 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. If application likes to add pads at the end of each line and + * end of the buffer , it can set bytesperline to line size and sizeimage to + * bytesperline * height of the buffer. If driver fills zero for active + * video width and height, and has requested user bytesperline and sizeimage, + * width and height is adjusted to maximum display limit or buffer width + * height which ever is lower + */ +static int vpbe_try_format(struct vpbe_display *disp_dev, + struct v4l2_pix_format *pixfmt, int check) +{ + int min_sizeimage, bpp, min_height = 1, min_width = 32, + max_width, max_height, user_info = 0; + + 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_ANY) { + 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->bytesperline) { + v4l2_err(&vpbe_dev->v4l2_dev, "bytesperline and width" + " cannot be zero\n"); + return -EINVAL; + } + + /* if user provided bytesperline, it must provide sizeimage as well */ + if (pixfmt->bytesperline && !pixfmt->sizeimage) { + v4l2_err(&vpbe_dev->v4l2_dev, + "sizeimage must be non zero, when user" + " provides bytesperline\n"); + return -EINVAL; + } + + /* adjust bytesperline as per hardware - multiple of 32 */ + if (!pixfmt->width) + pixfmt->width = pixfmt->bytesperline / bpp; + + if (!pixfmt->bytesperline) + pixfmt->bytesperline = pixfmt->width * bpp; + else + user_info = 1; + pixfmt->bytesperline = ((pixfmt->bytesperline + 31) & ~31); + + if (pixfmt->width < min_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is less than minimum," + "input width = %d, min_width = %d\n", + pixfmt->width, min_width); + return -EINVAL; + } + pixfmt->width = min_width; + } + + if (pixfmt->width > max_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is more than maximum," + "input width = %d, max_width = %d\n", + pixfmt->width, max_width); + return -EINVAL; + } + pixfmt->width = max_width; + } + + /* + * If height is zero, then atleast we need to have sizeimage + * to calculate height + */ + if (!pixfmt->height) { + if (user_info) { + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) { + /* + * for NV12 format, sizeimage is y-plane size + * + CbCr plane which is half of y-plane + */ + pixfmt->height = pixfmt->sizeimage / + (pixfmt->bytesperline + + (pixfmt->bytesperline >> 1)); + } else + pixfmt->height = pixfmt->sizeimage/ + pixfmt->bytesperline; + } + } + + if (pixfmt->height > max_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is more than maximum," + "input height = %d, max_height = %d\n", + pixfmt->height, max_height); + return -EINVAL; + } + pixfmt->height = max_height; + } + + if (pixfmt->height < min_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is less than minimum," + "input height = %d, min_height = %d\n", + pixfmt->height, min_height); + return -EINVAL; + } + pixfmt->height = min_width; + } + + /* if user has not provided bytesperline calculate it based on width */ + if (!user_info) + pixfmt->bytesperline = (((pixfmt->width * bpp) + 31) & ~31); + + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + min_sizeimage = pixfmt->bytesperline * pixfmt->height + + (pixfmt->bytesperline * pixfmt->height >> 1); + else + min_sizeimage = pixfmt->bytesperline * pixfmt->height; + + if (pixfmt->sizeimage < min_sizeimage) { + if (check && user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, "sizeimage is less, %d\n", + min_sizeimage); + return -EINVAL; + } + pixfmt->sizeimage = min_sizeimage; + } + 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_display_obj *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_display_obj *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_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id); + *cap = vpbe_display_videocap; + + return 0; +} + +static int vpbe_display_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct vpbe_display *disp_dev = video_drvdata(file); + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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) { + struct v4l2_rect *rect = &crop->c; + + if (rect->top < 0 || rect->left < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + + if (vpbe_disp_check_window_params(disp_dev, rect)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + 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); + + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + return ret; +} + +static int vpbe_display_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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) { + struct v4l2_rect *rect = &crop->c; + if (ret) + return ret; + 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; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + ret = -EINVAL; + } + + return ret; +} + +static int vpbe_display_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + + 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_display_obj *layer = fh->layer; + + 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) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Fill in the information about format */ + *pixfmt = layer->pix_fmt; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + + 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_display_obj *layer = fh->layer; + 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 > 0) { + 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 if (index == 1) { + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + 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; + } else { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* 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; + + /* Change of the default pixel format for both video windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { + struct vpbe_display_obj *otherlayer; + cfg->pixfmt = PIXFMT_NV12; + otherlayer = _vpbe_display_get_other_win(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); + + /* verify if readback values are as expected */ + if (layer->pix_fmt.width != cfg->xsize || + layer->pix_fmt.height != cfg->ysize || + layer->pix_fmt.bytesperline != layer->layer_info. + config.line_length || + (cfg->interlaced + && layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || + (!cfg->interlaced && layer->pix_fmt.field + != V4L2_FIELD_NONE)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "mismatch:layer conf params:\n"); + return -EINVAL; + } + } + + return 0; +} + +static int vpbe_display_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Check for valid field format */ + return vpbe_try_format(disp_dev, pixfmt, 0); + } + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; +} + +/** + * 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_display_obj *layer = fh->layer; + int ret = 0; + + 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; + } + } + 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) +{ + 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) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_outputs) { + 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_display_obj *layer = fh->layer; + int ret = 0; + + 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) { + 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 ret; +} + +/** + * 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) +{ + 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) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_dv_presets) { + 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 ret; +} + +/** + * 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_display_obj *layer = fh->layer; + int ret = 0; + + 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) { + 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 ret; +} + +/** + * 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) +{ + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + + 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_video_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_first_int[layer->device_id] = 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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_display_obj *layer = fh->layer; + + 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) +{ + unsigned int err = 0; + struct vpbe_fh *fh = filep->private_data; + struct vpbe_display_obj *layer = fh->layer; + + 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; +} + +static int vpbe_display_cfg_layer_default(enum vpbe_display_device_id id, + struct vpbe_display *disp_dev) +{ + int err = 0; + struct osd_layer_config *layer_config; + struct vpbe_display_obj *layer = disp_dev->dev[id]; + struct osd_layer_config *cfg = &layer->layer_info.config; + + /* 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"); + return -EBUSY; + } + + layer_config = cfg; + /* Set the default image and crop values */ + layer_config->pixfmt = PIXFMT_YCbCrI; + layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY; + layer->pix_fmt.bytesperline = layer_config->line_length = + vpbe_dev->current_timings.xres * 2; + + layer->pix_fmt.width = layer_config->xsize = + vpbe_dev->current_timings.xres; + layer->pix_fmt.height = layer_config->ysize = + vpbe_dev->current_timings.yres; + layer->pix_fmt.sizeimage = + layer->pix_fmt.bytesperline * layer->pix_fmt.height; + layer_config->xpos = 0; + layer_config->ypos = 0; + layer_config->interlaced = vpbe_dev->current_timings.interlaced; + + /* + * turn off ping-pong buffer and field inversion to fix + * the image shaking problem in 1080I mode + */ + + if (cfg->interlaced) + layer->pix_fmt.field = V4L2_FIELD_INTERLACED; + else + layer->pix_fmt.field = V4L2_FIELD_NONE; + + err = osd_device->ops.set_layer_config(osd_device, + layer->layer_info.id, + layer_config); + if (err < 0) { + /* Couldn't set layer */ + v4l2_err(&vpbe_dev->v4l2_dev, + "Display Manager failed to set osd layer\n"); + return -EBUSY; + } + + return 0; +} + +/* + * 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) +{ + int minor = iminor(file->f_path.dentry->d_inode); + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer; + struct vpbe_fh *fh = NULL; + int found = -1; + int i = 0; + + /* Check for valid minor number */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + layer = disp_dev->dev[i]; + if (minor == layer->video_dev->minor) { + found = i; + break; + } + } + + /* If not found, return error no device */ + if (0 > found) { + v4l2_err(&vpbe_dev->v4l2_dev, "device not found\n"); + return -ENODEV; + } + + /* 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) { + /* Configure the default values for the layer */ + if (vpbe_display_cfg_layer_default(layer->device_id, + disp_dev)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to configure video layer" + " for id = %d\n", layer->device_id); + 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_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); + /* If this is doing IO and other layer are not closed */ + if ((layer->usrs != 1) && fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "Close other instances\n"); + return -EAGAIN; + } + + /* 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_display_obj *otherlayer; + otherlayer = + _vpbe_display_get_other_win(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); + + if (strcmp("vpbe_controller", pdev->name) == 0) + vpbe_dev = platform_get_drvdata(pdev); + + if (strcmp("vpbe-osd", pdev->name) == 0) + osd_device = platform_get_drvdata(pdev); + + return 0; +} + +/*Configure the channels, buffer size */ +static int init_vpbe_layer_objects(int i) +{ + int free_buffer_index; + + /* Default number of buffers should be 3 */ + if ((video2_numbuffers > 0) && + (video2_numbuffers < display_buf_config_params.min_numbuffers)) + video2_numbuffers = display_buf_config_params.min_numbuffers; + if ((video3_numbuffers > 0) && + (video3_numbuffers < display_buf_config_params.min_numbuffers)) + video3_numbuffers = display_buf_config_params.min_numbuffers; + + /* + * Set buffer size to min buffers size if invalid + * buffer size is given + */ + if (video2_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]) + video2_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]; + + if (video3_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]) + video3_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]; + + /* set number of buffers, they could come from boot/args */ + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_0] = + video2_numbuffers; + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_1] = + video3_numbuffers; + + if (display_buf_config_params.numbuffers[0] == 0) + printk(KERN_ERR "no vid2 buffer allocated\n"); + if (display_buf_config_params.numbuffers[1] == 0) + printk(KERN_ERR "no vid3 buffer allocated\n"); + free_buffer_index = display_buf_config_params.numbuffers[i - 1]; + + 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 __init int vpbe_display_probe(struct platform_device *pdev) +{ + int i, j = 0, k, err = 0; + struct vpbe_display *disp_dev; + struct video_device *vbd = NULL; + struct vpbe_display_obj *vpbe_display_layer = NULL; + struct resource *res; + 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; + } + + /* Allocate memory for four plane display objects */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + disp_dev->dev[i] = + kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL); + /* If memory allocation fails, return error */ + if (!disp_dev->dev[i]) { + printk(KERN_ERR "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + spin_lock_init(&disp_dev->dev[i]->irqlock); + mutex_init(&disp_dev->dev[i]->opslock); + } + spin_lock_init(&disp_dev->dma_queue_lock); + + err = init_vpbe_layer_objects(i); + if (err) { + printk(KERN_ERR "Error initializing vpbe display\n"); + return err; + } + + /* + * 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, NULL, + vpbe_device_get); + if (err < 0) + return err; + + /* Initialize the vpbe display controller */ + if (NULL != vpbe_dev->ops.initialize) { + err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev); + if (err) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error initing vpbe\n"); + err = -ENOMEM; + goto probe_out; + } + } + + /* check the name of davinci device */ + if (vpbe_dev->cfg->module_name != NULL) + strcpy(vpbe_display_videocap.card, + vpbe_dev->cfg->module_name); + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[i]; + /* Allocate memory for video device */ + vbd = video_device_alloc(); + if (vbd == NULL) { + for (j = 0; j < i; j++) { + video_device_release( + disp_dev->dev[j]->video_dev); + } + v4l2_err(&vpbe_dev->v4l2_dev, "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + /* Initialize field of video device */ + vbd->release = video_device_release; + vbd->fops = &vpbe_fops; + vbd->ioctl_ops = &vpbe_ioctl_ops; + vbd->minor = -1; + vbd->v4l2_dev = &vpbe_dev->v4l2_dev; + vbd->lock = &vpbe_display_layer->opslock; + + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { + vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); + vbd->current_norm = + 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); + + /* Set video_dev to the video device */ + vpbe_display_layer->video_dev = vbd; + vpbe_display_layer->device_id = i; + + vpbe_display_layer->layer_info.id = + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); + if (display_buf_config_params.numbuffers[i] == 0) + vpbe_display_layer->memory = V4L2_MEMORY_USERPTR; + else + vpbe_display_layer->memory = V4L2_MEMORY_MMAP; + + /* Initialize field of the display layer objects */ + vpbe_display_layer->usrs = 0; + vpbe_display_layer->io_usrs = 0; + vpbe_display_layer->started = 0; + + /* Initialize prio member of layer object */ + v4l2_prio_init(&vpbe_display_layer->prio); + + /* Register video device */ + v4l2_info(&vpbe_dev->v4l2_dev, + "Trying to register VPBE display device.\n"); + v4l2_info(&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, + vpbe_display_nr[i]); + if (err) + goto probe_out; + /* set the driver data in platform device */ + platform_set_drvdata(pdev, disp_dev); + video_set_drvdata(vpbe_display_layer->video_dev, disp_dev); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to get VENC register address map\n"); + err = -ENODEV; + goto probe_out; + } + + reg_base_venc = ioremap_nocache(res->start, resource_size(res)); + if (!reg_base_venc) { + v4l2_err(&vpbe_dev->v4l2_dev, "Unable to map VENC IO space\n"); + err = -ENODEV; + goto probe_out; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + v4l2_err(&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(&vpbe_dev->v4l2_dev, "Unable to request interrupt\n"); + err = -ENODEV; + goto probe_out; + } + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); + return 0; +probe_out: + kfree(disp_dev); + + for (k = 0; k < j; k++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[k]; + /* Unregister video device */ + video_unregister_device(vpbe_display_layer->video_dev); + /* Release video device */ + video_device_release(vpbe_display_layer->video_dev); + vpbe_display_layer->video_dev = NULL; + } + return err; +} + +/* + * vpbe_display_remove() + * It un-register hardware layer from V4L2 driver + */ +static int vpbe_display_remove(struct platform_device *pdev) +{ + int i; + struct vpbe_display_obj *vpbe_display_layer; + struct vpbe_display *disp_dev = platform_get_drvdata(pdev); + struct resource *res; + + 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); + + vpbe_display_layer->video_dev = NULL; + } + 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 __init int vpbe_display_init(void) +{ + int err = 0; + + 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 t