From broonie at opensource.wolfsonmicro.com Mon Nov 1 07:23:29 2010 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Mon, 1 Nov 2010 09:23:29 -0400 Subject: [PATCH v6 3/9] davinci: ASoC support for Omapl138-Hawkboard In-Reply-To: <1288365045-2709-4-git-send-email-vm.rod25@gmail.com> References: <1288365045-2709-1-git-send-email-vm.rod25@gmail.com> <1288365045-2709-4-git-send-email-vm.rod25@gmail.com> Message-ID: <20101101132328.GE22639@opensource.wolfsonmicro.com> On Fri, Oct 29, 2010 at 10:10:39AM -0500, vm.rod25 at gmail.com wrote: > From: Victor Rodriguez > > This patch adds ASoC support for the Hawkboard-L138 system > > Signed-off-by: Victor Rodriguez Acked-by: Mark Brown From mugdha at ti.com Tue Nov 2 01:33:24 2010 From: mugdha at ti.com (Kamoolkar, Mugdha) Date: Tue, 2 Nov 2010 13:03:24 +0530 Subject: Need help to compile WINCE version DSPLINK for omapl138 In-Reply-To: <201010292114367504596@263.net> References: <201010292114367504596@263.net> Message-ID: Hi, This seems to be the problem: C:Perl\bin\perl: not found Does this help: http://processors.wiki.ti.com/index.php/Troubleshooting_DSPLink_build_issues#Problem:_Build_fails_on_Windows_and_paths_look_like_c:perl_etc. Regards, Mugdha ________________________________ From: davinci-linux-open-source-bounces at linux.davincidsp.com [mailto:davinci-linux-open-source-bounces at linux.davincidsp.com] On Behalf Of chensu.main Sent: Friday, October 29, 2010 6:45 PM To: davinci-linux-open-source Subject: Need help to compile WINCE version DSPLINK for omapl138 Hi all, I have download wince-portted-version DSPlink , and the filename is dsplink_1_65_00_03.tar.gz In the doc(InstallGuide_WinCE_OMAPL138.pdf ) , I have some question? Which tool chain should I use? I only enter dos window from VS2005's build menu.and I also modify some PATH by set path=C:\ti-tools\bios\xdctools;C:\ti-tools\bios\xdctools\bin;%path% set path=D:\dsplink\etc\host\scripts\msdos;c:\perl\bin;%path%; But compilling process cann't be continued.the error info is D:\dsplink\gpp\src>make debug [SRC ] ======= DIRS ================== INCLUDE ============ make[1]: Entering directory `D:/dsplink/gpp/src/arch' [ARCH ] ------- DIRS ------------------ INCLUDE ------------ C:Perl\bin\perl: not found make[1]: *** [D:\dsplink\\gpp\\export\\INCLUDE] Error 127 make[1]: Leaving directory `D:/dsplink/gpp/src/arch' make: *** [arch.dirinc] Error 2 Can anyone give me a Guide , or some indication? Thanks very much. 2010-10-29 ________________________________ chensu.main -------------- next part -------------- An HTML attachment was scrubbed... URL: From Andreas.Gaer at baslerweb.com Tue Nov 2 12:10:07 2010 From: Andreas.Gaer at baslerweb.com (Gaer, A.) Date: Tue, 2 Nov 2010 19:10:07 +0100 Subject: imp_*.c question Message-ID: Hello all, is there somebody taking care of the IMP_* drivers in the arago branch? I noticed two problems while using the resizer in single shot mode: 1. Via "wait_for_completion_interruptible()" the driver waits for an interrupt by the hardware, without timeout! If you set the clock for the resizer too high, this interrupt will never occur, deadlocking the application. As there is no "real" rule for calculating the N/M CLK_DIV, this is bad. 2. The way the completion object "imp_serializer_info.sem_isr" is used in "imp_common_start" seems to be problematic. It is waited on interruptible, but in case it is interrupted it is not resetted. In that case the next wait will return immediately with the old completion (this happened to us when we stopped and re-started our application) I have attached two patches as reference. They're just quick fixes, so I don't want to submit them right now. Any comments? Regards, Andreas. -------------- next part -------------- A non-text attachment was scrubbed... Name: 0001-davinci-Wait-with-timeout-on-image-pipe.patch Type: application/octet-stream Size: 1677 bytes Desc: 0001-davinci-Wait-with-timeout-on-image-pipe.patch URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 0002-davinci-Init-the-completion-object-we-re-waiting-on.patch Type: application/octet-stream Size: 868 bytes Desc: 0002-davinci-Init-the-completion-object-we-re-waiting-on.patch URL: From nsekhar at ti.com Tue Nov 2 12:20:19 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 2 Nov 2010 23:50:19 +0530 Subject: [PATCH v6 9/9] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: <1288365045-2709-10-git-send-email-vm.rod25@gmail.com> References: <1288365045-2709-1-git-send-email-vm.rod25@gmail.com> <1288365045-2709-10-git-send-email-vm.rod25@gmail.com> Message-ID: Hi Victor, On Fri, Oct 29, 2010 at 20:40:45, vm.rod25 at gmail.com wrote: > From: Victor Rodriguez > > This patch adds USB1.1 support for the Hawkboard-L138 system > > Signed-off-by: Victor Rodriguez > --- > +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); Per Documentation/CodingStyle, else should have braces too. > +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 */ Do we have this part on the hawkboard as well? [...] > +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. > + */ No need of multi-line comment style here. Sorry about the piecemeal review (you really generated new versions quickly). That is all I have on this series. Also, you are CCing a whole lot of folks on each patch. Instead, you can automate CCing the relevant folks using --cc-cmd= option of git-send-email Use --cc-cmd="scripts/get_maintainer.pl --pattern-depth=2 -s" to generate list of relevant CCs. Example: scripts/get_maintainer.pl -f arch/arm/mach-davinci/board-omapl138-hawk.c --pattern-depth=2 -s gives: Kevin Hilman Russell King davinci-linux-open-source at linux.davincidsp.com linux-arm-kernel at lists.infradead.org Recently Kevin requested all patches to CC Linux ARM Kernel mailing list so this should be OK. scripts/get_maintainer.pl -f sound/soc/davinci/davinci-evm.c --pattern-depth=2 -s gives: Liam Girdwood Mark Brown alsa-devel at alsa-project.org This is as per what Liam and Mark have been asking for. Also, use --dry-run with git-send-email before sending actual emails to make sure the To and CC lists look good. Thanks, Sekhar From vm.rod25 at gmail.com Tue Nov 2 18:00:43 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Tue, 2 Nov 2010 18:00:43 -0600 Subject: [PATCH v6 9/9] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: References: <1288365045-2709-1-git-send-email-vm.rod25@gmail.com> <1288365045-2709-10-git-send-email-vm.rod25@gmail.com> Message-ID: On Tue, Nov 2, 2010 at 12:20 PM, Nori, Sekhar wrote: > Hi Victor, > > On Fri, Oct 29, 2010 at 20:40:45, vm.rod25 at gmail.com wrote: >> From: Victor Rodriguez >> >> This patch adds USB1.1 support for the Hawkboard-L138 system >> >> Signed-off-by: Victor Rodriguez >> --- > >> +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); > > Per Documentation/CodingStyle, else should have braces too. Ok thanks >> +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 */ > > Do we have this part on the hawkboard as well? Yes the TPS2087 is the USB POWER MANAGER that we have on the board AFAIK this is the same and besides it is working, I have tested by my self by plugging different kind of USB devices, mas storage , Mouses and keyboards. > [...] > >> +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. >> + ? ? ?*/ > > No need of multi-line comment style here. Ok changed to /* Setup the Ref. clock frequency for the HAWK at 24 MHz. */ > Sorry about the piecemeal review (you really generated new versions quickly). Sorry about it I will take much more time for comments. > That is all I have on this series. > > Also, you are CCing a whole lot of folks on each patch. Instead, you can > automate CCing the relevant folks using --cc-cmd= option ?of > git-send-email > > Use --cc-cmd="scripts/get_maintainer.pl --pattern-depth=2 -s" to generate > list of relevant CCs. > > Example: > > scripts/get_maintainer.pl -f arch/arm/mach-davinci/board-omapl138-hawk.c --pattern-depth=2 -s > > gives: > > Kevin Hilman > Russell King > davinci-linux-open-source at linux.davincidsp.com > linux-arm-kernel at lists.infradead.org > > Recently Kevin requested all patches to CC Linux ARM Kernel mailing list so this > should be OK. > > scripts/get_maintainer.pl -f sound/soc/davinci/davinci-evm.c --pattern-depth=2 -s > > gives: > > Liam Girdwood > Mark Brown > alsa-devel at alsa-project.org > > This is as per what Liam and Mark have been asking for. > > Also, use --dry-run with git-send-email before sending actual emails to make > sure the To and CC lists look good. Ok I have done this git send-email --dry-run --from "" --to "" --cc-cmd =scripts/get_maintainer.pl patches/sendedv6/* I just have one question on the --to i think I should put the davinci mailing list as minimum right ? because the cover letter just will be send to me From: To: Cc: Victor Rodriguez Subject: [PATCH v6 0/9] Add Omapl138-Hawkboard support Date: Tue, 2 Nov 2010 17:48:03 -0600 Message-Id: <1288741692-20096-1-git-send-email-vm.rod25 at gmail.com> X-Mailer: git-send-email 1.7.0.4 Thanks for the help Sincerely yours Victor Rodriguez > Thanks, > Sekhar > > From david-b at pacbell.net Tue Nov 2 20:15:16 2010 From: david-b at pacbell.net (David Brownell) Date: Tue, 2 Nov 2010 19:15:16 -0700 (PDT) Subject: [PATCH v4 08/12] gpio: add ti-ssp gpio driver In-Reply-To: <1288124308-14999-9-git-send-email-cyril@ti.com> Message-ID: <956998.65651.qm@web180302.mail.gq1.yahoo.com> --- On Tue, 10/26/10, Cyril Chemparathy wrote: > From: Cyril Chemparathy > Subject: [PATCH v4 08/12] gpio: add ti-ssp gpio driver On the assumptions you've tested this *AND* will resubmit with the previously-requested change of removing all references to non-existent "virtual"ness in the driver ... I'll likely add my ack to that resubmitted version Also, chip2gpio seems an excessively generic name; maybe chip2_ti_ssp_gpio or somesuch? I'd still be happier seeing this "stack" packaged as a hardware library called by various drivers (like GPIO, SPI, etc) rather than a core driver that's called by other drivers. That seems like a better structure for various vendors' "SSP" hardware (multifunction serial interface logic) since most function drivers just need to poke the registers a bit differently, and don't have much to share with a "core driver" beyond a few setup routines (if that). - Dave From nsekhar at ti.com Wed Nov 3 00:30:35 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 3 Nov 2010 12:00:35 +0530 Subject: [PATCH v6 9/9] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: References: <1288365045-2709-1-git-send-email-vm.rod25@gmail.com> <1288365045-2709-10-git-send-email-vm.rod25@gmail.com> Message-ID: On Wed, Nov 03, 2010 at 05:30:43, Victor Rodriguez wrote: > >> +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 */ > > > > Do we have this part on the hawkboard as well? > > Yes the TPS2087 is the USB POWER MANAGER that we have on the board > AFAIK this is the same and besides it is working, I have tested by my > self by plugging different kind of USB devices, mas storage , Mouses > and keyboards. Just wanted to make sure we are not simply copying comments over from EVM code. If you have verified this, no action required. > > Also, you are CCing a whole lot of folks on each patch. Instead, you can > > automate CCing the relevant folks using --cc-cmd= option of > > git-send-email > > > > Use --cc-cmd="scripts/get_maintainer.pl --pattern-depth=2 -s" to generate > > list of relevant CCs. > > > > Example: > > > > scripts/get_maintainer.pl -f arch/arm/mach-davinci/board-omapl138-hawk.c --pattern-depth=2 -s > > > > gives: > > > > Kevin Hilman > > Russell King > > davinci-linux-open-source at linux.davincidsp.com > > linux-arm-kernel at lists.infradead.org > > > > Recently Kevin requested all patches to CC Linux ARM Kernel mailing list so this > > should be OK. > > > > scripts/get_maintainer.pl -f sound/soc/davinci/davinci-evm.c --pattern-depth=2 -s > > > > gives: > > > > Liam Girdwood > > Mark Brown > > alsa-devel at alsa-project.org > > > > This is as per what Liam and Mark have been asking for. > > > > Also, use --dry-run with git-send-email before sending actual emails to make > > sure the To and CC lists look good. > > Ok I have done this > > git send-email --dry-run --from "" --to > "" --cc-cmd =scripts/get_maintainer.pl > patches/sendedv6/* You should just try: git send-email --dry-run --from="" --to=davinci-linux-open-source at linux.davincidsp.com --cc-cmd="scripts/get_maintainer.pl --pattern-depth=2 -s" > I just have one question on the --to i think I should put the davinci > mailing list as minimum right ? because the cover letter just will be > send to me Yes. --to= should be davinci-linux-open-source at linux.davincidsp.com. You should automatically be CCed by git-send-email because of your sign-offs. If needed you can add an explicit --cc="" Thanks, Sekhar From david-b at pacbell.net Wed Nov 3 06:35:54 2010 From: david-b at pacbell.net (David Brownell) Date: Wed, 3 Nov 2010 05:35:54 -0700 (PDT) Subject: [PATCH v4 08/12] gpio: add ti-ssp gpio driver In-Reply-To: Message-ID: <129498.93810.qm@web180302.mail.gq1.yahoo.com> --- On Wed, 11/3/10, Linus Walleij wrote: \ > Date: Wednesday, November 3, 2010, 3:18 AM > 2010/11/3 David Brownell : > > > That seems like > > a better structure for various vendors' "SSP" > > hardware (multifunction serial interface logic) > > Incidentally the ARM PrimeCell PL022 is called SSP, > "Synchronous Serial Port" and is not multifunction at all. ISTR coming across that IP module; are you sure that it only supports a single serial protocol, instead of just a "small" variety" (multi)? Unless the hardware only supports one protocol, my point holds. I wasn't implying that modules named SSP have more in common than tending to support more than a single serial protocol. If they only support one, they get names like"USART" or sometimes CODEC. - Dave From sandeepk at verismonetworks.com Tue Nov 2 10:23:41 2010 From: sandeepk at verismonetworks.com (sandeepk at verismonetworks.com) Date: Tue, 02 Nov 2010 21:53:41 +0530 Subject: unsubscribe Message-ID: <20101102215341.11775m06qetnpa8d@webmail.verismonetworks.com> ---------------------------------------------------------------------------- This message was sent using Verismo WebMail, the Internet Messaging Program. From linus.ml.walleij at gmail.com Wed Nov 3 04:18:37 2010 From: linus.ml.walleij at gmail.com (Linus Walleij) Date: Wed, 3 Nov 2010 11:18:37 +0100 Subject: [PATCH v4 08/12] gpio: add ti-ssp gpio driver In-Reply-To: <956998.65651.qm@web180302.mail.gq1.yahoo.com> References: <1288124308-14999-9-git-send-email-cyril@ti.com> <956998.65651.qm@web180302.mail.gq1.yahoo.com> Message-ID: 2010/11/3 David Brownell : > That seems like > a better structure for various vendors' "SSP" > hardware (multifunction serial interface logic) Incidentally the ARM PrimeCell PL022 is called SSP, "Synchronous Serial Port" and is not multifunction at all. Just to add to the confusion... Yours, Linus Walleij From linus.ml.walleij at gmail.com Wed Nov 3 10:24:09 2010 From: linus.ml.walleij at gmail.com (Linus Walleij) Date: Wed, 3 Nov 2010 17:24:09 +0100 Subject: [PATCH v4 08/12] gpio: add ti-ssp gpio driver In-Reply-To: <129498.93810.qm@web180302.mail.gq1.yahoo.com> References: <129498.93810.qm@web180302.mail.gq1.yahoo.com> Message-ID: 2010/11/3 David Brownell : > --- On Wed, 11/3/10, Linus Walleij wrote: >> >> Incidentally the ARM PrimeCell PL022 is called SSP, >> "Synchronous Serial Port" and is not multifunction at all. > > ISTR coming across that IP module; are you sure > that it only supports a single serial protocol, > instead of just a "small" variety" (multi)? > Unless the hardware only supports one protocol, > my point holds. Yeah well: /** * enum ssp_interface - interfaces allowed for this SSP Controller * @SSP_INTERFACE_MOTOROLA_SPI: Motorola Interface * @SSP_INTERFACE_TI_SYNC_SERIAL: Texas Instrument Synchronous Serial * interface * @SSP_INTERFACE_NATIONAL_MICROWIRE: National Semiconductor Microwire * interface * @SSP_INTERFACE_UNIDIRECTIONAL: Unidirectional interface (STn8810 * &STn8815 only) */ enum ssp_interface { SSP_INTERFACE_MOTOROLA_SPI, SSP_INTERFACE_TI_SYNC_SERIAL, SSP_INTERFACE_NATIONAL_MICROWIRE, SSP_INTERFACE_UNIDIRECTIONAL }; If that is what you mean then yes. All of the protols are "SPI type" though. Yours, Linus Walleij From vm.rod25 at gmail.com Wed Nov 3 12:04:37 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:04:37 -0600 Subject: [PATCH v6 0/9] Add Omapl138-Hawkboard support Message-ID: <1288807477-31563-1-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds EMAC, EDMA, ASoC, SOUND, 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 (9): davinci: EMAC support for Omapl138-Hawkboard davinci: EDMA support for Omapl138-Hawkboard davinci: ASoC support for Omapl138-Hawkboard davinci: McASP configuration for Omapl138-Hawkboard davinci: Audio support for Omapl138-Hawkboard davinci: MMC/SD and USB-OHCI configuration for Omapl138-Hawkboard davinci: MMC/SD support for Omapl138-Hawkboar davinci: USB clocks for Omapl138-Hawkboard davinci: USB1.1 support for Omapl138-Hawkboard arch/arm/mach-davinci/board-omapl138-hawk.c | 312 +++++++++++++++++++++++++++ arch/arm/mach-davinci/da850.c | 22 ++- arch/arm/mach-davinci/include/mach/mux.h | 4 + sound/soc/davinci/Kconfig | 5 +- sound/soc/davinci/davinci-evm.c | 6 +- 5 files changed, 344 insertions(+), 5 deletions(-) From vm.rod25 at gmail.com Wed Nov 3 12:05:18 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:05:18 -0600 Subject: [PATCH v6 1/9] davinci: EMAC support for Omapl138-Hawkboard Message-ID: <1288807526-31761-1-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 --- 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 c472dd8..62d35f0 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 Nov 3 12:05:19 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:05:19 -0600 Subject: [PATCH v6 2/9] davinci: EDMA support for Omapl138-Hawkboard In-Reply-To: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> References: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288807526-31761-2-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 --- 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 62d35f0..114fc9b 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 Nov 3 12:05:20 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:05:20 -0600 Subject: [PATCH v6 3/9] davinci: ASoC support for Omapl138-Hawkboard In-Reply-To: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> References: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288807526-31761-3-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds ASoC support for the Hawkboard-L138 system Signed-off-by: Victor Rodriguez --- sound/soc/davinci/Kconfig | 5 +++-- sound/soc/davinci/davinci-evm.c | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index 6bbf001..72c6752 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -76,8 +76,9 @@ config SND_DA830_SOC_EVM DA830/OMAP-L137 EVM config SND_DA850_SOC_EVM - tristate "SoC Audio support for DA850/OMAP-L138 EVM" - depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA850_EVM + tristate "SoC Audio support for DA850/OMAP-L138 EVM/Hawkboard" + depends on SND_DAVINCI_SOC && (MACH_DAVINCI_DA850_EVM || \ + MACH_OMAPL138_HAWKBOARD) select SND_DAVINCI_SOC_MCASP select SND_SOC_TLV320AIC3X help diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 97f74d6..73093eb 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -59,7 +59,8 @@ static int evm_hw_params(struct snd_pcm_substream *substream, sysclk = 12288000; else if (machine_is_davinci_da830_evm() || - machine_is_davinci_da850_evm()) + machine_is_davinci_da850_evm() || + machine_is_omapl138_hawkboard()) sysclk = 24576000; else @@ -311,7 +312,8 @@ static int __init evm_init(void) } else if (machine_is_davinci_da830_evm()) { evm_snd_dev_data = &da830_evm_snd_devdata; index = 1; - } else if (machine_is_davinci_da850_evm()) { + } else if (machine_is_davinci_da850_evm() || + machine_is_omapl138_hawkboard()) { evm_snd_dev_data = &da850_evm_snd_devdata; index = 0; } else -- 1.7.0.4 From vm.rod25 at gmail.com Wed Nov 3 12:05:21 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:05:21 -0600 Subject: [PATCH v6 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> References: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288807526-31761-4-git-send-email-vm.rod25@gmail.com> 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 --- 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, -1 }; -- 1.7.0.4 From vm.rod25 at gmail.com Wed Nov 3 12:05:22 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:05:22 -0600 Subject: [PATCH v6 5/9] davinci: Audio support for Omapl138-Hawkboard In-Reply-To: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> References: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288807526-31761-5-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds sound support for the Hawkboard-L138 system Signed-off-by: Victor Rodriguez --- Notes: This patch works with da8xx_omapl_defconfig In order to test ALSA utils select in menuconfig like insmodule: Sound card support ---> Advanced Linux Sound Architecture ---> ALSA for SoC audio support ---> SoC Audio for the TI DAVINCI chip SoC Audio support for DA850/OMAP-L138 EVM/Hawkboard arch/arm/mach-davinci/board-omapl138-hawk.c | 46 +++++++++++++++++++++++++++ 1 files changed, 46 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index 114fc9b..115dac0 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -110,6 +111,38 @@ static struct edma_rsv_info *da850_edma_rsv[2] = { &da850_edma_cc1_rsv, }; +static struct i2c_board_info __initdata omapl138_hawk_i2c_devices[] = { + { + I2C_BOARD_INFO("tlv320aic3x", 0x18), + }, +}; + +static struct davinci_i2c_platform_data omapl138_hawk_i2c_0_pdata = { + .bus_freq = 100, /* kHz */ + .bus_delay = 0, /* usec */ +}; + +/* davinci Hawkboard audio machine driver */ +static u8 da850_iis_serializer_direction[] = { + INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, + INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, + INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, + INACTIVE_MODE, TX_MODE, RX_MODE, INACTIVE_MODE, +}; + +static struct snd_platform_data omapl138_hawk_snd_data = { + .tx_dma_offset = 0x2000, + .rx_dma_offset = 0x2000, + .op_mode = DAVINCI_MCASP_IIS_MODE, + .num_serializer = ARRAY_SIZE(da850_iis_serializer_direction), + .tdm_slots = 2, + .serial_dir = da850_iis_serializer_direction, + .asp_chan_q = EVENTQ_1, + .version = MCASP_VERSION_2, + .txnumevt = 1, + .rxnumevt = 1, +}; + static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { .enabled_uarts = 0x7, }; @@ -127,6 +160,19 @@ static __init void omapl138_hawk_init(void) pr_warning("%s: EDMA registration failed: %d\n", __func__, ret); + i2c_register_board_info(1, omapl138_hawk_i2c_devices, + ARRAY_SIZE(omapl138_hawk_i2c_devices)); + + ret = da8xx_register_i2c(0, &omapl138_hawk_i2c_0_pdata); + if (ret) + pr_warning("%s: i2c0 registration failed: %d\n", + __func__, ret); + + ret = davinci_cfg_reg_list(da850_mcasp_pins); + if (ret) + pr_warning("%s: mcasp mux setup failed: %d\n", __func__, ret); + da8xx_register_mcasp(0, &omapl138_hawk_snd_data); + ret = da8xx_register_watchdog(); if (ret) pr_warning("omapl138_hawk_init: " -- 1.7.0.4 From vm.rod25 at gmail.com Wed Nov 3 12:05:24 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:05:24 -0600 Subject: [PATCH v6 7/9] davinci: MMC/SD support for Omapl138-Hawkboar In-Reply-To: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> References: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288807526-31761-7-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 --- 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 115dac0..02e54fa 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -23,6 +23,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, @@ -143,6 +145,65 @@ static struct snd_platform_data omapl138_hawk_snd_data = { .rxnumevt = 1, }; +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, }; @@ -173,6 +234,8 @@ static __init void omapl138_hawk_init(void) pr_warning("%s: mcasp mux setup failed: %d\n", __func__, ret); da8xx_register_mcasp(0, &omapl138_hawk_snd_data); + 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 Nov 3 12:05:25 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:05:25 -0600 Subject: [PATCH v6 8/9] davinci: USB clocks for Omapl138-Hawkboard In-Reply-To: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> References: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288807526-31761-8-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 --- 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 4458bff..b3b1adb 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 Nov 3 12:05:26 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:05:26 -0600 Subject: [PATCH v6 9/9] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> References: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288807526-31761-9-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 --- 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 US 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 02e54fa..ec3d95e 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -26,6 +26,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, @@ -204,6 +207,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, }; @@ -236,6 +340,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 vm.rod25 at gmail.com Wed Nov 3 12:14:30 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:14:30 -0600 Subject: [PATCH v7 0/9] Add Omapl138-Hawkboard support Message-ID: <1288808070-2309-1-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds EMAC, EDMA, ASoC, SOUND, 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 (9): davinci: EMAC support for Omapl138-Hawkboard davinci: EDMA support for Omapl138-Hawkboard davinci: ASoC support for Omapl138-Hawkboard davinci: McASP configuration for Omapl138-Hawkboard davinci: Audio support for Omapl138-Hawkboard davinci: MMC/SD and USB-OHCI configuration for Omapl138-Hawkboard davinci: MMC/SD support for Omapl138-Hawkboar davinci: USB clocks for Omapl138-Hawkboard davinci: USB1.1 support for Omapl138-Hawkboard arch/arm/mach-davinci/board-omapl138-hawk.c | 312 +++++++++++++++++++++++++++ arch/arm/mach-davinci/da850.c | 22 ++- arch/arm/mach-davinci/include/mach/mux.h | 4 + sound/soc/davinci/Kconfig | 5 +- sound/soc/davinci/davinci-evm.c | 6 +- 5 files changed, 344 insertions(+), 5 deletions(-) From vm.rod25 at gmail.com Wed Nov 3 12:15:06 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:15:06 -0600 Subject: [PATCH v7 1/9] davinci: EMAC support for Omapl138-Hawkboard Message-ID: <1288808115-2661-1-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 --- 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 c472dd8..62d35f0 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 Nov 3 12:15:07 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:15:07 -0600 Subject: [PATCH v7 2/9] davinci: EDMA support for Omapl138-Hawkboard In-Reply-To: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> References: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288808115-2661-2-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 --- 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 62d35f0..114fc9b 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 Nov 3 12:15:08 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:15:08 -0600 Subject: [PATCH v7 3/9] davinci: ASoC support for Omapl138-Hawkboard In-Reply-To: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> References: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288808115-2661-3-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds ASoC support for the Hawkboard-L138 system Signed-off-by: Victor Rodriguez --- sound/soc/davinci/Kconfig | 5 +++-- sound/soc/davinci/davinci-evm.c | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index 6bbf001..72c6752 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -76,8 +76,9 @@ config SND_DA830_SOC_EVM DA830/OMAP-L137 EVM config SND_DA850_SOC_EVM - tristate "SoC Audio support for DA850/OMAP-L138 EVM" - depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA850_EVM + tristate "SoC Audio support for DA850/OMAP-L138 EVM/Hawkboard" + depends on SND_DAVINCI_SOC && (MACH_DAVINCI_DA850_EVM || \ + MACH_OMAPL138_HAWKBOARD) select SND_DAVINCI_SOC_MCASP select SND_SOC_TLV320AIC3X help diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 97f74d6..73093eb 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -59,7 +59,8 @@ static int evm_hw_params(struct snd_pcm_substream *substream, sysclk = 12288000; else if (machine_is_davinci_da830_evm() || - machine_is_davinci_da850_evm()) + machine_is_davinci_da850_evm() || + machine_is_omapl138_hawkboard()) sysclk = 24576000; else @@ -311,7 +312,8 @@ static int __init evm_init(void) } else if (machine_is_davinci_da830_evm()) { evm_snd_dev_data = &da830_evm_snd_devdata; index = 1; - } else if (machine_is_davinci_da850_evm()) { + } else if (machine_is_davinci_da850_evm() || + machine_is_omapl138_hawkboard()) { evm_snd_dev_data = &da850_evm_snd_devdata; index = 0; } else -- 1.7.0.4 From vm.rod25 at gmail.com Wed Nov 3 12:15:09 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:15:09 -0600 Subject: [PATCH v7 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> References: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288808115-2661-4-git-send-email-vm.rod25@gmail.com> 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 --- 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, -1 }; -- 1.7.0.4 From vm.rod25 at gmail.com Wed Nov 3 12:15:10 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:15:10 -0600 Subject: [PATCH v7 5/9] davinci: Audio support for Omapl138-Hawkboard In-Reply-To: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> References: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288808115-2661-5-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds sound support for the Hawkboard-L138 system Signed-off-by: Victor Rodriguez --- Notes: This patch works with da8xx_omapl_defconfig In order to test ALSA utils select in menuconfig like insmodule: Sound card support ---> Advanced Linux Sound Architecture ---> ALSA for SoC audio support ---> SoC Audio for the TI DAVINCI chip SoC Audio support for DA850/OMAP-L138 EVM/Hawkboard arch/arm/mach-davinci/board-omapl138-hawk.c | 46 +++++++++++++++++++++++++++ 1 files changed, 46 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index 114fc9b..115dac0 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -110,6 +111,38 @@ static struct edma_rsv_info *da850_edma_rsv[2] = { &da850_edma_cc1_rsv, }; +static struct i2c_board_info __initdata omapl138_hawk_i2c_devices[] = { + { + I2C_BOARD_INFO("tlv320aic3x", 0x18), + }, +}; + +static struct davinci_i2c_platform_data omapl138_hawk_i2c_0_pdata = { + .bus_freq = 100, /* kHz */ + .bus_delay = 0, /* usec */ +}; + +/* davinci Hawkboard audio machine driver */ +static u8 da850_iis_serializer_direction[] = { + INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, + INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, + INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, + INACTIVE_MODE, TX_MODE, RX_MODE, INACTIVE_MODE, +}; + +static struct snd_platform_data omapl138_hawk_snd_data = { + .tx_dma_offset = 0x2000, + .rx_dma_offset = 0x2000, + .op_mode = DAVINCI_MCASP_IIS_MODE, + .num_serializer = ARRAY_SIZE(da850_iis_serializer_direction), + .tdm_slots = 2, + .serial_dir = da850_iis_serializer_direction, + .asp_chan_q = EVENTQ_1, + .version = MCASP_VERSION_2, + .txnumevt = 1, + .rxnumevt = 1, +}; + static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { .enabled_uarts = 0x7, }; @@ -127,6 +160,19 @@ static __init void omapl138_hawk_init(void) pr_warning("%s: EDMA registration failed: %d\n", __func__, ret); + i2c_register_board_info(1, omapl138_hawk_i2c_devices, + ARRAY_SIZE(omapl138_hawk_i2c_devices)); + + ret = da8xx_register_i2c(0, &omapl138_hawk_i2c_0_pdata); + if (ret) + pr_warning("%s: i2c0 registration failed: %d\n", + __func__, ret); + + ret = davinci_cfg_reg_list(da850_mcasp_pins); + if (ret) + pr_warning("%s: mcasp mux setup failed: %d\n", __func__, ret); + da8xx_register_mcasp(0, &omapl138_hawk_snd_data); + ret = da8xx_register_watchdog(); if (ret) pr_warning("omapl138_hawk_init: " -- 1.7.0.4 From vm.rod25 at gmail.com Wed Nov 3 12:15:12 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:15:12 -0600 Subject: [PATCH v7 7/9] davinci: MMC/SD support for Omapl138-Hawkboar In-Reply-To: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> References: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288808115-2661-7-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 --- 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 115dac0..02e54fa 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -23,6 +23,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, @@ -143,6 +145,65 @@ static struct snd_platform_data omapl138_hawk_snd_data = { .rxnumevt = 1, }; +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, }; @@ -173,6 +234,8 @@ static __init void omapl138_hawk_init(void) pr_warning("%s: mcasp mux setup failed: %d\n", __func__, ret); da8xx_register_mcasp(0, &omapl138_hawk_snd_data); + 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 Nov 3 12:15:13 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:15:13 -0600 Subject: [PATCH v7 8/9] davinci: USB clocks for Omapl138-Hawkboard In-Reply-To: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> References: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288808115-2661-8-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 --- 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 4458bff..b3b1adb 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 Nov 3 12:15:14 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:15:14 -0600 Subject: [PATCH v7 9/9] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> References: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288808115-2661-9-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 --- 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 US 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 02e54fa..ec3d95e 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -26,6 +26,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, @@ -204,6 +207,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, }; @@ -236,6 +340,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 vm.rod25 at gmail.com Wed Nov 3 12:19:09 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:19:09 -0600 Subject: [PATCH v7 6/9] davinci: MMC/SD and USB-OHCI configuration for Omapl138-Hawkboard Message-ID: <1288808349-3896-1-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 --- 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 f033a0a..4458bff 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 luna.id at gmail.com Fri Nov 5 12:36:05 2010 From: luna.id at gmail.com (Nicolas Luna) Date: Fri, 5 Nov 2010 14:36:05 -0400 Subject: Touchscreen & PMIC Message-ID: Hi, I'm running kernel v2.6.34 from *linux/kernel/git/khilman/linux-davinci.git* */ *on custom hardware based on OMAP-L138 EVM. I got tps65070 to manage power, touchscreen and LCD backlight. My touchscreen is working fine but when I try to communicate with my PMIC by I2C, I got this error "errno 16 Device or resource busy". If I disable the touchscreen driver, everything is fine and I can modify PMIC registers without problem. Any ideas? Thanks Nicolas -------------- next part -------------- An HTML attachment was scrubbed... URL: From manjunath.hadli at ti.com Mon Nov 8 08:54:05 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Mon, 8 Nov 2010 20:24:05 +0530 Subject: [PATCH 0/6] davinci vpbe: V4L2 Display driver for DM644X Message-ID: <1289228045-4512-1-git-send-email-manjunath.hadli@ti.com> This driver is written for Texas Instruments's DM644X VPBE IP. This SoC supports 2 video planes and 2 OSD planes as part of its OSD (On Screen Display) block. The OSD lanes predminantly support RGB space and the Video planes support YUV data. Out of these 4, the 2 video planes are supported as part of the V4L2 driver. These would be enumerated as video2 and video3 dev nodes. The blending and video timing generator unit (VENC- for Video Encoder) is the unit which combines/blends the output of these 4 planes into a single stream and this output is given to Video input devices like TV and other digital LCDs. The software for VENC is designed as a subdevice with support for SD(NTSC and PAL) modes and 2 outputs. This SoC forms the iniial implementation of its later additions like DM355 and DM365. Muralidharan Karicheri (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 | 85 +- arch/arm/mach-davinci/dm644x.c | 181 ++- 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 | 861 ++++++++++ drivers/media/video/davinci/vpbe_display.c | 2283 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd.c | 1208 ++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++ drivers/media/video/davinci/vpbe_venc.c | 617 +++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++ include/media/davinci/vpbe.h | 187 +++ include/media/davinci/vpbe_display.h | 144 ++ include/media/davinci/vpbe_osd.h | 397 +++++ include/media/davinci/vpbe_types.h | 170 ++ include/media/davinci/vpbe_venc.h | 70 + 16 files changed, 6790 insertions(+), 19 deletions(-) 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 Nov 8 08:54:16 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Mon, 8 Nov 2010 20:24:16 +0530 Subject: [PATCH 1/6] davinci vpbe: V4L2 display driver for DM644X SoC Message-ID: <1289228056-4621-1-git-send-email-manjunath.hadli@ti.com> From: Muralidharan Karicheri 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: Muralidharan Karicheri Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/vpbe_display.c | 2283 ++++++++++++++++++++++++++++ include/media/davinci/vpbe_display.h | 144 ++ include/media/davinci/vpbe_types.h | 170 ++ 3 files changed, 2597 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..b02a98f --- /dev/null +++ b/drivers/media/video/davinci/vpbe_display.c @@ -0,0 +1,2283 @@ +/* + * 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 { + /* + * Mark status of the curFrm to + * done and unlock semaphore on it + */ + if (layer->curFrm != layer->nextFrm) { + layer->curFrm->ts = timevalue; + layer->curFrm->state = VIDEOBUF_DONE; + wake_up_interruptible( + &layer->curFrm->done); + /* Make curFrm pointing to nextFrm */ + layer->curFrm = layer->nextFrm; + } + } + /* Get the next buffer from buffer queue */ + layer->nextFrm = + list_entry(layer->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove that buffer from the buffer queue */ + list_del(&layer->nextFrm->queue); + /* Mark status of the buffer as active */ + layer->nextFrm->state = VIDEOBUF_ACTIVE; + + addr = videobuf_to_dma_contig(layer->nextFrm); + 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->curFrm == layer->nextFrm) + continue; + /* + * one frame is displayed If next frame is + * available, release curFrm and move on + * copy frame display time + */ + layer->curFrm->ts = timevalue; + /* Change status of the curFrm */ + layer->curFrm->state = VIDEOBUF_DONE; + /* unlock semaphore on curFrm */ + wake_up_interruptible(&layer->curFrm->done); + /* Make curFrm pointing to nextFrm */ + layer->curFrm = layer->nextFrm; + } else if (1 == fid) { /* odd field */ + if (list_empty(&layer->dma_queue) + || (layer->curFrm != layer->nextFrm)) + 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->nextFrm = list_entry(layer-> + dma_queue. + next, struct + videobuf_buffer, + queue); + + /* Remove that from the buffer queue */ + list_del(&layer->nextFrm->queue); + + /* Mark state of the frame to active */ + layer->nextFrm->state = VIDEOBUF_ACTIVE; + addr = videobuf_to_dma_contig(layer->nextFrm); + 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; + } + + 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; + } + } + 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); + /* 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->curFrm); + /* 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; + 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 (!strcmp(vpbe_dev->current_timings.name, + VID_ENC_STD_NTSC) || + !strcmp(vpbe_dev->current_timings.name, + VID_ENC_STD_PAL)) { + 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 (!strcmp(vpbe_dev->current_timings.name, + VID_ENC_STD_PAL)) { + 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; + 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 filed 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; + } + + 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); + + 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; + *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->nextFrm = layer->curFrm = list_entry(layer->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove buffer from the buffer queue */ + list_del(&layer->curFrm->queue); + /* Mark state of the current frame to active */ + layer->curFrm->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); + 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, + .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..235a8d4 --- /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 *curFrm; + /* Pointer pointing to next v4l2_buffer */ + struct videobuf_buffer *nextFrm; + /* 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 */ + /* 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..9a4c877 --- /dev/null +++ b/include/media/davinci/vpbe_types.h @@ -0,0 +1,170 @@ +/* + * 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, +}; + +enum vpbe_display_modes { + VPBE_MODE_SDTV, + VPBE_MODE_EDTV, + VPBE_MODE_HDTV, + VPBE_MODE_LCD +}; + +/* 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; +}; + +/* vpbe output interface types */ +enum vpbe_if_types { + VPBE_ANALOG_TV_IF, + VPBE_DIGITAL_IF_YCC8, + VPBE_DIGITAL_IF_YCC16, + VPBE_DIGITAL_IF_SRGB, + VPBE_DIGITAL_IF_PRGB, +}; + +/* RGB modes */ +enum vpbe_rgb_modes { + VPBE_RGB565, + VPBE_RGB666, + VPBE_RGB888, +}; + +struct vpbe_if_params { + enum vpbe_if_types if_type; + /* + * Bool to indicate if Y and C outputs to be swapped. This is applicable + * for YCC8 and YCC16 + */ + unsigned int swap_yc:1; + /* Bool to indicate if BT.656 is enabled. Applicable for YCC8/YCC16 */ + unsigned int bt656:1; + /* Enable RGB666. Applicable for PRGB if type */ + enum vpbe_rgb_modes rgb_mode; +}; + +/* + * struct vpbe_enc_fract + * @numerator: numerator part of a fractional number + * @denominator: denominator part of a fractional number + * + * Description: + * Structure used to represent fractional numbers + */ +struct vpbe_enc_fract { + unsigned int numerator; + unsigned int denominator; +}; + +/* + * 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 vpbe_enc_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; +}; + +/** + * constant strings for standard names or mode names. All modules uses this to + * refer a specific standard or mode name + */ +#define VID_ENC_STD_NTSC "NTSC" +#define VID_ENC_STD_NTSC_RGB "NTSC-RGB" +#define VID_ENC_STD_PAL "PAL" +#define VID_ENC_STD_PAL_RGB "PAL-RGB" +#define VID_ENC_STD_720P_24 "720P-24" +#define VID_ENC_STD_720P_25 "720P-25" +#define VID_ENC_STD_720P_30 "720P-30" +#define VID_ENC_STD_720P_50 "720P-50" +#define VID_ENC_STD_720P_60 "720P-60" +#define VID_ENC_STD_1080I_25 "1080I-25" +#define VID_ENC_STD_1080I_30 "1080I-30" +#define VID_ENC_STD_1080P_24 "1080P-24" +#define VID_ENC_STD_1080P_25 "1080P-25" +#define VID_ENC_STD_1080P_30 "1080P-30" +#define VID_ENC_STD_1080P_50 "1080P-50" +#define VID_ENC_STD_1080P_60 "1080P-60" +#define VID_ENC_STD_480P_60 "480P-60" +#define VID_ENC_STD_576P_50 "576P-50" +#define VID_ENC_STD_640x480 "640x480" +#define VID_ENC_STD_640x400 "640x400" +#define VID_ENC_STD_640x350 "640x350" +#define VID_ENC_STD_800x480 "800x480" +#define VID_ENC_STD_NON_STANDARD "NON-STANDARD" + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Mon Nov 8 08:54:25 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Mon, 8 Nov 2010 20:24:25 +0530 Subject: [PATCH 2/6] davinci vpbe: VPBE display driver Message-ID: <1289228065-4699-1-git-send-email-manjunath.hadli@ti.com> From: Muralidharan Karicheri 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: Muralidharan Karicheri Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/vpbe.c | 861 ++++++++++++++++++++++++++++++++++++ include/media/davinci/vpbe.h | 187 ++++++++ 2 files changed, 1048 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpbe.c create mode 100644 include/media/davinci/vpbe.h diff --git a/drivers/media/video/davinci/vpbe.c b/drivers/media/video/davinci/vpbe.c new file mode 100644 index 0000000..17ff1e7 --- /dev/null +++ b/drivers/media/video/davinci/vpbe.c @@ -0,0 +1,861 @@ +/* + * 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 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; + + memcpy(output, &vpbe_config->outputs[temp_index], sizeof(*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; + 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++) { + if (!strcmp(mode, + vpbe_dev->cfg->outputs[curr_output].modes[i].name)) { + memcpy(&vpbe_dev->current_timings, + &vpbe_dev->cfg->outputs[curr_output].modes[i], + sizeof(vpbe_dev->current_timings)); + 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; + + memcpy(mode_info, &vpbe_dev->current_timings, + sizeof(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; + int curr_output = vpbe_dev->current_out_index, i; + + for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) { + + if ((cfg->outputs[curr_output].modes[i].timings_type & + VPBE_ENC_DV_PRESET) && + (cfg->outputs[curr_output].modes[i].timings.dv_preset == + dv_preset)) { + memcpy(&vpbe_dev->current_timings, + &vpbe_dev->cfg->outputs[curr_output].modes[i], + sizeof(vpbe_dev->current_timings)); + 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; + int curr_output = vpbe_dev->current_out_index, i; + + for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) { + if ((cfg->outputs[curr_output].modes[i].timings_type & + VPBE_ENC_STD) && + (cfg->outputs[curr_output].modes[i].timings.std_id & + std_id)) { + memcpy(&vpbe_dev->current_timings, + &vpbe_dev->cfg->outputs[curr_output].modes[i], + sizeof(vpbe_dev->current_timings)); + 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; + int curr_output = vpbe_dev->current_out_index, i; + + for (i = 0; i < vpbe_dev->cfg->outputs[curr_output].num_modes; i++) { + if (!strcmp(cfg->outputs[curr_output].modes[i].name, + std_name)) { + memcpy(&vpbe_dev->current_timings, + &vpbe_dev->cfg->outputs[curr_output].modes[i], + sizeof(vpbe_dev->current_timings)); + 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; + struct vpbe_if_params *if_params; + + 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_params = &vpbe_config->outputs[index].if_params; + ret = v4l2_subdev_call(vpbe_dev->encoders[sd_index], + core, s_config, 0, if_params); + 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, 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) +{ + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { + *std_id = vpbe_dev->current_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) { + memcpy(&vpbe_dev->current_timings, + preset_mode, sizeof(*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 osd_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); + + 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, + osd_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; + ret = v4l2_subdev_call(vpbe_dev->encoders[output_index], + core, s_config, 0, + &vpbe_dev->cfg->outputs[output_index].if_params); + 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) + memcpy(&vpbe_dev->current_timings, + &vpbe_dev->cfg->outputs[0].modes[0], + sizeof(vpbe_dev->current_timings)); + 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..81f233a --- /dev/null +++ b/include/media/davinci/vpbe.h @@ -0,0 +1,187 @@ +/* + * 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 + */ + struct vpbe_if_params if_params; +}; + +/* 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 Mon Nov 8 08:54:35 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Mon, 8 Nov 2010 20:24:35 +0530 Subject: [PATCH 3/6] davinci vpbe: OSD(On Screen Display ) block Message-ID: <1289228075-4736-1-git-send-email-manjunath.hadli@ti.com> From: Muralidharan Karicheri 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: Muralidharan Karicheri Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/vpbe_osd.c | 1208 +++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++++++ include/media/davinci/vpbe_osd.h | 397 +++++++++ 3 files changed, 1994 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..64371b5 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd.c @@ -0,0 +1,1208 @@ +/* + * 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; + + 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 || + !win->lconfig.line_length || + !win->lconfig.xsize || + !win->lconfig.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 (win->lconfig.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; + + 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, + &win->lconfig); + } + + 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]; + 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) + && (win->lconfig.pixfmt != PIXFMT_OSD_ATTR)) + || ((lconfig->pixfmt != PIXFMT_OSD_ATTR) + && (win->lconfig.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) + && (win->lconfig.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) + && (win->lconfig.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 != win->lconfig.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); + } + } + } + + win->lconfig = *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, + &win->lconfig); + } + + 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; + 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); + + win->lconfig.line_length = 0; + win->lconfig.xsize = 0; + win->lconfig.ysize = 0; + win->lconfig.xpos = 0; + win->lconfig.ypos = 0; + win->lconfig.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. + */ + win->lconfig.pixfmt = PIXFMT_8BPP; + _osd_set_layer_config(sd, layer, &win->lconfig); + 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: + win->lconfig.pixfmt = osd->yc_pixfmt; + _osd_set_layer_config(sd, layer, &win->lconfig); + 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 Mon Nov 8 08:55:32 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Mon, 8 Nov 2010 20:25:32 +0530 Subject: [PATCH 4/6] davinci vpbe: VENC( Video Encoder) implementation Message-ID: <1289228132-5105-1-git-send-email-manjunath.hadli@ti.com> From: Muralidharan Karicheri 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: Muralidharan Karicheri Signed-off-by: Manjunath Hadli --- drivers/media/video/davinci/vpbe_venc.c | 617 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 189 ++++++++ include/media/davinci/vpbe_venc.h | 70 +++ 3 files changed, 876 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..3bfac47 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc.c @@ -0,0 +1,617 @@ +/* + * 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 vpbe_if_params *if_params; + 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_MODE_SDTV) < 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_MODE_SDTV) < 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_MODE_EDTV) < 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_MODE_EDTV) < 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_set_if_config(struct v4l2_subdev *sd, int irq, void *pdata) +{ + struct vpbe_if_params *if_params = (struct vpbe_if_params *)pdata; + struct venc_state *venc = to_state(sd); + unsigned int val = 0; + int ret = 0; + + v4l2_dbg(debug, 2, sd, "venc_set_if_config\n"); + + switch (if_params->if_type) { + case VPBE_DIGITAL_IF_YCC8: + val = VENC_VMOD_VDMD_YCBCR8 << VENC_VMOD_VDMD_SHIFT; + if (if_params->bt656) + venc_modify(sd, VENC_YCCCTL, 1, 1); + break; + case VPBE_DIGITAL_IF_YCC16: + val = VENC_VMOD_VDMD_YCBCR16 << VENC_VMOD_VDMD_SHIFT; + if (if_params->bt656) + venc_modify(sd, VENC_YCCCTL, 1, 1); + break; + case VPBE_DIGITAL_IF_SRGB: + val = VENC_VMOD_VDMD_RGB666 << VENC_VMOD_VDMD_SHIFT; + break; + case VPBE_DIGITAL_IF_PRGB: + val = VENC_VMOD_VDMD_RGB666 << VENC_VMOD_VDMD_SHIFT; + break; + case VPBE_ANALOG_TV_IF: + /* do nothing */ + break; + default: + ret = -EINVAL; + break; + } + if (!ret) { + venc_modify(sd, VENC_VMOD, val, VENC_VMOD_VDMD); + venc->if_params = if_params; + } + return ret; +} + + +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 = { + .s_config = venc_set_if_config, +#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..203072e --- /dev/null +++ b/include/media/davinci/vpbe_venc.h @@ -0,0 +1,70 @@ +/* + * 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_callback + * @next: used internally by the venc driver to maintain a liked list of + * callbacks + * @mask: a bitmask specifying the venc event(s) for which the + * callback will be invoked + * @handler: the callback routine + * @arg: a null pointer that is passed as the second argument to the callback + * routine + */ +/* +struct venc_callback { + struct venc_callback *next; + char *owner; + unsigned mask; + void (*handler) (unsigned event, void *arg); + void *arg; +}; + +struct v4l2_subdev_vpbe_venc_ops { + int (*register_callback)(struct v4l2_subdev *sd, + struct venc_callback *callback); + int (*unregister_callback)(struct v4l2_subdev *sd, + struct venc_callback *callback); + int (*configure_venc)(struct v4l2_subdev *sd, + struct vpbe_enc_mode_info *mode); + int (*is_second_field)(struct v4l2_subdev *sd); +}; +*/ +struct venc_platform_data { + enum vpbe_types venc_type; + int (*setup_pinmux)(enum vpbe_if_types if_type, + enum vpbe_rgb_modes rgb_mode, + int field); + int (*setup_clock)(enum vpbe_display_modes mode); + /* Number of LCD outputs supported */ + int num_lcd_outputs; + struct vpbe_if_params *lcd_if_params; +}; + + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Mon Nov 8 08:55:44 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Mon, 8 Nov 2010 20:25:44 +0530 Subject: [PATCH 5/6] davinci vpbe: platform specific additions Message-ID: <1289228144-5218-1-git-send-email-manjunath.hadli@ti.com> From: Muralidharan Karicheri 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: Muralidharan Karicheri Signed-off-by: Manjunath Hadli --- arch/arm/mach-davinci/board-dm644x-evm.c | 85 +++++++++++-- arch/arm/mach-davinci/dm644x.c | 181 ++++++++++++++++++++++++++- arch/arm/mach-davinci/include/mach/dm644x.h | 4 + 3 files changed, 251 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..b275d8a 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,77 @@ 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, + .if_params = { + .if_type = VPBE_ANALOG_TV_IF, + }, + }, + { + .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, + .if_params = { + .if_type = VPBE_ANALOG_TV_IF, + }, + }, +}; + +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 +677,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..bc62123 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -640,6 +640,159 @@ 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); +static int dm644x_vpbe_setup_pinmux(enum vpbe_if_types if_type, + enum vpbe_rgb_modes rgb_mode, + int field) +{ + int ret = 0; + + switch (if_type) { + case VPBE_DIGITAL_IF_PRGB: + if (rgb_mode == VPBE_RGB666) { + davinci_cfg_reg(DM644X_GPIO46_47); + davinci_cfg_reg(DM644X_GPIO0); + davinci_cfg_reg(DM644X_RGB666); + davinci_cfg_reg(DM644X_LOEEN); + davinci_cfg_reg(DM644X_GPIO3); + } else + ret = -EINVAL; + break; + case VPBE_DIGITAL_IF_YCC16: + if (field) + davinci_cfg_reg(DM644X_LFLDEN); + else + davinci_cfg_reg(DM644X_GPIO3); + davinci_cfg_reg(DM644X_LOEEN); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +#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_display_modes mode) +{ + int ret = 0; + + if (NULL == vpss_clkctl_reg) + return -EINVAL; + + switch (mode) { + case VPBE_MODE_HDTV: + /* + * For HD, use external clock source since HD requires higher + * clock rate + */ + __raw_writel(0xa, vpss_clkctl_reg); + break; + case VPBE_MODE_LCD: + case VPBE_MODE_SDTV: + __raw_writel(0x18, vpss_clkctl_reg); + break; + case VPBE_MODE_EDTV: + __raw_writel(0x19, vpss_clkctl_reg); + break; + default: + ret = -EINVAL; + } + return ret; +} + +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_pinmux = dm644x_vpbe_setup_pinmux, + .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 +920,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 Mon Nov 8 08:55:57 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Mon, 8 Nov 2010 20:25:57 +0530 Subject: [PATCH 6/6] davinci vpbe: Build infrastructure for VPBE driver Message-ID: <1289228157-5366-1-git-send-email-manjunath.hadli@ti.com> From: Muralidharan Karicheri This patch adds the build infra-structure for Davinci VPBE dislay driver. Signed-off-by: Muralidharan Karicheri Signed-off-by: Manjunath Hadli --- 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 vm.rod25 at gmail.com Wed Nov 3 12:05:23 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:05:23 -0600 Subject: [PATCH v6 6/9] davinci: MMC/SD and USB-OHCI configuration for Omapl138-Hawkboard In-Reply-To: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> References: <1288807526-31761-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288807526-31761-6-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 --- 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 f033a0a..4458bff 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 Nov 3 12:15:11 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Wed, 3 Nov 2010 12:15:11 -0600 Subject: [PATCH v7 6/9] davinci: MMC/SD and USB-OHCI configuration for Omapl138-Hawkboard In-Reply-To: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> References: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1288808115-2661-6-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 --- 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 f033a0a..4458bff 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 hverkuil at xs4all.nl Tue Nov 9 01:49:40 2010 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Tue, 9 Nov 2010 08:49:40 +0100 Subject: [PATCH 1/6] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: <1289228056-4621-1-git-send-email-manjunath.hadli@ti.com> References: <1289228056-4621-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <201011090849.40940.hverkuil@xs4all.nl> On Monday, November 08, 2010 15:54:16 Manjunath Hadli wrote: > From: Muralidharan Karicheri > > 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: Muralidharan Karicheri > Signed-off-by: Manjunath Hadli > --- > drivers/media/video/davinci/vpbe_display.c | 2283 ++++++++++++++++++++++++++++ > include/media/davinci/vpbe_display.h | 144 ++ > include/media/davinci/vpbe_types.h | 170 ++ > 3 files changed, 2597 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..b02a98f > --- /dev/null > +++ b/drivers/media/video/davinci/vpbe_display.c > @@ -0,0 +1,2283 @@ > +/* > + * 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 since the 'if' part continues to the 'for' loop. > + /* > + * Mark status of the curFrm to > + * done and unlock semaphore on it > + */ > + if (layer->curFrm != layer->nextFrm) { camelCase! Use cur_frm and next_frm instead (same as the dm6467 driver). > + layer->curFrm->ts = timevalue; > + layer->curFrm->state = VIDEOBUF_DONE; > + wake_up_interruptible( > + &layer->curFrm->done); > + /* Make curFrm pointing to nextFrm */ > + layer->curFrm = layer->nextFrm; > + } > + } > + /* Get the next buffer from buffer queue */ > + layer->nextFrm = > + list_entry(layer->dma_queue.next, > + struct videobuf_buffer, queue); > + /* Remove that buffer from the buffer queue */ > + list_del(&layer->nextFrm->queue); > + /* Mark status of the buffer as active */ > + layer->nextFrm->state = VIDEOBUF_ACTIVE; > + > + addr = videobuf_to_dma_contig(layer->nextFrm); > + osd_device->ops.start_layer(osd_device, > + layer->layer_info.id, > + addr, disp_dev->cbcr_ofst); Just a general remark: a lot of these TI drivers are all very similar, probably because they all have similar IP in them. Would it be possible to refactor those identical parts into some common code? This is not a requirement for this patch series, but some work in that direction would be helpful I think. > + } 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->curFrm == layer->nextFrm) > + continue; > + /* > + * one frame is displayed If next frame is > + * available, release curFrm and move on > + * copy frame display time > + */ > + layer->curFrm->ts = timevalue; > + /* Change status of the curFrm */ > + layer->curFrm->state = VIDEOBUF_DONE; > + /* unlock semaphore on curFrm */ > + wake_up_interruptible(&layer->curFrm->done); > + /* Make curFrm pointing to nextFrm */ > + layer->curFrm = layer->nextFrm; > + } else if (1 == fid) { /* odd field */ > + if (list_empty(&layer->dma_queue) > + || (layer->curFrm != layer->nextFrm)) > + 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->nextFrm = list_entry(layer-> > + dma_queue. > + next, struct Please, don't split up a line like this. Use something like this instead: layer->nextFrm = list_entry(layer->dma_queue.next, struct videobuf_buffer, queue); The 80 char limit is a warning only, don't blindly follow it. Code clarity is more important. > + videobuf_buffer, > + queue); > + > + /* Remove that from the buffer queue */ > + list_del(&layer->nextFrm->queue); > + > + /* Mark state of the frame to active */ > + layer->nextFrm->state = VIDEOBUF_ACTIVE; > + addr = videobuf_to_dma_contig(layer->nextFrm); > + 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; > + } > + > + 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; > + } > + } > + 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); > + /* 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->curFrm); > + /* 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; > + 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 (!strcmp(vpbe_dev->current_timings.name, > + VID_ENC_STD_NTSC) || > + !strcmp(vpbe_dev->current_timings.name, > + VID_ENC_STD_PAL)) { > + 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 (!strcmp(vpbe_dev->current_timings.name, > + VID_ENC_STD_PAL)) { > + 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; > + 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 filed format */ Typo: filed -> field > + 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; > + } > + > + 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); > + > + 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; > + *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->nextFrm = layer->curFrm = list_entry(layer->dma_queue.next, > + struct videobuf_buffer, queue); > + /* Remove buffer from the buffer queue */ > + list_del(&layer->curFrm->queue); > + /* Mark state of the current frame to active */ > + layer->curFrm->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); > + 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, > + .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..235a8d4 > --- /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 *curFrm; > + /* Pointer pointing to next v4l2_buffer */ > + struct videobuf_buffer *nextFrm; > + /* 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 */ > + /* 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..9a4c877 > --- /dev/null > +++ b/include/media/davinci/vpbe_types.h > @@ -0,0 +1,170 @@ > +/* > + * 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, > +}; > + > +enum vpbe_display_modes { > + VPBE_MODE_SDTV, > + VPBE_MODE_EDTV, > + VPBE_MODE_HDTV, > + VPBE_MODE_LCD > +}; > + > +/* 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; > +}; > + > +/* vpbe output interface types */ > +enum vpbe_if_types { > + VPBE_ANALOG_TV_IF, > + VPBE_DIGITAL_IF_YCC8, > + VPBE_DIGITAL_IF_YCC16, > + VPBE_DIGITAL_IF_SRGB, > + VPBE_DIGITAL_IF_PRGB, > +}; > + > +/* RGB modes */ > +enum vpbe_rgb_modes { > + VPBE_RGB565, > + VPBE_RGB666, > + VPBE_RGB888, > +}; > + > +struct vpbe_if_params { > + enum vpbe_if_types if_type; > + /* > + * Bool to indicate if Y and C outputs to be swapped. This is applicable > + * for YCC8 and YCC16 > + */ > + unsigned int swap_yc:1; > + /* Bool to indicate if BT.656 is enabled. Applicable for YCC8/YCC16 */ > + unsigned int bt656:1; > + /* Enable RGB666. Applicable for PRGB if type */ > + enum vpbe_rgb_modes rgb_mode; > +}; > + > +/* > + * struct vpbe_enc_fract > + * @numerator: numerator part of a fractional number > + * @denominator: denominator part of a fractional number > + * > + * Description: > + * Structure used to represent fractional numbers > + */ > +struct vpbe_enc_fract { > + unsigned int numerator; > + unsigned int denominator; > +}; What's wrong with struct v4l2_fract? > + > +/* > + * 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 vpbe_enc_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; > +}; > + > +/** > + * constant strings for standard names or mode names. All modules uses this to > + * refer a specific standard or mode name > + */ > +#define VID_ENC_STD_NTSC "NTSC" > +#define VID_ENC_STD_NTSC_RGB "NTSC-RGB" > +#define VID_ENC_STD_PAL "PAL" > +#define VID_ENC_STD_PAL_RGB "PAL-RGB" Hmm. Isn't this the same format, just with a different colorspace? > +#define VID_ENC_STD_720P_24 "720P-24" > +#define VID_ENC_STD_720P_25 "720P-25" > +#define VID_ENC_STD_720P_30 "720P-30" > +#define VID_ENC_STD_720P_50 "720P-50" > +#define VID_ENC_STD_720P_60 "720P-60" > +#define VID_ENC_STD_1080I_25 "1080I-25" > +#define VID_ENC_STD_1080I_30 "1080I-30" > +#define VID_ENC_STD_1080P_24 "1080P-24" > +#define VID_ENC_STD_1080P_25 "1080P-25" > +#define VID_ENC_STD_1080P_30 "1080P-30" > +#define VID_ENC_STD_1080P_50 "1080P-50" > +#define VID_ENC_STD_1080P_60 "1080P-60" > +#define VID_ENC_STD_480P_60 "480P-60" > +#define VID_ENC_STD_576P_50 "576P-50" > +#define VID_ENC_STD_640x480 "640x480" > +#define VID_ENC_STD_640x400 "640x400" > +#define VID_ENC_STD_640x350 "640x350" > +#define VID_ENC_STD_800x480 "800x480" > +#define VID_ENC_STD_NON_STANDARD "NON-STANDARD" Not sure if this is such a great idea. Why not use an enum instead rather than having to strcmp these strings every time? Or use the DV preset values (extending them as needed). Regards, Hans > + > +#endif > -- Hans Verkuil - video4linux developer - sponsored by Cisco From sshtylyov at mvista.com Tue Nov 9 08:51:50 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Tue, 09 Nov 2010 17:51:50 +0300 Subject: [PATCH 6/6] davinci vpbe: Build infrastructure for VPBE driver In-Reply-To: <1289228157-5366-1-git-send-email-manjunath.hadli@ti.com> References: <1289228157-5366-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <4CD96006.8010301@mvista.com> Hello. Manjunath Hadli wrote: > From: Muralidharan Karicheri > This patch adds the build infra-structure for Davinci > VPBE dislay driver. > Signed-off-by: Muralidharan Karicheri > Signed-off-by: Manjunath Hadli [...] > 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 Please use tabs for indentation uniformly. WBR, Sergei From manjunath.hadli at ti.com Tue Nov 9 09:47:52 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Tue, 9 Nov 2010 21:17:52 +0530 Subject: [PATCH 6/6] davinci vpbe: Build infrastructure for VPBE driver In-Reply-To: <4CD96006.8010301@mvista.com> Message-ID: On Tue, Nov 09, 2010 at 20:21:50, Sergei Shtylyov wrote: > Hello. > > Manjunath Hadli wrote: > > > From: Muralidharan Karicheri > > > This patch adds the build infra-structure for Davinci VPBE dislay > > driver. > > > Signed-off-by: Muralidharan Karicheri > > Signed-off-by: Manjunath Hadli > [...] > > > 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 > > Please use tabs for indentation uniformly. > > WBR, Sergei > > Sure. Thank you for the review. -Manju From grant.likely at secretlab.ca Tue Nov 9 22:45:30 2010 From: grant.likely at secretlab.ca (Grant Likely) Date: Tue, 9 Nov 2010 21:45:30 -0700 Subject: [PATCH v4 08/12] gpio: add ti-ssp gpio driver In-Reply-To: <956998.65651.qm@web180302.mail.gq1.yahoo.com> References: <1288124308-14999-9-git-send-email-cyril@ti.com> <956998.65651.qm@web180302.mail.gq1.yahoo.com> Message-ID: <20101110044530.GB4110@angua.secretlab.ca> On Tue, Nov 02, 2010 at 07:15:16PM -0700, David Brownell wrote: > > > --- On Tue, 10/26/10, Cyril Chemparathy wrote: > > > From: Cyril Chemparathy > > Subject: [PATCH v4 08/12] gpio: add ti-ssp gpio driver > > On the assumptions you've tested this *AND* will > resubmit with the previously-requested change of > removing all references to non-existent > "virtual"ness in the driver ... I'll likely > add my ack to that resubmitted version > > Also, chip2gpio seems an excessively generic name; > maybe chip2_ti_ssp_gpio or somesuch? > > I'd still be happier seeing this "stack" packaged > as a hardware library called by various drivers > (like GPIO, SPI, etc) rather than a core driver > that's called by other drivers. That seems like > a better structure for various vendors' "SSP" > hardware (multifunction serial interface logic) > since most function drivers just need to poke > the registers a bit differently, and don't have > much to share with a "core driver" beyond a few > setup routines (if that). I thought the point of this device was that a single device hosted a pair of multi-function serial interfaces, with each implementing a separate function. If so, then it makes sense for the base driver to register child devices of the appropriate kinds. g. From david-b at pacbell.net Wed Nov 10 00:16:22 2010 From: david-b at pacbell.net (David Brownell) Date: Tue, 9 Nov 2010 22:16:22 -0800 (PST) Subject: [PATCH v4 08/12] gpio: add ti-ssp gpio driver In-Reply-To: <20101110044530.GB4110@angua.secretlab.ca> Message-ID: <79124.87409.qm@web180307.mail.gq1.yahoo.com> > I thought the point of this device was that a single [SSP] device > hosted a > pair of multi-function serial interfaces, with each > implementing a > separate function. function chosen based on what the board needs. Codec interface, SPI, GPIO, etc. ? If so, then it makes sense for the > base driver to > register child devices of the appropriate kinds. I'd normally say board setup registers them; a "core"driver can't know what children would be needed. But the point I was making was about code factoring not driver setup. When the functions don't have much commonality, they might as well just write to the relevant registers instead of expecting to have a non-register programming interface (of dubious generality of a "core" driver, but much complexity). Easier just to have children use registers directly, in several similar cases. Less overhead, too. - Dave From grant.likely at secretlab.ca Wed Nov 10 00:23:10 2010 From: grant.likely at secretlab.ca (Grant Likely) Date: Tue, 9 Nov 2010 23:23:10 -0700 Subject: [PATCH v4 08/12] gpio: add ti-ssp gpio driver In-Reply-To: <79124.87409.qm@web180307.mail.gq1.yahoo.com> References: <20101110044530.GB4110@angua.secretlab.ca> <79124.87409.qm@web180307.mail.gq1.yahoo.com> Message-ID: <20101110062310.GB7431@angua.secretlab.ca> On Tue, Nov 09, 2010 at 10:16:22PM -0800, David Brownell wrote: > > > I thought the point of this device was that a single [SSP] device > > hosted a > > pair of multi-function serial interfaces, with each > > implementing a > > separate function. > > function chosen based on what the board needs. > Codec interface, SPI, GPIO, etc. > > ? If so, then it makes sense for the > > base driver to > > register child devices of the appropriate kinds. > > I'd normally say board setup registers them; a > "core"driver can't know what children would be needed. > > But the point I was making was about code factoring > not driver setup. When the functions don't have > much commonality, they might as well just write to > the relevant registers instead of expecting to have > a non-register programming interface (of dubious > generality of a "core" driver, but much complexity). > > Easier just to have children use registers directly, > in several similar cases. Less overhead, too. I guess it depends on how much overlap/interlock there is between the multiple channels. If there is shared context, then that is a stronger argument for a shared api. Cyril, what say you? g. From lamiaposta71 at gmail.com Wed Nov 10 05:39:17 2010 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Wed, 10 Nov 2010 12:39:17 +0100 Subject: tvp5151: driver Message-ID: I'm writing this driver against the head of arago linux davinci kernel. After that I'll try to port it to latest linux.davinci kernel. Just to be sure isn't anybody working on it, or , if present, to share considerations. Thx, Raffaele -- www.opensurf.it From hverkuil at xs4all.nl Wed Nov 10 05:45:31 2010 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Wed, 10 Nov 2010 12:45:31 +0100 Subject: tvp5151: driver In-Reply-To: References: Message-ID: <5c01ead53c6140c9293ce7c36c1850cc.squirrel@webmail.xs4all.nl> Hi Raffaele, There is already a tvp5150.c driver available. Are there many changes between tvp5150 and tvp5151? If the differences are fairly limited, then it might make more sense to adapt tvp5150.c. Please upstream the work you are doing into the mainline kernel as well! The linux-media mailinglist (www.linuxtv.org) is the best place to post the patches and get help. Regards, Hans > I'm writing this driver against the head of arago linux davinci kernel. > After that I'll try to port it to latest linux.davinci kernel. > Just to be sure isn't anybody working on it, or , if present, to share > considerations. > > Thx, > Raffaele > > -- > www.opensurf.it > _______________________________________________ > Davinci-linux-open-source mailing list > Davinci-linux-open-source at linux.davincidsp.com > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source > -- Hans Verkuil - video4linux developer - sponsored by Cisco From lamiaposta71 at gmail.com Wed Nov 10 05:59:06 2010 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Wed, 10 Nov 2010 12:59:06 +0100 Subject: tvp5151: driver In-Reply-To: <5c01ead53c6140c9293ce7c36c1850cc.squirrel@webmail.xs4all.nl> References: <5c01ead53c6140c9293ce7c36c1850cc.squirrel@webmail.xs4all.nl> Message-ID: On Wed, Nov 10, 2010 at 12:45 PM, Hans Verkuil wrote: > Hi Raffaele, > > There is already a tvp5150.c driver available. Are there many changes > between tvp5150 and tvp5151? If the differences are fairly limited, then > it might make more sense to adapt tvp5150.c. right guessing to move to tvp515x.c (similarly to tvp514x). I proceed this way. > Please upstream the work you are doing into the mainline kernel as well! > The linux-media mailinglist (www.linuxtv.org) is the best place to post > the patches and get help. trying. From mkaricheri at gmail.com Wed Nov 10 07:05:16 2010 From: mkaricheri at gmail.com (Muralidharan Karicheri) Date: Wed, 10 Nov 2010 08:05:16 -0500 Subject: [PATCH 0/6] davinci vpbe: V4L2 Display driver for DM644X In-Reply-To: <1289228045-4512-1-git-send-email-manjunath.hadli@ti.com> References: <1289228045-4512-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Manjunath, Thank you for putting up this patch together. I didn't see the 1/6 of this series in the mailing list. Also it appears as if the patch came from me. Please add my sign-off as second, you being the first. Murali On Mon, Nov 8, 2010 at 9:54 AM, Manjunath Hadli wrote: > This driver is written for Texas Instruments's DM644X VPBE IP. > This SoC supports 2 video planes and 2 OSD planes as part of its > OSD (On Screen Display) block. The OSD lanes predminantly support > RGB space and the Video planes support YUV data. Out of these 4, > the 2 video planes are supported as part of the V4L2 driver. These > would be enumerated as video2 and video3 dev nodes. > The blending and video timing generator unit (VENC- for Video Encoder) > is the unit which combines/blends the output of these 4 planes > into a single stream and this output is given to Video input devices > like TV and other digital LCDs. The software for VENC is designed as > a subdevice with support for SD(NTSC and PAL) modes and 2 outputs. > This SoC forms the iniial implementation of its later additions > like DM355 and DM365. > > Muralidharan Karicheri (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 ? ? | ? 85 +- > ?arch/arm/mach-davinci/dm644x.c ? ? ? ? ? ? ? | ?181 ++- > ?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 ? ? ? ? ? | ?861 ++++++++++ > ?drivers/media/video/davinci/vpbe_display.c ? | 2283 ++++++++++++++++++++++++++ > ?drivers/media/video/davinci/vpbe_osd.c ? ? ? | 1208 ++++++++++++++ > ?drivers/media/video/davinci/vpbe_osd_regs.h ?| ?389 +++++ > ?drivers/media/video/davinci/vpbe_venc.c ? ? ?| ?617 +++++++ > ?drivers/media/video/davinci/vpbe_venc_regs.h | ?189 +++ > ?include/media/davinci/vpbe.h ? ? ? ? ? ? ? ? | ?187 +++ > ?include/media/davinci/vpbe_display.h ? ? ? ? | ?144 ++ > ?include/media/davinci/vpbe_osd.h ? ? ? ? ? ? | ?397 +++++ > ?include/media/davinci/vpbe_types.h ? ? ? ? ? | ?170 ++ > ?include/media/davinci/vpbe_venc.h ? ? ? ? ? ?| ? 70 + > ?16 files changed, 6790 insertions(+), 19 deletions(-) > ?create mode 100644 drivers/media/video/davinci/vpbe.c > ?create mode 100644 drivers/media/video/davinci/vpbe_display.c > ?create mode 100644 drivers/media/video/davinci/vpbe_osd.c > ?create mode 100644 drivers/media/video/davinci/vpbe_osd_regs.h > ?create mode 100644 drivers/media/video/davinci/vpbe_venc.c > ?create mode 100644 drivers/media/video/davinci/vpbe_venc_regs.h > ?create mode 100644 include/media/davinci/vpbe.h > ?create mode 100644 include/media/davinci/vpbe_display.h > ?create mode 100644 include/media/davinci/vpbe_osd.h > ?create mode 100644 include/media/davinci/vpbe_types.h > ?create mode 100644 include/media/davinci/vpbe_venc.h > > -- > To unsubscribe from this list: send the line "unsubscribe linux-media" in > the body of a message to majordomo at vger.kernel.org > More majordomo info at ?http://vger.kernel.org/majordomo-info.html > -- Murali Karicheri mkaricheri at gmail.com From manjunath.hadli at ti.com Wed Nov 10 07:07:00 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Wed, 10 Nov 2010 18:37:00 +0530 Subject: [PATCH 1/6] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: <201011090849.40940.hverkuil@xs4all.nl> Message-ID: Hans, Thank you for the review. I have taken care of the points you mentioned. The name comparison is unnecessary. I have also replaced the native struct definitions with those of v4l2. Request you to go through the rest of the patches so I can send the set once again. Thanks and Regards, -Manju On Tue, Nov 09, 2010 at 13:19:40, Hans Verkuil wrote: > On Monday, November 08, 2010 15:54:16 Manjunath Hadli wrote: > > From: Muralidharan Karicheri > > > > 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: Muralidharan Karicheri > > Signed-off-by: Manjunath Hadli > > --- > > drivers/media/video/davinci/vpbe_display.c | 2283 ++++++++++++++++++++++++++++ > > include/media/davinci/vpbe_display.h | 144 ++ > > include/media/davinci/vpbe_types.h | 170 ++ > > 3 files changed, 2597 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..b02a98f > > --- /dev/null > > +++ b/drivers/media/video/davinci/vpbe_display.c > > @@ -0,0 +1,2283 @@ > > +/* > > + * 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 since the 'if' part continues to the 'for' loop. > > > + /* > > + * Mark status of the curFrm to > > + * done and unlock semaphore on it > > + */ > > + if (layer->curFrm != layer->nextFrm) { > > camelCase! Use cur_frm and next_frm instead (same as the dm6467 driver). > > > + layer->curFrm->ts = timevalue; > > + layer->curFrm->state = VIDEOBUF_DONE; > > + wake_up_interruptible( > > + &layer->curFrm->done); > > + /* Make curFrm pointing to nextFrm */ > > + layer->curFrm = layer->nextFrm; > > + } > > + } > > + /* Get the next buffer from buffer queue */ > > + layer->nextFrm = > > + list_entry(layer->dma_queue.next, > > + struct videobuf_buffer, queue); > > + /* Remove that buffer from the buffer queue */ > > + list_del(&layer->nextFrm->queue); > > + /* Mark status of the buffer as active */ > > + layer->nextFrm->state = VIDEOBUF_ACTIVE; > > + > > + addr = videobuf_to_dma_contig(layer->nextFrm); > > + osd_device->ops.start_layer(osd_device, > > + layer->layer_info.id, > > + addr, disp_dev->cbcr_ofst); > > Just a general remark: a lot of these TI drivers are all very similar, probably > because they all have similar IP in them. Would it be possible to refactor those > identical parts into some common code? This is not a requirement for this patch > series, but some work in that direction would be helpful I think. > > > + } 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->curFrm == layer->nextFrm) > > + continue; > > + /* > > + * one frame is displayed If next frame is > > + * available, release curFrm and move on > > + * copy frame display time > > + */ > > + layer->curFrm->ts = timevalue; > > + /* Change status of the curFrm */ > > + layer->curFrm->state = VIDEOBUF_DONE; > > + /* unlock semaphore on curFrm */ > > + wake_up_interruptible(&layer->curFrm->done); > > + /* Make curFrm pointing to nextFrm */ > > + layer->curFrm = layer->nextFrm; > > + } else if (1 == fid) { /* odd field */ > > + if (list_empty(&layer->dma_queue) > > + || (layer->curFrm != layer->nextFrm)) > > + 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->nextFrm = list_entry(layer-> > > + dma_queue. > > + next, struct > > Please, don't split up a line like this. Use something like this instead: > > layer->nextFrm = list_entry(layer->dma_queue.next, > struct videobuf_buffer, > queue); > > The 80 char limit is a warning only, don't blindly follow it. > Code clarity is more important. > > > + videobuf_buffer, > > + queue); > > + > > + /* Remove that from the buffer queue */ > > + list_del(&layer->nextFrm->queue); > > + > > + /* Mark state of the frame to active */ > > + layer->nextFrm->state = VIDEOBUF_ACTIVE; > > + addr = videobuf_to_dma_contig(layer->nextFrm); > > + 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; > > + } > > + > > + 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; > > + } > > + } > > + 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); > > + /* 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->curFrm); > > + /* 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; > > + 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 (!strcmp(vpbe_dev->current_timings.name, > > + VID_ENC_STD_NTSC) || > > + !strcmp(vpbe_dev->current_timings.name, > > + VID_ENC_STD_PAL)) { > > + 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 (!strcmp(vpbe_dev->current_timings.name, > > + VID_ENC_STD_PAL)) { > > + 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; > > + 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 filed format */ > > Typo: filed -> field > > > + 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; > > + } > > + > > + 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); > > + > > + 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; > > + *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->nextFrm = layer->curFrm = list_entry(layer->dma_queue.next, > > + struct videobuf_buffer, queue); > > + /* Remove buffer from the buffer queue */ > > + list_del(&layer->curFrm->queue); > > + /* Mark state of the current frame to active */ > > + layer->curFrm->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); > > + 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, > > + .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..235a8d4 > > --- /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 *curFrm; > > + /* Pointer pointing to next v4l2_buffer */ > > + struct videobuf_buffer *nextFrm; > > + /* 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 */ > > + /* 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..9a4c877 > > --- /dev/null > > +++ b/include/media/davinci/vpbe_types.h > > @@ -0,0 +1,170 @@ > > +/* > > + * 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, > > +}; > > + > > +enum vpbe_display_modes { > > + VPBE_MODE_SDTV, > > + VPBE_MODE_EDTV, > > + VPBE_MODE_HDTV, > > + VPBE_MODE_LCD > > +}; > > + > > +/* 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; > > +}; > > + > > +/* vpbe output interface types */ > > +enum vpbe_if_types { > > + VPBE_ANALOG_TV_IF, > > + VPBE_DIGITAL_IF_YCC8, > > + VPBE_DIGITAL_IF_YCC16, > > + VPBE_DIGITAL_IF_SRGB, > > + VPBE_DIGITAL_IF_PRGB, > > +}; > > + > > +/* RGB modes */ > > +enum vpbe_rgb_modes { > > + VPBE_RGB565, > > + VPBE_RGB666, > > + VPBE_RGB888, > > +}; > > + > > +struct vpbe_if_params { > > + enum vpbe_if_types if_type; > > + /* > > + * Bool to indicate if Y and C outputs to be swapped. This is applicable > > + * for YCC8 and YCC16 > > + */ > > + unsigned int swap_yc:1; > > + /* Bool to indicate if BT.656 is enabled. Applicable for YCC8/YCC16 */ > > + unsigned int bt656:1; > > + /* Enable RGB666. Applicable for PRGB if type */ > > + enum vpbe_rgb_modes rgb_mode; > > +}; > > + > > +/* > > + * struct vpbe_enc_fract > > + * @numerator: numerator part of a fractional number > > + * @denominator: denominator part of a fractional number > > + * > > + * Description: > > + * Structure used to represent fractional numbers > > + */ > > +struct vpbe_enc_fract { > > + unsigned int numerator; > > + unsigned int denominator; > > +}; > > What's wrong with struct v4l2_fract? > > > + > > +/* > > + * 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 vpbe_enc_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; > > +}; > > + > > +/** > > + * constant strings for standard names or mode names. All modules uses this to > > + * refer a specific standard or mode name > > + */ > > +#define VID_ENC_STD_NTSC "NTSC" > > +#define VID_ENC_STD_NTSC_RGB "NTSC-RGB" > > +#define VID_ENC_STD_PAL "PAL" > > +#define VID_ENC_STD_PAL_RGB "PAL-RGB" > > Hmm. Isn't this the same format, just with a different colorspace? > > > +#define VID_ENC_STD_720P_24 "720P-24" > > +#define VID_ENC_STD_720P_25 "720P-25" > > +#define VID_ENC_STD_720P_30 "720P-30" > > +#define VID_ENC_STD_720P_50 "720P-50" > > +#define VID_ENC_STD_720P_60 "720P-60" > > +#define VID_ENC_STD_1080I_25 "1080I-25" > > +#define VID_ENC_STD_1080I_30 "1080I-30" > > +#define VID_ENC_STD_1080P_24 "1080P-24" > > +#define VID_ENC_STD_1080P_25 "1080P-25" > > +#define VID_ENC_STD_1080P_30 "1080P-30" > > +#define VID_ENC_STD_1080P_50 "1080P-50" > > +#define VID_ENC_STD_1080P_60 "1080P-60" > > +#define VID_ENC_STD_480P_60 "480P-60" > > +#define VID_ENC_STD_576P_50 "576P-50" > > +#define VID_ENC_STD_640x480 "640x480" > > +#define VID_ENC_STD_640x400 "640x400" > > +#define VID_ENC_STD_640x350 "640x350" > > +#define VID_ENC_STD_800x480 "800x480" > > +#define VID_ENC_STD_NON_STANDARD "NON-STANDARD" > > Not sure if this is such a great idea. Why not use an enum instead rather than > having to strcmp these strings every time? Or use the DV preset values (extending > them as needed). > > Regards, > > Hans > > > + > > +#endif > > > > -- > Hans Verkuil - video4linux developer - sponsored by Cisco > From manjunath.hadli at ti.com Wed Nov 10 07:08:57 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Wed, 10 Nov 2010 18:38:57 +0530 Subject: [PATCH 0/6] davinci vpbe: V4L2 Display driver for DM644X In-Reply-To: Message-ID: Murali, I will change the name order. Thanks and Regards, -manju On Wed, Nov 10, 2010 at 18:35:16, Muralidharan Karicheri wrote: > Manjunath, > > Thank you for putting up this patch together. I didn't see the 1/6 of this series in the mailing list. Also it appears as if the patch came from me. Please add my sign-off as second, you being the first. > > Murali > On Mon, Nov 8, 2010 at 9:54 AM, Manjunath Hadli wrote: > > This driver is written for Texas Instruments's DM644X VPBE IP. > > This SoC supports 2 video planes and 2 OSD planes as part of its OSD > > (On Screen Display) block. The OSD lanes predminantly support RGB > > space and the Video planes support YUV data. Out of these 4, the 2 > > video planes are supported as part of the V4L2 driver. These would be > > enumerated as video2 and video3 dev nodes. > > The blending and video timing generator unit (VENC- for Video Encoder) > > is the unit which combines/blends the output of these 4 planes into a > > single stream and this output is given to Video input devices like TV > > and other digital LCDs. The software for VENC is designed as a > > subdevice with support for SD(NTSC and PAL) modes and 2 outputs. > > This SoC forms the iniial implementation of its later additions like > > DM355 and DM365. > > > > Muralidharan Karicheri (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 ? ? | ? 85 +- > > ?arch/arm/mach-davinci/dm644x.c ? ? ? ? ? ? ? | ?181 ++- > > ?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 ? ? ? ? ? | ?861 ++++++++++ > > ?drivers/media/video/davinci/vpbe_display.c ? | 2283 > > ++++++++++++++++++++++++++ > > ?drivers/media/video/davinci/vpbe_osd.c ? ? ? | 1208 ++++++++++++++ > > ?drivers/media/video/davinci/vpbe_osd_regs.h ?| ?389 +++++ > > ?drivers/media/video/davinci/vpbe_venc.c ? ? ?| ?617 +++++++ > > ?drivers/media/video/davinci/vpbe_venc_regs.h | ?189 +++ > > ?include/media/davinci/vpbe.h ? ? ? ? ? ? ? ? | ?187 +++ > > ?include/media/davinci/vpbe_display.h ? ? ? ? | ?144 ++ > > ?include/media/davinci/vpbe_osd.h ? ? ? ? ? ? | ?397 +++++ > > ?include/media/davinci/vpbe_types.h ? ? ? ? ? | ?170 ++ > > ?include/media/davinci/vpbe_venc.h ? ? ? ? ? ?| ? 70 + > > ?16 files changed, 6790 insertions(+), 19 deletions(-) > > ?create mode 100644 drivers/media/video/davinci/vpbe.c > > ?create mode 100644 drivers/media/video/davinci/vpbe_display.c > > ?create mode 100644 drivers/media/video/davinci/vpbe_osd.c > > ?create mode 100644 drivers/media/video/davinci/vpbe_osd_regs.h > > ?create mode 100644 drivers/media/video/davinci/vpbe_venc.c > > ?create mode 100644 drivers/media/video/davinci/vpbe_venc_regs.h > > ?create mode 100644 include/media/davinci/vpbe.h > > ?create mode 100644 include/media/davinci/vpbe_display.h > > ?create mode 100644 include/media/davinci/vpbe_osd.h > > ?create mode 100644 include/media/davinci/vpbe_types.h > > ?create mode 100644 include/media/davinci/vpbe_venc.h > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-media" > > in the body of a message to majordomo at vger.kernel.org More majordomo > > info at ?http://vger.kernel.org/majordomo-info.html > > > > > > -- > Murali Karicheri > mkaricheri at gmail.com > From manjunath.hadli at ti.com Wed Nov 10 07:23:40 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Wed, 10 Nov 2010 18:53:40 +0530 Subject: mediabus enums In-Reply-To: Message-ID: Hello Guennadi, Your media-bus enumerations capture the formats quite well. I needed the following for support on Davinci SOCs and liked to check with you if these are covered in some format in the list. 1. Parallel RGB 666 (18 data lines+ 5 sync lines) 2. YUYV16 (16 lines) (16 data lines + 4 or 5 sync lines) Thanks and Regards, -Manju From hverkuil at xs4all.nl Wed Nov 10 07:24:12 2010 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Wed, 10 Nov 2010 14:24:12 +0100 Subject: [PATCH 1/6] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: References: Message-ID: <4c82bf1c3e1d08acd513e99ac15a2f81.squirrel@webmail.xs4all.nl> > Hans, > Thank you for the review. I have taken care of the points you mentioned. > The name comparison is unnecessary. I have also replaced the native > struct definitions with those of v4l2. Request you to go through the rest > of the patches so I can send the set once again. I won't have time for that until Friday at the earliest. More likely it will be the weekend. I hope that's OK. Regards, Hans > > Thanks and Regards, > -Manju -- Hans Verkuil - video4linux developer - sponsored by Cisco From manjunath.hadli at ti.com Wed Nov 10 07:25:55 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Wed, 10 Nov 2010 18:55:55 +0530 Subject: [PATCH 1/6] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: <4c82bf1c3e1d08acd513e99ac15a2f81.squirrel@webmail.xs4all.nl> Message-ID: Will do. Thanks! -manju On Wed, Nov 10, 2010 at 18:54:12, Hans Verkuil wrote: > > > Hans, > > Thank you for the review. I have taken care of the points you mentioned. > > The name comparison is unnecessary. I have also replaced the native > > struct definitions with those of v4l2. Request you to go through the > > rest of the patches so I can send the set once again. > > I won't have time for that until Friday at the earliest. More likely it will be the weekend. I hope that's OK. > > Regards, > > Hans > > > > > Thanks and Regards, > > -Manju > > > -- > Hans Verkuil - video4linux developer - sponsored by Cisco > > From cyril at ti.com Wed Nov 10 08:34:42 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Wed, 10 Nov 2010 09:34:42 -0500 Subject: [PATCH v4 08/12] gpio: add ti-ssp gpio driver In-Reply-To: <20101110062310.GB7431@angua.secretlab.ca> References: <20101110044530.GB4110@angua.secretlab.ca> <79124.87409.qm@web180307.mail.gq1.yahoo.com> <20101110062310.GB7431@angua.secretlab.ca> Message-ID: <4CDAAD82.9090007@ti.com> On 11/10/2010 01:23 AM, Grant Likely wrote: > On Tue, Nov 09, 2010 at 10:16:22PM -0800, David Brownell wrote: >> >>> I thought the point of this device was that a single [SSP] device >>> hosted a >>> pair of multi-function serial interfaces, with each >>> implementing a >>> separate function. >> >> function chosen based on what the board needs. >> Codec interface, SPI, GPIO, etc. >> >> If so, then it makes sense for the >>> base driver to >>> register child devices of the appropriate kinds. >> >> I'd normally say board setup registers them; a >> "core"driver can't know what children would be needed. >> >> But the point I was making was about code factoring >> not driver setup. When the functions don't have >> much commonality, they might as well just write to >> the relevant registers instead of expecting to have >> a non-register programming interface (of dubious >> generality of a "core" driver, but much complexity). >> >> Easier just to have children use registers directly, >> in several similar cases. Less overhead, too. > > I guess it depends on how much overlap/interlock there is between the > multiple channels. If there is shared context, then that is a > stronger argument for a shared api. Cyril, what say you? > The channels (or ports) in this case are not very well separated out. The registers for these ports are interleaved, and in some cases different bits of the same register are meant for different ports. Second, with the exception of GPIO (which essentially bit bangs), all other functions would follow the same flow, i.e. set stuff up (mode, iosel), load up a sequence, kick off execution, and wait for completion. I thought it made sense to provide these pieces in a shared driver. Regards Cyril. From savinay.dharmappa at ti.com Thu Nov 11 01:55:53 2010 From: savinay.dharmappa at ti.com (Savinay Dharmappa) Date: Thu, 11 Nov 2010 13:25:53 +0530 Subject: [PATCH 1/2] mtd: NOR flash driver for OMAP-L137/AM17x Message-ID: <1289462153-4350-1-git-send-email-savinay.dharmappa@ti.com> 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: Savinay Dharmappa --- drivers/mtd/maps/Kconfig | 9 + drivers/mtd/maps/Makefile | 1 + drivers/mtd/maps/latch-addr-flash.c | 271 ++++++++++++++++++++++++++++++++++ include/linux/mtd/latch-addr-flash.h | 29 ++++ 4 files changed, 310 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/maps/latch-addr-flash.c create mode 100644 include/linux/mtd/latch-addr-flash.h diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 701d942..b794ea2 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -543,4 +543,13 @@ config MTD_PISMO When built as a module, it will be called pismo.ko +config MTD_LATCH_ADDR + tristate "Latch-assisted Flash Chip Support" + depends on MTD_COMPLEX_MAPPINGS + help + Map driver which allows flashes to be partially physically addressed + and have the upper address lines set by a board specific code. + + If compiled as a module, it will be called latch-addr-flash. + endmenu diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index f216bb5..35c3573 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -58,3 +58,4 @@ obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o obj-$(CONFIG_MTD_VMU) += vmu-flash.o obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o +obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c new file mode 100644 index 0000000..e3a7f91 --- /dev/null +++ b/drivers/mtd/maps/latch-addr-flash.c @@ -0,0 +1,271 @@ +/* + * Interface for NOR flash driver whose high address lines are latched + * + * Copyright 2000 Nicolas Pitre + * Copyright 2005-2008 Analog Devices Inc. + * Coyright (C) 2008 MontaVista Software, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "latch-addr-flash" + +struct latch_addr_flash_info { + struct mtd_info *mtd; + struct map_info map; + struct resource *res; + + void (*set_window)(unsigned long offset, void *data); + void *data; + + /* cache; could be found out of res */ + unsigned long win_mask; + + int nr_parts; + struct mtd_partition *parts; + + spinlock_t lock; +}; + +static map_word lf_read(struct map_info *map, unsigned long ofs) +{ + struct latch_addr_flash_info *info; + map_word datum; + + info = (struct latch_addr_flash_info *)map->map_priv_1; + + spin_lock(&info->lock); + + info->set_window(ofs, info->data); + datum = inline_map_read(map, info->win_mask & ofs); + + spin_unlock(&info->lock); + + return datum; +} + +static void lf_write(struct map_info *map, map_word datum, unsigned long ofs) +{ + struct latch_addr_flash_info *info; + + info = (struct latch_addr_flash_info *)map->map_priv_1; + + spin_lock(&info->lock); + + info->set_window(ofs, info->data); + inline_map_write(map, datum, info->win_mask & ofs); + + spin_unlock(&info->lock); +} + +static void lf_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + struct latch_addr_flash_info *info = + (struct latch_addr_flash_info *) map->map_priv_1; + unsigned n; + + while (len > 0) { + n = info->win_mask + 1 - (from & info->win_mask); + if (n > len) + n = len; + + spin_lock(&info->lock); + + info->set_window(from, info->data); + memcpy_fromio(to, map->virt + (from & info->win_mask), n); + + spin_unlock(&info->lock); + + to += n; + from += n; + len -= n; + } +} + +static const char *rom_probe_types[] = { "cfi_probe", NULL }; + +static const char *part_probe_types[] = { "cmdlinepart", NULL }; + +static int latch_addr_flash_remove(struct platform_device *dev) +{ + struct latch_addr_flash_info *info; + struct latch_addr_flash_data *latch_addr_data; + + info = platform_get_drvdata(dev); + if (info == NULL) + return 0; + platform_set_drvdata(dev, NULL); + + latch_addr_data = dev->dev.platform_data; + + if (info->mtd != NULL) { + if (mtd_has_partitions()) { + if (info->nr_parts) { + del_mtd_partitions(info->mtd); + kfree(info->parts); + } else if (latch_addr_data->nr_parts) { + del_mtd_partitions(info->mtd); + } else { + del_mtd_device(info->mtd); + } + } else { + del_mtd_device(info->mtd); + } + map_destroy(info->mtd); + } + + if (info->map.virt != NULL) + iounmap(info->map.virt); + + if (info->res != NULL) + release_mem_region(info->res->start, resource_size(info->res)); + + kfree(info); + + if (latch_addr_data->done) + latch_addr_data->done(latch_addr_data->data); + + return 0; +} + +static int __devinit latch_addr_flash_probe(struct platform_device *dev) +{ + struct latch_addr_flash_data *latch_addr_data; + struct latch_addr_flash_info *info; + resource_size_t win_base = dev->resource->start; + resource_size_t win_size = resource_size(dev->resource); + const char **probe_type; + int chipsel; + int err; + + latch_addr_data = dev->dev.platform_data; + if (latch_addr_data == NULL) + return -ENODEV; + + pr_notice("latch-addr platform flash device: %#llx byte " + "window at %#.8llx\n", + (unsigned long long)win_size, (unsigned long long)win_base); + + chipsel = dev->id; + + if (latch_addr_data->init) { + err = latch_addr_data->init(latch_addr_data->data, chipsel); + if (err != 0) + return err; + } + + info = kzalloc(sizeof(struct latch_addr_flash_info), GFP_KERNEL); + if (info == NULL) { + err = -ENOMEM; + goto done; + } + + platform_set_drvdata(dev, info); + + info->res = request_mem_region(win_base, win_size, DRIVER_NAME); + if (info->res == NULL) { + dev_err(&dev->dev, "Could not reserve memory region\n"); + err = -EBUSY; + goto free_info; + } + + info->map.name = DRIVER_NAME; + info->map.size = latch_addr_data->size; + info->map.bankwidth = latch_addr_data->width; + + info->map.phys = NO_XIP; + info->map.virt = ioremap(win_base, win_size); + if (!info->map.virt) { + err = -ENOMEM; + goto free_res; + } + + info->map.map_priv_1 = (unsigned long)info; + + info->map.read = lf_read; + info->map.copy_from = lf_copy_from; + info->map.write = lf_write; + info->set_window = latch_addr_data->set_window; + info->data = latch_addr_data->data; + info->win_mask = win_size - 1; + + spin_lock_init(&info->lock); + + for (probe_type = rom_probe_types; !info->mtd && *probe_type; + probe_type++) + info->mtd = do_map_probe(*probe_type, &info->map); + + if (info->mtd == NULL) { + dev_err(&dev->dev, "map_probe failed\n"); + err = -ENODEV; + goto iounmap; + } + info->mtd->owner = THIS_MODULE; + + if (mtd_has_partitions()) { + + err = parse_mtd_partitions(info->mtd, part_probe_types, + &info->parts, 0); + if (err > 0) { + add_mtd_partitions(info->mtd, info->parts, err); + return 0; + } + if (latch_addr_data->nr_parts) { + pr_notice("Using latch-addr-flash partition information\n"); + add_mtd_partitions(info->mtd, latch_addr_data->parts, + latch_addr_data->nr_parts); + return 0; + } + } + add_mtd_device(info->mtd); + return 0; + +iounmap: + iounmap(info->map.virt); +free_res: + release_mem_region(info->res->start, resource_size(info->res)); +free_info: + kfree(info); +done: + if (latch_addr_data->done) + latch_addr_data->done(latch_addr_data->data); + return err; +} + +static struct platform_driver latch_addr_flash_driver = { + .probe = latch_addr_flash_probe, + .remove = __devexit_p(latch_addr_flash_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init latch_addr_flash_init(void) +{ + return platform_driver_register(&latch_addr_flash_driver); +} +module_init(latch_addr_flash_init); + +static void __exit latch_addr_flash_exit(void) +{ + platform_driver_unregister(&latch_addr_flash_driver); +} +module_exit(latch_addr_flash_exit); + +MODULE_AUTHOR("David Griego "); +MODULE_DESCRIPTION("MTD map driver for flashes addressed physically with upper " + "address lines being set board specifically"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mtd/latch-addr-flash.h b/include/linux/mtd/latch-addr-flash.h new file mode 100644 index 0000000..c77e7c9 --- /dev/null +++ b/include/linux/mtd/latch-addr-flash.h @@ -0,0 +1,29 @@ +/* + * Interface for NOR flash driver whose high address lines are latched + * + * Copyright (C) 2008 MontaVista Software, Inc. + * + * This file is licensed underthe terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ +#ifndef __LATCH_ADDR_FLASH__ +#define __LATCH_ADDR_FLASH__ + +struct map_info; +struct mtd_partition; + +struct latch_addr_flash_data { + unsigned int width; + unsigned int size; + + int (*init)(void *data, int cs); + void (*done)(void *data); + void (*set_window)(unsigned long offset, void *data); + void *data; + + unsigned int nr_parts; + struct mtd_partition *parts; +}; + +#endif -- 1.5.6 From savinay.dharmappa at ti.com Thu Nov 11 01:57:37 2010 From: savinay.dharmappa at ti.com (Savinay Dharmappa) Date: Thu, 11 Nov 2010 13:27:37 +0530 Subject: [PATCH 2/2] davinci: Platform support for OMAP-L137/AM17x NOR flash driver Message-ID: <1289462257-7109-1-git-send-email-savinay.dharmappa@ti.com> From: Aleksey Makarov Adds platform support for OMAP-L137/AM17x NOR flash driver. Also, configures chip select 3 to control NOR flash's upper address lines. Signed-off-by: Aleksey Makarov Signed-off-by: Savinay Dharmappa --- arch/arm/mach-davinci/Kconfig | 8 + arch/arm/mach-davinci/board-da830-evm.c | 220 +++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index 9aca60c..baa39ee 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -142,6 +142,14 @@ config DA830_UI_NAND help Say Y here to use the NAND flash. Do not forget to setup the switch correctly. + +config DA830_UI_NOR + bool "NOR flash" + help + Configure this option to specify the that AEMIF CE2/CE3 will be used to + communicate to the NOR flash. Do not forget to setup the switch SW1 + on UI card correctly. + endchoice config MACH_DAVINCI_DA850_EVM diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c index 1bb89d3..c8a5d4b 100644 --- a/arch/arm/mach-davinci/board-da830-evm.c +++ b/arch/arm/mach-davinci/board-da830-evm.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -429,6 +431,222 @@ static inline void da830_evm_init_nand(int mux_mode) static inline void da830_evm_init_nand(int mux_mode) { } #endif +#ifdef CONFIG_DA830_UI_NOR +/* + * Number of Address line going to the NOR flash that are latched using + * AEMIF address lines B_EMIF_BA0-B_EMIF_A12 on CS2. + */ +#define NOR_WINDOW_SIZE_LOG2 15 +#define NOR_WINDOW_SIZE (1 << NOR_WINDOW_SIZE_LOG2) + +static struct { + struct clk *clk; + struct { + struct resource *res; + void __iomem *addr; + } latch, aemif; +} da830_evm_nor; + +static struct davinci_aemif_timing da830_evm_norflash_timing = { + .wsetup = 0, + .wstrobe = 40, + .whold = 0, + .rsetup = 0, + .rstrobe = 130, + .rhold = 0, + .ta = 20, +}; + +static void da830_evm_nor_set_window(unsigned long offset, void *data) +{ + /* + * CS2 and CS3 address lines are used to address nor flash. Address + * line A0-A14 going to the NOR flash are latched using AEMIF address + * lines B_EMIF_BA0-B_EMIF_A12 on CS2. Address lines A15-A23 of the + * NOR flash are latched using AEMIF address lines B_EMIF_A0-B_EMIF_A6 + * on CS3. The offset argument received by this function is the offset + * within NOR flash. Upper address is obtained by shifting the offset + * by the number of CS2 address lines used (13) and masking it with + * complement of 3 (2 address lines used to address banks) and adding + * the resultant offset value to CS3 base address. Writing a zero to + * this address will latch the upper address lines. + */ + writeb(0, da830_evm_nor.latch.addr + + (~3UL & (offset >> (NOR_WINDOW_SIZE_LOG2 - 2)))); +} + +static void da830_evm_nor_done(void *data) +{ + clk_disable(da830_evm_nor.clk); + clk_put(da830_evm_nor.clk); + iounmap(da830_evm_nor.latch.addr); + release_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE); + iounmap(da830_evm_nor.aemif.addr); + release_mem_region(DA8XX_AEMIF_CTL_BASE, SZ_32K); +} + +static int da830_evm_nor_init(void *data, int cs) +{ + /* Turn on AEMIF clocks */ + da830_evm_nor.clk = clk_get(NULL, "aemif"); + if (IS_ERR(da830_evm_nor.clk)) { + pr_err("%s: could not get AEMIF clock\n", __func__); + da830_evm_nor.clk = NULL; + return -ENODEV; + } + clk_enable(da830_evm_nor.clk); + + da830_evm_nor.aemif.res = request_mem_region(DA8XX_AEMIF_CTL_BASE, + SZ_32K, "AEMIF control"); + if (da830_evm_nor.aemif.res == NULL) { + pr_err("%s: could not request AEMIF control region\n", + __func__); + goto err_clk; + } + + da830_evm_nor.aemif.addr = ioremap_nocache(DA8XX_AEMIF_CTL_BASE, + SZ_32K); + if (da830_evm_nor.aemif.addr == NULL) { + pr_err("%s: could not remap AEMIF control region\n", __func__); + goto err_aemif_region; + } + + /* Setup AEMIF -- timings, etc. */ + + /* Set maximum wait cycles */ + davinci_aemif_setup_timing(&da830_evm_norflash_timing, + da830_evm_nor.aemif.addr, cs); + + davinci_aemif_setup_timing(&da830_evm_norflash_timing, + da830_evm_nor.aemif.addr, cs + 1); + + /* Setup the window to access the latch */ + da830_evm_nor.latch.res = + request_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE, + "DA830 UI NOR address latch"); + if (da830_evm_nor.latch.res == NULL) { + pr_err("%s: could not request address latch region\n", + __func__); + goto err_aemif_ioremap; + } + + da830_evm_nor.latch.addr = + ioremap_nocache(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE); + if (da830_evm_nor.latch.addr == NULL) { + pr_err("%s: could not remap address latch region\n", __func__); + goto err_latch_region; + } + return 0; + +err_latch_region: + release_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE); + da830_evm_nor.latch.res = NULL; +err_aemif_ioremap: + iounmap(da830_evm_nor.aemif.addr); + da830_evm_nor.aemif.addr = NULL; +err_aemif_region: + release_mem_region(DA8XX_AEMIF_CTL_BASE, SZ_32K); + da830_evm_nor.aemif.res = NULL; +err_clk: + clk_disable(da830_evm_nor.clk); + clk_put(da830_evm_nor.clk); + da830_evm_nor.clk = NULL; + + return -EBUSY; +} + +static struct mtd_partition da830_evm_nor_partitions[] = { + /* bootloader (U-Boot, etc) in first 2 sectors */ + [0] = { + .name = "bootloader", + .offset = 0, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + /* bootloader parameters in the next 1 sector */ + [1] = { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_64K, + }, + /* kernel */ + [2] = { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + }, + /* file system */ + [3] = { + .name = "filesystem", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + } +}; + +static struct latch_addr_flash_data da830_evm_nor_pdata = { + .width = 1, + .size = SZ_4M, + .init = da830_evm_nor_init, + .done = da830_evm_nor_done, + .set_window = da830_evm_nor_set_window, + .nr_parts = ARRAY_SIZE(da830_evm_nor_partitions), + .parts = da830_evm_nor_partitions, +}; + +static struct resource da830_evm_nor_resource[] = { + [0] = { + .start = DA8XX_AEMIF_CS2_BASE, + .end = DA8XX_AEMIF_CS2_BASE + NOR_WINDOW_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = DA8XX_AEMIF_CS3_BASE, + .end = DA8XX_AEMIF_CS3_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [2] = { + .start = DA8XX_AEMIF_CTL_BASE, + .end = DA8XX_AEMIF_CTL_BASE + SZ_32K - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device da830_evm_nor_device = { + .name = "latch-addr-flash", + .id = 0, + .dev = { + .platform_data = &da830_evm_nor_pdata, + }, + .num_resources = ARRAY_SIZE(da830_evm_nor_resource), + .resource = da830_evm_nor_resource, +}; + +static inline void da830_evm_init_nor(int mux_mode) +{ + int ret; + + if (HAS_MMC) { + pr_warning("WARNING: both MMC/SD and NOR are " + "enabled, but they share AEMIF pins.\n" + "\tDisable MMC/SD for NOR support.\n"); + return; + } + + ret = davinci_cfg_reg_list(da830_evm_emif25_pins); + if (ret) + pr_warning("da830_evm_init: emif25 mux setup failed: %d\n", + ret); + + ret = platform_device_register(&da830_evm_nor_device); + if (ret) + pr_warning("da830_evm_init: NOR device not registered.\n"); + + gpio_direction_output(mux_mode, 1); +} +#else +static inline void da830_evm_init_nor(int mux_mode) { } +#endif /* CONFIG_DA830_UI_NOR */ + #ifdef CONFIG_DA830_UI_LCD static inline void da830_evm_init_lcdc(int mux_mode) { @@ -469,6 +687,8 @@ static int __init da830_evm_ui_expander_setup(struct i2c_client *client, da830_evm_init_nand(gpio + 6); + da830_evm_init_nor(gpio + 6); + return 0; } -- 1.5.6 From laurent.pinchart at ideasonboard.com Thu Nov 11 10:47:23 2010 From: laurent.pinchart at ideasonboard.com (Laurent Pinchart) Date: Thu, 11 Nov 2010 17:47:23 +0100 Subject: mediabus enums In-Reply-To: References: Message-ID: <201011111747.23718.laurent.pinchart@ideasonboard.com> Hi Guennadi, On Thursday 11 November 2010 16:32:02 Guennadi Liakhovetski wrote: > On Wed, 10 Nov 2010, Hadli, Manjunath wrote: > > Hello Guennadi, > > > > Your media-bus enumerations capture the formats quite well. I needed > > > > the following for support on Davinci SOCs and liked to check with you if > > these are covered in some format in the list. > > 1. Parallel RGB 666 (18 data lines+ 5 sync lines) > > 2. YUYV16 (16 lines) (16 data lines + 4 or 5 sync lines) > > According to the subdev-formats.xml > > http://git.linuxtv.org/pinchartl/media.git?a=blob;f=Documentation/DocBook/v > 4l/subdev-formats.xml;h=3688f27185f72ab109e3094c268e04f67cb8643e;hb=refs/he > ads/media-0003-subdev-pad > > they should be called V4L2_MBUS_FMT_RGB666_1X18 (or BGR666...) Agreed. > and V4L2_MBUS_FMT_YUYV16_1X16. Depending on what Manjunath meant, this should be either YUYV16_2X16 or YUYV8_1X16. 16 bits per sample seems quite high to me, I suppose it should then be YUYV8_1X16. > Notice, that these codes do not define the complete bus topology, e.g., they > say nothing about sync signals. This is a separate topic. -- Regards, Laurent Pinchart From sshtylyov at mvista.com Thu Nov 11 12:15:45 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Thu, 11 Nov 2010 21:15:45 +0300 Subject: [PATCH 2/2] davinci: Platform support for OMAP-L137/AM17x NOR flash driver In-Reply-To: <1289462257-7109-1-git-send-email-savinay.dharmappa@ti.com> References: <1289462257-7109-1-git-send-email-savinay.dharmappa@ti.com> Message-ID: <4CDC32D1.9030508@mvista.com> Hello. Savinay Dharmappa wrote: > From: Aleksey Makarov > Adds platform support for OMAP-L137/AM17x NOR flash driver. > Also, configures chip select 3 to control NOR flash's upper > address lines. > Signed-off-by: Aleksey Makarov > Signed-off-by: Savinay Dharmappa It's not clear why you've dropped my signoff... > config MACH_DAVINCI_DA850_EVM > diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c > index 1bb89d3..c8a5d4b 100644 > --- a/arch/arm/mach-davinci/board-da830-evm.c > +++ b/arch/arm/mach-davinci/board-da830-evm.c [...] > +static void da830_evm_nor_set_window(unsigned long offset, void *data) > +{ > + /* > + * CS2 and CS3 address lines are used to address nor flash. Address NOR. Please be consistent. > + * line A0-A14 going to the NOR flash are latched using AEMIF address > + * lines B_EMIF_BA0-B_EMIF_A12 on CS2. Address lines A15-A23 of the > + * NOR flash are latched using AEMIF address lines B_EMIF_A0-B_EMIF_A6 > + * on CS3. The offset argument received by this function is the offset > + * within NOR flash. Upper address is obtained by shifting the offset > + * by the number of CS2 address lines used (13) and masking it with > + * complement of 3 (2 address lines used to address banks) and adding > + * the resultant offset value to CS3 base address. Writing a zero to Writing anything, not just zero, I guess... > + * this address will latch the upper address lines. > + */ > + writeb(0, da830_evm_nor.latch.addr + > + (~3UL & (offset >> (NOR_WINDOW_SIZE_LOG2 - 2)))); > +} [...] > +static int da830_evm_nor_init(void *data, int cs) > +{ > + /* Turn on AEMIF clocks */ > + da830_evm_nor.clk = clk_get(NULL, "aemif"); > + if (IS_ERR(da830_evm_nor.clk)) { > + pr_err("%s: could not get AEMIF clock\n", __func__); > + da830_evm_nor.clk = NULL; > + return -ENODEV; > + } > + clk_enable(da830_evm_nor.clk); > + > + da830_evm_nor.aemif.res = request_mem_region(DA8XX_AEMIF_CTL_BASE, > + SZ_32K, "AEMIF control"); > + if (da830_evm_nor.aemif.res == NULL) { > + pr_err("%s: could not request AEMIF control region\n", > + __func__); > + goto err_clk; > + } > + > + da830_evm_nor.aemif.addr = ioremap_nocache(DA8XX_AEMIF_CTL_BASE, > + SZ_32K); > + if (da830_evm_nor.aemif.addr == NULL) { > + pr_err("%s: could not remap AEMIF control region\n", __func__); > + goto err_aemif_region; > + } > + > + /* Setup AEMIF -- timings, etc. */ > + > + /* Set maximum wait cycles */ > + davinci_aemif_setup_timing(&da830_evm_norflash_timing, > + da830_evm_nor.aemif.addr, cs); > + > + davinci_aemif_setup_timing(&da830_evm_norflash_timing, > + da830_evm_nor.aemif.addr, cs + 1); > + > + /* Setup the window to access the latch */ > + da830_evm_nor.latch.res = > + request_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE, > + "DA830 UI NOR address latch"); > + if (da830_evm_nor.latch.res == NULL) { > + pr_err("%s: could not request address latch region\n", > + __func__); > + goto err_aemif_ioremap; > + } > + > + da830_evm_nor.latch.addr = > + ioremap_nocache(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE); > + if (da830_evm_nor.latch.addr == NULL) { > + pr_err("%s: could not remap address latch region\n", __func__); > + goto err_latch_region; > + } > + return 0; > + > +err_latch_region: > + release_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE); > + da830_evm_nor.latch.res = NULL; This assignment is pointless. > +err_aemif_ioremap: > + iounmap(da830_evm_nor.aemif.addr); > + da830_evm_nor.aemif.addr = NULL; This one likewise... > +err_aemif_region: > + release_mem_region(DA8XX_AEMIF_CTL_BASE, SZ_32K); > + da830_evm_nor.aemif.res = NULL; And this one too... > +err_clk: > + clk_disable(da830_evm_nor.clk); > + clk_put(da830_evm_nor.clk); > + da830_evm_nor.clk = NULL; This one as well... > +static inline void da830_evm_init_nor(int mux_mode) > +{ > + int ret; > + > + if (HAS_MMC) { > + pr_warning("WARNING: both MMC/SD and NOR are " > + "enabled, but they share AEMIF pins.\n" > + "\tDisable MMC/SD for NOR support.\n"); > + return; > + } > + > + ret = davinci_cfg_reg_list(da830_evm_emif25_pins); > + if (ret) > + pr_warning("da830_evm_init: emif25 mux setup failed: %d\n", It's called EMIF 2.5, not "emif25". WBR, Sergei From sshtylyov at mvista.com Thu Nov 11 12:18:55 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Thu, 11 Nov 2010 21:18:55 +0300 Subject: [PATCH 1/2] mtd: NOR flash driver for OMAP-L137/AM17x In-Reply-To: <1289462153-4350-1-git-send-email-savinay.dharmappa@ti.com> References: <1289462153-4350-1-git-send-email-savinay.dharmappa@ti.com> Message-ID: <4CDC338F.30600@mvista.com> Hello. 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 It's again not clear why you've dropped my and Alexei's signoff. Did we request you to do so? > Signed-off-by: Savinay Dharmappa WBR, Sergei From bengardiner at nanometrics.ca Thu Nov 11 15:55:57 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Thu, 11 Nov 2010 16:55:57 -0500 Subject: [PATCH] [PATCH] da850-evm: fix WARN_ON(chip->can_sleep) for rmii_sel Message-ID: <1289512557-28963-1-git-send-email-bengardiner@nanometrics.ca> When the RMII PHY on the UI board is enabled with CONFIG_DA850_UI_RMII then then following will be printed to the console when warnings are also enabled: WARNING: at drivers/gpio/gpiolib.c:1567 __gpio_set_value+0x4c/0x5c() Modules linked in: [] (unwind_backtrace+0x0/0xf8) from [] (warn_slowpath_common+0x4c/0x64) [] (warn_slowpath_common+0x4c/0x64) from [] (warn_slowpath_null+0x1c/0x24) [] (warn_slowpath_null+0x1c/0x24) from [] (__gpio_set_value+0x4c/0x5c) [] (__gpio_set_value+0x4c/0x5c) from [] (da850_evm_ui_expander_setup+0x1e4/0x2 44) [] (da850_evm_ui_expander_setup+0x1e4/0x244) from [] (pca953x_probe+0x1f8/0x29 0) Traced the WARN_ON to the gpio_set_value(rmii_sel,0) call in da850_evm_setup_emac_rmii. Replacing the call with the _cansleep variant results in no more warning. Signed-off-by: Ben Gardiner Reviewed-by: Chris Cordahi --- arch/arm/mach-davinci/board-da850-evm.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index c6e11c6..e8e54ff 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -266,7 +266,7 @@ static inline void da850_evm_setup_emac_rmii(int rmii_sel) struct davinci_soc_info *soc_info = &davinci_soc_info; soc_info->emac_pdata->rmii_en = 1; - gpio_set_value(rmii_sel, 0); + gpio_set_value_cansleep(rmii_sel, 0); } #else static inline void da850_evm_setup_emac_rmii(int rmii_sel) { } -- 1.7.0.4 From savinay.dharmappa at ti.com Thu Nov 11 22:20:25 2010 From: savinay.dharmappa at ti.com (Savinay Dharmappa) Date: Fri, 12 Nov 2010 09:50:25 +0530 Subject: [PATCH 1/2] mtd: NOR flash driver for OMAP-L137/AM17x In-Reply-To: <4CDC338F.30600@mvista.com> References: <1289462153-4350-1-git-send-email-savinay.dharmappa@ti.com> <4CDC338F.30600@mvista.com> Message-ID: <000901cb8220$edf40df0$c9dc29d0$@dharmappa@ti.com> On Thu, Nov 11, 2010 at 23:48:55, Sergei Shtylyov wrote: > Hello. > > 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 > > It's again not clear why you've dropped my and Alexei's signoff. Did we > request you to do so? I had sent this series to all MV folks to get their signoff but nobody replied. If you are OK with the updated version of this patch, which I'll be submitting shortly, please add your signoff. Regards, Savinay. From savinay.dharmappa at ti.com Thu Nov 11 22:24:15 2010 From: savinay.dharmappa at ti.com (Savinay Dharmappa) Date: Fri, 12 Nov 2010 09:54:15 +0530 Subject: [PATCH 2/2] davinci: Platform support for OMAP-L137/AM17x NOR flash driver In-Reply-To: <4CDC32D1.9030508@mvista.com> References: <1289462257-7109-1-git-send-email-savinay.dharmappa@ti.com> <4CDC32D1.9030508@mvista.com> Message-ID: <000a01cb8221$7727f4b0$6577de10$@dharmappa@ti.com> Hi Sergei, Thanks for your comments. On Thu, Nov 11, 2010 at 23:45:45, Sergei Shtylyov wrote: > Hello. > > Savinay Dharmappa wrote: > > > From: Aleksey Makarov > > > Adds platform support for OMAP-L137/AM17x NOR flash driver. > > > Also, configures chip select 3 to control NOR flash's upper address > > lines. > > > Signed-off-by: Aleksey Makarov > > Signed-off-by: Savinay Dharmappa > > It's not clear why you've dropped my signoff... > > > config MACH_DAVINCI_DA850_EVM > > diff --git a/arch/arm/mach-davinci/board-da830-evm.c > > b/arch/arm/mach-davinci/board-da830-evm.c > > index 1bb89d3..c8a5d4b 100644 > > --- a/arch/arm/mach-davinci/board-da830-evm.c > > +++ b/arch/arm/mach-davinci/board-da830-evm.c > [...] > > +static void da830_evm_nor_set_window(unsigned long offset, void > > +*data) { > > + /* > > + * CS2 and CS3 address lines are used to address nor flash. Address > > NOR. Please be consistent. Ok. > > > + * line A0-A14 going to the NOR flash are latched using AEMIF address > > + * lines B_EMIF_BA0-B_EMIF_A12 on CS2. Address lines A15-A23 of the > > + * NOR flash are latched using AEMIF address lines B_EMIF_A0-B_EMIF_A6 > > + * on CS3. The offset argument received by this function is the offset > > + * within NOR flash. Upper address is obtained by shifting the offset > > + * by the number of CS2 address lines used (13) and masking it with > > + * complement of 3 (2 address lines used to address banks) and adding > > + * the resultant offset value to CS3 base address. Writing a zero to > > Writing anything, not just zero, I guess... > I'll check this. > > + * this address will latch the upper address lines. > > + */ > > + writeb(0, da830_evm_nor.latch.addr + > > + (~3UL & (offset >> (NOR_WINDOW_SIZE_LOG2 - 2)))); } > > [...] > > > +static int da830_evm_nor_init(void *data, int cs) { > > + /* Turn on AEMIF clocks */ > > + da830_evm_nor.clk = clk_get(NULL, "aemif"); > > + if (IS_ERR(da830_evm_nor.clk)) { > > + pr_err("%s: could not get AEMIF clock\n", __func__); > > + da830_evm_nor.clk = NULL; > > + return -ENODEV; > > + } > > + clk_enable(da830_evm_nor.clk); > > + > > + da830_evm_nor.aemif.res = request_mem_region(DA8XX_AEMIF_CTL_BASE, > > + SZ_32K, "AEMIF control"); > > + if (da830_evm_nor.aemif.res == NULL) { > > + pr_err("%s: could not request AEMIF control region\n", > > + __func__); > > + goto err_clk; > > + } > > + > > + da830_evm_nor.aemif.addr = ioremap_nocache(DA8XX_AEMIF_CTL_BASE, > > + SZ_32K); > > + if (da830_evm_nor.aemif.addr == NULL) { > > + pr_err("%s: could not remap AEMIF control region\n", __func__); > > + goto err_aemif_region; > > + } > > + > > + /* Setup AEMIF -- timings, etc. */ > > + > > + /* Set maximum wait cycles */ > > + davinci_aemif_setup_timing(&da830_evm_norflash_timing, > > + da830_evm_nor.aemif.addr, cs); > > + > > + davinci_aemif_setup_timing(&da830_evm_norflash_timing, > > + da830_evm_nor.aemif.addr, cs + 1); > > + > > + /* Setup the window to access the latch */ > > + da830_evm_nor.latch.res = > > + request_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE, > > + "DA830 UI NOR address latch"); > > + if (da830_evm_nor.latch.res == NULL) { > > + pr_err("%s: could not request address latch region\n", > > + __func__); > > + goto err_aemif_ioremap; > > + } > > + > > + da830_evm_nor.latch.addr = > > + ioremap_nocache(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE); > > + if (da830_evm_nor.latch.addr == NULL) { > > + pr_err("%s: could not remap address latch region\n", __func__); > > + goto err_latch_region; > > + } > > + return 0; > > + > > +err_latch_region: > > + release_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE); > > + da830_evm_nor.latch.res = NULL; > > This assignment is pointless. I'll remove all such unnecessary assignments. > > > +err_aemif_ioremap: > > + iounmap(da830_evm_nor.aemif.addr); > > + da830_evm_nor.aemif.addr = NULL; > > This one likewise... > > > +err_aemif_region: > > + release_mem_region(DA8XX_AEMIF_CTL_BASE, SZ_32K); > > + da830_evm_nor.aemif.res = NULL; > > And this one too... > > > +err_clk: > > + clk_disable(da830_evm_nor.clk); > > + clk_put(da830_evm_nor.clk); > > + da830_evm_nor.clk = NULL; > > This one as well... > > > +static inline void da830_evm_init_nor(int mux_mode) { > > + int ret; > > + > > + if (HAS_MMC) { > > + pr_warning("WARNING: both MMC/SD and NOR are " > > + "enabled, but they share AEMIF pins.\n" > > + "\tDisable MMC/SD for NOR support.\n"); > > + return; > > + } > > + > > + ret = davinci_cfg_reg_list(da830_evm_emif25_pins); > > + if (ret) > > + pr_warning("da830_evm_init: emif25 mux setup failed: %d\n", > > It's called EMIF 2.5, not "emif25". Ok. I'll post the updated version soon. Regards, Savinay. From savinay.dharmappa at ti.com Fri Nov 12 02:01:51 2010 From: savinay.dharmappa at ti.com (Savinay Dharmappa) Date: Fri, 12 Nov 2010 13:31:51 +0530 Subject: [PATCH v2 1/2] mtd: NOR flash driver for OMAP-L137/AM17x Message-ID: <1289548911-21243-1-git-send-email-savinay.dharmappa@ti.com> 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: Savinay Dharmappa --- Since v1: No modifications. Made v2 because patch 2/2 has changed. drivers/mtd/maps/Kconfig | 9 + drivers/mtd/maps/Makefile | 1 + drivers/mtd/maps/latch-addr-flash.c | 271 ++++++++++++++++++++++++++++++++++ include/linux/mtd/latch-addr-flash.h | 29 ++++ 4 files changed, 310 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/maps/latch-addr-flash.c create mode 100644 include/linux/mtd/latch-addr-flash.h diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 701d942..b794ea2 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -543,4 +543,13 @@ config MTD_PISMO When built as a module, it will be called pismo.ko +config MTD_LATCH_ADDR + tristate "Latch-assisted Flash Chip Support" + depends on MTD_COMPLEX_MAPPINGS + help + Map driver which allows flashes to be partially physically addressed + and have the upper address lines set by a board specific code. + + If compiled as a module, it will be called latch-addr-flash. + endmenu diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index f216bb5..35c3573 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -58,3 +58,4 @@ obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o obj-$(CONFIG_MTD_VMU) += vmu-flash.o obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o +obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c new file mode 100644 index 0000000..e3a7f91 --- /dev/null +++ b/drivers/mtd/maps/latch-addr-flash.c @@ -0,0 +1,271 @@ +/* + * Interface for NOR flash driver whose high address lines are latched + * + * Copyright 2000 Nicolas Pitre + * Copyright 2005-2008 Analog Devices Inc. + * Coyright (C) 2008 MontaVista Software, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "latch-addr-flash" + +struct latch_addr_flash_info { + struct mtd_info *mtd; + struct map_info map; + struct resource *res; + + void (*set_window)(unsigned long offset, void *data); + void *data; + + /* cache; could be found out of res */ + unsigned long win_mask; + + int nr_parts; + struct mtd_partition *parts; + + spinlock_t lock; +}; + +static map_word lf_read(struct map_info *map, unsigned long ofs) +{ + struct latch_addr_flash_info *info; + map_word datum; + + info = (struct latch_addr_flash_info *)map->map_priv_1; + + spin_lock(&info->lock); + + info->set_window(ofs, info->data); + datum = inline_map_read(map, info->win_mask & ofs); + + spin_unlock(&info->lock); + + return datum; +} + +static void lf_write(struct map_info *map, map_word datum, unsigned long ofs) +{ + struct latch_addr_flash_info *info; + + info = (struct latch_addr_flash_info *)map->map_priv_1; + + spin_lock(&info->lock); + + info->set_window(ofs, info->data); + inline_map_write(map, datum, info->win_mask & ofs); + + spin_unlock(&info->lock); +} + +static void lf_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + struct latch_addr_flash_info *info = + (struct latch_addr_flash_info *) map->map_priv_1; + unsigned n; + + while (len > 0) { + n = info->win_mask + 1 - (from & info->win_mask); + if (n > len) + n = len; + + spin_lock(&info->lock); + + info->set_window(from, info->data); + memcpy_fromio(to, map->virt + (from & info->win_mask), n); + + spin_unlock(&info->lock); + + to += n; + from += n; + len -= n; + } +} + +static const char *rom_probe_types[] = { "cfi_probe", NULL }; + +static const char *part_probe_types[] = { "cmdlinepart", NULL }; + +static int latch_addr_flash_remove(struct platform_device *dev) +{ + struct latch_addr_flash_info *info; + struct latch_addr_flash_data *latch_addr_data; + + info = platform_get_drvdata(dev); + if (info == NULL) + return 0; + platform_set_drvdata(dev, NULL); + + latch_addr_data = dev->dev.platform_data; + + if (info->mtd != NULL) { + if (mtd_has_partitions()) { + if (info->nr_parts) { + del_mtd_partitions(info->mtd); + kfree(info->parts); + } else if (latch_addr_data->nr_parts) { + del_mtd_partitions(info->mtd); + } else { + del_mtd_device(info->mtd); + } + } else { + del_mtd_device(info->mtd); + } + map_destroy(info->mtd); + } + + if (info->map.virt != NULL) + iounmap(info->map.virt); + + if (info->res != NULL) + release_mem_region(info->res->start, resource_size(info->res)); + + kfree(info); + + if (latch_addr_data->done) + latch_addr_data->done(latch_addr_data->data); + + return 0; +} + +static int __devinit latch_addr_flash_probe(struct platform_device *dev) +{ + struct latch_addr_flash_data *latch_addr_data; + struct latch_addr_flash_info *info; + resource_size_t win_base = dev->resource->start; + resource_size_t win_size = resource_size(dev->resource); + const char **probe_type; + int chipsel; + int err; + + latch_addr_data = dev->dev.platform_data; + if (latch_addr_data == NULL) + return -ENODEV; + + pr_notice("latch-addr platform flash device: %#llx byte " + "window at %#.8llx\n", + (unsigned long long)win_size, (unsigned long long)win_base); + + chipsel = dev->id; + + if (latch_addr_data->init) { + err = latch_addr_data->init(latch_addr_data->data, chipsel); + if (err != 0) + return err; + } + + info = kzalloc(sizeof(struct latch_addr_flash_info), GFP_KERNEL); + if (info == NULL) { + err = -ENOMEM; + goto done; + } + + platform_set_drvdata(dev, info); + + info->res = request_mem_region(win_base, win_size, DRIVER_NAME); + if (info->res == NULL) { + dev_err(&dev->dev, "Could not reserve memory region\n"); + err = -EBUSY; + goto free_info; + } + + info->map.name = DRIVER_NAME; + info->map.size = latch_addr_data->size; + info->map.bankwidth = latch_addr_data->width; + + info->map.phys = NO_XIP; + info->map.virt = ioremap(win_base, win_size); + if (!info->map.virt) { + err = -ENOMEM; + goto free_res; + } + + info->map.map_priv_1 = (unsigned long)info; + + info->map.read = lf_read; + info->map.copy_from = lf_copy_from; + info->map.write = lf_write; + info->set_window = latch_addr_data->set_window; + info->data = latch_addr_data->data; + info->win_mask = win_size - 1; + + spin_lock_init(&info->lock); + + for (probe_type = rom_probe_types; !info->mtd && *probe_type; + probe_type++) + info->mtd = do_map_probe(*probe_type, &info->map); + + if (info->mtd == NULL) { + dev_err(&dev->dev, "map_probe failed\n"); + err = -ENODEV; + goto iounmap; + } + info->mtd->owner = THIS_MODULE; + + if (mtd_has_partitions()) { + + err = parse_mtd_partitions(info->mtd, part_probe_types, + &info->parts, 0); + if (err > 0) { + add_mtd_partitions(info->mtd, info->parts, err); + return 0; + } + if (latch_addr_data->nr_parts) { + pr_notice("Using latch-addr-flash partition information\n"); + add_mtd_partitions(info->mtd, latch_addr_data->parts, + latch_addr_data->nr_parts); + return 0; + } + } + add_mtd_device(info->mtd); + return 0; + +iounmap: + iounmap(info->map.virt); +free_res: + release_mem_region(info->res->start, resource_size(info->res)); +free_info: + kfree(info); +done: + if (latch_addr_data->done) + latch_addr_data->done(latch_addr_data->data); + return err; +} + +static struct platform_driver latch_addr_flash_driver = { + .probe = latch_addr_flash_probe, + .remove = __devexit_p(latch_addr_flash_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init latch_addr_flash_init(void) +{ + return platform_driver_register(&latch_addr_flash_driver); +} +module_init(latch_addr_flash_init); + +static void __exit latch_addr_flash_exit(void) +{ + platform_driver_unregister(&latch_addr_flash_driver); +} +module_exit(latch_addr_flash_exit); + +MODULE_AUTHOR("David Griego "); +MODULE_DESCRIPTION("MTD map driver for flashes addressed physically with upper " + "address lines being set board specifically"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mtd/latch-addr-flash.h b/include/linux/mtd/latch-addr-flash.h new file mode 100644 index 0000000..c77e7c9 --- /dev/null +++ b/include/linux/mtd/latch-addr-flash.h @@ -0,0 +1,29 @@ +/* + * Interface for NOR flash driver whose high address lines are latched + * + * Copyright (C) 2008 MontaVista Software, Inc. + * + * This file is licensed underthe terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ +#ifndef __LATCH_ADDR_FLASH__ +#define __LATCH_ADDR_FLASH__ + +struct map_info; +struct mtd_partition; + +struct latch_addr_flash_data { + unsigned int width; + unsigned int size; + + int (*init)(void *data, int cs); + void (*done)(void *data); + void (*set_window)(unsigned long offset, void *data); + void *data; + + unsigned int nr_parts; + struct mtd_partition *parts; +}; + +#endif -- 1.5.6 From savinay.dharmappa at ti.com Fri Nov 12 02:02:55 2010 From: savinay.dharmappa at ti.com (Savinay Dharmappa) Date: Fri, 12 Nov 2010 13:32:55 +0530 Subject: [PATCH v2 2/2] davinci: Platform support for OMAP-L137/AM17x NOR flash driver Message-ID: <1289548975-21296-1-git-send-email-savinay.dharmappa@ti.com> From: Aleksey Makarov Adds platform support for OMAP-L137/AM17x NOR flash driver. Also, configures chip select 3 to control NOR flash's upper address lines. Signed-off-by: Aleksey Makarov Signed-off-by: Savinay Dharmappa --- Since v1: Addressed Sergei's comments at [1]. [1] http://linux.davincidsp.com/pipermail/davinci-linux-open-source/2010-November/020923.html arch/arm/mach-davinci/Kconfig | 8 + arch/arm/mach-davinci/board-da830-evm.c | 219 +++++++++++++++++++++++++++++++ 2 files changed, 227 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index 9aca60c..baa39ee 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -142,6 +142,14 @@ config DA830_UI_NAND help Say Y here to use the NAND flash. Do not forget to setup the switch correctly. + +config DA830_UI_NOR + bool "NOR flash" + help + Configure this option to specify the that AEMIF CE2/CE3 will be used to + communicate to the NOR flash. Do not forget to setup the switch SW1 + on UI card correctly. + endchoice config MACH_DAVINCI_DA850_EVM diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c index 1bb89d3..cd35198 100644 --- a/arch/arm/mach-davinci/board-da830-evm.c +++ b/arch/arm/mach-davinci/board-da830-evm.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -429,6 +431,221 @@ static inline void da830_evm_init_nand(int mux_mode) static inline void da830_evm_init_nand(int mux_mode) { } #endif +#ifdef CONFIG_DA830_UI_NOR +/* + * Number of Address line going to the NOR flash that are latched using + * AEMIF address lines B_EMIF_BA0-B_EMIF_A12 on CS2. + */ +#define NOR_WINDOW_SIZE_LOG2 15 +#define NOR_WINDOW_SIZE (1 << NOR_WINDOW_SIZE_LOG2) + +static struct { + struct clk *clk; + struct { + struct resource *res; + void __iomem *addr; + } latch, aemif; +} da830_evm_nor; + +static struct davinci_aemif_timing da830_evm_norflash_timing = { + .wsetup = 0, + .wstrobe = 40, + .whold = 0, + .rsetup = 0, + .rstrobe = 130, + .rhold = 0, + .ta = 20, +}; + +static void da830_evm_nor_set_window(unsigned long offset, void *data) +{ + /* + * CS2 and CS3 address lines are used to address NOR flash. Address + * line A0-A14 going to the NOR flash are latched using AEMIF address + * lines B_EMIF_BA0-B_EMIF_A12 on CS2. Address lines A15-A23 of the + * NOR flash are latched using AEMIF address lines B_EMIF_A0-B_EMIF_A6 + * on CS3. The offset argument received by this function is the offset + * within NOR flash. Upper address is obtained by shifting the offset + * by the number of CS2 address lines used (13) and masking it with + * complement of 3 (2 address lines used to address banks) and adding + * the resultant offset value to CS3 base address. Writing to this + * address will latch the upper address lines. + */ + writeb(0x0, da830_evm_nor.latch.addr + + (~3UL & (offset >> (NOR_WINDOW_SIZE_LOG2 - 2)))); +} + +static void da830_evm_nor_done(void *data) +{ + clk_disable(da830_evm_nor.clk); + clk_put(da830_evm_nor.clk); + iounmap(da830_evm_nor.latch.addr); + release_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE); + iounmap(da830_evm_nor.aemif.addr); + release_mem_region(DA8XX_AEMIF_CTL_BASE, SZ_32K); +} + +static int da830_evm_nor_init(void *data, int cs) +{ + /* Turn on AEMIF clocks */ + da830_evm_nor.clk = clk_get(NULL, "aemif"); + if (IS_ERR(da830_evm_nor.clk)) { + pr_err("%s: could not get AEMIF clock\n", __func__); + da830_evm_nor.clk = NULL; + return -ENODEV; + } + clk_enable(da830_evm_nor.clk); + + da830_evm_nor.aemif.res = request_mem_region(DA8XX_AEMIF_CTL_BASE, + SZ_32K, "AEMIF control"); + if (da830_evm_nor.aemif.res == NULL) { + pr_err("%s: could not request AEMIF control region\n", + __func__); + goto err_clk; + } + + da830_evm_nor.aemif.addr = ioremap_nocache(DA8XX_AEMIF_CTL_BASE, + SZ_32K); + if (da830_evm_nor.aemif.addr == NULL) { + pr_err("%s: could not remap AEMIF control region\n", __func__); + goto err_aemif_region; + } + + /* Setup AEMIF -- timings, etc. */ + + /* Set maximum wait cycles */ + davinci_aemif_setup_timing(&da830_evm_norflash_timing, + da830_evm_nor.aemif.addr, cs); + + davinci_aemif_setup_timing(&da830_evm_norflash_timing, + da830_evm_nor.aemif.addr, cs + 1); + + /* Setup the window to access the latch */ + da830_evm_nor.latch.res = + request_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE, + "DA830 UI NOR address latch"); + if (da830_evm_nor.latch.res == NULL) { + pr_err("%s: could not request address latch region\n", + __func__); + goto err_aemif_ioremap; + } + + da830_evm_nor.latch.addr = + ioremap_nocache(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE); + if (da830_evm_nor.latch.addr == NULL) { + pr_err("%s: could not remap address latch region\n", __func__); + goto err_latch_region; + } + return 0; + +err_latch_region: + release_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE); + +err_aemif_ioremap: + iounmap(da830_evm_nor.aemif.addr); + +err_aemif_region: + release_mem_region(DA8XX_AEMIF_CTL_BASE, SZ_32K); + +err_clk: + clk_disable(da830_evm_nor.clk); + clk_put(da830_evm_nor.clk); + + return -EBUSY; +} + +static struct mtd_partition da830_evm_nor_partitions[] = { + /* bootloader (U-Boot, etc) in first 2 sectors */ + [0] = { + .name = "bootloader", + .offset = 0, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + /* bootloader parameters in the next 1 sector */ + [1] = { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_64K, + }, + /* kernel */ + [2] = { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + }, + /* file system */ + [3] = { + .name = "filesystem", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + } +}; + +static struct latch_addr_flash_data da830_evm_nor_pdata = { + .width = 1, + .size = SZ_4M, + .init = da830_evm_nor_init, + .done = da830_evm_nor_done, + .set_window = da830_evm_nor_set_window, + .nr_parts = ARRAY_SIZE(da830_evm_nor_partitions), + .parts = da830_evm_nor_partitions, +}; + +static struct resource da830_evm_nor_resource[] = { + [0] = { + .start = DA8XX_AEMIF_CS2_BASE, + .end = DA8XX_AEMIF_CS2_BASE + NOR_WINDOW_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = DA8XX_AEMIF_CS3_BASE, + .end = DA8XX_AEMIF_CS3_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [2] = { + .start = DA8XX_AEMIF_CTL_BASE, + .end = DA8XX_AEMIF_CTL_BASE + SZ_32K - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device da830_evm_nor_device = { + .name = "latch-addr-flash", + .id = 0, + .dev = { + .platform_data = &da830_evm_nor_pdata, + }, + .num_resources = ARRAY_SIZE(da830_evm_nor_resource), + .resource = da830_evm_nor_resource, +}; + +static inline void da830_evm_init_nor(int mux_mode) +{ + int ret; + + if (HAS_MMC) { + pr_warning("WARNING: both MMC/SD and NOR are " + "enabled, but they share AEMIF pins.\n" + "\tDisable MMC/SD for NOR support.\n"); + return; + } + + ret = davinci_cfg_reg_list(da830_evm_emif25_pins); + if (ret) + pr_warning("da830_evm_init: EMIF 2.5 mux setup failed: %d\n", + ret); + + ret = platform_device_register(&da830_evm_nor_device); + if (ret) + pr_warning("da830_evm_init: NOR device not registered.\n"); + + gpio_direction_output(mux_mode, 1); +} +#else +static inline void da830_evm_init_nor(int mux_mode) { } +#endif /* CONFIG_DA830_UI_NOR */ + #ifdef CONFIG_DA830_UI_LCD static inline void da830_evm_init_lcdc(int mux_mode) { @@ -469,6 +686,8 @@ static int __init da830_evm_ui_expander_setup(struct i2c_client *client, da830_evm_init_nand(gpio + 6); + da830_evm_init_nor(gpio + 6); + return 0; } -- 1.5.6 From dk.raghu at gmail.com Fri Nov 12 02:53:01 2010 From: dk.raghu at gmail.com (Raghu D K) Date: Fri, 12 Nov 2010 14:23:01 +0530 Subject: DVDSK Installer for DM365 And DM6446 Message-ID: Hello All, I have recently started to work on the Davinci family of processors. I am looking for a DVSDK package for DM365 and DM6446 processors. I have downloaded "dvsdk_setuplinux_2_00_00_22.bin" for DM6446 and I tried to build all the components, however I could not. I want to use open source kernel, codesourcery toolchain and have the control to build all the individual components like dsplink, dmai, framework etc Looking for some recommended version of DVSDK. Thanks. Warm Regards, Raghu From seanp at pfk.co.za Fri Nov 12 03:01:15 2010 From: seanp at pfk.co.za (Sean Preston) Date: Fri, 12 Nov 2010 11:01:15 +0200 Subject: DVDSK Installer for DM365 And DM6446 In-Reply-To: References: Message-ID: <000c01cb8248$2fb02400$8f106c00$@pfk.co.za> Hi > I have recently started to work on the Davinci family of processors. I am > looking for a DVSDK package for DM365 and DM6446 processors. > I have downloaded "dvsdk_setuplinux_2_00_00_22.bin" for DM6446 and I > tried to build all the components, however I could not. I want to use open > source kernel, codesourcery toolchain and have the control to build all the > individual components like dsplink, dmai, framework etc > > Looking for some recommended version of DVSDK. Thanks. As far as the DM365 goes you can find the latest v4 DVSDK at this URL: http://software-dl.ti.com/dsps/dsps_public_sw/sdo_sb/targetcontent/dvsdk/DVS DK_4_00/latest/index_FDS.html This uses Code Sourcery for the toolchain and Arago (based on OpenEmbedded) for the filesystem. Regards Sean -- Sean Preston Software Engineer Email: seanp at pfk.co.za From sshtylyov at mvista.com Fri Nov 12 05:48:35 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 12 Nov 2010 14:48:35 +0300 Subject: [PATCH 1/2] mtd: NOR flash driver for OMAP-L137/AM17x In-Reply-To: <000901cb8220$edf40df0$c9dc29d0$@dharmappa@ti.com> References: <1289462153-4350-1-git-send-email-savinay.dharmappa@ti.com> <4CDC338F.30600@mvista.com> <000901cb8220$edf40df0$c9dc29d0$@dharmappa@ti.com> Message-ID: <4CDD2993.9040005@mvista.com> Hello. On 12-11-2010 7:20, Savinay Dharmappa wrote: >>> 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 >> It's again not clear why you've dropped my and Alexei's signoff. Did > we >> request you to do so? > > I had sent this series to all MV folks to get their signoff but nobody > replied. Why get our signoffs again if they were on the original patches that you used? > If you are OK with the updated version of this patch, which I'll be > submitting > shortly, please add your signoff. Well, OK... > Regards, > Savinay. WBR, Sergei From manjunath.hadli at ti.com Fri Nov 12 10:00:36 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Fri, 12 Nov 2010 21:30:36 +0530 Subject: mediabus enums In-Reply-To: <201011111747.23718.laurent.pinchart@ideasonboard.com> Message-ID: On Thu, Nov 11, 2010 at 22:17:23, Laurent Pinchart wrote: > Hi Guennadi, > > On Thursday 11 November 2010 16:32:02 Guennadi Liakhovetski wrote: > > On Wed, 10 Nov 2010, Hadli, Manjunath wrote: > > > Hello Guennadi, > > > > > > Your media-bus enumerations capture the formats quite well. I > > > needed > > > > > > the following for support on Davinci SOCs and liked to check with > > > you if these are covered in some format in the list. > > > 1. Parallel RGB 666 (18 data lines+ 5 sync lines) 2. YUYV16 (16 > > > lines) (16 data lines + 4 or 5 sync lines) > > > > According to the subdev-formats.xml > > > > http://git.linuxtv.org/pinchartl/media.git?a=blob;f=Documentation/DocB > > ook/v > > 4l/subdev-formats.xml;h=3688f27185f72ab109e3094c268e04f67cb8643e;hb=re > > fs/he > > ads/media-0003-subdev-pad > > > > they should be called V4L2_MBUS_FMT_RGB666_1X18 (or BGR666...) > > Agreed. > > > and V4L2_MBUS_FMT_YUYV16_1X16. > > Depending on what Manjunath meant, this should be either YUYV16_2X16 or > > YUYV8_1X16. 16 bits per sample seems quite high to me, I suppose it should > then be YUYV8_1X16. Actually, the interface transfers 16 bits per sample (Y=8bits and C=8bits) For the YC16 and 18 data lines (parallel) for RGB666. probably V4L2_MBUS_FMT_RGB666_1X18 and V4L2_MBUS_FMT_YUYV16_1X16 fit the bill. > > > Notice, that these codes do not define the complete bus topology, > > e.g., they say nothing about sync signals. This is a separate topic. > > -- > Regards, > > Laurent Pinchart > From khilman at deeprootsystems.com Fri Nov 12 10:07:07 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 12 Nov 2010 08:07:07 -0800 Subject: [PATCH] davinci: simplify if-statement In-Reply-To: <20101025144118.62ec420c@absol.kitzblitz> (Nicolas Kaiser's message of "Mon, 25 Oct 2010 14:41:18 +0200") References: <20101025144118.62ec420c@absol.kitzblitz> Message-ID: <8739r6sez8.fsf@deeprootsystems.com> Nicolas Kaiser writes: > A common do-while loop can be factored out from the end of > the branches. > > Signed-off-by: Nicolas Kaiser Applied, queueing for 2.6.38. Kevin > --- > arch/arm/mach-davinci/psc.c | 13 ++++--------- > 1 files changed, 4 insertions(+), 9 deletions(-) > > diff --git a/arch/arm/mach-davinci/psc.c b/arch/arm/mach-davinci/psc.c > index 1b15dbd..a415804 100644 > --- a/arch/arm/mach-davinci/psc.c > +++ b/arch/arm/mach-davinci/psc.c > @@ -83,21 +83,16 @@ void davinci_psc_config(unsigned int domain, unsigned int ctlr, > pdctl1 = __raw_readl(psc_base + PDCTL1); > pdctl1 |= 0x100; > __raw_writel(pdctl1, psc_base + PDCTL1); > - > - do { > - ptstat = __raw_readl(psc_base + > - PTSTAT); > - } while (!(((ptstat >> domain) & 1) == 0)); > } else { > ptcmd = 1 << domain; > __raw_writel(ptcmd, psc_base + PTCMD); > - > - do { > - ptstat = __raw_readl(psc_base + PTSTAT); > - } while (!(((ptstat >> domain) & 1) == 0)); > } > > do { > + ptstat = __raw_readl(psc_base + PTSTAT); > + } while (!(((ptstat >> domain) & 1) == 0)); > + > + do { > mdstat = __raw_readl(psc_base + MDSTAT + 4 * id); > } while (!((mdstat & MDSTAT_STATE_MASK) == next_state)); From khilman at deeprootsystems.com Fri Nov 12 10:23:20 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 12 Nov 2010 08:23:20 -0800 Subject: [PATCH v7 3/9] davinci: ASoC support for Omapl138-Hawkboard In-Reply-To: <1288808115-2661-3-git-send-email-vm.rod25@gmail.com> (vm's message of "Wed, 3 Nov 2010 12:15:08 -0600") References: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> <1288808115-2661-3-git-send-email-vm.rod25@gmail.com> Message-ID: <87tyjmqznr.fsf@deeprootsystems.com> writes: > From: Victor Rodriguez > > This patch adds ASoC support for the Hawkboard-L138 system > > Signed-off-by: Victor Rodriguez I believe Mark Brown ack'd and earlier version of this patch. When you repost the series, you should collect any acks, sign-offs, tested-by etc. tags that others have reported. Mark, I assume you're OK with me merging this ASoC change via the davinci tree? Kevin > --- > sound/soc/davinci/Kconfig | 5 +++-- > sound/soc/davinci/davinci-evm.c | 6 ++++-- > 2 files changed, 7 insertions(+), 4 deletions(-) > > diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig > index 6bbf001..72c6752 100644 > --- a/sound/soc/davinci/Kconfig > +++ b/sound/soc/davinci/Kconfig > @@ -76,8 +76,9 @@ config SND_DA830_SOC_EVM > DA830/OMAP-L137 EVM > > config SND_DA850_SOC_EVM > - tristate "SoC Audio support for DA850/OMAP-L138 EVM" > - depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA850_EVM > + tristate "SoC Audio support for DA850/OMAP-L138 EVM/Hawkboard" > + depends on SND_DAVINCI_SOC && (MACH_DAVINCI_DA850_EVM || \ > + MACH_OMAPL138_HAWKBOARD) > select SND_DAVINCI_SOC_MCASP > select SND_SOC_TLV320AIC3X > help > diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c > index 97f74d6..73093eb 100644 > --- a/sound/soc/davinci/davinci-evm.c > +++ b/sound/soc/davinci/davinci-evm.c > @@ -59,7 +59,8 @@ static int evm_hw_params(struct snd_pcm_substream *substream, > sysclk = 12288000; > > else if (machine_is_davinci_da830_evm() || > - machine_is_davinci_da850_evm()) > + machine_is_davinci_da850_evm() || > + machine_is_omapl138_hawkboard()) > sysclk = 24576000; > > else > @@ -311,7 +312,8 @@ static int __init evm_init(void) > } else if (machine_is_davinci_da830_evm()) { > evm_snd_dev_data = &da830_evm_snd_devdata; > index = 1; > - } else if (machine_is_davinci_da850_evm()) { > + } else if (machine_is_davinci_da850_evm() || > + machine_is_omapl138_hawkboard()) { > evm_snd_dev_data = &da850_evm_snd_devdata; > index = 0; > } else From broonie at opensource.wolfsonmicro.com Fri Nov 12 10:25:16 2010 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Fri, 12 Nov 2010 16:25:16 +0000 Subject: [PATCH v7 3/9] davinci: ASoC support for Omapl138-Hawkboard In-Reply-To: <87tyjmqznr.fsf@deeprootsystems.com> References: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> <1288808115-2661-3-git-send-email-vm.rod25@gmail.com> <87tyjmqznr.fsf@deeprootsystems.com> Message-ID: <20101112162516.GG17283@rakim.wolfsonmicro.main> On Fri, Nov 12, 2010 at 08:23:20AM -0800, Kevin Hilman wrote: > Mark, I assume you're OK with me merging this ASoC change via the > davinci tree? Yes. From vm.rod25 at gmail.com Fri Nov 12 10:39:40 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Fri, 12 Nov 2010 10:39:40 -0600 Subject: [PATCH v7 3/9] davinci: ASoC support for Omapl138-Hawkboard In-Reply-To: <87tyjmqznr.fsf@deeprootsystems.com> References: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> <1288808115-2661-3-git-send-email-vm.rod25@gmail.com> <87tyjmqznr.fsf@deeprootsystems.com> Message-ID: On Fri, Nov 12, 2010 at 10:23 AM, Kevin Hilman wrote: > writes: > >> From: Victor Rodriguez >> >> This patch adds ASoC support for the Hawkboard-L138 system >> >> Signed-off-by: Victor Rodriguez > > I believe Mark Brown ack'd and earlier version of this patch. > > When you repost the series, you should collect any acks, sign-offs, > tested-by etc. tags that others have reported. > > Mark, I assume you're OK with me merging this ASoC change via the > davinci tree? > > Kevin Sorry for that Would you like that I resend the patches again with all the acks , sign offs and tested by tags ? Regards Victor Rodriguez >> --- >> ?sound/soc/davinci/Kconfig ? ? ? | ? ?5 +++-- >> ?sound/soc/davinci/davinci-evm.c | ? ?6 ++++-- >> ?2 files changed, 7 insertions(+), 4 deletions(-) >> >> diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig >> index 6bbf001..72c6752 100644 >> --- a/sound/soc/davinci/Kconfig >> +++ b/sound/soc/davinci/Kconfig >> @@ -76,8 +76,9 @@ config ?SND_DA830_SOC_EVM >> ? ? ? ? DA830/OMAP-L137 EVM >> >> ?config ?SND_DA850_SOC_EVM >> - ? ? tristate "SoC Audio support for DA850/OMAP-L138 EVM" >> - ? ? depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA850_EVM >> + ? ? tristate "SoC Audio support for DA850/OMAP-L138 EVM/Hawkboard" >> + ? ? depends on SND_DAVINCI_SOC && (MACH_DAVINCI_DA850_EVM || \ >> + ? ? ? ? ? ? ? ? ? ? MACH_OMAPL138_HAWKBOARD) >> ? ? ? select SND_DAVINCI_SOC_MCASP >> ? ? ? select SND_SOC_TLV320AIC3X >> ? ? ? help >> diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c >> index 97f74d6..73093eb 100644 >> --- a/sound/soc/davinci/davinci-evm.c >> +++ b/sound/soc/davinci/davinci-evm.c >> @@ -59,7 +59,8 @@ static int evm_hw_params(struct snd_pcm_substream *substream, >> ? ? ? ? ? ? ? sysclk = 12288000; >> >> ? ? ? else if (machine_is_davinci_da830_evm() || >> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? machine_is_davinci_da850_evm()) >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? machine_is_davinci_da850_evm() || >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? machine_is_omapl138_hawkboard()) >> ? ? ? ? ? ? ? sysclk = 24576000; >> >> ? ? ? else >> @@ -311,7 +312,8 @@ static int __init evm_init(void) >> ? ? ? } else if (machine_is_davinci_da830_evm()) { >> ? ? ? ? ? ? ? evm_snd_dev_data = &da830_evm_snd_devdata; >> ? ? ? ? ? ? ? index = 1; >> - ? ? } else if (machine_is_davinci_da850_evm()) { >> + ? ? } else if (machine_is_davinci_da850_evm() || >> + ? ? ? ? ? ? ? ? ? ? machine_is_omapl138_hawkboard()) { >> ? ? ? ? ? ? ? evm_snd_dev_data = &da850_evm_snd_devdata; >> ? ? ? ? ? ? ? index = 0; >> ? ? ? } else > From khilman at deeprootsystems.com Fri Nov 12 10:45:36 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 12 Nov 2010 08:45:36 -0800 Subject: [PATCH v7 3/9] davinci: ASoC support for Omapl138-Hawkboard In-Reply-To: (Victor Rodriguez's message of "Fri, 12 Nov 2010 10:39:40 -0600") References: <1288808115-2661-1-git-send-email-vm.rod25@gmail.com> <1288808115-2661-3-git-send-email-vm.rod25@gmail.com> <87tyjmqznr.fsf@deeprootsystems.com> Message-ID: <87oc9uqymn.fsf@deeprootsystems.com> Victor Rodriguez writes: > On Fri, Nov 12, 2010 at 10:23 AM, Kevin Hilman > wrote: >> writes: >> >>> From: Victor Rodriguez >>> >>> This patch adds ASoC support for the Hawkboard-L138 system >>> >>> Signed-off-by: Victor Rodriguez >> >> I believe Mark Brown ack'd and earlier version of this patch. >> >> When you repost the series, you should collect any acks, sign-offs, >> tested-by etc. tags that others have reported. >> >> Mark, I assume you're OK with me merging this ASoC change via the >> davinci tree? >> >> Kevin > > > Sorry for that Would you like that I resend the patches again with all > the acks , sign offs and tested by tags ? > Yes please. Thanks, Kevin From laurent.pinchart at ideasonboard.com Fri Nov 12 11:17:40 2010 From: laurent.pinchart at ideasonboard.com (Laurent Pinchart) Date: Fri, 12 Nov 2010 18:17:40 +0100 Subject: mediabus enums In-Reply-To: References: Message-ID: <201011121817.40926.laurent.pinchart@ideasonboard.com> Hi Manjunath, On Friday 12 November 2010 17:00:36 Hadli, Manjunath wrote: > On Thu, Nov 11, 2010 at 22:17:23, Laurent Pinchart wrote: > > On Thursday 11 November 2010 16:32:02 Guennadi Liakhovetski wrote: > > > On Wed, 10 Nov 2010, Hadli, Manjunath wrote: > > > > Hello Guennadi, > > > > > > > > Your media-bus enumerations capture the formats quite well. I > > > > > > > > needed > > > > > > > > the following for support on Davinci SOCs and liked to check with > > > > you if these are covered in some format in the list. > > > > 1. Parallel RGB 666 (18 data lines+ 5 sync lines) 2. YUYV16 (16 > > > > lines) (16 data lines + 4 or 5 sync lines) > > > > > > According to the subdev-formats.xml > > > > > > http://git.linuxtv.org/pinchartl/media.git?a=blob;f=Documentation/DocB > > > ook/v > > > 4l/subdev-formats.xml;h=3688f27185f72ab109e3094c268e04f67cb8643e;hb=re > > > fs/he > > > ads/media-0003-subdev-pad > > > > > > they should be called V4L2_MBUS_FMT_RGB666_1X18 (or BGR666...) > > > > Agreed. > > > > > and V4L2_MBUS_FMT_YUYV16_1X16. > > > > Depending on what Manjunath meant, this should be either YUYV16_2X16 or > > > > YUYV8_1X16. 16 bits per sample seems quite high to me, I suppose it > > should > then be YUYV8_1X16. > > Actually, the interface transfers 16 bits per sample (Y=8bits and C=8bits) > For the YC16 and 18 data lines (parallel) for RGB666. probably > V4L2_MBUS_FMT_RGB666_1X18 and V4L2_MBUS_FMT_YUYV16_1X16 fit the bill. V4L2_MBUS_FMT_RGB666_1X18 is correct, but for YUYV I think it should be V4L2_MBUS_FMT_YUYV8_1X16. You can find a detailed description of the format at http://www.ideasonboard.org/media/media/subdev.html#V4L2-MBUS-FMT-YUYV8-1X16 -- Regards, Laurent Pinchart From khilman at deeprootsystems.com Fri Nov 12 15:16:53 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 12 Nov 2010 13:16:53 -0800 Subject: [PATCH] [PATCH] da850-evm: fix WARN_ON(chip->can_sleep) for rmii_sel In-Reply-To: <1289512557-28963-1-git-send-email-bengardiner@nanometrics.ca> (Ben Gardiner's message of "Thu, 11 Nov 2010 16:55:57 -0500") References: <1289512557-28963-1-git-send-email-bengardiner@nanometrics.ca> Message-ID: <87k4kip7i2.fsf@deeprootsystems.com> Ben Gardiner writes: > When the RMII PHY on the UI board is enabled with CONFIG_DA850_UI_RMII then then > following will be printed to the console when warnings are also enabled: > > WARNING: at drivers/gpio/gpiolib.c:1567 __gpio_set_value+0x4c/0x5c() > Modules linked in: > [] (unwind_backtrace+0x0/0xf8) from [] (warn_slowpath_common+0x4c/0x64) > [] (warn_slowpath_common+0x4c/0x64) from [] (warn_slowpath_null+0x1c/0x24) > [] (warn_slowpath_null+0x1c/0x24) from [] (__gpio_set_value+0x4c/0x5c) > [] (__gpio_set_value+0x4c/0x5c) from [] (da850_evm_ui_expander_setup+0x1e4/0x2 > 44) > [] (da850_evm_ui_expander_setup+0x1e4/0x244) from [] (pca953x_probe+0x1f8/0x29 > 0) > > > Traced the WARN_ON to the gpio_set_value(rmii_sel,0) call in > da850_evm_setup_emac_rmii. Replacing the call with the _cansleep variant > results in no more warning. OK, looks like GPIOs on the GPIO expander used for the UI expander (pca953x) are of the 'can_sleep' variety, so any users of these should be cansleep. I suspect you'll still see the similar warnings in the 'teardown' path, since those on the same expander. Care to update/test that path as well? Also, can you update the subject to something like: da850-evm: UI expander GPIO usage can sleep, use _cansleep Thanks, Kevin > Signed-off-by: Ben Gardiner > Reviewed-by: Chris Cordahi > > --- > > arch/arm/mach-davinci/board-da850-evm.c | 2 +- > 1 files changed, 1 insertions(+), 1 deletions(-) > > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index c6e11c6..e8e54ff 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -266,7 +266,7 @@ static inline void da850_evm_setup_emac_rmii(int rmii_sel) > struct davinci_soc_info *soc_info = &davinci_soc_info; > > soc_info->emac_pdata->rmii_en = 1; > - gpio_set_value(rmii_sel, 0); > + gpio_set_value_cansleep(rmii_sel, 0); > } > #else > static inline void da850_evm_setup_emac_rmii(int rmii_sel) { } From vm.rod25 at gmail.com Fri Nov 12 16:38:46 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Fri, 12 Nov 2010 16:38:46 -0600 Subject: [PATCH v8 0/9] Add Omapl138-Hawkboard support Message-ID: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds EMAC, EDMA, ASoC, SOUND, 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 (9): davinci: EMAC support for Omapl138-Hawkboard davinci: EDMA support for Omapl138-Hawkboard davinci: ASoC support for Omapl138-Hawkboard davinci: McASP configuration for Omapl138-Hawkboard davinci: Audio support for Omapl138-Hawkboard davinci: MMC/SD and USB-OHCI configuration for Omapl138-Hawkboard davinci: MMC/SD support for Omapl138-Hawkboar davinci: USB clocks for Omapl138-Hawkboard davinci: USB1.1 support for Omapl138-Hawkboard arch/arm/mach-davinci/board-omapl138-hawk.c | 312 +++++++++++++++++++++++++++ arch/arm/mach-davinci/da850.c | 22 ++- arch/arm/mach-davinci/include/mach/mux.h | 4 + sound/soc/davinci/Kconfig | 5 +- sound/soc/davinci/davinci-evm.c | 6 +- 5 files changed, 344 insertions(+), 5 deletions(-) From vm.rod25 at gmail.com Fri Nov 12 16:38:47 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Fri, 12 Nov 2010 16:38:47 -0600 Subject: [PATCH v8 1/9] davinci: EMAC support for Omapl138-Hawkboard In-Reply-To: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1289601535-6746-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 c472dd8..62d35f0 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 Fri Nov 12 16:38:48 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Fri, 12 Nov 2010 16:38:48 -0600 Subject: [PATCH v8 2/9] davinci: EDMA support for Omapl138-Hawkboard In-Reply-To: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1289601535-6746-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 62d35f0..114fc9b 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 Fri Nov 12 16:38:49 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Fri, 12 Nov 2010 16:38:49 -0600 Subject: [PATCH v8 3/9] davinci: ASoC support for Omapl138-Hawkboard In-Reply-To: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1289601535-6746-4-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds ASoC support for the Hawkboard-L138 system Signed-off-by: Victor Rodriguez Acked-by: Mark Brown Acked-by: Liam Girdwood Tested-by: Rene Gonzalez --- sound/soc/davinci/Kconfig | 5 +++-- sound/soc/davinci/davinci-evm.c | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig index 6bbf001..72c6752 100644 --- a/sound/soc/davinci/Kconfig +++ b/sound/soc/davinci/Kconfig @@ -76,8 +76,9 @@ config SND_DA830_SOC_EVM DA830/OMAP-L137 EVM config SND_DA850_SOC_EVM - tristate "SoC Audio support for DA850/OMAP-L138 EVM" - depends on SND_DAVINCI_SOC && MACH_DAVINCI_DA850_EVM + tristate "SoC Audio support for DA850/OMAP-L138 EVM/Hawkboard" + depends on SND_DAVINCI_SOC && (MACH_DAVINCI_DA850_EVM || \ + MACH_OMAPL138_HAWKBOARD) select SND_DAVINCI_SOC_MCASP select SND_SOC_TLV320AIC3X help diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 97f74d6..73093eb 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -59,7 +59,8 @@ static int evm_hw_params(struct snd_pcm_substream *substream, sysclk = 12288000; else if (machine_is_davinci_da830_evm() || - machine_is_davinci_da850_evm()) + machine_is_davinci_da850_evm() || + machine_is_omapl138_hawkboard()) sysclk = 24576000; else @@ -311,7 +312,8 @@ static int __init evm_init(void) } else if (machine_is_davinci_da830_evm()) { evm_snd_dev_data = &da830_evm_snd_devdata; index = 1; - } else if (machine_is_davinci_da850_evm()) { + } else if (machine_is_davinci_da850_evm() || + machine_is_omapl138_hawkboard()) { evm_snd_dev_data = &da850_evm_snd_devdata; index = 0; } else -- 1.7.0.4 From vm.rod25 at gmail.com Fri Nov 12 16:38:50 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Fri, 12 Nov 2010 16:38:50 -0600 Subject: [PATCH v8 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1289601535-6746-5-git-send-email-vm.rod25@gmail.com> 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, -1 }; -- 1.7.0.4 From vm.rod25 at gmail.com Fri Nov 12 16:38:51 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Fri, 12 Nov 2010 16:38:51 -0600 Subject: [PATCH v8 5/9] davinci: Audio support for Omapl138-Hawkboard In-Reply-To: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1289601535-6746-6-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez This patch adds sound 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 ALSA utils select in menuconfig like insmodule: Sound card support ---> Advanced Linux Sound Architecture ---> ALSA for SoC audio support ---> SoC Audio for the TI DAVINCI chip SoC Audio support for DA850/OMAP-L138 EVM/Hawkboard arch/arm/mach-davinci/board-omapl138-hawk.c | 46 +++++++++++++++++++++++++++ 1 files changed, 46 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index 114fc9b..115dac0 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -110,6 +111,38 @@ static struct edma_rsv_info *da850_edma_rsv[2] = { &da850_edma_cc1_rsv, }; +static struct i2c_board_info __initdata omapl138_hawk_i2c_devices[] = { + { + I2C_BOARD_INFO("tlv320aic3x", 0x18), + }, +}; + +static struct davinci_i2c_platform_data omapl138_hawk_i2c_0_pdata = { + .bus_freq = 100, /* kHz */ + .bus_delay = 0, /* usec */ +}; + +/* davinci Hawkboard audio machine driver */ +static u8 da850_iis_serializer_direction[] = { + INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, + INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, + INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, INACTIVE_MODE, + INACTIVE_MODE, TX_MODE, RX_MODE, INACTIVE_MODE, +}; + +static struct snd_platform_data omapl138_hawk_snd_data = { + .tx_dma_offset = 0x2000, + .rx_dma_offset = 0x2000, + .op_mode = DAVINCI_MCASP_IIS_MODE, + .num_serializer = ARRAY_SIZE(da850_iis_serializer_direction), + .tdm_slots = 2, + .serial_dir = da850_iis_serializer_direction, + .asp_chan_q = EVENTQ_1, + .version = MCASP_VERSION_2, + .txnumevt = 1, + .rxnumevt = 1, +}; + static struct davinci_uart_config omapl138_hawk_uart_config __initdata = { .enabled_uarts = 0x7, }; @@ -127,6 +160,19 @@ static __init void omapl138_hawk_init(void) pr_warning("%s: EDMA registration failed: %d\n", __func__, ret); + i2c_register_board_info(1, omapl138_hawk_i2c_devices, + ARRAY_SIZE(omapl138_hawk_i2c_devices)); + + ret = da8xx_register_i2c(0, &omapl138_hawk_i2c_0_pdata); + if (ret) + pr_warning("%s: i2c0 registration failed: %d\n", + __func__, ret); + + ret = davinci_cfg_reg_list(da850_mcasp_pins); + if (ret) + pr_warning("%s: mcasp mux setup failed: %d\n", __func__, ret); + da8xx_register_mcasp(0, &omapl138_hawk_snd_data); + ret = da8xx_register_watchdog(); if (ret) pr_warning("omapl138_hawk_init: " -- 1.7.0.4 From vm.rod25 at gmail.com Fri Nov 12 16:38:53 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Fri, 12 Nov 2010 16:38:53 -0600 Subject: [PATCH v8 7/9] davinci: MMC/SD support for Omapl138-Hawkboard In-Reply-To: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1289601535-6746-8-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 115dac0..02e54fa 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -23,6 +23,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, @@ -143,6 +145,65 @@ static struct snd_platform_data omapl138_hawk_snd_data = { .rxnumevt = 1, }; +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, }; @@ -173,6 +234,8 @@ static __init void omapl138_hawk_init(void) pr_warning("%s: mcasp mux setup failed: %d\n", __func__, ret); da8xx_register_mcasp(0, &omapl138_hawk_snd_data); + 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 Fri Nov 12 16:38:54 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Fri, 12 Nov 2010 16:38:54 -0600 Subject: [PATCH v8 8/9] davinci: USB clocks for Omapl138-Hawkboard In-Reply-To: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1289601535-6746-9-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 4458bff..b3b1adb 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 Fri Nov 12 16:38:55 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Fri, 12 Nov 2010 16:38:55 -0600 Subject: [PATCH v8 9/9] davinci: USB1.1 support for Omapl138-Hawkboard In-Reply-To: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> Message-ID: <1289601535-6746-10-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 02e54fa..ec3d95e 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -26,6 +26,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, @@ -204,6 +207,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, }; @@ -236,6 +340,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 bengardiner at nanometrics.ca Fri Nov 12 20:47:28 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 12 Nov 2010 21:47:28 -0500 Subject: [PATCH] [PATCH] da850-evm: fix WARN_ON(chip->can_sleep) for rmii_sel In-Reply-To: <87k4kip7i2.fsf@deeprootsystems.com> References: <1289512557-28963-1-git-send-email-bengardiner@nanometrics.ca> <87k4kip7i2.fsf@deeprootsystems.com> Message-ID: Hello Kevin, Thank you for your review. On Fri, Nov 12, 2010 at 4:16 PM, Kevin Hilman wrote: > > Ben Gardiner writes: > > > When the RMII PHY on the UI board is enabled with CONFIG_DA850_UI_RMII then then > > following will be printed to the console when warnings are also enabled: > > > > WARNING: at drivers/gpio/gpiolib.c:1567 __gpio_set_value+0x4c/0x5c() > > Modules linked in: > > [] (unwind_backtrace+0x0/0xf8) from [] (warn_slowpath_common+0x4c/0x64) > > [] (warn_slowpath_common+0x4c/0x64) from [] (warn_slowpath_null+0x1c/0x24) > > [] (warn_slowpath_null+0x1c/0x24) from [] (__gpio_set_value+0x4c/0x5c) > > [] (__gpio_set_value+0x4c/0x5c) from [] (da850_evm_ui_expander_setup+0x1e4/0x2 > > 44) > > [] (da850_evm_ui_expander_setup+0x1e4/0x244) from [] (pca953x_probe+0x1f8/0x29 > > 0) > > > > > > Traced the WARN_ON to the gpio_set_value(rmii_sel,0) call in > > da850_evm_setup_emac_rmii. Replacing the call with the _cansleep variant > > results in no more warning. > > OK, looks like GPIOs on the GPIO expander used for the UI expander > (pca953x) are of the 'can_sleep' variety, so any users of these should > be cansleep. > > I suspect you'll still see the similar warnings in the 'teardown' path, > since those on the same expander. I can't say I've ever executed the teardown path for the UI expander but I agree. > Care to update/test that path as well? > > Also, can you update the subject to something like: > > ? ? ?da850-evm: UI expander GPIO usage can sleep, use _cansleep Will do; I expect to have a re-spin posted Monday. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From hverkuil at xs4all.nl Sat Nov 13 06:44:02 2010 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Sat, 13 Nov 2010 13:44:02 +0100 Subject: [PATCH 2/6] davinci vpbe: VPBE display driver In-Reply-To: <1289228065-4699-1-git-send-email-manjunath.hadli@ti.com> References: <1289228065-4699-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <201011131344.02885.hverkuil@xs4all.nl> Some minor review comments... On Monday, November 08, 2010 15:54:25 Manjunath Hadli wrote: > From: Muralidharan Karicheri > > 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: Muralidharan Karicheri > Signed-off-by: Manjunath Hadli > --- > drivers/media/video/davinci/vpbe.c | 861 ++++++++++++++++++++++++++++++++++++ > include/media/davinci/vpbe.h | 187 ++++++++ > 2 files changed, 1048 insertions(+), 0 deletions(-) > create mode 100644 drivers/media/video/davinci/vpbe.c > create mode 100644 include/media/davinci/vpbe.h > > diff --git a/drivers/media/video/davinci/vpbe.c b/drivers/media/video/davinci/vpbe.c > new file mode 100644 > index 0000000..17ff1e7 > --- /dev/null > +++ b/drivers/media/video/davinci/vpbe.c > @@ -0,0 +1,861 @@ > +/* > + * 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 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; > + > + memcpy(output, &vpbe_config->outputs[temp_index], sizeof(*output)); This memcpy can be replaced with a simple assignment: *output = vpbe_config->outputs[temp_index]; Type-safe and more readable as well. memcpy is used a lot in this source, I recommend replacing it with assignments where possible. > + output->index = temp_index; > + 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, > + osd_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); Note that the module_name argument will disappear soon. Laurent has a patch series ready for this that is expected to be merged soon. > + 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; > + ret = v4l2_subdev_call(vpbe_dev->encoders[output_index], > + core, s_config, 0, > + &vpbe_dev->cfg->outputs[output_index].if_params); Shouldn't this be passed through platform_data instead? It was introduced for backwards compatibility with pre-2.6.26 kernels and it should no longer be used. I actually forgotten to remove this op. > + 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) > + memcpy(&vpbe_dev->current_timings, > + &vpbe_dev->cfg->outputs[0].modes[0], > + sizeof(vpbe_dev->current_timings)); > + 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..81f233a > --- /dev/null > +++ b/include/media/davinci/vpbe.h > @@ -0,0 +1,187 @@ > +/* > + * 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 > + */ > + struct vpbe_if_params if_params; > +}; > + > +/* 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 Sat Nov 13 06:52:19 2010 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Sat, 13 Nov 2010 13:52:19 +0100 Subject: [PATCH 0/6] davinci vpbe: V4L2 Display driver for DM644X In-Reply-To: <1289228045-4512-1-git-send-email-manjunath.hadli@ti.com> References: <1289228045-4512-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <201011131352.20002.hverkuil@xs4all.nl> Hi Manju, I've reviewed the other patches as well. The only one for which I had comments was patch 2/6. However, what I think would be useful here is to have an overview document, either as part of a source or header, or as a separate text document. It is not easy to get a good overview of how everything fits together, and a document that describes the various parts and how they fit together would be very benificial. I am thinking in particular of vendors building a new board based on this device: how and where do you define new i2c display devices, how are they initialized, etc. Regards, Hans On Monday, November 08, 2010 15:54:05 Manjunath Hadli wrote: > This driver is written for Texas Instruments's DM644X VPBE IP. > This SoC supports 2 video planes and 2 OSD planes as part of its > OSD (On Screen Display) block. The OSD lanes predminantly support > RGB space and the Video planes support YUV data. Out of these 4, > the 2 video planes are supported as part of the V4L2 driver. These > would be enumerated as video2 and video3 dev nodes. > The blending and video timing generator unit (VENC- for Video Encoder) > is the unit which combines/blends the output of these 4 planes > into a single stream and this output is given to Video input devices > like TV and other digital LCDs. The software for VENC is designed as > a subdevice with support for SD(NTSC and PAL) modes and 2 outputs. > This SoC forms the iniial implementation of its later additions > like DM355 and DM365. > > Muralidharan Karicheri (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 | 85 +- > arch/arm/mach-davinci/dm644x.c | 181 ++- > 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 | 861 ++++++++++ > drivers/media/video/davinci/vpbe_display.c | 2283 ++++++++++++++++++++++++++ > drivers/media/video/davinci/vpbe_osd.c | 1208 ++++++++++++++ > drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++ > drivers/media/video/davinci/vpbe_venc.c | 617 +++++++ > drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++ > include/media/davinci/vpbe.h | 187 +++ > include/media/davinci/vpbe_display.h | 144 ++ > include/media/davinci/vpbe_osd.h | 397 +++++ > include/media/davinci/vpbe_types.h | 170 ++ > include/media/davinci/vpbe_venc.h | 70 + > 16 files changed, 6790 insertions(+), 19 deletions(-) > create mode 100644 drivers/media/video/davinci/vpbe.c > create mode 100644 drivers/media/video/davinci/vpbe_display.c > create mode 100644 drivers/media/video/davinci/vpbe_osd.c > create mode 100644 drivers/media/video/davinci/vpbe_osd_regs.h > create mode 100644 drivers/media/video/davinci/vpbe_venc.c > create mode 100644 drivers/media/video/davinci/vpbe_venc_regs.h > create mode 100644 include/media/davinci/vpbe.h > create mode 100644 include/media/davinci/vpbe_display.h > create mode 100644 include/media/davinci/vpbe_osd.h > create mode 100644 include/media/davinci/vpbe_types.h > create mode 100644 include/media/davinci/vpbe_venc.h > > -- > To unsubscribe from this list: send the line "unsubscribe linux-media" in > the body of a message to majordomo at vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > > -- Hans Verkuil - video4linux developer - sponsored by Cisco From nsekhar at ti.com Mon Nov 15 05:10:36 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 15 Nov 2010 16:40:36 +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, Looks like I missed pointing this out previously, but extending this list to take care of all boards will not be right since (for example) AXR13 and AXR14 pins could be used for different purpose on different boards. The right way would be to make this a per-board list. Since it is marked __initdata, that wouldn't lead to bloat. Thanks, Sekhar From sshtylyov at mvista.com Mon Nov 15 06:16:46 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Mon, 15 Nov 2010 15:16:46 +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: <4CE124AE.4080301@mvista.com> On 15.11.2010 14:10, Nori, Sekhar wrote: >> 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 >> 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, > Looks like I missed pointing this out previously, but extending > this list to take care of all boards will not be right since > (for example) AXR13 and AXR14 pins could be used for different > purpose on different boards. This is correct as the list in da850.c is a *generic* module's pin list. If the board needs less pins (and the pins it does not use for McASP are used differently), it should define its own pin list. > The right way would be to make this a per-board list. Since it > is marked __initdata, that wouldn't lead to bloat. This patch is correct anyway. Unless DA850 EVM board can't use these pins for McASP -- but in this case the corresponding board file needs the specific pin list added. > Thanks, > Sekhar WBR, Sergei From lamiaposta71 at gmail.com Mon Nov 15 06:49:49 2010 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Mon, 15 Nov 2010 13:49:49 +0100 Subject: DVDSK Installer for DM365 And DM6446 In-Reply-To: <000c01cb8248$2fb02400$8f106c00$@pfk.co.za> References: <000c01cb8248$2fb02400$8f106c00$@pfk.co.za> Message-ID: On Fri, Nov 12, 2010 at 10:01 AM, Sean Preston wrote: > Hi > >> I have recently started to work on the Davinci family of processors. I am >> looking for a DVSDK package for DM365 and DM6446 processors. >> I have downloaded "dvsdk_setuplinux_2_00_00_22.bin" for DM6446 and I >> tried to build all the components, however I could not. I want to use open >> source kernel, codesourcery toolchain and have the control to build all > the >> individual components like dsplink, dmai, framework etc >> >> Looking for some recommended version of DVSDK. Thanks. > > As far as the DM365 goes you can find the latest v4 DVSDK at this URL: > http://software-dl.ti.com/dsps/dsps_public_sw/sdo_sb/targetcontent/dvsdk/DVS > DK_4_00/latest/index_FDS.html > > This uses Code Sourcery for the toolchain and Arago (based on OpenEmbedded) > for the filesystem. stay tuned here http://e2e.ti.com/support/embedded/f/354.aspx > > Regards > Sean > > -- > Sean Preston > Software Engineer > Email: seanp at pfk.co.za > > _______________________________________________ > 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 From nsekhar at ti.com Mon Nov 15 07:07:21 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 15 Nov 2010 18:37:21 +0530 Subject: [PATCH v8 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: <4CE124AE.4080301@mvista.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> <1289601535-6746-5-git-send-email-vm.rod25@gmail.com> <4CE124AE.4080301@mvista.com> Message-ID: Hi Sergei, On Mon, Nov 15, 2010 at 17:46:46, Sergei Shtylyov wrote: > On 15.11.2010 14:10, Nori, Sekhar wrote: > > >> 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 > > >> 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, > > > Looks like I missed pointing this out previously, but extending > > this list to take care of all boards will not be right since > > (for example) AXR13 and AXR14 pins could be used for different > > purpose on different boards. > > This is correct as the list in da850.c is a *generic* module's pin list. > If the board needs less pins (and the pins it does not use for McASP are used > differently), it should define its own pin list. > > > The right way would be to make this a per-board list. Since it > > is marked __initdata, that wouldn't lead to bloat. > > This patch is correct anyway. Unless DA850 EVM board can't use these pins > for McASP -- but in this case the corresponding board file needs the specific > pin list added. Okay. I guess you are saying we will keep adding pins to the generic list as long as *all* supported boards don't get a conflict and if we run into a conflict we will spawn separate list for the affected board. The only issue I see with this approach is it puts too much burden on the developer to verify that none of the supported boards break. Since it is highly unlikely that any board will need all the McASP pins, the generic list will likely remain unused. It might just be easier to start using board specific lists right away. This is especially true for McASP where usage of pins across boards will likely vary widely. Thanks, Sekhar From vm.rod25 at gmail.com Mon Nov 15 08:31:21 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Mon, 15 Nov 2010 08:31:21 -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> <4CE124AE.4080301@mvista.com> Message-ID: On Mon, Nov 15, 2010 at 7:07 AM, Nori, Sekhar wrote: > Hi Sergei, > > On Mon, Nov 15, 2010 at 17:46:46, Sergei Shtylyov wrote: >> On 15.11.2010 14:10, Nori, Sekhar wrote: >> >> >> 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 >> >> >> 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, >> >> > Looks like I missed pointing this out previously, but extending >> > this list to take care of all boards will not be right since >> > (for example) AXR13 and AXR14 pins could be used for different >> > purpose on different boards. >> >> ? ? This is correct as the list in da850.c is a *generic* module's pin list. >> If the board needs less pins (and the pins it does not use for McASP are used >> differently), it should define its own pin list. >> >> > The right way would be to make this a per-board list. Since it >> > is marked __initdata, that wouldn't lead to bloat. >> >> ? ? This patch is correct anyway. Unless DA850 EVM board can't use these pins >> for McASP -- but in this case the corresponding board file needs the specific >> pin list added. > > Okay. I guess you are saying we will keep adding pins to the generic list > as long as *all* supported boards don't get a conflict and if we run into > a conflict we will spawn separate list for the affected board. > > The only issue I see with this approach is it puts too much burden on the > developer to verify that none of the supported boards break. > > Since it is highly unlikely that any board will need all the McASP pins, > the generic list will likely remain unused. It might just be easier to > start using board specific lists right away. This is especially true for > McASP where usage of pins across boards will likely vary widely. > > Thanks, > Sekhar > > Hi All Please confirme me if the path is rigth or if I should make a McASP list for the board. Regards Victor Rodriguez From bengardiner at nanometrics.ca Mon Nov 15 08:42:52 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Mon, 15 Nov 2010 09:42:52 -0500 Subject: [PATCH v2] da850-evm: UI expander gpio_set_value can sleep, use _cansleep In-Reply-To: <87k4kip7i2.fsf@deeprootsystems.com> Message-ID: <1289832172-21947-1-git-send-email-bengardiner@nanometrics.ca> When the RMII PHY on the UI board is enabled with CONFIG_DA850_UI_RMII then then following will be printed to the console when warnings are also enabled: WARNING: at drivers/gpio/gpiolib.c:1567 __gpio_set_value+0x4c/0x5c() Modules linked in: [] (unwind_backtrace+0x0/0xf8) from [] (warn_slowpath_common+0x4c/0x64) [] (warn_slowpath_common+0x4c/0x64) from [] (warn_slowpath_null+0x1c/0x24) [] (warn_slowpath_null+0x1c/0x24) from [] (__gpio_set_value+0x4c/0x5c) [] (__gpio_set_value+0x4c/0x5c) from [] (da850_evm_ui_expander_setup+0x1e4/0x2 44) [] (da850_evm_ui_expander_setup+0x1e4/0x244) from [] (pca953x_probe+0x1f8/0x29 0) Traced the WARN_ON to the gpio_set_value(rmii_sel,0) call in da850_evm_setup_emac_rmii. Replacing the call with the _cansleep variant results in no more warning. Also replacing the gpio_set_value calls in the teardown function. Signed-off-by: Ben Gardiner Reviewed-by: Chris Cordahi Reviewed-by: Kevin Killman -- Tested by modifying the config to allow pca953x as a module and modifying the board setup to forcibly run the NAND/NOR setup because I am using a UBIFS rootfs in NAND w/o initrd (yet) then inspecting the kernel output after 'insmod pca953x.ko' and 'rmmod -f pca953x.ko'; with this patch there are no WARNINGs printed. Changes since V1: * added _cansleep variant calls to the teardown() function, suggested by Kevin Hillman. * changed patch subject line as per Kevin Hillman's suggestion. --- arch/arm/mach-davinci/board-da850-evm.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index c6e11c6..f89b0b7 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -266,7 +266,7 @@ static inline void da850_evm_setup_emac_rmii(int rmii_sel) struct davinci_soc_info *soc_info = &davinci_soc_info; soc_info->emac_pdata->rmii_en = 1; - gpio_set_value(rmii_sel, 0); + gpio_set_value_cansleep(rmii_sel, 0); } #else static inline void da850_evm_setup_emac_rmii(int rmii_sel) { } @@ -325,9 +325,9 @@ static int da850_evm_ui_expander_teardown(struct i2c_client *client, unsigned gpio, unsigned ngpio, void *c) { /* deselect all functionalities */ - gpio_set_value(gpio + 5, 1); - gpio_set_value(gpio + 6, 1); - gpio_set_value(gpio + 7, 1); + gpio_set_value_cansleep(gpio + 5, 1); + gpio_set_value_cansleep(gpio + 6, 1); + gpio_set_value_cansleep(gpio + 7, 1); gpio_free(gpio + 5); gpio_free(gpio + 6); -- 1.7.0.4 From bengardiner at nanometrics.ca Mon Nov 15 08:45:53 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Mon, 15 Nov 2010 09:45:53 -0500 Subject: [PATCH] da850-evm: allow pca953x module build Message-ID: <1289832353-22158-1-git-send-email-bengardiner@nanometrics.ca> This patch changes the 'select GPIO_PCA953X' Kconfig command previously set by the da850-evm config to 'default GPIO_PCA953X' so that users may compile pca953x as a module. Signed-off-by: Ben Gardiner --- Note that since NAND/NOR setup is performed in the pcs953x setup() function of board-da850-evm.c, when building pca953x as a module it will need to be insmod'ed from early userspace (i.e. initrd) to use a NAND or NOR rootfs for the system. --- arch/arm/mach-davinci/Kconfig | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index b77b860..7410f70 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -148,7 +148,7 @@ config MACH_DAVINCI_DA850_EVM bool "TI DA850/OMAP-L138/AM18x Reference Platform" default ARCH_DAVINCI_DA850 depends on ARCH_DAVINCI_DA850 - select GPIO_PCA953X + default GPIO_PCA953X help Say Y here to select the TI DA850/OMAP-L138/AM18x Evaluation Module. -- 1.7.0.4 From sshtylyov at mvista.com Mon Nov 15 09:47:27 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Mon, 15 Nov 2010 18:47:27 +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> <4CE124AE.4080301@mvista.com> Message-ID: <4CE1560F.6080705@mvista.com> Hello. Nori, Sekhar wrote: >>>> 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 >>>> 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, >>> Looks like I missed pointing this out previously, but extending >>> this list to take care of all boards will not be right since >>> (for example) AXR13 and AXR14 pins could be used for different >>> purpose on different boards. >> This is correct as the list in da850.c is a *generic* module's pin list. >> If the board needs less pins (and the pins it does not use for McASP are used >> differently), it should define its own pin list. >>> The right way would be to make this a per-board list. Since it >>> is marked __initdata, that wouldn't lead to bloat. >> This patch is correct anyway. Unless DA850 EVM board can't use these pins >> for McASP -- but in this case the corresponding board file needs the specific >> pin list added. > Okay. I guess you are saying we will keep adding pins to the generic list > as long as *all* supported boards don't get a conflict No, I don't care about conflicts as long as da850.c is concerned. > and if we run into > a conflict we will spawn separate list for the affected board. Yes. > The only issue I see with this approach is it puts too much burden on the > developer to verify that none of the supported boards break. That should be verified by the owners of that board. They shouldn't even have used the generic pin lists in the first place. The conception behind pin lists in da850.c was celarly misunderstood when DA850 EVM code was developed. > Since it is highly unlikely that any board will need all the McASP pins, > the generic list will likely remain unused. At least for now, Hawkboard will use all the defined pins -- I don't know if there are any more undefined yet... > It might just be easier to > start using board specific lists right away. This is especially true for > McASP where usage of pins across boards will likely vary widely. Well, that depends on how complete the new generic McASP pin list is. If it's still not, then board specific pin list looks preferable indeed... > Thanks, > Sekhar WBR, Sergei From sshtylyov at mvista.com Mon Nov 15 09:50:12 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Mon, 15 Nov 2010 18:50:12 +0300 Subject: [PATCH] da850-evm: allow pca953x module build In-Reply-To: <1289832353-22158-1-git-send-email-bengardiner@nanometrics.ca> References: <1289832353-22158-1-git-send-email-bengardiner@nanometrics.ca> Message-ID: <4CE156B4.1020500@mvista.com> Hello. Ben Gardiner wrote: > This patch changes the 'select GPIO_PCA953X' Kconfig command previously > set by the da850-evm config to 'default GPIO_PCA953X' so that users may > compile pca953x as a module. > Signed-off-by: Ben Gardiner > --- > > Note that since NAND/NOR setup is performed in the pcs953x setup() function of > board-da850-evm.c, when building pca953x as a module it will need to be > insmod'ed from early userspace (i.e. initrd) to use a NAND or NOR rootfs for > the system. > --- > arch/arm/mach-davinci/Kconfig | 2 +- > 1 files changed, 1 insertions(+), 1 deletions(-) > diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig > index b77b860..7410f70 100644 > --- a/arch/arm/mach-davinci/Kconfig > +++ b/arch/arm/mach-davinci/Kconfig > @@ -148,7 +148,7 @@ config MACH_DAVINCI_DA850_EVM > bool "TI DA850/OMAP-L138/AM18x Reference Platform" > default ARCH_DAVINCI_DA850 > depends on ARCH_DAVINCI_DA850 > - select GPIO_PCA953X > + default GPIO_PCA953X Hm, we now have two 'default' dircetives in the 'config' entry. Which one would take precedence? Is it really correct to have two? WBR. Sergei From bengardiner at nanometrics.ca Mon Nov 15 10:06:01 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Mon, 15 Nov 2010 11:06:01 -0500 Subject: [PATCH] da850-evm: allow pca953x module build In-Reply-To: <4CE156B4.1020500@mvista.com> References: <1289832353-22158-1-git-send-email-bengardiner@nanometrics.ca> <4CE156B4.1020500@mvista.com> Message-ID: Hello, Sergei. On Mon, Nov 15, 2010 at 10:50 AM, Sergei Shtylyov wrote: > ? Hm, we now have two 'default' dircetives in the 'config' entry. Which one > would take precedence? Is it really correct to have two? My mistake, sorry -- I have completely misinterpreted the semantics of the 'default' attribute. What I meant was 'turn on GPIO_PCA953X by default when the config has enabled the DA850EVM' but now I see that the 'default' attribute is specifying the default value of MACH_DAVINCI_DA850_EVM. The fact that I needed this change to build pca953x as a module was entirely due to the deletion of the 'select' attribute. I don't know how to specify: 'turn on GPIO_PCA953X by default when the config has enabled the DA850EVM' in the mach-davinci/KConfig; should I put it in drivers/gpio/Kconfig instead? Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From nikai at nikai.net Mon Nov 15 12:40:28 2010 From: nikai at nikai.net (Nicolas Kaiser) Date: Mon, 15 Nov 2010 19:40:28 +0100 Subject: [PATCH] mach-davinci: signedness bug Message-ID: <20101115194028.30f8b7f0@absol.kitzblitz> aemif_calc_rate() can return a negative error value, so all the variables that get tested for this value need to be signed. The maximum bit width of WSETUP(WSETUP_MAX) appears to be 30 bits (0xf << 26). Using a signed instead of an unsigned integer shouldn't make a difference here. Signed-off-by: Nicolas Kaiser --- arch/arm/mach-davinci/aemif.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/aemif.c b/arch/arm/mach-davinci/aemif.c index 9c3f500..1ce70a9 100644 --- a/arch/arm/mach-davinci/aemif.c +++ b/arch/arm/mach-davinci/aemif.c @@ -90,7 +90,7 @@ int davinci_aemif_setup_timing(struct davinci_aemif_timing *t, void __iomem *base, unsigned cs) { unsigned set, val; - unsigned ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup; + int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup; unsigned offset = A1CR_OFFSET + cs * 4; struct clk *aemif_clk; unsigned long clkrate; -- 1.7.2.2 From bengardiner at nanometrics.ca Mon Nov 15 13:30:19 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Mon, 15 Nov 2010 14:30:19 -0500 Subject: [PATCH v2] da850-evm: allow pca953x module build In-Reply-To: <4CE156B4.1020500@mvista.com> Message-ID: <1289849419-32343-1-git-send-email-bengardiner@nanometrics.ca> Change the Kconfig files so that GPIO_PCF857X is default when MACH_DAVINCI_DA850_EVM is set instead of always selecting so that users may compile pca953x as a module. Note that since NAND/NOR setup is performed in the pcs953x setup() function of board-da850-evm.c, when building pca953x as a module it will need to be insmod'ed from early userspace (i.e. initrd) to use a NAND or NOR rootfs for the system. Signed-off-by: Ben Gardiner Cc: Sergei Shtylyov Cc: David Brownell --- Changes since V1: * make PCA953x default when MACH_DAVINCI_DA850_EVM in drivers/gpio/Kconfig instead of selecting GPIO_PCF857X when MACH_DAVINCI_DA850_EVM in arc/arm/mach-davinci/Kconfig * adding David Brownell because I think he is the pca953x goto guy --- arch/arm/mach-davinci/Kconfig | 1 - drivers/gpio/Kconfig | 1 + 2 files changed, 1 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index b77b860..1d42fb9 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -148,7 +148,6 @@ config MACH_DAVINCI_DA850_EVM bool "TI DA850/OMAP-L138/AM18x Reference Platform" default ARCH_DAVINCI_DA850 depends on ARCH_DAVINCI_DA850 - select GPIO_PCA953X help Say Y here to select the TI DA850/OMAP-L138/AM18x Evaluation Module. diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 510aa20..2e23017 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -149,6 +149,7 @@ config GPIO_MAX732X_IRQ config GPIO_PCA953X tristate "PCA953x, PCA955x, TCA64xx, and MAX7310 I/O ports" depends on I2C + default y if MACH_DAVINCI_DA850_EVM help Say yes here to provide access to several register-oriented SMBus I/O expanders, made mostly by NXP or TI. Compatible -- 1.7.0.4 From nsekhar at ti.com Mon Nov 15 22:49:49 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 16 Nov 2010 10:19:49 +0530 Subject: [PATCH] mach-davinci: signedness bug In-Reply-To: <20101115194028.30f8b7f0@absol.kitzblitz> References: <20101115194028.30f8b7f0@absol.kitzblitz> Message-ID: Hi Nicolas, On Tue, Nov 16, 2010 at 00:10:28, Nicolas Kaiser wrote: > aemif_calc_rate() can return a negative error value, so all the > variables that get tested for this value need to be signed. > > The maximum bit width of WSETUP(WSETUP_MAX) appears to be 30 bits > (0xf << 26). Using a signed instead of an unsigned integer > shouldn't make a difference here. > > Signed-off-by: Nicolas Kaiser Thanks for the fix. You could use the subject: "davinci: signedness bug in davinci_aemif_setup_timing()" Other than that: Acked-by: Sekhar Nori Thanks, Sekhar > --- > arch/arm/mach-davinci/aemif.c | 2 +- > 1 files changed, 1 insertions(+), 1 deletions(-) > > diff --git a/arch/arm/mach-davinci/aemif.c b/arch/arm/mach-davinci/aemif.c > index 9c3f500..1ce70a9 100644 > --- a/arch/arm/mach-davinci/aemif.c > +++ b/arch/arm/mach-davinci/aemif.c > @@ -90,7 +90,7 @@ int davinci_aemif_setup_timing(struct davinci_aemif_timing *t, > void __iomem *base, unsigned cs) > { > unsigned set, val; > - unsigned ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup; > + int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup; > unsigned offset = A1CR_OFFSET + cs * 4; > struct clk *aemif_clk; > unsigned long clkrate; > -- > 1.7.2.2 > From krunal.patil at einfochips.com Tue Nov 16 00:32:39 2010 From: krunal.patil at einfochips.com (Krunal Patil) Date: Tue, 16 Nov 2010 12:02:39 +0530 Subject: Confusion regarding DVSDK 3.10 on DM6467 Message-ID: <4CE22587.1070201@einfochips.com> An HTML attachment was scrubbed... URL: From nsekhar at ti.com Tue Nov 16 00:59:37 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 16 Nov 2010 12:29:37 +0530 Subject: Confusion regarding DVSDK 3.10 on DM6467 In-Reply-To: <4CE22587.1070201@einfochips.com> References: <4CE22587.1070201@einfochips.com> Message-ID: Hi Krunal, On Tue, Nov 16, 2010 at 12:02:39, Krunal Patil wrote: > Hi, > > We are planning to use DVSDK 3.10 for DM6467 (729 Mhz) based customize > board. I have some confusion regarding the DVSDK version to be used for > development purpose. > > DVSDK_3_10_00_19_release_notes.pdf page-4 says that "This DVSDK release > does not explicitly support the older DM6467 EVM platform. Users who > have the older DM6467 EVM are currently advised not to upgrade from the > existing DVSDK 2.0.0.22." > > May I know why this limitation is there? Can't I use DVSDK 3.10 for > DM6467? And if I use DVSDK 3.10 for DM6467 what challenges I may face? The reason for this disclaimer is the fact that we never ran a full system test on the DM6467 EVM. There was a test cycle, but it focused on functionality only. As such there are no DM6467 EVM specific issues I am aware of. You can use the e2e.ti.com website for queries specific to TI releases. This list is mainly watched by those interested in upstream software development. The advantage of using the e2e.ti.com is that it is watched by many of TI FAEs and managers (apart from engineers) who would work towards a timely response to your queries. Thanks, Sekhar From krunal.patil at einfochips.com Tue Nov 16 01:02:02 2010 From: krunal.patil at einfochips.com (Krunal Patil) Date: Tue, 16 Nov 2010 12:32:02 +0530 Subject: Confusion regarding DVSDK 3.10 on DM6467 In-Reply-To: References: <4CE22587.1070201@einfochips.com> Message-ID: <4CE22C6A.8050005@einfochips.com> An HTML attachment was scrubbed... URL: From hebert.marcandre at gmail.com Tue Nov 16 06:08:20 2010 From: hebert.marcandre at gmail.com (=?ISO-8859-1?Q?Marc=2DAndr=E9_H=E9bert?=) Date: Tue, 16 Nov 2010 07:08:20 -0500 Subject: OMAPL138 cpu frequency with SDRAM Message-ID: Hi, I have 2 OMAPL138/AM1808 boards: one with mDDR and ther other with SDRAM. I am looking at options to reduce power consumption. One the of options we are looking into is the cpu frequency. Not having a lot of success with the SDRAM board. Here is a little list of what I tried (with the Arago tree). 1) I have tried enabling the cpu frequency scaling. It works with the mDDR board but any change in frequency fails when using the SDRAM board. I am guessing this is because the DDR is derived from PLL1 while the SDRAM uses PLL0. Is there a way to make the cpu frequency work or is this impossible with SDRAM? 2) With the SDRAM board, I then tried downclocking the cpu directly in the bootloader using the AISgen utility. Under the PLL0 tab, I modified by DIV1 from 1 to 2 to operate the CPU at 150Mhz instead of 300Mhz while preserving my SDRAM at 100Mhz. With this, the kernel hangs at boot during the SATA initialization (I can reach the console fine if I disable the AHCI). I looked at the SATA controller datasheet but couldn't find a link between the SYSCLK1 and the SATA (since DIV1 affects SYSCLK1, right?). 3) Similar to #2, I changed the PLL0 settings post-divider (2 to 3) and div3 (3 to 2). This gave me CPU 200 Mhz (with settings matching what is found under da850_opp_200 in da850.c) and still keeping the 100Mhz SDRAM. This configuration stalls during the kernel boot right after the reset of unused clocks. I would appreciate if anyone had some information on either how to fix these issues or other ways to do this. Thanks and regards Marc From chris at edesix.com Tue Nov 16 06:27:09 2010 From: chris at edesix.com (Chris Paulson-Ellis) Date: Tue, 16 Nov 2010 12:27:09 +0000 Subject: [PATCH] ASoC: davinci: fixes for multi-component Message-ID: <1289910429-29347-1-git-send-email-chris@edesix.com> Multi-component commit f0fba2ad broke a few things which this patch should fix. Tested on the DM355 EVM. I've been as careful as I can, but it would be good if those with access to other Davinci boards could test. -- The multi-component commit put the initialisation of snd_soc_dai.[capture|playback]_dma_data into snd_soc_dai_ops.hw_params of the McBSP, McASP & VCIF drivers (davinci-i2s.c, davinci-mcasp.c & davinci-vcif.c). The initialisation had to be moved from the probe function in these drivers because davinci_*_dai changed from snd_soc_dai to snd_soc_dai_driver. Unfortunately, the DMA params pointer is needed by davinci_pcm_open (in davinci-pcm.c) before hw_params is called. I have moved the initialisation to a new snd_soc_dai_ops.startup function in each of these drivers. This fix indicates that all platforms that use davinci-pcm must have been broken and need to test with this fix. -- The multi-component commit also changed the McBSP driver name from "davinci-asp" to "davinci-i2s" in davinci-i2s.c without updating the board level references to the driver name. This change is understandable, as there is a similarly named "davinci-mcasp" driver in davinci-mcasp.c. There is probably no 'correct' name for this driver. The DM6446 datasheet calls it the "ASP" and describes it as a "specialised McBSP". The DM355 datasheet calls it the "ASP" and describes it as a "specialised ASP". The DM365 datasheet calls it the "McBSP". Rather than fix this problem by reverting to "davinci-asp", I've elected to avoid future confusion with the "davinci-mcasp" driver by changing it to "davinci-mcbsp", which is also consistent with the names of the functions in the driver. There are other fixes required, so it was never going to be as simple as a revert anyway. -- The DM365 only has one McBSP port (of the McBSP platforms, only the DM355 has 2 ports), so I've changed the the id of the platform_device from 0 to -1. -- In davinci-evm.c, the DM6446 EVM can no longer share a snd_soc_dai_link structure with the DM355 EVM as they use different cpu DAI names (the DM355 has 2 ports and the EVM uses the second port, but the DM6446 only has 1 port). This also means that the 2 boards need different snd_soc_card structures. -- The codec_name entries in davinci-evm.c didn't match the i2c ids in the board files. I have only checked and fixed the details of the names used for the McBSP based platforms. Someone with a McASP based platform (eg DA8xx) should check the others. Signed-off-by: Chris Paulson-Ellis --- arch/arm/mach-davinci/dm355.c | 6 ++-- arch/arm/mach-davinci/dm365.c | 6 ++-- arch/arm/mach-davinci/dm644x.c | 4 +- sound/soc/davinci/davinci-evm.c | 40 ++++++++++++++++++++++++++--------- sound/soc/davinci/davinci-i2s.c | 15 +++++++++--- sound/soc/davinci/davinci-mcasp.c | 13 +++++++++-- sound/soc/davinci/davinci-sffsdr.c | 2 +- sound/soc/davinci/davinci-vcif.c | 13 +++++++++-- 8 files changed, 69 insertions(+), 30 deletions(-) diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 9be261b..2652af1 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -359,8 +359,8 @@ static struct clk_lookup dm355_clks[] = { CLK(NULL, "uart1", &uart1_clk), CLK(NULL, "uart2", &uart2_clk), CLK("i2c_davinci.1", NULL, &i2c_clk), - CLK("davinci-asp.0", NULL, &asp0_clk), - CLK("davinci-asp.1", NULL, &asp1_clk), + CLK("davinci-mcbsp.0", NULL, &asp0_clk), + CLK("davinci-mcbsp.1", NULL, &asp1_clk), CLK("davinci_mmc.0", NULL, &mmcsd0_clk), CLK("davinci_mmc.1", NULL, &mmcsd1_clk), CLK("spi_davinci.0", NULL, &spi0_clk), @@ -664,7 +664,7 @@ static struct resource dm355_asp1_resources[] = { }; static struct platform_device dm355_asp1_device = { - .name = "davinci-asp", + .name = "davinci-mcbsp", .id = 1, .num_resources = ARRAY_SIZE(dm355_asp1_resources), .resource = dm355_asp1_resources, diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index a12065e..c466d71 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -459,7 +459,7 @@ static struct clk_lookup dm365_clks[] = { CLK(NULL, "usb", &usb_clk), CLK("davinci_emac.1", NULL, &emac_clk), CLK("davinci_voicecodec", NULL, &voicecodec_clk), - CLK("davinci-asp.0", NULL, &asp0_clk), + CLK("davinci-mcbsp", NULL, &asp0_clk), CLK(NULL, "rto", &rto_clk), CLK(NULL, "mjcp", &mjcp_clk), CLK(NULL, NULL, NULL), @@ -922,8 +922,8 @@ static struct resource dm365_asp_resources[] = { }; static struct platform_device dm365_asp_device = { - .name = "davinci-asp", - .id = 0, + .name = "davinci-mcbsp", + .id = -1, .num_resources = ARRAY_SIZE(dm365_asp_resources), .resource = dm365_asp_resources, }; diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 0608dd7..9a2376b 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -302,7 +302,7 @@ static struct clk_lookup dm644x_clks[] = { CLK("davinci_emac.1", NULL, &emac_clk), CLK("i2c_davinci.1", NULL, &i2c_clk), CLK("palm_bk3710", NULL, &ide_clk), - CLK("davinci-asp", NULL, &asp_clk), + CLK("davinci-mcbsp", NULL, &asp_clk), CLK("davinci_mmc.0", NULL, &mmcsd_clk), CLK(NULL, "spi", &spi_clk), CLK(NULL, "gpio", &gpio_clk), @@ -580,7 +580,7 @@ static struct resource dm644x_asp_resources[] = { }; static struct platform_device dm644x_asp_device = { - .name = "davinci-asp", + .name = "davinci-mcbsp", .id = -1, .num_resources = ARRAY_SIZE(dm644x_asp_resources), .resource = dm644x_asp_resources, diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index a2cf64b..fd96905 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -158,12 +158,23 @@ static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd) } /* davinci-evm digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link evm_dai = { +static struct snd_soc_dai_link dm6446_evm_dai = { .name = "TLV320AIC3X", .stream_name = "AIC3X", - .cpu_dai_name = "davinci-mcasp.0", + .cpu_dai_name = "davinci-mcbsp", .codec_dai_name = "tlv320aic3x-hifi", - .codec_name = "tlv320aic3x-codec.0-001a", + .codec_name = "tlv320aic3x-codec.1-001b", + .platform_name = "davinci-pcm-audio", + .init = evm_aic3x_init, + .ops = &evm_ops, +}; + +static struct snd_soc_dai_link dm355_evm_dai = { + .name = "TLV320AIC3X", + .stream_name = "AIC3X", + .cpu_dai_name = "davinci-mcbsp.1", + .codec_dai_name = "tlv320aic3x-hifi", + .codec_name = "tlv320aic3x-codec.1-001b", .platform_name = "davinci-pcm-audio", .init = evm_aic3x_init, .ops = &evm_ops, @@ -173,10 +184,10 @@ static struct snd_soc_dai_link dm365_evm_dai = { #ifdef CONFIG_SND_DM365_AIC3X_CODEC .name = "TLV320AIC3X", .stream_name = "AIC3X", - .cpu_dai_name = "davinci-i2s", + .cpu_dai_name = "davinci-mcbsp", .codec_dai_name = "tlv320aic3x-hifi", .init = evm_aic3x_init, - .codec_name = "tlv320aic3x-codec.0-001a", + .codec_name = "tlv320aic3x-codec.1-0018", .ops = &evm_ops, #elif defined(CONFIG_SND_DM365_VOICE_CODEC) .name = "Voice Codec - CQ93VC", @@ -220,10 +231,17 @@ static struct snd_soc_dai_link da8xx_evm_dai = { .ops = &evm_ops, }; -/* davinci dm6446, dm355 evm audio machine driver */ -static struct snd_soc_card snd_soc_card_evm = { - .name = "DaVinci EVM", - .dai_link = &evm_dai, +/* davinci dm6446 evm audio machine driver */ +static struct snd_soc_card dm6446_snd_soc_card_evm = { + .name = "DaVinci DM6446 EVM", + .dai_link = &dm6446_evm_dai, + .num_links = 1, +}; + +/* davinci dm355 evm audio machine driver */ +static struct snd_soc_card dm355_snd_soc_card_evm = { + .name = "DaVinci DM355 EVM", + .dai_link = &dm355_evm_dai, .num_links = 1, }; @@ -262,10 +280,10 @@ static int __init evm_init(void) int ret; if (machine_is_davinci_evm()) { - evm_snd_dev_data = &snd_soc_card_evm; + evm_snd_dev_data = &dm6446_snd_soc_card_evm; index = 0; } else if (machine_is_davinci_dm355_evm()) { - evm_snd_dev_data = &snd_soc_card_evm; + evm_snd_dev_data = &dm355_snd_soc_card_evm; index = 1; } else if (machine_is_davinci_dm365_evm()) { evm_snd_dev_data = &dm365_snd_soc_card_evm; diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index d46b545..9e0e565 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -426,9 +426,6 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, snd_pcm_format_t fmt; unsigned element_cnt = 1; - dai->capture_dma_data = dev->dma_params; - dai->playback_dma_data = dev->dma_params; - /* general line settings */ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { @@ -601,6 +598,15 @@ static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +static int davinci_i2s_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_set_dma_data(dai, substream, dev->dma_params); + return 0; +} + static void davinci_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -612,6 +618,7 @@ static void davinci_i2s_shutdown(struct snd_pcm_substream *substream, #define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000 static struct snd_soc_dai_ops davinci_i2s_dai_ops = { + .startup = davinci_i2s_startup, .shutdown = davinci_i2s_shutdown, .prepare = davinci_i2s_prepare, .trigger = davinci_i2s_trigger, @@ -749,7 +756,7 @@ static struct platform_driver davinci_mcbsp_driver = { .probe = davinci_i2s_probe, .remove = davinci_i2s_remove, .driver = { - .name = "davinci-i2s", + .name = "davinci-mcbsp", .owner = THIS_MODULE, }, }; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 86918ee..fb55d2c 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -715,9 +715,6 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, int word_length; u8 fifo_level; - cpu_dai->capture_dma_data = dev->dma_params; - cpu_dai->playback_dma_data = dev->dma_params; - davinci_hw_common_param(dev, substream->stream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) fifo_level = dev->txnumevt; @@ -799,7 +796,17 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream, return ret; } +static int davinci_mcasp_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct davinci_audio_dev *dev = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_set_dma_data(dai, substream, dev->dma_params); + return 0; +} + static struct snd_soc_dai_ops davinci_mcasp_dai_ops = { + .startup = davinci_mcasp_startup, .trigger = davinci_mcasp_trigger, .hw_params = davinci_mcasp_hw_params, .set_fmt = davinci_mcasp_set_dai_fmt, diff --git a/sound/soc/davinci/davinci-sffsdr.c b/sound/soc/davinci/davinci-sffsdr.c index 009b652..6c6666a 100644 --- a/sound/soc/davinci/davinci-sffsdr.c +++ b/sound/soc/davinci/davinci-sffsdr.c @@ -84,7 +84,7 @@ static struct snd_soc_ops sffsdr_ops = { static struct snd_soc_dai_link sffsdr_dai = { .name = "PCM3008", /* Codec name */ .stream_name = "PCM3008 HiFi", - .cpu_dai_name = "davinci-asp.0", + .cpu_dai_name = "davinci-mcbsp", .codec_dai_name = "pcm3008-hifi", .codec_name = "pcm3008-codec", .platform_name = "davinci-pcm-audio", diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c index ea232f6..fb4cc1e 100644 --- a/sound/soc/davinci/davinci-vcif.c +++ b/sound/soc/davinci/davinci-vcif.c @@ -97,9 +97,6 @@ static int davinci_vcif_hw_params(struct snd_pcm_substream *substream, &davinci_vcif_dev->dma_params[substream->stream]; u32 w; - dai->capture_dma_data = davinci_vcif_dev->dma_params; - dai->playback_dma_data = davinci_vcif_dev->dma_params; - /* Restart the codec before setup */ davinci_vcif_stop(substream); davinci_vcif_start(substream); @@ -174,9 +171,19 @@ static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd, return ret; } +static int davinci_vcif_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_set_dma_data(dai, substream, dev->dma_params); + return 0; +} + #define DAVINCI_VCIF_RATES SNDRV_PCM_RATE_8000_48000 static struct snd_soc_dai_ops davinci_vcif_dai_ops = { + .startup = davinci_vcif_startup, .trigger = davinci_vcif_trigger, .hw_params = davinci_vcif_hw_params, }; -- 1.7.2.3 From g.liakhovetski at gmx.de Thu Nov 11 09:32:02 2010 From: g.liakhovetski at gmx.de (Guennadi Liakhovetski) Date: Thu, 11 Nov 2010 16:32:02 +0100 (CET) Subject: mediabus enums In-Reply-To: References: Message-ID: On Wed, 10 Nov 2010, Hadli, Manjunath wrote: > Hello Guennadi, > Your media-bus enumerations capture the formats quite well. I needed > the following for support on Davinci SOCs and liked to check with you if > these are covered in some format in the list. > 1. Parallel RGB 666 (18 data lines+ 5 sync lines) > 2. YUYV16 (16 lines) (16 data lines + 4 or 5 sync lines) According to the subdev-formats.xml http://git.linuxtv.org/pinchartl/media.git?a=blob;f=Documentation/DocBook/v4l/subdev-formats.xml;h=3688f27185f72ab109e3094c268e04f67cb8643e;hb=refs/heads/media-0003-subdev-pad they should be called V4L2_MBUS_FMT_RGB666_1X18 (or BGR666...) and V4L2_MBUS_FMT_YUYV16_1X16. Notice, that these codes do not define the complete bus topology, e.g., they say nothing about sync signals. This is a separate topic. Thanks Guennadi --- Guennadi Liakhovetski, Ph.D. Freelance Open-Source Software Developer http://www.open-technology.de/ From g.liakhovetski at gmx.de Fri Nov 12 10:20:52 2010 From: g.liakhovetski at gmx.de (Guennadi Liakhovetski) Date: Fri, 12 Nov 2010 17:20:52 +0100 (CET) Subject: mediabus enums In-Reply-To: References: Message-ID: On Fri, 12 Nov 2010, Hadli, Manjunath wrote: > On Thu, Nov 11, 2010 at 22:17:23, Laurent Pinchart wrote: > > Hi Guennadi, > > > > On Thursday 11 November 2010 16:32:02 Guennadi Liakhovetski wrote: > > > On Wed, 10 Nov 2010, Hadli, Manjunath wrote: > > > > Hello Guennadi, > > > > > > > > Your media-bus enumerations capture the formats quite well. I > > > > needed > > > > > > > > the following for support on Davinci SOCs and liked to check with > > > > you if these are covered in some format in the list. > > > > 1. Parallel RGB 666 (18 data lines+ 5 sync lines) 2. YUYV16 (16 > > > > lines) (16 data lines + 4 or 5 sync lines) > > > > > > According to the subdev-formats.xml > > > > > > http://git.linuxtv.org/pinchartl/media.git?a=blob;f=Documentation/DocB > > > ook/v > > > 4l/subdev-formats.xml;h=3688f27185f72ab109e3094c268e04f67cb8643e;hb=re > > > fs/he > > > ads/media-0003-subdev-pad > > > > > > they should be called V4L2_MBUS_FMT_RGB666_1X18 (or BGR666...) > > > > Agreed. > > > > > and V4L2_MBUS_FMT_YUYV16_1X16. > > > > Depending on what Manjunath meant, this should be either YUYV16_2X16 or > > YUYV8_1X16. 16 bits per sample seems quite high to me, I suppose it should > > then be YUYV8_1X16. > > Actually, the interface transfers 16 bits per sample (Y=8bits and C=8bits) > For the YC16 and 18 data lines (parallel) for RGB666. probably > V4L2_MBUS_FMT_RGB666_1X18 and V4L2_MBUS_FMT_YUYV16_1X16 fit the bill. No, I think, Laurent was right. If that your 1 16-bit sample also corresponds to one pixel, then it's a normal 16-bit YUYV, i.e., V4L2_MBUS_FMT_YUYV8_1X16. In the aforementioned document the number after "YUYV" is "The number of bits per pixel component," where the "component" is either the luminance, or the chrominance, both of which are 8 bit wide. Thanks Guennadi --- Guennadi Liakhovetski, Ph.D. Freelance Open-Source Software Developer http://www.open-technology.de/ From cyril at ti.com Mon Nov 15 13:12:02 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Mon, 15 Nov 2010 14:12:02 -0500 Subject: [PATCH v5 00/12] tnetv107x ssp drivers Message-ID: <1289848334-8695-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 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 | 192 +++++++ 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/mfd/Kconfig | 31 + drivers/mfd/Makefile | 3 + drivers/mfd/ti-ssp-gpio.c | 205 +++++++ drivers/mfd/ti-ssp-spi.c | 402 ++++++++++++++ drivers/mfd/ti-ssp.c | 475 ++++++++++++++++ drivers/regulator/Kconfig | 10 + drivers/regulator/Makefile | 1 + drivers/regulator/tps6524x-regulator.c | 692 ++++++++++++++++++++++++ drivers/video/backlight/Kconfig | 7 + drivers/video/backlight/Makefile | 2 +- drivers/video/backlight/tps6116x.c | 339 ++++++++++++ include/linux/mfd/ti_ssp.h | 97 ++++ 16 files changed, 2483 insertions(+), 2 deletions(-) create mode 100644 drivers/mfd/ti-ssp-gpio.c create mode 100644 drivers/mfd/ti-ssp-spi.c create mode 100644 drivers/mfd/ti-ssp.c create mode 100644 drivers/regulator/tps6524x-regulator.c create mode 100644 drivers/video/backlight/tps6116x.c create mode 100644 include/linux/mfd/ti_ssp.h From cyril at ti.com Mon Nov 15 13:12:04 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Mon, 15 Nov 2010 14:12:04 -0500 Subject: [PATCH v5 02/12] davinci: add tnetv107x ssp platform device In-Reply-To: <1289848334-8695-1-git-send-email-cyril@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> Message-ID: <1289848334-8695-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 Mon Nov 15 13:12:06 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Mon, 15 Nov 2010 14:12:06 -0500 Subject: [PATCH v5 04/12] spi: add ti-ssp spi master driver In-Reply-To: <1289848334-8695-1-git-send-email-cyril@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> Message-ID: <1289848334-8695-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/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/ti-ssp-spi.c | 402 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/ti_ssp.h | 6 + 4 files changed, 419 insertions(+), 0 deletions(-) create mode 100644 drivers/mfd/ti-ssp-spi.c diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 6bedf53..7dcc03a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -92,6 +92,16 @@ config MFD_TI_SSP To compile this driver as a module, choose M here: the module will be called ti-ssp. +config MFD_TI_SSP_SPI + tristate "TI Sequencer Serial Port - SPI Support" + depends on SPI_MASTER && 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 HTC_EGPIO bool "HTC EGPIO support" depends on GENERIC_HARDIRQS && GPIOLIB && ARM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 098a932..ba97f48 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -15,6 +15,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_TI_SSP_SPI) += ti-ssp-spi.o obj-$(CONFIG_MFD_STMPE) += stmpe.o obj-$(CONFIG_MFD_TC35892) += tc35892.o diff --git a/drivers/mfd/ti-ssp-spi.c b/drivers/mfd/ti-ssp-spi.c new file mode 100644 index 0000000..d381a51 --- /dev/null +++ b/drivers/mfd/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); +} +subsys_initcall(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 Mon Nov 15 13:12:03 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Mon, 15 Nov 2010 14:12:03 -0500 Subject: [PATCH v5 01/12] misc: add driver for sequencer serial port In-Reply-To: <1289848334-8695-1-git-send-email-cyril@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> Message-ID: <1289848334-8695-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 | 475 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/ti_ssp.h | 87 ++++++++ 4 files changed, 574 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 db51ea1..6bedf53 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 feaeeae..098a932 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..ac3171a --- /dev/null +++ b/drivers/mfd/ti-ssp.c @@ -0,0 +1,475 @@ +/* + * 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; +}; + +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) +{ + u32 val = ssp_read(ssp, reg); + val &= ~mask; + val |= bits; + ssp_write(ssp, reg, val); +} + +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 port iosel, caller must hold ssp->lock */ +static int __set_iosel(struct ti_ssp *ssp, int port, u32 iosel) +{ + unsigned val; + + /* 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 = ssp_read(ssp, REG_IOSEL_2); + val &= 0x0007 << (port ? 0 : 16); + val |= (iosel & 0x00070000) >> (port ? 0 : 16); + ssp_write(ssp, REG_IOSEL_2, val); + + return 0; +} + +int ti_ssp_set_iosel(struct device *dev, u32 iosel) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + int ret; + + spin_lock(&ssp->lock); + ret = __set_iosel(ssp, port, iosel); + spin_unlock(&ssp->lock); + + return ret; +} +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); + u32 val; + + val = ssp_read(ssp, REG_IOSEL_2); + val >>= (port ? 27 : 11); + + return val & 0x0f; +} +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); + u32 mask; + + spin_lock(&ssp->lock); + + val &= 0x0f; + val <<= (port ? 22 : 6); + mask = 0x0f << (port ? 22 : 6); + ssp_rmw(ssp, REG_IOSEL_2, mask, val); + + 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); +} +arch_initcall_sync(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 Mon Nov 15 13:12:05 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Mon, 15 Nov 2010 14:12:05 -0500 Subject: [PATCH v5 03/12] davinci: add ssp config for tnetv107x evm board In-Reply-To: <1289848334-8695-1-git-send-email-cyril@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> Message-ID: <1289848334-8695-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 Mon Nov 15 13:12:07 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Mon, 15 Nov 2010 14:12:07 -0500 Subject: [PATCH v5 05/12] davinci: add spi devices on tnetv107x evm In-Reply-To: <1289848334-8695-1-git-send-email-cyril@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> Message-ID: <1289848334-8695-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 Mon Nov 15 13:12:10 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Mon, 15 Nov 2010 14:12:10 -0500 Subject: [PATCH v5 08/12] gpio: add ti-ssp gpio driver In-Reply-To: <1289848334-8695-1-git-send-email-cyril@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> Message-ID: <1289848334-8695-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/mfd/Kconfig | 10 ++ drivers/mfd/Makefile | 1 + drivers/mfd/ti-ssp-gpio.c | 205 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/ti_ssp.h | 4 + 4 files changed, 220 insertions(+), 0 deletions(-) create mode 100644 drivers/mfd/ti-ssp-gpio.c diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7dcc03a..b193580 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -102,6 +102,16 @@ config MFD_TI_SSP_SPI To compile this driver as a module, choose M here: the module will be called ti-ssp-spi. +config MFD_TI_SSP_GPIO + tristate "TI Sequencer Serial Port - GPIO Support" + depends on GPIOLIB && 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. + config HTC_EGPIO bool "HTC EGPIO support" depends on GENERIC_HARDIRQS && GPIOLIB && ARM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index ba97f48..87b5de8 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -16,6 +16,7 @@ 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_TI_SSP_SPI) += ti-ssp-spi.o +obj-$(CONFIG_MFD_TI_SSP_GPIO) += ti-ssp-gpio.o obj-$(CONFIG_MFD_STMPE) += stmpe.o obj-$(CONFIG_MFD_TC35892) += tc35892.o diff --git a/drivers/mfd/ti-ssp-gpio.c b/drivers/mfd/ti-ssp-gpio.c new file mode 100644 index 0000000..a3e909b --- /dev/null +++ b/drivers/mfd/ti-ssp-gpio.c @@ -0,0 +1,205 @@ +/* + * 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 { + struct gpio_chip chip; +#define chip2gpio(chip) container_of(chip, struct ti_ssp_gpio, chip) + struct device *dev; + spinlock_t lock; + u8 out; + u32 iosel; +}; + +static int direction_in(struct gpio_chip *chip, unsigned gpio_num) +{ + struct ti_ssp_gpio *gpio = chip2gpio(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 *gpio = chip2gpio(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 *gpio = chip2gpio(chip); + int ret; + + spin_lock(&gpio->lock); + + ret = ti_ssp_raw_read(gpio->dev); + if (ret >= 0) + ret = (ret & BIT(gpio_num)) ? 1 : 0; + + spin_unlock(&gpio->lock); + return ret; +} + +static void value_set(struct gpio_chip *chip, unsigned gpio_num, int val) +{ + struct ti_ssp_gpio *gpio = chip2gpio(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 *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 *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); +} +subsys_initcall(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 Mon Nov 15 13:12:09 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Mon, 15 Nov 2010 14:12:09 -0500 Subject: [PATCH v5 07/12] davinci: add tnetv107x evm regulators In-Reply-To: <1289848334-8695-1-git-send-email-cyril@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> Message-ID: <1289848334-8695-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 Mon Nov 15 13:12:11 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Mon, 15 Nov 2010 14:12:11 -0500 Subject: [PATCH v5 09/12] davinci: add tnetv107x evm ti-ssp gpio device In-Reply-To: <1289848334-8695-1-git-send-email-cyril@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> Message-ID: <1289848334-8695-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 Mon Nov 15 13:12:12 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Mon, 15 Nov 2010 14:12:12 -0500 Subject: [PATCH v5 10/12] backlight: add support for tps6116x controller In-Reply-To: <1289848334-8695-1-git-send-email-cyril@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> Message-ID: <1289848334-8695-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 | 339 ++++++++++++++++++++++++++++++++++++ 3 files changed, 347 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..a54bb15 --- /dev/null +++ b/drivers/video/backlight/tps6116x.c @@ -0,0 +1,339 @@ +/* + * 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 gpio_initialized; + 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); + + if (!hw->gpio_initialized) { + error = gpio_request_one(hw->gpio, GPIOF_DIR_OUT, + dev_name(hw->dev)); + if (error < 0) + goto error; + hw->gpio_initialized = true; + } + + 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 ssize_t intensity_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tps6116x *hw = dev_get_drvdata(dev); + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", hw->intensity); + + return len; +} + +static ssize_t intensity_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct tps6116x *hw = dev_get_drvdata(dev); + unsigned long intensity; + int error; + + error = strict_strtoul(buf, 10, &intensity); + if (error < 0) + return error; + + error = set_intensity(hw, intensity); + if (error < 0) + return error; + + return count; +} + +DEVICE_ATTR(intensity, S_IWUSR | S_IRUGO, intensity_show, intensity_store); + +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) + 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 = { + .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) { + error = -ENOMEM; + dev_err(dev, "cannot allocate driver data\n"); + goto fail0; + } + platform_set_drvdata(pdev, hw); + + hw->gpio = (int)dev->platform_data; + hw->dev = dev; + + mutex_init(&hw->lock); + + error = device_create_file(dev, &dev_attr_intensity); + if (error < 0) { + dev_err(dev, "cannot create device attributes\n"); + goto fail1; + } + + hw->regulator = regulator_get(dev, "vlcd"); + if (IS_ERR(hw->regulator)) { + error = PTR_ERR(hw->regulator); + dev_err(dev, "cannot claim regulator\n"); + goto fail2; + } + + memset(&props, 0, sizeof(props)); + props.max_brightness = TPS6116X_MAX_INTENSITY; + props.brightness = TPS6116X_DEFAULT_INTENSITY; + + 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 fail3; + } + + dev_info(dev, "registered backlight controller\n"); + return 0; + +fail3: + regulator_put(hw->regulator); +fail2: + device_remove_file(dev, &dev_attr_intensity); +fail1: + kfree(hw); + platform_set_drvdata(pdev, NULL); +fail0: + 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); + device_remove_file(hw->dev, &dev_attr_intensity); + 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 Mon Nov 15 13:12:08 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Mon, 15 Nov 2010 14:12:08 -0500 Subject: [PATCH v5 06/12] regulator: add driver for tps6524x regulator In-Reply-To: <1289848334-8695-1-git-send-email-cyril@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> Message-ID: <1289848334-8695-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 172951b..a6f8536 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -235,5 +235,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 8285fd8..a8e5bc0 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -33,6 +33,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..6d6cc5e --- /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); +} +subsys_initcall_sync(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 Mon Nov 15 13:12:13 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Mon, 15 Nov 2010 14:12:13 -0500 Subject: [PATCH v5 11/12] davinci: add tnetv107x evm backlight device In-Reply-To: <1289848334-8695-1-git-send-email-cyril@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> Message-ID: <1289848334-8695-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 | 9 +++++++++ 1 files changed, 9 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..945a958 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); @@ -361,6 +368,8 @@ static __init void tnetv107x_evm_board_init(void) tnetv107x_devices_init(&evm_device_info); + platform_device_register(&backlight_device); + spi_register_board_info(spi_info, ARRAY_SIZE(spi_info)); } -- 1.7.1 From cyril at ti.com Mon Nov 15 13:12:14 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Mon, 15 Nov 2010 14:12:14 -0500 Subject: [PATCH v5 12/12] davinci: add tnetv107x evm i2c eeprom device In-Reply-To: <1289848334-8695-1-git-send-email-cyril@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> Message-ID: <1289848334-8695-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 945a958..8059c7d 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,8 +397,10 @@ static __init void tnetv107x_evm_board_init(void) tnetv107x_devices_init(&evm_device_info); platform_device_register(&backlight_device); + platform_device_register(&i2c_device); spi_register_board_info(spi_info, ARRAY_SIZE(spi_info)); + i2c_register_board_info(0, i2c_info, ARRAY_SIZE(i2c_info)); } #ifdef CONFIG_SERIAL_8250_CONSOLE -- 1.7.1 From grant.likely at secretlab.ca Tue Nov 16 01:10:47 2010 From: grant.likely at secretlab.ca (Grant Likely) Date: Tue, 16 Nov 2010 00:10:47 -0700 Subject: [PATCH v5 01/12] misc: add driver for sequencer serial port In-Reply-To: <1289848334-8695-2-git-send-email-cyril@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> <1289848334-8695-2-git-send-email-cyril@ti.com> Message-ID: <20101116071047.GE4074@angua.secretlab.ca> On Mon, Nov 15, 2010 at 02:12:03PM -0500, Cyril Chemparathy wrote: > 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 | 475 ++++++++++++++++++++++++++++++++++++++++++++ > include/linux/mfd/ti_ssp.h | 87 ++++++++ > 4 files changed, 574 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 db51ea1..6bedf53 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 feaeeae..098a932 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..ac3171a > --- /dev/null > +++ b/drivers/mfd/ti-ssp.c > @@ -0,0 +1,475 @@ > +/* > + * 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; > +}; > + > +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); > +} I'm pretty sure it was resolved that __raw_ versions should not be used here. > + > +static inline void ssp_rmw(struct ti_ssp *ssp, int reg, u32 mask, u32 bits) > +{ > + u32 val = ssp_read(ssp, reg); > + val &= ~mask; > + val |= bits; > + ssp_write(ssp, reg, val); or simply: 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 port iosel, caller must hold ssp->lock */ > +static int __set_iosel(struct ti_ssp *ssp, int port, u32 iosel) > +{ > + unsigned val; > + > + /* 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 = ssp_read(ssp, REG_IOSEL_2); > + val &= 0x0007 << (port ? 0 : 16); > + val |= (iosel & 0x00070000) >> (port ? 0 : 16); > + ssp_write(ssp, REG_IOSEL_2, val); > + > + return 0; > +} > + > +int ti_ssp_set_iosel(struct device *dev, u32 iosel) > +{ > + struct ti_ssp *ssp = dev_to_ssp(dev); > + int port = dev_to_port(dev); > + int ret; > + > + spin_lock(&ssp->lock); > + ret = __set_iosel(ssp, port, iosel); > + spin_unlock(&ssp->lock); > + > + return ret; > +} > +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); > + u32 val; > + > + val = ssp_read(ssp, REG_IOSEL_2); > + val >>= (port ? 27 : 11); > + > + return val & 0x0f; > +} > +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); > + u32 mask; > + > + spin_lock(&ssp->lock); > + > + val &= 0x0f; > + val <<= (port ? 22 : 6); > + mask = 0x0f << (port ? 22 : 6); > + ssp_rmw(ssp, REG_IOSEL_2, mask, val); > + > + 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); > +} > +arch_initcall_sync(ti_ssp_init); This should be module_init(). More on this when I reply to the spi driver. > + > +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 grant.likely at secretlab.ca Tue Nov 16 01:22:25 2010 From: grant.likely at secretlab.ca (Grant Likely) Date: Tue, 16 Nov 2010 00:22:25 -0700 Subject: [PATCH v5 04/12] spi: add ti-ssp spi master driver In-Reply-To: <1289848334-8695-5-git-send-email-cyril@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> <1289848334-8695-5-git-send-email-cyril@ti.com> Message-ID: <20101116072225.GF4074@angua.secretlab.ca> On Mon, Nov 15, 2010 at 02:12:06PM -0500, Cyril Chemparathy wrote: > This patch adds an SPI master implementation that operates on top of an > underlying TI-SSP port. > > Signed-off-by: Cyril Chemparathy [...] > +static int __init ti_ssp_spi_init(void) > +{ > + return platform_driver_register(&ti_ssp_spi_driver); > +} > +subsys_initcall(ti_ssp_spi_init); After discussions about device init dependencies at plumbers, and since this is the first SPI device driver I've reviewed since that dicussion, this driver gets to be the first to implement the proposed policy. :-) Change this to module_initcall(). Many spi and i2c drivers use module or subsys_initcall to try and influence driver probe order so that certain regulator chips get registered before the devices that try to use them. This approach is insane. Instead, it is now incumbent on the board support code to ensure that any device that depends on another device (including i2c or spi regulators) will defer registration until the prerequisite devices are bound to drivers. I don't *think* this change will affect anything in this particular patch series, but if it does then let me know and I'll help you work out how to fix it using a bus notifier. g. From grant.likely at secretlab.ca Tue Nov 16 01:47:04 2010 From: grant.likely at secretlab.ca (Grant Likely) Date: Tue, 16 Nov 2010 00:47:04 -0700 Subject: [PATCH v5 04/12] spi: add ti-ssp spi master driver In-Reply-To: <20101116072225.GF4074@angua.secretlab.ca> References: <1289848334-8695-1-git-send-email-cyril@ti.com> <1289848334-8695-5-git-send-email-cyril@ti.com> <20101116072225.GF4074@angua.secretlab.ca> Message-ID: On Tue, Nov 16, 2010 at 12:22 AM, Grant Likely wrote: > On Mon, Nov 15, 2010 at 02:12:06PM -0500, Cyril Chemparathy wrote: >> This patch adds an SPI master implementation that operates on top of an >> underlying TI-SSP port. >> >> Signed-off-by: Cyril Chemparathy > [...] >> +static int __init ti_ssp_spi_init(void) >> +{ >> + ? ? return platform_driver_register(&ti_ssp_spi_driver); >> +} >> +subsys_initcall(ti_ssp_spi_init); > > After discussions about device init dependencies at plumbers, and > since this is the first SPI device driver I've reviewed since that > dicussion, this driver gets to be the first to implement the proposed > policy. ?:-) > > Change this to module_initcall(). ?Many spi and i2c drivers use > module or subsys_initcall to try and influence driver probe order so > that certain regulator chips get registered before the devices that > try to use them. ?This approach is insane. > > Instead, it is now incumbent on the board support code to ensure that > any device that depends on another device (including i2c or spi > regulators) will defer registration until the prerequisite devices are > bound to drivers. > > I don't *think* this change will affect anything in this particular > patch series, but if it does then let me know and I'll help you work out > how to fix it using a bus notifier. Oh, wait, spoke too soon. You do add a regulator in this series, so this change will require a fixup. The solution is to register an bus_notifier to the spi bus type before you start registering devices. It also requires deferring the musb_hdrc.1 and tps6116x registrations until the bus_notifier callback gets called with an indication that the regulator is bound. It will look something like this: static int tnetv107x_spi_notifier_call(struct notifier_block *nb, unsigned long event, void *__dev) { struct device *dev = __dev; if ((event == BUS_NOTIFY_BOUND_DRIVER) && (dev == (the-regulator))) { register-the-remaining-devices(); return NOTIFY_STOP; }; return NOTIFY_DONE; } static struct notifier_block tnetv107x_spi_nb = { .notifier_call = tnetv107x_spi_notifier_call, }; and then in the device registration code, before registering the regulator: bus_register_notifier(&spi_bus_type, &tnetv107x_spi_nb); Let me know if you have any trouble. g. From lethal at linux-sh.org Tue Nov 16 03:36:05 2010 From: lethal at linux-sh.org (Paul Mundt) Date: Tue, 16 Nov 2010 18:36:05 +0900 Subject: [PATCH 1/4] input: gpio_keys: polling mode support In-Reply-To: <8891d088e9a16122c780f737b1b1ec35f9517c36.1289835508.git.bengardiner@nanometrics.ca> References: <8891d088e9a16122c780f737b1b1ec35f9517c36.1289835508.git.bengardiner@nanometrics.ca> Message-ID: <20101116093605.GD1330@linux-sh.org> On Mon, Nov 15, 2010 at 02:07:50PM -0500, Ben Gardiner wrote: > diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c > index 6069abe..477802a 100644 > --- a/drivers/input/keyboard/gpio_keys.c > +++ b/drivers/input/keyboard/gpio_keys.c > @@ -44,6 +48,26 @@ struct gpio_keys_drvdata { > struct gpio_button_data data[0]; > }; > > +#if !defined(CONFIG_INPUT_POLLDEV) && !defined(CONFIG_INPUT_POLLDEV_MODULE) > +inline struct input_polled_dev *input_allocate_polled_device(void) > +{ > + return NULL; > +} > + > +inline void input_free_polled_device(struct input_polled_dev *poll_dev) > +{ > +} > + > +inline int input_register_polled_device(struct input_polled_dev *dev) > +{ > + return -ENXIO; > +} > + > +inline void input_unregister_polled_device(struct input_polled_dev *poll_dev) > +{ > +} > +#endif > + Wouldn't these be better off in linux/input-polldev.h? > +#if !defined(CONFIG_INPUT_POLLDEV) && !defined(CONFIG_INPUT_POLLDEV_MODULE) > + if (pdata->poll_interval) { > + dev_err(dev, "device needs polling (enable INPUT_POLLDEV)\n"); > + return -EINVAL; > + } > +#endif > + This you could probably just shove in to.. > ddata = kzalloc(sizeof(struct gpio_keys_drvdata) + > pdata->nbuttons * sizeof(struct gpio_button_data), > GFP_KERNEL); > - input = input_allocate_device(); > + if (pdata->poll_interval) { > + poll_dev = input_allocate_polled_device(); > + input = poll_dev ? poll_dev->input : 0; > + } else > + input = input_allocate_device(); > if (!ddata || !input) { > dev_err(dev, "failed to allocate state\n"); > error = -ENOMEM; > goto fail1; this. Then just get rid of the ifdefs, as the input_allocate_polled_device() case will have already NULL'ed out. The rest is obviously fine with me! From broonie at opensource.wolfsonmicro.com Tue Nov 16 05:34:09 2010 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Tue, 16 Nov 2010 11:34:09 +0000 Subject: [PATCH v5 04/12] spi: add ti-ssp spi master driver In-Reply-To: References: <1289848334-8695-1-git-send-email-cyril@ti.com> <1289848334-8695-5-git-send-email-cyril@ti.com> <20101116072225.GF4074@angua.secretlab.ca> Message-ID: <20101116113409.GH3338@sirena.org.uk> On Tue, Nov 16, 2010 at 12:47:04AM -0700, Grant Likely wrote: > On Tue, Nov 16, 2010 at 12:22 AM, Grant Likely > > Instead, it is now incumbent on the board support code to ensure that > > any device that depends on another device (including i2c or spi > > regulators) will defer registration until the prerequisite devices are > > bound to drivers. You did also say you were going to write helpers to make this easier - I do fear that we're going to end up with far too much boiler plate code in machine drivers if we have to open code this. I guess device tree is going to need the helpers anyway :) > > I don't *think* this change will affect anything in this particular > > patch series, but if it does then let me know and I'll help you work out > > how to fix it using a bus notifier. > Oh, wait, spoke too soon. You do add a regulator in this series, so > this change will require a fixup. The solution is to register an > bus_notifier to the spi bus type before you start registering devices. > It also requires deferring the musb_hdrc.1 and tps6116x registrations > until the bus_notifier callback gets called with an indication that > the regulator is bound. It will look something like this: Did you come up with a way of handling situations like cpufreq where we have no device to wait for? From nsekhar at ti.com Tue Nov 16 09:18:33 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 16 Nov 2010 20:48:33 +0530 Subject: [PATCH v2] da850-evm: allow pca953x module build In-Reply-To: <1289849419-32343-1-git-send-email-bengardiner@nanometrics.ca> References: <4CE156B4.1020500@mvista.com> <1289849419-32343-1-git-send-email-bengardiner@nanometrics.ca> Message-ID: Hi Ben, On Tue, Nov 16, 2010 at 01:00:19, Ben Gardiner wrote: > Change the Kconfig files so that GPIO_PCF857X is default when > MACH_DAVINCI_DA850_EVM is set instead of always selecting so that users may > compile pca953x as a module. > > Note that since NAND/NOR setup is performed in the pcs953x setup() function of > board-da850-evm.c, when building pca953x as a module it will need to be > insmod'ed from early userspace (i.e. initrd) to use a NAND or NOR rootfs for > the system. > > Signed-off-by: Ben Gardiner > Cc: Sergei Shtylyov > Cc: David Brownell > > --- > > Changes since V1: > * make PCA953x default when MACH_DAVINCI_DA850_EVM in drivers/gpio/Kconfig > instead of selecting GPIO_PCF857X when MACH_DAVINCI_DA850_EVM in > arc/arm/mach-davinci/Kconfig I am not sure if such platform specific changes are encouraged in a generic file like drivers/gpio/Kconfig. I don't know a better way to achieve this either :( This change will need to get reviewed by a wider list. > * adding David Brownell because I think he is the pca953x goto guy scripts/get_maintainer.pl -f drivers/gpio/Kconfig gives: Andrew Morton Samuel Ortiz Mark Brown Rabin Vincent Linus Walleij linux-kernel at vger.kernel.org Can you please re-send this patch with these folks and lists in CC too? Thanks, Sekhar From vm.rod25 at gmail.com Tue Nov 16 09:58:08 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Tue, 16 Nov 2010 09:58:08 -0600 Subject: [PATCH v8 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: <4CE1560F.6080705@mvista.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> <1289601535-6746-5-git-send-email-vm.rod25@gmail.com> <4CE124AE.4080301@mvista.com> <4CE1560F.6080705@mvista.com> Message-ID: On Mon, Nov 15, 2010 at 9:47 AM, Sergei Shtylyov wrote: > Hello. > > Nori, Sekhar wrote: > >>>>> 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 >>>>> 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, > >>>> Looks like I missed pointing this out previously, but extending >>>> this list to take care of all boards will not be right since >>>> (for example) AXR13 and AXR14 pins could be used for different >>>> purpose on different boards. > >>> ? ?This is correct as the list in da850.c is a *generic* module's pin >>> list. >>> If the board needs less pins (and the pins it does not use for McASP are >>> used >>> differently), it should define its own pin list. > >>>> The right way would be to make this a per-board list. Since it >>>> is marked __initdata, that wouldn't lead to bloat. > >>> ? ?This patch is correct anyway. Unless DA850 EVM board can't use these >>> pins >>> for McASP -- but in this case the corresponding board file needs the >>> specific >>> pin list added. > >> Okay. I guess you are saying we will keep adding pins to the generic list >> as long as *all* supported boards don't get a conflict > > ? No, I don't care about conflicts as long as da850.c is concerned. > >> and if we run into >> a conflict we will spawn separate list for the affected board. > > ? Yes. > >> The only issue I see with this approach is it puts too much burden on the >> developer to verify that none of the supported boards break. > > ? That should be verified by the owners of that board. They shouldn't even > have used the generic pin lists in the first place. The conception behind > pin lists in da850.c was celarly misunderstood when DA850 EVM code was > developed. > >> Since it is highly unlikely that any board will need all the McASP pins, >> the generic list will likely remain unused. > > ? At least for now, Hawkboard will use all the defined pins -- I don't know > if there are any more undefined yet... > >> It might just be easier to >> start using board specific lists right away. This is especially true for >> McASP where usage of pins across boards will likely vary widely. > > ? Well, that depends on how complete the new generic McASP pin list is. If > it's still not, then board specific pin list looks preferable indeed... > >> Thanks, >> Sekhar > > WBR, Sergei > HI Sergei and Sekhar Thanks for check the patch What I can do if you agree with this change is to leave da850.c as it is, and declare static short hawk_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_13, DA850_AXR_14, -1 }; on the hawkboard file and call it insted of da850_mcasp_pins. ret = davinci_cfg_reg_list(hawk_mcasp_pins); if (ret) pr_warning("%s: mcasp mux setup failed: %d\n", __func__, ret); Please tell me if you agree with this change, I think is better because I do not touch any other file besides my board file. Thanks Regards Victor Rodriguez From sshtylyov at mvista.com Tue Nov 16 10:04:03 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Tue, 16 Nov 2010 19:04:03 +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> <4CE124AE.4080301@mvista.com> <4CE1560F.6080705@mvista.com> Message-ID: <4CE2AB73.3050109@mvista.com> Hello. Victor Rodriguez wrote: >>>>>> 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 >>>>>> 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, >>>>> Looks like I missed pointing this out previously, but extending >>>>> this list to take care of all boards will not be right since >>>>> (for example) AXR13 and AXR14 pins could be used for different >>>>> purpose on different boards. >>>> This is correct as the list in da850.c is a *generic* module's pin >>>> list. >>>> If the board needs less pins (and the pins it does not use for McASP are >>>> used >>>> differently), it should define its own pin list. >>>>> The right way would be to make this a per-board list. Since it >>>>> is marked __initdata, that wouldn't lead to bloat. >>>> This patch is correct anyway. Unless DA850 EVM board can't use these >>>> pins >>>> for McASP -- but in this case the corresponding board file needs the >>>> specific >>>> pin list added. >>> Okay. I guess you are saying we will keep adding pins to the generic list >>> as long as *all* supported boards don't get a conflict >> No, I don't care about conflicts as long as da850.c is concerned. >>> and if we run into >>> a conflict we will spawn separate list for the affected board. >> Yes. >>> The only issue I see with this approach is it puts too much burden on the >>> developer to verify that none of the supported boards break. >> That should be verified by the owners of that board. They shouldn't even >> have used the generic pin lists in the first place. The conception behind >> pin lists in da850.c was celarly misunderstood when DA850 EVM code was >> developed. >>> Since it is highly unlikely that any board will need all the McASP pins, >>> the generic list will likely remain unused. >> At least for now, Hawkboard will use all the defined pins -- I don't know >> if there are any more undefined yet... >>> It might just be easier to >>> start using board specific lists right away. This is especially true for >>> McASP where usage of pins across boards will likely vary widely. >> Well, that depends on how complete the new generic McASP pin list is. If >> it's still not, then board specific pin list looks preferable indeed... >>> Thanks, >>> Sekhar >> WBR, Sergei > HI Sergei and Sekhar > Thanks for check the patch > What I can do if you agree with this change is to leave da850.c as it > is, No, please don't. > and declare > static short hawk_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_13, DA850_AXR_14, > -1 > }; > on the hawkboard file and call it insted of da850_mcasp_pins. > ret = davinci_cfg_reg_list(hawk_mcasp_pins); > if (ret) > pr_warning("%s: mcasp mux setup failed: %d\n", __func__, ret); > Please tell me if you agree with this change, I think is better > because I do not touch any other file besides my board file. No, it's not really better. The generic list in da850.c should be more complete, regardless... Ideally, you should go thru the DA850 manual and put in that list all McASP pins that aren't already there. Then you can use your own pin list if that *complete* pin list can't be used on your board. > Thanks > Regards > Victor Rodriguez WBR, Sergei From nsekhar at ti.com Tue Nov 16 10:37:08 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 16 Nov 2010 22:07:08 +0530 Subject: [PATCH v8 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: <4CE2AB73.3050109@mvista.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> <1289601535-6746-5-git-send-email-vm.rod25@gmail.com> <4CE124AE.4080301@mvista.com> <4CE1560F.6080705@mvista.com> <4CE2AB73.3050109@mvista.com> Message-ID: On Tue, Nov 16, 2010 at 21:34:03, Sergei Shtylyov wrote: > > > HI Sergei and Sekhar > > > Thanks for check the patch > > > What I can do if you agree with this change is to leave da850.c as it > > is, > > No, please don't. > > > and declare > > > static short hawk_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_13, DA850_AXR_14, > > -1 > > }; > > > on the hawkboard file and call it insted of da850_mcasp_pins. > > > ret = davinci_cfg_reg_list(hawk_mcasp_pins); > > if (ret) > > pr_warning("%s: mcasp mux setup failed: %d\n", __func__, ret); > > > Please tell me if you agree with this change, I think is better > > because I do not touch any other file besides my board file. > > No, it's not really better. The generic list in da850.c should be more > complete, regardless... Ideally, you should go thru the DA850 manual and put in > that list all McASP pins that aren't already there. Then you can use your own > pin list if that *complete* pin list can't be used on your board. That will cause a bunch of pin conflicts on the EVM so it will need its own list too. Thanks, Sekhar From vm.rod25 at gmail.com Tue Nov 16 10:45:29 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Tue, 16 Nov 2010 10:45:29 -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> <4CE124AE.4080301@mvista.com> <4CE1560F.6080705@mvista.com> <4CE2AB73.3050109@mvista.com> Message-ID: On Tue, Nov 16, 2010 at 10:37 AM, Nori, Sekhar wrote: > On Tue, Nov 16, 2010 at 21:34:03, Sergei Shtylyov wrote: > >> >> > HI Sergei and Sekhar >> >> > Thanks for check the patch >> >> > What I can do if you agree with this change is to leave da850.c as it >> > is, >> >> ? ? No, please don't. >> >> > and declare >> >> > static short hawk_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_13, DA850_AXR_14, >> > ? ? -1 >> > }; >> >> > on the hawkboard file and call it insted of da850_mcasp_pins. >> >> > ? ? ret = davinci_cfg_reg_list(hawk_mcasp_pins); >> > ? ? if (ret) >> > ? ? ? ? ? ? pr_warning("%s: mcasp mux setup failed: %d\n", __func__, ret); >> >> > Please tell me if you agree with this change, I think is better >> > because I do not touch any other file besides my board file. >> >> ? ? No, it's not really better. The generic list in da850.c should be more >> complete, regardless... Ideally, you should go thru the DA850 manual and put in >> that list all McASP pins that aren't already there. Then you can use your own >> pin list if that *complete* pin list can't be used on your board. > > That will cause a bunch of pin conflicts on the EVM so it will need > its own list too. > > Thanks, > Sekhar > > So do you think that the peer-board list (as I am implementing ) would be a better idea ? Regards Victor Rodriguez From bengardiner at nanometrics.ca Tue Nov 16 10:45:08 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 16 Nov 2010 11:45:08 -0500 Subject: [PATCH v2] da850-evm: allow pca953x module build In-Reply-To: References: <4CE156B4.1020500@mvista.com> <1289849419-32343-1-git-send-email-bengardiner@nanometrics.ca> Message-ID: Hi Sekhar, On Tue, Nov 16, 2010 at 10:18 AM, Nori, Sekhar wrote: > [...] > I am not sure if such platform specific changes are encouraged > in a generic file like drivers/gpio/Kconfig. I don't know a better > way to achieve this either :( I agree: it is wrong to put platform specific changes into the generic areas... > This change will need to get reviewed by a wider list. Yes, I was afraid of that. :) Perhaps it can be kept local instead -- more below. >> ?* adding David Brownell because I think he is the pca953x goto guy > > scripts/get_maintainer.pl -f drivers/gpio/Kconfig > > gives: > > Andrew Morton > Samuel Ortiz > Mark Brown > Rabin Vincent > Linus Walleij > linux-kernel at vger.kernel.org > > Can you please re-send this patch with these folks and lists in CC too? I had an even longer list generated with the --rolestats output on the patch, the pca593x.c file, the kconfig etc. I got gunshy right about posting time due to what I've read recently about the resentment of long CC lists [1] Producing this version of the patch made me think "it would be nice if I could just say select y OR m" or something like that... It turns out that Stephen Rothwell had already thought about this [2]. In that thread Sam Ravnborg pointed out [3] that assignments of attributes to config entries can be done in separate files. So I can add config GPIO_PCA953X default y if MACH_DAVINCI_DA850_EVM to the arch/arm/mach-davinci/Kconfig file. Problem solved; no need to involve a whole bunch of people who have more important things to do than look at this little patch of mine. Mr. David Brownell, I'm sorry for the noise. I wish I had thought of this method sooner. Best Regards, Ben Gardiner [1] http://lwn.net/Articles/404105/ [2] http://article.gmane.org/gmane.linux.ports.ppc.embedded/36839 [3] http://article.gmane.org/gmane.linux.ports.ppc.embedded/37132 --- Nanometrics Inc. http://www.nanometrics.ca From bengardiner at nanometrics.ca Tue Nov 16 10:58:30 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 16 Nov 2010 11:58:30 -0500 Subject: [PATCH v3] da850-evm: allow pca953x module build In-Reply-To: References: Message-ID: <1289926710-29892-1-git-send-email-bengardiner@nanometrics.ca> Change the mach-davinci Kconfig file so that GPIO_PCA953X is default when MACH_DAVINCI_DA850_EVM is set instead of always selecting. This allows users to compile pca953x as a module. Signed-off-by: Ben Gardiner CC: Sergei Shtylyov CC: Nori, Sekhar --- Changes since V2: * keep all Kconfig changes local to arch/arm/mach-davinci by exploting the fact that attribute assigment to config entries can span multiple files. * removing David Brownell since we don't need the wider scope in the changes Changes since V1: * make PCA953x default when MACH_DAVINCI_DA850_EVM in drivers/gpio/Kconfig instead of selecting GPIO_PCA953X when MACH_DAVINCI_DA850_EVM in arc/arm/mach-davinci/Kconfig * adding David Brownell because I think he is the pca953x goto guy Note that since NAND/NOR setup is performed in the pca953x setup() function of board-da850-evm.c, when building pca953x as a module it will need to be insmod'ed from early userspace (i.e. initrd) to use a NAND or NOR rootfs for the system. The following commands and their output illustrate that the changes do allow the build of pca953x.c as a module. $cat arch/arm/configs/da8xx_omapl_defconfig|grep -i GPIO_PCA953X;\ make mrproper; make da8xx_omapl_defconfig; cat .config |grep -i GPIO_PCA953X;\ sed -ie 's/GPIO_PCA953X=y//g' .config; make oldconfig CLEAN scripts/basic CLEAN scripts/kconfig CLEAN include/config CLEAN .config HOSTCC scripts/basic/fixdep HOSTCC scripts/basic/docproc scripts/basic/docproc.c: In function ?docsect?: scripts/basic/docproc.c:336: warning: ignoring return value of ?asprintf?, declared with attribute warn_unused_result HOSTCC scripts/basic/hash HOSTCC scripts/kconfig/conf.o HOSTCC scripts/kconfig/kxgettext.o SHIPPED scripts/kconfig/zconf.tab.c SHIPPED scripts/kconfig/lex.zconf.c SHIPPED scripts/kconfig/zconf.hash.c HOSTCC scripts/kconfig/zconf.tab.o HOSTLD scripts/kconfig/conf # # configuration written to .config # CONFIG_GPIO_PCA953X=y # CONFIG_GPIO_PCA953X_IRQ is not set scripts/kconfig/conf --oldconfig arch/arm/Kconfig * * Restart config... * * * GPIO Support * GPIO Support (GPIOLIB) [Y/?] y Debug GPIO calls (DEBUG_GPIO) [N/y/?] n /sys/class/gpio/... (sysfs interface) (GPIO_SYSFS) [N/y/?] n * * Memory mapped GPIO expanders: * IT8761E GPIO support (GPIO_IT8761E) [N/m/y/?] n * * I2C GPIO expanders: * Maxim MAX7300 GPIO expander (GPIO_MAX7300) [N/m/y/?] n MAX7319, MAX7320-7327 I2C Port Expanders (GPIO_MAX732X) [N/m/y/?] n PCA953x, PCA955x, TCA64xx, and MAX7310 I/O ports (GPIO_PCA953X) [Y/n/m/?] (NEW) Whereas the same commands executed in a working copy w/o this patch applied will result in no prompt for PCA953X. --- arch/arm/mach-davinci/Kconfig | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index b77b860..254a43f 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -148,7 +148,6 @@ config MACH_DAVINCI_DA850_EVM bool "TI DA850/OMAP-L138/AM18x Reference Platform" default ARCH_DAVINCI_DA850 depends on ARCH_DAVINCI_DA850 - select GPIO_PCA953X help Say Y here to select the TI DA850/OMAP-L138/AM18x Evaluation Module. @@ -178,6 +177,9 @@ config DA850_UI_RMII endchoice +config GPIO_PCA953X + default y if MACH_DAVINCI_DA850_EVM + config MACH_TNETV107X bool "TI TNETV107X Reference Platform" default ARCH_DAVINCI_TNETV107X -- 1.7.0.4 From nsekhar at ti.com Tue Nov 16 11:04:24 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 16 Nov 2010 22:34:24 +0530 Subject: OMAPL138 cpu frequency with SDRAM In-Reply-To: References: Message-ID: Hi Marc-Andr?, On Tue, Nov 16, 2010 at 17:38:20, Marc-Andr? H?bert wrote: > Hi, > > I have 2 OMAPL138/AM1808 boards: one with mDDR and ther other with > SDRAM. I am looking at options to reduce power consumption. One the of > options we are looking into is the cpu frequency. Not having a lot of > success with the SDRAM board. Here is a little list of what I tried > (with the Arago tree). > > 1) I have tried enabling the cpu frequency scaling. It works with the > mDDR board but any change in frequency fails when using the SDRAM > board. I am guessing this is because the DDR is derived from PLL1 > while the SDRAM uses PLL0. Is there a way to make the cpu frequency > work or is this impossible with SDRAM? This is something that is better clarified on the OMAP-L1 forum on e2e.ti.com, but I don't see why SDRAM cannot be connected to the same external memory interface that mDDR connects to and thus be insulated from PLL0 changes too. > > 2) With the SDRAM board, I then tried downclocking the cpu directly in > the bootloader using the AISgen utility. Under the PLL0 tab, I > modified by DIV1 from 1 to 2 to operate the CPU at 150Mhz instead of > 300Mhz while preserving my SDRAM at 100Mhz. With this, the kernel > hangs at boot during the SATA initialization (I can reach the console > fine if I disable the AHCI). I looked at the SATA controller datasheet > but couldn't find a link between the SYSCLK1 and the SATA (since DIV1 > affects SYSCLK1, right?). Why not use the cpufreq driver in kernel? Some usage information is available here: http://processors.wiki.ti.com/index.php/OMAP-L1_Linux_Drivers_Usage#CPUFreq > > 3) Similar to #2, I changed the PLL0 settings post-divider (2 to 3) > and div3 (3 to 2). This gave me CPU 200 Mhz (with settings matching > what is found under da850_opp_200 in da850.c) and still keeping the > 100Mhz SDRAM. This configuration stalls during the kernel boot right > after the reset of unused clocks. Hmm, no idea on this one. Thanks, Sekhar From bengardiner at nanometrics.ca Tue Nov 16 12:28:37 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 16 Nov 2010 13:28:37 -0500 Subject: [PATCH 1/4] input: gpio_keys: polling mode support In-Reply-To: <20101116093605.GD1330@linux-sh.org> References: <8891d088e9a16122c780f737b1b1ec35f9517c36.1289835508.git.bengardiner@nanometrics.ca> <20101116093605.GD1330@linux-sh.org> Message-ID: On Tue, Nov 16, 2010 at 4:36 AM, Paul Mundt wrote: > On Mon, Nov 15, 2010 at 02:07:50PM -0500, Ben Gardiner wrote: >> diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c >> index 6069abe..477802a 100644 >> --- a/drivers/input/keyboard/gpio_keys.c >> +++ b/drivers/input/keyboard/gpio_keys.c >> @@ -44,6 +48,26 @@ struct gpio_keys_drvdata { >> ? ? ? struct gpio_button_data data[0]; >> ?}; >> >> +#if !defined(CONFIG_INPUT_POLLDEV) && !defined(CONFIG_INPUT_POLLDEV_MODULE) >> +inline struct input_polled_dev *input_allocate_polled_device(void) >> +{ >> + ? ? return NULL; >> +} >> + >> +inline void input_free_polled_device(struct input_polled_dev *poll_dev) >> +{ >> +} >> + >> +inline int input_register_polled_device(struct input_polled_dev *dev) >> +{ >> + ? ? return -ENXIO; >> +} >> + >> +inline void input_unregister_polled_device(struct input_polled_dev *poll_dev) >> +{ >> +} >> +#endif >> + > > Wouldn't these be better off in linux/input-polldev.h? I'm not sure. gpio-keys.c with these proposed changes appears to be the only user of input-polldev.c that needs to compile and execute when CONFIG_INPUT_POLLDEV is not defined. Moving these to input-polldev.h wouldn't have any impact on other in-tree targets but could there be effects on out-of-tree ones? How would you feel about a shim layer that does a pass-thru when CONFIG_INPUT_POLLDEV is defined and does some error checking otherwise? I'll show you what I mean in the next version of the patch. >> +#if !defined(CONFIG_INPUT_POLLDEV) && !defined(CONFIG_INPUT_POLLDEV_MODULE) >> + ? ? if (pdata->poll_interval) { >> + ? ? ? ? ? ? dev_err(dev, "device needs polling (enable INPUT_POLLDEV)\n"); >> + ? ? ? ? ? ? return -EINVAL; >> + ? ? } >> +#endif >> + > This you could probably just shove in to.. > >> ? ? ? ddata = kzalloc(sizeof(struct gpio_keys_drvdata) + >> ? ? ? ? ? ? ? ? ? ? ? pdata->nbuttons * sizeof(struct gpio_button_data), >> ? ? ? ? ? ? ? ? ? ? ? GFP_KERNEL); >> - ? ? input = input_allocate_device(); >> + ? ? if (pdata->poll_interval) { >> + ? ? ? ? ? ? poll_dev = input_allocate_polled_device(); >> + ? ? ? ? ? ? input = poll_dev ? poll_dev->input : 0; >> + ? ? } else >> + ? ? ? ? ? ? input = input_allocate_device(); >> ? ? ? if (!ddata || !input) { >> ? ? ? ? ? ? ? dev_err(dev, "failed to allocate state\n"); >> ? ? ? ? ? ? ? error = -ENOMEM; >> ? ? ? ? ? ? ? goto fail1; > > this. Then just get rid of the ifdefs, as the > input_allocate_polled_device() case will have already NULL'ed out. I like removing that #ifdef block -- as long as we keep the dev_err about enabling INPUT_POLLDEV. It is possible for INPUT_POLLDEV to be defined and an allocation error to occur; I think it is useful to the clients of gpio_keys -- particularly the board setup developers -- to differentiate between 'INPUT_POLLDEV needs to be defined' and 'failed to allocate state', at least by printing an error message as in the current state of this patch. > The rest is obviously fine with me! Alright! :) I will re-spin a version that tries to address your comments. Thank you for your input so far. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From bengardiner at nanometrics.ca Tue Nov 16 13:39:33 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 16 Nov 2010 14:39:33 -0500 Subject: [PATCH v2 0/4] 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 Alexander Clouter. 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. [1] http://article.gmane.org/gmane.linux.kernel/1052551 [2] http://article.gmane.org/gmane.linux.kernel.commits.head/260919 [3] http://www.mail-archive.com/devicetree-discuss at lists.ozlabs.org/msg01974.html [4] http://article.gmane.org/gmane.linux.kernel.cross-arch/7786 Alexander Clouter (1): input: gpio_keys: polling mode support Ben Gardiner (3): da850-evm: add UI Expander pushbuttons da850-evm: extract defines for SEL{A,B,C} pins in UI expander da850-evm: add baseboard UI expander buttons, switches and LEDs arch/arm/mach-davinci/Kconfig | 3 + arch/arm/mach-davinci/board-da850-evm.c | 312 +++++++++++++++++++++++++++++-- drivers/input/keyboard/gpio_keys.c | 120 ++++++++++-- include/linux/gpio_keys.h | 1 + 4 files changed, 406 insertions(+), 30 deletions(-) --- 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 Tue Nov 16 13:39:34 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 16 Nov 2010 14:39:34 -0500 Subject: [PATCH v2 1/4] input: gpio_keys: polling mode support In-Reply-To: References: Message-ID: <5778af1b3a740718b3c8d3c727b386e5b1872d16.1289935504.git.bengardiner@nanometrics.ca> From: Alexander Clouter This implements an optional polling mode for the gpio_keys driver, necessary for GPIOs that are not able to generate IRQs. gpio_keys_platform_data has been extended with poll_interval which specifies the polling interval in ms, this is passed onto input-polldev. This work is a rebase of the patch by Alex Clouter [1] which was based on the patch [2] originally conceived by Paul Mundt. Signed-off-by: Paul Mundt Signed-off-by: Alexander Clouter Signed-off-by: Ben Gardiner Reviewed-by: Chris Cordahi CC: Paul Mundt [1] http://article.gmane.org/gmane.linux.kernel.input/13919 [2] http://article.gmane.org/gmane.linux.kernel.input/5814 --- 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 Changes since [1]: * rebased to 0b1c3ef1072f2b97c86351d3736d2b2d00293a11 of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git * use _cansleep variant of gpio_get_value in the polling task to avoid WARN_ON when using I2C GPIO expanders * prevent unitialized access to 'input' in gpio_keys_close() Changes since [2]: * absolute dependency on INPUT_POLLDEV removed Tested with CONFIG_INPUT_POLLDEV={n,m,y} (gpio_keys as module for 'm'). --- drivers/input/keyboard/gpio_keys.c | 120 ++++++++++++++++++++++++++++++------ include/linux/gpio_keys.h | 1 + 2 files changed, 103 insertions(+), 18 deletions(-) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 6069abe..d2f23d9 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -1,7 +1,9 @@ /* - * Driver for keys on GPIO lines capable of generating interrupts. + * Driver for keys on GPIO lines, either IRQ-driven or polled. * * Copyright 2005 Phil Blundell + * Copyright 2008 Paul Mundt + * Copyright 2010 Alexander Clouter * * 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 @@ -25,6 +27,7 @@ #include #include #include +#include struct gpio_button_data { struct gpio_keys_button *button; @@ -37,6 +40,7 @@ struct gpio_button_data { struct gpio_keys_drvdata { struct input_dev *input; + struct input_polled_dev *poll_dev; struct mutex disable_lock; unsigned int n_buttons; int (*enable)(struct device *dev); @@ -44,6 +48,30 @@ struct gpio_keys_drvdata { struct gpio_button_data data[0]; }; +#if (!defined(CONFIG_INPUT_POLLDEV) && !defined(CONFIG_INPUT_POLLDEV_MODULE)) \ + || (defined(CONFIG_INPUT_POLLDEV_MODULE) \ + && !defined(CONFIG_KEYBOARD_GPIO_MODULE)) + +static inline struct input_polled_dev *allocate_polled_device( + const struct device *dev) +{ + dev_err(dev, "device needs polling (enable INPUT_POLLDEV)\n"); + return NULL; +} + +#define free_polled_device(x) do { } while (0) +#define register_polled_device(x) (-ENXIO) +#define unregister_polled_device(x) do { } while (0) + +#else + +#define allocate_polled_device(x) input_allocate_polled_device() +#define free_polled_device(x) input_free_polled_device(x) +#define register_polled_device(x) input_register_polled_device(x) +#define unregister_polled_device(x) input_unregister_polled_device(x) + +#endif + /* * SYSFS interface for enabling/disabling keys and switches: * @@ -322,7 +350,8 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata) struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; - int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low; + int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) + ^ button->active_low; input_event(input, type, button->code, !!state); input_sync(input); @@ -343,6 +372,16 @@ static void gpio_keys_timer(unsigned long _data) schedule_work(&data->work); } +static void gpio_handle_button_event(struct gpio_keys_button *button, + struct gpio_button_data *bdata) +{ + if (bdata->timer_debounce) + mod_timer(&bdata->timer, + jiffies + msecs_to_jiffies(bdata->timer_debounce)); + else + gpio_keys_report_event(bdata); +} + static irqreturn_t gpio_keys_isr(int irq, void *dev_id) { struct gpio_button_data *bdata = dev_id; @@ -350,15 +389,24 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id) BUG_ON(irq != gpio_to_irq(button->gpio)); - if (bdata->timer_debounce) - mod_timer(&bdata->timer, - jiffies + msecs_to_jiffies(bdata->timer_debounce)); - else - schedule_work(&bdata->work); + gpio_handle_button_event(button, bdata); return IRQ_HANDLED; } +static void gpio_keys_poll(struct input_polled_dev *dev) +{ + struct gpio_keys_drvdata *ddata = dev->private; + int i; + + for (i = 0; i < ddata->n_buttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + struct gpio_keys_button *button = bdata->button; + + gpio_handle_button_event(button, bdata); + } +} + static int __devinit gpio_keys_setup_key(struct platform_device *pdev, struct gpio_button_data *bdata, struct gpio_keys_button *button) @@ -446,20 +494,28 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) struct gpio_keys_drvdata *ddata; struct device *dev = &pdev->dev; struct input_dev *input; + struct input_polled_dev *poll_dev; int i, error; int wakeup = 0; ddata = kzalloc(sizeof(struct gpio_keys_drvdata) + pdata->nbuttons * sizeof(struct gpio_button_data), GFP_KERNEL); - input = input_allocate_device(); + if (pdata->poll_interval) { + poll_dev = allocate_polled_device(dev); + input = poll_dev ? poll_dev->input : 0; + } else + input = input_allocate_device(); if (!ddata || !input) { dev_err(dev, "failed to allocate state\n"); error = -ENOMEM; goto fail1; } - ddata->input = input; + if (pdata->poll_interval) + ddata->poll_dev = poll_dev; + else + ddata->input = input; ddata->n_buttons = pdata->nbuttons; ddata->enable = pdata->enable; ddata->disable = pdata->disable; @@ -468,6 +524,12 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); input_set_drvdata(input, ddata); + if (pdata->poll_interval) { + poll_dev->private = ddata; + poll_dev->poll = gpio_keys_poll; + poll_dev->poll_interval = pdata->poll_interval; + } + input->name = pdev->name; input->phys = "gpio-keys/input0"; input->dev.parent = &pdev->dev; @@ -491,14 +553,17 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) bdata->input = input; bdata->button = button; + input_set_capability(input, type, button->code); + + if (pdata->poll_interval) + continue; + error = gpio_keys_setup_key(pdev, bdata, button); if (error) goto fail2; if (button->wakeup) wakeup = 1; - - input_set_capability(input, type, button->code); } error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); @@ -508,7 +573,10 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) goto fail2; } - error = input_register_device(input); + if (pdata->poll_interval) + error = register_polled_device(poll_dev); + else + error = input_register_device(input); if (error) { dev_err(dev, "Unable to register input device, error: %d\n", error); @@ -528,7 +596,9 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); fail2: while (--i >= 0) { - free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]); + if (!pdata->poll_interval) + free_irq(gpio_to_irq(pdata->buttons[i].gpio), + &ddata->data[i]); if (ddata->data[i].timer_debounce) del_timer_sync(&ddata->data[i].timer); cancel_work_sync(&ddata->data[i].work); @@ -537,7 +607,10 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); fail1: - input_free_device(input); + if (pdata->poll_interval) + free_polled_device(poll_dev); + else + input_free_device(input); kfree(ddata); return error; @@ -547,7 +620,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) { struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); - struct input_dev *input = ddata->input; + struct input_dev *input; + struct input_polled_dev *poll_dev; int i; sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); @@ -555,15 +629,25 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); for (i = 0; i < pdata->nbuttons; i++) { - int irq = gpio_to_irq(pdata->buttons[i].gpio); - free_irq(irq, &ddata->data[i]); + if (!pdata->poll_interval) { + int irq = gpio_to_irq(pdata->buttons[i].gpio); + free_irq(irq, &ddata->data[i]); + } if (ddata->data[i].timer_debounce) del_timer_sync(&ddata->data[i].timer); cancel_work_sync(&ddata->data[i].work); gpio_free(pdata->buttons[i].gpio); } - input_unregister_device(input); + if (pdata->poll_interval) { + poll_dev = ddata->poll_dev; + unregister_polled_device(poll_dev); + free_polled_device(poll_dev); + } else { + input = ddata->input; + input_unregister_device(input); + input_free_device(input); + } return 0; } diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index ce73a30..5fdd495 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -19,6 +19,7 @@ struct gpio_keys_platform_data { unsigned int rep:1; /* enable input subsystem auto repeat */ int (*enable)(struct device *dev); void (*disable)(struct device *dev); + unsigned int poll_interval; /* polling interval in ms */ }; #endif -- 1.7.0.4 From bengardiner at nanometrics.ca Tue Nov 16 13:39:36 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 16 Nov 2010 14:39:36 -0500 Subject: [PATCH v2 3/4] da850-evm: extract defines for SEL{A, B, C} pins in UI expander In-Reply-To: References: Message-ID: The setup and teardown methods of the UI expander reference the SEL_{A,B,C} pins by 'magic number' in each function. This patch extracts common #defines for their offsets in the expander and uses them. Signed-off-by: Ben Gardiner Reviewed-by: Chris Cordahi --- No changes since v1 --- arch/arm/mach-davinci/board-da850-evm.c | 27 +++++++++++++++------------ 1 files changed, 15 insertions(+), 12 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index ff71ffd..dcf21e5 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -292,6 +292,9 @@ static const char const *ui_expander_names[] = { "pb7", "pb6", "pb5", "pb4", "pb3", "pb2", "pb1" }; +#define DA850_SEL_A_OFFSET 7 +#define DA850_SEL_B_OFFSET 6 +#define DA850_SEL_C_OFFSET 5 #define DA850_UI_PB8_OFFSET 8 #define DA850_N_UI_PB 8 @@ -336,23 +339,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_SEL_A_OFFSET; + sel_b = gpio + DA850_SEL_B_OFFSET; + sel_c = gpio + DA850_SEL_C_OFFSET; - ret = gpio_request(sel_a, "sel_a"); + ret = gpio_request(sel_a, ui_expander_names[DA850_SEL_A_OFFSET]); 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, ui_expander_names[DA850_SEL_B_OFFSET]); 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, ui_expander_names[DA850_SEL_C_OFFSET]); if (ret) { pr_warning("Cannot open UI expander pin %d\n", sel_c); goto exp_setup_selc_fail; @@ -396,13 +399,13 @@ static int da850_evm_ui_expander_teardown(struct i2c_client *client, platform_device_unregister(&user_ui_pb_gpio_key_device); /* deselect all functionalities */ - gpio_set_value(gpio + 5, 1); - gpio_set_value(gpio + 6, 1); - gpio_set_value(gpio + 7, 1); + gpio_set_value(gpio + DA850_SEL_C_OFFSET, 1); + gpio_set_value(gpio + DA850_SEL_B_OFFSET, 1); + gpio_set_value(gpio + DA850_SEL_A_OFFSET, 1); - gpio_free(gpio + 5); - gpio_free(gpio + 6); - gpio_free(gpio + 7); + gpio_free(gpio + DA850_SEL_C_OFFSET); + gpio_free(gpio + DA850_SEL_B_OFFSET); + gpio_free(gpio + DA850_SEL_A_OFFSET); return 0; } -- 1.7.0.4 From bengardiner at nanometrics.ca Tue Nov 16 13:39:35 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 16 Nov 2010 14:39:35 -0500 Subject: [PATCH v2 2/4] da850-evm: add UI Expander pushbuttons In-Reply-To: References: Message-ID: <95f48a32a0256ecdb7148aa08d16f64928a7e5d8.1289935504.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 --- 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/Kconfig | 3 + arch/arm/mach-davinci/board-da850-evm.c | 76 +++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index b77b860..5163a1c 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -178,6 +178,9 @@ config DA850_UI_RMII endchoice +config INPUT_POLLDEV + default MACH_DAVINCI_DA850_EVM + config MACH_TNETV107X bool "TI TNETV107X Reference Platform" default ARCH_DAVINCI_TNETV107X diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index c6e11c6..ff71ffd 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,63 @@ 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_PB_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_PB_POLL_MS 200 +/* No need to poll switches anywhere near as fast */ +#define DA850_SW_POLL_MS 700 + +static const char const *ui_expander_names[] = { + "dgnd", "dgnd", "dgnd", "dgnd", "nc", "sel_c", "sel_b", "sel_a", "pb8", + "pb7", "pb6", "pb5", "pb4", "pb3", "pb2", "pb1" +}; + +#define DA850_UI_PB8_OFFSET 8 +#define DA850_N_UI_PB 8 + +static struct gpio_keys_button user_ui_pbs_gpio_keys[DA850_N_UI_PB]; + +static struct gpio_keys_platform_data user_ui_pbs_gpio_key_platform_data = { + .buttons = user_ui_pbs_gpio_keys, + .nbuttons = ARRAY_SIZE(user_ui_pbs_gpio_keys), + .rep = 0, /* disable auto-repeat */ + .poll_interval = DA850_PB_POLL_MS, +}; + +static struct platform_device user_ui_pb_gpio_key_device = { + .name = "gpio-keys", + .id = 0, + .dev = { + .platform_data = &user_ui_pbs_gpio_key_platform_data + } +}; + +static void da850_ui_pushbuttons_init(unsigned gpio) +{ + int i; + struct gpio_keys_button *button; + + for (i = 0; i < DA850_N_UI_PB; i++) { + button = &user_ui_pbs_gpio_keys[i]; + + button->code = KEY_F8 - i; + button->type = EV_KEY; + button->active_low = 1; + button->wakeup = 0; + button->debounce_interval = DA850_PB_DEBOUNCE_MS; + button->desc = (char *) + ui_expander_names[DA850_UI_PB8_OFFSET + i]; + button->gpio = gpio + DA850_UI_PB8_OFFSET + i; + } +} + static int da850_evm_ui_expander_setup(struct i2c_client *client, unsigned gpio, unsigned ngpio, void *c) { @@ -304,6 +363,14 @@ 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_ui_pushbuttons_init(gpio); + ret = platform_device_register(&user_ui_pb_gpio_key_device); + if (ret) { + pr_warning("Could not register UI GPIO expander push-buttons" + " device\n"); + goto exp_setup_keys_fail; + } + ui_card_detected = 1; pr_info("DA850/OMAP-L138 EVM UI card detected\n"); @@ -313,6 +380,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 +393,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(&user_ui_pb_gpio_key_device); + /* deselect all functionalities */ gpio_set_value(gpio + 5, 1); gpio_set_value(gpio + 6, 1); @@ -340,6 +411,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 = ui_expander_names, }; static struct i2c_board_info __initdata da850_evm_i2c_devices[] = { @@ -349,6 +421,10 @@ static struct i2c_board_info __initdata da850_evm_i2c_devices[] = { { I2C_BOARD_INFO("tca6416", 0x20), .platform_data = &da850_evm_ui_expander_info, + /* + * TODO : populate at runtime using + * .irq = gpio_to_irq(GPIO_TO_PIN(2,7)), + */ }, }; -- 1.7.0.4 From bengardiner at nanometrics.ca Tue Nov 16 13:39:37 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 16 Nov 2010 14:39:37 -0500 Subject: [PATCH v2 4/4] da850-evm: add baseboard UI 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 --- 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 | 217 ++++++++++++++++++++++++++++++- 1 files changed, 213 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index dcf21e5..79f2c95 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -410,6 +410,208 @@ 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(ui_expander_names) +#define DA850_BB_EXPANDER_GPIO_BASE (DAVINCI_N_GPIO + DA850_UI_EXPANDER_N_GPIOS) + +static const char const *baseboard_expander_names[] = { + "deep_sleep_en", "sw_rst", "tp_23", "tp_22", "tp_21", "user_pb1", + "user_led2", "user_led1", "user_sw_1", "user_sw_2", "user_sw_3", + "user_sw_4", "user_sw_5", "user_sw_6", "user_sw_7", "user_sw_8" +}; + +#define DA850_DEEP_SLEEP_EN_OFFSET 0 +#define DA850_SW_RST_OFFSET 1 +#define DA850_PB1_OFFSET 5 +#define DA850_USER_LED2_OFFSET 6 +#define DA850_USER_SW_1_OFFSET 8 + +#define DA850_N_USER_SW 8 +#define DA850_N_USER_LED 2 + +static struct gpio_keys_button user_pb_gpio_key = { + .code = KEY_PROG1, + .type = EV_KEY, + .active_low = 1, + .wakeup = 0, + .debounce_interval = DA850_PB_DEBOUNCE_MS, + .gpio = -1, /* assigned at runtime */ + .desc = NULL, /* assigned at runtime */ +}; + +static struct gpio_keys_platform_data user_pb_gpio_key_platform_data = { + .buttons = &user_pb_gpio_key, + .nbuttons = 1, + .rep = 0, /* disable auto-repeat */ + .poll_interval = DA850_PB_POLL_MS, +}; + +static struct platform_device user_pb_gpio_key_device = { + .name = "gpio-keys", + .id = 1, + .dev = { + .platform_data = &user_pb_gpio_key_platform_data + } +}; + +static struct gpio_keys_button user_sw_gpio_keys[DA850_N_USER_SW]; + +static struct gpio_keys_platform_data user_sw_gpio_key_platform_data = { + .buttons = user_sw_gpio_keys, + .nbuttons = ARRAY_SIZE(user_sw_gpio_keys), + .rep = 0, /* disable auto-repeat */ + .poll_interval = DA850_SW_POLL_MS, +}; + +static struct platform_device user_sw_gpio_key_device = { + .name = "gpio-keys", + .id = 2, + .dev = { + .platform_data = &user_sw_gpio_key_platform_data + } +}; + +static void da850_user_switches_init(unsigned gpio) +{ + int i; + struct gpio_keys_button *button; + + for (i = 0; i < DA850_N_USER_SW; i++) { + button = &user_sw_gpio_keys[i]; + + button->code = SW_LID + i; + button->type = EV_SW; + button->active_low = 1; + button->wakeup = 0; + button->debounce_interval = DA850_PB_DEBOUNCE_MS; + button->desc = (char *) + baseboard_expander_names[DA850_USER_SW_1_OFFSET + i]; + + button->gpio = gpio + DA850_USER_SW_1_OFFSET + i; + } +} + +static struct gpio_led user_leds[DA850_N_USER_LED]; + +static struct gpio_led_platform_data user_led_gpio_data = { + .leds = user_leds, + .num_leds = ARRAY_SIZE(user_leds), +}; + +static struct platform_device user_leds_gpio_device = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &user_led_gpio_data + } +}; + +static void da850_user_leds_init(unsigned gpio) +{ + int i; + struct gpio_led *led; + + for (i = 0; i < DA850_N_USER_LED; i++) { + led = &user_leds[i]; + + led->active_low = 1; + led->gpio = gpio + DA850_USER_LED2_OFFSET + i; + led->name = + baseboard_expander_names[DA850_USER_LED2_OFFSET + i]; + } +} + +static int da850_evm_baseboard_expander_setup(struct i2c_client *client, + unsigned gpio, unsigned ngpio, + void *c) +{ + int ret; + int deep_sleep_en, sw_rst; + + deep_sleep_en = gpio + DA850_DEEP_SLEEP_EN_OFFSET; + sw_rst = gpio + DA850_SW_RST_OFFSET; + + /* Do not allow sysfs control of deep_sleep_en */ + ret = gpio_request(deep_sleep_en, + baseboard_expander_names[DA850_DEEP_SLEEP_EN_OFFSET]); + if (ret) { + pr_warning("Cannot open IO expander pin %d\n", deep_sleep_en); + goto io_exp_setup_deep_sleep_en_fail; + } + /* do not drive a value on deep_sleep_en */ + gpio_direction_input(deep_sleep_en); + + /* Do not allow sysfs control of sw_rst */ + ret = gpio_request(sw_rst, + baseboard_expander_names[DA850_SW_RST_OFFSET]); + if (ret) { + pr_warning("Cannot open IO expander pin %d\n", sw_rst); + goto io_exp_setup_sw_rst_fail; + } + /* do not drive a value on sw_rst */ + gpio_direction_input(sw_rst); + + /* Register user_pb1 as a gpio-keys button */ + user_pb_gpio_key.desc = (char *) + baseboard_expander_names[DA850_PB1_OFFSET]; + user_pb_gpio_key.gpio = gpio + DA850_PB1_OFFSET; + ret = platform_device_register(&user_pb_gpio_key_device); + if (ret) { + pr_warning("Cannot register user pb: %d\n", ret); + goto io_exp_setup_pb_fail; + } + + /* + * Register the switches as a separate gpio-keys device so the fast + * polling interval for the pushbuttons is not wasted on switches + */ + da850_user_switches_init(gpio); + ret = platform_device_register(&user_sw_gpio_key_device); + if (ret) { + pr_warning("Could not register baseboard GPIO expander switches" + " device\n"); + goto io_exp_setup_sw_fail; + } + + da850_user_leds_init(gpio); + ret = platform_device_register(&user_leds_gpio_device); + if (ret) { + pr_warning("Could not register baseboard GPIO expander LEDS " + "device\n"); + goto io_exp_setup_leds_fail; + } + + return 0; + +io_exp_setup_leds_fail: + platform_device_unregister(&user_sw_gpio_key_device); +io_exp_setup_sw_fail: + platform_device_unregister(&user_pb_gpio_key_device); +io_exp_setup_pb_fail: + gpio_free(sw_rst); +io_exp_setup_sw_rst_fail: + gpio_free(deep_sleep_en); +io_exp_setup_deep_sleep_en_fail: + return ret; +} + +static int da850_evm_baseboard_expander_teardown(struct i2c_client *client, + unsigned gpio, unsigned ngpio, void *c) +{ + int deep_sleep_en, sw_rst; + + deep_sleep_en = gpio + DA850_DEEP_SLEEP_EN_OFFSET; + sw_rst = gpio + DA850_SW_RST_OFFSET; + + platform_device_unregister(&user_leds_gpio_device); + platform_device_unregister(&user_sw_gpio_key_device); + platform_device_unregister(&user_pb_gpio_key_device); + gpio_free(sw_rst); + gpio_free(deep_sleep_en); + + return 0; +} + static struct pca953x_platform_data da850_evm_ui_expander_info = { .gpio_base = DAVINCI_N_GPIO, .setup = da850_evm_ui_expander_setup, @@ -417,6 +619,13 @@ static struct pca953x_platform_data da850_evm_ui_expander_info = { .names = ui_expander_names, }; +static struct pca953x_platform_data da850_evm_baseboard_expander_info = { + .gpio_base = DA850_BB_EXPANDER_GPIO_BASE, + .setup = da850_evm_baseboard_expander_setup, + .teardown = da850_evm_baseboard_expander_teardown, + .names = baseboard_expander_names, +}; + static struct i2c_board_info __initdata da850_evm_i2c_devices[] = { { I2C_BOARD_INFO("tlv320aic3x", 0x18), @@ -424,10 +633,10 @@ static struct i2c_board_info __initdata da850_evm_i2c_devices[] = { { I2C_BOARD_INFO("tca6416", 0x20), .platform_data = &da850_evm_ui_expander_info, - /* - * TODO : populate at runtime using - * .irq = gpio_to_irq(GPIO_TO_PIN(2,7)), - */ + }, + { + I2C_BOARD_INFO("tca6416", 0x21), + .platform_data = &da850_evm_baseboard_expander_info, }, }; -- 1.7.0.4 From michael.williamson at criticallink.com Tue Nov 16 14:42:53 2010 From: michael.williamson at criticallink.com (Michael Williamson) Date: Tue, 16 Nov 2010 15:42:53 -0500 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> <4CE124AE.4080301@mvista.com> <4CE1560F.6080705@mvista.com> <4CE2AB73.3050109@mvista.com> Message-ID: <4CE2ECCD.8090603@criticallink.com> On 11/16/2010 11:37 AM, Nori, Sekhar wrote: > On Tue, Nov 16, 2010 at 21:34:03, Sergei Shtylyov wrote: > >> >>> HI Sergei and Sekhar >> >>> Thanks for check the patch >> >>> What I can do if you agree with this change is to leave da850.c as it >>> is, >> >> No, please don't. >> >>> and declare >> >>> static short hawk_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_13, DA850_AXR_14, >>> -1 >>> }; >> >>> on the hawkboard file and call it insted of da850_mcasp_pins. >> >>> ret = davinci_cfg_reg_list(hawk_mcasp_pins); >>> if (ret) >>> pr_warning("%s: mcasp mux setup failed: %d\n", __func__, ret); >> >>> Please tell me if you agree with this change, I think is better >>> because I do not touch any other file besides my board file. >> >> No, it's not really better. The generic list in da850.c should be more >> complete, regardless... Ideally, you should go thru the DA850 manual and put in >> that list all McASP pins that aren't already there. Then you can use your own >> pin list if that *complete* pin list can't be used on your board. > > That will cause a bunch of pin conflicts on the EVM so it will need > its own list too. > Help me out. Why do we need generic pin lists? It seems to me that the "generic pin list" for da850.c isn't practical for most (if not all) of the peripherals. They should be done using __initdata in each board file. Just a cursory glance at what's in da850.c highlights several items being set up for the EVM and not generically. For example: - da850_uart1_pins and da850_uart2_pins: I believe both have RTS/CTS pins which for a generic definition should be included as for UART0, but would then be unused as the EVM doesn't use these pins in this function. - da850_mcasp_pins: if generic, must include all 16 AXR pins. I think you'd be hard pressed to find a board configuration that would use all 16 AXR pins for the McASP. I'm fairly sure the EVM uses the pins called out, and uses other pins for other functions. So it's likely this structure wouldn't get used. - da850_mmcsd0_pins : includes 2 GPIO pins (specific to the EVM, though possible for other boards) for the card detect and write protect signals. These pins are completely arbitrary for that particular board design. I also believe that the complete mmcsd0 port has 4 more data lines as part of it's peripheral, although the driver doesn't support using them. - da850_emif25_pins interface doesn't include the generic pins for some of the SDRAM functions. - da850_cpgmac_pins defines both RMII and MII pins. I don't think any board would want to configure both sets at the same time. Seems like this should never get used... It's also incomplete. What about the uPP pin list? Or the VPIF? Etc. I think a board file author should be familiar enough with the SoC to understand what peripheral pins he should be configuring for his/her particular hardware setup and explicitly specify them in the board file. If you remove the common pin-mux lists and move them to a board file, then once you configure your specific platform, is there any more memory used than with the common scheme? Of course, there would be replication of pin-mux code in the board files that had identical configurations. Is that the driver for the current implementation? Is there a thread that hashes through the decisions on this that I should be googling? Thanks for any insight. -Mike From cyril at ti.com Tue Nov 16 15:19:20 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 16 Nov 2010 16:19:20 -0500 Subject: [PATCH v5 01/12] misc: add driver for sequencer serial port In-Reply-To: References: <1289848334-8695-1-git-send-email-cyril@ti.com> <1289848334-8695-2-git-send-email-cyril@ti.com> <20101116071047.GE4074@angua.secretlab.ca> <4CE2AE3C.4040805@ti.com> Message-ID: <4CE2F558.9030602@ti.com> On 11/16/2010 03:35 PM, Grant Likely wrote: > On Tue, Nov 16, 2010 at 9:15 AM, Cyril Chemparathy wrote: >> On 11/16/2010 02:10 AM, Grant Likely wrote: >>> On Mon, Nov 15, 2010 at 02:12:03PM -0500, Cyril Chemparathy wrote: >>>> 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. >>>> >> [...] >>>> +static inline void ssp_write(struct ti_ssp *ssp, int reg, u32 val) >>>> +{ >>>> + __raw_writel(val, ssp->regs + reg); >>>> +} >>> >>> I'm pretty sure it was resolved that __raw_ versions should not be >>> used here. >> >> The endian-swap done by writel/readl are incorrect since these devices >> are meant to be accessed native-endian at all times. >> >> See [1] below for Russell King's earlier response on this. In this >> case, I don't think memory-device ordering matters, and therefore the >> __raw_ variants should be ok. Should I just insert barriers into the >> read/write wrappers here? > > I'll let Russel make the decision here; but I must admit I'm puzzled. > Are you running an ARMEB machine? the le32_to_cpu macros should be > no-ops on little endian. If you do still use the __raw variants, then > at the very least the reason for doing so must be well documented. > > Personally, I'd rather see the appropriate macros added to io.h > ioread32be()/iowrite32be() perhaps? Or am I missing something subtle > about the hardware behaviour? > As with most of the davinci series devices, the tnetv107x SOC can "theoretically" run both big and little endian. Since the CPU and SOC endian-ness are tied, on-chip peripherals are to be accessed native-endian (i.e. without swap) at all times. However, many of these parts do not "officially" support big-endian, and this is the case with tnetv107x as well. Even so, it would be best not to break this, just in case these h/w blocks get reused on some future big-endian capable derivative. Further, I found this to be common practice across many davinci drivers: $ find . -name '*davinci*' | xargs grep __raw_ | cut -d: -f1 | uniq ./drivers/input/keyboard/davinci_keyscan.c ./drivers/mtd/nand/davinci_nand.c ./drivers/mfd/davinci_voicecodec.c ./drivers/i2c/busses/i2c-davinci.c ./drivers/usb/musb/davinci.c ./drivers/net/davinci_mdio.c ./drivers/net/davinci_cpdma.c ./sound/soc/davinci/davinci-mcasp.c ./sound/soc/davinci/davinci-i2s.c Regards Cyril. From vm.rod25 at gmail.com Tue Nov 16 15:21:17 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Tue, 16 Nov 2010 15:21:17 -0600 Subject: [PATCH v8 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: <4CE2ECCD.8090603@criticallink.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> <1289601535-6746-5-git-send-email-vm.rod25@gmail.com> <4CE124AE.4080301@mvista.com> <4CE1560F.6080705@mvista.com> <4CE2AB73.3050109@mvista.com> <4CE2ECCD.8090603@criticallink.com> Message-ID: On Tue, Nov 16, 2010 at 2:42 PM, Michael Williamson wrote: > On 11/16/2010 11:37 AM, Nori, Sekhar wrote: > >> On Tue, Nov 16, 2010 at 21:34:03, Sergei Shtylyov wrote: >> >>> >>>> HI Sergei and Sekhar >>> >>>> Thanks for check the patch >>> >>>> What I can do if you agree with this change is to leave da850.c as it >>>> is, >>> >>> ? ? No, please don't. >>> >>>> and declare >>> >>>> static short hawk_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_13, DA850_AXR_14, >>>> ? ? -1 >>>> }; >>> >>>> on the hawkboard file and call it insted of da850_mcasp_pins. >>> >>>> ? ? ret = davinci_cfg_reg_list(hawk_mcasp_pins); >>>> ? ? if (ret) >>>> ? ? ? ? ? ? pr_warning("%s: mcasp mux setup failed: %d\n", __func__, ret); >>> >>>> Please tell me if you agree with this change, I think is better >>>> because I do not touch any other file besides my board file. >>> >>> ? ? No, it's not really better. The generic list in da850.c should be more >>> complete, regardless... Ideally, you should go thru the DA850 manual and put in >>> that list all McASP pins that aren't already there. Then you can use your own >>> pin list if that *complete* pin list can't be used on your board. >> >> That will cause a bunch of pin conflicts on the EVM so it will need >> its own list too. >> > > > Help me out. ?Why do we need generic pin lists? > > It seems to me that the "generic pin list" for da850.c isn't practical for most > (if not all) of the peripherals. ?They should be done using __initdata in > each board file. > > Just a cursory glance at what's in da850.c highlights several items being set > up for the EVM and not generically. ?For example: > > - da850_uart1_pins and da850_uart2_pins: I believe both have RTS/CTS pins which > ?for a generic definition should be included as for UART0, but would then > ?be unused as the EVM doesn't use these pins in this function. > > - da850_mcasp_pins: if generic, must include all 16 AXR pins. ?I think you'd > ?be hard pressed to find a board configuration that would use all 16 AXR pins > ?for the McASP. ?I'm fairly sure the EVM uses the pins called out, and uses > ?other pins for other functions. ?So it's likely this structure wouldn't get used. > > - da850_mmcsd0_pins : includes 2 GPIO pins (specific to the EVM, though possible for > ?other boards) for the card detect and write protect signals. ?These pins are > ?completely arbitrary for that particular board design. I also believe that > ?the complete mmcsd0 port has 4 more data lines as part of it's peripheral, although > ?the driver doesn't support using them. > > - da850_emif25_pins interface doesn't include the generic pins for some of > ?the SDRAM functions. > > - da850_cpgmac_pins defines both RMII and MII pins. ?I don't think any board > ?would want to configure both sets at the same time. ?Seems like this should > ?never get used... > > It's also incomplete. ?What about the uPP pin list? ?Or the VPIF? ?Etc. > > I think a board file author should be familiar enough with the SoC to understand > what peripheral pins he should be configuring for his/her particular hardware setup > and explicitly specify them in the board file. > > If you remove the common pin-mux lists and move them to a board file, then once you > configure your specific platform, is there any more memory used than with > the common scheme? ?Of course, there would be replication of pin-mux code in the board > files that had identical configurations. ?Is that the driver for the current implementation? > > Is there a thread that hashes through the decisions on this that I should be googling? > > Thanks for any insight. > > -Mike > > > > > > > Hi Mike thanks for the comments I agree with the part of replication So do you recommend that the best way to solve this problem is to make a pin list that has all the 16 pins on da850.c ? I have checked and on the file arch/arm/mach-davinci/include/mach/mux.h are declared /* McASP function */ DA850_ACLKR, DA850_ACLKX, DA850_AFSR, DA850_AFSX, DA850_AHCLKR, DA850_AHCLKX, DA850_AMUTE, DA850_AXR_15, DA850_AXR_14, DA850_AXR_13, DA850_AXR_12, DA850_AXR_11, DA850_AXR_10, DA850_AXR_9, DA850_AXR_8, DA850_AXR_7, DA850_AXR_6, DA850_AXR_5, DA850_AXR_4, DA850_AXR_3, DA850_AXR_2, DA850_AXR_1, DA850_AXR_0, I have changed the pin list to 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_13, DA850_AXR_14, DA850_AXR_15, -1 }; and after checked with the hawk board it works fine I have put on the list all the available pins for McASP even if hawkboard does not need the last one DA850_AXR_15 , and is still working Please tell me which way do you recommend me Regards Victor Rodriguez From vm.rod25 at gmail.com Tue Nov 16 15:53:48 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Tue, 16 Nov 2010 15:53:48 -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> <4CE124AE.4080301@mvista.com> <4CE1560F.6080705@mvista.com> <4CE2AB73.3050109@mvista.com> <4CE2ECCD.8090603@criticallink.com> Message-ID: On Tue, Nov 16, 2010 at 3:21 PM, Victor Rodriguez wrote: > On Tue, Nov 16, 2010 at 2:42 PM, Michael Williamson > wrote: >> On 11/16/2010 11:37 AM, Nori, Sekhar wrote: >> >>> On Tue, Nov 16, 2010 at 21:34:03, Sergei Shtylyov wrote: >>> >>>> >>>>> HI Sergei and Sekhar >>>> >>>>> Thanks for check the patch >>>> >>>>> What I can do if you agree with this change is to leave da850.c as it >>>>> is, >>>> >>>> ? ? No, please don't. >>>> >>>>> and declare >>>> >>>>> static short hawk_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_13, DA850_AXR_14, >>>>> ? ? -1 >>>>> }; >>>> >>>>> on the hawkboard file and call it insted of da850_mcasp_pins. >>>> >>>>> ? ? ret = davinci_cfg_reg_list(hawk_mcasp_pins); >>>>> ? ? if (ret) >>>>> ? ? ? ? ? ? pr_warning("%s: mcasp mux setup failed: %d\n", __func__, ret); >>>> >>>>> Please tell me if you agree with this change, I think is better >>>>> because I do not touch any other file besides my board file. >>>> >>>> ? ? No, it's not really better. The generic list in da850.c should be more >>>> complete, regardless... Ideally, you should go thru the DA850 manual and put in >>>> that list all McASP pins that aren't already there. Then you can use your own >>>> pin list if that *complete* pin list can't be used on your board. >>> >>> That will cause a bunch of pin conflicts on the EVM so it will need >>> its own list too. >>> >> >> >> Help me out. ?Why do we need generic pin lists? >> >> It seems to me that the "generic pin list" for da850.c isn't practical for most >> (if not all) of the peripherals. ?They should be done using __initdata in >> each board file. >> >> Just a cursory glance at what's in da850.c highlights several items being set >> up for the EVM and not generically. ?For example: >> >> - da850_uart1_pins and da850_uart2_pins: I believe both have RTS/CTS pins which >> ?for a generic definition should be included as for UART0, but would then >> ?be unused as the EVM doesn't use these pins in this function. >> >> - da850_mcasp_pins: if generic, must include all 16 AXR pins. ?I think you'd >> ?be hard pressed to find a board configuration that would use all 16 AXR pins >> ?for the McASP. ?I'm fairly sure the EVM uses the pins called out, and uses >> ?other pins for other functions. ?So it's likely this structure wouldn't get used. >> >> - da850_mmcsd0_pins : includes 2 GPIO pins (specific to the EVM, though possible for >> ?other boards) for the card detect and write protect signals. ?These pins are >> ?completely arbitrary for that particular board design. I also believe that >> ?the complete mmcsd0 port has 4 more data lines as part of it's peripheral, although >> ?the driver doesn't support using them. >> >> - da850_emif25_pins interface doesn't include the generic pins for some of >> ?the SDRAM functions. >> >> - da850_cpgmac_pins defines both RMII and MII pins. ?I don't think any board >> ?would want to configure both sets at the same time. ?Seems like this should >> ?never get used... >> >> It's also incomplete. ?What about the uPP pin list? ?Or the VPIF? ?Etc. >> >> I think a board file author should be familiar enough with the SoC to understand >> what peripheral pins he should be configuring for his/her particular hardware setup >> and explicitly specify them in the board file. >> >> If you remove the common pin-mux lists and move them to a board file, then once you >> configure your specific platform, is there any more memory used than with >> the common scheme? ?Of course, there would be replication of pin-mux code in the board >> files that had identical configurations. ?Is that the driver for the current implementation? >> >> Is there a thread that hashes through the decisions on this that I should be googling? >> >> Thanks for any insight. >> >> -Mike >> >> >> >> >> >> >> > > > Hi Mike thanks for the comments I agree with the part of replication > > So do you recommend that the best way to solve this problem is to make > a ?pin list that has all the 16 pins on da850.c ? > > I have checked and on the file > > arch/arm/mach-davinci/include/mach/mux.h > > are declared > > ? ? ? ?/* McASP function */ > ? ? ? ?DA850_ACLKR, > ? ? ? ?DA850_ACLKX, > ? ? ? ?DA850_AFSR, > ? ? ? ?DA850_AFSX, > ? ? ? ?DA850_AHCLKR, > ? ? ? ?DA850_AHCLKX, > ? ? ? ?DA850_AMUTE, > ? ? ? ?DA850_AXR_15, > ? ? ? ?DA850_AXR_14, > ? ? ? ?DA850_AXR_13, > ? ? ? ?DA850_AXR_12, > ? ? ? ?DA850_AXR_11, > ? ? ? ?DA850_AXR_10, > ? ? ? ?DA850_AXR_9, > ? ? ? ?DA850_AXR_8, > ? ? ? ?DA850_AXR_7, > ? ? ? ?DA850_AXR_6, > ? ? ? ?DA850_AXR_5, > ? ? ? ?DA850_AXR_4, > ? ? ? ?DA850_AXR_3, > ? ? ? ?DA850_AXR_2, > ? ? ? ?DA850_AXR_1, > ? ? ? ?DA850_AXR_0, > > I have changed the pin list to > > > 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_13, DA850_AXR_14, DA850_AXR_15, > ? ? ? ?-1 > }; > > and after checked with the hawk board it works fine > > I have put on the list all the available pins for McASP even if > hawkboard does not need the last one DA850_AXR_15 , and is still > working > > Please tell me which way do you recommend me > > Regards > > Victor Rodriguez > From linux at arm.linux.org.uk Tue Nov 16 16:23:41 2010 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Tue, 16 Nov 2010 22:23:41 +0000 Subject: [PATCH v5 01/12] misc: add driver for sequencer serial port In-Reply-To: References: <1289848334-8695-1-git-send-email-cyril@ti.com> <1289848334-8695-2-git-send-email-cyril@ti.com> <20101116071047.GE4074@angua.secretlab.ca> <4CE2AE3C.4040805@ti.com> Message-ID: <20101116222340.GF21926@n2100.arm.linux.org.uk> On Tue, Nov 16, 2010 at 01:35:40PM -0700, Grant Likely wrote: > I'll let Russel make the decision here; but I must admit I'm puzzled. ^ Grr. I'm ambivalent over it, provided that the author realises that the __raw variants have no ordering guarantees other than their accesses being ordered only with respect to themselves and nothing else. If we do want to go down the route of creating some official native endian accessors, then I'm fine with that too. From broonie at opensource.wolfsonmicro.com Tue Nov 16 16:48:47 2010 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Tue, 16 Nov 2010 22:48:47 +0000 Subject: [PATCH v5 04/12] spi: add ti-ssp spi master driver In-Reply-To: <20101116204554.GB5016@angua.secretlab.ca> References: <1289848334-8695-1-git-send-email-cyril@ti.com> <1289848334-8695-5-git-send-email-cyril@ti.com> <20101116072225.GF4074@angua.secretlab.ca> <20101116113409.GH3338@sirena.org.uk> <20101116204554.GB5016@angua.secretlab.ca> Message-ID: <20101116224846.GA24623@opensource.wolfsonmicro.com> On Tue, Nov 16, 2010 at 01:45:54PM -0700, Grant Likely wrote: > On Tue, Nov 16, 2010 at 11:34:09AM +0000, Mark Brown wrote: > > You did also say you were going to write helpers to make this easier - I > > do fear that we're going to end up with far too much boiler plate code > > in machine drivers if we have to open code this. I guess device tree is > > going to need the helpers anyway :) > Yup, and I will, but as can be seen the boilerplate required is > actually pretty minimal, and I'd like to have a couple of platforms to > work with before actually settling on what the helpers need to look > like. Yeah, but I do have a bit of nervousness about the scalability of open coding and the possibility that we'll come up with a better approach so I'd be a bit more comfortable if we didn't have code in random board files as they're always the hardest thing to update. From grant.likely at secretlab.ca Tue Nov 16 17:57:59 2010 From: grant.likely at secretlab.ca (Grant Likely) Date: Tue, 16 Nov 2010 16:57:59 -0700 Subject: [PATCH v5 01/12] misc: add driver for sequencer serial port In-Reply-To: <20101116222340.GF21926@n2100.arm.linux.org.uk> References: <1289848334-8695-1-git-send-email-cyril@ti.com> <1289848334-8695-2-git-send-email-cyril@ti.com> <20101116071047.GE4074@angua.secretlab.ca> <4CE2AE3C.4040805@ti.com> <20101116222340.GF21926@n2100.arm.linux.org.uk> Message-ID: On Tue, Nov 16, 2010 at 3:23 PM, Russell King - ARM Linux wrote: > On Tue, Nov 16, 2010 at 01:35:40PM -0700, Grant Likely wrote: >> I'll let Russel make the decision here; but I must admit I'm puzzled. > ? ? ? ? ? ? ? ? ^ > Grr. Sorry; typo. I *do* know how to spell your name. :-) I owe you a pint and we'll call it even. g. From cyril at ti.com Tue Nov 16 18:17:18 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Tue, 16 Nov 2010 19:17:18 -0500 Subject: [PATCH v5 04/12] spi: add ti-ssp spi master driver In-Reply-To: References: <1289848334-8695-1-git-send-email-cyril@ti.com> <1289848334-8695-5-git-send-email-cyril@ti.com> <20101116072225.GF4074@angua.secretlab.ca> Message-ID: <4CE31F0E.7050103@ti.com> On 11/16/2010 02:47 AM, Grant Likely wrote: > On Tue, Nov 16, 2010 at 12:22 AM, Grant Likely > wrote: >> On Mon, Nov 15, 2010 at 02:12:06PM -0500, Cyril Chemparathy wrote: >>> This patch adds an SPI master implementation that operates on top of an >>> underlying TI-SSP port. >>> >>> Signed-off-by: Cyril Chemparathy >> [...] >>> +static int __init ti_ssp_spi_init(void) >>> +{ >>> + return platform_driver_register(&ti_ssp_spi_driver); >>> +} >>> +subsys_initcall(ti_ssp_spi_init); >> >> After discussions about device init dependencies at plumbers, and >> since this is the first SPI device driver I've reviewed since that >> dicussion, this driver gets to be the first to implement the proposed >> policy. :-) >> >> Change this to module_initcall(). Many spi and i2c drivers use >> module or subsys_initcall to try and influence driver probe order so >> that certain regulator chips get registered before the devices that >> try to use them. This approach is insane. >> >> Instead, it is now incumbent on the board support code to ensure that >> any device that depends on another device (including i2c or spi >> regulators) will defer registration until the prerequisite devices are >> bound to drivers. >> >> I don't *think* this change will affect anything in this particular >> patch series, but if it does then let me know and I'll help you work out >> how to fix it using a bus notifier. > > Oh, wait, spoke too soon. You do add a regulator in this series, so > this change will require a fixup. The solution is to register an > bus_notifier to the spi bus type before you start registering devices. > It also requires deferring the musb_hdrc.1 and tps6116x registrations > until the bus_notifier callback gets called with an indication that > the regulator is bound. It will look something like this: > > static int tnetv107x_spi_notifier_call(struct notifier_block *nb, > unsigned long event, void *__dev) > { > struct device *dev = __dev; > > if ((event == BUS_NOTIFY_BOUND_DRIVER) && (dev == (the-regulator))) { > register-the-remaining-devices(); > return NOTIFY_STOP; > }; > return NOTIFY_DONE; > } > > static struct notifier_block tnetv107x_spi_nb = { > .notifier_call = tnetv107x_spi_notifier_call, > }; > > and then in the device registration code, before registering the > regulator: > > bus_register_notifier(&spi_bus_type, &tnetv107x_spi_nb); > > Let me know if you have any trouble. The ability to wait on multiple devices may come handy. In this particular case the backlight driver depends on both ssp-gpio and regulator devices. Ideally, boards should be able to do something like... platform_device_register_after(&backlight_device, "spi1.0"); platform_device_register_after(&backlight_device, "ti-ssp-gpio.0"); ... and the device should get automatically registered once its dependencies have been met. Crude and incomplete code follows: struct platform_device_depend { struct list_head node; const char *dev_name; struct platform_device *pdev; }; LIST_HEAD(platform_device_depend); static int platform_device_register_after(struct platform_device *pdev, const char *dev_name) { struct platform_device_depend *dep; dep = kzalloc(sizeof(*dep), GFP_KERNEL); if (!dep) return -ENOMEM; dep->dev_name = dev_name; dep->pdev = pdev; list_add_tail(&dep->node, &platform_device_depend); return 0; } static int platform_device_try_register(struct platform_device *pdev) { struct platform_device_depend *dep; list_for_each_entry(dep, &platform_device_depend, node) { if (dep->pdev == pdev) return -EBUSY; } return platform_device_register(pdev); } static int bus_notifier_func(struct notifier_block *nb, unsigned long event, void *__dev) { struct platform_device_depend *dep, *tmp; struct device *dev = __dev; if (event != BUS_NOTIFY_BOUND_DRIVER) return NOTIFY_DONE; list_for_each_entry_safe(dep, tmp, &platform_device_depend, node) { if (strcmp(dep->dev_name, dev_name(dev)) != 0) continue; list_del(&dep->node); platform_device_try_register(dep->pdev); kfree(dep); } return NOTIFY_OK; } Regards Cyril. From anavatta at imnet.it Wed Nov 17 03:31:39 2010 From: anavatta at imnet.it (Alberto Navatta) Date: Wed, 17 Nov 2010 10:31:39 +0100 Subject: DM6446 and WVGA display problem In-Reply-To: <201010291036.06557.caglarakyuz@gmail.com> References: <008d01cb76ae$c88f4250$59adc6f0$@it> <201010291036.06557.caglarakyuz@gmail.com> Message-ID: <003701cb863a$415e8610$c41b9230$@imnet.it> Hi all, at the end we've identified the origin of the problem: there's a display horizontal expansion configuration bit that expands pixels to 9/8 of their dimension. We had inadvertently set this bit during LCD config, for this reason we have experienced the issue, 712 pixels when expanded by 9/8 reach a dimension on the screen very close to 800 (712 *9 / 8 = 801). Regards, Alberto -----Original Message----- From: Caglar Akyuz [mailto:caglarakyuz at gmail.com] Sent: venerd? 29 ottobre 2010 09:36 To: davinci-linux-open-source at linux.davincidsp.com Cc: Alberto Navatta Subject: Re: DM6446 and WVGA display problem On Thursday 28 October 2010 05:45:30 pm Alberto Navatta wrote: > Hi, > > > > We're experiencing a strange behavior on our DM6446 board with a WVGA > LCD digital display (display max resolution is 800 x 480 pixels): > > . VID0 and VID1 windows work fine, we can successfully achieve the > 800 x 480 resolution > > . OSD0 doesn't work properly: we're able to see only 712 pixels of > the 800 available, the last column visible is column 712 and it is > positioned at the end of the visible area on the display and columns > from > 712 to 800 are not visible (it looks like if this window is stretched > horizzontally) > > > > We're not able to identify any possible source of the problem: > > . We expect the VPBE is correctly configured to support our LCD > display (Output in RGB888, Syncs, LCD_OE, VCLK and other signals seems > to be good as seen outside the Davinci) > > . As said the VID0 and VID1 windows are correctly displayed on the > screen (if there was a misconfiguration of the Video Encoder we had to > see the problem on all the windows) > > . If we switch the resolution to use the display at a standard VGA > resolution (e.g. by changing HSTART, HVALID, HSPLS, HSDLY, etc. to > see the display as a standard 640 x 480 pixel display), we still have > the same > behavior: VID0 and VID1 are ok, OSD0 displays less than 640 columns > horizontally and the OSD0 window is stretched to cover the whole > visible display area > > > > Does anyone has experienced a similar problem or has an idea of what > we're doing wrong/what could lead to this behavior? > > Regards, > Hi, FYI, we're using WVGA display with DM6446 as well and we don't see any problem with 2.6.32 Arago kernel. Regards, Caglar > Alberto Navatta > From lrg at slimlogic.co.uk Wed Nov 17 03:57:23 2010 From: lrg at slimlogic.co.uk (Liam Girdwood) Date: Wed, 17 Nov 2010 09:57:23 +0000 Subject: [alsa-devel] [PATCH] ASoC: davinci: fixes for multi-component In-Reply-To: <1289910429-29347-1-git-send-email-chris@edesix.com> References: <1289910429-29347-1-git-send-email-chris@edesix.com> Message-ID: <1289987843.3288.0.camel@odin> On Tue, 2010-11-16 at 12:27 +0000, Chris Paulson-Ellis wrote: > Multi-component commit f0fba2ad broke a few things which this patch should > fix. Tested on the DM355 EVM. I've been as careful as I can, but it would be > good if those with access to other Davinci boards could test. > > -- Acked-by: Liam Girdwood -- Freelance Developer, SlimLogic Ltd ASoC and Voltage Regulator Maintainer. http://www.slimlogic.co.uk From nsekhar at ti.com Wed Nov 17 04:43:37 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:37 +0530 Subject: [PATCH 05/49] spi: davinci: remove unnecessary typecast In-Reply-To: <1289990661-30126-5-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-6-git-send-email-nsekhar@ti.com> The typecasting of SPI base address to davinci_spi_reg is unused. Remove it. Tested-By: Michael Williamson Tested-By: Brian Niebuhr Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 7bd0a55..76decda 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -1020,8 +1020,7 @@ static int davinci_spi_probe(struct platform_device *pdev) goto free_master; } - davinci_spi->base = (struct davinci_spi_reg __iomem *) - ioremap(r->start, davinci_spi->region_size); + davinci_spi->base = ioremap(r->start, davinci_spi->region_size); if (davinci_spi->base == NULL) { ret = -ENOMEM; goto release_region; -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:36 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:36 +0530 Subject: [PATCH 04/49] spi: davinci: removed unused #defines In-Reply-To: <1289990661-30126-4-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-5-git-send-email-nsekhar@ti.com> Remove unused defines from code which should help in easy reading of code. Also, use the opportuinity to keep the SPIGCR1 register defines together. Tested-By: Michael Williamson Tested-By: Brian Niebuhr Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 33 ++------------------------------- 1 files changed, 2 insertions(+), 31 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index a92f507..7bd0a55 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -54,16 +54,12 @@ #define SPIFMT_WDELAY_SHIFT 24 #define SPIFMT_CHARLEN_MASK 0x0000001Fu -/* SPIGCR1 */ -#define SPIGCR1_SPIENA_MASK 0x01000000u /* SPIPC0 */ #define SPIPC0_DIFUN_MASK BIT(11) /* MISO */ #define SPIPC0_DOFUN_MASK BIT(10) /* MOSI */ #define SPIPC0_CLKFUN_MASK BIT(9) /* CLK */ #define SPIPC0_SPIENA_MASK BIT(8) /* nREADY */ -#define SPIPC0_EN1FUN_MASK BIT(1) -#define SPIPC0_EN0FUN_MASK BIT(0) #define SPIINT_MASKALL 0x0101035F #define SPI_INTLVL_1 0x000001FFu @@ -75,6 +71,7 @@ #define SPIGCR1_CLKMOD_MASK BIT(1) #define SPIGCR1_MASTER_MASK BIT(0) #define SPIGCR1_LOOPBACK_MASK BIT(16) +#define SPIGCR1_SPIENA_MASK BIT(24) /* SPIBUF */ #define SPIBUF_TXFULL_MASK BIT(29) @@ -90,23 +87,12 @@ #define SPIFLG_RX_INTR_MASK BIT(8) #define SPIFLG_TX_INTR_MASK BIT(9) #define SPIFLG_BUF_INIT_ACTIVE_MASK BIT(24) -#define SPIFLG_MASK (SPIFLG_DLEN_ERR_MASK \ - | SPIFLG_TIMEOUT_MASK | SPIFLG_PARERR_MASK \ - | SPIFLG_DESYNC_MASK | SPIFLG_BITERR_MASK \ - | SPIFLG_OVRRUN_MASK | SPIFLG_RX_INTR_MASK \ - | SPIFLG_TX_INTR_MASK \ - | SPIFLG_BUF_INIT_ACTIVE_MASK) - -#define SPIINT_DLEN_ERR_INTR BIT(0) -#define SPIINT_TIMEOUT_INTR BIT(1) -#define SPIINT_PARERR_INTR BIT(2) -#define SPIINT_DESYNC_INTR BIT(3) + #define SPIINT_BITERR_INTR BIT(4) #define SPIINT_OVRRUN_INTR BIT(6) #define SPIINT_RX_INTR BIT(8) #define SPIINT_TX_INTR BIT(9) #define SPIINT_DMA_REQ_EN BIT(16) -#define SPIINT_ENABLE_HIGHZ BIT(24) #define SPI_T2CDELAY_SHIFT 16 #define SPI_C2TDELAY_SHIFT 24 @@ -118,26 +104,11 @@ #define SPILVL 0x0c #define SPIFLG 0x10 #define SPIPC0 0x14 -#define SPIPC1 0x18 -#define SPIPC2 0x1c -#define SPIPC3 0x20 -#define SPIPC4 0x24 -#define SPIPC5 0x28 -#define SPIPC6 0x2c -#define SPIPC7 0x30 -#define SPIPC8 0x34 -#define SPIDAT0 0x38 #define SPIDAT1 0x3c #define SPIBUF 0x40 -#define SPIEMU 0x44 #define SPIDELAY 0x48 #define SPIDEF 0x4c #define SPIFMT0 0x50 -#define SPIFMT1 0x54 -#define SPIFMT2 0x58 -#define SPIFMT3 0x5c -#define TGINTVEC0 0x60 -#define TGINTVEC1 0x64 struct davinci_spi_slave { u32 cmd_to_write; -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:32 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:32 +0530 Subject: [PATCH 00/49] spi: davinci: re-write existing driver Message-ID: <1289990661-30126-1-git-send-email-nsekhar@ti.com> This patch series represents a break-up into reviewable portions of the work originally done by Brian and posted here: https://patchwork.kernel.org/patch/114924/ While this series does not exactly add up to what Brian's patch is, it is pretty close functionally. This series was tested by me on OMAP-L138, OMAP-L137, DM365 and DM355 EVMs and on their OMAP-L138 boards (not EVMs) by Brian and Michael. The patch series applies to latest of Linus's tree in hope that it will be considered for merge in 2.6.37 since the series is about bug fixes and clean-up. This series along with some patches to add SPI platform support to OMAP-L1x EVMs and Mity-DSP board is also available here: http://arago-project.org/git/projects/?p=linux-davinci.git;a=shortlog;h=refs/heads/davinci-spi-rewrite Brian Niebuhr (42): spi: davinci: fix checkpatch errors spi: davinci: whitespace cleanup spi: davinci: remove unused variable 'pdata' spi: davinci: set chip-select mode in SPIDEF only once spi: davinci: enable both activation and deactivation of chip-selects spi: davinci: remove unnecessary data transmit on CS disable spi: davinci: enable GPIO lines to be used as chip selects spi: davinci: simplify prescalar calculation spi: davinci: remove 'wait_enable' platform data member spi: davinci: make chip-slect specific parameters really chip-select specific spi: davinci: consolidate setup of SPIFMTn in one function spi: davinci: setup chip-select timers values only if timer enabled spi: davinci: add support for wait enable timeouts spi: davinci: remove unused members of davinci_spi_slave spi: davinci: eliminate the single member structure davinci_spi_slave spi: davinci: eliminate unnecessary update of davinci_spi->count spi: davinci: simplify calculation of edma acount value spi: davinci: check for NULL buffer pointer before using it spi: davinci: remove unnecessary disable of SPI spi: davinci: remove unnecessary 'count' variable in driver private data spi: davinci: remove unnecessary completion variable initialization spi: davinci: remove non-useful interrupt mode support spi: davinci: simplify poll mode transfers spi: davinci: add support for interrupt mode spi: davinci: configure the invariable bits in spipc0 only once spi: davinci: remove unnecessary function davinci_spi_bufs_prep() spi: davinci: remove unnecessary call to davinci_spi_setup_transfer() spi: davinci: do not store DMA channel information per chip select spi: davinci: always start transmit DMA spi: davinci: do not use temporary buffer if no transmit data provided spi: davinci: always start receive DMA spi: davinci: use edma_write_slot() to setup EDMA PaRAM slot spi: davinci: fix DMA event generation stoppage spi: davinci: fix EDMA CC errors at end of transfers spi: davinci: handle DMA completion errors correctly spi: davinci: remove usage of additional completion variables for DMA spi: davinci: let DMA operation be specified on per-device basis spi: davinci: remove non-useful "clk_internal" platform data spi: davinci: enable and power-up SPI only when required spi: davinci: setup the driver owner spi: davinci: add additional comments spi: davinci: add EF Johnson Technologies copyright Sekhar Nori (7): spi: davinci: removed unused #defines spi: davinci: remove unnecessary typecast spi: davinci: do not treat Tx interrupt being set as error spi: davinci: do not allocate DMA channels during SPI device setup spi: davinci: remove unnecessary private data member 'region_size' spi: davinci: shorten variable names spi: davinci: kconfig: add manufacturer name to prompt string arch/arm/mach-davinci/dm355.c | 7 +- arch/arm/mach-davinci/dm365.c | 6 - arch/arm/mach-davinci/include/mach/spi.h | 52 +- drivers/spi/Kconfig | 7 +- drivers/spi/davinci_spi.c | 1314 ++++++++++++------------------ 5 files changed, 587 insertions(+), 799 deletions(-) -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:40 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:40 +0530 Subject: [PATCH 08/49] spi: davinci: remove unnecessary data transmit on CS disable In-Reply-To: <1289990661-30126-8-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-9-git-send-email-nsekhar@ti.com> From: Brian Niebuhr On TI DaVinci's SPI controller, the SPIDAT1 register which controls the chip slect status, also has data transmit register in the lower 16 bits. Writing to the whole 32-bits triggers an additional data transmit every time the chip select is disabled. While most SPI slaves cope-up with this, some cannot. This patch fixes this by doing a 16-bit write on the upper half of the SPIDAT1 register While at it, group the SPIGCR1 register related defines seperately from SPIDAT1 register defines. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 20 ++++++++------------ 1 files changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 105c686..82dddf8 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -65,9 +65,10 @@ #define SPI_INTLVL_1 0x000001FFu #define SPI_INTLVL_0 0x00000000u -/* SPIDAT1 */ -#define SPIDAT1_CSHOLD_MASK BIT(28) -#define SPIDAT1_CSNR_SHIFT 16 +/* SPIDAT1 (upper 16 bit defines) */ +#define SPIDAT1_CSHOLD_MASK BIT(12) + +/* SPIGCR1 */ #define SPIGCR1_CLKMOD_MASK BIT(1) #define SPIGCR1_MASTER_MASK BIT(0) #define SPIGCR1_LOOPBACK_MASK BIT(16) @@ -235,8 +236,8 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) { struct davinci_spi *davinci_spi; struct davinci_spi_platform_data *pdata; - u32 data1_reg_val; u8 chip_sel = spi->chip_select; + u16 spidat1_cfg = CS_DEFAULT; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; @@ -245,17 +246,12 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) * Board specific chip select logic decides the polarity and cs * line for the controller */ - data1_reg_val = CS_DEFAULT << SPIDAT1_CSNR_SHIFT; if (value == BITBANG_CS_ACTIVE) { - data1_reg_val |= SPIDAT1_CSHOLD_MASK; - data1_reg_val &= ~((0x1 << chip_sel) << SPIDAT1_CSNR_SHIFT); + spidat1_cfg |= SPIDAT1_CSHOLD_MASK; + spidat1_cfg &= ~(0x1 << chip_sel); } - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); - while ((ioread32(davinci_spi->base + SPIBUF) - & SPIBUF_RXEMPTY_MASK) == 0) - cpu_relax(); - + iowrite16(spidat1_cfg, davinci_spi->base + SPIDAT1 + 2); } /** -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:33 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:33 +0530 Subject: [PATCH 01/49] spi: davinci: fix checkpatch errors In-Reply-To: <1289990661-30126-1-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-2-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Fix the following checkpatch error: WARNING: unnecessary whitespace before a quoted newline + dev_info(&pdev->dev, "Controller at 0x%p \n", davinci_spi->base); Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index b85090c..6b04762 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -1184,7 +1184,7 @@ static int davinci_spi_probe(struct platform_device *pdev) if (ret) goto free_clk; - dev_info(&pdev->dev, "Controller at 0x%p \n", davinci_spi->base); + dev_info(&pdev->dev, "Controller at 0x%p\n", davinci_spi->base); if (!pdata->poll_mode) dev_info(&pdev->dev, "Operating in interrupt mode" -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:34 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:34 +0530 Subject: [PATCH 02/49] spi: davinci: whitespace cleanup In-Reply-To: <1289990661-30126-2-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-3-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Cleanup unnecessary white space from various parts of the file. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 22 ++++++++++------------ 1 files changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 6b04762..2fa5bec 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -175,7 +175,7 @@ struct davinci_spi { u8 *tmp_buf; int count; struct davinci_spi_dma *dma_channels; - struct davinci_spi_platform_data *pdata; + struct davinci_spi_platform_data *pdata; void (*get_rx)(u32 rx_data, struct davinci_spi *); u32 (*get_tx)(struct davinci_spi *); @@ -435,7 +435,6 @@ static int davinci_spi_request_dma(struct spi_device *spi) * * This functions sets the default transfer method. */ - static int davinci_spi_setup(struct spi_device *spi) { int retval; @@ -1096,7 +1095,6 @@ static int davinci_spi_probe(struct platform_device *pdev) } clk_enable(davinci_spi->clk); - master->bus_num = pdev->id; master->num_chipselect = pdata->num_chipselect; master->setup = davinci_spi_setup; @@ -1113,15 +1111,15 @@ static int davinci_spi_probe(struct platform_device *pdev) davinci_spi->bitbang.flags |= SPI_READY; if (use_dma) { - r = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (r) - dma_rx_chan = r->start; - r = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (r) - dma_tx_chan = r->start; - r = platform_get_resource(pdev, IORESOURCE_DMA, 2); - if (r) - dma_eventq = r->start; + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (r) + dma_rx_chan = r->start; + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (r) + dma_tx_chan = r->start; + r = platform_get_resource(pdev, IORESOURCE_DMA, 2); + if (r) + dma_eventq = r->start; } if (!use_dma || -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:39 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:39 +0530 Subject: [PATCH 07/49] spi: davinci: enable both activation and deactivation of chip-selects In-Reply-To: <1289990661-30126-7-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-8-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Let davinci_spi_chipselect() perform both activation and deactivation of chip selects. This lets spi_bitbang fully control chip select activation, as intended by the SPI API. With this change, the chip select activation code need not be duplicated in davinci_spi_bufs_{pio|dma}(). Also, keeping chip select active control is removed as a platform data and simply controlled using information from spi_bitbang on whether chip slect should be activated or de-activated. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/dm355.c | 1 - arch/arm/mach-davinci/dm365.c | 1 - arch/arm/mach-davinci/include/mach/spi.h | 1 - drivers/spi/davinci_spi.c | 53 ++++++++++------------------- 4 files changed, 18 insertions(+), 38 deletions(-) diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 9be261b..6a76dfa 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -413,7 +413,6 @@ static struct davinci_spi_platform_data dm355_spi0_pdata = { .version = SPI_VERSION_1, .num_chipselect = 2, .clk_internal = 1, - .cs_hold = 1, .intr_level = 0, .poll_mode = 1, /* 0 -> interrupt mode 1-> polling mode */ .c2tdelay = 0, diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index a12065e..cd623db 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -626,7 +626,6 @@ static struct davinci_spi_platform_data dm365_spi0_pdata = { .version = SPI_VERSION_1, .num_chipselect = 2, .clk_internal = 1, - .cs_hold = 1, .intr_level = 0, .poll_mode = 1, /* 0 -> interrupt mode 1-> polling mode */ .c2tdelay = 0, diff --git a/arch/arm/mach-davinci/include/mach/spi.h b/arch/arm/mach-davinci/include/mach/spi.h index 910efbf..2cb326e 100644 --- a/arch/arm/mach-davinci/include/mach/spi.h +++ b/arch/arm/mach-davinci/include/mach/spi.h @@ -33,7 +33,6 @@ struct davinci_spi_platform_data { u8 wait_enable; u8 timer_disable; u8 clk_internal; - u8 cs_hold; u8 intr_level; u8 poll_mode; u8 use_dma; diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index d6b6a49..105c686 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -66,7 +66,7 @@ #define SPI_INTLVL_0 0x00000000u /* SPIDAT1 */ -#define SPIDAT1_CSHOLD_SHIFT 28 +#define SPIDAT1_CSHOLD_MASK BIT(28) #define SPIDAT1_CSNR_SHIFT 16 #define SPIGCR1_CLKMOD_MASK BIT(1) #define SPIGCR1_MASTER_MASK BIT(0) @@ -235,7 +235,8 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) { struct davinci_spi *davinci_spi; struct davinci_spi_platform_data *pdata; - u32 data1_reg_val = 0; + u32 data1_reg_val; + u8 chip_sel = spi->chip_select; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; @@ -244,14 +245,17 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) * Board specific chip select logic decides the polarity and cs * line for the controller */ - if (value == BITBANG_CS_INACTIVE) { - data1_reg_val |= CS_DEFAULT << SPIDAT1_CSNR_SHIFT; - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); - - while ((ioread32(davinci_spi->base + SPIBUF) - & SPIBUF_RXEMPTY_MASK) == 0) - cpu_relax(); + data1_reg_val = CS_DEFAULT << SPIDAT1_CSNR_SHIFT; + if (value == BITBANG_CS_ACTIVE) { + data1_reg_val |= SPIDAT1_CSHOLD_MASK; + data1_reg_val &= ~((0x1 << chip_sel) << SPIDAT1_CSNR_SHIFT); } + + iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); + while ((ioread32(davinci_spi->base + SPIBUF) + & SPIBUF_RXEMPTY_MASK) == 0) + cpu_relax(); + } /** @@ -632,7 +636,7 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; int int_status, count, ret; - u8 conv, tmp; + u8 conv; u32 tx_data, data1_reg_val; u32 buf_val, flg_val; struct davinci_spi_platform_data *pdata; @@ -647,6 +651,8 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) conv = davinci_spi->slave[spi->chip_select].bytes_per_word; davinci_spi->count = t->len / conv; + data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); + INIT_COMPLETION(davinci_spi->done); ret = davinci_spi_bufs_prep(spi, davinci_spi); @@ -661,16 +667,6 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) davinci_spi->base + SPIDELAY); count = davinci_spi->count; - data1_reg_val = pdata->cs_hold << SPIDAT1_CSHOLD_SHIFT; - tmp = ~(0x1 << spi->chip_select); - - clear_io_bits(davinci_spi->base + SPIDEF, ~tmp); - - data1_reg_val |= tmp << SPIDAT1_CSNR_SHIFT; - - while ((ioread32(davinci_spi->base + SPIBUF) - & SPIBUF_RXEMPTY_MASK) == 0) - cpu_relax(); /* Determine the command to execute READ or WRITE */ if (t->tx_buf) { @@ -770,7 +766,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) int int_status = 0; int count, temp_count; u8 conv = 1; - u8 tmp; u32 data1_reg_val; struct davinci_spi_dma *davinci_spi_dma; int word_len, data_type, ret; @@ -794,6 +789,8 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) conv = davinci_spi->slave[spi->chip_select].bytes_per_word; davinci_spi->count = t->len / conv; + data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); + INIT_COMPLETION(davinci_spi->done); init_completion(&davinci_spi_dma->dma_rx_completion); @@ -820,28 +817,14 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) davinci_spi->base + SPIDELAY); count = davinci_spi->count; /* the number of elements */ - data1_reg_val = pdata->cs_hold << SPIDAT1_CSHOLD_SHIFT; - - /* CS default = 0xFF */ - tmp = ~(0x1 << spi->chip_select); - - clear_io_bits(davinci_spi->base + SPIDEF, ~tmp); - - data1_reg_val |= tmp << SPIDAT1_CSNR_SHIFT; /* disable all interrupts for dma transfers */ clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); /* Disable SPI to write configuration bits in SPIDAT */ clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - while ((ioread32(davinci_spi->base + SPIBUF) - & SPIBUF_RXEMPTY_MASK) == 0) - cpu_relax(); - - if (t->tx_buf) { t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, count, DMA_TO_DEVICE); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:38 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:38 +0530 Subject: [PATCH 06/49] spi: davinci: set chip-select mode in SPIDEF only once In-Reply-To: <1289990661-30126-6-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-7-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Quit writing the same constant value determining the chip-select mode when no transmissions are in progress in davinci_spi_chipelect(). Instead just setup the SPIDEF register once during probe. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 76decda..d6b6a49 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -245,8 +245,6 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) * line for the controller */ if (value == BITBANG_CS_INACTIVE) { - set_io_bits(davinci_spi->base + SPIDEF, CS_DEFAULT); - data1_reg_val |= CS_DEFAULT << SPIDAT1_CSNR_SHIFT; iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); @@ -1132,6 +1130,8 @@ static int davinci_spi_probe(struct platform_device *pdev) clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_CLKMOD_MASK); + iowrite32(CS_DEFAULT, davinci_spi->base + SPIDEF); + /* master mode default */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_MASTER_MASK); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:41 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:41 +0530 Subject: [PATCH 09/49] spi: davinci: enable GPIO lines to be used as chip selects In-Reply-To: <1289990661-30126-9-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-10-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Sometimes, the chip selects provided by SPI module are muxed with other functionality and cannot be used in some designs. In such cases, it becomes convenient to use an available GPIO line as chip select. This patch enables the DaVinci SPI driver to treat specific GPIO lines as chip selects based on information provided in platform data. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/include/mach/spi.h | 3 ++ drivers/spi/davinci_spi.c | 39 ++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/spi.h b/arch/arm/mach-davinci/include/mach/spi.h index 2cb326e..734d1fb 100644 --- a/arch/arm/mach-davinci/include/mach/spi.h +++ b/arch/arm/mach-davinci/include/mach/spi.h @@ -19,6 +19,8 @@ #ifndef __ARCH_ARM_DAVINCI_SPI_H #define __ARCH_ARM_DAVINCI_SPI_H +#define SPI_INTERN_CS 0xFF + enum { SPI_VERSION_1, /* For DM355/DM365/DM6467 */ SPI_VERSION_2, /* For DA8xx */ @@ -38,6 +40,7 @@ struct davinci_spi_platform_data { u8 use_dma; u8 c2tdelay; u8 t2cdelay; + u8 *chip_sel; }; #endif /* __ARCH_ARM_DAVINCI_SPI_H */ diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 82dddf8..d5d7014 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -238,20 +238,32 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) struct davinci_spi_platform_data *pdata; u8 chip_sel = spi->chip_select; u16 spidat1_cfg = CS_DEFAULT; + bool gpio_chipsel = false; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; + if (pdata->chip_sel && chip_sel < pdata->num_chipselect && + pdata->chip_sel[chip_sel] != SPI_INTERN_CS) + gpio_chipsel = true; + /* * Board specific chip select logic decides the polarity and cs * line for the controller */ - if (value == BITBANG_CS_ACTIVE) { - spidat1_cfg |= SPIDAT1_CSHOLD_MASK; - spidat1_cfg &= ~(0x1 << chip_sel); - } + if (gpio_chipsel) { + if (value == BITBANG_CS_ACTIVE) + gpio_set_value(pdata->chip_sel[chip_sel], 0); + else + gpio_set_value(pdata->chip_sel[chip_sel], 1); + } else { + if (value == BITBANG_CS_ACTIVE) { + spidat1_cfg |= SPIDAT1_CSHOLD_MASK; + spidat1_cfg &= ~(0x1 << chip_sel); + } - iowrite16(spidat1_cfg, davinci_spi->base + SPIDAT1 + 2); + iowrite16(spidat1_cfg, davinci_spi->base + SPIDAT1 + 2); + } } /** @@ -546,6 +558,7 @@ static void davinci_spi_cleanup(struct spi_device *spi) static int davinci_spi_bufs_prep(struct spi_device *spi, struct davinci_spi *davinci_spi) { + struct davinci_spi_platform_data *pdata; int op_mode = 0; /* @@ -558,8 +571,12 @@ static int davinci_spi_bufs_prep(struct spi_device *spi, op_mode = SPIPC0_DIFUN_MASK | SPIPC0_DOFUN_MASK | SPIPC0_CLKFUN_MASK; - if (!(spi->mode & SPI_NO_CS)) - op_mode |= 1 << spi->chip_select; + if (!(spi->mode & SPI_NO_CS)) { + pdata = davinci_spi->pdata; + if (!pdata->chip_sel || + pdata->chip_sel[spi->chip_select] == SPI_INTERN_CS) + op_mode |= 1 << spi->chip_select; + } if (spi->mode & SPI_READY) op_mode |= SPIPC0_SPIENA_MASK; @@ -1101,6 +1118,14 @@ static int davinci_spi_probe(struct platform_device *pdev) udelay(100); iowrite32(1, davinci_spi->base + SPIGCR0); + /* initialize chip selects */ + if (pdata->chip_sel) { + for (i = 0; i < pdata->num_chipselect; i++) { + if (pdata->chip_sel[i] != SPI_INTERN_CS) + gpio_direction_output(pdata->chip_sel[i], 1); + } + } + /* Clock internal */ if (davinci_spi->pdata->clk_internal) set_io_bits(davinci_spi->base + SPIGCR1, -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:42 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:42 +0530 Subject: [PATCH 10/49] spi: davinci: simplify prescalar calculation In-Reply-To: <1289990661-30126-10-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-11-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Simplify pre-scalar calculation and move it into a seprate function. Refuse to correct invalid pre-scalar values silently as this might lead to unexpected bugs and lower performance. Instead an error will force users to dig into the root-cause of the issue. While at it, remove some device specific checks on the maximum SPI frequency. As the driver supports the SPI interface implemented on various devices, it should only take care of core SPI limitations and leave the device specific handling to platform code. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 53 ++++++++++++++++++++++++++------------------ 1 files changed, 31 insertions(+), 22 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index d5d7014..17269ad 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -53,6 +53,7 @@ #define SPIFMT_WDELAY_MASK 0x3f000000u #define SPIFMT_WDELAY_SHIFT 24 #define SPIFMT_CHARLEN_MASK 0x0000001Fu +#define SPIFMT_PRESCALE_SHIFT 8 /* SPIPC0 */ @@ -267,6 +268,29 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) } /** + * davinci_spi_get_prescale - Calculates the correct prescale value + * @maxspeed_hz: the maximum rate the SPI clock can run at + * + * This function calculates the prescale value that generates a clock rate + * less than or equal to the specified maximum. + * + * Returns: calculated prescale - 1 for easy programming into SPI registers + * or negative error number if valid prescalar cannot be updated. + */ +static inline int davinci_spi_get_prescale(struct davinci_spi *davinci_spi, + u32 max_speed_hz) +{ + int ret; + + ret = DIV_ROUND_UP(clk_get_rate(davinci_spi->clk), max_speed_hz); + + if (ret < 3 || ret > 256) + return -EINVAL; + + return ret - 1; +} + +/** * davinci_spi_setup_transfer - This functions will determine transfer method * @spi: spi device on which data transfer to be done * @t: spi transfer in which transfer info is filled @@ -281,7 +305,7 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, struct davinci_spi *davinci_spi; u8 bits_per_word = 0; - u32 hz = 0, prescale = 0, clkspeed; + u32 hz = 0, prescale = 0; davinci_spi = spi_master_get_devdata(spi->master); @@ -312,21 +336,18 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, if (!hz) hz = spi->max_speed_hz; + prescale = davinci_spi_get_prescale(davinci_spi, hz); + if (prescale < 0) + return prescale; + clear_fmt_bits(davinci_spi->base, SPIFMT_CHARLEN_MASK, spi->chip_select); set_fmt_bits(davinci_spi->base, bits_per_word & 0x1f, spi->chip_select); - clkspeed = clk_get_rate(davinci_spi->clk); - if (hz > clkspeed / 2) - prescale = 1 << 8; - if (hz < clkspeed / 256) - prescale = 255 << 8; - if (!prescale) - prescale = ((clkspeed / hz - 1) << 8) & 0x0000ff00; - clear_fmt_bits(davinci_spi->base, 0x0000ff00, spi->chip_select); - set_fmt_bits(davinci_spi->base, prescale, spi->chip_select); + set_fmt_bits(davinci_spi->base, + prescale << SPIFMT_PRESCALE_SHIFT, spi->chip_select); return 0; } @@ -413,10 +434,8 @@ static int davinci_spi_setup(struct spi_device *spi) int retval; struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; - struct device *sdev; davinci_spi = spi_master_get_devdata(spi->master); - sdev = davinci_spi->bitbang.master->dev.parent; /* if bits per word length is zero then set it default 8 */ if (!spi->bits_per_word) @@ -436,16 +455,6 @@ static int davinci_spi_setup(struct spi_device *spi) } /* - * SPI in DaVinci and DA8xx operate between - * 600 KHz and 50 MHz - */ - if (spi->max_speed_hz < 600000 || spi->max_speed_hz > 50000000) { - dev_dbg(sdev, "Operating frequency is not in acceptable " - "range\n"); - return -EINVAL; - } - - /* * Set up SPIFMTn register, unique to this chipselect. * * NOTE: we could do all of these with one write. Also, some -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:43 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:43 +0530 Subject: [PATCH 11/49] spi: davinci: remove 'wait_enable' platform data member In-Reply-To: <1289990661-30126-11-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-12-git-send-email-nsekhar@ti.com> From: Brian Niebuhr The SPI_READY bit of struct spi_device:mode serves the purpose of letting the SPI master know if the slave can signal if it is ready for transfer or not. The 'wait_enable' platform data was duplicating this functionality. Use the framework provided method of indicating this capability. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/include/mach/spi.h | 1 - drivers/spi/davinci_spi.c | 2 +- 2 files changed, 1 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/spi.h b/arch/arm/mach-davinci/include/mach/spi.h index 734d1fb..fe699140 100644 --- a/arch/arm/mach-davinci/include/mach/spi.h +++ b/arch/arm/mach-davinci/include/mach/spi.h @@ -32,7 +32,6 @@ struct davinci_spi_platform_data { u8 wdelay; u8 odd_parity; u8 parity_enable; - u8 wait_enable; u8 timer_disable; u8 clk_internal; u8 intr_level; diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 17269ad..5480857 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -522,7 +522,7 @@ static int davinci_spi_setup(struct spi_device *spi) SPIFMT_PARITYENA_MASK, spi->chip_select); - if (davinci_spi->pdata->wait_enable) + if (spi->mode & SPI_READY) set_fmt_bits(davinci_spi->base, SPIFMT_WAITENA_MASK, spi->chip_select); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:35 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:35 +0530 Subject: [PATCH 03/49] spi: davinci: remove unused variable 'pdata' In-Reply-To: <1289990661-30126-3-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-4-git-send-email-nsekhar@ti.com> From: Brian Niebuhr The 'pdata' variable is unused in couple of routines. Remove such occurences. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 8 -------- 1 files changed, 0 insertions(+), 8 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 2fa5bec..a92f507 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -299,12 +299,10 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, { struct davinci_spi *davinci_spi; - struct davinci_spi_platform_data *pdata; u8 bits_per_word = 0; u32 hz = 0, prescale = 0, clkspeed; davinci_spi = spi_master_get_devdata(spi->master); - pdata = davinci_spi->pdata; if (t) { bits_per_word = t->bits_per_word; @@ -357,11 +355,9 @@ static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data) struct spi_device *spi = (struct spi_device *)data; struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; - struct davinci_spi_platform_data *pdata; davinci_spi = spi_master_get_devdata(spi->master); davinci_spi_dma = &(davinci_spi->dma_channels[spi->chip_select]); - pdata = davinci_spi->pdata; if (ch_status == DMA_COMPLETE) edma_stop(davinci_spi_dma->dma_rx_channel); @@ -378,11 +374,9 @@ static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) struct spi_device *spi = (struct spi_device *)data; struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; - struct davinci_spi_platform_data *pdata; davinci_spi = spi_master_get_devdata(spi->master); davinci_spi_dma = &(davinci_spi->dma_channels[spi->chip_select]); - pdata = davinci_spi->pdata; if (ch_status == DMA_COMPLETE) edma_stop(davinci_spi_dma->dma_tx_channel); @@ -398,13 +392,11 @@ static int davinci_spi_request_dma(struct spi_device *spi) { struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; - struct davinci_spi_platform_data *pdata; struct device *sdev; int r; davinci_spi = spi_master_get_devdata(spi->master); davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; - pdata = davinci_spi->pdata; sdev = davinci_spi->bitbang.master->dev.parent; r = edma_alloc_channel(davinci_spi_dma->dma_rx_sync_dev, -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:44 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:44 +0530 Subject: [PATCH 12/49] spi: davinci: make chip-slect specific parameters really chip-select specific In-Reply-To: <1289990661-30126-12-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-13-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Some chip-select specific paramterers like wdelay, parity, usage of chip-select timers (and the actual timer values) are included in platform data forcing the same behaviour across all chip-selects. Create a new davinci_spi_config data structure which can be passed along using controller_data member of spi_device data structure on a per-device basis. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/dm355.c | 3 - arch/arm/mach-davinci/dm365.c | 3 - arch/arm/mach-davinci/include/mach/spi.h | 13 ++++-- drivers/spi/davinci_spi.c | 67 ++++++++++++++++------------- 4 files changed, 45 insertions(+), 41 deletions(-) diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 6a76dfa..a6d9b72 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -413,10 +413,7 @@ static struct davinci_spi_platform_data dm355_spi0_pdata = { .version = SPI_VERSION_1, .num_chipselect = 2, .clk_internal = 1, - .intr_level = 0, .poll_mode = 1, /* 0 -> interrupt mode 1-> polling mode */ - .c2tdelay = 0, - .t2cdelay = 0, }; static struct platform_device dm355_spi0_device = { .name = "spi_davinci", diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index cd623db..80dd159 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -626,10 +626,7 @@ static struct davinci_spi_platform_data dm365_spi0_pdata = { .version = SPI_VERSION_1, .num_chipselect = 2, .clk_internal = 1, - .intr_level = 0, .poll_mode = 1, /* 0 -> interrupt mode 1-> polling mode */ - .c2tdelay = 0, - .t2cdelay = 0, }; static struct resource dm365_spi0_resources[] = { diff --git a/arch/arm/mach-davinci/include/mach/spi.h b/arch/arm/mach-davinci/include/mach/spi.h index fe699140..29c19c4 100644 --- a/arch/arm/mach-davinci/include/mach/spi.h +++ b/arch/arm/mach-davinci/include/mach/spi.h @@ -29,17 +29,20 @@ enum { struct davinci_spi_platform_data { u8 version; u8 num_chipselect; - u8 wdelay; - u8 odd_parity; - u8 parity_enable; - u8 timer_disable; u8 clk_internal; u8 intr_level; u8 poll_mode; u8 use_dma; + u8 *chip_sel; +}; + +struct davinci_spi_config { + u8 wdelay; + u8 odd_parity; + u8 parity_enable; + u8 timer_disable; u8 c2tdelay; u8 t2cdelay; - u8 *chip_sel; }; #endif /* __ARCH_ARM_DAVINCI_SPI_H */ diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 5480857..d4320f7 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -156,6 +156,8 @@ struct davinci_spi { struct davinci_spi_slave slave[SPI_MAX_CHIPSELECT]; }; +static struct davinci_spi_config davinci_spi_default_cfg; + static unsigned use_dma; static void davinci_spi_rx_buf_u8(u32 data, struct davinci_spi *davinci_spi) @@ -434,8 +436,12 @@ static int davinci_spi_setup(struct spi_device *spi) int retval; struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; + struct davinci_spi_config *spicfg; davinci_spi = spi_master_get_devdata(spi->master); + spicfg = (struct davinci_spi_config *)spi->controller_data; + if (!spicfg) + spicfg = &davinci_spi_default_cfg; /* if bits per word length is zero then set it default 8 */ if (!spi->bits_per_word) @@ -496,31 +502,34 @@ static int davinci_spi_setup(struct spi_device *spi) */ if (davinci_spi->version == SPI_VERSION_2) { + clear_fmt_bits(davinci_spi->base, SPIFMT_WDELAY_MASK, - spi->chip_select); + spi->chip_select); set_fmt_bits(davinci_spi->base, - (davinci_spi->pdata->wdelay - << SPIFMT_WDELAY_SHIFT) - & SPIFMT_WDELAY_MASK, - spi->chip_select); + (spicfg->wdelay << SPIFMT_WDELAY_SHIFT) & + SPIFMT_WDELAY_MASK, spi->chip_select); - if (davinci_spi->pdata->odd_parity) - set_fmt_bits(davinci_spi->base, - SPIFMT_ODD_PARITY_MASK, - spi->chip_select); + if (spicfg->odd_parity) + set_fmt_bits(davinci_spi->base, SPIFMT_ODD_PARITY_MASK, + spi->chip_select); else clear_fmt_bits(davinci_spi->base, SPIFMT_ODD_PARITY_MASK, spi->chip_select); - if (davinci_spi->pdata->parity_enable) - set_fmt_bits(davinci_spi->base, - SPIFMT_PARITYENA_MASK, - spi->chip_select); + if (spicfg->parity_enable) + set_fmt_bits(davinci_spi->base, SPIFMT_PARITYENA_MASK, + spi->chip_select); else - clear_fmt_bits(davinci_spi->base, - SPIFMT_PARITYENA_MASK, - spi->chip_select); + clear_fmt_bits(davinci_spi->base, SPIFMT_PARITYENA_MASK, + spi->chip_select); + + if (spicfg->timer_disable) + set_fmt_bits(davinci_spi->base, SPIFMT_DISTIMER_MASK, + spi->chip_select); + else + clear_fmt_bits(davinci_spi->base, SPIFMT_DISTIMER_MASK, + spi->chip_select); if (spi->mode & SPI_READY) set_fmt_bits(davinci_spi->base, @@ -531,14 +540,6 @@ static int davinci_spi_setup(struct spi_device *spi) SPIFMT_WAITENA_MASK, spi->chip_select); - if (davinci_spi->pdata->timer_disable) - set_fmt_bits(davinci_spi->base, - SPIFMT_DISTIMER_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, - SPIFMT_DISTIMER_MASK, - spi->chip_select); } retval = davinci_spi_setup_transfer(spi, NULL); @@ -662,9 +663,13 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) u32 tx_data, data1_reg_val; u32 buf_val, flg_val; struct davinci_spi_platform_data *pdata; + struct davinci_spi_config *spicfg; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; + spicfg = (struct davinci_spi_config *)spi->controller_data; + if (!spicfg) + spicfg = &davinci_spi_default_cfg; davinci_spi->tx = t->tx_buf; davinci_spi->rx = t->rx_buf; @@ -684,8 +689,8 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - iowrite32(0 | (pdata->c2tdelay << SPI_C2TDELAY_SHIFT) | - (pdata->t2cdelay << SPI_T2CDELAY_SHIFT), + iowrite32((spicfg->c2tdelay << SPI_C2TDELAY_SHIFT) | + (spicfg->t2cdelay << SPI_T2CDELAY_SHIFT), davinci_spi->base + SPIDELAY); count = davinci_spi->count; @@ -792,12 +797,14 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) struct davinci_spi_dma *davinci_spi_dma; int word_len, data_type, ret; unsigned long tx_reg, rx_reg; - struct davinci_spi_platform_data *pdata; + struct davinci_spi_config *spicfg; struct device *sdev; davinci_spi = spi_master_get_devdata(spi->master); - pdata = davinci_spi->pdata; sdev = davinci_spi->bitbang.master->dev.parent; + spicfg = (struct davinci_spi_config *)spi->controller_data; + if (!spicfg) + spicfg = &davinci_spi_default_cfg; davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; @@ -834,8 +841,8 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) return ret; /* Put delay val if required */ - iowrite32(0 | (pdata->c2tdelay << SPI_C2TDELAY_SHIFT) | - (pdata->t2cdelay << SPI_T2CDELAY_SHIFT), + iowrite32((spicfg->c2tdelay << SPI_C2TDELAY_SHIFT) | + (spicfg->t2cdelay << SPI_T2CDELAY_SHIFT), davinci_spi->base + SPIDELAY); count = davinci_spi->count; /* the number of elements */ -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:47 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:47 +0530 Subject: [PATCH 15/49] spi: davinci: add support for wait enable timeouts In-Reply-To: <1289990661-30126-15-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-16-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Just enabling WAITENA in SPIFMTn register waits for the enable signal from the slave indefinitely. Allow support for finite waiting by adding support for c2e delay (maximum time for addressed slave to respond) and t2e delay (maximum time for slave to respond after transmit data finished). While at it, modify the T2C and C2T defines by prepending the register name as is the convention followed for other register field elsewhere in the driver. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/include/mach/spi.h | 2 + drivers/spi/davinci_spi.c | 36 ++++++++++++++++++++++------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/spi.h b/arch/arm/mach-davinci/include/mach/spi.h index 29c19c4..483b055 100644 --- a/arch/arm/mach-davinci/include/mach/spi.h +++ b/arch/arm/mach-davinci/include/mach/spi.h @@ -43,6 +43,8 @@ struct davinci_spi_config { u8 timer_disable; u8 c2tdelay; u8 t2cdelay; + u8 t2edelay; + u8 c2edelay; }; #endif /* __ARCH_ARM_DAVINCI_SPI_H */ diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index d09b63c..e94c638 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -78,6 +78,16 @@ #define SPIBUF_TXFULL_MASK BIT(29) #define SPIBUF_RXEMPTY_MASK BIT(31) +/* SPIDELAY */ +#define SPIDELAY_C2TDELAY_SHIFT 24 +#define SPIDELAY_C2TDELAY_MASK (0xFF << SPIDELAY_C2TDELAY_SHIFT) +#define SPIDELAY_T2CDELAY_SHIFT 16 +#define SPIDELAY_T2CDELAY_MASK (0xFF << SPIDELAY_T2CDELAY_SHIFT) +#define SPIDELAY_T2EDELAY_SHIFT 8 +#define SPIDELAY_T2EDELAY_MASK (0xFF << SPIDELAY_T2EDELAY_SHIFT) +#define SPIDELAY_C2EDELAY_SHIFT 0 +#define SPIDELAY_C2EDELAY_MASK 0xFF + /* Error Masks */ #define SPIFLG_DLEN_ERR_MASK BIT(0) #define SPIFLG_TIMEOUT_MASK BIT(1) @@ -95,9 +105,6 @@ #define SPIINT_TX_INTR BIT(9) #define SPIINT_DMA_REQ_EN BIT(16) -#define SPI_T2CDELAY_SHIFT 16 -#define SPI_C2TDELAY_SHIFT 24 - /* SPI Controller registers */ #define SPIGCR0 0x00 #define SPIGCR1 0x04 @@ -363,6 +370,8 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, if (davinci_spi->version == SPI_VERSION_2) { + u32 delay = 0; + spifmt |= ((spicfg->wdelay << SPIFMT_WDELAY_SHIFT) & SPIFMT_WDELAY_MASK); @@ -372,15 +381,24 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, if (spicfg->parity_enable) spifmt |= SPIFMT_PARITYENA_MASK; - if (spicfg->timer_disable) + if (spicfg->timer_disable) { spifmt |= SPIFMT_DISTIMER_MASK; - else - iowrite32((spicfg->c2tdelay << SPI_C2TDELAY_SHIFT) | - (spicfg->t2cdelay << SPI_T2CDELAY_SHIFT), - davinci_spi->base + SPIDELAY); + } else { + delay |= (spicfg->c2tdelay << SPIDELAY_C2TDELAY_SHIFT) + & SPIDELAY_C2TDELAY_MASK; + delay |= (spicfg->t2cdelay << SPIDELAY_T2CDELAY_SHIFT) + & SPIDELAY_T2CDELAY_MASK; + } - if (spi->mode & SPI_READY) + if (spi->mode & SPI_READY) { spifmt |= SPIFMT_WAITENA_MASK; + delay |= (spicfg->t2edelay << SPIDELAY_T2EDELAY_SHIFT) + & SPIDELAY_T2EDELAY_MASK; + delay |= (spicfg->c2edelay << SPIDELAY_C2EDELAY_SHIFT) + & SPIDELAY_C2EDELAY_MASK; + } + + iowrite32(delay, davinci_spi->base + SPIDELAY); } iowrite32(spifmt, davinci_spi->base + SPIFMT0); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:45 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:45 +0530 Subject: [PATCH 13/49] spi: davinci: consolidate setup of SPIFMTn in one function In-Reply-To: <1289990661-30126-13-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-14-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Consolidate the setup of SPIFMTn register under davinci_spi_setup_transfer() simplifying the code and avoiding unnecessary reads and writes to the register. The two inline functions {set|clear}_fmt_bits() can be eliminated because of this. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 154 ++++++++++++++------------------------------ 1 files changed, 49 insertions(+), 105 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index d4320f7..34b28fe 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -52,7 +52,6 @@ #define SPIFMT_ODD_PARITY_MASK BIT(23) #define SPIFMT_WDELAY_MASK 0x3f000000u #define SPIFMT_WDELAY_SHIFT 24 -#define SPIFMT_CHARLEN_MASK 0x0000001Fu #define SPIFMT_PRESCALE_SHIFT 8 @@ -212,16 +211,6 @@ static inline void clear_io_bits(void __iomem *addr, u32 bits) iowrite32(v, addr); } -static inline void set_fmt_bits(void __iomem *addr, u32 bits, int cs_num) -{ - set_io_bits(addr + SPIFMT0 + (0x4 * cs_num), bits); -} - -static inline void clear_fmt_bits(void __iomem *addr, u32 bits, int cs_num) -{ - clear_io_bits(addr + SPIFMT0 + (0x4 * cs_num), bits); -} - static void davinci_spi_set_dma_req(const struct spi_device *spi, int enable) { struct davinci_spi *davinci_spi = spi_master_get_devdata(spi->master); @@ -306,10 +295,14 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, { struct davinci_spi *davinci_spi; + struct davinci_spi_config *spicfg; u8 bits_per_word = 0; - u32 hz = 0, prescale = 0; + u32 hz = 0, spifmt = 0, prescale = 0; davinci_spi = spi_master_get_devdata(spi->master); + spicfg = (struct davinci_spi_config *)spi->controller_data; + if (!spicfg) + spicfg = &davinci_spi_default_cfg; if (t) { bits_per_word = t->bits_per_word; @@ -338,18 +331,55 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, if (!hz) hz = spi->max_speed_hz; + /* Set up SPIFMTn register, unique to this chipselect. */ + prescale = davinci_spi_get_prescale(davinci_spi, hz); if (prescale < 0) return prescale; - clear_fmt_bits(davinci_spi->base, SPIFMT_CHARLEN_MASK, - spi->chip_select); - set_fmt_bits(davinci_spi->base, bits_per_word & 0x1f, - spi->chip_select); + spifmt = (prescale << SPIFMT_PRESCALE_SHIFT) | (bits_per_word & 0x1f); + + if (spi->mode & SPI_LSB_FIRST) + spifmt |= SPIFMT_SHIFTDIR_MASK; + + if (spi->mode & SPI_CPOL) + spifmt |= SPIFMT_POLARITY_MASK; + + if (!(spi->mode & SPI_CPHA)) + spifmt |= SPIFMT_PHASE_MASK; + + /* + * Version 1 hardware supports two basic SPI modes: + * - Standard SPI mode uses 4 pins, with chipselect + * - 3 pin SPI is a 4 pin variant without CS (SPI_NO_CS) + * (distinct from SPI_3WIRE, with just one data wire; + * or similar variants without MOSI or without MISO) + * + * Version 2 hardware supports an optional handshaking signal, + * so it can support two more modes: + * - 5 pin SPI variant is standard SPI plus SPI_READY + * - 4 pin with enable is (SPI_READY | SPI_NO_CS) + */ + + if (davinci_spi->version == SPI_VERSION_2) { + + spifmt |= ((spicfg->wdelay << SPIFMT_WDELAY_SHIFT) + & SPIFMT_WDELAY_MASK); - clear_fmt_bits(davinci_spi->base, 0x0000ff00, spi->chip_select); - set_fmt_bits(davinci_spi->base, - prescale << SPIFMT_PRESCALE_SHIFT, spi->chip_select); + if (spicfg->odd_parity) + spifmt |= SPIFMT_ODD_PARITY_MASK; + + if (spicfg->parity_enable) + spifmt |= SPIFMT_PARITYENA_MASK; + + if (spicfg->timer_disable) + spifmt |= SPIFMT_DISTIMER_MASK; + + if (spi->mode & SPI_READY) + spifmt |= SPIFMT_WAITENA_MASK; + } + + iowrite32(spifmt, davinci_spi->base + SPIFMT0); return 0; } @@ -436,12 +466,8 @@ static int davinci_spi_setup(struct spi_device *spi) int retval; struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; - struct davinci_spi_config *spicfg; davinci_spi = spi_master_get_devdata(spi->master); - spicfg = (struct davinci_spi_config *)spi->controller_data; - if (!spicfg) - spicfg = &davinci_spi_default_cfg; /* if bits per word length is zero then set it default 8 */ if (!spi->bits_per_word) @@ -460,88 +486,6 @@ static int davinci_spi_setup(struct spi_device *spi) } } - /* - * Set up SPIFMTn register, unique to this chipselect. - * - * NOTE: we could do all of these with one write. Also, some - * of the "version 2" features are found in chips that don't - * support all of them... - */ - if (spi->mode & SPI_LSB_FIRST) - set_fmt_bits(davinci_spi->base, SPIFMT_SHIFTDIR_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, SPIFMT_SHIFTDIR_MASK, - spi->chip_select); - - if (spi->mode & SPI_CPOL) - set_fmt_bits(davinci_spi->base, SPIFMT_POLARITY_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, SPIFMT_POLARITY_MASK, - spi->chip_select); - - if (!(spi->mode & SPI_CPHA)) - set_fmt_bits(davinci_spi->base, SPIFMT_PHASE_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, SPIFMT_PHASE_MASK, - spi->chip_select); - - /* - * Version 1 hardware supports two basic SPI modes: - * - Standard SPI mode uses 4 pins, with chipselect - * - 3 pin SPI is a 4 pin variant without CS (SPI_NO_CS) - * (distinct from SPI_3WIRE, with just one data wire; - * or similar variants without MOSI or without MISO) - * - * Version 2 hardware supports an optional handshaking signal, - * so it can support two more modes: - * - 5 pin SPI variant is standard SPI plus SPI_READY - * - 4 pin with enable is (SPI_READY | SPI_NO_CS) - */ - - if (davinci_spi->version == SPI_VERSION_2) { - - clear_fmt_bits(davinci_spi->base, SPIFMT_WDELAY_MASK, - spi->chip_select); - set_fmt_bits(davinci_spi->base, - (spicfg->wdelay << SPIFMT_WDELAY_SHIFT) & - SPIFMT_WDELAY_MASK, spi->chip_select); - - if (spicfg->odd_parity) - set_fmt_bits(davinci_spi->base, SPIFMT_ODD_PARITY_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, - SPIFMT_ODD_PARITY_MASK, - spi->chip_select); - - if (spicfg->parity_enable) - set_fmt_bits(davinci_spi->base, SPIFMT_PARITYENA_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, SPIFMT_PARITYENA_MASK, - spi->chip_select); - - if (spicfg->timer_disable) - set_fmt_bits(davinci_spi->base, SPIFMT_DISTIMER_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, SPIFMT_DISTIMER_MASK, - spi->chip_select); - - if (spi->mode & SPI_READY) - set_fmt_bits(davinci_spi->base, - SPIFMT_WAITENA_MASK, - spi->chip_select); - else - clear_fmt_bits(davinci_spi->base, - SPIFMT_WAITENA_MASK, - spi->chip_select); - - } - retval = davinci_spi_setup_transfer(spi, NULL); return retval; -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:46 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:46 +0530 Subject: [PATCH 14/49] spi: davinci: setup chip-select timers values only if timer enabled In-Reply-To: <1289990661-30126-14-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-15-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Setup chip-select timers values only if timer is enabled (timer_disbled in spi configuration is false). As a nice side effect, this patch removes code duplicated in davinci_spi_bufs_pio() and davinci_spi_bufs_dma(). Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 21 ++++----------------- 1 files changed, 4 insertions(+), 17 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 34b28fe..d09b63c 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -374,6 +374,10 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, if (spicfg->timer_disable) spifmt |= SPIFMT_DISTIMER_MASK; + else + iowrite32((spicfg->c2tdelay << SPI_C2TDELAY_SHIFT) | + (spicfg->t2cdelay << SPI_T2CDELAY_SHIFT), + davinci_spi->base + SPIDELAY); if (spi->mode & SPI_READY) spifmt |= SPIFMT_WAITENA_MASK; @@ -607,13 +611,9 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) u32 tx_data, data1_reg_val; u32 buf_val, flg_val; struct davinci_spi_platform_data *pdata; - struct davinci_spi_config *spicfg; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; - spicfg = (struct davinci_spi_config *)spi->controller_data; - if (!spicfg) - spicfg = &davinci_spi_default_cfg; davinci_spi->tx = t->tx_buf; davinci_spi->rx = t->rx_buf; @@ -633,10 +633,6 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - iowrite32((spicfg->c2tdelay << SPI_C2TDELAY_SHIFT) | - (spicfg->t2cdelay << SPI_T2CDELAY_SHIFT), - davinci_spi->base + SPIDELAY); - count = davinci_spi->count; /* Determine the command to execute READ or WRITE */ @@ -741,14 +737,10 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) struct davinci_spi_dma *davinci_spi_dma; int word_len, data_type, ret; unsigned long tx_reg, rx_reg; - struct davinci_spi_config *spicfg; struct device *sdev; davinci_spi = spi_master_get_devdata(spi->master); sdev = davinci_spi->bitbang.master->dev.parent; - spicfg = (struct davinci_spi_config *)spi->controller_data; - if (!spicfg) - spicfg = &davinci_spi_default_cfg; davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; @@ -784,11 +776,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) if (ret) return ret; - /* Put delay val if required */ - iowrite32((spicfg->c2tdelay << SPI_C2TDELAY_SHIFT) | - (spicfg->t2cdelay << SPI_T2CDELAY_SHIFT), - davinci_spi->base + SPIDELAY); - count = davinci_spi->count; /* the number of elements */ /* disable all interrupts for dma transfers */ -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:52 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:52 +0530 Subject: [PATCH 20/49] spi: davinci: check for NULL buffer pointer before using it In-Reply-To: <1289990661-30126-20-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-21-git-send-email-nsekhar@ti.com> From: Brian Niebuhr In the davinci_spi_{tx|rx}_u{8|16}() functions, check for buffer pointer being valid before using it. While providing for better error checking, this change will help simplify code in the caller. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 40 ++++++++++++++++++++++------------------ 1 files changed, 22 insertions(+), 18 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index b0b338f..10b0a08 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -158,37 +158,41 @@ static unsigned use_dma; static void davinci_spi_rx_buf_u8(u32 data, struct davinci_spi *davinci_spi) { - u8 *rx = davinci_spi->rx; - - *rx++ = (u8)data; - davinci_spi->rx = rx; + if (davinci_spi->rx) { + u8 *rx = davinci_spi->rx; + *rx++ = (u8)data; + davinci_spi->rx = rx; + } } static void davinci_spi_rx_buf_u16(u32 data, struct davinci_spi *davinci_spi) { - u16 *rx = davinci_spi->rx; - - *rx++ = (u16)data; - davinci_spi->rx = rx; + if (davinci_spi->rx) { + u16 *rx = davinci_spi->rx; + *rx++ = (u16)data; + davinci_spi->rx = rx; + } } static u32 davinci_spi_tx_buf_u8(struct davinci_spi *davinci_spi) { - u32 data; - const u8 *tx = davinci_spi->tx; - - data = *tx++; - davinci_spi->tx = tx; + u32 data = 0; + if (davinci_spi->tx) { + const u8 *tx = davinci_spi->tx; + data = *tx++; + davinci_spi->tx = tx; + } return data; } static u32 davinci_spi_tx_buf_u16(struct davinci_spi *davinci_spi) { - u32 data; - const u16 *tx = davinci_spi->tx; - - data = *tx++; - davinci_spi->tx = tx; + u32 data = 0; + if (davinci_spi->tx) { + const u16 *tx = davinci_spi->tx; + data = *tx++; + davinci_spi->tx = tx; + } return data; } -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:48 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:48 +0530 Subject: [PATCH 16/49] spi: davinci: remove unused members of davinci_spi_slave In-Reply-To: <1289990661-30126-16-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-17-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Several members of struct davinci_spi_slave are unused in code. Remove such members. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 5 ----- 1 files changed, 0 insertions(+), 5 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index e94c638..31b9c22 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -119,10 +119,7 @@ #define SPIFMT0 0x50 struct davinci_spi_slave { - u32 cmd_to_write; - u32 clk_ctrl_to_write; u32 bytes_per_word; - u8 active_cs; }; /* We have 2 DMA channels per CS, one for RX and one for TX */ @@ -495,8 +492,6 @@ static int davinci_spi_setup(struct spi_device *spi) if (!spi->bits_per_word) spi->bits_per_word = 8; - davinci_spi->slave[spi->chip_select].cmd_to_write = 0; - if (use_dma && davinci_spi->dma_channels) { davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:58 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:58 +0530 Subject: [PATCH 26/49] spi: davinci: simplify poll mode transfers In-Reply-To: <1289990661-30126-26-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-27-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Use the fact that the get_tx and get_rx can now cope with NULL buffer pointers to simplify the poll mode transfer code. While at it, check for SPI errors every transfer rather than at the end of the whole transfer. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 96 +++++++++++++++++++-------------------------- 1 files changed, 41 insertions(+), 55 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 198f062..cd37697 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -91,6 +91,10 @@ #define SPIFLG_BITERR_MASK BIT(4) #define SPIFLG_OVRRUN_MASK BIT(6) #define SPIFLG_BUF_INIT_ACTIVE_MASK BIT(24) +#define SPIFLG_ERROR_MASK (SPIFLG_DLEN_ERR_MASK \ + | SPIFLG_TIMEOUT_MASK | SPIFLG_PARERR_MASK \ + | SPIFLG_DESYNC_MASK | SPIFLG_BITERR_MASK \ + | SPIFLG_OVRRUN_MASK) #define SPIINT_DMA_REQ_EN BIT(16) @@ -601,10 +605,10 @@ static int davinci_spi_check_error(struct davinci_spi *davinci_spi, static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; - int status, count, ret; - u8 conv; + int ret; + int rcount, wcount; u32 tx_data, data1_reg_val; - u32 buf_val, flg_val; + u32 errors = 0; struct davinci_spi_platform_data *pdata; davinci_spi = spi_master_get_devdata(spi->master); @@ -612,70 +616,51 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) davinci_spi->tx = t->tx_buf; davinci_spi->rx = t->rx_buf; - - /* convert len to words based on bits_per_word */ - conv = davinci_spi->bytes_per_word[spi->chip_select]; - data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); + wcount = t->len / davinci_spi->bytes_per_word[spi->chip_select]; + rcount = wcount; ret = davinci_spi_bufs_prep(spi, davinci_spi); if (ret) return ret; + data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); + /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - count = t->len / conv; - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); - /* Determine the command to execute READ or WRITE */ - if (t->tx_buf) { + /* start the transfer */ + wcount--; + tx_data = davinci_spi->get_tx(davinci_spi); + data1_reg_val &= 0xFFFF0000; + data1_reg_val |= tx_data & 0xFFFF; + iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); - while (1) { - tx_data = davinci_spi->get_tx(davinci_spi); + while (rcount > 0 || wcount > 0) { - data1_reg_val &= ~(0xFFFF); - data1_reg_val |= (0xFFFF & tx_data); - - buf_val = ioread32(davinci_spi->base + SPIBUF); - if ((buf_val & SPIBUF_TXFULL_MASK) == 0) { - iowrite32(data1_reg_val, - davinci_spi->base + SPIDAT1); - - count--; - } - while (ioread32(davinci_spi->base + SPIBUF) - & SPIBUF_RXEMPTY_MASK) - cpu_relax(); - - /* getting the returned byte */ - if (t->rx_buf) { - buf_val = ioread32(davinci_spi->base + SPIBUF); - davinci_spi->get_rx(buf_val, davinci_spi); - } - if (count <= 0) - break; - } - } else { - while (1) { - /* keeps the serial clock going */ - if ((ioread32(davinci_spi->base + SPIBUF) - & SPIBUF_TXFULL_MASK) == 0) - iowrite32(data1_reg_val, - davinci_spi->base + SPIDAT1); + u32 buf, status; - while (ioread32(davinci_spi->base + SPIBUF) & - SPIBUF_RXEMPTY_MASK) - cpu_relax(); + buf = ioread32(davinci_spi->base + SPIBUF); - flg_val = ioread32(davinci_spi->base + SPIFLG); - buf_val = ioread32(davinci_spi->base + SPIBUF); + if (!(buf & SPIBUF_RXEMPTY_MASK)) { + davinci_spi->get_rx(buf & 0xFFFF, davinci_spi); + rcount--; + } - davinci_spi->get_rx(buf_val, davinci_spi); + status = ioread32(davinci_spi->base + SPIFLG); - count--; - if (count <= 0) - break; + if (unlikely(status & SPIFLG_ERROR_MASK)) { + errors = status & SPIFLG_ERROR_MASK; + break; + } + + if (wcount > 0 && !(buf & SPIBUF_TXFULL_MASK)) { + wcount--; + tx_data = davinci_spi->get_tx(davinci_spi); + data1_reg_val &= ~0xFFFF; + data1_reg_val |= 0xFFFF & tx_data; + iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); } } @@ -683,11 +668,12 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) * Check for bit error, desync error,parity error,timeout error and * receive overflow errors */ - status = ioread32(davinci_spi->base + SPIFLG); - - ret = davinci_spi_check_error(davinci_spi, status); - if (ret != 0) + if (errors) { + ret = davinci_spi_check_error(davinci_spi, errors); + WARN(!ret, "%s: error reported but no error found!\n", + dev_name(&spi->dev)); return ret; + } return t->len; } -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:53 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:53 +0530 Subject: [PATCH 21/49] spi: davinci: remove unnecessary disable of SPI In-Reply-To: <1289990661-30126-21-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-22-git-send-email-nsekhar@ti.com> From: Brian Niebuhr In the davinci_spi_bufs_dma() function, SPI is briefly disabled before enabling it immediately back again. Remove this unnecessary disable. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 2 -- 1 files changed, 0 insertions(+), 2 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 10b0a08..1169e8e 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -771,8 +771,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) /* disable all interrupts for dma transfers */ clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); - /* Disable SPI to write configuration bits in SPIDAT */ - clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:55 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:55 +0530 Subject: [PATCH 23/49] spi: davinci: do not treat Tx interrupt being set as error In-Reply-To: <1289990661-30126-23-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-24-git-send-email-nsekhar@ti.com> In davinci_spi_check_error(), Tx interrupt being set is treated as error. This function is only meant to flag bus error conditions and Tx interrupt being set at that point is not a bus error but rather a driver bug. Stop checking for Tx interrupt and flagging that as an IO error. Tested-By: Michael Williamson Tested-By: Brian Niebuhr Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 4 ---- 1 files changed, 0 insertions(+), 4 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index ad814f2..a60a65c 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -590,10 +590,6 @@ static int davinci_spi_check_error(struct davinci_spi *davinci_spi, dev_dbg(sdev, "SPI Data Overrun error\n"); return -EIO; } - if (int_status & SPIFLG_TX_INTR_MASK) { - dev_dbg(sdev, "SPI TX intr bit set\n"); - return -EIO; - } if (int_status & SPIFLG_BUF_INIT_ACTIVE_MASK) { dev_dbg(sdev, "SPI Buffer Init Active\n"); return -EBUSY; -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:56 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:56 +0530 Subject: [PATCH 24/49] spi: davinci: remove unnecessary completion variable initialization In-Reply-To: <1289990661-30126-24-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-25-git-send-email-nsekhar@ti.com> From: Brian Niebuhr The completion variable 'done' is unnecessarly initialized by the function davinci_spi_bufs_dma() where as it is not used for DMA transfers at all. Remove the unnecessary initialization. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 2 -- 1 files changed, 0 insertions(+), 2 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index a60a65c..54d06f4 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -750,8 +750,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); - INIT_COMPLETION(davinci_spi->done); - init_completion(&davinci_spi_dma->dma_rx_completion); init_completion(&davinci_spi_dma->dma_tx_completion); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:49 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:49 +0530 Subject: [PATCH 17/49] spi: davinci: eliminate the single member structure davinci_spi_slave In-Reply-To: <1289990661-30126-17-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-18-git-send-email-nsekhar@ti.com> From: Brian Niebuhr The struct davinci_spi_slave has a single member. Eliminate it and store the per-chipselect data in struct davinci_spi directly. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 14 +++++--------- 1 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 31b9c22..3dac2038 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -118,10 +118,6 @@ #define SPIDEF 0x4c #define SPIFMT0 0x50 -struct davinci_spi_slave { - u32 bytes_per_word; -}; - /* We have 2 DMA channels per CS, one for RX and one for TX */ struct davinci_spi_dma { int dma_tx_channel; @@ -156,7 +152,7 @@ struct davinci_spi { void (*get_rx)(u32 rx_data, struct davinci_spi *); u32 (*get_tx)(struct davinci_spi *); - struct davinci_spi_slave slave[SPI_MAX_CHIPSELECT]; + u8 bytes_per_word[SPI_MAX_CHIPSELECT]; }; static struct davinci_spi_config davinci_spi_default_cfg; @@ -324,11 +320,11 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, if (bits_per_word <= 8 && bits_per_word >= 2) { davinci_spi->get_rx = davinci_spi_rx_buf_u8; davinci_spi->get_tx = davinci_spi_tx_buf_u8; - davinci_spi->slave[spi->chip_select].bytes_per_word = 1; + davinci_spi->bytes_per_word[spi->chip_select] = 1; } else if (bits_per_word <= 16 && bits_per_word >= 2) { davinci_spi->get_rx = davinci_spi_rx_buf_u16; davinci_spi->get_tx = davinci_spi_tx_buf_u16; - davinci_spi->slave[spi->chip_select].bytes_per_word = 2; + davinci_spi->bytes_per_word[spi->chip_select] = 2; } else return -EINVAL; @@ -632,7 +628,7 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) davinci_spi->rx = t->rx_buf; /* convert len to words based on bits_per_word */ - conv = davinci_spi->slave[spi->chip_select].bytes_per_word; + conv = davinci_spi->bytes_per_word[spi->chip_select]; davinci_spi->count = t->len / conv; data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); @@ -764,7 +760,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) davinci_spi->rx = t->rx_buf; /* convert len to words based on bits_per_word */ - conv = davinci_spi->slave[spi->chip_select].bytes_per_word; + conv = davinci_spi->bytes_per_word[spi->chip_select]; davinci_spi->count = t->len / conv; data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:50 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:50 +0530 Subject: [PATCH 18/49] spi: davinci: eliminate unnecessary update of davinci_spi->count In-Reply-To: <1289990661-30126-18-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-19-git-send-email-nsekhar@ti.com> From: Brian Niebuhr The count member of davinci_spi is internal to the driver and is not shared with framework. Eliminate its unnecessary update. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 6 ------ 1 files changed, 0 insertions(+), 6 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 3dac2038..f1c3502 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -726,9 +726,6 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) if (ret != 0) return ret; - /* SPI Framework maintains the count only in bytes so convert back */ - davinci_spi->count *= conv; - return t->len; } @@ -880,9 +877,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) if (ret != 0) return ret; - /* SPI Framework maintains the count only in bytes so convert back */ - davinci_spi->count *= conv; - return t->len; } -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:51 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:51 +0530 Subject: [PATCH 19/49] spi: davinci: simplify calculation of edma acount value In-Reply-To: <1289990661-30126-19-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-20-git-send-email-nsekhar@ti.com> From: Brian Niebuhr The EDMA acount (called data_type in davinci_spi_bufs_dma()) is simply the bytes_per_word obtained in the transfer setup function. The current code calculates the acount value from bytes_per_word in a convoluted manner. Simplify the code. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 25 +++---------------------- 1 files changed, 3 insertions(+), 22 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index f1c3502..b0b338f 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -39,9 +39,6 @@ #define CS_DEFAULT 0xFF #define SPI_BUFSIZ (SMP_CACHE_BYTES + 1) -#define DAVINCI_DMA_DATA_TYPE_S8 0x01 -#define DAVINCI_DMA_DATA_TYPE_S16 0x02 -#define DAVINCI_DMA_DATA_TYPE_S32 0x04 #define SPIFMT_PHASE_MASK BIT(16) #define SPIFMT_POLARITY_MASK BIT(17) @@ -729,19 +726,14 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) return t->len; } -#define DAVINCI_DMA_DATA_TYPE_S8 0x01 -#define DAVINCI_DMA_DATA_TYPE_S16 0x02 -#define DAVINCI_DMA_DATA_TYPE_S32 0x04 - static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; int int_status = 0; int count, temp_count; - u8 conv = 1; u32 data1_reg_val; struct davinci_spi_dma *davinci_spi_dma; - int word_len, data_type, ret; + int data_type, ret; unsigned long tx_reg, rx_reg; struct device *sdev; @@ -757,8 +749,8 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) davinci_spi->rx = t->rx_buf; /* convert len to words based on bits_per_word */ - conv = davinci_spi->bytes_per_word[spi->chip_select]; - davinci_spi->count = t->len / conv; + data_type = davinci_spi->bytes_per_word[spi->chip_select]; + davinci_spi->count = t->len / data_type; data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); @@ -767,17 +759,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) init_completion(&davinci_spi_dma->dma_rx_completion); init_completion(&davinci_spi_dma->dma_tx_completion); - word_len = conv * 8; - - if (word_len <= 8) - data_type = DAVINCI_DMA_DATA_TYPE_S8; - else if (word_len <= 16) - data_type = DAVINCI_DMA_DATA_TYPE_S16; - else if (word_len <= 32) - data_type = DAVINCI_DMA_DATA_TYPE_S32; - else - return -EINVAL; - ret = davinci_spi_bufs_prep(spi, davinci_spi); if (ret) return ret; -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:57 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:57 +0530 Subject: [PATCH 25/49] spi: davinci: remove non-useful interrupt mode support In-Reply-To: <1289990661-30126-25-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-26-git-send-email-nsekhar@ti.com> From: Brian Niebuhr The interrupt mode support as it stands is another version of poll mode. Even when interrupt mode is selected, the code tight loops on interrupt status register, rendering it totally useless. A completion variable is initialized, but never used. Remove this fake interrupt mode since users can anyway use poll mode with no functional difference. A usefully implemented interrupt mode support can be added later. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/dm355.c | 1 - arch/arm/mach-davinci/dm365.c | 1 - arch/arm/mach-davinci/include/mach/spi.h | 2 - drivers/spi/davinci_spi.c | 126 +++++------------------------- 4 files changed, 19 insertions(+), 111 deletions(-) diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index a6d9b72..e311f29 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -413,7 +413,6 @@ static struct davinci_spi_platform_data dm355_spi0_pdata = { .version = SPI_VERSION_1, .num_chipselect = 2, .clk_internal = 1, - .poll_mode = 1, /* 0 -> interrupt mode 1-> polling mode */ }; static struct platform_device dm355_spi0_device = { .name = "spi_davinci", diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 80dd159..1e5012e 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -626,7 +626,6 @@ static struct davinci_spi_platform_data dm365_spi0_pdata = { .version = SPI_VERSION_1, .num_chipselect = 2, .clk_internal = 1, - .poll_mode = 1, /* 0 -> interrupt mode 1-> polling mode */ }; static struct resource dm365_spi0_resources[] = { diff --git a/arch/arm/mach-davinci/include/mach/spi.h b/arch/arm/mach-davinci/include/mach/spi.h index 483b055..e68afe2 100644 --- a/arch/arm/mach-davinci/include/mach/spi.h +++ b/arch/arm/mach-davinci/include/mach/spi.h @@ -30,8 +30,6 @@ struct davinci_spi_platform_data { u8 version; u8 num_chipselect; u8 clk_internal; - u8 intr_level; - u8 poll_mode; u8 use_dma; u8 *chip_sel; }; diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 54d06f4..198f062 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -59,8 +59,6 @@ #define SPIPC0_SPIENA_MASK BIT(8) /* nREADY */ #define SPIINT_MASKALL 0x0101035F -#define SPI_INTLVL_1 0x000001FFu -#define SPI_INTLVL_0 0x00000000u /* SPIDAT1 (upper 16 bit defines) */ #define SPIDAT1_CSHOLD_MASK BIT(12) @@ -92,14 +90,8 @@ #define SPIFLG_DESYNC_MASK BIT(3) #define SPIFLG_BITERR_MASK BIT(4) #define SPIFLG_OVRRUN_MASK BIT(6) -#define SPIFLG_RX_INTR_MASK BIT(8) -#define SPIFLG_TX_INTR_MASK BIT(9) #define SPIFLG_BUF_INIT_ACTIVE_MASK BIT(24) -#define SPIINT_BITERR_INTR BIT(4) -#define SPIINT_OVRRUN_INTR BIT(6) -#define SPIINT_RX_INTR BIT(8) -#define SPIINT_TX_INTR BIT(9) #define SPIINT_DMA_REQ_EN BIT(16) /* SPI Controller registers */ @@ -136,8 +128,6 @@ struct davinci_spi { resource_size_t pbase; void __iomem *base; size_t region_size; - u32 irq; - struct completion done; const void *tx; void *rx; @@ -611,7 +601,7 @@ static int davinci_spi_check_error(struct davinci_spi *davinci_spi, static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; - int int_status, count, ret; + int status, count, ret; u8 conv; u32 tx_data, data1_reg_val; u32 buf_val, flg_val; @@ -627,8 +617,6 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) conv = davinci_spi->bytes_per_word[spi->chip_select]; data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); - INIT_COMPLETION(davinci_spi->done); - ret = davinci_spi_bufs_prep(spi, davinci_spi); if (ret) return ret; @@ -638,9 +626,10 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) count = t->len / conv; + clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); + /* Determine the command to execute READ or WRITE */ if (t->tx_buf) { - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); while (1) { tx_data = davinci_spi->get_tx(davinci_spi); @@ -668,45 +657,25 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) break; } } else { - if (pdata->poll_mode) { - while (1) { - /* keeps the serial clock going */ - if ((ioread32(davinci_spi->base + SPIBUF) - & SPIBUF_TXFULL_MASK) == 0) - iowrite32(data1_reg_val, - davinci_spi->base + SPIDAT1); + while (1) { + /* keeps the serial clock going */ + if ((ioread32(davinci_spi->base + SPIBUF) + & SPIBUF_TXFULL_MASK) == 0) + iowrite32(data1_reg_val, + davinci_spi->base + SPIDAT1); while (ioread32(davinci_spi->base + SPIBUF) & - SPIBUF_RXEMPTY_MASK) + SPIBUF_RXEMPTY_MASK) cpu_relax(); - flg_val = ioread32(davinci_spi->base + SPIFLG); - buf_val = ioread32(davinci_spi->base + SPIBUF); - - davinci_spi->get_rx(buf_val, davinci_spi); - - count--; - if (count <= 0) - break; - } - } else { /* Receive in Interrupt mode */ - int i; - - for (i = 0; i < count; i++) { - set_io_bits(davinci_spi->base + SPIINT, - SPIINT_BITERR_INTR - | SPIINT_OVRRUN_INTR - | SPIINT_RX_INTR); + flg_val = ioread32(davinci_spi->base + SPIFLG); + buf_val = ioread32(davinci_spi->base + SPIBUF); - iowrite32(data1_reg_val, - davinci_spi->base + SPIDAT1); + davinci_spi->get_rx(buf_val, davinci_spi); - while (ioread32(davinci_spi->base + SPIINT) & - SPIINT_RX_INTR) - cpu_relax(); - } - iowrite32((data1_reg_val & 0x0ffcffff), - davinci_spi->base + SPIDAT1); + count--; + if (count <= 0) + break; } } @@ -714,9 +683,9 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) * Check for bit error, desync error,parity error,timeout error and * receive overflow errors */ - int_status = ioread32(davinci_spi->base + SPIFLG); + status = ioread32(davinci_spi->base + SPIFLG); - ret = davinci_spi_check_error(davinci_spi, int_status); + ret = davinci_spi_check_error(davinci_spi, status); if (ret != 0) return ret; @@ -854,38 +823,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) } /** - * davinci_spi_irq - IRQ handler for DaVinci SPI - * @irq: IRQ number for this SPI Master - * @context_data: structure for SPI Master controller davinci_spi - */ -static irqreturn_t davinci_spi_irq(s32 irq, void *context_data) -{ - struct davinci_spi *davinci_spi = context_data; - u32 int_status, rx_data = 0; - irqreturn_t ret = IRQ_NONE; - - int_status = ioread32(davinci_spi->base + SPIFLG); - - while ((int_status & SPIFLG_RX_INTR_MASK)) { - if (likely(int_status & SPIFLG_RX_INTR_MASK)) { - ret = IRQ_HANDLED; - - rx_data = ioread32(davinci_spi->base + SPIBUF); - davinci_spi->get_rx(rx_data, davinci_spi); - - /* Disable Receive Interrupt */ - iowrite32(~(SPIINT_RX_INTR | SPIINT_TX_INTR), - davinci_spi->base + SPIINT); - } else - (void)davinci_spi_check_error(davinci_spi, int_status); - - int_status = ioread32(davinci_spi->base + SPIFLG); - } - - return ret; -} - -/** * davinci_spi_probe - probe function for SPI Master Controller * @pdev: platform_device structure which contains plateform specific data */ @@ -943,22 +880,11 @@ static int davinci_spi_probe(struct platform_device *pdev) goto release_region; } - davinci_spi->irq = platform_get_irq(pdev, 0); - if (davinci_spi->irq <= 0) { - ret = -EINVAL; - goto unmap_io; - } - - ret = request_irq(davinci_spi->irq, davinci_spi_irq, IRQF_DISABLED, - dev_name(&pdev->dev), davinci_spi); - if (ret) - goto unmap_io; - /* Allocate tmp_buf for tx_buf */ davinci_spi->tmp_buf = kzalloc(SPI_BUFSIZ, GFP_KERNEL); if (davinci_spi->tmp_buf == NULL) { ret = -ENOMEM; - goto irq_free; + goto unmap_io; } davinci_spi->bitbang.master = spi_master_get(master); @@ -1034,8 +960,6 @@ static int davinci_spi_probe(struct platform_device *pdev) davinci_spi->get_rx = davinci_spi_rx_buf_u8; davinci_spi->get_tx = davinci_spi_tx_buf_u8; - init_completion(&davinci_spi->done); - /* Reset In/OUT SPI module */ iowrite32(0, davinci_spi->base + SPIGCR0); udelay(100); @@ -1062,21 +986,12 @@ static int davinci_spi_probe(struct platform_device *pdev) /* master mode default */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_MASTER_MASK); - if (davinci_spi->pdata->intr_level) - iowrite32(SPI_INTLVL_1, davinci_spi->base + SPILVL); - else - iowrite32(SPI_INTLVL_0, davinci_spi->base + SPILVL); - ret = spi_bitbang_start(&davinci_spi->bitbang); if (ret) goto free_clk; dev_info(&pdev->dev, "Controller at 0x%p\n", davinci_spi->base); - if (!pdata->poll_mode) - dev_info(&pdev->dev, "Operating in interrupt mode" - " using IRQ %d\n", davinci_spi->irq); - return ret; free_clk: @@ -1086,8 +1001,6 @@ put_master: spi_master_put(master); free_tmp_buf: kfree(davinci_spi->tmp_buf); -irq_free: - free_irq(davinci_spi->irq, davinci_spi); unmap_io: iounmap(davinci_spi->base); release_region: @@ -1121,7 +1034,6 @@ static int __exit davinci_spi_remove(struct platform_device *pdev) clk_put(davinci_spi->clk); spi_master_put(master); kfree(davinci_spi->tmp_buf); - free_irq(davinci_spi->irq, davinci_spi); iounmap(davinci_spi->base); release_mem_region(davinci_spi->pbase, davinci_spi->region_size); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:00 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:00 +0530 Subject: [PATCH 28/49] spi: davinci: configure the invariable bits in spipc0 only once In-Reply-To: <1289990661-30126-28-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-29-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Configure the data-in, data-out and clock functionality pins in SPIPC0 register only once during probe. No need to set these bits for each transfer. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 8 +++++--- 1 files changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 45cc1a7..6f279c5 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -536,9 +536,6 @@ static int davinci_spi_bufs_prep(struct spi_device *spi, * optimize for both flags staying cleared. */ - op_mode = SPIPC0_DIFUN_MASK - | SPIPC0_DOFUN_MASK - | SPIPC0_CLKFUN_MASK; if (!(spi->mode & SPI_NO_CS)) { pdata = davinci_spi->pdata; if (!pdata->chip_sel || @@ -886,6 +883,7 @@ static int davinci_spi_probe(struct platform_device *pdev) resource_size_t dma_tx_chan = SPI_NO_RESOURCE; resource_size_t dma_eventq = SPI_NO_RESOURCE; int i = 0, ret = 0; + u32 spipc0; pdata = pdev->dev.platform_data; if (pdata == NULL) { @@ -1028,6 +1026,10 @@ static int davinci_spi_probe(struct platform_device *pdev) udelay(100); iowrite32(1, davinci_spi->base + SPIGCR0); + /* Set up SPIPC0. CS and ENA init is done in davinci_spi_bufs_prep */ + spipc0 = SPIPC0_DIFUN_MASK | SPIPC0_DOFUN_MASK | SPIPC0_CLKFUN_MASK; + iowrite32(spipc0, davinci_spi->base + SPIPC0); + /* initialize chip selects */ if (pdata->chip_sel) { for (i = 0; i < pdata->num_chipselect; i++) { -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:59 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:59 +0530 Subject: [PATCH 27/49] spi: davinci: add support for interrupt mode In-Reply-To: <1289990661-30126-27-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-28-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Add support for SPI interrupt mode operation. Define a per chip-select "io type" variable which specifies if the transfers on this chip-select should happen in interrupt mode or polled mode. Introduce a new function davinci_spi_process_events() to help consolidate the code between interrupt mode processing and polled mode processing. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/include/mach/spi.h | 4 + drivers/spi/davinci_spi.c | 145 +++++++++++++++++++++++------ 2 files changed, 119 insertions(+), 30 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/spi.h b/arch/arm/mach-davinci/include/mach/spi.h index e68afe2..ab45b89 100644 --- a/arch/arm/mach-davinci/include/mach/spi.h +++ b/arch/arm/mach-davinci/include/mach/spi.h @@ -30,6 +30,7 @@ struct davinci_spi_platform_data { u8 version; u8 num_chipselect; u8 clk_internal; + u8 intr_line; u8 use_dma; u8 *chip_sel; }; @@ -38,6 +39,9 @@ struct davinci_spi_config { u8 wdelay; u8 odd_parity; u8 parity_enable; +#define SPI_IO_TYPE_INTR 0 +#define SPI_IO_TYPE_POLL 1 + u8 io_type; u8 timer_disable; u8 c2tdelay; u8 t2cdelay; diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index cd37697..45cc1a7 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -59,6 +59,9 @@ #define SPIPC0_SPIENA_MASK BIT(8) /* nREADY */ #define SPIINT_MASKALL 0x0101035F +#define SPIINT_MASKINT 0x0000015F +#define SPI_INTLVL_1 0x000001FF +#define SPI_INTLVL_0 0x00000000 /* SPIDAT1 (upper 16 bit defines) */ #define SPIDAT1_CSHOLD_MASK BIT(12) @@ -132,10 +135,14 @@ struct davinci_spi { resource_size_t pbase; void __iomem *base; size_t region_size; + u32 irq; + struct completion done; const void *tx; void *rx; u8 *tmp_buf; + int rcount; + int wcount; struct davinci_spi_dma *dma_channels; struct davinci_spi_platform_data *pdata; @@ -594,6 +601,43 @@ static int davinci_spi_check_error(struct davinci_spi *davinci_spi, } /** + * davinci_spi_process_events - check for and handle any SPI controller events + * @davinci_spi: the controller data + * + * This function will check the SPIFLG register and handle any events that are + * detected there + */ +static int davinci_spi_process_events(struct davinci_spi *davinci_spi) +{ + u32 buf, status, errors = 0, data1_reg_val; + + buf = ioread32(davinci_spi->base + SPIBUF); + + if (davinci_spi->rcount > 0 && !(buf & SPIBUF_RXEMPTY_MASK)) { + davinci_spi->get_rx(buf & 0xFFFF, davinci_spi); + davinci_spi->rcount--; + } + + status = ioread32(davinci_spi->base + SPIFLG); + + if (unlikely(status & SPIFLG_ERROR_MASK)) { + errors = status & SPIFLG_ERROR_MASK; + goto out; + } + + if (davinci_spi->wcount > 0 && !(buf & SPIBUF_TXFULL_MASK)) { + data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); + davinci_spi->wcount--; + data1_reg_val &= ~0xFFFF; + data1_reg_val |= 0xFFFF & davinci_spi->get_tx(davinci_spi); + iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); + } + +out: + return errors; +} + +/** * davinci_spi_bufs - functions which will handle transfer data * @spi: spi device on which data transfer to be done * @t: spi transfer in which transfer info is filled @@ -606,18 +650,22 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; int ret; - int rcount, wcount; u32 tx_data, data1_reg_val; u32 errors = 0; + struct davinci_spi_config *spicfg; struct davinci_spi_platform_data *pdata; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; + spicfg = (struct davinci_spi_config *)spi->controller_data; + if (!spicfg) + spicfg = &davinci_spi_default_cfg; davinci_spi->tx = t->tx_buf; davinci_spi->rx = t->rx_buf; - wcount = t->len / davinci_spi->bytes_per_word[spi->chip_select]; - rcount = wcount; + davinci_spi->wcount = t->len / + davinci_spi->bytes_per_word[spi->chip_select]; + davinci_spi->rcount = davinci_spi->wcount; ret = davinci_spi_bufs_prep(spi, davinci_spi); if (ret) @@ -628,42 +676,32 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); + if (spicfg->io_type == SPI_IO_TYPE_INTR) { + set_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKINT); + INIT_COMPLETION(davinci_spi->done); + } /* start the transfer */ - wcount--; + davinci_spi->wcount--; tx_data = davinci_spi->get_tx(davinci_spi); data1_reg_val &= 0xFFFF0000; data1_reg_val |= tx_data & 0xFFFF; iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); - while (rcount > 0 || wcount > 0) { - - u32 buf, status; - - buf = ioread32(davinci_spi->base + SPIBUF); - - if (!(buf & SPIBUF_RXEMPTY_MASK)) { - davinci_spi->get_rx(buf & 0xFFFF, davinci_spi); - rcount--; - } - - status = ioread32(davinci_spi->base + SPIFLG); - - if (unlikely(status & SPIFLG_ERROR_MASK)) { - errors = status & SPIFLG_ERROR_MASK; - break; - } - - if (wcount > 0 && !(buf & SPIBUF_TXFULL_MASK)) { - wcount--; - tx_data = davinci_spi->get_tx(davinci_spi); - data1_reg_val &= ~0xFFFF; - data1_reg_val |= 0xFFFF & tx_data; - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); + /* Wait for the transfer to complete */ + if (spicfg->io_type == SPI_IO_TYPE_INTR) { + wait_for_completion_interruptible(&(davinci_spi->done)); + } else { + while (davinci_spi->rcount > 0 || davinci_spi->wcount > 0) { + errors = davinci_spi_process_events(davinci_spi); + if (errors) + break; + cpu_relax(); } } + clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); + /* * Check for bit error, desync error,parity error,timeout error and * receive overflow errors @@ -678,6 +716,32 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) return t->len; } +/** + * davinci_spi_irq - Interrupt handler for SPI Master Controller + * @irq: IRQ number for this SPI Master + * @context_data: structure for SPI Master controller davinci_spi + * + * ISR will determine that interrupt arrives either for READ or WRITE command. + * According to command it will do the appropriate action. It will check + * transfer length and if it is not zero then dispatch transfer command again. + * If transfer length is zero then it will indicate the COMPLETION so that + * davinci_spi_bufs function can go ahead. + */ +static irqreturn_t davinci_spi_irq(s32 irq, void *context_data) +{ + struct davinci_spi *davinci_spi = context_data; + int status; + + status = davinci_spi_process_events(davinci_spi); + if (unlikely(status != 0)) + clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKINT); + + if ((!davinci_spi->rcount && !davinci_spi->wcount) || status) + complete(&davinci_spi->done); + + return IRQ_HANDLED; +} + static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; @@ -866,11 +930,22 @@ static int davinci_spi_probe(struct platform_device *pdev) goto release_region; } + davinci_spi->irq = platform_get_irq(pdev, 0); + if (davinci_spi->irq <= 0) { + ret = -EINVAL; + goto unmap_io; + } + + ret = request_irq(davinci_spi->irq, davinci_spi_irq, 0, + dev_name(&pdev->dev), davinci_spi); + if (ret) + goto unmap_io; + /* Allocate tmp_buf for tx_buf */ davinci_spi->tmp_buf = kzalloc(SPI_BUFSIZ, GFP_KERNEL); if (davinci_spi->tmp_buf == NULL) { ret = -ENOMEM; - goto unmap_io; + goto irq_free; } davinci_spi->bitbang.master = spi_master_get(master); @@ -946,6 +1021,8 @@ static int davinci_spi_probe(struct platform_device *pdev) davinci_spi->get_rx = davinci_spi_rx_buf_u8; davinci_spi->get_tx = davinci_spi_tx_buf_u8; + init_completion(&davinci_spi->done); + /* Reset In/OUT SPI module */ iowrite32(0, davinci_spi->base + SPIGCR0); udelay(100); @@ -967,6 +1044,11 @@ static int davinci_spi_probe(struct platform_device *pdev) clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_CLKMOD_MASK); + if (pdata->intr_line) + iowrite32(SPI_INTLVL_1, davinci_spi->base + SPILVL); + else + iowrite32(SPI_INTLVL_0, davinci_spi->base + SPILVL); + iowrite32(CS_DEFAULT, davinci_spi->base + SPIDEF); /* master mode default */ @@ -987,6 +1069,8 @@ put_master: spi_master_put(master); free_tmp_buf: kfree(davinci_spi->tmp_buf); +irq_free: + free_irq(davinci_spi->irq, davinci_spi); unmap_io: iounmap(davinci_spi->base); release_region: @@ -1020,6 +1104,7 @@ static int __exit davinci_spi_remove(struct platform_device *pdev) clk_put(davinci_spi->clk); spi_master_put(master); kfree(davinci_spi->tmp_buf); + free_irq(davinci_spi->irq, davinci_spi); iounmap(davinci_spi->base); release_mem_region(davinci_spi->pbase, davinci_spi->region_size); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:03 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:03 +0530 Subject: [PATCH 31/49] spi: davinci: do not store DMA channel information per chip select In-Reply-To: <1289990661-30126-31-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-32-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Do not store DMA channel related information per chip-select since that information does not depend on the chip select. The same DMA channels can be used for transfers on all chip-selects since the transfer happens one-at-a-time. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 47 +++++++++++++++++--------------------------- 1 files changed, 18 insertions(+), 29 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 77109dc..4aa5221 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -143,7 +143,7 @@ struct davinci_spi { u8 *tmp_buf; int rcount; int wcount; - struct davinci_spi_dma *dma_channels; + struct davinci_spi_dma dma_channels; struct davinci_spi_platform_data *pdata; void (*get_rx)(u32 rx_data, struct davinci_spi *); @@ -407,7 +407,7 @@ static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data) struct davinci_spi_dma *davinci_spi_dma; davinci_spi = spi_master_get_devdata(spi->master); - davinci_spi_dma = &(davinci_spi->dma_channels[spi->chip_select]); + davinci_spi_dma = &davinci_spi->dma_channels; if (ch_status == DMA_COMPLETE) edma_stop(davinci_spi_dma->dma_rx_channel); @@ -426,7 +426,7 @@ static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) struct davinci_spi_dma *davinci_spi_dma; davinci_spi = spi_master_get_devdata(spi->master); - davinci_spi_dma = &(davinci_spi->dma_channels[spi->chip_select]); + davinci_spi_dma = &davinci_spi->dma_channels; if (ch_status == DMA_COMPLETE) edma_stop(davinci_spi_dma->dma_tx_channel); @@ -446,7 +446,7 @@ static int davinci_spi_request_dma(struct spi_device *spi) int r; davinci_spi = spi_master_get_devdata(spi->master); - davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; + davinci_spi_dma = &davinci_spi->dma_channels; sdev = davinci_spi->bitbang.master->dev.parent; r = edma_alloc_channel(davinci_spi_dma->dma_rx_sync_dev, @@ -509,8 +509,8 @@ static int davinci_spi_setup(struct spi_device *spi) clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_LOOPBACK_MASK); - if (use_dma && davinci_spi->dma_channels) { - davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; + if (use_dma) { + davinci_spi_dma = &davinci_spi->dma_channels; if ((davinci_spi_dma->dma_rx_channel == -1) || (davinci_spi_dma->dma_tx_channel == -1)) @@ -522,13 +522,11 @@ static int davinci_spi_setup(struct spi_device *spi) static void davinci_spi_cleanup(struct spi_device *spi) { - struct davinci_spi *davinci_spi = spi_master_get_devdata(spi->master); - struct davinci_spi_dma *davinci_spi_dma; - - davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; - - if (use_dma && davinci_spi->dma_channels) { - davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; + if (use_dma) { + struct davinci_spi *davinci_spi = + spi_master_get_devdata(spi->master); + struct davinci_spi_dma *davinci_spi_dma = + &davinci_spi->dma_channels; if ((davinci_spi_dma->dma_rx_channel != -1) && (davinci_spi_dma->dma_tx_channel != -1)) { @@ -730,7 +728,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) davinci_spi = spi_master_get_devdata(spi->master); sdev = davinci_spi->bitbang.master->dev.parent; - davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; + davinci_spi_dma = &davinci_spi->dma_channels; tx_reg = (unsigned long)davinci_spi->pbase + SPIDAT1; rx_reg = (unsigned long)davinci_spi->pbase + SPIBUF; @@ -967,22 +965,13 @@ static int davinci_spi_probe(struct platform_device *pdev) use_dma = 0; } else { davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_dma; - davinci_spi->dma_channels = kzalloc(master->num_chipselect - * sizeof(struct davinci_spi_dma), GFP_KERNEL); - if (davinci_spi->dma_channels == NULL) { - ret = -ENOMEM; - goto free_clk; - } - for (i = 0; i < master->num_chipselect; i++) { - davinci_spi->dma_channels[i].dma_rx_channel = -1; - davinci_spi->dma_channels[i].dma_rx_sync_dev = - dma_rx_chan; - davinci_spi->dma_channels[i].dma_tx_channel = -1; - davinci_spi->dma_channels[i].dma_tx_sync_dev = - dma_tx_chan; - davinci_spi->dma_channels[i].eventq = dma_eventq; - } + davinci_spi->dma_channels.dma_rx_channel = -1; + davinci_spi->dma_channels.dma_rx_sync_dev = dma_rx_chan; + davinci_spi->dma_channels.dma_tx_channel = -1; + davinci_spi->dma_channels.dma_tx_sync_dev = dma_tx_chan; + davinci_spi->dma_channels.eventq = dma_eventq; + dev_info(&pdev->dev, "DaVinci SPI driver in EDMA mode\n" "Using RX channel = %d , TX channel = %d and " "event queue = %d", dma_rx_chan, dma_tx_chan, -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:06 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:06 +0530 Subject: [PATCH 34/49] spi: davinci: always start receive DMA In-Reply-To: <1289990661-30126-34-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-35-git-send-email-nsekhar@ti.com> From: Brian Niebuhr In keeping with the full duplex nature of the SPI bus. Always start receive DMA along with transmit DMA. If there is no receive buffer provided with the transfer, use a temporary buffer to receive the data to be thrown away. [michael.williamson at criticallink.com: receive DMA size should be same as transfer length to avoid hang-up when transfer length is smaller than temporary rx buffer size (rx buffer not provided)] Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 70 +++++++++++++++++++++++++++------------------ 1 files changed, 42 insertions(+), 28 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index f512939..705d006 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -138,6 +138,8 @@ struct davinci_spi { const void *tx; void *rx; +#define SPI_TMP_BUFSZ (SMP_CACHE_BYTES + 1) + u8 rx_tmp_buf[SPI_TMP_BUFSZ]; int rcount; int wcount; struct davinci_spi_dma dma_channels; @@ -716,10 +718,12 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) struct davinci_spi *davinci_spi; int int_status = 0; int count; + unsigned rx_buf_count; struct davinci_spi_dma *davinci_spi_dma; int data_type, ret; unsigned long tx_reg, rx_reg; struct davinci_spi_platform_data *pdata; + void *rx_buf; struct device *sdev; davinci_spi = spi_master_get_devdata(spi->master); @@ -778,50 +782,60 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) t->tx_buf ? data_type : 0, 0); edma_set_dest_index(davinci_spi_dma->dma_tx_channel, 0, 0); + /* + * Receive DMA setup + * + * If there is receive buffer, use it to receive data. If there + * is none provided, use a temporary receive buffer. Set the + * destination B index to 0 so effectively only one byte is used + * in the temporary buffer (address does not increment). + * + * The source of receive data is the receive data register. The + * source address never increments. + */ + if (t->rx_buf) { - t->rx_dma = dma_map_single(&spi->dev, (void *)t->rx_buf, count, - DMA_FROM_DEVICE); - if (dma_mapping_error(&spi->dev, t->rx_dma)) { - dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", - count); - if (t->tx_buf != NULL) - dma_unmap_single(NULL, t->tx_dma, - count, DMA_TO_DEVICE); - return -ENOMEM; - } - edma_set_transfer_params(davinci_spi_dma->dma_rx_channel, - data_type, count, 1, 0, ASYNC); - edma_set_src(davinci_spi_dma->dma_rx_channel, - rx_reg, INCR, W8BIT); - edma_set_dest(davinci_spi_dma->dma_rx_channel, - t->rx_dma, INCR, W8BIT); - edma_set_src_index(davinci_spi_dma->dma_rx_channel, 0, 0); - edma_set_dest_index(davinci_spi_dma->dma_rx_channel, - data_type, 0); + rx_buf = t->rx_buf; + rx_buf_count = count; + } else { + rx_buf = davinci_spi->rx_tmp_buf; + rx_buf_count = sizeof(davinci_spi->rx_tmp_buf); + } + + t->rx_dma = dma_map_single(&spi->dev, rx_buf, rx_buf_count, + DMA_FROM_DEVICE); + if (dma_mapping_error(&spi->dev, t->rx_dma)) { + dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", + rx_buf_count); + if (t->tx_buf) + dma_unmap_single(NULL, t->tx_dma, count, DMA_TO_DEVICE); + return -ENOMEM; } + edma_set_transfer_params(davinci_spi_dma->dma_rx_channel, data_type, + count, 1, 0, ASYNC); + edma_set_src(davinci_spi_dma->dma_rx_channel, rx_reg, INCR, W8BIT); + edma_set_dest(davinci_spi_dma->dma_rx_channel, t->rx_dma, INCR, W8BIT); + edma_set_src_index(davinci_spi_dma->dma_rx_channel, 0, 0); + edma_set_dest_index(davinci_spi_dma->dma_rx_channel, + t->rx_buf ? data_type : 0, 0); + if (pdata->cshold_bug) { u16 spidat1 = ioread16(davinci_spi->base + SPIDAT1 + 2); iowrite16(spidat1, davinci_spi->base + SPIDAT1 + 2); } - if (t->rx_buf) - edma_start(davinci_spi_dma->dma_rx_channel); - + edma_start(davinci_spi_dma->dma_rx_channel); edma_start(davinci_spi_dma->dma_tx_channel); davinci_spi_set_dma_req(spi, 1); wait_for_completion_interruptible(&davinci_spi_dma->dma_tx_completion); - - if (t->rx_buf) - wait_for_completion_interruptible( - &davinci_spi_dma->dma_rx_completion); + wait_for_completion_interruptible(&davinci_spi_dma->dma_rx_completion); if (t->tx_buf) dma_unmap_single(NULL, t->tx_dma, count, DMA_TO_DEVICE); - if (t->rx_buf) - dma_unmap_single(NULL, t->rx_dma, count, DMA_FROM_DEVICE); + dma_unmap_single(NULL, t->rx_dma, rx_buf_count, DMA_FROM_DEVICE); /* * Check for bit error, desync error,parity error,timeout error and -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:04 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:04 +0530 Subject: [PATCH 32/49] spi: davinci: always start transmit DMA In-Reply-To: <1289990661-30126-32-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-33-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Due to the full duplex nature of the SPI bus, the SPI master on DaVinci needs transmit to be active even if the tranfer is only meant to collect receive data. The current code achieves this by using a temporary zeroed buffer to provide DMA data in case the transfer does not have a transmit buffer provided. However, the transmit DMA is started only if transmit buffer is provided rendering the temporary buffer unused. Instead the code relies on a write to SPIDAT1 register to trigger transmit operation. This however only sends two bytes of data. Fix this by starting transmit DMA always. This changes exposes a bug on DM355 where the CSHOLD bit in SPIDAT1 needs to be written to in between transfers. Handle that by introducing a "cshold_bug" platform data which is set to true for DM355. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/dm355.c | 1 + arch/arm/mach-davinci/include/mach/spi.h | 1 + drivers/spi/davinci_spi.c | 22 +++++++++------------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index e311f29..27ee870 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -413,6 +413,7 @@ static struct davinci_spi_platform_data dm355_spi0_pdata = { .version = SPI_VERSION_1, .num_chipselect = 2, .clk_internal = 1, + .cshold_bug = true, }; static struct platform_device dm355_spi0_device = { .name = "spi_davinci", diff --git a/arch/arm/mach-davinci/include/mach/spi.h b/arch/arm/mach-davinci/include/mach/spi.h index ab45b89..68db6d5 100644 --- a/arch/arm/mach-davinci/include/mach/spi.h +++ b/arch/arm/mach-davinci/include/mach/spi.h @@ -33,6 +33,7 @@ struct davinci_spi_platform_data { u8 intr_line; u8 use_dma; u8 *chip_sel; + bool cshold_bug; }; struct davinci_spi_config { diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 4aa5221..a5f03dd 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -719,13 +719,14 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) struct davinci_spi *davinci_spi; int int_status = 0; int count, temp_count; - u32 data1_reg_val; struct davinci_spi_dma *davinci_spi_dma; int data_type, ret; unsigned long tx_reg, rx_reg; + struct davinci_spi_platform_data *pdata; struct device *sdev; davinci_spi = spi_master_get_devdata(spi->master); + pdata = davinci_spi->pdata; sdev = davinci_spi->bitbang.master->dev.parent; davinci_spi_dma = &davinci_spi->dma_channels; @@ -739,8 +740,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) /* convert len to words based on bits_per_word */ data_type = davinci_spi->bytes_per_word[spi->chip_select]; - data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); - init_completion(&davinci_spi_dma->dma_rx_completion); init_completion(&davinci_spi_dma->dma_tx_completion); @@ -781,9 +780,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) edma_set_dest_index(davinci_spi_dma->dma_tx_channel, 0, 0); if (t->rx_buf) { - /* initiate transaction */ - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); - t->rx_dma = dma_map_single(&spi->dev, (void *)t->rx_buf, count, DMA_FROM_DEVICE); if (dma_mapping_error(&spi->dev, t->rx_dma)) { @@ -805,18 +801,18 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) data_type, 0); } - if ((t->tx_buf) || (t->rx_buf)) - edma_start(davinci_spi_dma->dma_tx_channel); + if (pdata->cshold_bug) { + u16 spidat1 = ioread16(davinci_spi->base + SPIDAT1 + 2); + iowrite16(spidat1, davinci_spi->base + SPIDAT1 + 2); + } if (t->rx_buf) edma_start(davinci_spi_dma->dma_rx_channel); - if ((t->rx_buf) || (t->tx_buf)) - davinci_spi_set_dma_req(spi, 1); + edma_start(davinci_spi_dma->dma_tx_channel); + davinci_spi_set_dma_req(spi, 1); - if (t->tx_buf) - wait_for_completion_interruptible( - &davinci_spi_dma->dma_tx_completion); + wait_for_completion_interruptible(&davinci_spi_dma->dma_tx_completion); if (t->rx_buf) wait_for_completion_interruptible( -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:05 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:05 +0530 Subject: [PATCH 33/49] spi: davinci: do not use temporary buffer if no transmit data provided In-Reply-To: <1289990661-30126-33-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-34-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Remove usage of temporary buffer when no transmit data is provided. Instead, use the transmit register itself as the source of data. By choosing the transmit register itself as the source of data, this patch helps remove unnecessary accesses to memory when no real data is being transmitted. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 52 ++++++++++++++++++-------------------------- 1 files changed, 21 insertions(+), 31 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index a5f03dd..f512939 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -38,8 +38,6 @@ #define CS_DEFAULT 0xFF -#define SPI_BUFSIZ (SMP_CACHE_BYTES + 1) - #define SPIFMT_PHASE_MASK BIT(16) #define SPIFMT_POLARITY_MASK BIT(17) #define SPIFMT_DISTIMER_MASK BIT(18) @@ -140,7 +138,6 @@ struct davinci_spi { const void *tx; void *rx; - u8 *tmp_buf; int rcount; int wcount; struct davinci_spi_dma dma_channels; @@ -718,7 +715,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; int int_status = 0; - int count, temp_count; + int count; struct davinci_spi_dma *davinci_spi_dma; int data_type, ret; unsigned long tx_reg, rx_reg; @@ -750,6 +747,18 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); + /* + * Transmit DMA setup + * + * If there is transmit data, map the transmit buffer, set it as the + * source of data and set the source B index to data size. + * If there is no transmit data, set the transmit register as the + * source of data, and set the source B index to zero. + * + * The destination is always the transmit register itself. And the + * destination never increments. + */ + if (t->tx_buf) { t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, count, DMA_TO_DEVICE); @@ -758,25 +767,15 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) " TX buffer\n", count); return -ENOMEM; } - temp_count = count; - } else { - /* We need TX clocking for RX transaction */ - t->tx_dma = dma_map_single(&spi->dev, - (void *)davinci_spi->tmp_buf, count + 1, - DMA_TO_DEVICE); - if (dma_mapping_error(&spi->dev, t->tx_dma)) { - dev_dbg(sdev, "Unable to DMA map a %d bytes" - " TX tmp buffer\n", count); - return -ENOMEM; - } - temp_count = count + 1; } edma_set_transfer_params(davinci_spi_dma->dma_tx_channel, - data_type, temp_count, 1, 0, ASYNC); + data_type, count, 1, 0, ASYNC); edma_set_dest(davinci_spi_dma->dma_tx_channel, tx_reg, INCR, W8BIT); - edma_set_src(davinci_spi_dma->dma_tx_channel, t->tx_dma, INCR, W8BIT); - edma_set_src_index(davinci_spi_dma->dma_tx_channel, data_type, 0); + edma_set_src(davinci_spi_dma->dma_tx_channel, + t->tx_buf ? t->tx_dma : tx_reg, INCR, W8BIT); + edma_set_src_index(davinci_spi_dma->dma_tx_channel, + t->tx_buf ? data_type : 0, 0); edma_set_dest_index(davinci_spi_dma->dma_tx_channel, 0, 0); if (t->rx_buf) { @@ -818,7 +817,8 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) wait_for_completion_interruptible( &davinci_spi_dma->dma_rx_completion); - dma_unmap_single(NULL, t->tx_dma, temp_count, DMA_TO_DEVICE); + if (t->tx_buf) + dma_unmap_single(NULL, t->tx_dma, count, DMA_TO_DEVICE); if (t->rx_buf) dma_unmap_single(NULL, t->rx_dma, count, DMA_FROM_DEVICE); @@ -906,17 +906,10 @@ static int davinci_spi_probe(struct platform_device *pdev) if (ret) goto unmap_io; - /* Allocate tmp_buf for tx_buf */ - davinci_spi->tmp_buf = kzalloc(SPI_BUFSIZ, GFP_KERNEL); - if (davinci_spi->tmp_buf == NULL) { - ret = -ENOMEM; - goto irq_free; - } - davinci_spi->bitbang.master = spi_master_get(master); if (davinci_spi->bitbang.master == NULL) { ret = -ENODEV; - goto free_tmp_buf; + goto irq_free; } davinci_spi->clk = clk_get(&pdev->dev, NULL); @@ -1027,8 +1020,6 @@ free_clk: clk_put(davinci_spi->clk); put_master: spi_master_put(master); -free_tmp_buf: - kfree(davinci_spi->tmp_buf); irq_free: free_irq(davinci_spi->irq, davinci_spi); unmap_io: @@ -1063,7 +1054,6 @@ static int __exit davinci_spi_remove(struct platform_device *pdev) clk_disable(davinci_spi->clk); clk_put(davinci_spi->clk); spi_master_put(master); - kfree(davinci_spi->tmp_buf); free_irq(davinci_spi->irq, davinci_spi); iounmap(davinci_spi->base); release_mem_region(davinci_spi->pbase, davinci_spi->region_size); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:43:54 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:13:54 +0530 Subject: [PATCH 22/49] spi: davinci: remove unnecessary 'count' variable in driver private data In-Reply-To: <1289990661-30126-22-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-23-git-send-email-nsekhar@ti.com> From: Brian Niebuhr The variable count in DaVinci SPI driver's private data is largely unused and its minor use can easily be eliminated. Remove the variable. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 10 +++------- 1 files changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 1169e8e..ad814f2 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -142,7 +142,6 @@ struct davinci_spi { const void *tx; void *rx; u8 *tmp_buf; - int count; struct davinci_spi_dma *dma_channels; struct davinci_spi_platform_data *pdata; @@ -630,8 +629,6 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) /* convert len to words based on bits_per_word */ conv = davinci_spi->bytes_per_word[spi->chip_select]; - davinci_spi->count = t->len / conv; - data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); INIT_COMPLETION(davinci_spi->done); @@ -643,7 +640,7 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - count = davinci_spi->count; + count = t->len / conv; /* Determine the command to execute READ or WRITE */ if (t->tx_buf) { @@ -699,7 +696,7 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) } else { /* Receive in Interrupt mode */ int i; - for (i = 0; i < davinci_spi->count; i++) { + for (i = 0; i < count; i++) { set_io_bits(davinci_spi->base + SPIINT, SPIINT_BITERR_INTR | SPIINT_OVRRUN_INTR @@ -754,7 +751,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) /* convert len to words based on bits_per_word */ data_type = davinci_spi->bytes_per_word[spi->chip_select]; - davinci_spi->count = t->len / data_type; data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); @@ -767,7 +763,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) if (ret) return ret; - count = davinci_spi->count; /* the number of elements */ + count = t->len / data_type; /* the number of elements */ /* disable all interrupts for dma transfers */ clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:02 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:02 +0530 Subject: [PATCH 30/49] spi: davinci: remove unnecessary call to davinci_spi_setup_transfer() In-Reply-To: <1289990661-30126-30-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-31-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Remove unnecessary call to davinci_spi_setup_transfer() at the end of davinci_spi_setup(). davinci_spi_setup_transfer() is registered as the setup_transfer callback for the bitbang layer and is called independently by the bitbang layer to setup the transfer before it begins. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 11 +++-------- 1 files changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 05b6145..77109dc 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -479,7 +479,7 @@ static int davinci_spi_request_dma(struct spi_device *spi) */ static int davinci_spi_setup(struct spi_device *spi) { - int retval; + int retval = 0; struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; struct davinci_spi_platform_data *pdata; @@ -512,16 +512,11 @@ static int davinci_spi_setup(struct spi_device *spi) if (use_dma && davinci_spi->dma_channels) { davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; - if ((davinci_spi_dma->dma_rx_channel == -1) - || (davinci_spi_dma->dma_tx_channel == -1)) { + if ((davinci_spi_dma->dma_rx_channel == -1) || + (davinci_spi_dma->dma_tx_channel == -1)) retval = davinci_spi_request_dma(spi); - if (retval < 0) - return retval; - } } - retval = davinci_spi_setup_transfer(spi, NULL); - return retval; } -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:08 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:08 +0530 Subject: [PATCH 36/49] spi: davinci: fix DMA event generation stoppage In-Reply-To: <1289990661-30126-36-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> <1289990661-30126-35-git-send-email-nsekhar@ti.com> <1289990661-30126-36-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-37-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Do not stop SPI DMA event generation in either transmit or receive DMA event call back because the single setting affects both transmit and receive event generation. Depending on the order in which the callbacks happen, transmit or receive events can get unintentionally stalled. Instead, disable event generation once after both the transmit and receive DMA completes. While at it, remove the largely under-used function to set or clear DMA event generation. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 18 +++--------------- 1 files changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 67f1e46..9695f98 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -211,16 +211,6 @@ static inline void clear_io_bits(void __iomem *addr, u32 bits) iowrite32(v, addr); } -static void davinci_spi_set_dma_req(const struct spi_device *spi, int enable) -{ - struct davinci_spi *davinci_spi = spi_master_get_devdata(spi->master); - - if (enable) - set_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); - else - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); -} - /* * Interface to control the chip select signal */ @@ -414,8 +404,6 @@ static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data) edma_clean_channel(davinci_spi_dma->dma_rx_channel); complete(&davinci_spi_dma->dma_rx_completion); - /* We must disable the DMA RX request */ - davinci_spi_set_dma_req(spi, 0); } static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) @@ -433,8 +421,6 @@ static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) edma_clean_channel(davinci_spi_dma->dma_tx_channel); complete(&davinci_spi_dma->dma_tx_completion); - /* We must disable the DMA TX request */ - davinci_spi_set_dma_req(spi, 0); } static int davinci_spi_request_dma(struct spi_device *spi) @@ -831,7 +817,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) edma_start(davinci_spi_dma->dma_rx_channel); edma_start(davinci_spi_dma->dma_tx_channel); - davinci_spi_set_dma_req(spi, 1); + set_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); wait_for_completion_interruptible(&davinci_spi_dma->dma_tx_completion); wait_for_completion_interruptible(&davinci_spi_dma->dma_rx_completion); @@ -841,6 +827,8 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) dma_unmap_single(NULL, t->rx_dma, rx_buf_count, DMA_FROM_DEVICE); + clear_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); + /* * Check for bit error, desync error,parity error,timeout error and * receive overflow errors -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:01 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:01 +0530 Subject: [PATCH 29/49] spi: davinci: remove unnecessary function davinci_spi_bufs_prep() In-Reply-To: <1289990661-30126-29-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-30-git-send-email-nsekhar@ti.com> From: Brian Niebuhr The function davinci_spi_bufs_prep() is doing stuff that davinci_spi_setup() is doing. Eliminate it and move the work to davinci_spi_setup() Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 64 ++++++++++++++------------------------------ 1 files changed, 21 insertions(+), 43 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 6f279c5..05b6145 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -482,13 +482,33 @@ static int davinci_spi_setup(struct spi_device *spi) int retval; struct davinci_spi *davinci_spi; struct davinci_spi_dma *davinci_spi_dma; + struct davinci_spi_platform_data *pdata; davinci_spi = spi_master_get_devdata(spi->master); + pdata = davinci_spi->pdata; /* if bits per word length is zero then set it default 8 */ if (!spi->bits_per_word) spi->bits_per_word = 8; + if (!(spi->mode & SPI_NO_CS)) { + if ((pdata->chip_sel == NULL) || + (pdata->chip_sel[spi->chip_select] == SPI_INTERN_CS)) + set_io_bits(davinci_spi->base + SPIPC0, + 1 << spi->chip_select); + + } + + if (spi->mode & SPI_READY) + set_io_bits(davinci_spi->base + SPIPC0, SPIPC0_SPIENA_MASK); + + if (spi->mode & SPI_LOOP) + set_io_bits(davinci_spi->base + SPIGCR1, + SPIGCR1_LOOPBACK_MASK); + else + clear_io_bits(davinci_spi->base + SPIGCR1, + SPIGCR1_LOOPBACK_MASK); + if (use_dma && davinci_spi->dma_channels) { davinci_spi_dma = &davinci_spi->dma_channels[spi->chip_select]; @@ -523,40 +543,6 @@ static void davinci_spi_cleanup(struct spi_device *spi) } } -static int davinci_spi_bufs_prep(struct spi_device *spi, - struct davinci_spi *davinci_spi) -{ - struct davinci_spi_platform_data *pdata; - int op_mode = 0; - - /* - * REVISIT unless devices disagree about SPI_LOOP or - * SPI_READY (SPI_NO_CS only allows one device!), this - * should not need to be done before each message... - * optimize for both flags staying cleared. - */ - - if (!(spi->mode & SPI_NO_CS)) { - pdata = davinci_spi->pdata; - if (!pdata->chip_sel || - pdata->chip_sel[spi->chip_select] == SPI_INTERN_CS) - op_mode |= 1 << spi->chip_select; - } - if (spi->mode & SPI_READY) - op_mode |= SPIPC0_SPIENA_MASK; - - iowrite32(op_mode, davinci_spi->base + SPIPC0); - - if (spi->mode & SPI_LOOP) - set_io_bits(davinci_spi->base + SPIGCR1, - SPIGCR1_LOOPBACK_MASK); - else - clear_io_bits(davinci_spi->base + SPIGCR1, - SPIGCR1_LOOPBACK_MASK); - - return 0; -} - static int davinci_spi_check_error(struct davinci_spi *davinci_spi, int int_status) { @@ -664,10 +650,6 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) davinci_spi->bytes_per_word[spi->chip_select]; davinci_spi->rcount = davinci_spi->wcount; - ret = davinci_spi_bufs_prep(spi, davinci_spi); - if (ret) - return ret; - data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); /* Enable SPI */ @@ -769,10 +751,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) init_completion(&davinci_spi_dma->dma_rx_completion); init_completion(&davinci_spi_dma->dma_tx_completion); - ret = davinci_spi_bufs_prep(spi, davinci_spi); - if (ret) - return ret; - count = t->len / data_type; /* the number of elements */ /* disable all interrupts for dma transfers */ @@ -1026,7 +1004,7 @@ static int davinci_spi_probe(struct platform_device *pdev) udelay(100); iowrite32(1, davinci_spi->base + SPIGCR0); - /* Set up SPIPC0. CS and ENA init is done in davinci_spi_bufs_prep */ + /* Set up SPIPC0. CS and ENA init is done in davinci_spi_setup */ spipc0 = SPIPC0_DIFUN_MASK | SPIPC0_DOFUN_MASK | SPIPC0_CLKFUN_MASK; iowrite32(spipc0, davinci_spi->base + SPIPC0); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:13 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:13 +0530 Subject: [PATCH 41/49] spi: davinci: let DMA operation be specified on per-device basis In-Reply-To: <1289990661-30126-41-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> <1289990661-30126-35-git-send-email-nsekhar@ti.com> <1289990661-30126-36-git-send-email-nsekhar@ti.com> <1289990661-30126-37-git-send-email-nsekhar@ti.com> <1289990661-30126-38-git-send-email-nsekhar@ti.com> <1289990661-30126-39-git-send-email-nsekhar@ti.com> <1289990661-30126-40-git-send-email-nsekhar@ti.com> <1289990661-30126-41-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-42-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Let DMA operation be specified on a per-device basis instead of selecting it once during probe. A side effect of this is the need to combine the PIO and DMA buffer txrx_bufs routine. This is good since they anyway share some common functionality. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/include/mach/spi.h | 1 + drivers/spi/davinci_spi.c | 342 ++++++++++++++---------------- 2 files changed, 156 insertions(+), 187 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/spi.h b/arch/arm/mach-davinci/include/mach/spi.h index f7586a0..b3ab7d0 100644 --- a/arch/arm/mach-davinci/include/mach/spi.h +++ b/arch/arm/mach-davinci/include/mach/spi.h @@ -41,6 +41,7 @@ struct davinci_spi_config { u8 parity_enable; #define SPI_IO_TYPE_INTR 0 #define SPI_IO_TYPE_POLL 1 +#define SPI_IO_TYPE_DMA 2 u8 io_type; u8 timer_disable; u8 c2tdelay; diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 6094e3a..5fe2980 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -500,6 +500,25 @@ out: return errors; } +static void davinci_spi_dma_callback(unsigned lch, u16 status, void *data) +{ + struct davinci_spi *davinci_spi = data; + struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; + + edma_stop(lch); + + if (status == DMA_COMPLETE) { + if (lch == davinci_spi_dma->dma_rx_channel) + davinci_spi->rcount = 0; + if (lch == davinci_spi_dma->dma_tx_channel) + davinci_spi->wcount = 0; + } + + if ((!davinci_spi->wcount && !davinci_spi->rcount) || + (status != DMA_COMPLETE)) + complete(&davinci_spi->done); +} + /** * davinci_spi_bufs - functions which will handle transfer data * @spi: spi device on which data transfer to be done @@ -509,25 +528,30 @@ out: * of SPI controller and then wait until the completion will be marked * by the IRQ Handler. */ -static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) +static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; - int ret; + int data_type, ret; u32 tx_data, data1_reg_val; u32 errors = 0; struct davinci_spi_config *spicfg; struct davinci_spi_platform_data *pdata; + unsigned uninitialized_var(rx_buf_count); + struct device *sdev; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; spicfg = (struct davinci_spi_config *)spi->controller_data; if (!spicfg) spicfg = &davinci_spi_default_cfg; + sdev = davinci_spi->bitbang.master->dev.parent; + + /* convert len to words based on bits_per_word */ + data_type = davinci_spi->bytes_per_word[spi->chip_select]; davinci_spi->tx = t->tx_buf; davinci_spi->rx = t->rx_buf; - davinci_spi->wcount = t->len / - davinci_spi->bytes_per_word[spi->chip_select]; + davinci_spi->wcount = t->len / data_type; davinci_spi->rcount = davinci_spi->wcount; data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); @@ -535,20 +559,117 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) /* Enable SPI */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - if (spicfg->io_type == SPI_IO_TYPE_INTR) { + INIT_COMPLETION(davinci_spi->done); + + if (spicfg->io_type == SPI_IO_TYPE_INTR) set_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKINT); - INIT_COMPLETION(davinci_spi->done); - } - /* start the transfer */ - davinci_spi->wcount--; - tx_data = davinci_spi->get_tx(davinci_spi); - data1_reg_val &= 0xFFFF0000; - data1_reg_val |= tx_data & 0xFFFF; - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); + if (spicfg->io_type != SPI_IO_TYPE_DMA) { + /* start the transfer */ + davinci_spi->wcount--; + tx_data = davinci_spi->get_tx(davinci_spi); + data1_reg_val &= 0xFFFF0000; + data1_reg_val |= tx_data & 0xFFFF; + iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); + } else { + struct davinci_spi_dma *davinci_spi_dma; + unsigned long tx_reg, rx_reg; + struct edmacc_param param; + void *rx_buf; + + davinci_spi_dma = &davinci_spi->dma_channels; + + tx_reg = (unsigned long)davinci_spi->pbase + SPIDAT1; + rx_reg = (unsigned long)davinci_spi->pbase + SPIBUF; + + /* + * Transmit DMA setup + * + * If there is transmit data, map the transmit buffer, set it + * as the source of data and set the source B index to data + * size. If there is no transmit data, set the transmit register + * as the source of data, and set the source B index to zero. + * + * The destination is always the transmit register itself. And + * the destination never increments. + */ + + if (t->tx_buf) { + t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, + davinci_spi->wcount, DMA_TO_DEVICE); + if (dma_mapping_error(&spi->dev, t->tx_dma)) { + dev_dbg(sdev, "Unable to DMA map %d bytes" + "TX buffer\n", + davinci_spi->wcount); + return -ENOMEM; + } + } + + param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_tx_channel); + param.src = t->tx_buf ? t->tx_dma : tx_reg; + param.a_b_cnt = davinci_spi->wcount << 16 | data_type; + param.dst = tx_reg; + param.src_dst_bidx = t->tx_buf ? data_type : 0; + param.link_bcntrld = 0xffff; + param.src_dst_cidx = 0; + param.ccnt = 1; + edma_write_slot(davinci_spi_dma->dma_tx_channel, ¶m); + edma_link(davinci_spi_dma->dma_tx_channel, + davinci_spi_dma->dummy_param_slot); + + /* + * Receive DMA setup + * + * If there is receive buffer, use it to receive data. If there + * is none provided, use a temporary receive buffer. Set the + * destination B index to 0 so effectively only one byte is used + * in the temporary buffer (address does not increment). + * + * The source of receive data is the receive data register. The + * source address never increments. + */ + + if (t->rx_buf) { + rx_buf = t->rx_buf; + rx_buf_count = davinci_spi->rcount; + } else { + rx_buf = davinci_spi->rx_tmp_buf; + rx_buf_count = sizeof(davinci_spi->rx_tmp_buf); + } + + t->rx_dma = dma_map_single(&spi->dev, rx_buf, rx_buf_count, + DMA_FROM_DEVICE); + if (dma_mapping_error(&spi->dev, t->rx_dma)) { + dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", + rx_buf_count); + if (t->tx_buf) + dma_unmap_single(NULL, t->tx_dma, + davinci_spi->wcount, + DMA_TO_DEVICE); + return -ENOMEM; + } + + param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_rx_channel); + param.src = rx_reg; + param.a_b_cnt = davinci_spi->rcount << 16 | data_type; + param.dst = t->rx_dma; + param.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16; + param.link_bcntrld = 0xffff; + param.src_dst_cidx = 0; + param.ccnt = 1; + edma_write_slot(davinci_spi_dma->dma_rx_channel, ¶m); + + if (pdata->cshold_bug) + iowrite16(data1_reg_val >> 16, + davinci_spi->base + SPIDAT1 + 2); + + edma_start(davinci_spi_dma->dma_rx_channel); + edma_start(davinci_spi_dma->dma_tx_channel); + set_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); + } /* Wait for the transfer to complete */ - if (spicfg->io_type == SPI_IO_TYPE_INTR) { + if (spicfg->io_type != SPI_IO_TYPE_POLL) { wait_for_completion_interruptible(&(davinci_spi->done)); } else { while (davinci_spi->rcount > 0 || davinci_spi->wcount > 0) { @@ -560,6 +681,17 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) } clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); + if (spicfg->io_type == SPI_IO_TYPE_DMA) { + + if (t->tx_buf) + dma_unmap_single(NULL, t->tx_dma, davinci_spi->wcount, + DMA_TO_DEVICE); + + dma_unmap_single(NULL, t->rx_dma, rx_buf_count, + DMA_FROM_DEVICE); + + clear_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); + } /* * Check for bit error, desync error,parity error,timeout error and @@ -572,6 +704,11 @@ static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) return ret; } + if (davinci_spi->rcount != 0 || davinci_spi->wcount != 0) { + dev_err(sdev, "SPI data transfer error\n"); + return -EIO; + } + return t->len; } @@ -601,174 +738,6 @@ static irqreturn_t davinci_spi_irq(s32 irq, void *context_data) return IRQ_HANDLED; } -static void davinci_spi_dma_callback(unsigned lch, u16 status, void *data) -{ - struct davinci_spi *davinci_spi = data; - struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; - - edma_stop(lch); - - if (status == DMA_COMPLETE) { - if (lch == davinci_spi_dma->dma_rx_channel) - davinci_spi->rcount = 0; - if (lch == davinci_spi_dma->dma_tx_channel) - davinci_spi->wcount = 0; - } - - if ((!davinci_spi->wcount && !davinci_spi->rcount) || - (status != DMA_COMPLETE)) - complete(&davinci_spi->done); -} - -static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) -{ - struct davinci_spi *davinci_spi; - int int_status = 0; - unsigned rx_buf_count; - struct davinci_spi_dma *davinci_spi_dma; - int data_type, ret; - unsigned long tx_reg, rx_reg; - struct davinci_spi_platform_data *pdata; - void *rx_buf; - struct device *sdev; - struct edmacc_param param; - - davinci_spi = spi_master_get_devdata(spi->master); - pdata = davinci_spi->pdata; - sdev = davinci_spi->bitbang.master->dev.parent; - - davinci_spi_dma = &davinci_spi->dma_channels; - - /* convert len to words based on bits_per_word */ - data_type = davinci_spi->bytes_per_word[spi->chip_select]; - - tx_reg = (unsigned long)davinci_spi->pbase + SPIDAT1; - rx_reg = (unsigned long)davinci_spi->pbase + SPIBUF; - - davinci_spi->tx = t->tx_buf; - davinci_spi->rx = t->rx_buf; - davinci_spi->wcount = t->len / data_type; - davinci_spi->rcount = davinci_spi->wcount; - - INIT_COMPLETION(davinci_spi->done); - - /* disable all interrupts for dma transfers */ - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); - /* Enable SPI */ - set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - - /* - * Transmit DMA setup - * - * If there is transmit data, map the transmit buffer, set it as the - * source of data and set the source B index to data size. - * If there is no transmit data, set the transmit register as the - * source of data, and set the source B index to zero. - * - * The destination is always the transmit register itself. And the - * destination never increments. - */ - - if (t->tx_buf) { - t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, - davinci_spi->wcount, DMA_TO_DEVICE); - if (dma_mapping_error(&spi->dev, t->tx_dma)) { - dev_dbg(sdev, "Unable to DMA map %d bytes TX buffer\n", - davinci_spi->wcount); - return -ENOMEM; - } - } - - param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_tx_channel); - param.src = t->tx_buf ? t->tx_dma : tx_reg; - param.a_b_cnt = davinci_spi->wcount << 16 | data_type; - param.dst = tx_reg; - param.src_dst_bidx = t->tx_buf ? data_type : 0; - param.link_bcntrld = 0xffff; - param.src_dst_cidx = 0; - param.ccnt = 1; - edma_write_slot(davinci_spi_dma->dma_tx_channel, ¶m); - edma_link(davinci_spi_dma->dma_tx_channel, - davinci_spi_dma->dummy_param_slot); - - /* - * Receive DMA setup - * - * If there is receive buffer, use it to receive data. If there - * is none provided, use a temporary receive buffer. Set the - * destination B index to 0 so effectively only one byte is used - * in the temporary buffer (address does not increment). - * - * The source of receive data is the receive data register. The - * source address never increments. - */ - - if (t->rx_buf) { - rx_buf = t->rx_buf; - rx_buf_count = davinci_spi->rcount; - } else { - rx_buf = davinci_spi->rx_tmp_buf; - rx_buf_count = sizeof(davinci_spi->rx_tmp_buf); - } - - t->rx_dma = dma_map_single(&spi->dev, rx_buf, rx_buf_count, - DMA_FROM_DEVICE); - if (dma_mapping_error(&spi->dev, t->rx_dma)) { - dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", - rx_buf_count); - if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, davinci_spi->wcount, - DMA_TO_DEVICE); - return -ENOMEM; - } - - param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_rx_channel); - param.src = rx_reg; - param.a_b_cnt = davinci_spi->rcount << 16 | data_type; - param.dst = t->rx_dma; - param.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16; - param.link_bcntrld = 0xffff; - param.src_dst_cidx = 0; - param.ccnt = 1; - edma_write_slot(davinci_spi_dma->dma_rx_channel, ¶m); - - if (pdata->cshold_bug) { - u16 spidat1 = ioread16(davinci_spi->base + SPIDAT1 + 2); - iowrite16(spidat1, davinci_spi->base + SPIDAT1 + 2); - } - - edma_start(davinci_spi_dma->dma_rx_channel); - edma_start(davinci_spi_dma->dma_tx_channel); - set_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); - - wait_for_completion_interruptible(&davinci_spi->done); - - if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, davinci_spi->wcount, - DMA_TO_DEVICE); - - dma_unmap_single(NULL, t->rx_dma, rx_buf_count, DMA_FROM_DEVICE); - - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); - - /* - * Check for bit error, desync error,parity error,timeout error and - * receive overflow errors - */ - int_status = ioread32(davinci_spi->base + SPIFLG); - - ret = davinci_spi_check_error(davinci_spi, int_status); - if (ret != 0) - return ret; - - if (davinci_spi->rcount != 0 || davinci_spi->wcount != 0) { - dev_err(sdev, "SPI data transfer error\n"); - return -EIO; - } - - return t->len; -} - static int davinci_spi_request_dma(struct davinci_spi *davinci_spi) { int r; @@ -918,7 +887,7 @@ static int davinci_spi_probe(struct platform_device *pdev) if (r) dma_eventq = r->start; - davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_pio; + davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs; if (dma_rx_chan != SPI_NO_RESOURCE && dma_tx_chan != SPI_NO_RESOURCE && dma_eventq != SPI_NO_RESOURCE) { @@ -930,10 +899,9 @@ static int davinci_spi_probe(struct platform_device *pdev) if (ret) goto free_clk; - davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_dma; - dev_info(&pdev->dev, "DaVinci SPI driver in EDMA mode\n" - "Using RX channel = %d , TX channel = %d and " - "event queue = %d", dma_rx_chan, dma_tx_chan, + dev_info(&pdev->dev, "DMA: supported\n"); + dev_info(&pdev->dev, "DMA: RX channel: %d, TX channel: %d, " + "event queue: %d\n", dma_rx_chan, dma_tx_chan, dma_eventq); } -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:07 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:07 +0530 Subject: [PATCH 35/49] spi: davinci: use edma_write_slot() to setup EDMA PaRAM slot In-Reply-To: <1289990661-30126-35-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> <1289990661-30126-35-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-36-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Currently a series of EDMA API calls are being made to setup various aspects of EDMA PaRAM slots for receive and transmit. Instead setup the PaRAM using a local structure and write once to the hardware using edma_write_slot() Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 34 +++++++++++++++++++--------------- 1 files changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 705d006..67f1e46 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -725,6 +725,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) struct davinci_spi_platform_data *pdata; void *rx_buf; struct device *sdev; + struct edmacc_param param; davinci_spi = spi_master_get_devdata(spi->master); pdata = davinci_spi->pdata; @@ -773,14 +774,15 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) } } - edma_set_transfer_params(davinci_spi_dma->dma_tx_channel, - data_type, count, 1, 0, ASYNC); - edma_set_dest(davinci_spi_dma->dma_tx_channel, tx_reg, INCR, W8BIT); - edma_set_src(davinci_spi_dma->dma_tx_channel, - t->tx_buf ? t->tx_dma : tx_reg, INCR, W8BIT); - edma_set_src_index(davinci_spi_dma->dma_tx_channel, - t->tx_buf ? data_type : 0, 0); - edma_set_dest_index(davinci_spi_dma->dma_tx_channel, 0, 0); + param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_tx_channel); + param.src = t->tx_buf ? t->tx_dma : tx_reg; + param.a_b_cnt = count << 16 | data_type; + param.dst = tx_reg; + param.src_dst_bidx = t->tx_buf ? data_type : 0; + param.link_bcntrld = 0xffff; + param.src_dst_cidx = 0; + param.ccnt = 1; + edma_write_slot(davinci_spi_dma->dma_tx_channel, ¶m); /* * Receive DMA setup @@ -812,13 +814,15 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) return -ENOMEM; } - edma_set_transfer_params(davinci_spi_dma->dma_rx_channel, data_type, - count, 1, 0, ASYNC); - edma_set_src(davinci_spi_dma->dma_rx_channel, rx_reg, INCR, W8BIT); - edma_set_dest(davinci_spi_dma->dma_rx_channel, t->rx_dma, INCR, W8BIT); - edma_set_src_index(davinci_spi_dma->dma_rx_channel, 0, 0); - edma_set_dest_index(davinci_spi_dma->dma_rx_channel, - t->rx_buf ? data_type : 0, 0); + param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_rx_channel); + param.src = rx_reg; + param.a_b_cnt = count << 16 | data_type; + param.dst = t->rx_dma; + param.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16; + param.link_bcntrld = 0xffff; + param.src_dst_cidx = 0; + param.ccnt = 1; + edma_write_slot(davinci_spi_dma->dma_rx_channel, ¶m); if (pdata->cshold_bug) { u16 spidat1 = ioread16(davinci_spi->base + SPIDAT1 + 2); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:12 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:12 +0530 Subject: [PATCH 40/49] spi: davinci: remove usage of additional completion variables for DMA In-Reply-To: <1289990661-30126-40-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> <1289990661-30126-35-git-send-email-nsekhar@ti.com> <1289990661-30126-36-git-send-email-nsekhar@ti.com> <1289990661-30126-37-git-send-email-nsekhar@ti.com> <1289990661-30126-38-git-send-email-nsekhar@ti.com> <1289990661-30126-39-git-send-email-nsekhar@ti.com> <1289990661-30126-40-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-41-git-send-email-nsekhar@ti.com> From: Brian Niebuhr The DMA code does not use the existing completion variable 'done' which is being used for interrupt mode transfers. Instead it uses two different completion variables specific to DMA mode transfers. Eliminate the usage of new completion variables for DMA mode and use the existing completion variable. [nsekhar at ti.com: To make this process easy, eliminate the two different DMA completion callback functions for tx and rx and use a single callback function instead] Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 58 +++++++++++++++++--------------------------- 1 files changed, 23 insertions(+), 35 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index a47947d..6094e3a 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -118,9 +118,6 @@ struct davinci_spi_dma { int dma_rx_channel; int dummy_param_slot; enum dma_event_q eventq; - - struct completion dma_tx_completion; - struct completion dma_rx_completion; }; /* SPI Controller driver's private data. */ @@ -386,32 +383,6 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, return 0; } -static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data) -{ - struct davinci_spi *davinci_spi = data; - struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; - - edma_stop(davinci_spi_dma->dma_rx_channel); - - if (ch_status == DMA_COMPLETE) - davinci_spi->rcount = 0; - - complete(&davinci_spi_dma->dma_rx_completion); -} - -static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) -{ - struct davinci_spi *davinci_spi = data; - struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; - - edma_stop(davinci_spi_dma->dma_tx_channel); - - if (ch_status == DMA_COMPLETE) - davinci_spi->wcount = 0; - - complete(&davinci_spi_dma->dma_tx_completion); -} - /** * davinci_spi_setup - This functions will set default transfer method * @spi: spi device on which data transfer to be done @@ -630,6 +601,25 @@ static irqreturn_t davinci_spi_irq(s32 irq, void *context_data) return IRQ_HANDLED; } +static void davinci_spi_dma_callback(unsigned lch, u16 status, void *data) +{ + struct davinci_spi *davinci_spi = data; + struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; + + edma_stop(lch); + + if (status == DMA_COMPLETE) { + if (lch == davinci_spi_dma->dma_rx_channel) + davinci_spi->rcount = 0; + if (lch == davinci_spi_dma->dma_tx_channel) + davinci_spi->wcount = 0; + } + + if ((!davinci_spi->wcount && !davinci_spi->rcount) || + (status != DMA_COMPLETE)) + complete(&davinci_spi->done); +} + static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; @@ -660,8 +650,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) davinci_spi->wcount = t->len / data_type; davinci_spi->rcount = davinci_spi->wcount; - init_completion(&davinci_spi_dma->dma_rx_completion); - init_completion(&davinci_spi_dma->dma_tx_completion); + INIT_COMPLETION(davinci_spi->done); /* disable all interrupts for dma transfers */ clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); @@ -752,8 +741,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) edma_start(davinci_spi_dma->dma_tx_channel); set_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); - wait_for_completion_interruptible(&davinci_spi_dma->dma_tx_completion); - wait_for_completion_interruptible(&davinci_spi_dma->dma_rx_completion); + wait_for_completion_interruptible(&davinci_spi->done); if (t->tx_buf) dma_unmap_single(NULL, t->tx_dma, davinci_spi->wcount, @@ -787,7 +775,7 @@ static int davinci_spi_request_dma(struct davinci_spi *davinci_spi) struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; r = edma_alloc_channel(davinci_spi_dma->dma_rx_channel, - davinci_spi_dma_rx_callback, davinci_spi, + davinci_spi_dma_callback, davinci_spi, davinci_spi_dma->eventq); if (r < 0) { pr_err("Unable to request DMA channel for SPI RX\n"); @@ -796,7 +784,7 @@ static int davinci_spi_request_dma(struct davinci_spi *davinci_spi) } r = edma_alloc_channel(davinci_spi_dma->dma_tx_channel, - davinci_spi_dma_tx_callback, davinci_spi, + davinci_spi_dma_callback, davinci_spi, davinci_spi_dma->eventq); if (r < 0) { pr_err("Unable to request DMA channel for SPI TX\n"); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:10 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:10 +0530 Subject: [PATCH 38/49] spi: davinci: fix EDMA CC errors at end of transfers In-Reply-To: <1289990661-30126-38-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> <1289990661-30126-35-git-send-email-nsekhar@ti.com> <1289990661-30126-36-git-send-email-nsekhar@ti.com> <1289990661-30126-37-git-send-email-nsekhar@ti.com> <1289990661-30126-38-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-39-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Use a dummy param slot linked to itself to take care of the extra "sync event" that gets sent to EDMA controller after the last byte has been transferred. The dummy PaRAM slot that is linked to the actual DMA PaRAM slot "absorbs" this event and prevents a EDMA CC error to be asserted. Without this provision, the EDMA CC error would be asserted because the channel PaRAM would be empty after the transfer and EDMA would not know what to make out of the extra sync event. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 28 +++++++++++++++++++++++++--- 1 files changed, 25 insertions(+), 3 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 6db4786..975c2a2 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -116,6 +116,7 @@ struct davinci_spi_dma { int dma_tx_channel; int dma_rx_channel; + int dummy_param_slot; enum dma_event_q eventq; struct completion dma_tx_completion; @@ -697,6 +698,8 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) param.src_dst_cidx = 0; param.ccnt = 1; edma_write_slot(davinci_spi_dma->dma_tx_channel, ¶m); + edma_link(davinci_spi_dma->dma_tx_channel, + davinci_spi_dma->dummy_param_slot); /* * Receive DMA setup @@ -779,19 +782,37 @@ static int davinci_spi_request_dma(struct davinci_spi_dma *davinci_spi_dma) davinci_spi_dma->eventq); if (r < 0) { pr_err("Unable to request DMA channel for SPI RX\n"); - return -EAGAIN; + r = -EAGAIN; + goto rx_dma_failed; } r = edma_alloc_channel(davinci_spi_dma->dma_tx_channel, davinci_spi_dma_tx_callback, davinci_spi_dma, davinci_spi_dma->eventq); if (r < 0) { - edma_free_channel(davinci_spi_dma->dma_rx_channel); pr_err("Unable to request DMA channel for SPI TX\n"); - return -EAGAIN; + r = -EAGAIN; + goto tx_dma_failed; } + r = edma_alloc_slot(EDMA_CTLR(davinci_spi_dma->dma_tx_channel), + EDMA_SLOT_ANY); + if (r < 0) { + pr_err("Unable to request SPI TX DMA param slot\n"); + r = -EAGAIN; + goto param_failed; + } + davinci_spi_dma->dummy_param_slot = r; + edma_link(davinci_spi_dma->dummy_param_slot, + davinci_spi_dma->dummy_param_slot); + return 0; +param_failed: + edma_free_channel(davinci_spi_dma->dma_tx_channel); +tx_dma_failed: + edma_free_channel(davinci_spi_dma->dma_rx_channel); +rx_dma_failed: + return r; } /** @@ -970,6 +991,7 @@ static int davinci_spi_probe(struct platform_device *pdev) free_dma: edma_free_channel(davinci_spi->dma_channels.dma_tx_channel); edma_free_channel(davinci_spi->dma_channels.dma_rx_channel); + edma_free_slot(davinci_spi->dma_channels.dummy_param_slot); free_clk: clk_disable(davinci_spi->clk); clk_put(davinci_spi->clk); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:14 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:14 +0530 Subject: [PATCH 42/49] spi: davinci: remove non-useful "clk_internal" platform data In-Reply-To: <1289990661-30126-42-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> <1289990661-30126-35-git-send-email-nsekhar@ti.com> <1289990661-30126-36-git-send-email-nsekhar@ti.com> <1289990661-30126-37-git-send-email-nsekhar@ti.com> <1289990661-30126-38-git-send-email-nsekhar@ti.com> <1289990661-30126-39-git-send-email-nsekhar@ti.com> <1289990661-30126-40-git-send-email-nsekhar@ti.com> <1289990661-30126-41-git-send-email-nsekhar@ti.com> <1289990661-30126-42-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-43-git-send-email-nsekhar@ti.com> From: Brian Niebuhr The "clk_internal" platform data member which contols the CLKMOD bit in Global Control Register 1 is not useful since CLKMOD needs be set to 1 *always* to ensure master mode operation. Remove this platform data. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/dm355.c | 1 - arch/arm/mach-davinci/dm365.c | 1 - arch/arm/mach-davinci/include/mach/spi.h | 1 - drivers/spi/davinci_spi.c | 9 +-------- 4 files changed, 1 insertions(+), 11 deletions(-) diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 27ee870..c0c501a 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -412,7 +412,6 @@ static struct resource dm355_spi0_resources[] = { static struct davinci_spi_platform_data dm355_spi0_pdata = { .version = SPI_VERSION_1, .num_chipselect = 2, - .clk_internal = 1, .cshold_bug = true, }; static struct platform_device dm355_spi0_device = { diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 1e5012e..8b8204f 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -625,7 +625,6 @@ static u64 dm365_spi0_dma_mask = DMA_BIT_MASK(32); static struct davinci_spi_platform_data dm365_spi0_pdata = { .version = SPI_VERSION_1, .num_chipselect = 2, - .clk_internal = 1, }; static struct resource dm365_spi0_resources[] = { diff --git a/arch/arm/mach-davinci/include/mach/spi.h b/arch/arm/mach-davinci/include/mach/spi.h index b3ab7d0..1f8b786 100644 --- a/arch/arm/mach-davinci/include/mach/spi.h +++ b/arch/arm/mach-davinci/include/mach/spi.h @@ -29,7 +29,6 @@ enum { struct davinci_spi_platform_data { u8 version; u8 num_chipselect; - u8 clk_internal; u8 intr_line; u8 *chip_sel; bool cshold_bug; diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 5fe2980..2e74fcd 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -927,14 +927,6 @@ static int davinci_spi_probe(struct platform_device *pdev) } } - /* Clock internal */ - if (davinci_spi->pdata->clk_internal) - set_io_bits(davinci_spi->base + SPIGCR1, - SPIGCR1_CLKMOD_MASK); - else - clear_io_bits(davinci_spi->base + SPIGCR1, - SPIGCR1_CLKMOD_MASK); - if (pdata->intr_line) iowrite32(SPI_INTLVL_1, davinci_spi->base + SPILVL); else @@ -943,6 +935,7 @@ static int davinci_spi_probe(struct platform_device *pdev) iowrite32(CS_DEFAULT, davinci_spi->base + SPIDEF); /* master mode default */ + set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_CLKMOD_MASK); set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_MASTER_MASK); ret = spi_bitbang_start(&davinci_spi->bitbang); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:09 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:09 +0530 Subject: [PATCH 37/49] spi: davinci: do not allocate DMA channels during SPI device setup In-Reply-To: <1289990661-30126-37-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> <1289990661-30126-35-git-send-email-nsekhar@ti.com> <1289990661-30126-36-git-send-email-nsekhar@ti.com> <1289990661-30126-37-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-38-git-send-email-nsekhar@ti.com> Do not allocate (and de-allocate) SPI DMA channels during setup (and cleanup) for each SPI device. Instead, allocate the DMA channels once duing probe and use them for the life time of the driver. This makes sense since there are dedicated DMA channels meant for SPI use. This also helps remove the unnecessary DMA "sync_dev" variables being used to store DMA channel information. Also, the "use_dma" platform variable is now eliminated since it is possible to check if the platform supports DMA or not based upon whether DMA resources can be found or not. Tested-By: Michael Williamson Tested-By: Brian Niebuhr Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/include/mach/spi.h | 1 - drivers/spi/davinci_spi.c | 153 ++++++++++-------------------- 2 files changed, 51 insertions(+), 103 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/spi.h b/arch/arm/mach-davinci/include/mach/spi.h index 68db6d5..f7586a0 100644 --- a/arch/arm/mach-davinci/include/mach/spi.h +++ b/arch/arm/mach-davinci/include/mach/spi.h @@ -31,7 +31,6 @@ struct davinci_spi_platform_data { u8 num_chipselect; u8 clk_internal; u8 intr_line; - u8 use_dma; u8 *chip_sel; bool cshold_bug; }; diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 9695f98..6db4786 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -116,8 +116,6 @@ struct davinci_spi_dma { int dma_tx_channel; int dma_rx_channel; - int dma_tx_sync_dev; - int dma_rx_sync_dev; enum dma_event_q eventq; struct completion dma_tx_completion; @@ -153,8 +151,6 @@ struct davinci_spi { static struct davinci_spi_config davinci_spi_default_cfg; -static unsigned use_dma; - static void davinci_spi_rx_buf_u8(u32 data, struct davinci_spi *davinci_spi) { if (davinci_spi->rx) { @@ -391,12 +387,7 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data) { - struct spi_device *spi = (struct spi_device *)data; - struct davinci_spi *davinci_spi; - struct davinci_spi_dma *davinci_spi_dma; - - davinci_spi = spi_master_get_devdata(spi->master); - davinci_spi_dma = &davinci_spi->dma_channels; + struct davinci_spi_dma *davinci_spi_dma = data; if (ch_status == DMA_COMPLETE) edma_stop(davinci_spi_dma->dma_rx_channel); @@ -408,12 +399,7 @@ static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data) static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) { - struct spi_device *spi = (struct spi_device *)data; - struct davinci_spi *davinci_spi; - struct davinci_spi_dma *davinci_spi_dma; - - davinci_spi = spi_master_get_devdata(spi->master); - davinci_spi_dma = &davinci_spi->dma_channels; + struct davinci_spi_dma *davinci_spi_dma = data; if (ch_status == DMA_COMPLETE) edma_stop(davinci_spi_dma->dma_tx_channel); @@ -423,39 +409,6 @@ static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) complete(&davinci_spi_dma->dma_tx_completion); } -static int davinci_spi_request_dma(struct spi_device *spi) -{ - struct davinci_spi *davinci_spi; - struct davinci_spi_dma *davinci_spi_dma; - struct device *sdev; - int r; - - davinci_spi = spi_master_get_devdata(spi->master); - davinci_spi_dma = &davinci_spi->dma_channels; - sdev = davinci_spi->bitbang.master->dev.parent; - - r = edma_alloc_channel(davinci_spi_dma->dma_rx_sync_dev, - davinci_spi_dma_rx_callback, spi, - davinci_spi_dma->eventq); - if (r < 0) { - dev_dbg(sdev, "Unable to request DMA channel for SPI RX\n"); - return -EAGAIN; - } - davinci_spi_dma->dma_rx_channel = r; - r = edma_alloc_channel(davinci_spi_dma->dma_tx_sync_dev, - davinci_spi_dma_tx_callback, spi, - davinci_spi_dma->eventq); - if (r < 0) { - edma_free_channel(davinci_spi_dma->dma_rx_channel); - davinci_spi_dma->dma_rx_channel = -1; - dev_dbg(sdev, "Unable to request DMA channel for SPI TX\n"); - return -EAGAIN; - } - davinci_spi_dma->dma_tx_channel = r; - - return 0; -} - /** * davinci_spi_setup - This functions will set default transfer method * @spi: spi device on which data transfer to be done @@ -466,7 +419,6 @@ static int davinci_spi_setup(struct spi_device *spi) { int retval = 0; struct davinci_spi *davinci_spi; - struct davinci_spi_dma *davinci_spi_dma; struct davinci_spi_platform_data *pdata; davinci_spi = spi_master_get_devdata(spi->master); @@ -494,33 +446,9 @@ static int davinci_spi_setup(struct spi_device *spi) clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_LOOPBACK_MASK); - if (use_dma) { - davinci_spi_dma = &davinci_spi->dma_channels; - - if ((davinci_spi_dma->dma_rx_channel == -1) || - (davinci_spi_dma->dma_tx_channel == -1)) - retval = davinci_spi_request_dma(spi); - } - return retval; } -static void davinci_spi_cleanup(struct spi_device *spi) -{ - if (use_dma) { - struct davinci_spi *davinci_spi = - spi_master_get_devdata(spi->master); - struct davinci_spi_dma *davinci_spi_dma = - &davinci_spi->dma_channels; - - if ((davinci_spi_dma->dma_rx_channel != -1) - && (davinci_spi_dma->dma_tx_channel != -1)) { - edma_free_channel(davinci_spi_dma->dma_tx_channel); - edma_free_channel(davinci_spi_dma->dma_rx_channel); - } - } -} - static int davinci_spi_check_error(struct davinci_spi *davinci_spi, int int_status) { @@ -842,6 +770,30 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) return t->len; } +static int davinci_spi_request_dma(struct davinci_spi_dma *davinci_spi_dma) +{ + int r; + + r = edma_alloc_channel(davinci_spi_dma->dma_rx_channel, + davinci_spi_dma_rx_callback, davinci_spi_dma, + davinci_spi_dma->eventq); + if (r < 0) { + pr_err("Unable to request DMA channel for SPI RX\n"); + return -EAGAIN; + } + + r = edma_alloc_channel(davinci_spi_dma->dma_tx_channel, + davinci_spi_dma_tx_callback, davinci_spi_dma, + davinci_spi_dma->eventq); + if (r < 0) { + edma_free_channel(davinci_spi_dma->dma_rx_channel); + pr_err("Unable to request DMA channel for SPI TX\n"); + return -EAGAIN; + } + + return 0; +} + /** * davinci_spi_probe - probe function for SPI Master Controller * @pdev: platform_device structure which contains plateform specific data @@ -928,45 +880,39 @@ static int davinci_spi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->num_chipselect = pdata->num_chipselect; master->setup = davinci_spi_setup; - master->cleanup = davinci_spi_cleanup; davinci_spi->bitbang.chipselect = davinci_spi_chipselect; davinci_spi->bitbang.setup_transfer = davinci_spi_setup_transfer; davinci_spi->version = pdata->version; - use_dma = pdata->use_dma; davinci_spi->bitbang.flags = SPI_NO_CS | SPI_LSB_FIRST | SPI_LOOP; if (davinci_spi->version == SPI_VERSION_2) davinci_spi->bitbang.flags |= SPI_READY; - if (use_dma) { - r = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (r) - dma_rx_chan = r->start; - r = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (r) - dma_tx_chan = r->start; - r = platform_get_resource(pdev, IORESOURCE_DMA, 2); - if (r) - dma_eventq = r->start; - } - - if (!use_dma || - dma_rx_chan == SPI_NO_RESOURCE || - dma_tx_chan == SPI_NO_RESOURCE || - dma_eventq == SPI_NO_RESOURCE) { - davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_pio; - use_dma = 0; - } else { - davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_dma; - - davinci_spi->dma_channels.dma_rx_channel = -1; - davinci_spi->dma_channels.dma_rx_sync_dev = dma_rx_chan; - davinci_spi->dma_channels.dma_tx_channel = -1; - davinci_spi->dma_channels.dma_tx_sync_dev = dma_tx_chan; + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (r) + dma_rx_chan = r->start; + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (r) + dma_tx_chan = r->start; + r = platform_get_resource(pdev, IORESOURCE_DMA, 2); + if (r) + dma_eventq = r->start; + + davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_pio; + if (dma_rx_chan != SPI_NO_RESOURCE && + dma_tx_chan != SPI_NO_RESOURCE && + dma_eventq != SPI_NO_RESOURCE) { + davinci_spi->dma_channels.dma_rx_channel = dma_rx_chan; + davinci_spi->dma_channels.dma_tx_channel = dma_tx_chan; davinci_spi->dma_channels.eventq = dma_eventq; + ret = davinci_spi_request_dma(&davinci_spi->dma_channels); + if (ret) + goto free_clk; + + davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_dma; dev_info(&pdev->dev, "DaVinci SPI driver in EDMA mode\n" "Using RX channel = %d , TX channel = %d and " "event queue = %d", dma_rx_chan, dma_tx_chan, @@ -1015,12 +961,15 @@ static int davinci_spi_probe(struct platform_device *pdev) ret = spi_bitbang_start(&davinci_spi->bitbang); if (ret) - goto free_clk; + goto free_dma; dev_info(&pdev->dev, "Controller at 0x%p\n", davinci_spi->base); return ret; +free_dma: + edma_free_channel(davinci_spi->dma_channels.dma_tx_channel); + edma_free_channel(davinci_spi->dma_channels.dma_rx_channel); free_clk: clk_disable(davinci_spi->clk); clk_put(davinci_spi->clk); -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:11 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:11 +0530 Subject: [PATCH 39/49] spi: davinci: handle DMA completion errors correctly In-Reply-To: <1289990661-30126-39-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> <1289990661-30126-35-git-send-email-nsekhar@ti.com> <1289990661-30126-36-git-send-email-nsekhar@ti.com> <1289990661-30126-37-git-send-email-nsekhar@ti.com> <1289990661-30126-38-git-send-email-nsekhar@ti.com> <1289990661-30126-39-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-40-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Do not simply clean the DMA channel on a DMA completion error. Instead, use wcount and rcount members of davinci_spi to detecion non-completion of DMA and signal EIO to the application. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 63 +++++++++++++++++++++++++------------------- 1 files changed, 36 insertions(+), 27 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 975c2a2..a47947d 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -388,24 +388,26 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, static void davinci_spi_dma_rx_callback(unsigned lch, u16 ch_status, void *data) { - struct davinci_spi_dma *davinci_spi_dma = data; + struct davinci_spi *davinci_spi = data; + struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; + + edma_stop(davinci_spi_dma->dma_rx_channel); if (ch_status == DMA_COMPLETE) - edma_stop(davinci_spi_dma->dma_rx_channel); - else - edma_clean_channel(davinci_spi_dma->dma_rx_channel); + davinci_spi->rcount = 0; complete(&davinci_spi_dma->dma_rx_completion); } static void davinci_spi_dma_tx_callback(unsigned lch, u16 ch_status, void *data) { - struct davinci_spi_dma *davinci_spi_dma = data; + struct davinci_spi *davinci_spi = data; + struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; + + edma_stop(davinci_spi_dma->dma_tx_channel); if (ch_status == DMA_COMPLETE) - edma_stop(davinci_spi_dma->dma_tx_channel); - else - edma_clean_channel(davinci_spi_dma->dma_tx_channel); + davinci_spi->wcount = 0; complete(&davinci_spi_dma->dma_tx_completion); } @@ -632,7 +634,6 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) { struct davinci_spi *davinci_spi; int int_status = 0; - int count; unsigned rx_buf_count; struct davinci_spi_dma *davinci_spi_dma; int data_type, ret; @@ -648,20 +649,20 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) davinci_spi_dma = &davinci_spi->dma_channels; + /* convert len to words based on bits_per_word */ + data_type = davinci_spi->bytes_per_word[spi->chip_select]; + tx_reg = (unsigned long)davinci_spi->pbase + SPIDAT1; rx_reg = (unsigned long)davinci_spi->pbase + SPIBUF; davinci_spi->tx = t->tx_buf; davinci_spi->rx = t->rx_buf; - - /* convert len to words based on bits_per_word */ - data_type = davinci_spi->bytes_per_word[spi->chip_select]; + davinci_spi->wcount = t->len / data_type; + davinci_spi->rcount = davinci_spi->wcount; init_completion(&davinci_spi_dma->dma_rx_completion); init_completion(&davinci_spi_dma->dma_tx_completion); - count = t->len / data_type; /* the number of elements */ - /* disable all interrupts for dma transfers */ clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); /* Enable SPI */ @@ -680,18 +681,18 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) */ if (t->tx_buf) { - t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, count, - DMA_TO_DEVICE); + t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, + davinci_spi->wcount, DMA_TO_DEVICE); if (dma_mapping_error(&spi->dev, t->tx_dma)) { - dev_dbg(sdev, "Unable to DMA map a %d bytes" - " TX buffer\n", count); + dev_dbg(sdev, "Unable to DMA map %d bytes TX buffer\n", + davinci_spi->wcount); return -ENOMEM; } } param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_tx_channel); param.src = t->tx_buf ? t->tx_dma : tx_reg; - param.a_b_cnt = count << 16 | data_type; + param.a_b_cnt = davinci_spi->wcount << 16 | data_type; param.dst = tx_reg; param.src_dst_bidx = t->tx_buf ? data_type : 0; param.link_bcntrld = 0xffff; @@ -715,7 +716,7 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) if (t->rx_buf) { rx_buf = t->rx_buf; - rx_buf_count = count; + rx_buf_count = davinci_spi->rcount; } else { rx_buf = davinci_spi->rx_tmp_buf; rx_buf_count = sizeof(davinci_spi->rx_tmp_buf); @@ -727,13 +728,14 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", rx_buf_count); if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, count, DMA_TO_DEVICE); + dma_unmap_single(NULL, t->tx_dma, davinci_spi->wcount, + DMA_TO_DEVICE); return -ENOMEM; } param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_rx_channel); param.src = rx_reg; - param.a_b_cnt = count << 16 | data_type; + param.a_b_cnt = davinci_spi->rcount << 16 | data_type; param.dst = t->rx_dma; param.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16; param.link_bcntrld = 0xffff; @@ -754,7 +756,8 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) wait_for_completion_interruptible(&davinci_spi_dma->dma_rx_completion); if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, count, DMA_TO_DEVICE); + dma_unmap_single(NULL, t->tx_dma, davinci_spi->wcount, + DMA_TO_DEVICE); dma_unmap_single(NULL, t->rx_dma, rx_buf_count, DMA_FROM_DEVICE); @@ -770,15 +773,21 @@ static int davinci_spi_bufs_dma(struct spi_device *spi, struct spi_transfer *t) if (ret != 0) return ret; + if (davinci_spi->rcount != 0 || davinci_spi->wcount != 0) { + dev_err(sdev, "SPI data transfer error\n"); + return -EIO; + } + return t->len; } -static int davinci_spi_request_dma(struct davinci_spi_dma *davinci_spi_dma) +static int davinci_spi_request_dma(struct davinci_spi *davinci_spi) { int r; + struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; r = edma_alloc_channel(davinci_spi_dma->dma_rx_channel, - davinci_spi_dma_rx_callback, davinci_spi_dma, + davinci_spi_dma_rx_callback, davinci_spi, davinci_spi_dma->eventq); if (r < 0) { pr_err("Unable to request DMA channel for SPI RX\n"); @@ -787,7 +796,7 @@ static int davinci_spi_request_dma(struct davinci_spi_dma *davinci_spi_dma) } r = edma_alloc_channel(davinci_spi_dma->dma_tx_channel, - davinci_spi_dma_tx_callback, davinci_spi_dma, + davinci_spi_dma_tx_callback, davinci_spi, davinci_spi_dma->eventq); if (r < 0) { pr_err("Unable to request DMA channel for SPI TX\n"); @@ -929,7 +938,7 @@ static int davinci_spi_probe(struct platform_device *pdev) davinci_spi->dma_channels.dma_tx_channel = dma_tx_chan; davinci_spi->dma_channels.eventq = dma_eventq; - ret = davinci_spi_request_dma(&davinci_spi->dma_channels); + ret = davinci_spi_request_dma(davinci_spi); if (ret) goto free_clk; -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:15 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:15 +0530 Subject: [PATCH 43/49] spi: davinci: enable and power-up SPI only when required In-Reply-To: <1289990661-30126-43-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> <1289990661-30126-35-git-send-email-nsekhar@ti.com> <1289990661-30126-36-git-send-email-nsekhar@ti.com> <1289990661-30126-37-git-send-email-nsekhar@ti.com> <1289990661-30126-38-git-send-email-nsekhar@ti.com> <1289990661-30126-39-git-send-email-nsekhar@ti.com> <1289990661-30126-40-git-send-email-nsekhar@ti.com> <1289990661-30126-41-git-send-email-nsekhar@ti.com> <1289990661-30126-42-git-send-email-nsekhar@ti.com> <1289990661-30126-43-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-44-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Enable SPI only when active transfers are in progress. Keep it in local low power when not in use. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 8 ++++++-- 1 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 2e74fcd..1652bba 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -49,7 +49,6 @@ #define SPIFMT_WDELAY_SHIFT 24 #define SPIFMT_PRESCALE_SHIFT 8 - /* SPIPC0 */ #define SPIPC0_DIFUN_MASK BIT(11) /* MISO */ #define SPIPC0_DOFUN_MASK BIT(10) /* MOSI */ @@ -67,6 +66,7 @@ /* SPIGCR1 */ #define SPIGCR1_CLKMOD_MASK BIT(1) #define SPIGCR1_MASTER_MASK BIT(0) +#define SPIGCR1_POWERDOWN_MASK BIT(8) #define SPIGCR1_LOOPBACK_MASK BIT(16) #define SPIGCR1_SPIENA_MASK BIT(24) @@ -556,7 +556,7 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); - /* Enable SPI */ + clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); INIT_COMPLETION(davinci_spi->done); @@ -693,6 +693,9 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) clear_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); } + clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); + set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); + /* * Check for bit error, desync error,parity error,timeout error and * receive overflow errors @@ -937,6 +940,7 @@ static int davinci_spi_probe(struct platform_device *pdev) /* master mode default */ set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_CLKMOD_MASK); set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_MASTER_MASK); + set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); ret = spi_bitbang_start(&davinci_spi->bitbang); if (ret) -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:16 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:16 +0530 Subject: [PATCH 44/49] spi: davinci: setup the driver owner In-Reply-To: <1289990661-30126-44-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> <1289990661-30126-35-git-send-email-nsekhar@ti.com> <1289990661-30126-36-git-send-email-nsekhar@ti.com> <1289990661-30126-37-git-send-email-nsekhar@ti.com> <1289990661-30126-38-git-send-email-nsekhar@ti.com> <1289990661-30126-39-git-send-email-nsekhar@ti.com> <1289990661-30126-40-git-send-email-nsekhar@ti.com> <1289990661-30126-41-git-send-email-nsekhar@ti.com> <1289990661-30126-42-git-send-email-nsekhar@ti.com> <1289990661-30126-43-git-send-email-nsekhar@ti.com> <1289990661-30126-44-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-45-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Setup the owner member of the platform driver to THIS_MODULE instead of leaving it NULL. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 5 ++++- 1 files changed, 4 insertions(+), 1 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 1652bba..2ec5fd2 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -1001,7 +1001,10 @@ static int __exit davinci_spi_remove(struct platform_device *pdev) } static struct platform_driver davinci_spi_driver = { - .driver.name = "spi_davinci", + .driver = { + .name = "spi_davinci", + .owner = THIS_MODULE, + }, .remove = __exit_p(davinci_spi_remove), }; -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:17 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:17 +0530 Subject: [PATCH 45/49] spi: davinci: add additional comments In-Reply-To: <1289990661-30126-45-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> <1289990661-30126-35-git-send-email-nsekhar@ti.com> <1289990661-30126-36-git-send-email-nsekhar@ti.com> <1289990661-30126-37-git-send-email-nsekhar@ti.com> <1289990661-30126-38-git-send-email-nsekhar@ti.com> <1289990661-30126-39-git-send-email-nsekhar@ti.com> <1289990661-30126-40-git-send-email-nsekhar@ti.com> <1289990661-30126-41-git-send-email-nsekhar@ti.com> <1289990661-30126-42-git-send-email-nsekhar@ti.com> <1289990661-30126-43-git-send-email-nsekhar@ti.com> <1289990661-30126-44-git-send-email-nsekhar@ti.com> <1289990661-30126-45-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-46-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Add comments describing the platform data members and per-chip-select SPI configuration structure. Also, add some comments describing the what happens during the driver probe. Signed-off-by: Brian Niebuhr Tested-By: Michael Williamson Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/include/mach/spi.h | 32 ++++++++++++++++++++++++++++++ drivers/spi/davinci_spi.c | 7 ++++++ 2 files changed, 39 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/spi.h b/arch/arm/mach-davinci/include/mach/spi.h index 1f8b786..38f4da5 100644 --- a/arch/arm/mach-davinci/include/mach/spi.h +++ b/arch/arm/mach-davinci/include/mach/spi.h @@ -26,6 +26,20 @@ enum { SPI_VERSION_2, /* For DA8xx */ }; +/** + * davinci_spi_platform_data - Platform data for SPI master device on DaVinci + * + * @version: version of the SPI IP. Different DaVinci devices have slightly + * varying versions of the same IP. + * @num_chipselect: number of chipselects supported by this SPI master + * @intr_line: interrupt line used to connect the SPI IP to the ARM interrupt + * controller withn the SoC. Possible values are 0 and 1. + * @chip_sel: list of GPIOs which can act as chip-selects for the SPI. + * SPI_INTERN_CS denotes internal SPI chip-select. Not necessary + * to populate if all chip-selects are internal. + * @cshold_bug: set this to true if the SPI controller on your chip requires + * a write to CSHOLD bit in between transfers (like in DM355). + */ struct davinci_spi_platform_data { u8 version; u8 num_chipselect; @@ -34,6 +48,24 @@ struct davinci_spi_platform_data { bool cshold_bug; }; +/** + * davinci_spi_config - Per-chip-select configuration for SPI slave devices + * + * @wdelay: amount of delay between transmissions. Measured in number of + * SPI module clocks. + * @odd_parity: polarity of parity flag at the end of transmit data stream. + * 0 - odd parity, 1 - even parity. + * @parity_enable: enable transmission of parity at end of each transmit + * data stream. + * @io_type: type of IO transfer. Choose between polled, interrupt and DMA. + * @timer_disable: disable chip-select timers (setup and hold) + * @c2tdelay: chip-select setup time. Measured in number of SPI module clocks. + * @t2cdelay: chip-select hold time. Measured in number of SPI module clocks. + * @t2edelay: transmit data finished to SPI ENAn pin inactive time. Measured + * in number of SPI clocks. + * @c2edelay: chip-select active to SPI ENAn signal active time. Measured in + * number of SPI clocks. + */ struct davinci_spi_config { u8 wdelay; u8 odd_parity; diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 2ec5fd2..f40f1be 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -787,6 +787,13 @@ rx_dma_failed: /** * davinci_spi_probe - probe function for SPI Master Controller * @pdev: platform_device structure which contains plateform specific data + * + * According to Linux Device Model this function will be invoked by Linux + * with platform_device struct which contains the device specific info. + * This function will map the SPI controller's memory, register IRQ, + * Reset SPI controller and setting its registers to default value. + * It will invoke spi_bitbang_start to create work queue so that client driver + * can register transfer method to work queue. */ static int davinci_spi_probe(struct platform_device *pdev) { -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:19 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:19 +0530 Subject: [PATCH 47/49] spi: davinci: remove unnecessary private data member 'region_size' In-Reply-To: <1289990661-30126-47-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> <1289990661-30126-35-git-send-email-nsekhar@ti.com> <1289990661-30126-36-git-send-email-nsekhar@ti.com> <1289990661-30126-37-git-send-email-nsekhar@ti.com> <1289990661-30126-38-git-send-email-nsekhar@ti.com> <1289990661-30126-39-git-send-email-nsekhar@ti.com> <1289990661-30126-40-git-send-email-nsekhar@ti.com> <1289990661-30126-41-git-send-email-nsekhar@ti.com> <1289990661-30126-42-git-send-email-nsekhar@ti.com> <1289990661-30126-43-git-send-email-nsekhar@ti.com> <1289990661-30126-44-git-send-email-nsekhar@ti.com> <1289990661-30126-45-git-send-email-nsekhar@ti.com> <1289990661-30126-46-git-send-email-nsekhar@ti.com> <1289990661-30126-47-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-48-git-send-email-nsekhar@ti.com> Remove unnecesary private data member 'region_size' being used to store the size of SPI memory region. Instead, get the memory resource size directly from the platform data. Tested-By: Brian Niebuhr Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 13 ++++++------- 1 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index 42314a32..f6f63d0 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -129,7 +129,6 @@ struct davinci_spi { u8 version; resource_size_t pbase; void __iomem *base; - size_t region_size; u32 irq; struct completion done; @@ -835,17 +834,15 @@ static int davinci_spi_probe(struct platform_device *pdev) } davinci_spi->pbase = r->start; - davinci_spi->region_size = resource_size(r); davinci_spi->pdata = pdata; - mem = request_mem_region(r->start, davinci_spi->region_size, - pdev->name); + mem = request_mem_region(r->start, resource_size(r), pdev->name); if (mem == NULL) { ret = -EBUSY; goto free_master; } - davinci_spi->base = ioremap(r->start, davinci_spi->region_size); + davinci_spi->base = ioremap(r->start, resource_size(r)); if (davinci_spi->base == NULL) { ret = -ENOMEM; goto release_region; @@ -972,7 +969,7 @@ irq_free: unmap_io: iounmap(davinci_spi->base); release_region: - release_mem_region(davinci_spi->pbase, davinci_spi->region_size); + release_mem_region(davinci_spi->pbase, resource_size(r)); free_master: kfree(master); err: @@ -992,6 +989,7 @@ static int __exit davinci_spi_remove(struct platform_device *pdev) { struct davinci_spi *davinci_spi; struct spi_master *master; + struct resource *r; master = dev_get_drvdata(&pdev->dev); davinci_spi = spi_master_get_devdata(master); @@ -1003,7 +1001,8 @@ static int __exit davinci_spi_remove(struct platform_device *pdev) spi_master_put(master); free_irq(davinci_spi->irq, davinci_spi); iounmap(davinci_spi->base); - release_mem_region(davinci_spi->pbase, davinci_spi->region_size); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(davinci_spi->pbase, resource_size(r)); return 0; } -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:18 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:18 +0530 Subject: [PATCH 46/49] spi: davinci: add EF Johnson Technologies copyright In-Reply-To: <1289990661-30126-46-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> <1289990661-30126-35-git-send-email-nsekhar@ti.com> <1289990661-30126-36-git-send-email-nsekhar@ti.com> <1289990661-30126-37-git-send-email-nsekhar@ti.com> <1289990661-30126-38-git-send-email-nsekhar@ti.com> <1289990661-30126-39-git-send-email-nsekhar@ti.com> <1289990661-30126-40-git-send-email-nsekhar@ti.com> <1289990661-30126-41-git-send-email-nsekhar@ti.com> <1289990661-30126-42-git-send-email-nsekhar@ti.com> <1289990661-30126-43-git-send-email-nsekhar@ti.com> <1289990661-30126-44-git-send-email-nsekhar@ti.com> <1289990661-30126-45-git-send-email-nsekhar@ti.com> <1289990661-30126-46-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-47-git-send-email-nsekhar@ti.com> From: Brian Niebuhr Add copyright for EF Johnson Technologies since the driver has been majorly overhauled by Brian. Signed-off-by: Brian Niebuhr Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index f40f1be..42314a32 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2009 Texas Instruments. + * Copyright (C) 2010 EF Johnson Technologies * * 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 -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:21 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:21 +0530 Subject: [PATCH 49/49] spi: davinci: kconfig: add manufacturer name to prompt string In-Reply-To: <1289990661-30126-49-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> <1289990661-30126-35-git-send-email-nsekhar@ti.com> <1289990661-30126-36-git-send-email-nsekhar@ti.com> <1289990661-30126-37-git-send-email-nsekhar@ti.com> <1289990661-30126-38-git-send-email-nsekhar@ti.com> <1289990661-30126-39-git-send-email-nsekhar@ti.com> <1289990661-30126-40-git-send-email-nsekhar@ti.com> <1289990661-30126-41-git-send-email-nsekhar@ti.com> <1289990661-30126-42-git-send-email-nsekhar@ti.com> <1289990661-30126-43-git-send-email-nsekhar@ti.com> <1289990661-30126-44-git-send-email-nsekhar@ti.com> <1289990661-30126-45-git-send-email-nsekhar@ti.com> <1289990661-30126-46-git-send-email-nsekhar@ti.com> <1289990661-30126-47-git-send-email-nsekhar@ti.com> <1289990661-30126-48-git-send-email-nsekhar@ti.com> <1289990661-30126-49-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-50-git-send-email-nsekhar@ti.com> Add manufacturer name to the Kconfig prompt string and move the controller name to the begining of the prompt. This helps locate the driver easily among the list of existing drivers. While at it, also add information about being able to build the driver as module. Tested-By: Brian Niebuhr Signed-off-by: Sekhar Nori --- drivers/spi/Kconfig | 7 +++++-- 1 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 78f9fd0..a32796e 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -111,11 +111,14 @@ config SPI_COLDFIRE_QSPI will be called coldfire_qspi. config SPI_DAVINCI - tristate "SPI controller driver for DaVinci/DA8xx SoC's" + tristate "Texas Instruments DaVinci/DA8x/OMAP-L/AM1x SoC SPI controller" depends on SPI_MASTER && ARCH_DAVINCI select SPI_BITBANG help - SPI master controller for DaVinci and DA8xx SPI modules. + SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules. + + This driver can also be built as a module. The module will be called + davinci_spi. config SPI_EP93XX tristate "Cirrus Logic EP93xx SPI controller" -- 1.7.3.2 From nsekhar at ti.com Wed Nov 17 04:44:20 2010 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 17 Nov 2010 16:14:20 +0530 Subject: [PATCH 48/49] spi: davinci: shorten variable names In-Reply-To: <1289990661-30126-48-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <1289990661-30126-7-git-send-email-nsekhar@ti.com> <1289990661-30126-8-git-send-email-nsekhar@ti.com> <1289990661-30126-9-git-send-email-nsekhar@ti.com> <1289990661-30126-10-git-send-email-nsekhar@ti.com> <1289990661-30126-11-git-send-email-nsekhar@ti.com> <1289990661-30126-12-git-send-email-nsekhar@ti.com> <1289990661-30126-13-git-send-email-nsekhar@ti.com> <1289990661-30126-14-git-send-email-nsekhar@ti.com> <1289990661-30126-15-git-send-email-nsekhar@ti.com> <1289990661-30126-16-git-send-email-nsekhar@ti.com> <1289990661-30126-17-git-send-email-nsekhar@ti.com> <1289990661-30126-18-git-send-email-nsekhar@ti.com> <1289990661-30126-19-git-send-email-nsekhar@ti.com> <1289990661-30126-20-git-send-email-nsekhar@ti.com> <1289990661-30126-21-git-send-email-nsekhar@ti.com> <1289990661-30126-22-git-send-email-nsekhar@ti.com> <1289990661-30126-23-git-send-email-nsekhar@ti.com> <1289990661-30126-24-git-send-email-nsekhar@ti.com> <1289990661-30126-25-git-send-email-nsekhar@ti.com> <1289990661-30126-26-git-send-email-nsekhar@ti.com> <1289990661-30126-27-git-send-email-nsekhar@ti.com> <1289990661-30126-28-git-send-email-nsekhar@ti.com> <1289990661-30126-29-git-send-email-nsekhar@ti.com> <1289990661-30126-30-git-send-email-nsekhar@ti.com> <1289990661-30126-31-git-send-email-nsekhar@ti.com> <1289990661-30126-32-git-send-email-nsekhar@ti.com> <1289990661-30126-33-git-send-email-nsekhar@ti.com> <1289990661-30126-34-git-send-email-nsekhar@ti.com> <1289990661-30126-35-git-send-email-nsekhar@ti.com> <1289990661-30126-36-git-send-email-nsekhar@ti.com> <1289990661-30126-37-git-send-email-nsekhar@ti.com> <1289990661-30126-38-git-send-email-nsekhar@ti.com> <1289990661-30126-39-git-send-email-nsekhar@ti.com> <1289990661-30126-40-git-send-email-nsekhar@ti.com> <1289990661-30126-41-git-send-email-nsekhar@ti.com> <1289990661-30126-42-git-send-email-nsekhar@ti.com> <1289990661-30126-43-git-send-email-nsekhar@ti.com> <1289990661-30126-44-git-send-email-nsekhar@ti.com> <1289990661-30126-45-git-send-email-nsekhar@ti.com> <1289990661-30126-46-git-send-email-nsekhar@ti.com> <1289990661-30126-47-git-send-email-nsekhar@ti.com> <1289990661-30126-48-git-send-email-nsekhar@ti.com> Message-ID: <1289990661-30126-49-git-send-email-nsekhar@ti.com> Shorten names of local variables and structure members where possible. Local variables: * 'davinci_spi' is being renamed 'dspi' * 'davinci_spi_dma' is being renamed 'dma' Structure members: * 'dma_{tx|rx}_channel' is being renamed '{tx|rx}_channel' since the structure containing them is already called 'davinci_spi_dma' * 'davinci_spi_dma' in 'davinci_spi' structure is being renamed 'dma' Tested-By: Brian Niebuhr Signed-off-by: Sekhar Nori --- drivers/spi/davinci_spi.c | 409 ++++++++++++++++++++++----------------------- 1 files changed, 198 insertions(+), 211 deletions(-) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c index f6f63d0..6beab99 100644 --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -115,8 +115,8 @@ /* We have 2 DMA channels per CS, one for RX and one for TX */ struct davinci_spi_dma { - int dma_tx_channel; - int dma_rx_channel; + int tx_channel; + int rx_channel; int dummy_param_slot; enum dma_event_q eventq; }; @@ -138,7 +138,7 @@ struct davinci_spi { u8 rx_tmp_buf[SPI_TMP_BUFSZ]; int rcount; int wcount; - struct davinci_spi_dma dma_channels; + struct davinci_spi_dma dma; struct davinci_spi_platform_data *pdata; void (*get_rx)(u32 rx_data, struct davinci_spi *); @@ -149,42 +149,42 @@ struct davinci_spi { static struct davinci_spi_config davinci_spi_default_cfg; -static void davinci_spi_rx_buf_u8(u32 data, struct davinci_spi *davinci_spi) +static void davinci_spi_rx_buf_u8(u32 data, struct davinci_spi *dspi) { - if (davinci_spi->rx) { - u8 *rx = davinci_spi->rx; + if (dspi->rx) { + u8 *rx = dspi->rx; *rx++ = (u8)data; - davinci_spi->rx = rx; + dspi->rx = rx; } } -static void davinci_spi_rx_buf_u16(u32 data, struct davinci_spi *davinci_spi) +static void davinci_spi_rx_buf_u16(u32 data, struct davinci_spi *dspi) { - if (davinci_spi->rx) { - u16 *rx = davinci_spi->rx; + if (dspi->rx) { + u16 *rx = dspi->rx; *rx++ = (u16)data; - davinci_spi->rx = rx; + dspi->rx = rx; } } -static u32 davinci_spi_tx_buf_u8(struct davinci_spi *davinci_spi) +static u32 davinci_spi_tx_buf_u8(struct davinci_spi *dspi) { u32 data = 0; - if (davinci_spi->tx) { - const u8 *tx = davinci_spi->tx; + if (dspi->tx) { + const u8 *tx = dspi->tx; data = *tx++; - davinci_spi->tx = tx; + dspi->tx = tx; } return data; } -static u32 davinci_spi_tx_buf_u16(struct davinci_spi *davinci_spi) +static u32 davinci_spi_tx_buf_u16(struct davinci_spi *dspi) { u32 data = 0; - if (davinci_spi->tx) { - const u16 *tx = davinci_spi->tx; + if (dspi->tx) { + const u16 *tx = dspi->tx; data = *tx++; - davinci_spi->tx = tx; + dspi->tx = tx; } return data; } @@ -210,14 +210,14 @@ static inline void clear_io_bits(void __iomem *addr, u32 bits) */ static void davinci_spi_chipselect(struct spi_device *spi, int value) { - struct davinci_spi *davinci_spi; + struct davinci_spi *dspi; struct davinci_spi_platform_data *pdata; u8 chip_sel = spi->chip_select; - u16 spidat1_cfg = CS_DEFAULT; + u16 spidat1 = CS_DEFAULT; bool gpio_chipsel = false; - davinci_spi = spi_master_get_devdata(spi->master); - pdata = davinci_spi->pdata; + dspi = spi_master_get_devdata(spi->master); + pdata = dspi->pdata; if (pdata->chip_sel && chip_sel < pdata->num_chipselect && pdata->chip_sel[chip_sel] != SPI_INTERN_CS) @@ -234,11 +234,11 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) gpio_set_value(pdata->chip_sel[chip_sel], 1); } else { if (value == BITBANG_CS_ACTIVE) { - spidat1_cfg |= SPIDAT1_CSHOLD_MASK; - spidat1_cfg &= ~(0x1 << chip_sel); + spidat1 |= SPIDAT1_CSHOLD_MASK; + spidat1 &= ~(0x1 << chip_sel); } - iowrite16(spidat1_cfg, davinci_spi->base + SPIDAT1 + 2); + iowrite16(spidat1, dspi->base + SPIDAT1 + 2); } } @@ -252,12 +252,12 @@ static void davinci_spi_chipselect(struct spi_device *spi, int value) * Returns: calculated prescale - 1 for easy programming into SPI registers * or negative error number if valid prescalar cannot be updated. */ -static inline int davinci_spi_get_prescale(struct davinci_spi *davinci_spi, +static inline int davinci_spi_get_prescale(struct davinci_spi *dspi, u32 max_speed_hz) { int ret; - ret = DIV_ROUND_UP(clk_get_rate(davinci_spi->clk), max_speed_hz); + ret = DIV_ROUND_UP(clk_get_rate(dspi->clk), max_speed_hz); if (ret < 3 || ret > 256) return -EINVAL; @@ -278,12 +278,12 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) { - struct davinci_spi *davinci_spi; + struct davinci_spi *dspi; struct davinci_spi_config *spicfg; u8 bits_per_word = 0; u32 hz = 0, spifmt = 0, prescale = 0; - davinci_spi = spi_master_get_devdata(spi->master); + dspi = spi_master_get_devdata(spi->master); spicfg = (struct davinci_spi_config *)spi->controller_data; if (!spicfg) spicfg = &davinci_spi_default_cfg; @@ -302,13 +302,13 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, * 8bit, 16bit or 32bit transfer */ if (bits_per_word <= 8 && bits_per_word >= 2) { - davinci_spi->get_rx = davinci_spi_rx_buf_u8; - davinci_spi->get_tx = davinci_spi_tx_buf_u8; - davinci_spi->bytes_per_word[spi->chip_select] = 1; + dspi->get_rx = davinci_spi_rx_buf_u8; + dspi->get_tx = davinci_spi_tx_buf_u8; + dspi->bytes_per_word[spi->chip_select] = 1; } else if (bits_per_word <= 16 && bits_per_word >= 2) { - davinci_spi->get_rx = davinci_spi_rx_buf_u16; - davinci_spi->get_tx = davinci_spi_tx_buf_u16; - davinci_spi->bytes_per_word[spi->chip_select] = 2; + dspi->get_rx = davinci_spi_rx_buf_u16; + dspi->get_tx = davinci_spi_tx_buf_u16; + dspi->bytes_per_word[spi->chip_select] = 2; } else return -EINVAL; @@ -317,7 +317,7 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, /* Set up SPIFMTn register, unique to this chipselect. */ - prescale = davinci_spi_get_prescale(davinci_spi, hz); + prescale = davinci_spi_get_prescale(dspi, hz); if (prescale < 0) return prescale; @@ -345,7 +345,7 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, * - 4 pin with enable is (SPI_READY | SPI_NO_CS) */ - if (davinci_spi->version == SPI_VERSION_2) { + if (dspi->version == SPI_VERSION_2) { u32 delay = 0; @@ -375,10 +375,10 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, & SPIDELAY_C2EDELAY_MASK; } - iowrite32(delay, davinci_spi->base + SPIDELAY); + iowrite32(delay, dspi->base + SPIDELAY); } - iowrite32(spifmt, davinci_spi->base + SPIFMT0); + iowrite32(spifmt, dspi->base + SPIFMT0); return 0; } @@ -392,11 +392,11 @@ static int davinci_spi_setup_transfer(struct spi_device *spi, static int davinci_spi_setup(struct spi_device *spi) { int retval = 0; - struct davinci_spi *davinci_spi; + struct davinci_spi *dspi; struct davinci_spi_platform_data *pdata; - davinci_spi = spi_master_get_devdata(spi->master); - pdata = davinci_spi->pdata; + dspi = spi_master_get_devdata(spi->master); + pdata = dspi->pdata; /* if bits per word length is zero then set it default 8 */ if (!spi->bits_per_word) @@ -405,28 +405,24 @@ static int davinci_spi_setup(struct spi_device *spi) if (!(spi->mode & SPI_NO_CS)) { if ((pdata->chip_sel == NULL) || (pdata->chip_sel[spi->chip_select] == SPI_INTERN_CS)) - set_io_bits(davinci_spi->base + SPIPC0, - 1 << spi->chip_select); + set_io_bits(dspi->base + SPIPC0, 1 << spi->chip_select); } if (spi->mode & SPI_READY) - set_io_bits(davinci_spi->base + SPIPC0, SPIPC0_SPIENA_MASK); + set_io_bits(dspi->base + SPIPC0, SPIPC0_SPIENA_MASK); if (spi->mode & SPI_LOOP) - set_io_bits(davinci_spi->base + SPIGCR1, - SPIGCR1_LOOPBACK_MASK); + set_io_bits(dspi->base + SPIGCR1, SPIGCR1_LOOPBACK_MASK); else - clear_io_bits(davinci_spi->base + SPIGCR1, - SPIGCR1_LOOPBACK_MASK); + clear_io_bits(dspi->base + SPIGCR1, SPIGCR1_LOOPBACK_MASK); return retval; } -static int davinci_spi_check_error(struct davinci_spi *davinci_spi, - int int_status) +static int davinci_spi_check_error(struct davinci_spi *dspi, int int_status) { - struct device *sdev = davinci_spi->bitbang.master->dev.parent; + struct device *sdev = dspi->bitbang.master->dev.parent; if (int_status & SPIFLG_TIMEOUT_MASK) { dev_dbg(sdev, "SPI Time-out Error\n"); @@ -441,7 +437,7 @@ static int davinci_spi_check_error(struct davinci_spi *davinci_spi, return -EIO; } - if (davinci_spi->version == SPI_VERSION_2) { + if (dspi->version == SPI_VERSION_2) { if (int_status & SPIFLG_DLEN_ERR_MASK) { dev_dbg(sdev, "SPI Data Length Error\n"); return -EIO; @@ -465,35 +461,35 @@ static int davinci_spi_check_error(struct davinci_spi *davinci_spi, /** * davinci_spi_process_events - check for and handle any SPI controller events - * @davinci_spi: the controller data + * @dspi: the controller data * * This function will check the SPIFLG register and handle any events that are * detected there */ -static int davinci_spi_process_events(struct davinci_spi *davinci_spi) +static int davinci_spi_process_events(struct davinci_spi *dspi) { - u32 buf, status, errors = 0, data1_reg_val; + u32 buf, status, errors = 0, spidat1; - buf = ioread32(davinci_spi->base + SPIBUF); + buf = ioread32(dspi->base + SPIBUF); - if (davinci_spi->rcount > 0 && !(buf & SPIBUF_RXEMPTY_MASK)) { - davinci_spi->get_rx(buf & 0xFFFF, davinci_spi); - davinci_spi->rcount--; + if (dspi->rcount > 0 && !(buf & SPIBUF_RXEMPTY_MASK)) { + dspi->get_rx(buf & 0xFFFF, dspi); + dspi->rcount--; } - status = ioread32(davinci_spi->base + SPIFLG); + status = ioread32(dspi->base + SPIFLG); if (unlikely(status & SPIFLG_ERROR_MASK)) { errors = status & SPIFLG_ERROR_MASK; goto out; } - if (davinci_spi->wcount > 0 && !(buf & SPIBUF_TXFULL_MASK)) { - data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); - davinci_spi->wcount--; - data1_reg_val &= ~0xFFFF; - data1_reg_val |= 0xFFFF & davinci_spi->get_tx(davinci_spi); - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); + if (dspi->wcount > 0 && !(buf & SPIBUF_TXFULL_MASK)) { + spidat1 = ioread32(dspi->base + SPIDAT1); + dspi->wcount--; + spidat1 &= ~0xFFFF; + spidat1 |= 0xFFFF & dspi->get_tx(dspi); + iowrite32(spidat1, dspi->base + SPIDAT1); } out: @@ -502,21 +498,20 @@ out: static void davinci_spi_dma_callback(unsigned lch, u16 status, void *data) { - struct davinci_spi *davinci_spi = data; - struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; + struct davinci_spi *dspi = data; + struct davinci_spi_dma *dma = &dspi->dma; edma_stop(lch); if (status == DMA_COMPLETE) { - if (lch == davinci_spi_dma->dma_rx_channel) - davinci_spi->rcount = 0; - if (lch == davinci_spi_dma->dma_tx_channel) - davinci_spi->wcount = 0; + if (lch == dma->rx_channel) + dspi->rcount = 0; + if (lch == dma->tx_channel) + dspi->wcount = 0; } - if ((!davinci_spi->wcount && !davinci_spi->rcount) || - (status != DMA_COMPLETE)) - complete(&davinci_spi->done); + if ((!dspi->wcount && !dspi->rcount) || (status != DMA_COMPLETE)) + complete(&dspi->done); } /** @@ -530,57 +525,57 @@ static void davinci_spi_dma_callback(unsigned lch, u16 status, void *data) */ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) { - struct davinci_spi *davinci_spi; + struct davinci_spi *dspi; int data_type, ret; - u32 tx_data, data1_reg_val; + u32 tx_data, spidat1; u32 errors = 0; struct davinci_spi_config *spicfg; struct davinci_spi_platform_data *pdata; unsigned uninitialized_var(rx_buf_count); struct device *sdev; - davinci_spi = spi_master_get_devdata(spi->master); - pdata = davinci_spi->pdata; + dspi = spi_master_get_devdata(spi->master); + pdata = dspi->pdata; spicfg = (struct davinci_spi_config *)spi->controller_data; if (!spicfg) spicfg = &davinci_spi_default_cfg; - sdev = davinci_spi->bitbang.master->dev.parent; + sdev = dspi->bitbang.master->dev.parent; /* convert len to words based on bits_per_word */ - data_type = davinci_spi->bytes_per_word[spi->chip_select]; + data_type = dspi->bytes_per_word[spi->chip_select]; - davinci_spi->tx = t->tx_buf; - davinci_spi->rx = t->rx_buf; - davinci_spi->wcount = t->len / data_type; - davinci_spi->rcount = davinci_spi->wcount; + dspi->tx = t->tx_buf; + dspi->rx = t->rx_buf; + dspi->wcount = t->len / data_type; + dspi->rcount = dspi->wcount; - data1_reg_val = ioread32(davinci_spi->base + SPIDAT1); + spidat1 = ioread32(dspi->base + SPIDAT1); - clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); - set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); + clear_io_bits(dspi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); + set_io_bits(dspi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - INIT_COMPLETION(davinci_spi->done); + INIT_COMPLETION(dspi->done); if (spicfg->io_type == SPI_IO_TYPE_INTR) - set_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKINT); + set_io_bits(dspi->base + SPIINT, SPIINT_MASKINT); if (spicfg->io_type != SPI_IO_TYPE_DMA) { /* start the transfer */ - davinci_spi->wcount--; - tx_data = davinci_spi->get_tx(davinci_spi); - data1_reg_val &= 0xFFFF0000; - data1_reg_val |= tx_data & 0xFFFF; - iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); + dspi->wcount--; + tx_data = dspi->get_tx(dspi); + spidat1 &= 0xFFFF0000; + spidat1 |= tx_data & 0xFFFF; + iowrite32(spidat1, dspi->base + SPIDAT1); } else { - struct davinci_spi_dma *davinci_spi_dma; + struct davinci_spi_dma *dma; unsigned long tx_reg, rx_reg; struct edmacc_param param; void *rx_buf; - davinci_spi_dma = &davinci_spi->dma_channels; + dma = &dspi->dma; - tx_reg = (unsigned long)davinci_spi->pbase + SPIDAT1; - rx_reg = (unsigned long)davinci_spi->pbase + SPIBUF; + tx_reg = (unsigned long)dspi->pbase + SPIDAT1; + rx_reg = (unsigned long)dspi->pbase + SPIBUF; /* * Transmit DMA setup @@ -596,26 +591,24 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) if (t->tx_buf) { t->tx_dma = dma_map_single(&spi->dev, (void *)t->tx_buf, - davinci_spi->wcount, DMA_TO_DEVICE); + dspi->wcount, DMA_TO_DEVICE); if (dma_mapping_error(&spi->dev, t->tx_dma)) { dev_dbg(sdev, "Unable to DMA map %d bytes" - "TX buffer\n", - davinci_spi->wcount); + "TX buffer\n", dspi->wcount); return -ENOMEM; } } - param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_tx_channel); + param.opt = TCINTEN | EDMA_TCC(dma->tx_channel); param.src = t->tx_buf ? t->tx_dma : tx_reg; - param.a_b_cnt = davinci_spi->wcount << 16 | data_type; + param.a_b_cnt = dspi->wcount << 16 | data_type; param.dst = tx_reg; param.src_dst_bidx = t->tx_buf ? data_type : 0; param.link_bcntrld = 0xffff; param.src_dst_cidx = 0; param.ccnt = 1; - edma_write_slot(davinci_spi_dma->dma_tx_channel, ¶m); - edma_link(davinci_spi_dma->dma_tx_channel, - davinci_spi_dma->dummy_param_slot); + edma_write_slot(dma->tx_channel, ¶m); + edma_link(dma->tx_channel, dma->dummy_param_slot); /* * Receive DMA setup @@ -631,10 +624,10 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) if (t->rx_buf) { rx_buf = t->rx_buf; - rx_buf_count = davinci_spi->rcount; + rx_buf_count = dspi->rcount; } else { - rx_buf = davinci_spi->rx_tmp_buf; - rx_buf_count = sizeof(davinci_spi->rx_tmp_buf); + rx_buf = dspi->rx_tmp_buf; + rx_buf_count = sizeof(dspi->rx_tmp_buf); } t->rx_dma = dma_map_single(&spi->dev, rx_buf, rx_buf_count, @@ -643,71 +636,69 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) dev_dbg(sdev, "Couldn't DMA map a %d bytes RX buffer\n", rx_buf_count); if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, - davinci_spi->wcount, - DMA_TO_DEVICE); + dma_unmap_single(NULL, t->tx_dma, dspi->wcount, + DMA_TO_DEVICE); return -ENOMEM; } - param.opt = TCINTEN | EDMA_TCC(davinci_spi_dma->dma_rx_channel); + param.opt = TCINTEN | EDMA_TCC(dma->rx_channel); param.src = rx_reg; - param.a_b_cnt = davinci_spi->rcount << 16 | data_type; + param.a_b_cnt = dspi->rcount << 16 | data_type; param.dst = t->rx_dma; param.src_dst_bidx = (t->rx_buf ? data_type : 0) << 16; param.link_bcntrld = 0xffff; param.src_dst_cidx = 0; param.ccnt = 1; - edma_write_slot(davinci_spi_dma->dma_rx_channel, ¶m); + edma_write_slot(dma->rx_channel, ¶m); if (pdata->cshold_bug) - iowrite16(data1_reg_val >> 16, - davinci_spi->base + SPIDAT1 + 2); + iowrite16(spidat1 >> 16, dspi->base + SPIDAT1 + 2); - edma_start(davinci_spi_dma->dma_rx_channel); - edma_start(davinci_spi_dma->dma_tx_channel); - set_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); + edma_start(dma->rx_channel); + edma_start(dma->tx_channel); + set_io_bits(dspi->base + SPIINT, SPIINT_DMA_REQ_EN); } /* Wait for the transfer to complete */ if (spicfg->io_type != SPI_IO_TYPE_POLL) { - wait_for_completion_interruptible(&(davinci_spi->done)); + wait_for_completion_interruptible(&(dspi->done)); } else { - while (davinci_spi->rcount > 0 || davinci_spi->wcount > 0) { - errors = davinci_spi_process_events(davinci_spi); + while (dspi->rcount > 0 || dspi->wcount > 0) { + errors = davinci_spi_process_events(dspi); if (errors) break; cpu_relax(); } } - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKALL); + clear_io_bits(dspi->base + SPIINT, SPIINT_MASKALL); if (spicfg->io_type == SPI_IO_TYPE_DMA) { if (t->tx_buf) - dma_unmap_single(NULL, t->tx_dma, davinci_spi->wcount, + dma_unmap_single(NULL, t->tx_dma, dspi->wcount, DMA_TO_DEVICE); dma_unmap_single(NULL, t->rx_dma, rx_buf_count, DMA_FROM_DEVICE); - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_DMA_REQ_EN); + clear_io_bits(dspi->base + SPIINT, SPIINT_DMA_REQ_EN); } - clear_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); - set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); + clear_io_bits(dspi->base + SPIGCR1, SPIGCR1_SPIENA_MASK); + set_io_bits(dspi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); /* * Check for bit error, desync error,parity error,timeout error and * receive overflow errors */ if (errors) { - ret = davinci_spi_check_error(davinci_spi, errors); + ret = davinci_spi_check_error(dspi, errors); WARN(!ret, "%s: error reported but no error found!\n", dev_name(&spi->dev)); return ret; } - if (davinci_spi->rcount != 0 || davinci_spi->wcount != 0) { + if (dspi->rcount != 0 || dspi->wcount != 0) { dev_err(sdev, "SPI data transfer error\n"); return -EIO; } @@ -726,60 +717,56 @@ static int davinci_spi_bufs(struct spi_device *spi, struct spi_transfer *t) * If transfer length is zero then it will indicate the COMPLETION so that * davinci_spi_bufs function can go ahead. */ -static irqreturn_t davinci_spi_irq(s32 irq, void *context_data) +static irqreturn_t davinci_spi_irq(s32 irq, void *data) { - struct davinci_spi *davinci_spi = context_data; + struct davinci_spi *dspi = data; int status; - status = davinci_spi_process_events(davinci_spi); + status = davinci_spi_process_events(dspi); if (unlikely(status != 0)) - clear_io_bits(davinci_spi->base + SPIINT, SPIINT_MASKINT); + clear_io_bits(dspi->base + SPIINT, SPIINT_MASKINT); - if ((!davinci_spi->rcount && !davinci_spi->wcount) || status) - complete(&davinci_spi->done); + if ((!dspi->rcount && !dspi->wcount) || status) + complete(&dspi->done); return IRQ_HANDLED; } -static int davinci_spi_request_dma(struct davinci_spi *davinci_spi) +static int davinci_spi_request_dma(struct davinci_spi *dspi) { int r; - struct davinci_spi_dma *davinci_spi_dma = &davinci_spi->dma_channels; + struct davinci_spi_dma *dma = &dspi->dma; - r = edma_alloc_channel(davinci_spi_dma->dma_rx_channel, - davinci_spi_dma_callback, davinci_spi, - davinci_spi_dma->eventq); + r = edma_alloc_channel(dma->rx_channel, davinci_spi_dma_callback, dspi, + dma->eventq); if (r < 0) { pr_err("Unable to request DMA channel for SPI RX\n"); r = -EAGAIN; goto rx_dma_failed; } - r = edma_alloc_channel(davinci_spi_dma->dma_tx_channel, - davinci_spi_dma_callback, davinci_spi, - davinci_spi_dma->eventq); + r = edma_alloc_channel(dma->tx_channel, davinci_spi_dma_callback, dspi, + dma->eventq); if (r < 0) { pr_err("Unable to request DMA channel for SPI TX\n"); r = -EAGAIN; goto tx_dma_failed; } - r = edma_alloc_slot(EDMA_CTLR(davinci_spi_dma->dma_tx_channel), - EDMA_SLOT_ANY); + r = edma_alloc_slot(EDMA_CTLR(dma->tx_channel), EDMA_SLOT_ANY); if (r < 0) { pr_err("Unable to request SPI TX DMA param slot\n"); r = -EAGAIN; goto param_failed; } - davinci_spi_dma->dummy_param_slot = r; - edma_link(davinci_spi_dma->dummy_param_slot, - davinci_spi_dma->dummy_param_slot); + dma->dummy_param_slot = r; + edma_link(dma->dummy_param_slot, dma->dummy_param_slot); return 0; param_failed: - edma_free_channel(davinci_spi_dma->dma_tx_channel); + edma_free_channel(dma->tx_channel); tx_dma_failed: - edma_free_channel(davinci_spi_dma->dma_rx_channel); + edma_free_channel(dma->rx_channel); rx_dma_failed: return r; } @@ -798,7 +785,7 @@ rx_dma_failed: static int davinci_spi_probe(struct platform_device *pdev) { struct spi_master *master; - struct davinci_spi *davinci_spi; + struct davinci_spi *dspi; struct davinci_spi_platform_data *pdata; struct resource *r, *mem; resource_size_t dma_rx_chan = SPI_NO_RESOURCE; @@ -821,8 +808,8 @@ static int davinci_spi_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, master); - davinci_spi = spi_master_get_devdata(master); - if (davinci_spi == NULL) { + dspi = spi_master_get_devdata(master); + if (dspi == NULL) { ret = -ENOENT; goto free_master; } @@ -833,8 +820,8 @@ static int davinci_spi_probe(struct platform_device *pdev) goto free_master; } - davinci_spi->pbase = r->start; - davinci_spi->pdata = pdata; + dspi->pbase = r->start; + dspi->pdata = pdata; mem = request_mem_region(r->start, resource_size(r), pdev->name); if (mem == NULL) { @@ -842,48 +829,48 @@ static int davinci_spi_probe(struct platform_device *pdev) goto free_master; } - davinci_spi->base = ioremap(r->start, resource_size(r)); - if (davinci_spi->base == NULL) { + dspi->base = ioremap(r->start, resource_size(r)); + if (dspi->base == NULL) { ret = -ENOMEM; goto release_region; } - davinci_spi->irq = platform_get_irq(pdev, 0); - if (davinci_spi->irq <= 0) { + dspi->irq = platform_get_irq(pdev, 0); + if (dspi->irq <= 0) { ret = -EINVAL; goto unmap_io; } - ret = request_irq(davinci_spi->irq, davinci_spi_irq, 0, - dev_name(&pdev->dev), davinci_spi); + ret = request_irq(dspi->irq, davinci_spi_irq, 0, dev_name(&pdev->dev), + dspi); if (ret) goto unmap_io; - davinci_spi->bitbang.master = spi_master_get(master); - if (davinci_spi->bitbang.master == NULL) { + dspi->bitbang.master = spi_master_get(master); + if (dspi->bitbang.master == NULL) { ret = -ENODEV; goto irq_free; } - davinci_spi->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(davinci_spi->clk)) { + dspi->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dspi->clk)) { ret = -ENODEV; goto put_master; } - clk_enable(davinci_spi->clk); + clk_enable(dspi->clk); master->bus_num = pdev->id; master->num_chipselect = pdata->num_chipselect; master->setup = davinci_spi_setup; - davinci_spi->bitbang.chipselect = davinci_spi_chipselect; - davinci_spi->bitbang.setup_transfer = davinci_spi_setup_transfer; + dspi->bitbang.chipselect = davinci_spi_chipselect; + dspi->bitbang.setup_transfer = davinci_spi_setup_transfer; - davinci_spi->version = pdata->version; + dspi->version = pdata->version; - davinci_spi->bitbang.flags = SPI_NO_CS | SPI_LSB_FIRST | SPI_LOOP; - if (davinci_spi->version == SPI_VERSION_2) - davinci_spi->bitbang.flags |= SPI_READY; + dspi->bitbang.flags = SPI_NO_CS | SPI_LSB_FIRST | SPI_LOOP; + if (dspi->version == SPI_VERSION_2) + dspi->bitbang.flags |= SPI_READY; r = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (r) @@ -895,15 +882,15 @@ static int davinci_spi_probe(struct platform_device *pdev) if (r) dma_eventq = r->start; - davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs; + dspi->bitbang.txrx_bufs = davinci_spi_bufs; if (dma_rx_chan != SPI_NO_RESOURCE && dma_tx_chan != SPI_NO_RESOURCE && dma_eventq != SPI_NO_RESOURCE) { - davinci_spi->dma_channels.dma_rx_channel = dma_rx_chan; - davinci_spi->dma_channels.dma_tx_channel = dma_tx_chan; - davinci_spi->dma_channels.eventq = dma_eventq; + dspi->dma.rx_channel = dma_rx_chan; + dspi->dma.tx_channel = dma_tx_chan; + dspi->dma.eventq = dma_eventq; - ret = davinci_spi_request_dma(davinci_spi); + ret = davinci_spi_request_dma(dspi); if (ret) goto free_clk; @@ -913,19 +900,19 @@ static int davinci_spi_probe(struct platform_device *pdev) dma_eventq); } - davinci_spi->get_rx = davinci_spi_rx_buf_u8; - davinci_spi->get_tx = davinci_spi_tx_buf_u8; + dspi->get_rx = davinci_spi_rx_buf_u8; + dspi->get_tx = davinci_spi_tx_buf_u8; - init_completion(&davinci_spi->done); + init_completion(&dspi->done); /* Reset In/OUT SPI module */ - iowrite32(0, davinci_spi->base + SPIGCR0); + iowrite32(0, dspi->base + SPIGCR0); udelay(100); - iowrite32(1, davinci_spi->base + SPIGCR0); + iowrite32(1, dspi->base + SPIGCR0); /* Set up SPIPC0. CS and ENA init is done in davinci_spi_setup */ spipc0 = SPIPC0_DIFUN_MASK | SPIPC0_DOFUN_MASK | SPIPC0_CLKFUN_MASK; - iowrite32(spipc0, davinci_spi->base + SPIPC0); + iowrite32(spipc0, dspi->base + SPIPC0); /* initialize chip selects */ if (pdata->chip_sel) { @@ -936,40 +923,40 @@ static int davinci_spi_probe(struct platform_device *pdev) } if (pdata->intr_line) - iowrite32(SPI_INTLVL_1, davinci_spi->base + SPILVL); + iowrite32(SPI_INTLVL_1, dspi->base + SPILVL); else - iowrite32(SPI_INTLVL_0, davinci_spi->base + SPILVL); + iowrite32(SPI_INTLVL_0, dspi->base + SPILVL); - iowrite32(CS_DEFAULT, davinci_spi->base + SPIDEF); + iowrite32(CS_DEFAULT, dspi->base + SPIDEF); /* master mode default */ - set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_CLKMOD_MASK); - set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_MASTER_MASK); - set_io_bits(davinci_spi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); + set_io_bits(dspi->base + SPIGCR1, SPIGCR1_CLKMOD_MASK); + set_io_bits(dspi->base + SPIGCR1, SPIGCR1_MASTER_MASK); + set_io_bits(dspi->base + SPIGCR1, SPIGCR1_POWERDOWN_MASK); - ret = spi_bitbang_start(&davinci_spi->bitbang); + ret = spi_bitbang_start(&dspi->bitbang); if (ret) goto free_dma; - dev_info(&pdev->dev, "Controller at 0x%p\n", davinci_spi->base); + dev_info(&pdev->dev, "Controller at 0x%p\n", dspi->base); return ret; free_dma: - edma_free_channel(davinci_spi->dma_channels.dma_tx_channel); - edma_free_channel(davinci_spi->dma_channels.dma_rx_channel); - edma_free_slot(davinci_spi->dma_channels.dummy_param_slot); + edma_free_channel(dspi->dma.tx_channel); + edma_free_channel(dspi->dma.rx_channel); + edma_free_slot(dspi->dma.dummy_param_slot); free_clk: - clk_disable(davinci_spi->clk); - clk_put(davinci_spi->clk); + clk_disable(dspi->clk); + clk_put(dspi->clk); put_master: spi_master_put(master); irq_free: - free_irq(davinci_spi->irq, davinci_spi); + free_irq(dspi->irq, dspi); unmap_io: - iounmap(davinci_spi->base); + iounmap(dspi->base); release_region: - release_mem_region(davinci_spi->pbase, resource_size(r)); + release_mem_region(dspi->pbase, resource_size(r)); free_master: kfree(master); err: @@ -987,22 +974,22 @@ err: */ static int __exit davinci_spi_remove(struct platform_device *pdev) { - struct davinci_spi *davinci_spi; + struct davinci_spi *dspi; struct spi_master *master; struct resource *r; master = dev_get_drvdata(&pdev->dev); - davinci_spi = spi_master_get_devdata(master); + dspi = spi_master_get_devdata(master); - spi_bitbang_stop(&davinci_spi->bitbang); + spi_bitbang_stop(&dspi->bitbang); - clk_disable(davinci_spi->clk); - clk_put(davinci_spi->clk); + clk_disable(dspi->clk); + clk_put(dspi->clk); spi_master_put(master); - free_irq(davinci_spi->irq, davinci_spi); - iounmap(davinci_spi->base); + free_irq(dspi->irq, dspi); + iounmap(dspi->base); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(davinci_spi->pbase, resource_size(r)); + release_mem_region(dspi->pbase, resource_size(r)); return 0; } -- 1.7.3.2 From sshtylyov at mvista.com Wed Nov 17 05:15:06 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Wed, 17 Nov 2010 14:15:06 +0300 Subject: [PATCH 05/49] spi: davinci: remove unnecessary typecast In-Reply-To: <1289990661-30126-6-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> Message-ID: <4CE3B93A.7090408@mvista.com> Hello. On 17-11-2010 13:43, Sekhar Nori wrote: > The typecasting of SPI base address to davinci_spi_reg is > unused. You mean unnecessary? > Remove it. > Tested-By: Michael Williamson > Tested-By: Brian Niebuhr > Signed-off-by: Sekhar Nori WBR, Sergei From nsekhar at ti.com Wed Nov 17 05:35:09 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 17 Nov 2010 17:05:09 +0530 Subject: [PATCH 05/49] spi: davinci: remove unnecessary typecast In-Reply-To: <4CE3B93A.7090408@mvista.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <1289990661-30126-2-git-send-email-nsekhar@ti.com> <1289990661-30126-3-git-send-email-nsekhar@ti.com> <1289990661-30126-4-git-send-email-nsekhar@ti.com> <1289990661-30126-5-git-send-email-nsekhar@ti.com> <1289990661-30126-6-git-send-email-nsekhar@ti.com> <4CE3B93A.7090408@mvista.com> Message-ID: Hi Sergei, On Wed, Nov 17, 2010 at 16:45:06, Sergei Shtylyov wrote: > Hello. > > On 17-11-2010 13:43, Sekhar Nori wrote: > > > The typecasting of SPI base address to davinci_spi_reg is > > unused. > > You mean unnecessary? I used "unused" because "struct davinci_spi_reg" is actually undefined. Thanks, Sekhar From broonie at opensource.wolfsonmicro.com Wed Nov 17 07:31:37 2010 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Wed, 17 Nov 2010 13:31:37 +0000 Subject: [PATCH v5 04/12] spi: add ti-ssp spi master driver In-Reply-To: <4CE31F0E.7050103@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> <1289848334-8695-5-git-send-email-cyril@ti.com> <20101116072225.GF4074@angua.secretlab.ca> <4CE31F0E.7050103@ti.com> Message-ID: <20101117133136.GA19488@rakim.wolfsonmicro.main> On Tue, Nov 16, 2010 at 07:17:18PM -0500, Cyril Chemparathy wrote: > The ability to wait on multiple devices may come handy. In this This is essential for any helpers, otherwise we can't cope easily with mixes of GPIO and power or with regulators from multiple chips (like an extra DCDC, for example). From david-b at pacbell.net Tue Nov 16 08:19:00 2010 From: david-b at pacbell.net (David Brownell) Date: Tue, 16 Nov 2010 06:19:00 -0800 Subject: [PATCH v5 04/12] spi: add ti-ssp spi master driver In-Reply-To: <20101116072225.GF4074@angua.secretlab.ca> References: <1289848334-8695-1-git-send-email-cyril@ti.com> <1289848334-8695-5-git-send-email-cyril@ti.com> <20101116072225.GF4074@angua.secretlab.ca> Message-ID: <1289917140.1752.7.camel@helium> On Tue, 2010-11-16 at 00:22 -0700, Grant Likely wrote: > > After discussions about device init dependencies at plumbers, and > since this is the first SPI device driver I've reviewed since that > dicussion, this driver gets to be the first to implement the proposed > policy. :-) > > Change this to module_initcall(). Many spi and i2c drivers use > module or subsys_initcall to try and influence driver probe order so > that certain regulator chips get registered before the devices that > try to use them. This approach is insane. Last I observed the issue ... the situation was that various core drivers were required to be able to say that a key subsystem was functional ... with said subsystem required to boot properly. The dependency being, that is, driver on subsystem (but that subsystem needed a core/bus driver running before it could work. What's insane is that Linux *STILL* has no clean way to express that type of system dependency ... where driver dependencies are pure side effects. (Albeit ones that never change on many platforms, but where board-specific differences are routine, and have the same kind of indirect dependencies. - Dave From Jeff.Graham at brukeroptics.com Wed Nov 17 09:03:20 2010 From: Jeff.Graham at brukeroptics.com (Jeff Graham) Date: Wed, 17 Nov 2010 10:03:20 -0500 Subject: kernel improvements general applicability? Message-ID: Hello List, I have just been lurking around, and there seems to be quite a bit of improvements to the kernel in support of the hawkboard, and others that would seem to be generic davinci, and still others that are focused on the DM6446. The patch descriptions generally seem specific to certain boards. I am very interested in GPIO and SPI support for my TI Dm3730 (currently using TI part# TMDXEVM3730). Can anyone say if the patches for the above-mentioned boards/chips might work or have been tested to work on mine? Thanks! -------------- next part -------------- An HTML attachment was scrubbed... URL: From grant.likely at secretlab.ca Wed Nov 17 09:24:26 2010 From: grant.likely at secretlab.ca (Grant Likely) Date: Wed, 17 Nov 2010 08:24:26 -0700 Subject: [PATCH 00/49] spi: davinci: re-write existing driver In-Reply-To: <1289990661-30126-1-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> Message-ID: <20101117152426.GA5757@angua.secretlab.ca> On Wed, Nov 17, 2010 at 04:13:32PM +0530, Sekhar Nori wrote: > This patch series represents a break-up into reviewable portions > of the work originally done by Brian and posted here: > > https://patchwork.kernel.org/patch/114924/ > > While this series does not exactly add up to what Brian's patch > is, it is pretty close functionally. > > This series was tested by me on OMAP-L138, OMAP-L137, DM365 and DM355 > EVMs and on their OMAP-L138 boards (not EVMs) by Brian and Michael. > > The patch series applies to latest of Linus's tree in hope that > it will be considered for merge in 2.6.37 since the series is > about bug fixes and clean-up. > > This series along with some patches to add SPI platform support > to OMAP-L1x EVMs and Mity-DSP board is also available here: > > http://arago-project.org/git/projects/?p=linux-davinci.git;a=shortlog;h=refs/heads/davinci-spi-rewrite I went through all the patches, and it looks good to me. Is there a public git:// url I can use to pull this series? > 5 files changed, 587 insertions(+), 799 deletions(-) Nice diffstat. :-) g. From david-b at pacbell.net Wed Nov 17 09:25:33 2010 From: david-b at pacbell.net (David Brownell) Date: Wed, 17 Nov 2010 07:25:33 -0800 (PST) Subject: [PATCH v5 04/12] spi: add ti-ssp spi master driver In-Reply-To: <20101117133136.GA19488@rakim.wolfsonmicro.main> Message-ID: <781931.83221.qm@web180301.mail.gq1.yahoo.com> > On Tue, Nov 16, 2010 at 07:17:18PM > -0500, Cyril Chemparathy wrote: > > > The ability to wait on multiple devices may come > handy. ... You mean you'd like to add such a mechanism to the framework? Or do you want a driver-specific mechanism (non-portable)? If I had to do that, I'd just use existing kernel mechanisms to wait, and then issue SPI transactions as needed. Portable, straightforward. From grant.likely at secretlab.ca Wed Nov 17 10:11:30 2010 From: grant.likely at secretlab.ca (Grant Likely) Date: Wed, 17 Nov 2010 09:11:30 -0700 Subject: [PATCH v5 04/12] spi: add ti-ssp spi master driver In-Reply-To: <4CE31F0E.7050103@ti.com> References: <1289848334-8695-1-git-send-email-cyril@ti.com> <1289848334-8695-5-git-send-email-cyril@ti.com> <20101116072225.GF4074@angua.secretlab.ca> <4CE31F0E.7050103@ti.com> Message-ID: <20101117161130.GC5757@angua.secretlab.ca> On Tue, Nov 16, 2010 at 07:17:18PM -0500, Cyril Chemparathy wrote: > On 11/16/2010 02:47 AM, Grant Likely wrote: > > On Tue, Nov 16, 2010 at 12:22 AM, Grant Likely > > wrote: > >> On Mon, Nov 15, 2010 at 02:12:06PM -0500, Cyril Chemparathy wrote: > >>> This patch adds an SPI master implementation that operates on top of an > >>> underlying TI-SSP port. > >>> > >>> Signed-off-by: Cyril Chemparathy > >> [...] > >>> +static int __init ti_ssp_spi_init(void) > >>> +{ > >>> + return platform_driver_register(&ti_ssp_spi_driver); > >>> +} > >>> +subsys_initcall(ti_ssp_spi_init); > >> > >> After discussions about device init dependencies at plumbers, and > >> since this is the first SPI device driver I've reviewed since that > >> dicussion, this driver gets to be the first to implement the proposed > >> policy. :-) > >> > >> Change this to module_initcall(). Many spi and i2c drivers use > >> module or subsys_initcall to try and influence driver probe order so > >> that certain regulator chips get registered before the devices that > >> try to use them. This approach is insane. > >> > >> Instead, it is now incumbent on the board support code to ensure that > >> any device that depends on another device (including i2c or spi > >> regulators) will defer registration until the prerequisite devices are > >> bound to drivers. > >> > >> I don't *think* this change will affect anything in this particular > >> patch series, but if it does then let me know and I'll help you work out > >> how to fix it using a bus notifier. > > > > Oh, wait, spoke too soon. You do add a regulator in this series, so > > this change will require a fixup. The solution is to register an > > bus_notifier to the spi bus type before you start registering devices. > > It also requires deferring the musb_hdrc.1 and tps6116x registrations > > until the bus_notifier callback gets called with an indication that > > the regulator is bound. It will look something like this: > > > > static int tnetv107x_spi_notifier_call(struct notifier_block *nb, > > unsigned long event, void *__dev) > > { > > struct device *dev = __dev; > > > > if ((event == BUS_NOTIFY_BOUND_DRIVER) && (dev == (the-regulator))) { > > register-the-remaining-devices(); > > return NOTIFY_STOP; > > }; > > return NOTIFY_DONE; > > } > > > > static struct notifier_block tnetv107x_spi_nb = { > > .notifier_call = tnetv107x_spi_notifier_call, > > }; > > > > and then in the device registration code, before registering the > > regulator: > > > > bus_register_notifier(&spi_bus_type, &tnetv107x_spi_nb); > > > > Let me know if you have any trouble. > > The ability to wait on multiple devices may come handy. In this > particular case the backlight driver depends on both ssp-gpio and > regulator devices. Ideally, boards should be able to do something like... > > platform_device_register_after(&backlight_device, "spi1.0"); > platform_device_register_after(&backlight_device, "ti-ssp-gpio.0"); [cc'ing Rafael and Greg since they are certain to be interested in this discussion] It's a good start. However, I think it can be done more generically To start, I'm not a fan of matching by name. It's fragile because it makes assumptions about how devices will be names when they actually appear (ie. Sometimes .id is dynamically assigned). Ideally I'd prefer to have direct references (ie. pointers) to the devices that need to be registered, which *shouldn't* be difficult to handle. That guarantees that the correct device is always referenced. (aside: the device-tree use case provides this information by having direct 'phandle' references between dependencies.) Also, any dependency tracking must work across bus_types. It is not sufficient to only handle the platform_devices use-case. All bus types have this need. I see a few potential approaches. Option 1: Add a list of prerequisite devices to struct device and modify driver core so that it defers binding to a driver until all the prerequisites are already bound. This can probably be implemented in a way that works for all bus types transparently. Before binding a driver, the driver core would increase the ref count on each of the prereq devices, and decrease it again when the driver is removed. Option 2: On a per-bus_type basis have a separate registration and the bus type code would hold the device unregistered in a pending list. This option would also add a prerequisites list to struct device. Option 3: Don't try to handle it in the bus_type or driver core code at all, but rather provide board support code with helpers to make waiting for other devices easy. Even in this case I think it is probably still a good idea for the dependant devices to increment the refcount of the prereq devices. Perhaps something that is used like: void do_second_registration(void) { /* register list of dependant devices */ } and then in the primary registration function: ... device_notify_when_bound(&prereq_pdev->dev, do_second_registration); platform_device_add(prereq_pdev); ... which would reduce the boilerplate, but remain flexible. However, while flexable, option 3 doesn't solve the dependency tracking problem, and it still requires some gymnastics to handle multiple dependencies. On a related issue, it may also be desirable for each device to maintain a list of devices that have claimed dependency on it for the purpose of debugability. ... some other random comments below. > > ... and the device should get automatically registered once its > dependencies have been met. unfortunately it also means that the device is in limbo between when it is "registered" by board support code, and when it is actually registered. ie. It won't show up in sysfs. > > > Crude and incomplete code follows: > > struct platform_device_depend { > struct list_head node; > const char *dev_name; > struct platform_device *pdev; > }; > > LIST_HEAD(platform_device_depend); > > static int platform_device_register_after(struct platform_device *pdev, > const char *dev_name) > { > struct platform_device_depend *dep; > > dep = kzalloc(sizeof(*dep), GFP_KERNEL); > if (!dep) > return -ENOMEM; > dep->dev_name = dev_name; > dep->pdev = pdev; > list_add_tail(&dep->node, &platform_device_depend); > return 0; > } > > static int platform_device_try_register(struct platform_device *pdev) > { > struct platform_device_depend *dep; > > list_for_each_entry(dep, &platform_device_depend, node) { > if (dep->pdev == pdev) > return -EBUSY; > } > return platform_device_register(pdev); > } > > static int bus_notifier_func(struct notifier_block *nb, > unsigned long event, void *__dev) > { > struct platform_device_depend *dep, *tmp; > struct device *dev = __dev; > > if (event != BUS_NOTIFY_BOUND_DRIVER) > return NOTIFY_DONE; > > list_for_each_entry_safe(dep, tmp, &platform_device_depend, > node) { > if (strcmp(dep->dev_name, dev_name(dev)) != 0) > continue; > list_del(&dep->node); > platform_device_try_register(dep->pdev); Unfortunately this approach means the board support code will never know if there is a problem with registering the device. > kfree(dep); > } > > return NOTIFY_OK; If it is part of the core platform_bus_type code, then there is no need to use a notifier. The hook can be added directly. g. From nneitzke at appareo.com Wed Nov 17 10:40:46 2010 From: nneitzke at appareo.com (Nathan Neitzke) Date: Wed, 17 Nov 2010 10:40:46 -0600 Subject: MMC/SD umount issues Message-ID: <4AF3472E1928D648979691C8C65C30740457ED0743@apmailbox01> There is a known issue with the latest TI release for the DM355 linux-davinci-staging kernel regarding un-mounting a partition on an SD card after the SD card has been removed from the system. Since our embedded system relies on SD card for storage, and the user can remove the SD card at any point without our knowledge, this is a scenario we must handle. Data corruption is not a big issue, we have ways around it. Our largest concern is not having the system lock up when we un-mount the device. I have performed testing with several kernels. Our current kernel, the latest TI Davinci PSP release from the linux-davinci-staging, and the HEAD of the master branch for linux-davinci-staging all exhibit this behavior. However, the linux-davinci tree from khilman does not - the issue appears to be fixed. So I am assuming this was fixed in the core Linux MMC drivers (mmc_block driver possibly) since khilman's tree is now up to 2.6.36. My questions being - is there any way to determine which features/fixes (in regards to DM355 and related peripherals) we would lose out on by simply using the linux-davinci tree as a basis for our kernel? Or, is there any knowledge of when linux-davinci-staging will be rebased and integrated with the 2.6.36 release? >From our testing it appears it is locking up on umount because it attempts to sync the journal to the device to no avail since the device is unavailable. It continues to retry until the device is restarted. Thanks, Nathan NATHAN NEITZKE EMBEDDED SOFTWARE ENGINEER Appareo Systems, LLC 1810 NDSU Research Circle N Fargo, ND 58104 P: (701) 356-2200 Ext 285 F: (701) 356-3157 http://www.appareo.com mailto:nneitzke at appareo.com {*} NOTICE: This message {including attachments} is covered by the Electronic Communication Privacy Act, 18 U.S.C. sections 2510-2521, is CONFIDENTIAL and may also be protected by ATTORNEY-CLIENT OR OTHER PRIVILEGE. If you believe that it has been sent to you in error, do not read it. If you are not the intended recipient, you are hereby notified that any retention, dissemination, distribution, or copying of this communication is strictly prohibited. Please reply to the sender that you have received the message in error and then delete it. -------------- next part -------------- An HTML attachment was scrubbed... URL: From broonie at opensource.wolfsonmicro.com Wed Nov 17 11:23:58 2010 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Wed, 17 Nov 2010 17:23:58 +0000 Subject: [PATCH v5 04/12] spi: add ti-ssp spi master driver In-Reply-To: <20101117161130.GC5757@angua.secretlab.ca> References: <1289848334-8695-1-git-send-email-cyril@ti.com> <1289848334-8695-5-git-send-email-cyril@ti.com> <20101116072225.GF4074@angua.secretlab.ca> <4CE31F0E.7050103@ti.com> <20101117161130.GC5757@angua.secretlab.ca> Message-ID: <20101117172357.GC19488@rakim.wolfsonmicro.main> On Wed, Nov 17, 2010 at 09:11:30AM -0700, Grant Likely wrote: > To start, I'm not a fan of matching by name. It's fragile because it > makes assumptions about how devices will be names when they actually > appear (ie. Sometimes .id is dynamically assigned). Ideally I'd > prefer to have direct references (ie. pointers) to the devices that > need to be registered, which *shouldn't* be difficult to handle. That Pointers are a complete pain for buses where things are dynamically instantiated, the things you have to point at in machine data aren't the things that end up appearing during device registration when the struct device gets dynamically allocated so you need some way of translating them. This is why everyone ends up using dev_name for this sort of thing ATM, it's easy to put into the code and match against the actual struct device at runtime without needing to know about bus types. > guarantees that the correct device is always referenced. (aside: the > device-tree use case provides this information by having direct > 'phandle' references between dependencies.) Device tree makes this a lot easier. > Option 1: Add a list of prerequisite devices to struct device and > modify driver core so that it defers binding to a driver until all the > prerequisites are already bound. This can probably be implemented > in a way that works for all bus types transparently. Before binding > a driver, the driver core would increase the ref count on each of the > prereq devices, and decrease it again when the driver is removed. > Option 2: On a per-bus_type basis have a separate registration and the > bus type code would hold the device unregistered in a pending list. > This option would also add a prerequisites list to struct device. Per bus type sounds like a lot of hassle going through all the bus types but we may need to do that to handle autoprobable buses like USB. I do like the idea that the core would know what's going on so that things do become visible for the debuggabilty reasons you mention, though helpers could also provide similar infrastructure. > Option 3: Don't try to handle it in the bus_type or driver core code > at all, but rather provide board support code with helpers to make > waiting for other devices easy. Even in this case I think it is > probably still a good idea for the dependant devices to increment > the refcount of the prereq devices. Agreed on the refcounting. From cyril at ti.com Wed Nov 17 11:35:37 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Wed, 17 Nov 2010 12:35:37 -0500 Subject: [PATCH v5 04/12] spi: add ti-ssp spi master driver In-Reply-To: <20101117161130.GC5757@angua.secretlab.ca> References: <1289848334-8695-1-git-send-email-cyril@ti.com> <1289848334-8695-5-git-send-email-cyril@ti.com> <20101116072225.GF4074@angua.secretlab.ca> <4CE31F0E.7050103@ti.com> <20101117161130.GC5757@angua.secretlab.ca> Message-ID: <4CE41269.2040303@ti.com> On 11/17/2010 11:11 AM, Grant Likely wrote: [...] > To start, I'm not a fan of matching by name. It's fragile because it > makes assumptions about how devices will be names when they actually > appear (ie. Sometimes .id is dynamically assigned). Ideally I'd > prefer to have direct references (ie. pointers) to the devices that > need to be registered, which *shouldn't* be difficult to handle. That > guarantees that the correct device is always referenced. (aside: the > device-tree use case provides this information by having direct > 'phandle' references between dependencies.) The pointer approach is possibly problematic with devices that get registered by other drivers/masters (mfd, spi, i2c, etc.). With these devices the board doesn't really have a reference to the device in question. Something along the following lines may be better: int device_requires(struct device *dev, const char *res); int device_provides(struct device *provider, const char *res); where res is a string of the form "gpio:25", "regulator:vlcd", or even "dev:". device_requires() would typically be used by board implementations, and device_provides() would be called by gpiolib, regulator core, device core, etc. I guess this is somewhat along the lines of an earlier discussion on the PPC list ([1] below), except that the drivers don't get probed until specified prereqs are available. Regards Cyril. [1] http://www.spinics.net/lists/linux-embedded/msg02764.html From cyril at ti.com Wed Nov 17 11:54:07 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Wed, 17 Nov 2010 12:54:07 -0500 Subject: [PATCH v5 04/12] spi: add ti-ssp spi master driver In-Reply-To: <781931.83221.qm@web180301.mail.gq1.yahoo.com> References: <781931.83221.qm@web180301.mail.gq1.yahoo.com> Message-ID: <4CE416BF.6000707@ti.com> On 11/17/2010 10:25 AM, David Brownell wrote: > >> On Tue, Nov 16, 2010 at 07:17:18PM >> -0500, Cyril Chemparathy wrote: >> >>> The ability to wait on multiple devices may come >> handy. > > ... You mean you'd like to add such a > mechanism to the framework? Ideally in the framework. This appears to be a common enough problem across platforms. Regards Cyril. From broonie at opensource.wolfsonmicro.com Wed Nov 17 12:42:16 2010 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Wed, 17 Nov 2010 18:42:16 +0000 Subject: [PATCH] ASoC: davinci: fixes for multi-component In-Reply-To: <1289910429-29347-1-git-send-email-chris@edesix.com> References: <1289910429-29347-1-git-send-email-chris@edesix.com> Message-ID: <20101117184216.GD19488@rakim.wolfsonmicro.main> On Tue, Nov 16, 2010 at 12:27:09PM +0000, Chris Paulson-Ellis wrote: > Multi-component commit f0fba2ad broke a few things which this patch should > fix. Tested on the DM355 EVM. I've been as careful as I can, but it would be > good if those with access to other Davinci boards could test. Applied on the basis that we can always do further fixups and it looks sane on review - if people encounter issues in test could you please submit incremental patches? Thanks! From vm.rod25 at gmail.com Wed Nov 17 16:35:28 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Wed, 17 Nov 2010 16:35:28 -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> <4CE124AE.4080301@mvista.com> <4CE1560F.6080705@mvista.com> <4CE2AB73.3050109@mvista.com> <4CE2ECCD.8090603@criticallink.com> Message-ID: On Tue, Nov 16, 2010 at 3:53 PM, Victor Rodriguez wrote: > On Tue, Nov 16, 2010 at 3:21 PM, Victor Rodriguez wrote: >> On Tue, Nov 16, 2010 at 2:42 PM, Michael Williamson >> wrote: >>> On 11/16/2010 11:37 AM, Nori, Sekhar wrote: >>> >>>> On Tue, Nov 16, 2010 at 21:34:03, Sergei Shtylyov wrote: >>>> >>>>> >>>>>> HI Sergei and Sekhar >>>>> >>>>>> Thanks for check the patch >>>>> >>>>>> What I can do if you agree with this change is to leave da850.c as it >>>>>> is, >>>>> >>>>> ? ? No, please don't. >>>>> >>>>>> and declare >>>>> >>>>>> static short hawk_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_13, DA850_AXR_14, >>>>>> ? ? -1 >>>>>> }; >>>>> >>>>>> on the hawkboard file and call it insted of da850_mcasp_pins. >>>>> >>>>>> ? ? ret = davinci_cfg_reg_list(hawk_mcasp_pins); >>>>>> ? ? if (ret) >>>>>> ? ? ? ? ? ? pr_warning("%s: mcasp mux setup failed: %d\n", __func__, ret); >>>>> >>>>>> Please tell me if you agree with this change, I think is better >>>>>> because I do not touch any other file besides my board file. >>>>> >>>>> ? ? No, it's not really better. The generic list in da850.c should be more >>>>> complete, regardless... Ideally, you should go thru the DA850 manual and put in >>>>> that list all McASP pins that aren't already there. Then you can use your own >>>>> pin list if that *complete* pin list can't be used on your board. >>>> >>>> That will cause a bunch of pin conflicts on the EVM so it will need >>>> its own list too. >>>> >>> >>> >>> Help me out. ?Why do we need generic pin lists? >>> >>> It seems to me that the "generic pin list" for da850.c isn't practical for most >>> (if not all) of the peripherals. ?They should be done using __initdata in >>> each board file. >>> >>> Just a cursory glance at what's in da850.c highlights several items being set >>> up for the EVM and not generically. ?For example: >>> >>> - da850_uart1_pins and da850_uart2_pins: I believe both have RTS/CTS pins which >>> ?for a generic definition should be included as for UART0, but would then >>> ?be unused as the EVM doesn't use these pins in this function. >>> >>> - da850_mcasp_pins: if generic, must include all 16 AXR pins. ?I think you'd >>> ?be hard pressed to find a board configuration that would use all 16 AXR pins >>> ?for the McASP. ?I'm fairly sure the EVM uses the pins called out, and uses >>> ?other pins for other functions. ?So it's likely this structure wouldn't get used. >>> >>> - da850_mmcsd0_pins : includes 2 GPIO pins (specific to the EVM, though possible for >>> ?other boards) for the card detect and write protect signals. ?These pins are >>> ?completely arbitrary for that particular board design. I also believe that >>> ?the complete mmcsd0 port has 4 more data lines as part of it's peripheral, although >>> ?the driver doesn't support using them. >>> >>> - da850_emif25_pins interface doesn't include the generic pins for some of >>> ?the SDRAM functions. >>> >>> - da850_cpgmac_pins defines both RMII and MII pins. ?I don't think any board >>> ?would want to configure both sets at the same time. ?Seems like this should >>> ?never get used... >>> >>> It's also incomplete. ?What about the uPP pin list? ?Or the VPIF? ?Etc. >>> >>> I think a board file author should be familiar enough with the SoC to understand >>> what peripheral pins he should be configuring for his/her particular hardware setup >>> and explicitly specify them in the board file. >>> >>> If you remove the common pin-mux lists and move them to a board file, then once you >>> configure your specific platform, is there any more memory used than with >>> the common scheme? ?Of course, there would be replication of pin-mux code in the board >>> files that had identical configurations. ?Is that the driver for the current implementation? >>> >>> Is there a thread that hashes through the decisions on this that I should be googling? >>> >>> Thanks for any insight. >>> >>> -Mike >>> >>> >>> >>> >>> >>> >>> >> >> >> Hi Mike thanks for the comments I agree with the part of replication >> >> So do you recommend that the best way to solve this problem is to make >> a ?pin list that has all the 16 pins on da850.c ? >> >> I have checked and on the file >> >> arch/arm/mach-davinci/include/mach/mux.h >> >> are declared >> >> ? ? ? ?/* McASP function */ >> ? ? ? ?DA850_ACLKR, >> ? ? ? ?DA850_ACLKX, >> ? ? ? ?DA850_AFSR, >> ? ? ? ?DA850_AFSX, >> ? ? ? ?DA850_AHCLKR, >> ? ? ? ?DA850_AHCLKX, >> ? ? ? ?DA850_AMUTE, >> ? ? ? ?DA850_AXR_15, >> ? ? ? ?DA850_AXR_14, >> ? ? ? ?DA850_AXR_13, >> ? ? ? ?DA850_AXR_12, >> ? ? ? ?DA850_AXR_11, >> ? ? ? ?DA850_AXR_10, >> ? ? ? ?DA850_AXR_9, >> ? ? ? ?DA850_AXR_8, >> ? ? ? ?DA850_AXR_7, >> ? ? ? ?DA850_AXR_6, >> ? ? ? ?DA850_AXR_5, >> ? ? ? ?DA850_AXR_4, >> ? ? ? ?DA850_AXR_3, >> ? ? ? ?DA850_AXR_2, >> ? ? ? ?DA850_AXR_1, >> ? ? ? ?DA850_AXR_0, >> >> I have changed the pin list to >> >> >> 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_13, DA850_AXR_14, DA850_AXR_15, >> ? ? ? ?-1 >> }; >> >> and after checked with the hawk board it works fine >> >> I have put on the list all the available pins for McASP even if >> hawkboard does not need the last one DA850_AXR_15 , and is still >> working >> >> Please tell me which way do you recommend me >> >> Regards >> >> Victor Rodriguez >> > Hi Any update on this ? Regards Victor Rodriguez From synmyth at gmail.com Wed Nov 17 19:42:54 2010 From: synmyth at gmail.com (Yinglin Luan) Date: Thu, 18 Nov 2010 09:42:54 +0800 Subject: MMC/SD umount issues In-Reply-To: <4AF3472E1928D648979691C8C65C30740457ED0743@apmailbox01> References: <4AF3472E1928D648979691C8C65C30740457ED0743@apmailbox01> Message-ID: We have the same problem, when umount SD card, the kernel get locked. You may paste the log here to help others locate the problem for you. -- Yinglin Luan Best Regards -------------- next part -------------- An HTML attachment was scrubbed... URL: From gregkh at suse.de Wed Nov 17 23:46:11 2010 From: gregkh at suse.de (Greg KH) Date: Wed, 17 Nov 2010 21:46:11 -0800 Subject: [PATCH v5 04/12] spi: add ti-ssp spi master driver In-Reply-To: <20101117161130.GC5757@angua.secretlab.ca> References: <1289848334-8695-1-git-send-email-cyril@ti.com> <1289848334-8695-5-git-send-email-cyril@ti.com> <20101116072225.GF4074@angua.secretlab.ca> <4CE31F0E.7050103@ti.com> <20101117161130.GC5757@angua.secretlab.ca> Message-ID: <20101118054611.GB13567@suse.de> On Wed, Nov 17, 2010 at 09:11:30AM -0700, Grant Likely wrote: > Also, any dependency tracking must work across bus_types. It is not > sufficient to only handle the platform_devices use-case. All bus > types have this need. Agreed. > I see a few potential approaches. > > Option 1: Add a list of prerequisite devices to struct device and > modify driver core so that it defers binding to a driver until all the > prerequisites are already bound. This can probably be implemented > in a way that works for all bus types transparently. Before binding > a driver, the driver core would increase the ref count on each of the > prereq devices, and decrease it again when the driver is removed. I see loops happening easily that prevent any device from being bound. That could get tricky to debug :( > Option 2: On a per-bus_type basis have a separate registration and the > bus type code would hold the device unregistered in a pending list. > This option would also add a prerequisites list to struct device. When would we process the "pending list"? I can see something like this working, up to a point. Once device (or driver) registration is complete, we would walk the lists of busses and see if anything new wants to be bound again, now that something new has shown up, instead of just walking the single bus. But then we have the issue of devices that never are bound to anything, it could get a little "noisy" with them never binding, but we can't rely on the fact that not all devices are not bound to know when to stop trying. This might be the simplest to try out as we do almost all of this today in the driver core (walk the list and try to bind all unbound devices to a new driver.) Just extend that walking to walk all other busses as well. But this would require that the drivers know when they can't bind to a device due to something not being set up properly, which I don't know if is always true. > Option 3: Don't try to handle it in the bus_type or driver core code > at all, but rather provide board support code with helpers to make > waiting for other devices easy. Even in this case I think it is > probably still a good idea for the dependant devices to increment > the refcount of the prereq devices. > > Perhaps something that is used like: > > void do_second_registration(void) > { > /* register list of dependant devices */ > } > > and then in the primary registration function: > ... > device_notify_when_bound(&prereq_pdev->dev, > do_second_registration); > platform_device_add(prereq_pdev); > ... > > which would reduce the boilerplate, but remain flexible. However, > while flexable, option 3 doesn't solve the dependency tracking > problem, and it still requires some gymnastics to handle multiple > dependencies. > > On a related issue, it may also be desirable for each device to > maintain a list of devices that have claimed dependency on it for the > purpose of debugability. That might be the simplest as each "board type" knows which devices it wants to register and in what order, right? Hm, what about the case for dynamic devices that want to turn on and off different parts and bring them up in specific order depending on what "mode" the user wanted at the moment? That would force stuff to be hard coded in wierd ways I imagine. Ick, not a simple problem... greg k-h From nsekhar at ti.com Thu Nov 18 00:11:05 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 18 Nov 2010 11:41:05 +0530 Subject: kernel improvements general applicability? In-Reply-To: References: Message-ID: Hi Jeff, On Wed, Nov 17, 2010 at 20:33:20, Jeff Graham wrote: > Hello List, > > > > I have just been lurking around, and there seems to be quite a bit of > improvements to the kernel > > in support of the hawkboard, and others that would seem to be generic > davinci, and still others that are focused on the DM6446. The patch > descriptions generally seem specific to certain boards. > > > > I am very interested in GPIO and SPI support for my TI Dm3730 (currently > using TI part# TMDXEVM3730). In spite of being called a DM device DM3730 actually belongs to OMAP family. Patches relevant to this device would be posted on the linux-omap list. http://vger.kernel.org/vger-lists.html#linux-omap Thanks, Sekhar From nsekhar at ti.com Thu Nov 18 00:53:24 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 18 Nov 2010 12:23:24 +0530 Subject: [PATCH 00/49] spi: davinci: re-write existing driver In-Reply-To: <20101117152426.GA5757@angua.secretlab.ca> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> <20101117152426.GA5757@angua.secretlab.ca> Message-ID: Hi Grant, On Wed, Nov 17, 2010 at 20:54:26, Grant Likely wrote: > On Wed, Nov 17, 2010 at 04:13:32PM +0530, Sekhar Nori wrote: > > This patch series represents a break-up into reviewable portions > > of the work originally done by Brian and posted here: > > > > https://patchwork.kernel.org/patch/114924/ > > > > While this series does not exactly add up to what Brian's patch > > is, it is pretty close functionally. > > > > This series was tested by me on OMAP-L138, OMAP-L137, DM365 and DM355 > > EVMs and on their OMAP-L138 boards (not EVMs) by Brian and Michael. > > > > The patch series applies to latest of Linus's tree in hope that > > it will be considered for merge in 2.6.37 since the series is > > about bug fixes and clean-up. > > > > This series along with some patches to add SPI platform support > > to OMAP-L1x EVMs and Mity-DSP board is also available here: > > > > http://arago-project.org/git/projects/?p=linux-davinci.git;a=shortlog;h=refs/heads/davinci-spi-rewrite > > I went through all the patches, and it looks good to me. Is there a > public git:// url I can use to pull this series? Thanks for the quick review. 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 Thanks, Sekhar From sshtylyov at mvista.com Thu Nov 18 05:22:12 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Thu, 18 Nov 2010 14:22:12 +0300 Subject: [PATCH v2 1/2] mtd: NOR flash driver for OMAP-L137/AM17x In-Reply-To: <1289548911-21243-1-git-send-email-savinay.dharmappa@ti.com> References: <1289548911-21243-1-git-send-email-savinay.dharmappa@ti.com> Message-ID: <4CE50C64.60001@mvista.com> Hello. On 12-11-2010 11:01, 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. Please add back my signoff, omitted in this version. Some of the code, including a bug fix, was authored by me. > Signed-off-by: David Griego Signed-off-by: Sergei Shtylyov > Signed-off-by: Savinay Dharmappa > diff --git a/include/linux/mtd/latch-addr-flash.h b/include/linux/mtd/latch-addr-flash.h > new file mode 100644 > index 0000000..c77e7c9 > --- /dev/null > +++ b/include/linux/mtd/latch-addr-flash.h > @@ -0,0 +1,29 @@ > +/* > + * Interface for NOR flash driver whose high address lines are latched > + * > + * Copyright (C) 2008 MontaVista Software, Inc. > + * > + * This file is licensed underthe terms of the GNU General Public License ^ Oops, forgot a space here. WBR, Sergei From nsekhar at ti.com Thu Nov 18 05:31:24 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 18 Nov 2010 17:01:24 +0530 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: 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. Thanks, Sekhar From sshtylyov at mvista.com Thu Nov 18 05:52:26 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Thu, 18 Nov 2010 14:52:26 +0300 Subject: [PATCH v2 2/2] davinci: Platform support for OMAP-L137/AM17x NOR flash driver In-Reply-To: <1289548975-21296-1-git-send-email-savinay.dharmappa@ti.com> References: <1289548975-21296-1-git-send-email-savinay.dharmappa@ti.com> Message-ID: <4CE5137A.8060507@mvista.com> Hello. On 12-11-2010 11:02, Savinay Dharmappa wrote: > From: Aleksey Makarov > Adds platform support for OMAP-L137/AM17x NOR flash driver. > Also, configures chip select 3 to control NOR flash's upper > address lines. Please add back my signoff, omitted in this version. Some of the code, including bug fixes, was authored by me. > Signed-off-by: Aleksey Makarov Signed-off-by: Sergei Shtylyov > Signed-off-by: Savinay Dharmappa > diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c > index 1bb89d3..cd35198 100644 > --- a/arch/arm/mach-davinci/board-da830-evm.c > +++ b/arch/arm/mach-davinci/board-da830-evm.c [...] > @@ -429,6 +431,221 @@ static inline void da830_evm_init_nand(int mux_mode) > static inline void da830_evm_init_nand(int mux_mode) { } > #endif > > +#ifdef CONFIG_DA830_UI_NOR > +/* > + * Number of Address line Only "lines". > going to the NOR flash that are latched using > + * AEMIF address lines B_EMIF_BA0-B_EMIF_A12 on CS2. > +#define NOR_WINDOW_SIZE_LOG2 15 > +#define NOR_WINDOW_SIZE (1 << NOR_WINDOW_SIZE_LOG2) > + > +static struct { > + struct clk *clk; > + struct { > + struct resource *res; You're not using this field outside da830_evm_nor_init() now, so there's not much point in keeping it... > + void __iomem *addr; > + } latch, aemif; > +} da830_evm_nor; > +static void da830_evm_nor_set_window(unsigned long offset, void *data) > +{ > + /* > + * CS2 and CS3 address lines are used to address NOR flash. Address > + * line A0-A14 going to the NOR flash are latched using AEMIF address > + * lines B_EMIF_BA0-B_EMIF_A12 on CS2. Are they really latched, and not just used live when the NOR chip is accessed? What's the point of latching them? > +static int da830_evm_nor_init(void *data, int cs) > +{ > + /* Turn on AEMIF clocks */ > + da830_evm_nor.clk = clk_get(NULL, "aemif"); > + if (IS_ERR(da830_evm_nor.clk)) { > + pr_err("%s: could not get AEMIF clock\n", __func__); > + da830_evm_nor.clk = NULL; > + return -ENODEV; > + } > + clk_enable(da830_evm_nor.clk); > + > + da830_evm_nor.aemif.res = request_mem_region(DA8XX_AEMIF_CTL_BASE, > + SZ_32K, "AEMIF control"); No need to store it -- you don't use it afterwards. > + if (da830_evm_nor.aemif.res == NULL) { > + pr_err("%s: could not request AEMIF control region\n", > + __func__); > + goto err_clk; > + } [...] > + /* Setup the window to access the latch */ > + da830_evm_nor.latch.res = > + request_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE, > + "DA830 UI NOR address latch"); Same comment here... > + if (da830_evm_nor.latch.res == NULL) { > + pr_err("%s: could not request address latch region\n", > + __func__); > + goto err_aemif_ioremap; > + } [...] > +static inline void da830_evm_init_nor(int mux_mode) > +{ > + int ret; > + > + if (HAS_MMC) { > + pr_warning("WARNING: both MMC/SD and NOR are " > + "enabled, but they share AEMIF pins.\n" > + "\tDisable MMC/SD for NOR support.\n"); This line is over-indented. WBR, Sergei From savinay.dharmappa at ti.com Thu Nov 18 05:50:32 2010 From: savinay.dharmappa at ti.com (Savinay Dharmappa) Date: Thu, 18 Nov 2010 17:20:32 +0530 Subject: [PATCH v2 1/2] mtd: NOR flash driver for OMAP-L137/AM17x In-Reply-To: <4CE50C64.60001@mvista.com> References: <1289548911-21243-1-git-send-email-savinay.dharmappa@ti.com> <4CE50C64.60001@mvista.com> Message-ID: <003c01cb8716$ce4bb750$6ae325f0$@dharmappa@ti.com> Hi Sergei, On Thu, Nov 18, 2010 at 16:52:12, Sergei Shtylyov wrote: > Hello. > > On 12-11-2010 11:01, 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. > > Please add back my signoff, omitted in this version. Some of the code, > including a bug fix, was authored by me. > I'll surely do this. What about your Sign-off on 2/2 patch? Also shall I add Aleksey's Sign-off on this patch and David's Sign-off on 2/2 patch? Thanks, Savinay. From sshtylyov at mvista.com Thu Nov 18 06:09:00 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Thu, 18 Nov 2010 15:09:00 +0300 Subject: [PATCH v2 1/2] mtd: NOR flash driver for OMAP-L137/AM17x In-Reply-To: <003c01cb8716$ce4bb750$6ae325f0$@dharmappa@ti.com> References: <1289548911-21243-1-git-send-email-savinay.dharmappa@ti.com> <4CE50C64.60001@mvista.com> <003c01cb8716$ce4bb750$6ae325f0$@dharmappa@ti.com> Message-ID: <4CE5175C.2080505@mvista.com> Hello. On 18-11-2010 14:50, Savinay Dharmappa wrote: >>> 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. >> Please add back my signoff, omitted in this version. Some of the code, >> including a bug fix, was authored by me. > I'll surely do this. What about your Sign-off on 2/2 patch? Already replied. > Also shall I add > Aleksey's Sign-off on this patch AFAIR, there was his signoff on it, so yes. > and David's Sign-off on 2/2 patch? David didn't touch patch 2/2. > Thanks, > Savinay. WBR, Sergei From nsekhar at ti.com Thu Nov 18 07:14:18 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 18 Nov 2010 18:44:18 +0530 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: 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 From nsekhar at ti.com Thu Nov 18 10:21:59 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 18 Nov 2010 21:51:59 +0530 Subject: [PATCH v8 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: <4CE2ECCD.8090603@criticallink.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> <1289601535-6746-5-git-send-email-vm.rod25@gmail.com> <4CE124AE.4080301@mvista.com> <4CE1560F.6080705@mvista.com> <4CE2AB73.3050109@mvista.com> <4CE2ECCD.8090603@criticallink.com> Message-ID: Hi Michael, On Wed, Nov 17, 2010 at 02:12:53, Michael Williamson wrote: > > Help me out. Why do we need generic pin lists? > They might help in cases where all boards will use the same set of pins. For example, every one who uses I2C will most likely both the clock and data pins from the IP. For more complex peripherals with different pins options they serve a documentation purpose at best. > It seems to me that the "generic pin list" for da850.c isn't practical for most > (if not all) of the peripherals. They should be done using __initdata in > each board file. Yes, agreed. > > Just a cursory glance at what's in da850.c highlights several items being set > up for the EVM and not generically. For example: > > - da850_uart1_pins and da850_uart2_pins: I believe both have RTS/CTS pins which > for a generic definition should be included as for UART0, but would then > be unused as the EVM doesn't use these pins in this function. Yes, the generic pin list should have RTS and CTS pins defined for UART1 and UART2. This needs fixing. > > - da850_mcasp_pins: if generic, must include all 16 AXR pins. I think you'd > be hard pressed to find a board configuration that would use all 16 AXR pins > for the McASP. I'm fairly sure the EVM uses the pins called out, and uses > other pins for other functions. So it's likely this structure wouldn't get used. Yes, the generic pin list should either be completed or removed altogether and the existing pin list da850_mcasp_pins should be copied into the board file and called da850_evm_mcasp_pins. > > - da850_mmcsd0_pins : includes 2 GPIO pins (specific to the EVM, though possible for > other boards) for the card detect and write protect signals. These pins are > completely arbitrary for that particular board design. I also believe that > the complete mmcsd0 port has 4 more data lines as part of it's peripheral, although > the driver doesn't support using them. This is incorrect again. The generic pin list should be completed (or removed) and the existing list should be copied into the EVM board file as da850_evm_mmcsd0_pins. > > - da850_emif25_pins interface doesn't include the generic pins for some of > the SDRAM functions. Yes, this should be completed (or removed). This list is unused anyway. > > - da850_cpgmac_pins defines both RMII and MII pins. I don't think any board > would want to configure both sets at the same time. Seems like this should > never get used... Agreed. > > It's also incomplete. What about the uPP pin list? Or the VPIF? Etc. These should be added as the drivers for these devices are supported. > > I think a board file author should be familiar enough with the SoC to understand > what peripheral pins he should be configuring for his/her particular hardware setup > and explicitly specify them in the board file. Agree. > > If you remove the common pin-mux lists and move them to a board file, then once you > configure your specific platform, is there any more memory used than with > the common scheme? Of course, there would be replication of pin-mux code in the board There is no memory wastage. All the pin lists are init data. I too prefer all generic pin lists which are most likely not going to be used at all to be removed. Unused stuff like this will only make code difficult to read. Thanks, Sekhar From vm.rod25 at gmail.com Thu Nov 18 10:38:34 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Thu, 18 Nov 2010 10:38:34 -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> <4CE124AE.4080301@mvista.com> <4CE1560F.6080705@mvista.com> <4CE2AB73.3050109@mvista.com> <4CE2ECCD.8090603@criticallink.com> Message-ID: On Thu, Nov 18, 2010 at 10:21 AM, Nori, Sekhar wrote: > Hi Michael, > > On Wed, Nov 17, 2010 at 02:12:53, Michael Williamson wrote: >> >> Help me out. ?Why do we need generic pin lists? >> > > They might help in cases where all boards will use the same set of > pins. For example, every one who uses I2C will most likely both the > clock and data pins from the IP. For more complex peripherals with > different pins options they serve a documentation purpose at best. > >> It seems to me that the "generic pin list" for da850.c isn't practical for most >> (if not all) of the peripherals. ?They should be done using __initdata in >> each board file. > > Yes, agreed. > >> >> Just a cursory glance at what's in da850.c highlights several items being set >> up for the EVM and not generically. ?For example: >> >> - da850_uart1_pins and da850_uart2_pins: I believe both have RTS/CTS pins which >> ? for a generic definition should be included as for UART0, but would then >> ? be unused as the EVM doesn't use these pins in this function. > > Yes, the generic pin list should have RTS and CTS pins defined for UART1 > and UART2. This needs fixing. > >> >> - da850_mcasp_pins: if generic, must include all 16 AXR pins. ?I think you'd >> ? be hard pressed to find a board configuration that would use all 16 AXR pins >> ? for the McASP. ?I'm fairly sure the EVM uses the pins called out, and uses >> ? other pins for other functions. ?So it's likely this structure wouldn't get used. > > Yes, the generic pin list should either be completed or removed > altogether and the existing pin list da850_mcasp_pins should be > copied into the board file and called da850_evm_mcasp_pins. > >> >> - da850_mmcsd0_pins : includes 2 GPIO pins (specific to the EVM, though possible for >> ? other boards) for the card detect and write protect signals. ?These pins are >> ? completely arbitrary for that particular board design. I also believe that >> ? the complete mmcsd0 port has 4 more data lines as part of it's peripheral, although >> ? the driver doesn't support using them. > > This is incorrect again. The generic pin list should be completed > (or removed) and the existing list should be copied into the EVM board > file as da850_evm_mmcsd0_pins. > >> >> - da850_emif25_pins interface doesn't include the generic pins for some of >> ? the SDRAM functions. > > Yes, this should be completed (or removed). This list is unused anyway. > >> >> - da850_cpgmac_pins defines both RMII and MII pins. ?I don't think any board >> ? would want to configure both sets at the same time. ?Seems like this should >> ? never get used... > > Agreed. > >> >> It's also incomplete. ?What about the uPP pin list? ?Or the VPIF? ?Etc. > > These should be added as the drivers for these devices are > supported. > >> >> I think a board file author should be familiar enough with the SoC to understand >> what peripheral pins he should be configuring for his/her particular hardware setup >> and explicitly specify them in the board file. > > Agree. > >> >> If you remove the common pin-mux lists and move them to a board file, then once you >> configure your specific platform, is there any more memory used than with >> the common scheme? ?Of course, there would be replication of pin-mux code in the board > > There is no memory wastage. All the pin lists are init data. > > I too prefer all generic pin lists which are most likely not > going to be used at all to be removed. Unused stuff like this > will only make code difficult to read. > > Thanks, > Sekhar > > HI Sekhar Let me see if I understand, you prefer to remove the unused pin list, but that will produce a lot of changes on each board that used these pin list. I do not have those boards, in order to test if that works :( I want to help to fix it and give a complete and right support to the hawk board. Please give me a tip of how to do it Regards Victor Rodriguez From nsekhar at ti.com Thu Nov 18 10:47:11 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 18 Nov 2010 22:17:11 +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> <4CE124AE.4080301@mvista.com> <4CE1560F.6080705@mvista.com> <4CE2AB73.3050109@mvista.com> <4CE2ECCD.8090603@criticallink.com> Message-ID: Hi Victor, On Thu, Nov 18, 2010 at 22:08:34, Victor Rodriguez wrote: [...] > HI Sekhar > > Let me see if I understand, you prefer to remove the unused pin list, > but that will produce a lot of changes on each board that used these > pin list. I do not have those boards, in order to test if that works > :( > > I want to help to fix it and give a complete and right support to the > hawk board. > > Please give me a tip of how to do it If you make the changes and post the patches, we can ack for the EVM. Mike would be able to test on the Mity-DSP platform. Anyway, I expect the changes to be fairly innocuous. Thanks, Sekhar From GabrieleF at cosmed.it Thu Nov 18 11:06:25 2010 From: GabrieleF at cosmed.it (Gabriele Filosofi) Date: Thu, 18 Nov 2010 18:06:25 +0100 Subject: How to add a da850-based custom board to mainline kernel ? In-Reply-To: <8209BF53A743EA48B99B591AEFD2CB1D46166A74@ServerMail.intranet.cosmed.it> References: <8209BF53A743EA48B99B591AEFD2CB1D46166A74@ServerMail.intranet.cosmed.it> Message-ID: Hi All, we would like to develop a custom board and add it to mainline kernel. I've read the first thing I need to have is a machine ID. After that I need to create a basic configuration file for our board, something like: arch/arm/configs/BOARDNAME_defconfig arch/arm/mach-PROCESSORNAME/Kconfig arch/arm/mach-PROCESSORNAME/Makefile arch/arm/mach-PROCESSORNAME/BOARDNAME.c Now, our board is based on an existing one, i.e. the LogicPD's AM1808 System-On-Module. This SOM is also plugged onto the AM1808's EVM board, already registered under PROCESSORNAME = "davinci" BOARDNAME = "board-da850-evm.c" Please, does anybody know how to proceed in this case? Thanks a lot, Gabriele -------------- next part -------------- An HTML attachment was scrubbed... URL: From khilman at deeprootsystems.com Thu Nov 18 17:57:02 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Thu, 18 Nov 2010 15:57:02 -0800 Subject: [PATCH v8 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: (Sekhar Nori's message of "Thu, 18 Nov 2010 21:51:59 +0530") References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> <1289601535-6746-5-git-send-email-vm.rod25@gmail.com> <4CE124AE.4080301@mvista.com> <4CE1560F.6080705@mvista.com> <4CE2AB73.3050109@mvista.com> <4CE2ECCD.8090603@criticallink.com> Message-ID: <87fwuyfanl.fsf@deeprootsystems.com> "Nori, Sekhar" writes: > Hi Michael, > > On Wed, Nov 17, 2010 at 02:12:53, Michael Williamson wrote: >> >> Help me out. Why do we need generic pin lists? >> > > They might help in cases where all boards will use the same set of > pins. For example, every one who uses I2C will most likely both the > clock and data pins from the IP. For more complex peripherals with > different pins options they serve a documentation purpose at best. > >> It seems to me that the "generic pin list" for da850.c isn't practical for most >> (if not all) of the peripherals. They should be done using __initdata in >> each board file. > > Yes, agreed. > >> >> Just a cursory glance at what's in da850.c highlights several items being set >> up for the EVM and not generically. For example: >> >> - da850_uart1_pins and da850_uart2_pins: I believe both have RTS/CTS pins which >> for a generic definition should be included as for UART0, but would then >> be unused as the EVM doesn't use these pins in this function. > > Yes, the generic pin list should have RTS and CTS pins defined for UART1 > and UART2. This needs fixing. > >> >> - da850_mcasp_pins: if generic, must include all 16 AXR pins. I think you'd >> be hard pressed to find a board configuration that would use all 16 AXR pins >> for the McASP. I'm fairly sure the EVM uses the pins called out, and uses >> other pins for other functions. So it's likely this structure wouldn't get used. > > Yes, the generic pin list should either be completed or removed > altogether and the existing pin list da850_mcasp_pins should be > copied into the board file and called da850_evm_mcasp_pins. > >> >> - da850_mmcsd0_pins : includes 2 GPIO pins (specific to the EVM, though possible for >> other boards) for the card detect and write protect signals. These pins are >> completely arbitrary for that particular board design. I also believe that >> the complete mmcsd0 port has 4 more data lines as part of it's peripheral, although >> the driver doesn't support using them. > > This is incorrect again. The generic pin list should be completed > (or removed) and the existing list should be copied into the EVM board > file as da850_evm_mmcsd0_pins. > >> >> - da850_emif25_pins interface doesn't include the generic pins for some of >> the SDRAM functions. > > Yes, this should be completed (or removed). This list is unused anyway. > >> >> - da850_cpgmac_pins defines both RMII and MII pins. I don't think any board >> would want to configure both sets at the same time. Seems like this should >> never get used... > > Agreed. > >> >> It's also incomplete. What about the uPP pin list? Or the VPIF? Etc. > > These should be added as the drivers for these devices are > supported. > >> >> I think a board file author should be familiar enough with the SoC to understand >> what peripheral pins he should be configuring for his/her particular hardware setup >> and explicitly specify them in the board file. > > Agree. > >> >> If you remove the common pin-mux lists and move them to a board file, then once you >> configure your specific platform, is there any more memory used than with >> the common scheme? Of course, there would be replication of pin-mux code in the board > > There is no memory wastage. All the pin lists are init data. > > I too prefer all generic pin lists which are most likely not > going to be used at all to be removed. Unused stuff like this > will only make code difficult to read. FWIW, I agree. Now, who wants to tackle it? Kevin From khilman at deeprootsystems.com Thu Nov 18 17:59:53 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Thu, 18 Nov 2010 15:59:53 -0800 Subject: [PATCH v2] da850-evm: UI expander gpio_set_value can sleep, use _cansleep In-Reply-To: <1289832172-21947-1-git-send-email-bengardiner@nanometrics.ca> (Ben Gardiner's message of "Mon, 15 Nov 2010 09:42:52 -0500") References: <1289832172-21947-1-git-send-email-bengardiner@nanometrics.ca> Message-ID: <874obefaiu.fsf@deeprootsystems.com> Ben Gardiner writes: > When the RMII PHY on the UI board is enabled with CONFIG_DA850_UI_RMII then then > following will be printed to the console when warnings are also enabled: > > WARNING: at drivers/gpio/gpiolib.c:1567 __gpio_set_value+0x4c/0x5c() > Modules linked in: > [] (unwind_backtrace+0x0/0xf8) from [] (warn_slowpath_common+0x4c/0x64) > [] (warn_slowpath_common+0x4c/0x64) from [] (warn_slowpath_null+0x1c/0x24) > [] (warn_slowpath_null+0x1c/0x24) from [] (__gpio_set_value+0x4c/0x5c) > [] (__gpio_set_value+0x4c/0x5c) from [] (da850_evm_ui_expander_setup+0x1e4/0x2 > 44) > [] (da850_evm_ui_expander_setup+0x1e4/0x244) from [] (pca953x_probe+0x1f8/0x29 > 0) > > > Traced the WARN_ON to the gpio_set_value(rmii_sel,0) call in > da850_evm_setup_emac_rmii. Replacing the call with the _cansleep variant > results in no more warning. Also replacing the gpio_set_value calls in the > teardown function. > > Signed-off-by: Ben Gardiner > Reviewed-by: Chris Cordahi > Reviewed-by: Kevin Killman s/Killman/Hilman/ with only one 'L' Other than that, looks good. And thanks for the detailed description on how this was tested. Applying and queuing for 2.6.38. Kevin > -- > > Tested by modifying the config to allow pca953x as a module and modifying the > board setup to forcibly run the NAND/NOR setup because I am using a UBIFS > rootfs in NAND w/o initrd (yet) then inspecting the kernel output after > 'insmod pca953x.ko' and 'rmmod -f pca953x.ko'; with this patch there are no > WARNINGs printed. > > Changes since V1: > * added _cansleep variant calls to the teardown() function, suggested by > Kevin Hillman. > * changed patch subject line as per Kevin Hillman's suggestion. > > --- > arch/arm/mach-davinci/board-da850-evm.c | 8 ++++---- > 1 files changed, 4 insertions(+), 4 deletions(-) > > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index c6e11c6..f89b0b7 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -266,7 +266,7 @@ static inline void da850_evm_setup_emac_rmii(int rmii_sel) > struct davinci_soc_info *soc_info = &davinci_soc_info; > > soc_info->emac_pdata->rmii_en = 1; > - gpio_set_value(rmii_sel, 0); > + gpio_set_value_cansleep(rmii_sel, 0); > } > #else > static inline void da850_evm_setup_emac_rmii(int rmii_sel) { } > @@ -325,9 +325,9 @@ static int da850_evm_ui_expander_teardown(struct i2c_client *client, > unsigned gpio, unsigned ngpio, void *c) > { > /* deselect all functionalities */ > - gpio_set_value(gpio + 5, 1); > - gpio_set_value(gpio + 6, 1); > - gpio_set_value(gpio + 7, 1); > + gpio_set_value_cansleep(gpio + 5, 1); > + gpio_set_value_cansleep(gpio + 6, 1); > + gpio_set_value_cansleep(gpio + 7, 1); > > gpio_free(gpio + 5); > gpio_free(gpio + 6); From khilman at deeprootsystems.com Thu Nov 18 18:06:10 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Thu, 18 Nov 2010 16:06:10 -0800 Subject: [PATCH v3] da850-evm: allow pca953x module build In-Reply-To: <1289926710-29892-1-git-send-email-bengardiner@nanometrics.ca> (Ben Gardiner's message of "Tue, 16 Nov 2010 11:58:30 -0500") References: <1289926710-29892-1-git-send-email-bengardiner@nanometrics.ca> Message-ID: <87sjyydvnx.fsf@deeprootsystems.com> Ben Gardiner writes: > Change the mach-davinci Kconfig file so that GPIO_PCA953X is default when > MACH_DAVINCI_DA850_EVM is set instead of always selecting. This allows users > to compile pca953x as a module. > > Signed-off-by: Ben Gardiner > CC: Sergei Shtylyov > CC: Nori, Sekhar > > --- > > Changes since V2: > * keep all Kconfig changes local to arch/arm/mach-davinci by exploting the > fact that attribute assigment to config entries can span multiple files. This is a neat trick, I wasn't aware of this either. [...] > +config GPIO_PCA953X > + default y if MACH_DAVINCI_DA850_EVM > + the 'y' part is redundant. You can just do: default MACH_DAVINCI_DA850_EVM Kevin From vm.rod25 at gmail.com Thu Nov 18 18:17:14 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Thu, 18 Nov 2010 18:17:14 -0600 Subject: [PATCH v8 4/9] davinci: McASP configuration for Omapl138-Hawkboard In-Reply-To: <87fwuyfanl.fsf@deeprootsystems.com> References: <1289601535-6746-1-git-send-email-vm.rod25@gmail.com> <1289601535-6746-5-git-send-email-vm.rod25@gmail.com> <4CE124AE.4080301@mvista.com> <4CE1560F.6080705@mvista.com> <4CE2AB73.3050109@mvista.com> <4CE2ECCD.8090603@criticallink.com> <87fwuyfanl.fsf@deeprootsystems.com> Message-ID: On Thu, Nov 18, 2010 at 5:57 PM, Kevin Hilman wrote: > "Nori, Sekhar" writes: > >> Hi Michael, >> >> On Wed, Nov 17, 2010 at 02:12:53, Michael Williamson wrote: >>> >>> Help me out. ?Why do we need generic pin lists? >>> >> >> They might help in cases where all boards will use the same set of >> pins. For example, every one who uses I2C will most likely both the >> clock and data pins from the IP. For more complex peripherals with >> different pins options they serve a documentation purpose at best. >> >>> It seems to me that the "generic pin list" for da850.c isn't practical for most >>> (if not all) of the peripherals. ?They should be done using __initdata in >>> each board file. >> >> Yes, agreed. >> >>> >>> Just a cursory glance at what's in da850.c highlights several items being set >>> up for the EVM and not generically. ?For example: >>> >>> - da850_uart1_pins and da850_uart2_pins: I believe both have RTS/CTS pins which >>> ? for a generic definition should be included as for UART0, but would then >>> ? be unused as the EVM doesn't use these pins in this function. >> >> Yes, the generic pin list should have RTS and CTS pins defined for UART1 >> and UART2. This needs fixing. >> >>> >>> - da850_mcasp_pins: if generic, must include all 16 AXR pins. ?I think you'd >>> ? be hard pressed to find a board configuration that would use all 16 AXR pins >>> ? for the McASP. ?I'm fairly sure the EVM uses the pins called out, and uses >>> ? other pins for other functions. ?So it's likely this structure wouldn't get used. >> >> Yes, the generic pin list should either be completed or removed >> altogether and the existing pin list da850_mcasp_pins should be >> copied into the board file and called da850_evm_mcasp_pins. >> >>> >>> - da850_mmcsd0_pins : includes 2 GPIO pins (specific to the EVM, though possible for >>> ? other boards) for the card detect and write protect signals. ?These pins are >>> ? completely arbitrary for that particular board design. I also believe that >>> ? the complete mmcsd0 port has 4 more data lines as part of it's peripheral, although >>> ? the driver doesn't support using them. >> >> This is incorrect again. The generic pin list should be completed >> (or removed) and the existing list should be copied into the EVM board >> file as da850_evm_mmcsd0_pins. >> >>> >>> - da850_emif25_pins interface doesn't include the generic pins for some of >>> ? the SDRAM functions. >> >> Yes, this should be completed (or removed). This list is unused anyway. >> >>> >>> - da850_cpgmac_pins defines both RMII and MII pins. ?I don't think any board >>> ? would want to configure both sets at the same time. ?Seems like this should >>> ? never get used... >> >> Agreed. >> >>> >>> It's also incomplete. ?What about the uPP pin list? ?Or the VPIF? ?Etc. >> >> These should be added as the drivers for these devices are >> supported. >> >>> >>> I think a board file author should be familiar enough with the SoC to understand >>> what peripheral pins he should be configuring for his/her particular hardware setup >>> and explicitly specify them in the board file. >> >> Agree. >> >>> >>> If you remove the common pin-mux lists and move them to a board file, then once you >>> configure your specific platform, is there any more memory used than with >>> the common scheme? ?Of course, there would be replication of pin-mux code in the board >> >> There is no memory wastage. All the pin lists are init data. >> >> I too prefer all generic pin lists which are most likely not >> going to be used at all to be removed. Unused stuff like this >> will only make code difficult to read. > > FWIW, I agree. > > Now, who wants to tackle it? > > Kevin > I could but I will need a little help and from all of you. :) Regards Victor Rodriguez > _______________________________________________ > 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 khilman at deeprootsystems.com Thu Nov 18 18:28:08 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Thu, 18 Nov 2010 16:28:08 -0800 Subject: [PATCH] mach-davinci: signedness bug In-Reply-To: (Sekhar Nori's message of "Tue, 16 Nov 2010 10:19:49 +0530") References: <20101115194028.30f8b7f0@absol.kitzblitz> Message-ID: <87lj4qduna.fsf@deeprootsystems.com> "Nori, Sekhar" writes: > Hi Nicolas, > > On Tue, Nov 16, 2010 at 00:10:28, Nicolas Kaiser wrote: >> aemif_calc_rate() can return a negative error value, so all the >> variables that get tested for this value need to be signed. >> >> The maximum bit width of WSETUP(WSETUP_MAX) appears to be 30 bits >> (0xf << 26). Using a signed instead of an unsigned integer >> shouldn't make a difference here. >> >> Signed-off-by: Nicolas Kaiser > > Thanks for the fix. You could use the subject: > > "davinci: signedness bug in davinci_aemif_setup_timing()" > > Other than that: > > Acked-by: Sekhar Nori Thanks, I fixed up the subject as Sekhar suggested. Applied, queuing for 2.6.38. Kevin From bengardiner at nanometrics.ca Thu Nov 18 21:32:47 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Thu, 18 Nov 2010 22:32:47 -0500 Subject: [PATCH v2] da850-evm: UI expander gpio_set_value can sleep, use _cansleep In-Reply-To: <874obefaiu.fsf@deeprootsystems.com> References: <1289832172-21947-1-git-send-email-bengardiner@nanometrics.ca> <874obefaiu.fsf@deeprootsystems.com> Message-ID: On Thu, Nov 18, 2010 at 6:59 PM, Kevin Hilman wrote: > [...] >> Reviewed-by: Kevin Killman > > s/Killman/Hilman/ ?with only one 'L' My apologies. > Other than that, looks good. And thanks for the detailed description on > how this was tested. > > Applying and queuing for 2.6.38. Thank you. We are happy to contribute back to the davinci-linux project. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From savinay.dharmappa at ti.com Thu Nov 18 23:39:58 2010 From: savinay.dharmappa at ti.com (Savinay Dharmappa) Date: Fri, 19 Nov 2010 11:09:58 +0530 Subject: [PATCH v2 2/2] davinci: Platform support for OMAP-L137/AM17x NOR flash driver In-Reply-To: <4CE5137A.8060507@mvista.com> References: <1289548975-21296-1-git-send-email-savinay.dharmappa@ti.com> <4CE5137A.8060507@mvista.com> Message-ID: <004601cb87ac$33a1d680$9ae58380$@dharmappa@ti.com> Hi Sergei, On Thu, Nov 18, 2010 at 17:22:26, Sergei Shtylyov wrote: > Hello. > > On 12-11-2010 11:02, Savinay Dharmappa wrote: > > > From: Aleksey Makarov > > > Adds platform support for OMAP-L137/AM17x NOR flash driver. > > > Also, configures chip select 3 to control NOR flash's upper > > address lines. > > Please add back my signoff, omitted in this version. Some of the code, > including bug fixes, was authored by me. > I'll add your sign-off. > > Signed-off-by: Aleksey Makarov > > Signed-off-by: Sergei Shtylyov > > > Signed-off-by: Savinay Dharmappa > > > diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c > > index 1bb89d3..cd35198 100644 > > --- a/arch/arm/mach-davinci/board-da830-evm.c > > +++ b/arch/arm/mach-davinci/board-da830-evm.c > [...] > > @@ -429,6 +431,221 @@ static inline void da830_evm_init_nand(int mux_mode) > > static inline void da830_evm_init_nand(int mux_mode) { } > > #endif > > > > +#ifdef CONFIG_DA830_UI_NOR > > +/* > > + * Number of Address line > > Only "lines". > Ok. > > going to the NOR flash that are latched using > > + * AEMIF address lines B_EMIF_BA0-B_EMIF_A12 on CS2. > > +#define NOR_WINDOW_SIZE_LOG2 15 > > +#define NOR_WINDOW_SIZE (1 << NOR_WINDOW_SIZE_LOG2) > > + > > +static struct { > > + struct clk *clk; > > + struct { > > + struct resource *res; > > You're not using this field outside da830_evm_nor_init() now, so there's > not much point in keeping it... > Ok. I'll use it as a local variable in da830_evm_nor_init(). > > + void __iomem *addr; > > + } latch, aemif; > > +} da830_evm_nor; > > > +static void da830_evm_nor_set_window(unsigned long offset, void *data) > > +{ > > + /* > > + * CS2 and CS3 address lines are used to address NOR flash. Address > > + * line A0-A14 going to the NOR flash are latched using AEMIF address > > + * lines B_EMIF_BA0-B_EMIF_A12 on CS2. > > Are they really latched, and not just used live when the NOR chip is > accessed? What's the point of latching them? > You are right. I'll correct the comment. > > +static int da830_evm_nor_init(void *data, int cs) > > +{ > > + /* Turn on AEMIF clocks */ > > + da830_evm_nor.clk = clk_get(NULL, "aemif"); > > + if (IS_ERR(da830_evm_nor.clk)) { > > + pr_err("%s: could not get AEMIF clock\n", __func__); > > + da830_evm_nor.clk = NULL; > > + return -ENODEV; > > + } > > + clk_enable(da830_evm_nor.clk); > > + > > + da830_evm_nor.aemif.res = request_mem_region(DA8XX_AEMIF_CTL_BASE, > > + SZ_32K, "AEMIF control"); > > No need to store it -- you don't use it afterwards. > Ok. > > + if (da830_evm_nor.aemif.res == NULL) { > > + pr_err("%s: could not request AEMIF control region\n", > > + __func__); > > + goto err_clk; > > + } > > [...] > > > + /* Setup the window to access the latch */ > > + da830_evm_nor.latch.res = > > + request_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE, > > + "DA830 UI NOR address latch"); > > Same comment here... > > > + if (da830_evm_nor.latch.res == NULL) { > > + pr_err("%s: could not request address latch region\n", > > + __func__); > > + goto err_aemif_ioremap; > > + } > > [...] > > > +static inline void da830_evm_init_nor(int mux_mode) > > +{ > > + int ret; > > + > > + if (HAS_MMC) { > > + pr_warning("WARNING: both MMC/SD and NOR are " > > + "enabled, but they share AEMIF pins.\n" > > + "\tDisable MMC/SD for NOR support.\n"); > > This line is over-indented. Ok. Will address your comments and send an updated an version Thanks, Savinay From manjunath.hadli at ti.com Fri Nov 19 02:18:16 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Fri, 19 Nov 2010 13:48:16 +0530 Subject: [PATCH 0/6] davinci vpbe: V4L2 Display driver for DM644X In-Reply-To: <201011131352.20002.hverkuil@xs4all.nl> Message-ID: Hans, Thank you for the review comments. I will include a section on customizing for additions and changes to different boards. Thanks again, -Manju On Sat, Nov 13, 2010 at 18:22:19, Hans Verkuil wrote: > Hi Manju, > > I've reviewed the other patches as well. The only one for which I had comments was patch 2/6. > > However, what I think would be useful here is to have an overview document, either as part of a source or header, or as a separate text document. It is not easy to get a good overview of how everything fits together, and a document that describes the various parts and how they fit together would be very benificial. > > I am thinking in particular of vendors building a new board based on this > device: how and where do you define new i2c display devices, how are they initialized, etc. > > Regards, > > Hans > > On Monday, November 08, 2010 15:54:05 Manjunath Hadli wrote: > > This driver is written for Texas Instruments's DM644X VPBE IP. > > This SoC supports 2 video planes and 2 OSD planes as part of its OSD > > (On Screen Display) block. The OSD lanes predminantly support RGB > > space and the Video planes support YUV data. Out of these 4, the 2 > > video planes are supported as part of the V4L2 driver. These would be > > enumerated as video2 and video3 dev nodes. > > The blending and video timing generator unit (VENC- for Video Encoder) > > is the unit which combines/blends the output of these 4 planes into a > > single stream and this output is given to Video input devices like TV > > and other digital LCDs. The software for VENC is designed as a > > subdevice with support for SD(NTSC and PAL) modes and 2 outputs. > > This SoC forms the iniial implementation of its later additions like > > DM355 and DM365. > > > > Muralidharan Karicheri (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 | 85 +- > > arch/arm/mach-davinci/dm644x.c | 181 ++- > > 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 | 861 ++++++++++ > > drivers/media/video/davinci/vpbe_display.c | 2283 ++++++++++++++++++++++++++ > > drivers/media/video/davinci/vpbe_osd.c | 1208 ++++++++++++++ > > drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++ > > drivers/media/video/davinci/vpbe_venc.c | 617 +++++++ > > drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++ > > include/media/davinci/vpbe.h | 187 +++ > > include/media/davinci/vpbe_display.h | 144 ++ > > include/media/davinci/vpbe_osd.h | 397 +++++ > > include/media/davinci/vpbe_types.h | 170 ++ > > include/media/davinci/vpbe_venc.h | 70 + > > 16 files changed, 6790 insertions(+), 19 deletions(-) create mode > > 100644 drivers/media/video/davinci/vpbe.c > > create mode 100644 drivers/media/video/davinci/vpbe_display.c > > create mode 100644 drivers/media/video/davinci/vpbe_osd.c > > create mode 100644 drivers/media/video/davinci/vpbe_osd_regs.h > > create mode 100644 drivers/media/video/davinci/vpbe_venc.c > > create mode 100644 drivers/media/video/davinci/vpbe_venc_regs.h > > create mode 100644 include/media/davinci/vpbe.h create mode 100644 > > include/media/davinci/vpbe_display.h > > create mode 100644 include/media/davinci/vpbe_osd.h create mode > > 100644 include/media/davinci/vpbe_types.h > > create mode 100644 include/media/davinci/vpbe_venc.h > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-media" > > in the body of a message to majordomo at vger.kernel.org More majordomo > > info at http://vger.kernel.org/majordomo-info.html > > > > > > -- > Hans Verkuil - video4linux developer - sponsored by Cisco > From nsekhar at ti.com Fri Nov 19 03:58:00 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 19 Nov 2010 15:28:00 +0530 Subject: [PATCH v2 2/4] da850-evm: add UI Expander pushbuttons In-Reply-To: <95f48a32a0256ecdb7148aa08d16f64928a7e5d8.1289935504.git.bengardiner@nanometrics.ca> References: <95f48a32a0256ecdb7148aa08d16f64928a7e5d8.1289935504.git.bengardiner@nanometrics.ca> Message-ID: Hi Ben, Thanks for the patches. Some comments/questions below: On Wed, Nov 17, 2010 at 01:09:35, Ben Gardiner wrote: > 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 > > --- > > Changes since v1: > * set INPUT_POLLDEV default for DA850_EVM machine, but don't select it > unconditionally I didn't see the v1 posting (wonder why), but why is this required? Why cant we depend on this being selected from Device Drivers->Input device support in menuconfig? [...] > @@ -349,6 +421,10 @@ static struct i2c_board_info __initdata da850_evm_i2c_devices[] = { > { > I2C_BOARD_INFO("tca6416", 0x20), > .platform_data = &da850_evm_ui_expander_info, > + /* > + * TODO : populate at runtime using > + * .irq = gpio_to_irq(GPIO_TO_PIN(2,7)), > + */ You seem to be adding this in this patch and removing in 4/4. Thanks, Sekhar From savinay.dharmappa at ti.com Fri Nov 19 03:54:13 2010 From: savinay.dharmappa at ti.com (Savinay Dharmappa) Date: Fri, 19 Nov 2010 15:24:13 +0530 Subject: [PATCH v3 1/2] mtd: NOR flash driver for OMAP-L137/AM17x Message-ID: <1290160453-9316-1-git-send-email-savinay.dharmappa@ti.com> 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 --- Since v2: a. Added signoff from Aleksey Makarov and Sergei Shtylyov. b. Fixed a minor commenting issue. drivers/mtd/maps/Kconfig | 9 + drivers/mtd/maps/Makefile | 1 + drivers/mtd/maps/latch-addr-flash.c | 272 ++++++++++++++++++++++++++++++++++ include/linux/mtd/latch-addr-flash.h | 29 ++++ 4 files changed, 311 insertions(+), 0 deletions(-) create mode 100644 drivers/mtd/maps/latch-addr-flash.c create mode 100644 include/linux/mtd/latch-addr-flash.h diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index a0dd7bb..c0993d0 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -552,4 +552,13 @@ config MTD_PISMO When built as a module, it will be called pismo.ko +config MTD_LATCH_ADDR + tristate "Latch-assisted Flash Chip Support" + depends on MTD_COMPLEX_MAPPINGS + help + Map driver which allows flashes to be partially physically addressed + and have the upper address lines set by a board specific code. + + If compiled as a module, it will be called latch-addr-flash. + endmenu diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index c7869c7..08533bd 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -59,3 +59,4 @@ obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o obj-$(CONFIG_MTD_VMU) += vmu-flash.o obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o obj-$(CONFIG_MTD_BCM963XX) += bcm963xx-flash.o +obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o diff --git a/drivers/mtd/maps/latch-addr-flash.c b/drivers/mtd/maps/latch-addr-flash.c new file mode 100644 index 0000000..2f91291 --- /dev/null +++ b/drivers/mtd/maps/latch-addr-flash.c @@ -0,0 +1,272 @@ +/* + * Interface for NOR flash driver whose high address lines are latched + * + * Copyright 2000 Nicolas Pitre + * Copyright 2005-2008 Analog Devices Inc. + * Coyright (C) 2008 MontaVista Software, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "latch-addr-flash" + +struct latch_addr_flash_info { + struct mtd_info *mtd; + struct map_info map; + struct resource *res; + + void (*set_window)(unsigned long offset, void *data); + void *data; + + /* cache; could be found out of res */ + unsigned long win_mask; + + int nr_parts; + struct mtd_partition *parts; + + spinlock_t lock; +}; + +static map_word lf_read(struct map_info *map, unsigned long ofs) +{ + struct latch_addr_flash_info *info; + map_word datum; + + info = (struct latch_addr_flash_info *)map->map_priv_1; + + spin_lock(&info->lock); + + info->set_window(ofs, info->data); + datum = inline_map_read(map, info->win_mask & ofs); + + spin_unlock(&info->lock); + + return datum; +} + +static void lf_write(struct map_info *map, map_word datum, unsigned long ofs) +{ + struct latch_addr_flash_info *info; + + info = (struct latch_addr_flash_info *)map->map_priv_1; + + spin_lock(&info->lock); + + info->set_window(ofs, info->data); + inline_map_write(map, datum, info->win_mask & ofs); + + spin_unlock(&info->lock); +} + +static void lf_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + struct latch_addr_flash_info *info = + (struct latch_addr_flash_info *) map->map_priv_1; + unsigned n; + + while (len > 0) { + n = info->win_mask + 1 - (from & info->win_mask); + if (n > len) + n = len; + + spin_lock(&info->lock); + + info->set_window(from, info->data); + memcpy_fromio(to, map->virt + (from & info->win_mask), n); + + spin_unlock(&info->lock); + + to += n; + from += n; + len -= n; + } +} + +static char *rom_probe_types[] = { "cfi_probe", NULL }; + +static char *part_probe_types[] = { "cmdlinepart", NULL }; + +static int latch_addr_flash_remove(struct platform_device *dev) +{ + struct latch_addr_flash_info *info; + struct latch_addr_flash_data *latch_addr_data; + + info = platform_get_drvdata(dev); + if (info == NULL) + return 0; + platform_set_drvdata(dev, NULL); + + latch_addr_data = dev->dev.platform_data; + + if (info->mtd != NULL) { + if (mtd_has_partitions()) { + if (info->nr_parts) { + del_mtd_partitions(info->mtd); + kfree(info->parts); + } else if (latch_addr_data->nr_parts) { + del_mtd_partitions(info->mtd); + } else { + del_mtd_device(info->mtd); + } + } else { + del_mtd_device(info->mtd); + } + map_destroy(info->mtd); + } + + if (info->map.virt != NULL) + iounmap(info->map.virt); + + if (info->res != NULL) + release_mem_region(info->res->start, resource_size(info->res)); + + kfree(info); + + if (latch_addr_data->done) + latch_addr_data->done(latch_addr_data->data); + + return 0; +} + +static int __devinit latch_addr_flash_probe(struct platform_device *dev) +{ + struct latch_addr_flash_data *latch_addr_data; + struct latch_addr_flash_info *info; + resource_size_t win_base = dev->resource->start; + resource_size_t win_size = resource_size(dev->resource); + char **probe_type; + int chipsel; + int err; + + latch_addr_data = dev->dev.platform_data; + if (latch_addr_data == NULL) + return -ENODEV; + + pr_notice("latch-addr platform flash device: %#llx byte " + "window at %#.8llx\n", + (unsigned long long)win_size, (unsigned long long)win_base); + + chipsel = dev->id; + + if (latch_addr_data->init) { + err = latch_addr_data->init(latch_addr_data->data, chipsel); + if (err != 0) + return err; + } + + info = kzalloc(sizeof(struct latch_addr_flash_info), GFP_KERNEL); + if (info == NULL) { + err = -ENOMEM; + goto done; + } + + platform_set_drvdata(dev, info); + + info->res = request_mem_region(win_base, win_size, DRIVER_NAME); + if (info->res == NULL) { + dev_err(&dev->dev, "Could not reserve memory region\n"); + err = -EBUSY; + goto free_info; + } + + info->map.name = DRIVER_NAME; + info->map.size = latch_addr_data->size; + info->map.bankwidth = latch_addr_data->width; + + info->map.phys = NO_XIP; + info->map.virt = ioremap(win_base, win_size); + if (!info->map.virt) { + err = -ENOMEM; + goto free_res; + } + + info->map.map_priv_1 = (unsigned long)info; + + info->map.read = lf_read; + info->map.copy_from = lf_copy_from; + info->map.write = lf_write; + info->set_window = latch_addr_data->set_window; + info->data = latch_addr_data->data; + info->win_mask = win_size - 1; + + spin_lock_init(&info->lock); + + for (probe_type = rom_probe_types; !info->mtd && *probe_type; + probe_type++) + info->mtd = do_map_probe(*probe_type, &info->map); + + if (info->mtd == NULL) { + dev_err(&dev->dev, "map_probe failed\n"); + err = -ENODEV; + goto iounmap; + } + info->mtd->owner = THIS_MODULE; + + if (mtd_has_partitions()) { + + err = parse_mtd_partitions(info->mtd, + (const char **)part_probe_types, + &info->parts, 0); + if (err > 0) { + add_mtd_partitions(info->mtd, info->parts, err); + return 0; + } + if (latch_addr_data->nr_parts) { + pr_notice("Using latch-addr-flash partition information\n"); + add_mtd_partitions(info->mtd, latch_addr_data->parts, + latch_addr_data->nr_parts); + return 0; + } + } + add_mtd_device(info->mtd); + return 0; + +iounmap: + iounmap(info->map.virt); +free_res: + release_mem_region(info->res->start, resource_size(info->res)); +free_info: + kfree(info); +done: + if (latch_addr_data->done) + latch_addr_data->done(latch_addr_data->data); + return err; +} + +static struct platform_driver latch_addr_flash_driver = { + .probe = latch_addr_flash_probe, + .remove = __devexit_p(latch_addr_flash_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init latch_addr_flash_init(void) +{ + return platform_driver_register(&latch_addr_flash_driver); +} +module_init(latch_addr_flash_init); + +static void __exit latch_addr_flash_exit(void) +{ + platform_driver_unregister(&latch_addr_flash_driver); +} +module_exit(latch_addr_flash_exit); + +MODULE_AUTHOR("David Griego "); +MODULE_DESCRIPTION("MTD map driver for flashes addressed physically with upper " + "address lines being set board specifically"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mtd/latch-addr-flash.h b/include/linux/mtd/latch-addr-flash.h new file mode 100644 index 0000000..d35d070 --- /dev/null +++ b/include/linux/mtd/latch-addr-flash.h @@ -0,0 +1,29 @@ +/* + * Interface for NOR flash driver whose high address lines are latched + * + * Copyright (C) 2008 MontaVista Software, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ +#ifndef __LATCH_ADDR_FLASH__ +#define __LATCH_ADDR_FLASH__ + +struct map_info; +struct mtd_partition; + +struct latch_addr_flash_data { + unsigned int width; + unsigned int size; + + int (*init)(void *data, int cs); + void (*done)(void *data); + void (*set_window)(unsigned long offset, void *data); + void *data; + + unsigned int nr_parts; + struct mtd_partition *parts; +}; + +#endif -- 1.5.6 From savinay.dharmappa at ti.com Fri Nov 19 03:54:48 2010 From: savinay.dharmappa at ti.com (Savinay Dharmappa) Date: Fri, 19 Nov 2010 15:24:48 +0530 Subject: [PATCH v3 2/2] davinci: Platform support for OMAP-L137/AM17x NOR flash driver Message-ID: <1290160488-10344-1-git-send-email-savinay.dharmappa@ti.com> From: Aleksey Makarov Adds platform support for OMAP-L137/AM17x NOR flash driver. Also, configures chip select 3 to control NOR flash's upper address lines. Signed-off-by: Aleksey Makarov Signed-off-by: Sergei Shtylyov Signed-off-by: Savinay Dharmappa --- Since v2: a. Added signoff from Sergei Shtylyov. b. Removed resource member from the da830_evm_nor structure and used a local member inside the da830_evm_nor_init() function. c. Fixed few commenting/indenting issues. arch/arm/mach-davinci/Kconfig | 8 + arch/arm/mach-davinci/board-da830-evm.c | 218 +++++++++++++++++++++++++++++++ 2 files changed, 226 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index b77b860..89cc62e 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -142,6 +142,14 @@ config DA830_UI_NAND help Say Y here to use the NAND flash. Do not forget to setup the switch correctly. + +config DA830_UI_NOR + bool "NOR flash" + help + Configure this option to specify the that AEMIF CE2/CE3 will be used to + communicate to the NOR flash. Do not forget to setup the switch SW1 + on UI card correctly. + endchoice config MACH_DAVINCI_DA850_EVM diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c index b52a3a1..c807c1b 100644 --- a/arch/arm/mach-davinci/board-da830-evm.c +++ b/arch/arm/mach-davinci/board-da830-evm.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -429,6 +431,220 @@ static inline void da830_evm_init_nand(int mux_mode) static inline void da830_evm_init_nand(int mux_mode) { } #endif +#ifdef CONFIG_DA830_UI_NOR +/* + * Number of lines going to the NOR flash that are latched using + * AEMIF address lines B_EMIF_BA0-B_EMIF_A12 on CS2. + */ +#define NOR_WINDOW_SIZE_LOG2 15 +#define NOR_WINDOW_SIZE (1 << NOR_WINDOW_SIZE_LOG2) + +static struct { + struct clk *clk; + struct { + void __iomem *addr; + } latch, aemif; +} da830_evm_nor; + +static struct davinci_aemif_timing da830_evm_norflash_timing = { + .wsetup = 0, + .wstrobe = 40, + .whold = 0, + .rsetup = 0, + .rstrobe = 130, + .rhold = 0, + .ta = 20, +}; + +static void da830_evm_nor_set_window(unsigned long offset, void *data) +{ + /* + * CS2 and CS3 address lines are used to address NOR flash. Address + * line A0-A14 going to the NOR flash are connected to AEMIF address + * lines B_EMIF_BA0-B_EMIF_A12 on CS2. Address lines A15-A23 of the + * NOR flash are connected to AEMIF address lines B_EMIF_A0-B_EMIF_A6 + * on CS3. The offset argument received by this function is the offset + * within NOR flash. Upper address is obtained by shifting the offset + * by the number of CS2 address lines used (13) and masking it with + * complement of 3 (2 address lines used to address banks) and adding + * the resultant offset value to CS3 base address. Writing to this + * address will latch the upper address lines. + */ + writeb(0x0, da830_evm_nor.latch.addr + + (~3UL & (offset >> (NOR_WINDOW_SIZE_LOG2 - 2)))); +} + +static void da830_evm_nor_done(void *data) +{ + clk_disable(da830_evm_nor.clk); + clk_put(da830_evm_nor.clk); + iounmap(da830_evm_nor.latch.addr); + release_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE); + iounmap(da830_evm_nor.aemif.addr); + release_mem_region(DA8XX_AEMIF_CTL_BASE, SZ_32K); +} + +static int da830_evm_nor_init(void *data, int cs) +{ + struct resource *res = NULL; + + /* Turn on AEMIF clocks */ + da830_evm_nor.clk = clk_get(NULL, "aemif"); + if (IS_ERR(da830_evm_nor.clk)) { + pr_err("%s: could not get AEMIF clock\n", __func__); + da830_evm_nor.clk = NULL; + return -ENODEV; + } + clk_enable(da830_evm_nor.clk); + + res = request_mem_region(DA8XX_AEMIF_CTL_BASE, SZ_32K, "AEMIF control"); + if (res == NULL) { + pr_err("%s: could not request AEMIF control region\n", + __func__); + goto err_clk; + } + + da830_evm_nor.aemif.addr = ioremap_nocache(DA8XX_AEMIF_CTL_BASE, + SZ_32K); + if (da830_evm_nor.aemif.addr == NULL) { + pr_err("%s: could not remap AEMIF control region\n", __func__); + goto err_aemif_region; + } + + /* Setup AEMIF -- timings, etc. */ + + /* Set maximum wait cycles */ + davinci_aemif_setup_timing(&da830_evm_norflash_timing, + da830_evm_nor.aemif.addr, cs); + + davinci_aemif_setup_timing(&da830_evm_norflash_timing, + da830_evm_nor.aemif.addr, cs + 1); + + /* Setup the window to access the latch */ + res = request_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE, + "DA830 UI NOR address latch"); + if (res == NULL) { + pr_err("%s: could not request address latch region\n", + __func__); + goto err_aemif_ioremap; + } + + da830_evm_nor.latch.addr = + ioremap_nocache(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE); + if (da830_evm_nor.latch.addr == NULL) { + pr_err("%s: could not remap address latch region\n", __func__); + goto err_latch_region; + } + return 0; + +err_latch_region: + release_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE); + +err_aemif_ioremap: + iounmap(da830_evm_nor.aemif.addr); + +err_aemif_region: + release_mem_region(DA8XX_AEMIF_CTL_BASE, SZ_32K); + +err_clk: + clk_disable(da830_evm_nor.clk); + clk_put(da830_evm_nor.clk); + + return -EBUSY; +} + +static struct mtd_partition da830_evm_nor_partitions[] = { + /* bootloader (U-Boot, etc) in first 2 sectors */ + [0] = { + .name = "bootloader", + .offset = 0, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + /* bootloader parameters in the next 1 sector */ + [1] = { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_64K, + }, + /* kernel */ + [2] = { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + }, + /* file system */ + [3] = { + .name = "filesystem", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + } +}; + +static struct latch_addr_flash_data da830_evm_nor_pdata = { + .width = 1, + .size = SZ_4M, + .init = da830_evm_nor_init, + .done = da830_evm_nor_done, + .set_window = da830_evm_nor_set_window, + .nr_parts = ARRAY_SIZE(da830_evm_nor_partitions), + .parts = da830_evm_nor_partitions, +}; + +static struct resource da830_evm_nor_resource[] = { + [0] = { + .start = DA8XX_AEMIF_CS2_BASE, + .end = DA8XX_AEMIF_CS2_BASE + NOR_WINDOW_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = DA8XX_AEMIF_CS3_BASE, + .end = DA8XX_AEMIF_CS3_BASE + PAGE_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [2] = { + .start = DA8XX_AEMIF_CTL_BASE, + .end = DA8XX_AEMIF_CTL_BASE + SZ_32K - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device da830_evm_nor_device = { + .name = "latch-addr-flash", + .id = 0, + .dev = { + .platform_data = &da830_evm_nor_pdata, + }, + .num_resources = ARRAY_SIZE(da830_evm_nor_resource), + .resource = da830_evm_nor_resource, +}; + +static inline void da830_evm_init_nor(int mux_mode) +{ + int ret; + + if (HAS_MMC) { + pr_warning("WARNING: both MMC/SD and NOR are " + "enabled, but they share AEMIF pins.\n" + "\tDisable MMC/SD for NOR support.\n"); + return; + } + + ret = davinci_cfg_reg_list(da830_evm_emif25_pins); + if (ret) + pr_warning("da830_evm_init: EMIF 2.5 mux setup failed: %d\n", + ret); + + ret = platform_device_register(&da830_evm_nor_device); + if (ret) + pr_warning("da830_evm_init: NOR device not registered.\n"); + + gpio_direction_output(mux_mode, 1); +} +#else +static inline void da830_evm_init_nor(int mux_mode) { } +#endif /* CONFIG_DA830_UI_NOR */ + #ifdef CONFIG_DA830_UI_LCD static inline void da830_evm_init_lcdc(int mux_mode) { @@ -469,6 +685,8 @@ static int __init da830_evm_ui_expander_setup(struct i2c_client *client, da830_evm_init_nand(gpio + 6); + da830_evm_init_nor(gpio + 6); + return 0; } -- 1.5.6 From nsekhar at ti.com Fri Nov 19 05:19:18 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 19 Nov 2010 16:49:18 +0530 Subject: [PATCH v2 3/4] da850-evm: extract defines for SEL{A, B, C} pins in UI expander In-Reply-To: References: Message-ID: Hi Ben, On Wed, Nov 17, 2010 at 01:09:36, Ben Gardiner wrote: > The setup and teardown methods of the UI expander reference the SEL_{A,B,C} > pins by 'magic number' in each function. This patch extracts common #defines > for their offsets in the expander and uses them. > > Signed-off-by: Ben Gardiner > Reviewed-by: Chris Cordahi > > --- > > No changes since v1 > --- > arch/arm/mach-davinci/board-da850-evm.c | 27 +++++++++++++++------------ > 1 files changed, 15 insertions(+), 12 deletions(-) > > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index ff71ffd..dcf21e5 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -292,6 +292,9 @@ static const char const *ui_expander_names[] = { > "pb7", "pb6", "pb5", "pb4", "pb3", "pb2", "pb1" > }; > > +#define DA850_SEL_A_OFFSET 7 > +#define DA850_SEL_B_OFFSET 6 > +#define DA850_SEL_C_OFFSET 5 > #define DA850_UI_PB8_OFFSET 8 > #define DA850_N_UI_PB 8 In this case, I think in this case indexed array initialization will help keep the offsets and names matched. Here is an untested patch on your patch, please consider using it. Thanks, Sekhar ---8<---- diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index dcf21e5..508e5f2 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -287,16 +287,35 @@ static inline void da850_evm_setup_emac_rmii(int rmii_sel) { } /* No need to poll switches anywhere near as fast */ #define DA850_SW_POLL_MS 700 -static const char const *ui_expander_names[] = { - "dgnd", "dgnd", "dgnd", "dgnd", "nc", "sel_c", "sel_b", "sel_a", "pb8", - "pb7", "pb6", "pb5", "pb4", "pb3", "pb2", "pb1" -}; - -#define DA850_SEL_A_OFFSET 7 -#define DA850_SEL_B_OFFSET 6 -#define DA850_SEL_C_OFFSET 5 -#define DA850_UI_PB8_OFFSET 8 -#define DA850_N_UI_PB 8 +static 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 user_ui_pbs_gpio_keys[DA850_N_UI_PB]; @@ -329,8 +348,8 @@ static void da850_ui_pushbuttons_init(unsigned gpio) button->wakeup = 0; button->debounce_interval = DA850_PB_DEBOUNCE_MS; button->desc = (char *) - ui_expander_names[DA850_UI_PB8_OFFSET + i]; - button->gpio = gpio + DA850_UI_PB8_OFFSET + i; + da850_evm_ui_exp[DA850_EVM_UI_EXP_PB8 + i]; + button->gpio = gpio + DA850_EVM_UI_EXP_PB8 + i; } } @@ -339,23 +358,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 + DA850_SEL_A_OFFSET; - sel_b = gpio + DA850_SEL_B_OFFSET; - sel_c = gpio + DA850_SEL_C_OFFSET; + 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, ui_expander_names[DA850_SEL_A_OFFSET]); + 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, ui_expander_names[DA850_SEL_B_OFFSET]); + 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, ui_expander_names[DA850_SEL_C_OFFSET]); + ret = gpio_request(sel_c, da850_evm_ui_exp[DA850_EVM_UI_EXP_SEL_B]); if (ret) { pr_warning("Cannot open UI expander pin %d\n", sel_c); goto exp_setup_selc_fail; @@ -399,13 +418,13 @@ static int da850_evm_ui_expander_teardown(struct i2c_client *client, platform_device_unregister(&user_ui_pb_gpio_key_device); /* deselect all functionalities */ - gpio_set_value(gpio + DA850_SEL_C_OFFSET, 1); - gpio_set_value(gpio + DA850_SEL_B_OFFSET, 1); - gpio_set_value(gpio + DA850_SEL_A_OFFSET, 1); + gpio_set_value(gpio + DA850_EVM_UI_EXP_SEL_C, 1); + gpio_set_value(gpio + DA850_EVM_UI_EXP_SEL_B, 1); + gpio_set_value(gpio + DA850_EVM_UI_EXP_SEL_A, 1); - gpio_free(gpio + DA850_SEL_C_OFFSET); - gpio_free(gpio + DA850_SEL_B_OFFSET); - gpio_free(gpio + DA850_SEL_A_OFFSET); + 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; } @@ -414,7 +433,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 = ui_expander_names, + .names = da850_evm_ui_exp, }; static struct i2c_board_info __initdata da850_evm_i2c_devices[] = { From sshtylyov at mvista.com Fri Nov 19 05:42:28 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 19 Nov 2010 14:42:28 +0300 Subject: [PATCH v3 2/2] davinci: Platform support for OMAP-L137/AM17x NOR flash driver In-Reply-To: <1290160488-10344-1-git-send-email-savinay.dharmappa@ti.com> References: <1290160488-10344-1-git-send-email-savinay.dharmappa@ti.com> Message-ID: <4CE662A4.6060204@mvista.com> Hello. On 19-11-2010 12:54, Savinay Dharmappa wrote: > From: Aleksey Makarov > Adds platform support for OMAP-L137/AM17x NOR flash driver. > Also, configures chip select 3 to control NOR flash's upper > address lines. > Signed-off-by: Aleksey Makarov > Signed-off-by: Sergei Shtylyov > Signed-off-by: Savinay Dharmappa [...] > diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c > index b52a3a1..c807c1b 100644 > --- a/arch/arm/mach-davinci/board-da830-evm.c > +++ b/arch/arm/mach-davinci/board-da830-evm.c > @@ -20,6 +20,8 @@ > #include > #include > #include > +#include > +#include > > #include > #include > @@ -429,6 +431,220 @@ static inline void da830_evm_init_nand(int mux_mode) > static inline void da830_evm_init_nand(int mux_mode) { } > #endif > > +#ifdef CONFIG_DA830_UI_NOR > +/* > + * Number of lines going to the NOR flash that are latched using > + * AEMIF address lines B_EMIF_BA0-B_EMIF_A12 on CS2. You still left the words about latching the low address bytes... > + */ > +#define NOR_WINDOW_SIZE_LOG2 15 > +#define NOR_WINDOW_SIZE (1<< NOR_WINDOW_SIZE_LOG2) > + > +static struct { > + struct clk *clk; > + struct { > + void __iomem *addr; > + } latch, aemif; Don't need a structure here anymore. > +static void da830_evm_nor_set_window(unsigned long offset, void *data) > +{ > + /* > + * CS2 and CS3 address lines are used to address NOR flash. Address > + * line Only "lines". > A0-A14 going to Rather "from"... > the NOR flash are connected to AEMIF address > + * lines B_EMIF_BA0-B_EMIF_A12 on CS2. Address lines A15-A23 of the > + * NOR flash are connected to AEMIF address lines B_EMIF_A0-B_EMIF_A6 > + * on CS3. They are not connected but latched from B_EMIF_A0-B_EMIF_A6 on CS3. > + /* Setup AEMIF -- timings, etc. */ > + > + /* Set maximum wait cycles */ > + davinci_aemif_setup_timing(&da830_evm_norflash_timing, > + da830_evm_nor.aemif.addr, cs); > + > + davinci_aemif_setup_timing(&da830_evm_norflash_timing, > + da830_evm_nor.aemif.addr, cs + 1); > + > + /* Setup the window to access the latch */ > + res = request_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE, > + "DA830 UI NOR address latch"); > + if (res == NULL) { > + pr_err("%s: could not request address latch region\n", > + __func__); > + goto err_aemif_ioremap; > + } > + > + da830_evm_nor.latch.addr = > + ioremap_nocache(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE); > + if (da830_evm_nor.latch.addr == NULL) { > + pr_err("%s: could not remap address latch region\n", __func__); > + goto err_latch_region; > + } > + return 0; > + > +err_latch_region: > + release_mem_region(DA8XX_AEMIF_CS3_BASE, PAGE_SIZE); > + > +err_aemif_ioremap: > + iounmap(da830_evm_nor.aemif.addr); > + > +err_aemif_region: > + release_mem_region(DA8XX_AEMIF_CTL_BASE, SZ_32K); > + > +err_clk: > + clk_disable(da830_evm_nor.clk); > + clk_put(da830_evm_nor.clk); > + > + return -EBUSY; You should return -ENOMEM for ioremap() failures... WBR, Sergei From nsekhar at ti.com Fri Nov 19 06:14:49 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 19 Nov 2010 17:44:49 +0530 Subject: [PATCH v2 4/4] da850-evm: add baseboard UI expander buttons, switches and LEDs In-Reply-To: References: Message-ID: Hi Ben, The board patches look good to me overall. Some minor comments below: On Wed, Nov 17, 2010 at 01:09:37, Ben Gardiner wrote: > 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 > > --- > > 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 | 217 ++++++++++++++++++++++++++++++- > 1 files changed, 213 insertions(+), 4 deletions(-) > > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index dcf21e5..79f2c95 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -410,6 +410,208 @@ 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(ui_expander_names) > +#define DA850_BB_EXPANDER_GPIO_BASE (DAVINCI_N_GPIO + DA850_UI_EXPANDER_N_GPIOS) > + > +static const char const *baseboard_expander_names[] = { > + "deep_sleep_en", "sw_rst", "tp_23", "tp_22", "tp_21", "user_pb1", > + "user_led2", "user_led1", "user_sw_1", "user_sw_2", "user_sw_3", > + "user_sw_4", "user_sw_5", "user_sw_6", "user_sw_7", "user_sw_8" > +}; > + > +#define DA850_DEEP_SLEEP_EN_OFFSET 0 > +#define DA850_SW_RST_OFFSET 1 > +#define DA850_PB1_OFFSET 5 > +#define DA850_USER_LED2_OFFSET 6 > +#define DA850_USER_SW_1_OFFSET 8 Again, I think index initialized array will work much better here. Currently it is error prone to keep the defines and the array of names in sync. > + > +#define DA850_N_USER_SW 8 > +#define DA850_N_USER_LED 2 > + > +static struct gpio_keys_button user_pb_gpio_key = { "da850evm_bb_pb" might be a better name? > + .code = KEY_PROG1, > + .type = EV_KEY, > + .active_low = 1, > + .wakeup = 0, > + .debounce_interval = DA850_PB_DEBOUNCE_MS, > + .gpio = -1, /* assigned at runtime */ > + .desc = NULL, /* assigned at runtime */ > +}; > + > +static struct gpio_keys_platform_data user_pb_gpio_key_platform_data = { Similarly "da850evm_bb_pb_pdata" instead of the long name? > + .buttons = &user_pb_gpio_key, > + .nbuttons = 1, > + .rep = 0, /* disable auto-repeat */ > + .poll_interval = DA850_PB_POLL_MS, > +}; > + > +static struct platform_device user_pb_gpio_key_device = { > + .name = "gpio-keys", > + .id = 1, > + .dev = { > + .platform_data = &user_pb_gpio_key_platform_data > + } > +}; > + > +static struct gpio_keys_button user_sw_gpio_keys[DA850_N_USER_SW]; You could initialize most static fields here itself using: static struct gpio_keys_button user_sw_gpio_keys[] = { [0 ... DA850_N_USER_SW] = { ... ... ... }, }; This way your runtime initialization will come down. > + > +static struct gpio_keys_platform_data user_sw_gpio_key_platform_data = { > + .buttons = user_sw_gpio_keys, > + .nbuttons = ARRAY_SIZE(user_sw_gpio_keys), > + .rep = 0, /* disable auto-repeat */ > + .poll_interval = DA850_SW_POLL_MS, > +}; I wonder if we really have create to separate platform data for switches and push buttons. If it is only the debounce period that is different, it can be handled by initializing that field differently. > + > +static struct platform_device user_sw_gpio_key_device = { > + .name = "gpio-keys", > + .id = 2, > + .dev = { > + .platform_data = &user_sw_gpio_key_platform_data End with a ',' here. > + } > +}; > + > +static void da850_user_switches_init(unsigned gpio) > +{ > + int i; > + struct gpio_keys_button *button; > + > + for (i = 0; i < DA850_N_USER_SW; i++) { > + button = &user_sw_gpio_keys[i]; > + > + button->code = SW_LID + i; > + button->type = EV_SW; > + button->active_low = 1; > + button->wakeup = 0; > + button->debounce_interval = DA850_PB_DEBOUNCE_MS; > + button->desc = (char *) > + baseboard_expander_names[DA850_USER_SW_1_OFFSET + i]; You could use some shorter names here. In the context of EVM file, 'bb' will fit for "base board", "exp" works for expander. Also, here it is clear the macro is used as an array offset, so _OFFSET can be dropped altogether. Similarly with _names. Also, the global and static symbols should be pre-fixed with da850evm_ so it is easy to look up the symbol file or object dump. > + > + button->gpio = gpio + DA850_USER_SW_1_OFFSET + i; > + } > +} > + > +static struct gpio_led user_leds[DA850_N_USER_LED]; > + > +static struct gpio_led_platform_data user_led_gpio_data = { > + .leds = user_leds, > + .num_leds = ARRAY_SIZE(user_leds), > +}; > + > +static struct platform_device user_leds_gpio_device = { > + .name = "leds-gpio", > + .id = -1, > + .dev = { > + .platform_data = &user_led_gpio_data > + } > +}; > + > +static void da850_user_leds_init(unsigned gpio) > +{ > + int i; > + struct gpio_led *led; > + > + for (i = 0; i < DA850_N_USER_LED; i++) { > + led = &user_leds[i]; > + > + led->active_low = 1; > + led->gpio = gpio + DA850_USER_LED2_OFFSET + i; > + led->name = > + baseboard_expander_names[DA850_USER_LED2_OFFSET + i]; > + } > +} > + > +static int da850_evm_baseboard_expander_setup(struct i2c_client *client, > + unsigned gpio, unsigned ngpio, > + void *c) > +{ > + int ret; > + int deep_sleep_en, sw_rst; > + > + deep_sleep_en = gpio + DA850_DEEP_SLEEP_EN_OFFSET; > + sw_rst = gpio + DA850_SW_RST_OFFSET; > + > + /* Do not allow sysfs control of deep_sleep_en */ > + ret = gpio_request(deep_sleep_en, > + baseboard_expander_names[DA850_DEEP_SLEEP_EN_OFFSET]); How does gpio_request prevent sysfs control? Thanks, Sekhar From nsekhar at ti.com Fri Nov 19 07:24:50 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 19 Nov 2010 18:54:50 +0530 Subject: MMC/SD umount issues In-Reply-To: <4AF3472E1928D648979691C8C65C30740457ED0743@apmailbox01> References: <4AF3472E1928D648979691C8C65C30740457ED0743@apmailbox01> Message-ID: Hi Nathan, On Wed, Nov 17, 2010 at 22:10:46, Nathan Neitzke wrote: > There is a known issue with the latest TI release for the DM355 > linux-davinci-staging kernel regarding un-mounting a partition on an SD > card after the SD card has been removed from the system. Since our > embedded system relies on SD card for storage, and the user can remove > the SD card at any point without our knowledge, this is a scenario we > must handle. Data corruption is not a big issue, we have ways around > it. Our largest concern is not having the system lock up when we > un-mount the device. > > > > I have performed testing with several kernels. Our current kernel, the > latest TI Davinci PSP release from the linux-davinci-staging, and the > HEAD of the master branch for linux-davinci-staging all exhibit this > behavior. However, the linux-davinci tree from khilman does not ? the > issue appears to be fixed. So I am assuming this was fixed in the core > Linux MMC drivers (mmc_block driver possibly) since khilman?s tree is > now up to 2.6.36. > > > > My questions being ? is there any way to determine which features/fixes > (in regards to DM355 and related peripherals) we would lose out on by > simply using the linux-davinci tree as a basis for our kernel? Or, is > there any knowledge of when linux-davinci-staging will be rebased and > integrated with the 2.6.36 release? The next kernel rebase of TI release would only happen in Q1 of next year. You can have a look at this wiki for information on how DM355 support stands on linux-davinci tree: http://processors.wiki.ti.com/index.php/DaVinci_GIT_Linux_Kernel If you suspect a single patch upstream fixed this, then you can try and use git-bisect to locate the exact fix. You can bisect between HEAD and 2.6.32-davinci1. git-bisect would normally search for the offending commit, but here you would have to use it in the opposite sense to search for the commit which fixed the issue. Thanks, Sekhar From bengardiner at nanometrics.ca Fri Nov 19 08:17:35 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 19 Nov 2010 09:17:35 -0500 Subject: [PATCH v4] da850-evm: allow pca953x module build In-Reply-To: <1289926710-29892-1-git-send-email-bengardiner@nanometrics.ca> References: <1289926710-29892-1-git-send-email-bengardiner@nanometrics.ca> Message-ID: <1290176255-13187-1-git-send-email-bengardiner@nanometrics.ca> Change the mach-davinci Kconfig file so that GPIO_PCA953X is default when MACH_DAVINCI_DA850_EVM is set instead of always selecting. This allows users to compile pca953x as a module. Signed-off-by: Ben Gardiner CC: Sergei Shtylyov CC: Nori, Sekhar Reviewed-by: Kevin Hilman --- Changes since V3: * use default "default MACH_DAVINCI_DA850_EVM" without 'if' (Kevin Hilman) * rebased to df2e3886aeb6a93c602616d77c9a303ec202e69c of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git Changes since V2: * keep all Kconfig changes local to arch/arm/mach-davinci by exploting the fact that attribute assigment to config entries can span multiple files. * removing David Brownell since we don't need the wider scope in the changes Changes since V1: * make PCA953x default when MACH_DAVINCI_DA850_EVM in drivers/gpio/Kconfig instead of selecting GPIO_PCA953X when MACH_DAVINCI_DA850_EVM in arc/arm/mach-davinci/Kconfig * adding David Brownell because I think he is the pca953x goto guy Note that since NAND/NOR setup is performed in the pcs953x setup() function of board-da850-evm.c, when building pca953x as a module it will need to be insmod'ed from early userspace (i.e. initrd) to use a NAND or NOR rootfs for the system. The following commands and their output illustrate the the changes do allow the build of pca953x.c as a module. $cat arch/arm/configs/da8xx_omapl_defconfig|grep -i GPIO_PCA953X;\ make mrproper; make da8xx_omapl_defconfig ;\ cat .config |grep -i GPIO_PCA953X ; sed -ie 's/GPIO_PCA953X=y//g' .config;\ cat .config |grep -i GPIO_PCA953X; make oldconfig CLEAN scripts/basic CLEAN scripts/kconfig CLEAN include/config CLEAN .config HOSTCC scripts/basic/fixdep HOSTCC scripts/basic/docproc scripts/basic/docproc.c: In function ?docsect?: scripts/basic/docproc.c:336: warning: ignoring return value of ?asprintf?, declared with attribute warn_unused_result HOSTCC scripts/basic/hash HOSTCC scripts/kconfig/conf.o HOSTCC scripts/kconfig/kxgettext.o SHIPPED scripts/kconfig/zconf.tab.c SHIPPED scripts/kconfig/lex.zconf.c SHIPPED scripts/kconfig/zconf.hash.c HOSTCC scripts/kconfig/zconf.tab.o HOSTLD scripts/kconfig/conf CONFIG_GPIO_PCA953X=y scripts/kconfig/conf --oldconfig arch/arm/Kconfig * * Restart config... * * * GPIO Support * GPIO Support (GPIOLIB) [Y/?] y Debug GPIO calls (DEBUG_GPIO) [N/y/?] n /sys/class/gpio/... (sysfs interface) (GPIO_SYSFS) [N/y/?] n * * Memory mapped GPIO expanders: * IT8761E GPIO support (GPIO_IT8761E) [N/m/y/?] n * * I2C GPIO expanders: * Maxim MAX7300 GPIO expander (GPIO_MAX7300) [N/m/y/?] n MAX7319, MAX7320-7327 I2C Port Expanders (GPIO_MAX732X) [N/m/y/?] n PCA953x, PCA955x, TCA64xx, and MAX7310 I/O ports (GPIO_PCA953X) [Y/n/m/?] (NEW) fixup pca953x module build --- arch/arm/mach-davinci/Kconfig | 4 +++- 1 files changed, 3 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index b77b860..84066e8 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -148,7 +148,6 @@ config MACH_DAVINCI_DA850_EVM bool "TI DA850/OMAP-L138/AM18x Reference Platform" default ARCH_DAVINCI_DA850 depends on ARCH_DAVINCI_DA850 - select GPIO_PCA953X help Say Y here to select the TI DA850/OMAP-L138/AM18x Evaluation Module. @@ -178,6 +177,9 @@ config DA850_UI_RMII endchoice +config GPIO_PCA953X + default MACH_DAVINCI_DA850_EVM + config MACH_TNETV107X bool "TI TNETV107X Reference Platform" default ARCH_DAVINCI_TNETV107X -- 1.7.0.4 From khilman at deeprootsystems.com Fri Nov 19 09:20:17 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 19 Nov 2010 07:20:17 -0800 Subject: [PATCH v4] da850-evm: allow pca953x module build In-Reply-To: <1290176255-13187-1-git-send-email-bengardiner@nanometrics.ca> (Ben Gardiner's message of "Fri, 19 Nov 2010 09:17:35 -0500") References: <1289926710-29892-1-git-send-email-bengardiner@nanometrics.ca> <1290176255-13187-1-git-send-email-bengardiner@nanometrics.ca> Message-ID: <87eiahe3wu.fsf@deeprootsystems.com> Ben Gardiner writes: > Change the mach-davinci Kconfig file so that GPIO_PCA953X is default when > MACH_DAVINCI_DA850_EVM is set instead of always selecting. This allows users > to compile pca953x as a module. > > Signed-off-by: Ben Gardiner > CC: Sergei Shtylyov > CC: Nori, Sekhar > Reviewed-by: Kevin Hilman Applied, queuing for 2.6.38. Thanks, Kevin > --- > > Changes since V3: > * use default "default MACH_DAVINCI_DA850_EVM" without 'if' (Kevin Hilman) > * rebased to df2e3886aeb6a93c602616d77c9a303ec202e69c of > git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git > > Changes since V2: > * keep all Kconfig changes local to arch/arm/mach-davinci by exploting the > fact that attribute assigment to config entries can span multiple files. > * removing David Brownell since we don't need the wider scope in the changes > > Changes since V1: > * make PCA953x default when MACH_DAVINCI_DA850_EVM in drivers/gpio/Kconfig > instead of selecting GPIO_PCA953X when MACH_DAVINCI_DA850_EVM in > arc/arm/mach-davinci/Kconfig > * adding David Brownell because I think he is the pca953x goto guy > > Note that since NAND/NOR setup is performed in the pcs953x setup() function of > board-da850-evm.c, when building pca953x as a module it will need to be > insmod'ed from early userspace (i.e. initrd) to use a NAND or NOR rootfs for > the system. > > The following commands and their output illustrate the the changes do allow > the build of pca953x.c as a module. > > $cat arch/arm/configs/da8xx_omapl_defconfig|grep -i GPIO_PCA953X;\ > make mrproper; make da8xx_omapl_defconfig ;\ > cat .config |grep -i GPIO_PCA953X ; sed -ie 's/GPIO_PCA953X=y//g' .config;\ > cat .config |grep -i GPIO_PCA953X; make oldconfig > CLEAN scripts/basic > CLEAN scripts/kconfig > CLEAN include/config > CLEAN .config > HOSTCC scripts/basic/fixdep > HOSTCC scripts/basic/docproc > scripts/basic/docproc.c: In function ?docsect?: > scripts/basic/docproc.c:336: warning: ignoring return value of ?asprintf?, declared with attribute warn_unused_result > HOSTCC scripts/basic/hash > HOSTCC scripts/kconfig/conf.o > HOSTCC scripts/kconfig/kxgettext.o > SHIPPED scripts/kconfig/zconf.tab.c > SHIPPED scripts/kconfig/lex.zconf.c > SHIPPED scripts/kconfig/zconf.hash.c > HOSTCC scripts/kconfig/zconf.tab.o > HOSTLD scripts/kconfig/conf > CONFIG_GPIO_PCA953X=y > scripts/kconfig/conf --oldconfig arch/arm/Kconfig > * > * Restart config... > * > * > * GPIO Support > * > GPIO Support (GPIOLIB) [Y/?] y > Debug GPIO calls (DEBUG_GPIO) [N/y/?] n > /sys/class/gpio/... (sysfs interface) (GPIO_SYSFS) [N/y/?] n > * > * Memory mapped GPIO expanders: > * > IT8761E GPIO support (GPIO_IT8761E) [N/m/y/?] n > * > * I2C GPIO expanders: > * > Maxim MAX7300 GPIO expander (GPIO_MAX7300) [N/m/y/?] n > MAX7319, MAX7320-7327 I2C Port Expanders (GPIO_MAX732X) [N/m/y/?] n > PCA953x, PCA955x, TCA64xx, and MAX7310 I/O ports (GPIO_PCA953X) [Y/n/m/?] (NEW) > > fixup pca953x module build > --- > arch/arm/mach-davinci/Kconfig | 4 +++- > 1 files changed, 3 insertions(+), 1 deletions(-) > > diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig > index b77b860..84066e8 100644 > --- a/arch/arm/mach-davinci/Kconfig > +++ b/arch/arm/mach-davinci/Kconfig > @@ -148,7 +148,6 @@ config MACH_DAVINCI_DA850_EVM > bool "TI DA850/OMAP-L138/AM18x Reference Platform" > default ARCH_DAVINCI_DA850 > depends on ARCH_DAVINCI_DA850 > - select GPIO_PCA953X > help > Say Y here to select the TI DA850/OMAP-L138/AM18x Evaluation Module. > > @@ -178,6 +177,9 @@ config DA850_UI_RMII > > endchoice > > +config GPIO_PCA953X > + default MACH_DAVINCI_DA850_EVM > + > config MACH_TNETV107X > bool "TI TNETV107X Reference Platform" > default ARCH_DAVINCI_TNETV107X From bengardiner at nanometrics.ca Fri Nov 19 09:38:10 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 19 Nov 2010 10:38:10 -0500 Subject: [PATCH v2 2/4] da850-evm: add UI Expander pushbuttons In-Reply-To: References: <95f48a32a0256ecdb7148aa08d16f64928a7e5d8.1289935504.git.bengardiner@nanometrics.ca> Message-ID: Hi Sekhar, Thank you for your reviews. On Fri, Nov 19, 2010 at 4:58 AM, Nori, Sekhar wrote: > [...] > On Wed, Nov 17, 2010 at 01:09:35, Ben Gardiner wrote: >> [...] >> Changes since v1: >> ?* set INPUT_POLLDEV default for DA850_EVM machine, but don't select it >> ? ?unconditionally > > I didn't see the v1 posting (wonder why), but why is this > required? Why cant we depend on this being selected from > Device Drivers->Input device support in menuconfig? I'm sorry about that. I checked gmane and the v1 never made it to the davinci-linux list. I think it was because of my (mis)use of git send-email. Version 2 made it to the list so I've now found the magic incantation. The reason we should at least select INPUT_POLLDEV as a default option is: when it is not set the gpio-keys instances registered by the da850-evm board setup routine will fail probe() of the gpio-keys driver since it will not have polled-input support enabled. By setting INPUT_POLLDEV default for the da850-evm users will get functioning pushbuttons and switches with the default config but they will be able to disable INPUT_POLLDEV or gpio-keys drivers in their defconfig at their convenience. > [...] > >> @@ -349,6 +421,10 @@ static struct i2c_board_info __initdata da850_evm_i2c_devices[] = { >> ? ? ? { >> ? ? ? ? ? ? ? I2C_BOARD_INFO("tca6416", 0x20), >> ? ? ? ? ? ? ? .platform_data = &da850_evm_ui_expander_info, >> + ? ? ? ? ? ? /* >> + ? ? ? ? ? ? ?* TODO : populate at runtime using >> + ? ? ? ? ? ? ?* .irq = gpio_to_irq(GPIO_TO_PIN(2,7)), >> + ? ? ? ? ? ? ?*/ > > You seem to be adding this in this patch and removing > in 4/4. Oops. That's my fault. The internal reviewers also reminded me to remove these before sending. Sorry for the noise/ I will remove this block in _this_ patch in the re-spin. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From bengardiner at nanometrics.ca Fri Nov 19 09:38:34 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 19 Nov 2010 10:38:34 -0500 Subject: [PATCH v2 3/4] da850-evm: extract defines for SEL{A, B, C} pins in UI expander In-Reply-To: References: Message-ID: Hi Sehkar, On Fri, Nov 19, 2010 at 6:19 AM, Nori, Sekhar wrote: > [...] > In this case, I think in this case indexed array initialization > will help keep the offsets and names matched. Here is an untested > patch on your patch, please consider using it. I love it. I'll post a re-spin with this patch integrated and you SOB added. Thank you, Sehkar. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From bengardiner at nanometrics.ca Fri Nov 19 09:40:50 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 19 Nov 2010 10:40:50 -0500 Subject: [PATCH v2 4/4] da850-evm: add baseboard UI expander buttons, switches and LEDs In-Reply-To: References: Message-ID: Hi Sehkar, On Fri, Nov 19, 2010 at 7:14 AM, Nori, Sekhar wrote: > The board patches look good to me overall. Some minor comments below: Thanks -- and I appreciate your input. > On Wed, Nov 17, 2010 at 01:09:37, Ben Gardiner wrote: >> [...] >> +static const char const *baseboard_expander_names[] = { >> + ? ? "deep_sleep_en", "sw_rst", "tp_23", "tp_22", "tp_21", "user_pb1", >> + ? ? "user_led2", "user_led1", "user_sw_1", "user_sw_2", "user_sw_3", >> + ? ? "user_sw_4", "user_sw_5", "user_sw_6", "user_sw_7", "user_sw_8" >> +}; >> + >> +#define DA850_DEEP_SLEEP_EN_OFFSET ? ? ? ? ? 0 >> +#define DA850_SW_RST_OFFSET ? ? ? ? ? ? ? ? ?1 >> +#define DA850_PB1_OFFSET ? ? ? ? ? ? ? ? ? ? 5 >> +#define DA850_USER_LED2_OFFSET ? ? ? ? ? ? ? ? ? ? ? 6 >> +#define DA850_USER_SW_1_OFFSET ? ? ? ? ? ? ? ? ? ? ? 8 > > Again, I think index initialized array will work much > better here. Currently it is error prone to keep the defines > and the array of names in sync. Agreed. Now that I've seen what you did with the previous patch I am eager to apply that pattern also to the definitions in this patch. >> [...] >> +static struct gpio_keys_platform_data user_pb_gpio_key_platform_data = { > > Similarly "da850evm_bb_pb_pdata" instead of the long name? Will do. >> [...] >> +static struct gpio_keys_button user_sw_gpio_keys[DA850_N_USER_SW]; > > You could initialize most static fields here itself using: > > static struct gpio_keys_button user_sw_gpio_keys[] = { > ? ? ? ?[0 ... DA850_N_USER_SW] = { > ? ? ? ? ? ? ? ?... > ? ? ? ? ? ? ? ?... > ? ? ? ? ? ? ? ?... > ? ? ? ?}, > }; > > This way your runtime initialization will come down. Indeed; I am eager to extend the pattern you introduced to this initialization also. >> + >> +static struct gpio_keys_platform_data user_sw_gpio_key_platform_data = { >> + ? ? .buttons = user_sw_gpio_keys, >> + ? ? .nbuttons = ARRAY_SIZE(user_sw_gpio_keys), >> + ? ? .rep = 0, /* disable auto-repeat */ >> + ? ? .poll_interval = DA850_SW_POLL_MS, >> +}; > > I wonder if we really have create to separate platform data > for switches and push buttons. If it is only the debounce period > that is different, it can be handled by initializing that field > differently. I see. Good idea; we can declare an array of gpio_keys_platform_data. Note; it is the polling interval which differs, not the debounce interval. >> + >> +static struct platform_device user_sw_gpio_key_device = { >> + ? ? .name = "gpio-keys", >> + ? ? .id = 2, >> + ? ? .dev = { >> + ? ? ? ? ? ? .platform_data = &user_sw_gpio_key_platform_data > > End with a ',' here. Will do. >> [...] >> +static void da850_user_switches_init(unsigned gpio) >> +{ >> + ? ? int i; >> + ? ? struct gpio_keys_button *button; >> + >> + ? ? for (i = 0; i < DA850_N_USER_SW; i++) { >> + ? ? ? ? ? ? button = &user_sw_gpio_keys[i]; >> + >> + ? ? ? ? ? ? button->code = SW_LID + i; >> + ? ? ? ? ? ? button->type = EV_SW; >> + ? ? ? ? ? ? button->active_low = 1; >> + ? ? ? ? ? ? button->wakeup = 0; >> + ? ? ? ? ? ? button->debounce_interval = DA850_PB_DEBOUNCE_MS; >> + ? ? ? ? ? ? button->desc = (char *) >> + ? ? ? ? ? ? ? ? ? ? baseboard_expander_names[DA850_USER_SW_1_OFFSET + i]; > > You could use some shorter names here. In the context of EVM file, 'bb' > will fit for "base board", "exp" works for expander. Also, here it is > clear the macro is used as an array offset, so _OFFSET can be dropped > altogether. Similarly with _names. Also, the global and static symbols > should be pre-fixed with da850evm_ so it is easy to look up the symbol > file or object dump. I see your points; 1) exp and bb for expander and baseboard variable names, respectively 2) drop _OFFSET and _names 3) prefix statics and globals with da850evm. >> [...] > > How does gpio_request prevent sysfs control? To obtain access to a gpio through the sysfs interface the user must first write the gpio number to 'export'. When the gpio has been gpio_request()'d the result of writing to 'export' is nothing; whereas writing to export would normally result in the appearance of a named gpio line alongside 'export'. I hope the following commands and their output illustrate my point: $ cd /sys/class/gpio/ $ ls export gpiochip128 gpiochip160 gpiochip64 unexport gpiochip0 gpiochip144 gpiochip32 gpiochip96 $ echo 160 > export $ ls export gpiochip128 gpiochip160 gpiochip64 unexport gpiochip0 gpiochip144 gpiochip32 gpiochip96 $ echo 163 > export $ ls export gpiochip128 gpiochip160 gpiochip64 tp_22 gpiochip0 gpiochip144 gpiochip32 gpiochip96 unexport Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From cyril at ti.com Fri Nov 19 09:55:44 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 19 Nov 2010 10:55:44 -0500 Subject: [PATCH v6 00/12] tnetv107x ssp drivers Message-ID: <1290182156-20104-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 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 | 472 ++++++++++++++++ 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 | 298 ++++++++++ include/linux/mfd/ti_ssp.h | 97 ++++ 20 files changed, 2445 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 Fri Nov 19 09:55:46 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 19 Nov 2010 10:55:46 -0500 Subject: [PATCH v6 02/12] davinci: add tnetv107x ssp platform device In-Reply-To: <1290182156-20104-1-git-send-email-cyril@ti.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> Message-ID: <1290182156-20104-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 Fri Nov 19 09:55:47 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 19 Nov 2010 10:55:47 -0500 Subject: [PATCH v6 03/12] davinci: add ssp config for tnetv107x evm board In-Reply-To: <1290182156-20104-1-git-send-email-cyril@ti.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> Message-ID: <1290182156-20104-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 Fri Nov 19 09:55:45 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 19 Nov 2010 10:55:45 -0500 Subject: [PATCH v6 01/12] misc: add driver for sequencer serial port In-Reply-To: <1290182156-20104-1-git-send-email-cyril@ti.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> Message-ID: <1290182156-20104-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 | 472 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/ti_ssp.h | 87 ++++++++ 4 files changed, 571 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..7efb87b --- /dev/null +++ b/drivers/mfd/ti-ssp.c @@ -0,0 +1,472 @@ +/* + * 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; +}; + +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 port iosel, caller must hold ssp->lock */ +static int __set_iosel(struct ti_ssp *ssp, int port, u32 iosel) +{ + unsigned val; + + /* 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 = ssp_read(ssp, REG_IOSEL_2); + val &= 0x0007 << (port ? 0 : 16); + val |= (iosel & 0x00070000) >> (port ? 0 : 16); + ssp_write(ssp, REG_IOSEL_2, val); + + return 0; +} + +int ti_ssp_set_iosel(struct device *dev, u32 iosel) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + int ret; + + spin_lock(&ssp->lock); + ret = __set_iosel(ssp, port, iosel); + spin_unlock(&ssp->lock); + + return ret; +} +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); + u32 val; + + val = ssp_read(ssp, REG_IOSEL_2); + val >>= (port ? 27 : 11); + + return val & 0x0f; +} +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); + u32 mask; + + spin_lock(&ssp->lock); + + val &= 0x0f; + val <<= (port ? 22 : 6); + mask = 0x0f << (port ? 22 : 6); + ssp_rmw(ssp, REG_IOSEL_2, mask, val); + + 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 Fri Nov 19 09:55:48 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 19 Nov 2010 10:55:48 -0500 Subject: [PATCH v6 04/12] spi: add ti-ssp spi master driver In-Reply-To: <1290182156-20104-1-git-send-email-cyril@ti.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> Message-ID: <1290182156-20104-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 Fri Nov 19 09:55:50 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 19 Nov 2010 10:55:50 -0500 Subject: [PATCH v6 06/12] regulator: add driver for tps6524x regulator In-Reply-To: <1290182156-20104-1-git-send-email-cyril@ti.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> Message-ID: <1290182156-20104-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 Fri Nov 19 09:55:52 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 19 Nov 2010 10:55:52 -0500 Subject: [PATCH v6 08/12] gpio: add ti-ssp gpio driver In-Reply-To: <1290182156-20104-1-git-send-email-cyril@ti.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> Message-ID: <1290182156-20104-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 Fri Nov 19 09:55:53 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 19 Nov 2010 10:55:53 -0500 Subject: [PATCH v6 09/12] davinci: add tnetv107x evm ti-ssp gpio device In-Reply-To: <1290182156-20104-1-git-send-email-cyril@ti.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> Message-ID: <1290182156-20104-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 Fri Nov 19 09:55:54 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 19 Nov 2010 10:55:54 -0500 Subject: [PATCH v6 10/12] backlight: add support for tps6116x controller In-Reply-To: <1290182156-20104-1-git-send-email-cyril@ti.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> Message-ID: <1290182156-20104-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 | 298 ++++++++++++++++++++++++++++++++++++ 3 files changed, 306 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..d7d8cca --- /dev/null +++ b/drivers/video/backlight/tps6116x.c @@ -0,0 +1,298 @@ +/* + * 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; + } + + 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 Fri Nov 19 09:55:55 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 19 Nov 2010 10:55:55 -0500 Subject: [PATCH v6 11/12] davinci: add tnetv107x evm backlight device In-Reply-To: <1290182156-20104-1-git-send-email-cyril@ti.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> Message-ID: <1290182156-20104-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 cyril at ti.com Fri Nov 19 09:55:49 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 19 Nov 2010 10:55:49 -0500 Subject: [PATCH v6 05/12] davinci: add spi devices on tnetv107x evm In-Reply-To: <1290182156-20104-1-git-send-email-cyril@ti.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> Message-ID: <1290182156-20104-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 Fri Nov 19 09:55:56 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 19 Nov 2010 10:55:56 -0500 Subject: [PATCH v6 12/12] davinci: add tnetv107x evm i2c eeprom device In-Reply-To: <1290182156-20104-1-git-send-email-cyril@ti.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> Message-ID: <1290182156-20104-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 Fri Nov 19 09:55:51 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 19 Nov 2010 10:55:51 -0500 Subject: [PATCH v6 07/12] davinci: add tnetv107x evm regulators In-Reply-To: <1290182156-20104-1-git-send-email-cyril@ti.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> Message-ID: <1290182156-20104-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 khilman at deeprootsystems.com Fri Nov 19 10:11:31 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Fri, 19 Nov 2010 08:11:31 -0800 Subject: [PATCH] davinci: kconfig: select at24 eeprom for selected boards Message-ID: <1290183091-25956-1-git-send-email-khilman@deeprootsystems.com> Ensure that the at24 eeprom driver is selected for certain boards that need boot data (e.g. MAC address) from EEPROM. Signed-off-by: Kevin Hilman --- arch/arm/mach-davinci/Kconfig | 12 ++++++++++++ 1 files changed, 12 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index 84066e8..77671b7 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -61,6 +61,8 @@ config MACH_DAVINCI_EVM bool "TI DM644x EVM" default ARCH_DAVINCI_DM644x depends on ARCH_DAVINCI_DM644x + select MISC_DEVICES + select EEPROM_AT24 help Configure this option to specify the whether the board used for development is a DM644x EVM @@ -68,6 +70,8 @@ config MACH_DAVINCI_EVM config MACH_SFFSDR bool "Lyrtech SFFSDR" depends on ARCH_DAVINCI_DM644x + select MISC_DEVICES + select EEPROM_AT24 help Say Y here to select the Lyrtech Small Form Factor Software Defined Radio (SFFSDR) board. @@ -99,6 +103,8 @@ config MACH_DAVINCI_DM6467_EVM default ARCH_DAVINCI_DM646x depends on ARCH_DAVINCI_DM646x select MACH_DAVINCI_DM6467TEVM + select MISC_DEVICES + select EEPROM_AT24 help Configure this option to specify the whether the board used for development is a DM6467 EVM @@ -110,6 +116,8 @@ config MACH_DAVINCI_DM365_EVM bool "TI DM365 EVM" default ARCH_DAVINCI_DM365 depends on ARCH_DAVINCI_DM365 + select MISC_DEVICES + select EEPROM_AT24 help Configure this option to specify whether the board used for development is a DM365 EVM @@ -119,6 +127,8 @@ config MACH_DAVINCI_DA830_EVM default ARCH_DAVINCI_DA830 depends on ARCH_DAVINCI_DA830 select GPIO_PCF857X + select MISC_DEVICES + select EEPROM_AT24 help Say Y here to select the TI DA830/OMAP-L137/AM17x Evaluation Module. @@ -190,6 +200,8 @@ config MACH_TNETV107X config MACH_MITYOMAPL138 bool "Critical Link MityDSP-L138/MityARM-1808 SoM" depends on ARCH_DAVINCI_DA850 + select MISC_DEVICES + select EEPROM_AT24 help Say Y here to select the Critical Link MityDSP-L138/MityARM-1808 System on Module. Information on this SoM may be found at -- 1.7.2.1 From vm.rod25 at gmail.com Fri Nov 19 10:22:15 2010 From: vm.rod25 at gmail.com (vm.rod25 at gmail.com) Date: Fri, 19 Nov 2010 10:22:15 -0600 Subject: [RFC PATCH] davinci: Fix UARTs configuration da850 Message-ID: <1290183735-4501-1-git-send-email-vm.rod25@gmail.com> From: Victor Rodriguez Generic pin list has RTS and CTS pins defined for UART1 and UART2. Signed-off-by: Victor Rodriguez --- arch/arm/mach-davinci/da850.c | 8 ++++++-- 1 files changed, 6 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index b3b1adb..d36e767 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -420,9 +420,13 @@ static const struct mux_config da850_pins[] = { MUX_CFG(DA850, UART0_RXD, 3, 16, 15, 2, false) MUX_CFG(DA850, UART0_TXD, 3, 20, 15, 2, false) /* UART1 function */ + MUX_CFG(DA850, NUART1_CTS, 0, 20, 15, 2, false) + MUX_CFG(DA850, NUART1_RTS, 0, 16, 15, 2, false) MUX_CFG(DA850, UART1_RXD, 4, 24, 15, 2, false) MUX_CFG(DA850, UART1_TXD, 4, 28, 15, 2, false) /* UART2 function */ + MUX_CFG(DA850, NUART2_CTS, 0, 28, 15, 2, false) + MUX_CFG(DA850, NUART2_RTS, 0, 24, 15, 2, false) MUX_CFG(DA850, UART2_RXD, 4, 16, 15, 2, false) MUX_CFG(DA850, UART2_TXD, 4, 20, 15, 2, false) /* I2C1 function */ @@ -578,12 +582,12 @@ const short da850_uart0_pins[] __initdata = { }; const short da850_uart1_pins[] __initdata = { - DA850_UART1_RXD, DA850_UART1_TXD, + DA850_NUART1_CTS, DA850_NUART1_RTS, DA850_UART1_RXD, DA850_UART1_TXD, -1 }; const short da850_uart2_pins[] __initdata = { - DA850_UART2_RXD, DA850_UART2_TXD, + DA850_NUART2_CTS, DA850_NUART2_RTS, DA850_UART2_RXD, DA850_UART2_TXD, -1 }; -- 1.7.0.4 From vm.rod25 at gmail.com Fri Nov 19 10:23:31 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Fri, 19 Nov 2010 10:23:31 -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> <4CE124AE.4080301@mvista.com> <4CE1560F.6080705@mvista.com> <4CE2AB73.3050109@mvista.com> <4CE2ECCD.8090603@criticallink.com> <87fwuyfanl.fsf@deeprootsystems.com> Message-ID: On Thu, Nov 18, 2010 at 6:17 PM, Victor Rodriguez wrote: > On Thu, Nov 18, 2010 at 5:57 PM, Kevin Hilman > wrote: >> "Nori, Sekhar" writes: >> >>> Hi Michael, >>> >>> On Wed, Nov 17, 2010 at 02:12:53, Michael Williamson wrote: >>>> >>>> Help me out. ?Why do we need generic pin lists? >>>> >>> >>> They might help in cases where all boards will use the same set of >>> pins. For example, every one who uses I2C will most likely both the >>> clock and data pins from the IP. For more complex peripherals with >>> different pins options they serve a documentation purpose at best. >>> >>>> It seems to me that the "generic pin list" for da850.c isn't practical for most >>>> (if not all) of the peripherals. ?They should be done using __initdata in >>>> each board file. >>> >>> Yes, agreed. >>> >>>> >>>> Just a cursory glance at what's in da850.c highlights several items being set >>>> up for the EVM and not generically. ?For example: >>>> >>>> - da850_uart1_pins and da850_uart2_pins: I believe both have RTS/CTS pins which >>>> ? for a generic definition should be included as for UART0, but would then >>>> ? be unused as the EVM doesn't use these pins in this function. >>> >>> Yes, the generic pin list should have RTS and CTS pins defined for UART1 >>> and UART2. This needs fixing. >>> >>>> >>>> - da850_mcasp_pins: if generic, must include all 16 AXR pins. ?I think you'd >>>> ? be hard pressed to find a board configuration that would use all 16 AXR pins >>>> ? for the McASP. ?I'm fairly sure the EVM uses the pins called out, and uses >>>> ? other pins for other functions. ?So it's likely this structure wouldn't get used. >>> >>> Yes, the generic pin list should either be completed or removed >>> altogether and the existing pin list da850_mcasp_pins should be >>> copied into the board file and called da850_evm_mcasp_pins. >>> >>>> >>>> - da850_mmcsd0_pins : includes 2 GPIO pins (specific to the EVM, though possible for >>>> ? other boards) for the card detect and write protect signals. ?These pins are >>>> ? completely arbitrary for that particular board design. I also believe that >>>> ? the complete mmcsd0 port has 4 more data lines as part of it's peripheral, although >>>> ? the driver doesn't support using them. >>> >>> This is incorrect again. The generic pin list should be completed >>> (or removed) and the existing list should be copied into the EVM board >>> file as da850_evm_mmcsd0_pins. >>> >>>> >>>> - da850_emif25_pins interface doesn't include the generic pins for some of >>>> ? the SDRAM functions. >>> >>> Yes, this should be completed (or removed). This list is unused anyway. >>> >>>> >>>> - da850_cpgmac_pins defines both RMII and MII pins. ?I don't think any board >>>> ? would want to configure both sets at the same time. ?Seems like this should >>>> ? never get used... >>> >>> Agreed. >>> >>>> >>>> It's also incomplete. ?What about the uPP pin list? ?Or the VPIF? ?Etc. >>> >>> These should be added as the drivers for these devices are >>> supported. >>> >>>> >>>> I think a board file author should be familiar enough with the SoC to understand >>>> what peripheral pins he should be configuring for his/her particular hardware setup >>>> and explicitly specify them in the board file. >>> >>> Agree. >>> >>>> >>>> If you remove the common pin-mux lists and move them to a board file, then once you >>>> configure your specific platform, is there any more memory used than with >>>> the common scheme? ?Of course, there would be replication of pin-mux code in the board >>> >>> There is no memory wastage. All the pin lists are init data. >>> >>> I too prefer all generic pin lists which are most likely not >>> going to be used at all to be removed. Unused stuff like this >>> will only make code difficult to read. >> >> FWIW, I agree. >> >> Now, who wants to tackle it? >> >> Kevin >> > > I could but I will need a little help and from all of you. :) > > Regards > > Victor Rodriguez > Hey I have sent the first patch in order to fix UARTs on da850. please check if this is right Regards Victor Rodriguez >> _______________________________________________ >> 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 Fri Nov 19 10:25:58 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 19 Nov 2010 19:25:58 +0300 Subject: [PATCH v6 05/12] davinci: add spi devices on tnetv107x evm In-Reply-To: <1290182156-20104-6-git-send-email-cyril@ti.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> <1290182156-20104-6-git-send-email-cyril@ti.com> Message-ID: <4CE6A516.402@mvista.com> Hello. Cyril Chemparathy wrote: > 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 [...] > @@ -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"); Why not just: int ret = gpio_request(EVM_SPI_CS_GPIO, "spi chipsel"); Also, and empty line after declaration wouldn't hurt. > + 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); Why not do it right with gpio_direction_output() call? WBR, Sergei From michael.williamson at criticallink.com Fri Nov 19 10:41:56 2010 From: michael.williamson at criticallink.com (Michael Williamson) Date: Fri, 19 Nov 2010 11:41:56 -0500 Subject: [RFC PATCH] davinci: Fix UARTs configuration da850 In-Reply-To: <1290183735-4501-1-git-send-email-vm.rod25@gmail.com> References: <1290183735-4501-1-git-send-email-vm.rod25@gmail.com> Message-ID: <4CE6A8D4.1010406@criticallink.com> Hello Victor, On 11/19/2010 11:22 AM, vm.rod25 at gmail.com wrote: > From: Victor Rodriguez > > Generic pin list has RTS and CTS pins defined > for UART1 and UART2. > > Signed-off-by: Victor Rodriguez > --- I don't think this patch is complete, it will break the current boards with out some other modifications first. (see below) > arch/arm/mach-davinci/da850.c | 8 ++++++-- > 1 files changed, 6 insertions(+), 2 deletions(-) > > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index b3b1adb..d36e767 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -420,9 +420,13 @@ static const struct mux_config da850_pins[] = { > MUX_CFG(DA850, UART0_RXD, 3, 16, 15, 2, false) > MUX_CFG(DA850, UART0_TXD, 3, 20, 15, 2, false) > /* UART1 function */ > + MUX_CFG(DA850, NUART1_CTS, 0, 20, 15, 2, false) > + MUX_CFG(DA850, NUART1_RTS, 0, 16, 15, 2, false) > MUX_CFG(DA850, UART1_RXD, 4, 24, 15, 2, false) > MUX_CFG(DA850, UART1_TXD, 4, 28, 15, 2, false) > /* UART2 function */ > + MUX_CFG(DA850, NUART2_CTS, 0, 28, 15, 2, false) > + MUX_CFG(DA850, NUART2_RTS, 0, 24, 15, 2, false) > MUX_CFG(DA850, UART2_RXD, 4, 16, 15, 2, false) > MUX_CFG(DA850, UART2_TXD, 4, 20, 15, 2, false) > /* I2C1 function */ > @@ -578,12 +582,12 @@ const short da850_uart0_pins[] __initdata = { > }; > > const short da850_uart1_pins[] __initdata = { > - DA850_UART1_RXD, DA850_UART1_TXD, > + DA850_NUART1_CTS, DA850_NUART1_RTS, DA850_UART1_RXD, DA850_UART1_TXD, > -1 > }; > > const short da850_uart2_pins[] __initdata = { > - DA850_UART2_RXD, DA850_UART2_TXD, > + DA850_NUART2_CTS, DA850_NUART2_RTS, DA850_UART2_RXD, DA850_UART2_TXD, > -1 > }; > I think all the da850_uart?_pins[] should be deleted from this file. Instead, each of the board files should have a local/static __initdata definition for these pins (without the RTS/CTS for the uart1 and uart2 for current boards). You'll have to hit each of the da850 board files. Same approach would apply for the mcasp pins (delete from da850.c, add to board files supporting mcasp configuration). I think the others (ethernet pins, emif25 pins) could simply be deleted (nothing is referencing them). You could probably cover all those in a common patch series under the title of "clean up generic pin lists for da850"... Thanks for taking a crack at it. I was going to volunteer seeing as I made the stink about it but you beat me to the punch. -Mike From sshtylyov at mvista.com Fri Nov 19 10:44:00 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 19 Nov 2010 19:44:00 +0300 Subject: [PATCH v6 11/12] davinci: add tnetv107x evm backlight device In-Reply-To: <1290182156-20104-12-git-send-email-cyril@ti.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> <1290182156-20104-12-git-send-email-cyril@ti.com> Message-ID: <4CE6A950.8040704@mvista.com> Hello. Cyril Chemparathy 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) Why it's not aligned with others? WBR, Sergei From dmitry.torokhov at gmail.com Fri Nov 19 11:02:32 2010 From: dmitry.torokhov at gmail.com (Dmitry Torokhov) Date: Fri, 19 Nov 2010 09:02:32 -0800 Subject: [PATCH v2 4/4] da850-evm: add baseboard UI expander buttons, switches and LEDs In-Reply-To: References: Message-ID: <20101119170232.GB6236@core.coreip.homeip.net> Hi Ben, On Fri, Nov 19, 2010 at 10:40:50AM -0500, Ben Gardiner wrote: > > >> + > >> +static struct gpio_keys_platform_data user_sw_gpio_key_platform_data = { > >> + ? ? .buttons = user_sw_gpio_keys, > >> + ? ? .nbuttons = ARRAY_SIZE(user_sw_gpio_keys), > >> + ? ? .rep = 0, /* disable auto-repeat */ > >> + ? ? .poll_interval = DA850_SW_POLL_MS, > >> +}; > > > > I wonder if we really have create to separate platform data > > for switches and push buttons. If it is only the debounce period > > that is different, it can be handled by initializing that field > > differently. > > I see. Good idea; we can declare an array of gpio_keys_platform_data. > > Note; it is the polling interval which differs, not the debounce interval. Another question is why do you want to poll the same device at different intervals? The processor is already woken up so it makes sense to do as much as possible instead of going to sleep. In your case 200/700 for every 1400ms interval you wake an extra time to poll switches so unless polling switches takes very long time it is better to combine them together (and into one input device, probably). -- Dmitry From cyril at ti.com Fri Nov 19 11:04:25 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 19 Nov 2010 12:04:25 -0500 Subject: [PATCH v6 11/12] davinci: add tnetv107x evm backlight device In-Reply-To: <4CE6A950.8040704@mvista.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> <1290182156-20104-12-git-send-email-cyril@ti.com> <4CE6A950.8040704@mvista.com> Message-ID: <4CE6AE19.6090600@ti.com> On 11/19/2010 11:44 AM, Sergei Shtylyov wrote: > Hello. > > Cyril Chemparathy 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) > > Why it's not aligned with others? It actually is aligned if you remove the leading space/+. Regards Cyril. From cyril at ti.com Fri Nov 19 11:11:57 2010 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 19 Nov 2010 12:11:57 -0500 Subject: [PATCH v6 05/12] davinci: add spi devices on tnetv107x evm In-Reply-To: <4CE6A516.402@mvista.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> <1290182156-20104-6-git-send-email-cyril@ti.com> <4CE6A516.402@mvista.com> Message-ID: <4CE6AFDD.50806@ti.com> On 11/19/2010 11:25 AM, Sergei Shtylyov wrote: [...] >> + ret = gpio_request(EVM_SPI_CS_GPIO, "spi chipsel"); > > Why not just: > > int ret = gpio_request(EVM_SPI_CS_GPIO, "spi chipsel"); > > Also, and empty line after declaration wouldn't hurt. > [...] >> + return gpio_set_value(gpio, cs ? 1 : 0); > > Why not do it right with gpio_direction_output() call? Sure. I might as well change to use gpio_request_one() while I'm at it. If I get enough comments to warrant another revision of this series, I will put in this change at the time. Otherwise, I'd rather roll this in with other patches on my pending list. Is that ok? Thanks for taking a look, Sergei. Regards - Cyril. From sshtylyov at mvista.com Fri Nov 19 11:11:59 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 19 Nov 2010 20:11:59 +0300 Subject: [PATCH v6 11/12] davinci: add tnetv107x evm backlight device In-Reply-To: <4CE6AE19.6090600@ti.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> <1290182156-20104-12-git-send-email-cyril@ti.com> <4CE6A950.8040704@mvista.com> <4CE6AE19.6090600@ti.com> Message-ID: <4CE6AFDF.3080709@mvista.com> Hello. Cyril Chemparathy 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) >> Why it's not aligned with others? > It actually is aligned if you remove the leading space/+. Heh, realized that after sending. :-) That's why I use spaces instead of one-space tab. Unfortunately, scripts/checkpatch.pl does not like it now... :-/ > Regards > Cyril. WBR, Sergei From bengardiner at nanometrics.ca Fri Nov 19 11:15:32 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 19 Nov 2010 12:15:32 -0500 Subject: [PATCH v2 4/4] da850-evm: add baseboard UI expander buttons, switches and LEDs In-Reply-To: <20101119170232.GB6236@core.coreip.homeip.net> References: <20101119170232.GB6236@core.coreip.homeip.net> Message-ID: Hi Dmitry, On Fri, Nov 19, 2010 at 12:02 PM, Dmitry Torokhov wrote: > Another question is why do you want to poll the same device at > different intervals? The processor is already woken up so it makes sense > to do as much as possible instead of going to sleep. In your case > 200/700 for every 1400ms interval you wake an extra time to poll > switches so unless polling switches takes very long time it is better to > combine them together (and into one input device, probably). I see. That is a good question :) I didn't realize that splitting into two separate input devices with different polling intervals would have the opposite effect than what I intended. Thank you for pointing this out. I will definitely integrate them into one input device polled at 200ms. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From bengardiner at nanometrics.ca Fri Nov 19 15:37:19 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 19 Nov 2010 16:37:19 -0500 Subject: [PATCH v3 0/4] 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 Alexander Clouter. 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. [1] http://article.gmane.org/gmane.linux.kernel/1052551 [2] http://article.gmane.org/gmane.linux.kernel.commits.head/260919 [3] http://www.mail-archive.com/devicetree-discuss at lists.ozlabs.org/msg01974.html [4] http://article.gmane.org/gmane.linux.kernel.cross-arch/7786 Alexander Clouter (1): input: gpio_keys: polling mode support Ben Gardiner (3): 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 arch/arm/mach-davinci/Kconfig | 3 + arch/arm/mach-davinci/board-da850-evm.c | 347 +++++++++++++++++++++++++++++-- drivers/input/keyboard/gpio_keys.c | 120 +++++++++-- include/linux/gpio_keys.h | 1 + 4 files changed, 440 insertions(+), 31 deletions(-) --- 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 Fri Nov 19 15:37:21 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 19 Nov 2010 16:37:21 -0500 Subject: [PATCH v3 2/4] da850-evm: add UI Expander pushbuttons In-Reply-To: References: Message-ID: 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 --- 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/Kconfig | 3 + arch/arm/mach-davinci/board-da850-evm.c | 98 ++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index 84066e8..1fbf73f 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 INPUT_POLLDEV + default MACH_DAVINCI_DA850_EVM + config MACH_TNETV107X bool "TI TNETV107X Reference Platform" default ARCH_DAVINCI_TNETV107X diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index f89b0b7..b0763f7 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,88 @@ 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), + .rep = 0, /* disable auto-repeat */ + .poll_interval = DA850_GPIO_KEYS_POLL_MS, +}; + +static struct platform_device da850_evm_ui_keys_device = { + .name = "gpio-keys", + .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,15 +388,24 @@ 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" + " device\n"); + goto exp_setup_keys_fail; + } + ui_card_detected = 1; pr_info("DA850/OMAP-L138 EVM UI card detected\n"); da850_evm_setup_nor_nand(); - da850_evm_setup_emac_rmii(sel_a); return 0; +exp_setup_keys_fail: + gpio_free(sel_c); exp_setup_selc_fail: gpio_free(sel_b); exp_setup_selb_fail: @@ -324,6 +417,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 +435,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 Fri Nov 19 15:37:23 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 19 Nov 2010 16:37:23 -0500 Subject: [PATCH v3 4/4] da850-evm: add baseboard GPIO expander buttons, switches and LEDs In-Reply-To: References: Message-ID: <784b0a3281316ff90ecd3d52d224bbba1dd4ddb9.1290201713.git.bengardiner@nanometrics.ca> 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 --- 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 | 225 +++++++++++++++++++++++++++++++ 1 files changed, 225 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index c689e7e..32c7a02 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -431,6 +431,220 @@ 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), + .rep = 0, /* disable auto-repeat */ + .poll_interval = DA850_GPIO_KEYS_POLL_MS, +}; + +static struct platform_device da850_evm_bb_keys_device = { + .name = "gpio-keys", + .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; + int deep_sleep_en, sw_rst; + + deep_sleep_en = gpio + DA850_EVM_BB_EXP_DEEP_SLEEP_EN; + sw_rst = gpio + DA850_EVM_BB_EXP_SW_RST; + + /* Do not allow sysfs control of deep_sleep_en */ + ret = gpio_request(deep_sleep_en, + da850_evm_bb_exp[DA850_EVM_BB_EXP_DEEP_SLEEP_EN]); + if (ret) { + pr_warning("Cannot open IO expander pin %d\n", deep_sleep_en); + goto io_exp_setup_deep_sleep_en_fail; + } + /* do not drive a value on deep_sleep_en */ + gpio_direction_input(deep_sleep_en); + + /* Do not allow sysfs control of sw_rst */ + ret = gpio_request(sw_rst, + da850_evm_bb_exp[DA850_EVM_BB_EXP_SW_RST]); + if (ret) { + pr_warning("Cannot open IO expander pin %d\n", sw_rst); + goto io_exp_setup_sw_rst_fail; + } + /* do not drive a value on sw_rst */ + gpio_direction_input(sw_rst); + + /* + * 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 switches" + " device\n"); + 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 " + "device\n"); + 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: + gpio_free(sw_rst); +io_exp_setup_sw_rst_fail: + gpio_free(deep_sleep_en); +io_exp_setup_deep_sleep_en_fail: + return ret; +} + +static int da850_evm_bb_expander_teardown(struct i2c_client *client, + unsigned gpio, unsigned ngpio, void *c) +{ + int deep_sleep_en, sw_rst; + + deep_sleep_en = gpio + DA850_EVM_BB_EXP_DEEP_SLEEP_EN; + sw_rst = gpio + DA850_EVM_BB_EXP_SW_RST; + + platform_device_unregister(&da850_evm_bb_leds_device); + platform_device_unregister(&da850_evm_bb_keys_device); + gpio_free(sw_rst); + gpio_free(deep_sleep_en); + + return 0; +} + static struct pca953x_platform_data da850_evm_ui_expander_info = { .gpio_base = DAVINCI_N_GPIO, .setup = da850_evm_ui_expander_setup, @@ -438,6 +652,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), @@ -446,6 +667,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 Fri Nov 19 15:37:22 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 19 Nov 2010 16:37:22 -0500 Subject: [PATCH v3 3/4] da850-evm: extract defines for SEL{A, B, C} pins in UI expander In-Reply-To: References: Message-ID: 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 --- 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 b0763f7..c689e7e 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -361,23 +361,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; @@ -420,13 +420,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 Fri Nov 19 15:37:20 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 19 Nov 2010 16:37:20 -0500 Subject: [PATCH v3 1/4] input: gpio_keys: polling mode support In-Reply-To: References: Message-ID: <860d58a7de235648ec637c1fec8c817307af549a.1290201713.git.bengardiner@nanometrics.ca> From: Alexander Clouter This implements an optional polling mode for the gpio_keys driver, necessary for GPIOs that are not able to generate IRQs. gpio_keys_platform_data has been extended with poll_interval which specifies the polling interval in ms, this is passed onto input-polldev. This work is a rebase of the patch by Alex Clouter [1] which was based on the patch [2] originally conceived by Paul Mundt. Signed-off-by: Paul Mundt Signed-off-by: Alexander Clouter Signed-off-by: Ben Gardiner Reviewed-by: Chris Cordahi CC: Paul Mundt [1] http://article.gmane.org/gmane.linux.kernel.input/13919 [2] http://article.gmane.org/gmane.linux.kernel.input/5814 --- Changes since v2: * rebased to 083eae3e28643e0eefc5243719f8b1572cf98299 of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git 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 Changes since [1]: * rebased to 0b1c3ef1072f2b97c86351d3736d2b2d00293a11 of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git * use _cansleep variant of gpio_get_value in the polling task to avoid WARN_ON when using I2C GPIO expanders * prevent unitialized access to 'input' in gpio_keys_close() Changes since [2]: * absolute dependency on INPUT_POLLDEV removed Tested with CONFIG_INPUT_POLLDEV={n,m,y} (gpio_keys as module for 'm'). --- drivers/input/keyboard/gpio_keys.c | 120 ++++++++++++++++++++++++++++++------ include/linux/gpio_keys.h | 1 + 2 files changed, 103 insertions(+), 18 deletions(-) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 6069abe..d2f23d9 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -1,7 +1,9 @@ /* - * Driver for keys on GPIO lines capable of generating interrupts. + * Driver for keys on GPIO lines, either IRQ-driven or polled. * * Copyright 2005 Phil Blundell + * Copyright 2008 Paul Mundt + * Copyright 2010 Alexander Clouter * * 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 @@ -25,6 +27,7 @@ #include #include #include +#include struct gpio_button_data { struct gpio_keys_button *button; @@ -37,6 +40,7 @@ struct gpio_button_data { struct gpio_keys_drvdata { struct input_dev *input; + struct input_polled_dev *poll_dev; struct mutex disable_lock; unsigned int n_buttons; int (*enable)(struct device *dev); @@ -44,6 +48,30 @@ struct gpio_keys_drvdata { struct gpio_button_data data[0]; }; +#if (!defined(CONFIG_INPUT_POLLDEV) && !defined(CONFIG_INPUT_POLLDEV_MODULE)) \ + || (defined(CONFIG_INPUT_POLLDEV_MODULE) \ + && !defined(CONFIG_KEYBOARD_GPIO_MODULE)) + +static inline struct input_polled_dev *allocate_polled_device( + const struct device *dev) +{ + dev_err(dev, "device needs polling (enable INPUT_POLLDEV)\n"); + return NULL; +} + +#define free_polled_device(x) do { } while (0) +#define register_polled_device(x) (-ENXIO) +#define unregister_polled_device(x) do { } while (0) + +#else + +#define allocate_polled_device(x) input_allocate_polled_device() +#define free_polled_device(x) input_free_polled_device(x) +#define register_polled_device(x) input_register_polled_device(x) +#define unregister_polled_device(x) input_unregister_polled_device(x) + +#endif + /* * SYSFS interface for enabling/disabling keys and switches: * @@ -322,7 +350,8 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata) struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; - int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low; + int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) + ^ button->active_low; input_event(input, type, button->code, !!state); input_sync(input); @@ -343,6 +372,16 @@ static void gpio_keys_timer(unsigned long _data) schedule_work(&data->work); } +static void gpio_handle_button_event(struct gpio_keys_button *button, + struct gpio_button_data *bdata) +{ + if (bdata->timer_debounce) + mod_timer(&bdata->timer, + jiffies + msecs_to_jiffies(bdata->timer_debounce)); + else + gpio_keys_report_event(bdata); +} + static irqreturn_t gpio_keys_isr(int irq, void *dev_id) { struct gpio_button_data *bdata = dev_id; @@ -350,15 +389,24 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id) BUG_ON(irq != gpio_to_irq(button->gpio)); - if (bdata->timer_debounce) - mod_timer(&bdata->timer, - jiffies + msecs_to_jiffies(bdata->timer_debounce)); - else - schedule_work(&bdata->work); + gpio_handle_button_event(button, bdata); return IRQ_HANDLED; } +static void gpio_keys_poll(struct input_polled_dev *dev) +{ + struct gpio_keys_drvdata *ddata = dev->private; + int i; + + for (i = 0; i < ddata->n_buttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + struct gpio_keys_button *button = bdata->button; + + gpio_handle_button_event(button, bdata); + } +} + static int __devinit gpio_keys_setup_key(struct platform_device *pdev, struct gpio_button_data *bdata, struct gpio_keys_button *button) @@ -446,20 +494,28 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) struct gpio_keys_drvdata *ddata; struct device *dev = &pdev->dev; struct input_dev *input; + struct input_polled_dev *poll_dev; int i, error; int wakeup = 0; ddata = kzalloc(sizeof(struct gpio_keys_drvdata) + pdata->nbuttons * sizeof(struct gpio_button_data), GFP_KERNEL); - input = input_allocate_device(); + if (pdata->poll_interval) { + poll_dev = allocate_polled_device(dev); + input = poll_dev ? poll_dev->input : 0; + } else + input = input_allocate_device(); if (!ddata || !input) { dev_err(dev, "failed to allocate state\n"); error = -ENOMEM; goto fail1; } - ddata->input = input; + if (pdata->poll_interval) + ddata->poll_dev = poll_dev; + else + ddata->input = input; ddata->n_buttons = pdata->nbuttons; ddata->enable = pdata->enable; ddata->disable = pdata->disable; @@ -468,6 +524,12 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); input_set_drvdata(input, ddata); + if (pdata->poll_interval) { + poll_dev->private = ddata; + poll_dev->poll = gpio_keys_poll; + poll_dev->poll_interval = pdata->poll_interval; + } + input->name = pdev->name; input->phys = "gpio-keys/input0"; input->dev.parent = &pdev->dev; @@ -491,14 +553,17 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) bdata->input = input; bdata->button = button; + input_set_capability(input, type, button->code); + + if (pdata->poll_interval) + continue; + error = gpio_keys_setup_key(pdev, bdata, button); if (error) goto fail2; if (button->wakeup) wakeup = 1; - - input_set_capability(input, type, button->code); } error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); @@ -508,7 +573,10 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) goto fail2; } - error = input_register_device(input); + if (pdata->poll_interval) + error = register_polled_device(poll_dev); + else + error = input_register_device(input); if (error) { dev_err(dev, "Unable to register input device, error: %d\n", error); @@ -528,7 +596,9 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); fail2: while (--i >= 0) { - free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]); + if (!pdata->poll_interval) + free_irq(gpio_to_irq(pdata->buttons[i].gpio), + &ddata->data[i]); if (ddata->data[i].timer_debounce) del_timer_sync(&ddata->data[i].timer); cancel_work_sync(&ddata->data[i].work); @@ -537,7 +607,10 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); fail1: - input_free_device(input); + if (pdata->poll_interval) + free_polled_device(poll_dev); + else + input_free_device(input); kfree(ddata); return error; @@ -547,7 +620,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) { struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); - struct input_dev *input = ddata->input; + struct input_dev *input; + struct input_polled_dev *poll_dev; int i; sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); @@ -555,15 +629,25 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); for (i = 0; i < pdata->nbuttons; i++) { - int irq = gpio_to_irq(pdata->buttons[i].gpio); - free_irq(irq, &ddata->data[i]); + if (!pdata->poll_interval) { + int irq = gpio_to_irq(pdata->buttons[i].gpio); + free_irq(irq, &ddata->data[i]); + } if (ddata->data[i].timer_debounce) del_timer_sync(&ddata->data[i].timer); cancel_work_sync(&ddata->data[i].work); gpio_free(pdata->buttons[i].gpio); } - input_unregister_device(input); + if (pdata->poll_interval) { + poll_dev = ddata->poll_dev; + unregister_polled_device(poll_dev); + free_polled_device(poll_dev); + } else { + input = ddata->input; + input_unregister_device(input); + input_free_device(input); + } return 0; } diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index ce73a30..5fdd495 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -19,6 +19,7 @@ struct gpio_keys_platform_data { unsigned int rep:1; /* enable input subsystem auto repeat */ int (*enable)(struct device *dev); void (*disable)(struct device *dev); + unsigned int poll_interval; /* polling interval in ms */ }; #endif -- 1.7.0.4 From vm.rod25 at gmail.com Fri Nov 19 15:41:11 2010 From: vm.rod25 at gmail.com (Victor Rodriguez) Date: Fri, 19 Nov 2010 15:41:11 -0600 Subject: [PATCH v3 3/4] da850-evm: extract defines for SEL{A, B, C} pins in UI expander In-Reply-To: References: Message-ID: On Fri, Nov 19, 2010 at 3:37 PM, Ben Gardiner wrote: > 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 > > --- > > 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 > > --- I think that ther must be just one --- , This is extra Regards Victor Rodriguez > ?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 b0763f7..c689e7e 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -361,23 +361,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; > @@ -420,13 +420,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 > > _______________________________________________ > 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 Fri Nov 19 15:43:04 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 19 Nov 2010 16:43:04 -0500 Subject: [PATCH] da850-evm, trivial: use da850_evm prefix for consistency Message-ID: <1290202984-3702-1-git-send-email-bengardiner@nanometrics.ca> There was a single case of 'da850evm' prefix in the board-da850-evm.c file where the reset of the prefixes were 'da850_evm'; change it to 'da850_evm' for consistency. Signed-off-by: Ben Gardiner --- @Sekhar, you asked me to prefix all the static symbols I added to board-da850-evm.c with 'da850evm' -- but I noticed that the current convention is a prefix of 'da850_evm' so I decided to stick with the convention and replace the only outlier with this patch. --- arch/arm/mach-davinci/board-da850-evm.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index f89b0b7..fbdbd58 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -591,7 +591,7 @@ static struct tps6507x_board tps_board = { .tps6507x_ts_init_data = &tps6507x_touchscreen_data, }; -static struct i2c_board_info __initdata da850evm_tps65070_info[] = { +static struct i2c_board_info __initdata da850_evm_tps65070_info[] = { { I2C_BOARD_INFO("tps6507x", 0x48), .platform_data = &tps_board, @@ -600,8 +600,8 @@ static struct i2c_board_info __initdata da850evm_tps65070_info[] = { static int __init pmic_tps65070_init(void) { - return i2c_register_board_info(1, da850evm_tps65070_info, - ARRAY_SIZE(da850evm_tps65070_info)); + return i2c_register_board_info(1, da850_evm_tps65070_info, + ARRAY_SIZE(da850_evm_tps65070_info)); } static const short da850_evm_lcdc_pins[] = { -- 1.7.0.4 From bengardiner at nanometrics.ca Fri Nov 19 16:25:40 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 19 Nov 2010 17:25:40 -0500 Subject: [PATCH v3 3/4] da850-evm: extract defines for SEL{A, B, C} pins in UI expander In-Reply-To: References: Message-ID: Hi Victor, On Fri, Nov 19, 2010 at 4:41 PM, Victor Rodriguez wrote: [...] >> --- > > I think that ther must be just one ? --- , This is extra Sorry, that is my bad habit. I believe that the patch should still be usable as-is. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From grant.likely at secretlab.ca Sun Nov 21 15:31:38 2010 From: grant.likely at secretlab.ca (Grant Likely) Date: Sun, 21 Nov 2010 14:31:38 -0700 Subject: [PATCH v6 08/12] gpio: add ti-ssp gpio driver In-Reply-To: <4CE97AE5.3040801@bluewatersys.com> References: <1290182156-20104-1-git-send-email-cyril@ti.com> <1290182156-20104-9-git-send-email-cyril@ti.com> <4CE97AE5.3040801@bluewatersys.com> Message-ID: On Sun, Nov 21, 2010 at 1:02 PM, Ryan Mallon wrote: > On 11/20/2010 04:55 AM, Cyril Chemparathy wrote: >> 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 >> --- > >> +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); > > This looks wrong. gpio is of type ti_ssp_gpio_chip, but the ssp core > functions, ssp_read, etc, use dev_to_ssp: This sets the child device's private data pointer, not the parents. dev_to_ssp still correctly returns the parent device private data. > > static inline struct ti_ssp *dev_to_ssp(struct device *dev) > { > ? ? ? ?return dev_get_drvdata(dev->parent); > } > > If I understand correctly, the ssp core can only be used to for one > peripheral at a time. I believe the ssp has 2 channels; thus up to 2 child devices. > Currently the code will allow you to install > several peripherals at once, which will lead to odd behaviour at > runtime. Maybe a better approach would be to have something like this in > drivers/mfd/ti-ssp.c: Only the ssp core itself is allowed to register the peripheral devices, and so using the parent pointer is perfectly valid. I think Cyril's approach is fine. g. From setsu at emi.yamaha.co.jp Sun Nov 21 20:00:56 2010 From: setsu at emi.yamaha.co.jp (Suzuki Takashi) Date: Mon, 22 Nov 2010 11:00:56 +0900 Subject: I2C driver slave functionality status Message-ID: Hello, Is there any progress for I2C driver slave functionality? The only topic I could find out is following one: http://linux.omap.com/pipermail/davinci-linux-open-source/2008-February/005500.html But it seems there are no feedback from that to mainline kernel. Does anyone know the I2C driver slave functionality status? Thanks, -- T.Suzuki From csuzhsh at gmail.com Mon Nov 22 01:04:52 2010 From: csuzhsh at gmail.com (zhou sheng) Date: Mon, 22 Nov 2010 15:04:52 +0800 Subject: Where i set the IMX035 is wrong? Message-ID: Hi all, In the last month, we have implemented the AF(Auto Focus) on the APPRO's DM365-IMX035, but now i want to replace the AE and AWB of appro's. I set the registers of IMX035 like appro's,but i set the IPIPE GAIN, once i set the IPIPE GAIN, the video twinkles ,but when doesn't set ,the video is dark ,who can tell me why? the IMX035 setting is : addr[38] = 0 addr[4] = 19 addr[1] = 0 addr[2] = 3 addr[3] = 2 addr[83] = 107 addr[124] = 58 addr[125] = 4 addr[126] = 128 addr[127] = 103 addr[28] = 0 addr[29] = 16 addr[30] = 0 addr[6] = 0 addr[7] = 0 addr[18] = 0 addr[19] = 0 addr[8] = 0 addr[9] = 0 addr[32] = 0 addr[31] = 32 addr[39] = 0 addr[0] = 12 Thank you! RGS! zhou sheng ------------------------------------ csuzhsh at gmail.com 15012490142 zhou sheng ------------------------------------- -------------- next part -------------- An HTML attachment was scrubbed... URL: From broonie at opensource.wolfsonmicro.com Mon Nov 22 05:20:38 2010 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Mon, 22 Nov 2010 11:20:38 +0000 Subject: [PATCH v6 08/12] gpio: add ti-ssp gpio driver In-Reply-To: References: <1290182156-20104-1-git-send-email-cyril@ti.com> <1290182156-20104-9-git-send-email-cyril@ti.com> <4CE97AE5.3040801@bluewatersys.com> Message-ID: <20101122112038.GD16504@rakim.wolfsonmicro.main> On Sun, Nov 21, 2010 at 02:31:38PM -0700, Grant Likely wrote: > On Sun, Nov 21, 2010 at 1:02 PM, Ryan Mallon wrote: > > Currently the code will allow you to install > > several peripherals at once, which will lead to odd behaviour at > > runtime. Maybe a better approach would be to have something like this in > > drivers/mfd/ti-ssp.c: > Only the ssp core itself is allowed to register the peripheral > devices, and so using the parent pointer is perfectly valid. I think > Cyril's approach is fine. This is very much a standard idiom for MFD children in Linux - if the children are entirely reliant on working through a parent-specific API anyway there's no issue with making the parent device be part of that API. From nsekhar at ti.com Mon Nov 22 05:49:29 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 22 Nov 2010 17:19:29 +0530 Subject: [PATCH v2 2/4] da850-evm: add UI Expander pushbuttons In-Reply-To: References: <95f48a32a0256ecdb7148aa08d16f64928a7e5d8.1289935504.git.bengardiner@nanometrics.ca> Message-ID: Hi Ben, On Fri, Nov 19, 2010 at 21:08:10, Ben Gardiner wrote: > On Fri, Nov 19, 2010 at 4:58 AM, Nori, Sekhar wrote: > > [...] > > On Wed, Nov 17, 2010 at 01:09:35, Ben Gardiner wrote: > >> [...] > >> Changes since v1: > >> * set INPUT_POLLDEV default for DA850_EVM machine, but don't select it > >> unconditionally > > > > I didn't see the v1 posting (wonder why), but why is this > > required? Why cant we depend on this being selected from > > Device Drivers->Input device support in menuconfig? > > I'm sorry about that. I checked gmane and the v1 never made it to the > davinci-linux list. I think it was because of my (mis)use of git > send-email. Version 2 made it to the list so I've now found the magic > incantation. > > The reason we should at least select INPUT_POLLDEV as a default option > is: when it is not set the gpio-keys instances registered by the > da850-evm board setup routine will fail probe() of the gpio-keys > driver since it will not have polled-input support enabled. > > By setting INPUT_POLLDEV default for the da850-evm users will get > functioning pushbuttons and switches with the default config but they > will be able to disable INPUT_POLLDEV or gpio-keys drivers in their > defconfig at their convenience. I guess we could also just modify the defconfig to switch on INPUT_POLLDEV? Since only gpio-keys functionality is affected by not enabling the correct options in kernel, it should be OK. Thanks, Sekhar From nsekhar at ti.com Mon Nov 22 06:00:09 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 22 Nov 2010 17:30:09 +0530 Subject: [PATCH v2 4/4] da850-evm: add baseboard UI expander buttons, switches and LEDs In-Reply-To: References: Message-ID: Hi Ben, On Fri, Nov 19, 2010 at 21:10:50, Ben Gardiner wrote: > >> [...] > > > > How does gpio_request prevent sysfs control? > > To obtain access to a gpio through the sysfs interface the user must > first write the gpio number to 'export'. When the gpio has been > gpio_request()'d the result of writing to 'export' is nothing; whereas > writing to export would normally result in the appearance of a named > gpio line alongside 'export'. > > I hope the following commands and their output illustrate my point: > > $ cd /sys/class/gpio/ > $ ls > export gpiochip128 gpiochip160 gpiochip64 unexport > gpiochip0 gpiochip144 gpiochip32 gpiochip96 > $ echo 160 > export > $ ls > export gpiochip128 gpiochip160 gpiochip64 unexport > gpiochip0 gpiochip144 gpiochip32 gpiochip96 > $ echo 163 > export > $ ls > export gpiochip128 gpiochip160 gpiochip64 tp_22 > gpiochip0 gpiochip144 gpiochip32 gpiochip96 unexport Thanks for the explanation. I should have probably asked earlier, why do we need to prevent sysfs access of deep sleep enable and sw reset pins? We don't seem to be using them in the kernel either. Thanks, Sekhar From nsekhar at ti.com Mon Nov 22 06:05:47 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 22 Nov 2010 17:35:47 +0530 Subject: [PATCH] da850-evm, trivial: use da850_evm prefix for consistency In-Reply-To: <1290202984-3702-1-git-send-email-bengardiner@nanometrics.ca> References: <1290202984-3702-1-git-send-email-bengardiner@nanometrics.ca> Message-ID: On Sat, Nov 20, 2010 at 03:13:04, Ben Gardiner wrote: > There was a single case of 'da850evm' prefix in the board-da850-evm.c file > where the reset of the prefixes were 'da850_evm'; change it to 'da850_evm' for > consistency. > > Signed-off-by: Ben Gardiner > > --- > > @Sekhar, you asked me to prefix all the static symbols I added to > board-da850-evm.c with 'da850evm' -- but I noticed that the current convention > is a prefix of 'da850_evm' so I decided to stick with the convention and > replace the only outlier with this patch. Thanks. I personally prefer da850evm, but consistency is more important so that a search-replace is possible later on. So I am okay with this too. Regards, Sekhar From nsekhar at ti.com Mon Nov 22 06:08:51 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 22 Nov 2010 17:38:51 +0530 Subject: I2C driver slave functionality status In-Reply-To: References: Message-ID: Hello, On Mon, Nov 22, 2010 at 07:30:56, Suzuki Takashi wrote: > Hello, > > Is there any progress for I2C driver slave functionality? > > The only topic I could find out is following one: > http://linux.omap.com/pipermail/davinci-linux-open-source/2008-February/005500.html > > But it seems there are no feedback from that to mainline kernel. > > Does anyone know the I2C driver slave functionality status? As far as I know, the current I2C framework in Linux allows master mode support only. It may be worthwhile checking on the Linux/I2C list linux-i2c at vger.kernel.org to see if anyone is working on slave mode framework support. Thanks, Sekhar From bengardiner at nanometrics.ca Mon Nov 22 07:50:48 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Mon, 22 Nov 2010 08:50:48 -0500 Subject: [PATCH v2 2/4] da850-evm: add UI Expander pushbuttons In-Reply-To: References: <95f48a32a0256ecdb7148aa08d16f64928a7e5d8.1289935504.git.bengardiner@nanometrics.ca> Message-ID: On Mon, Nov 22, 2010 at 6:49 AM, Nori, Sekhar wrote: > On Fri, Nov 19, 2010 at 21:08:10, Ben Gardiner wrote: >> [...] >> By setting INPUT_POLLDEV default for the da850-evm users will get >> functioning pushbuttons and switches with the default config but they >> will be able to disable INPUT_POLLDEV or gpio-keys drivers in their >> defconfig at their convenience. > > I guess we could also just modify the defconfig to switch on > INPUT_POLLDEV? Since only gpio-keys functionality is affected > by not enabling the correct options in kernel, it should be OK. Yes -- only gpio-keys is affected but enabling the option does introduce an additional .o file: drivers/input/Makefile:obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o I agree that in its current state a user couls inadvertently disable the gpio-keys support on da850-evm simply by disabling INPUT_POLLDEV -- which is less than Ideal. How about I replace the current changes to arch/arm/mach-davinci/Kconfig with: config KEYBOARD_GPIO default MACH_DAVINCI_DA850_EVM select INPUT_POLLDEV So 1) gpio-keys functionality is default for the da850evm and 2) whenever gpio-keys is enabled so is INPUT_POLLDEV. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From bengardiner at nanometrics.ca Mon Nov 22 08:15:46 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Mon, 22 Nov 2010 09:15:46 -0500 Subject: [PATCH v2 4/4] da850-evm: add baseboard UI expander buttons, switches and LEDs In-Reply-To: References: Message-ID: Hi Sekhar, On Mon, Nov 22, 2010 at 7:00 AM, Nori, Sekhar wrote: > Thanks for the explanation. I should have probably asked > earlier, why do we need to prevent sysfs access of > deep sleep enable and sw reset pins? We don't seem to be > using them in the kernel either. You're welcome. I was assuming that those pins were not exported as gpio pins on purpose; I was taking the prudent approach to prevent haphazard toggling of sw_rst and deep_sleep_en from userspace. sw_rst because it could initiate a reset of the cpu when toggled and deep_sleep_en because it can override the behaviour of davinci_pm_enter(). I'm not sure how they would be used by existing kernel classes either. The sw_rst pin could be used for reset but since it is on the other end of an i2c bus and there is an existing implementation of reset using the on chip watchdog I don't think it would be benficial to switch. Deep_sleep_en could override the behaviour in davinci_pm_enter() -- _maybe_ (I don't really know) it could be used as a hardware-assisted suspend-blocker? But I totally guessing here. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From lamiaposta71 at gmail.com Mon Nov 22 08:44:19 2010 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Mon, 22 Nov 2010 15:44:19 +0100 Subject: tvp5150 extension to tvp5151 In-Reply-To: <4CEA54C9.4030300@infradead.org> References: <4CEA54C9.4030300@infradead.org> Message-ID: On Mon, Nov 22, 2010 at 12:32 PM, Mauro Carvalho Chehab wrote: > Hi Raffaele, > > Em 19-11-2010 16:26, Raffaele Recalcati escreveu: >> I need to support fully tvp5151. >> So I'm trying to understand your driver, that is ready for VBI and not >> for video acquisition. >> I also take sometimes a look at tvp514x.c, for instance trying to add >> VIDIOC_ENUM_FMT and other ioctls. >> I think we can move from tvp5150.c to tvp515x.c, maybe... >> I don't think is good to have tvp51xx.c because tvp514x.c family is >> more complex (more inputs...). >> By now I'm using tvp5150.c with some modifications and video acquisition works. >> I need to complete the support in order to have gstreamer fully running. >> I'm working on 2.6.32, but I have planned to port it to mainline. > > Renaming it to tvp515x.c and adding support for tvp5051 seems a good way. > With tvp5150, video acquisition works fine with several devices with em28xx, > although we may need to add more video formats at the media bus, depending > on what you're doing. I didn't test VBI support on it, because the devices > I have are based on em28xx, and I didn't find any way to capture the VBI > decoded packages and sent to userspace with that design. So, the basic stuff > is there, but maybe some adjusts may be needed for VBI. > >> Do you have any suggestion for my work? > > The better is to submit the patches you have, for us to apply upstream. Thx for your reply.. I have added many v4l2 support similar to tvp514x. I need to test them, hoping they work. When all we'll be ok I'll send patches. So I go on renaming all strings from "tvp5150" to "tvp515x". Thx, Raffaele From jeong7909 at gmail.com Mon Nov 22 20:47:57 2010 From: jeong7909 at gmail.com (=?EUC-KR?B?waS/tbG4?=) Date: Tue, 23 Nov 2010 11:47:57 +0900 Subject: Please I want to remove my e-amil in mailling list. Message-ID: Please I want to remove my e-amil in mailling list. I don`t want recieve e-mail from Davinci-linux-open-source. Please. -------------- next part -------------- An HTML attachment was scrubbed... URL: From pchevalier at imajing.fr Tue Nov 23 05:13:27 2010 From: pchevalier at imajing.fr (Pierre) Date: Tue, 23 Nov 2010 12:13:27 +0100 Subject: [PATCH 00/49] spi: davinci: re-write existing driver, DM355 DMA problem Message-ID: <4CEBA1D7.6080001@imajing.fr> Hi, I have tested the new driver using DMA mode on our custom DM355 board. I replaced the linux 2.6.32 spi driver by this driver and it seemed to work except for on point. The dm355 spi polling mode was correctly working but the DMA mode was causing some communication problems. It appeared that when dm355 was reading on the spi, it was actually resending received data to our slave device on the spi bus, i didn't fully checked what i am saying i am just supposing that it's what occured. Anyway the behaviour was different in DMA and polling mode. I didn't dig deep into the driver i just solved the problem (quick and dirty) by setting tx buffer to zero in the spidev driver. The problem can be caused by using SPIDAT as dma buffer when the tx buffer is empty. (If it helps i have tested before the spi patch V5 from Brian Niebuhr and the problem was already there) So is this a bug or a feature ? I don't have time right know to work on that problem but i can test solutions if anyone have. Regards. Pierre From nsekhar at ti.com Tue Nov 23 06:32:58 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 23 Nov 2010 18:02:58 +0530 Subject: [PATCH 00/49] spi: davinci: re-write existing driver, DM355 DMA problem In-Reply-To: <4CEBA1D7.6080001@imajing.fr> References: <4CEBA1D7.6080001@imajing.fr> Message-ID: Hi Pierre, On Tue, Nov 23, 2010 at 16:43:27, Pierre wrote: > Hi, > > I have tested the new driver using DMA mode on our custom DM355 board. I > replaced the linux 2.6.32 spi driver by this driver and it seemed to > work except for on point. The dm355 spi polling mode was correctly > working but the DMA mode was causing some communication problems. It > appeared that when dm355 was reading on the spi, it was actually > resending received data to our slave device on the spi bus, i didn't Hmm, didn't really see this before. > fully checked what i am saying i am just supposing that it's what > occured. Anyway the behaviour was different in DMA and polling mode. I While working on the patches I noticed that DM355 has an errata regarding behavior of CSHOLD. In the patches, I take care of it by having DM355 define cshold_bug to true. Can you please make sure you have taken care of this as well while porting to 2.6.32? http://linux.davincidsp.com/pipermail/davinci-linux-open-source/2010-November/021045.html > didn't dig deep into the driver i just solved the problem (quick and > dirty) by setting tx buffer to zero in the spidev driver. > The problem can be caused by using SPIDAT as dma buffer when the tx > buffer is empty. > (If it helps i have tested before the spi patch V5 from Brian Niebuhr > and the problem was already there) How about with Brian's patch applied? Thanks, Sekhar From nsekhar at ti.com Tue Nov 23 06:38:22 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 23 Nov 2010 18:08:22 +0530 Subject: [PATCH v2 2/4] da850-evm: add UI Expander pushbuttons In-Reply-To: References: <95f48a32a0256ecdb7148aa08d16f64928a7e5d8.1289935504.git.bengardiner@nanometrics.ca> Message-ID: Hi Ben, On Mon, Nov 22, 2010 at 19:20:48, Ben Gardiner wrote: > On Mon, Nov 22, 2010 at 6:49 AM, Nori, Sekhar wrote: > > On Fri, Nov 19, 2010 at 21:08:10, Ben Gardiner wrote: > >> [...] > >> By setting INPUT_POLLDEV default for the da850-evm users will get > >> functioning pushbuttons and switches with the default config but they > >> will be able to disable INPUT_POLLDEV or gpio-keys drivers in their > >> defconfig at their convenience. > > > > I guess we could also just modify the defconfig to switch on > > INPUT_POLLDEV? Since only gpio-keys functionality is affected > > by not enabling the correct options in kernel, it should be OK. > > Yes -- only gpio-keys is affected but enabling the option does > introduce an additional .o file: > > drivers/input/Makefile:obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o > > I agree that in its current state a user couls inadvertently disable > the gpio-keys support on da850-evm simply by disabling INPUT_POLLDEV > -- which is less than Ideal. How about I replace the current changes > to arch/arm/mach-davinci/Kconfig with: > > config KEYBOARD_GPIO > default MACH_DAVINCI_DA850_EVM > select INPUT_POLLDEV > > So 1) gpio-keys functionality is default for the da850evm and 2) > whenever gpio-keys is enabled so is INPUT_POLLDEV. This looks better than what was posted earlier, but I am not sure if platforms should influence driver configuration this way. I guess I am just afraid that this makes a precedent for many driver config symbols to get replicated in the platform Kconfig files. Lets see if others have an opinion on this. Thanks, Sekhar From nsekhar at ti.com Tue Nov 23 06:42:38 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 23 Nov 2010 18:12:38 +0530 Subject: [PATCH v2 4/4] da850-evm: add baseboard UI expander buttons, switches and LEDs In-Reply-To: References: Message-ID: Hi Ben, On Mon, Nov 22, 2010 at 19:45:46, Ben Gardiner wrote: > Hi Sekhar, > > On Mon, Nov 22, 2010 at 7:00 AM, Nori, Sekhar wrote: > > Thanks for the explanation. I should have probably asked > > earlier, why do we need to prevent sysfs access of > > deep sleep enable and sw reset pins? We don't seem to be > > using them in the kernel either. > > You're welcome. > > I was assuming that those pins were not exported as gpio pins on > purpose; I was taking the prudent approach to prevent haphazard > toggling of sw_rst and deep_sleep_en from userspace. sw_rst because it > could initiate a reset of the cpu when toggled and deep_sleep_en > because it can override the behaviour of davinci_pm_enter(). > > I'm not sure how they would be used by existing kernel classes either. > The sw_rst pin could be used for reset but since it is on the other > end of an i2c bus and there is an existing implementation of reset > using the on chip watchdog I don't think it would be benficial to > switch. Deep_sleep_en could override the behaviour in > davinci_pm_enter() -- _maybe_ (I don't really know) it could be used > as a hardware-assisted suspend-blocker? But I totally guessing here. My preference would be to leave these pins as is (don't call a gpio_request() on them) till someone comes up with a use case for them. From what you described, sysfs access cannot happen "accidently" so someone accessing these pins from sysfs surely knows what he is doing. Thanks, Sekhar From bengardiner at nanometrics.ca Tue Nov 23 07:29:17 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 23 Nov 2010 08:29:17 -0500 Subject: [PATCH v2 2/4] da850-evm: add UI Expander pushbuttons In-Reply-To: References: <95f48a32a0256ecdb7148aa08d16f64928a7e5d8.1289935504.git.bengardiner@nanometrics.ca> Message-ID: Hi Sekhar, On Tue, Nov 23, 2010 at 7:38 AM, Nori, Sekhar wrote: > On Mon, Nov 22, 2010 at 19:20:48, Ben Gardiner wrote: >> [...] >> config KEYBOARD_GPIO >> ? ? ? ? default MACH_DAVINCI_DA850_EVM >> ? ? ? ? select INPUT_POLLDEV >> >> So 1) gpio-keys functionality is default for the da850evm and 2) >> whenever gpio-keys is enabled so is INPUT_POLLDEV. > > This looks better than what was posted earlier, but I am not > sure if platforms should influence driver configuration this > way. > > I guess I am just afraid that this makes a precedent for > many driver config symbols to get replicated in the platform > Kconfig files. Ok . I understand your concerns. > Lets see if others have an opinion on this. Yes, good idea. I would welcome more opinions on this and any other aspects of the patch series. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From bengardiner at nanometrics.ca Tue Nov 23 07:32:34 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 23 Nov 2010 08:32:34 -0500 Subject: [PATCH v2 4/4] da850-evm: add baseboard UI expander buttons, switches and LEDs In-Reply-To: References: Message-ID: Hi Sekhar, On Tue, Nov 23, 2010 at 7:42 AM, Nori, Sekhar wrote: > On Mon, Nov 22, 2010 at 19:45:46, Ben Gardiner wrote: >> [...] >> I was assuming that those pins were not exported as gpio pins on >> purpose; I was taking the prudent approach to prevent haphazard >> toggling of sw_rst and deep_sleep_en from userspace. sw_rst because it >> could initiate a reset of the cpu when toggled and deep_sleep_en >> because it can override the behaviour of davinci_pm_enter(). >> >> I'm not sure how they would be used by existing kernel classes either. >> The sw_rst pin could be used for reset but since it is on the other >> end of an i2c bus and there is an existing implementation of reset >> using the on chip watchdog I don't think it would be benficial to >> switch. Deep_sleep_en could override the behaviour in >> davinci_pm_enter() -- _maybe_ (I don't really know) it could be used >> as a hardware-assisted suspend-blocker? But I totally guessing here. > > My preference would be to leave these pins as is > (don't call a gpio_request() on them) till someone > comes up with a use case for them. From what you > described, sysfs access cannot happen "accidently" > so someone accessing these pins from sysfs surely > knows what he is doing. No problem. I will re-spin the patch shortly with the deep_sleep_en and sw_rst pins free for use by client code. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From pchevalier at imajing.fr Tue Nov 23 08:44:17 2010 From: pchevalier at imajing.fr (Pierre) Date: Tue, 23 Nov 2010 15:44:17 +0100 Subject: [PATCH 00/49] spi: davinci: re-write existing driver, DM355 DMA problem In-Reply-To: References: <4CEBA1D7.6080001@imajing.fr> Message-ID: <4CEBD341.8060408@imajing.fr> I took the tree version of the davinci_spi.c file after every patches have been applied ( the 49 spi patches ). I tested with the csbug on and off before and if i remember well, it was not working. I'll try again as soon as i have time. The weird thing is that the problem doesn't occur at each transfer. The details of what i noticed is: -Chipselect to the client is a GPIO, chip_sel array is a 1 byte array containing the gpio pin. -The DM355 application uses spidev char driver to read and write data to spidev0.0 -When dm355 was reading a 16 bytes buffer, the client sent the data correctly but afterwards it received a byte : the 15th byte of the sent buffer. -When I added to spidev_sync_read a tx zeroed buffer everything worked perfectly. (-The problem was there even if cshold_bug was true, but i'll check that again) -The spi was correctly working in polling mode. Pierre Le 11/23/2010 01:32 PM, Nori, Sekhar a ?crit : > Hi Pierre, > > On Tue, Nov 23, 2010 at 16:43:27, Pierre wrote: > >> Hi, >> >> I have tested the new driver using DMA mode on our custom DM355 board. I >> replaced the linux 2.6.32 spi driver by this driver and it seemed to >> work except for on point. The dm355 spi polling mode was correctly >> working but the DMA mode was causing some communication problems. It >> appeared that when dm355 was reading on the spi, it was actually >> resending received data to our slave device on the spi bus, i didn't >> > Hmm, didn't really see this before. > > >> fully checked what i am saying i am just supposing that it's what >> occured. Anyway the behaviour was different in DMA and polling mode. I >> > While working on the patches I noticed that DM355 has an errata > regarding behavior of CSHOLD. In the patches, I take care of it > by having DM355 define cshold_bug to true. Can you please make sure > you have taken care of this as well while porting to 2.6.32? > > http://linux.davincidsp.com/pipermail/davinci-linux-open-source/2010-November/021045.html > > >> didn't dig deep into the driver i just solved the problem (quick and >> dirty) by setting tx buffer to zero in the spidev driver. >> The problem can be caused by using SPIDAT as dma buffer when the tx >> buffer is empty. >> (If it helps i have tested before the spi patch V5 from Brian Niebuhr >> and the problem was already there) >> > How about with Brian's patch applied? > > Thanks, > Sekhar > > > > From BNiebuhr at efjohnson.com Tue Nov 23 08:47:44 2010 From: BNiebuhr at efjohnson.com (Brian Niebuhr) Date: Tue, 23 Nov 2010 08:47:44 -0600 Subject: [PATCH 00/49] spi: davinci: re-write existing driver, DM355 DMAproblem In-Reply-To: <4CEBA1D7.6080001@imajing.fr> References: <4CEBA1D7.6080001@imajing.fr> Message-ID: > -----Original Message----- > From: davinci-linux-open-source-bounces at linux.davincidsp.com > [mailto:davinci-linux-open-source-bounces at linux.davincidsp.com] On > Behalf Of Pierre > Sent: Tuesday, November 23, 2010 5:13 AM > To: davinci-linux-open-source at linux.davincidsp.com > Subject: Re: [PATCH 00/49] spi: davinci: re-write existing driver, > DM355 DMAproblem > > Hi, > > I have tested the new driver using DMA mode on our custom DM355 board. > I > replaced the linux 2.6.32 spi driver by this driver and it seemed to > work except for on point. The dm355 spi polling mode was correctly > working but the DMA mode was causing some communication problems. It > appeared that when dm355 was reading on the spi, it was actually > resending received data to our slave device on the spi bus, i didn't > fully checked what i am saying i am just supposing that it's what > occured. If I understand you correctly, what you are saying is that when you are trying to read data from your SPI device, the data on the DM355 output signal (MOSI) is not what you expected, and is causing issues. If that is the case, I can say that I made no effort to make sure that the inactive portion of a half-duplex SPI transfer had any particular data pattern. In other words, if you have been getting a certain pattern of outbound data it is purely coincidence, and the different implementations of polled and DMA mode expose the fact that you had been getting lucky. I am not a SPI expert by any means, but it has been my approach that SPI is a full-duplex protocol and you need to specify outbound data if you want certain outbound data sent. As it turns out, most SPI devices have a protocol for determining when they are supposed to be sending or receiving, and if the device is sending it usually ignores whatever it is receiving. You mention in your later email that using a zeroed Tx buffer makes everything work OK. I think that's actually the most correct solution. The driver could be modified to ensure that it transmits zeros on a half-duplex read, and maybe it should, but I think the best solution is for the application to specify what it wants to be sent and not rely on non-portable behavior from a driver. If I'm misunderstanding the problem, then just ignore all of the above :-) > Anyway the behaviour was different in DMA and polling mode. I > didn't dig deep into the driver i just solved the problem (quick and > dirty) by setting tx buffer to zero in the spidev driver. > The problem can be caused by using SPIDAT as dma buffer when the tx > buffer is empty. > (If it helps i have tested before the spi patch V5 from Brian Niebuhr > and the problem was already there) > > So is this a bug or a feature ? > > I don't have time right know to work on that problem but i can test > solutions if anyone have. > > Regards. > Pierre > _______________________________________________ > 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 khilman at deeprootsystems.com Tue Nov 23 09:31:21 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Tue, 23 Nov 2010 07:31:21 -0800 Subject: [PATCH] da850-evm, trivial: use da850_evm prefix for consistency In-Reply-To: (Sekhar Nori's message of "Mon, 22 Nov 2010 17:35:47 +0530") References: <1290202984-3702-1-git-send-email-bengardiner@nanometrics.ca> Message-ID: <87bp5ghx9y.fsf@deeprootsystems.com> "Nori, Sekhar" writes: > On Sat, Nov 20, 2010 at 03:13:04, Ben Gardiner wrote: >> There was a single case of 'da850evm' prefix in the board-da850-evm.c file >> where the reset of the prefixes were 'da850_evm'; change it to 'da850_evm' for >> consistency. >> >> Signed-off-by: Ben Gardiner >> >> --- >> >> @Sekhar, you asked me to prefix all the static symbols I added to >> board-da850-evm.c with 'da850evm' -- but I noticed that the current convention >> is a prefix of 'da850_evm' so I decided to stick with the convention and >> replace the only outlier with this patch. > > Thanks. I personally prefer da850evm, but consistency > is more important so that a search-replace is possible > later on. So I am okay with this too. I'll take that as an Ack. Applying, queuing for 2.6.38. Kevin From pchevalier at imajing.fr Tue Nov 23 10:41:01 2010 From: pchevalier at imajing.fr (Pierre) Date: Tue, 23 Nov 2010 17:41:01 +0100 Subject: [PATCH 00/49] spi: davinci: re-write existing driver, DM355 DMAproblem In-Reply-To: References: <4CEBA1D7.6080001@imajing.fr> Message-ID: <4CEBEE9D.8000704@imajing.fr> You are correctly understanding my problem. Ok, So spidev should specify the out buffer in case of a read or the application should specify the out buffer. By the way, thanks for this new driver that is fully satisfying. Regards Le 11/23/2010 03:47 PM, Brian Niebuhr a ?crit : >> -----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 Pierre >> Sent: Tuesday, November 23, 2010 5:13 AM >> To: davinci-linux-open-source at linux.davincidsp.com >> Subject: Re: [PATCH 00/49] spi: davinci: re-write existing driver, >> DM355 DMAproblem >> >> Hi, >> >> I have tested the new driver using DMA mode on our custom DM355 board. >> I >> replaced the linux 2.6.32 spi driver by this driver and it seemed to >> work except for on point. The dm355 spi polling mode was correctly >> working but the DMA mode was causing some communication problems. It >> appeared that when dm355 was reading on the spi, it was actually >> resending received data to our slave device on the spi bus, i didn't >> fully checked what i am saying i am just supposing that it's what >> occured. >> > If I understand you correctly, what you are saying is that when you are > trying to read data from your SPI device, the data on the DM355 output > signal (MOSI) is not what you expected, and is causing issues. If that > is the case, I can say that I made no effort to make sure that the > inactive portion of a half-duplex SPI transfer had any particular data > pattern. In other words, if you have been getting a certain pattern of > outbound data it is purely coincidence, and the different > implementations of polled and DMA mode expose the fact that you had been > getting lucky. > > I am not a SPI expert by any means, but it has been my approach that SPI > is a full-duplex protocol and you need to specify outbound data if you > want certain outbound data sent. As it turns out, most SPI devices have > a protocol for determining when they are supposed to be sending or > receiving, and if the device is sending it usually ignores whatever it > is receiving. > > You mention in your later email that using a zeroed Tx buffer makes > everything work OK. I think that's actually the most correct solution. > The driver could be modified to ensure that it transmits zeros on a > half-duplex read, and maybe it should, but I think the best solution is > for the application to specify what it wants to be sent and not rely on > non-portable behavior from a driver. > > If I'm misunderstanding the problem, then just ignore all of the above > :-) > > >> Anyway the behaviour was different in DMA and polling mode. I >> didn't dig deep into the driver i just solved the problem (quick and >> dirty) by setting tx buffer to zero in the spidev driver. >> The problem can be caused by using SPIDAT as dma buffer when the tx >> buffer is empty. >> (If it helps i have tested before the spi patch V5 from Brian Niebuhr >> and the problem was already there) >> >> So is this a bug or a feature ? >> >> I don't have time right know to work on that problem but i can test >> solutions if anyone have. >> >> Regards. >> Pierre >> _______________________________________________ >> 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 khilman at deeprootsystems.com Tue Nov 23 09:48:21 2010 From: khilman at deeprootsystems.com (Kevin Hilman) Date: Tue, 23 Nov 2010 07:48:21 -0800 Subject: [PATCH v2 2/4] da850-evm: add UI Expander pushbuttons In-Reply-To: (Sekhar Nori's message of "Tue, 23 Nov 2010 18:08:22 +0530") References: <95f48a32a0256ecdb7148aa08d16f64928a7e5d8.1289935504.git.bengardiner@nanometrics.ca> Message-ID: <8739qshwhm.fsf@deeprootsystems.com> "Nori, Sekhar" writes: > Hi Ben, > > On Mon, Nov 22, 2010 at 19:20:48, Ben Gardiner wrote: >> On Mon, Nov 22, 2010 at 6:49 AM, Nori, Sekhar wrote: >> > On Fri, Nov 19, 2010 at 21:08:10, Ben Gardiner wrote: >> >> [...] >> >> By setting INPUT_POLLDEV default for the da850-evm users will get >> >> functioning pushbuttons and switches with the default config but they >> >> will be able to disable INPUT_POLLDEV or gpio-keys drivers in their >> >> defconfig at their convenience. >> > >> > I guess we could also just modify the defconfig to switch on >> > INPUT_POLLDEV? Since only gpio-keys functionality is affected >> > by not enabling the correct options in kernel, it should be OK. >> >> Yes -- only gpio-keys is affected but enabling the option does >> introduce an additional .o file: >> >> drivers/input/Makefile:obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o >> >> I agree that in its current state a user couls inadvertently disable >> the gpio-keys support on da850-evm simply by disabling INPUT_POLLDEV >> -- which is less than Ideal. How about I replace the current changes >> to arch/arm/mach-davinci/Kconfig with: >> >> config KEYBOARD_GPIO >> default MACH_DAVINCI_DA850_EVM >> select INPUT_POLLDEV >> >> So 1) gpio-keys functionality is default for the da850evm and 2) >> whenever gpio-keys is enabled so is INPUT_POLLDEV. > > This looks better than what was posted earlier, but I am not > sure if platforms should influence driver configuration this > way. Agreed. In general, we should not have machine/platform specific conditionals in generic Kconfigs. Generally, this should be handled in machine/platform specific Kconfigs. Kevin > I guess I am just afraid that this makes a precedent for > many driver config symbols to get replicated in the platform > Kconfig files. > > Lets see if others have an opinion on this. > > Thanks, > Sekhar From bengardiner at nanometrics.ca Tue Nov 23 11:58:46 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 23 Nov 2010 12:58:46 -0500 Subject: [PATCH v2 2/4] da850-evm: add UI Expander pushbuttons In-Reply-To: <8739qshwhm.fsf@deeprootsystems.com> References: <95f48a32a0256ecdb7148aa08d16f64928a7e5d8.1289935504.git.bengardiner@nanometrics.ca> <8739qshwhm.fsf@deeprootsystems.com> Message-ID: Hi Kevin, Thank you for weighing in. On Tue, Nov 23, 2010 at 10:48 AM, Kevin Hilman wrote: > "Nori, Sekhar" writes: >>> [...] >>> -- which is less than Ideal. How about I replace the current changes >>> to arch/arm/mach-davinci/Kconfig with: >>> >>> config KEYBOARD_GPIO >>> ? ? ? ? default MACH_DAVINCI_DA850_EVM >>> ? ? ? ? select INPUT_POLLDEV >>> >>> So 1) gpio-keys functionality is default for the da850evm and 2) >>> whenever gpio-keys is enabled so is INPUT_POLLDEV. >> >> This looks better than what was posted earlier, but I am not >> sure if platforms should influence driver configuration this >> way. > > Agreed. ?In general, we should not have machine/platform specific > conditionals in generic Kconfigs. ? Generally, this should be handled in > machine/platform specific Kconfigs. My understanding is that Sekhar was expressing concern over putting generic config conditionals into machine/platform specific Kconfigs. It sounds like you are OK with generic config conditionals in machine/platform specific Kconfigs ala recent pca953x module build changes [1]. To be clear: I am proposing the following additions to arch/arm/mach-davinci/Kconfig: (slightly different than above). config KEYBOARD_GPIO ? ? ? ? default MACH_DAVINCI_DA850_EVM ? ? ? ? select INPUT_POLLDEV if MACH_DAVINCI_DA850_EVM I know it is always better to show the code: I will extract these contentious Kconfig changes into their own patch in the series when I post a new version. Best Regards, Ben Gardiner [1] http://article.gmane.org/gmane.linux.davinci/20918 --- Nanometrics Inc. http://www.nanometrics.ca From bengardiner at nanometrics.ca Tue Nov 23 13:40:55 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 23 Nov 2010 14:40:55 -0500 Subject: [PATCH v4 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 Alexander Clouter. 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. [1] http://article.gmane.org/gmane.linux.kernel/1052551 [2] http://article.gmane.org/gmane.linux.kernel.commits.head/260919 [3] http://www.mail-archive.com/devicetree-discuss at lists.ozlabs.org/msg01974.html [4] http://article.gmane.org/gmane.linux.kernel.cross-arch/7786 Alexander Clouter (1): input: gpio_keys: polling mode support 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 and INPUT_POLLDEV Kconfig conditionals arch/arm/mach-davinci/Kconfig | 4 + arch/arm/mach-davinci/board-da850-evm.c | 312 +++++++++++++++++++++++++++++-- drivers/input/keyboard/gpio_keys.c | 120 ++++++++++-- include/linux/gpio_keys.h | 1 + 4 files changed, 406 insertions(+), 31 deletions(-) --- 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 Tue Nov 23 13:40:56 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 23 Nov 2010 14:40:56 -0500 Subject: [PATCH v4 1/5] input: gpio_keys: polling mode support In-Reply-To: References: Message-ID: From: Alexander Clouter This implements an optional polling mode for the gpio_keys driver, necessary for GPIOs that are not able to generate IRQs. gpio_keys_platform_data has been extended with poll_interval which specifies the polling interval in ms, this is passed onto input-polldev. This work is a rebase of the patch by Alex Clouter [1] which was based on the patch [2] originally conceived by Paul Mundt. Signed-off-by: Paul Mundt Signed-off-by: Alexander Clouter Signed-off-by: Ben Gardiner Reviewed-by: Chris Cordahi CC: Paul Mundt [1] http://article.gmane.org/gmane.linux.kernel.input/13919 [2] http://article.gmane.org/gmane.linux.kernel.input/5814 --- Changes since v3: * no changes to this patch in the series Changes since v2: * rebased to 083eae3e28643e0eefc5243719f8b1572cf98299 of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git 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 Changes since [1]: * rebased to 0b1c3ef1072f2b97c86351d3736d2b2d00293a11 of git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-davinci.git * use _cansleep variant of gpio_get_value in the polling task to avoid WARN_ON when using I2C GPIO expanders * prevent unitialized access to 'input' in gpio_keys_close() Changes since [2]: * absolute dependency on INPUT_POLLDEV removed Tested with CONFIG_INPUT_POLLDEV={n,m,y} (gpio_keys as module for 'm'). --- drivers/input/keyboard/gpio_keys.c | 120 ++++++++++++++++++++++++++++++------ include/linux/gpio_keys.h | 1 + 2 files changed, 103 insertions(+), 18 deletions(-) diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 6069abe..d2f23d9 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -1,7 +1,9 @@ /* - * Driver for keys on GPIO lines capable of generating interrupts. + * Driver for keys on GPIO lines, either IRQ-driven or polled. * * Copyright 2005 Phil Blundell + * Copyright 2008 Paul Mundt + * Copyright 2010 Alexander Clouter * * 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 @@ -25,6 +27,7 @@ #include #include #include +#include struct gpio_button_data { struct gpio_keys_button *button; @@ -37,6 +40,7 @@ struct gpio_button_data { struct gpio_keys_drvdata { struct input_dev *input; + struct input_polled_dev *poll_dev; struct mutex disable_lock; unsigned int n_buttons; int (*enable)(struct device *dev); @@ -44,6 +48,30 @@ struct gpio_keys_drvdata { struct gpio_button_data data[0]; }; +#if (!defined(CONFIG_INPUT_POLLDEV) && !defined(CONFIG_INPUT_POLLDEV_MODULE)) \ + || (defined(CONFIG_INPUT_POLLDEV_MODULE) \ + && !defined(CONFIG_KEYBOARD_GPIO_MODULE)) + +static inline struct input_polled_dev *allocate_polled_device( + const struct device *dev) +{ + dev_err(dev, "device needs polling (enable INPUT_POLLDEV)\n"); + return NULL; +} + +#define free_polled_device(x) do { } while (0) +#define register_polled_device(x) (-ENXIO) +#define unregister_polled_device(x) do { } while (0) + +#else + +#define allocate_polled_device(x) input_allocate_polled_device() +#define free_polled_device(x) input_free_polled_device(x) +#define register_polled_device(x) input_register_polled_device(x) +#define unregister_polled_device(x) input_unregister_polled_device(x) + +#endif + /* * SYSFS interface for enabling/disabling keys and switches: * @@ -322,7 +350,8 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata) struct gpio_keys_button *button = bdata->button; struct input_dev *input = bdata->input; unsigned int type = button->type ?: EV_KEY; - int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low; + int state = (gpio_get_value_cansleep(button->gpio) ? 1 : 0) + ^ button->active_low; input_event(input, type, button->code, !!state); input_sync(input); @@ -343,6 +372,16 @@ static void gpio_keys_timer(unsigned long _data) schedule_work(&data->work); } +static void gpio_handle_button_event(struct gpio_keys_button *button, + struct gpio_button_data *bdata) +{ + if (bdata->timer_debounce) + mod_timer(&bdata->timer, + jiffies + msecs_to_jiffies(bdata->timer_debounce)); + else + gpio_keys_report_event(bdata); +} + static irqreturn_t gpio_keys_isr(int irq, void *dev_id) { struct gpio_button_data *bdata = dev_id; @@ -350,15 +389,24 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id) BUG_ON(irq != gpio_to_irq(button->gpio)); - if (bdata->timer_debounce) - mod_timer(&bdata->timer, - jiffies + msecs_to_jiffies(bdata->timer_debounce)); - else - schedule_work(&bdata->work); + gpio_handle_button_event(button, bdata); return IRQ_HANDLED; } +static void gpio_keys_poll(struct input_polled_dev *dev) +{ + struct gpio_keys_drvdata *ddata = dev->private; + int i; + + for (i = 0; i < ddata->n_buttons; i++) { + struct gpio_button_data *bdata = &ddata->data[i]; + struct gpio_keys_button *button = bdata->button; + + gpio_handle_button_event(button, bdata); + } +} + static int __devinit gpio_keys_setup_key(struct platform_device *pdev, struct gpio_button_data *bdata, struct gpio_keys_button *button) @@ -446,20 +494,28 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) struct gpio_keys_drvdata *ddata; struct device *dev = &pdev->dev; struct input_dev *input; + struct input_polled_dev *poll_dev; int i, error; int wakeup = 0; ddata = kzalloc(sizeof(struct gpio_keys_drvdata) + pdata->nbuttons * sizeof(struct gpio_button_data), GFP_KERNEL); - input = input_allocate_device(); + if (pdata->poll_interval) { + poll_dev = allocate_polled_device(dev); + input = poll_dev ? poll_dev->input : 0; + } else + input = input_allocate_device(); if (!ddata || !input) { dev_err(dev, "failed to allocate state\n"); error = -ENOMEM; goto fail1; } - ddata->input = input; + if (pdata->poll_interval) + ddata->poll_dev = poll_dev; + else + ddata->input = input; ddata->n_buttons = pdata->nbuttons; ddata->enable = pdata->enable; ddata->disable = pdata->disable; @@ -468,6 +524,12 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) platform_set_drvdata(pdev, ddata); input_set_drvdata(input, ddata); + if (pdata->poll_interval) { + poll_dev->private = ddata; + poll_dev->poll = gpio_keys_poll; + poll_dev->poll_interval = pdata->poll_interval; + } + input->name = pdev->name; input->phys = "gpio-keys/input0"; input->dev.parent = &pdev->dev; @@ -491,14 +553,17 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) bdata->input = input; bdata->button = button; + input_set_capability(input, type, button->code); + + if (pdata->poll_interval) + continue; + error = gpio_keys_setup_key(pdev, bdata, button); if (error) goto fail2; if (button->wakeup) wakeup = 1; - - input_set_capability(input, type, button->code); } error = sysfs_create_group(&pdev->dev.kobj, &gpio_keys_attr_group); @@ -508,7 +573,10 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) goto fail2; } - error = input_register_device(input); + if (pdata->poll_interval) + error = register_polled_device(poll_dev); + else + error = input_register_device(input); if (error) { dev_err(dev, "Unable to register input device, error: %d\n", error); @@ -528,7 +596,9 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); fail2: while (--i >= 0) { - free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]); + if (!pdata->poll_interval) + free_irq(gpio_to_irq(pdata->buttons[i].gpio), + &ddata->data[i]); if (ddata->data[i].timer_debounce) del_timer_sync(&ddata->data[i].timer); cancel_work_sync(&ddata->data[i].work); @@ -537,7 +607,10 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); fail1: - input_free_device(input); + if (pdata->poll_interval) + free_polled_device(poll_dev); + else + input_free_device(input); kfree(ddata); return error; @@ -547,7 +620,8 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) { struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev); - struct input_dev *input = ddata->input; + struct input_dev *input; + struct input_polled_dev *poll_dev; int i; sysfs_remove_group(&pdev->dev.kobj, &gpio_keys_attr_group); @@ -555,15 +629,25 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); for (i = 0; i < pdata->nbuttons; i++) { - int irq = gpio_to_irq(pdata->buttons[i].gpio); - free_irq(irq, &ddata->data[i]); + if (!pdata->poll_interval) { + int irq = gpio_to_irq(pdata->buttons[i].gpio); + free_irq(irq, &ddata->data[i]); + } if (ddata->data[i].timer_debounce) del_timer_sync(&ddata->data[i].timer); cancel_work_sync(&ddata->data[i].work); gpio_free(pdata->buttons[i].gpio); } - input_unregister_device(input); + if (pdata->poll_interval) { + poll_dev = ddata->poll_dev; + unregister_polled_device(poll_dev); + free_polled_device(poll_dev); + } else { + input = ddata->input; + input_unregister_device(input); + input_free_device(input); + } return 0; } diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h index ce73a30..5fdd495 100644 --- a/include/linux/gpio_keys.h +++ b/include/linux/gpio_keys.h @@ -19,6 +19,7 @@ struct gpio_keys_platform_data { unsigned int rep:1; /* enable input subsystem auto repeat */ int (*enable)(struct device *dev); void (*disable)(struct device *dev); + unsigned int poll_interval; /* polling interval in ms */ }; #endif -- 1.7.0.4 From bengardiner at nanometrics.ca Tue Nov 23 13:40:59 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 23 Nov 2010 14:40:59 -0500 Subject: [PATCH v4 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 --- 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 | 190 +++++++++++++++++++++++++++++++ 1 files changed, 190 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 420b628..3cff221 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -431,6 +431,185 @@ 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), + .rep = 0, /* disable auto-repeat */ + .poll_interval = DA850_GPIO_KEYS_POLL_MS, +}; + +static struct platform_device da850_evm_bb_keys_device = { + .name = "gpio-keys", + .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 switches" + " device\n"); + 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 " + "device\n"); + 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, @@ -438,6 +617,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), @@ -446,6 +632,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 Tue Nov 23 13:41:00 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 23 Nov 2010 14:41:00 -0500 Subject: [PATCH v4 5/5] da850-evm: KEYBOARD_GPIO and INPUT_POLLDEV Kconfig conditionals In-Reply-To: References: Message-ID: <3f75a5067091aebdac52d2b0d8788abe5dddbfb0.1290540916.git.bengardiner@nanometrics.ca> Use the mach-davinci/Kconfig to enable gpio-keys as default when da850-evm machine is enabled and to also select INPUT_POLLDEV when gpio-keys is enabled. INPUT_POLLDEV needs to be enabled for gpio-keys devices to function properly on da850-evm. Signed-off-by: Ben Gardiner CC: Kevin Hilman CC: "Nori, Sekhar" --- 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 | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index 84066e8..70d1758 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -180,6 +180,10 @@ endchoice config GPIO_PCA953X default MACH_DAVINCI_DA850_EVM +config KEYBOARD_GPIO + default MACH_DAVINCI_DA850_EVM + select INPUT_POLLDEV if MACH_DAVINCI_DA850_EVM + config MACH_TNETV107X bool "TI TNETV107X Reference Platform" default ARCH_DAVINCI_TNETV107X -- 1.7.0.4 From bengardiner at nanometrics.ca Tue Nov 23 13:40:58 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 23 Nov 2010 14:40:58 -0500 Subject: [PATCH v4 3/5] da850-evm: extract defines for SEL{A, B, C} pins in UI expander In-Reply-To: References: Message-ID: 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 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 51f5ffa..420b628 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -361,23 +361,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; @@ -420,13 +420,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 Tue Nov 23 13:40:57 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Tue, 23 Nov 2010 14:40:57 -0500 Subject: [PATCH v4 2/5] da850-evm: add UI Expander pushbuttons In-Reply-To: References: Message-ID: <9e73184bfb5c0a0d47377dd9b0627405dd87ce8a.1290540916.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 --- 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 | 98 ++++++++++++++++++++++++++++++- 1 files changed, 97 insertions(+), 1 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index f89b0b7..51f5ffa 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,88 @@ 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), + .rep = 0, /* disable auto-repeat */ + .poll_interval = DA850_GPIO_KEYS_POLL_MS, +}; + +static struct platform_device da850_evm_ui_keys_device = { + .name = "gpio-keys", + .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,15 +388,24 @@ 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" + " device\n"); + goto exp_setup_keys_fail; + } + ui_card_detected = 1; pr_info("DA850/OMAP-L138 EVM UI card detected\n"); da850_evm_setup_nor_nand(); - da850_evm_setup_emac_rmii(sel_a); return 0; +exp_setup_keys_fail: + gpio_free(sel_c); exp_setup_selc_fail: gpio_free(sel_b); exp_setup_selb_fail: @@ -324,6 +417,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 +435,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 lethal at linux-sh.org Wed Nov 24 00:09:13 2010 From: lethal at linux-sh.org (Paul Mundt) Date: Wed, 24 Nov 2010 15:09:13 +0900 Subject: [PATCH v2 2/4] da850-evm: add UI Expander pushbuttons In-Reply-To: <8739qshwhm.fsf@deeprootsystems.com> References: <95f48a32a0256ecdb7148aa08d16f64928a7e5d8.1289935504.git.bengardiner@nanometrics.ca> <8739qshwhm.fsf@deeprootsystems.com> Message-ID: <20101124060913.GE11705@linux-sh.org> On Tue, Nov 23, 2010 at 07:48:21AM -0800, Kevin Hilman wrote: > "Nori, Sekhar" writes: > > On Mon, Nov 22, 2010 at 19:20:48, Ben Gardiner wrote: > >> Yes -- only gpio-keys is affected but enabling the option does > >> introduce an additional .o file: > >> > >> drivers/input/Makefile:obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o > >> > >> I agree that in its current state a user couls inadvertently disable > >> the gpio-keys support on da850-evm simply by disabling INPUT_POLLDEV > >> -- which is less than Ideal. How about I replace the current changes > >> to arch/arm/mach-davinci/Kconfig with: > >> > >> config KEYBOARD_GPIO > >> default MACH_DAVINCI_DA850_EVM > >> select INPUT_POLLDEV > >> > >> So 1) gpio-keys functionality is default for the da850evm and 2) > >> whenever gpio-keys is enabled so is INPUT_POLLDEV. > > > > This looks better than what was posted earlier, but I am not > > sure if platforms should influence driver configuration this > > way. > > Agreed. In general, we should not have machine/platform specific > conditionals in generic Kconfigs. Generally, this should be handled in > machine/platform specific Kconfigs. > The patch that I originally wrote for this had the select under the Kconfig option for the driver itself, with the decision to use it or not being dynamically determined based on the platform data. I maintain that this is the only sensible way to deal with things, but this was rejected by the input folks at the time who felt that it was adding in extra overhead for a corner case. The alternatives then are to either make an identical copy of the driver that uses a polled interface, come up with lame INPUT_POLLDEV wrapper shims, or simply accept the fact that drivers using optional interfaces are going to have to have those interfaces built in to the kernel. There has been zero progress on getting this functionality integrated for years now because of this issue, and I don't really see that changing until people accept that drivers will sometimes require additional functionality to be built-in, or provide a suitable alternative. Personally I don't care enough about this particular problem to put any more cycles in to it, and the hardware that I wrote this patch for initially will be end-of-lifed long before there's any coherent consensus in driver land. From aw-davinci-linux at mw-itcon.de Wed Nov 24 06:18:07 2010 From: aw-davinci-linux at mw-itcon.de (aw-davinci-linux at mw-itcon.de) Date: Wed, 24 Nov 2010 13:18:07 +0100 Subject: [PATCH 00/49] spi: davinci: re-write existing driver In-Reply-To: <1289990661-30126-1-git-send-email-nsekhar@ti.com> References: <1289990661-30126-1-git-send-email-nsekhar@ti.com> Message-ID: <4CED027F.8030606@mw-itcon.de> Hi, I tested the new driver on a da830 based board and 2.6.37-rc2 based kernel (head of the master branch of http://git.kernel.org/?p=linux/kernel/git/khilman/linux-davinci.git). It works fine on spi0 with direct CS for a flash It works fine on spi1 with GPIO chip select. The only little problem I found was with MMC_SPI driver that complained about the mode_bits - it checks if the driver supports what in needs. After having set them up in the davinci_spi.c I got rid of that problem - still does not work :( Find below my patch. I just added all mode flags the driver used - not sure if right. Regards, Andreas [FIX][SPI] set the mode_bits - selected all I found in the driver --- a/drivers/spi/davinci_spi.c +++ b/drivers/spi/davinci_spi.c @@ -805,6 +805,8 @@ static int davinci_spi_probe(struct platform_device *pdev) ret = -ENOMEM; goto err; } + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_LSB_FIRST | SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_NO_CS | SPI_READY | SPI_LOOP; dev_set_drvdata(&pdev->dev, master); From nsekhar at ti.com Wed Nov 24 07:16:01 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 24 Nov 2010 18:46:01 +0530 Subject: [PATCH v4 2/5] da850-evm: add UI Expander pushbuttons In-Reply-To: <9e73184bfb5c0a0d47377dd9b0627405dd87ce8a.1290540916.git.bengardiner@nanometrics.ca> References: <9e73184bfb5c0a0d47377dd9b0627405dd87ce8a.1290540916.git.bengardiner@nanometrics.ca> Message-ID: Hi Ben, I have some minor comments on this patch. You could wait for other pending items to get resolved before posting a re-spin. On Wed, Nov 24, 2010 at 01:10:57, Ben Gardiner wrote: > arch/arm/mach-davinci/board-da850-evm.c | 98 ++++++++++++++++++++++++++++++- > 1 files changed, 97 insertions(+), 1 deletions(-) > > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > +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), > + .rep = 0, /* disable auto-repeat */ > + .poll_interval = DA850_GPIO_KEYS_POLL_MS, > +}; > + > +static struct platform_device da850_evm_ui_keys_device = { > + .name = "gpio-keys", > + .id = 0, > + .dev = { > + .platform_data = &da850_evm_ui_keys_pdata > + }, > +}; No need of zero/NULL initialization in structures above since they are static. > @@ -304,15 +388,24 @@ 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" > + " device\n"); Line-breaking an error message is not preferred since it becomes difficult to grep in code. Looking at this message, you could drop " device" altogether. > + goto exp_setup_keys_fail; > + } > + > ui_card_detected = 1; > pr_info("DA850/OMAP-L138 EVM UI card detected\n"); > > da850_evm_setup_nor_nand(); > - Random white space change? Thanks, Sekhar From manjunath.hadli at ti.com Wed Nov 24 08:08:17 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 24 Nov 2010 19:38:17 +0530 Subject: [PATCH v2 0/6] davinci vpbe: display driver for DM644X Message-ID: <1290607697-9714-1-git-send-email-manjunath.hadli@ti.com> This driver is written for Texas Instruments's DM644X VPBE IP. This SoC supports 2 video planes and 2 OSD planes as part of its OSD (On Screen Display) block. At present, the patches conatin the basic support of DM644X V4L2 driver, and subsequent patch sets would add support for external encoders,other DMXXX family SoC and fbdev support. 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. 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 | 81 +- arch/arm/mach-davinci/dm644x.c | 238 +++- 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 | 853 ++++++++++ drivers/media/video/davinci/vpbe_display.c | 2282 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd.c | 1208 ++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++ drivers/media/video/davinci/vpbe_venc.c | 575 +++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++ include/media/davinci/vpbe.h | 187 +++ include/media/davinci/vpbe_display.h | 144 ++ include/media/davinci/vpbe_osd.h | 397 +++++ include/media/davinci/vpbe_types.h | 93 ++ include/media/davinci/vpbe_venc.h | 41 + 16 files changed, 6686 insertions(+), 19 deletions(-) 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 Nov 24 08:09:15 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 24 Nov 2010 19:39:15 +0530 Subject: [PATCHi v2 1/6] davinci vpbe: V4L2 display driver for DM644X SoC Message-ID: <1290607755-10183-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 | 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 { + /* + * 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); + /* 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; + } + + 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; + } + } + 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); + /* 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; + 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 + == 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; + 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; + } + + 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); + + 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; + *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); + 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, + .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 */ + /* 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 -- 1.6.2.4 From manjunath.hadli at ti.com Wed Nov 24 08:09:34 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 24 Nov 2010 19:39:34 +0530 Subject: [PATCH v2 2/6] davinci vpbe: VPBE display driver Message-ID: <1290607774-10359-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 | 853 ++++++++++++++++++++++++++++++++++++ include/media/davinci/vpbe.h | 187 ++++++++ 2 files changed, 1040 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..b367082 --- /dev/null +++ b/drivers/media/video/davinci/vpbe.c @@ -0,0 +1,853 @@ +/* + * 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; + enum v4l2_mbus_pixelcode if_params; + + 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_params = vpbe_config->outputs[index].if_params; + venc_device->setup_if_config(if_params); + + 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; + venc_device->setup_if_config( + vpbe_dev->cfg->outputs[output_index].if_params); + + 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..26ee29b --- /dev/null +++ b/include/media/davinci/vpbe.h @@ -0,0 +1,187 @@ +/* + * 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 + */ + enum v4l2_mbus_pixelcode if_params; +}; + +/* 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 Nov 24 08:09:54 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 24 Nov 2010 19:39:54 +0530 Subject: [PATCH v2 3/6] davinci vpbe: OSD(On Screen Display) block Message-ID: <1290607794-10476-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 | 1208 +++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++++++ include/media/davinci/vpbe_osd.h | 397 +++++++++ 3 files changed, 1994 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..64371b5 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd.c @@ -0,0 +1,1208 @@ +/* + * 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; + + 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 || + !win->lconfig.line_length || + !win->lconfig.xsize || + !win->lconfig.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 (win->lconfig.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; + + 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, + &win->lconfig); + } + + 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]; + 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) + && (win->lconfig.pixfmt != PIXFMT_OSD_ATTR)) + || ((lconfig->pixfmt != PIXFMT_OSD_ATTR) + && (win->lconfig.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) + && (win->lconfig.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) + && (win->lconfig.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 != win->lconfig.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); + } + } + } + + win->lconfig = *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, + &win->lconfig); + } + + 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; + 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); + + win->lconfig.line_length = 0; + win->lconfig.xsize = 0; + win->lconfig.ysize = 0; + win->lconfig.xpos = 0; + win->lconfig.ypos = 0; + win->lconfig.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. + */ + win->lconfig.pixfmt = PIXFMT_8BPP; + _osd_set_layer_config(sd, layer, &win->lconfig); + 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: + win->lconfig.pixfmt = osd->yc_pixfmt; + _osd_set_layer_config(sd, layer, &win->lconfig); + 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 Nov 24 08:10:17 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 24 Nov 2010 19:40:17 +0530 Subject: [PATCH v2 4/6] davinci vpbe: VENC( Video Encoder) implementation Message-ID: <1290607817-10550-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 | 575 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++++++++ include/media/davinci/vpbe_venc.h | 41 ++ 3 files changed, 805 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..cbd20f2 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc.c @@ -0,0 +1,575 @@ +/* + * 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; + /*enum v4l2_mbus_pixelcode if_params;*/ + 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..2fff9c4 --- /dev/null +++ b/include/media/davinci/vpbe_venc.h @@ -0,0 +1,41 @@ +/* + * 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_pinmux)(enum v4l2_mbus_pixelcode if_type, + int field); + int (*setup_clock)(enum vpbe_enc_timings_type type, + __u64 mode); + int (*setup_if_config)(enum v4l2_mbus_pixelcode pixcode); + /* Number of LCD outputs supported */ + int num_lcd_outputs; +}; + + +#endif -- 1.6.2.4 From manjunath.hadli at ti.com Wed Nov 24 08:10:35 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 24 Nov 2010 19:40:35 +0530 Subject: [PATCH v2 5/6] davinci vpbe: platform specific additions Message-ID: <1290607835-10612-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 | 81 ++++++++-- arch/arm/mach-davinci/dm644x.c | 238 ++++++++++++++++++++++++++- arch/arm/mach-davinci/include/mach/dm644x.h | 4 + 3 files changed, 304 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..1ca1456 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,73 @@ 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, + .if_params = V4L2_MBUS_FMT_FIXED, + }, + { + .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, + .if_params = V4L2_MBUS_FMT_FIXED, + }, +}; + +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 +673,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..bad5b89 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -640,6 +640,216 @@ 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); + +static int dm644x_vpbe_setup_pinmux(enum v4l2_mbus_pixelcode if_type, + int field) +{ + int ret = 0; + + switch (if_type) { + case V4L2_MBUS_FMT_SGRBG8_1X8: + /* This was VPBE_DIGITAL_IF_PRGB.Replace the enum accordingly + * when the right one gets into open source */ + davinci_cfg_reg(DM644X_GPIO46_47); + davinci_cfg_reg(DM644X_GPIO0); + davinci_cfg_reg(DM644X_RGB666); + davinci_cfg_reg(DM644X_LOEEN); + davinci_cfg_reg(DM644X_GPIO3); + break; + case V4L2_MBUS_FMT_YUYV10_1X20: + /* This was VPBE_DIGITAL_IF_YCC16.Replace the enum accordingly + * when the right one gets into open source */ + if (field) + davinci_cfg_reg(DM644X_LFLDEN); + else + davinci_cfg_reg(DM644X_GPIO3); + davinci_cfg_reg(DM644X_LOEEN); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +#define VPSS_CLKCTL 0x01C40044 +#define VENC_VMOD 0x01C72400 +#define VENC_YCCTL 0x01C72438 +static void __iomem *vpss_clkctl_reg; +static void __iomem *venc_vmod_reg; +static void __iomem *venc_ycctl_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 int dm644x_set_if_config(enum v4l2_mbus_pixelcode pixcode) +{ + unsigned int val = 0; + int ret = 0; + + switch (pixcode) { + case V4L2_MBUS_FMT_FIXED: + /* Analog out.do nothing */ + break; + case V4L2_MBUS_FMT_YUYV8_2X8: + /* BT656 */ + val = (1<<12); + /*set VDMD in VMOD */ + dm644x_reg_modify(venc_vmod_reg, val, (7 << 12)); + /* Set YCCTL */ + dm644x_reg_modify(venc_ycctl_reg, 1, 1); + break; + case V4L2_MBUS_FMT_YUYV10_1X20: + /* This was VPBE_DIGITAL_IF_YCC16.BT656.Replace the enum accordingly + * when the right one gets into open source */ + val = 0 << 12; + dm644x_reg_modify(venc_vmod_reg, val, (7 << 12)); + dm644x_reg_modify(venc_ycctl_reg, 1, 1); + break; + case V4L2_MBUS_FMT_SGRBG8_1X8: + /* This was VPBE_DIGITAL_IF_PRGB/SRGB.Replace the enum accordingly + * when the right one gets into open source */ + val = 2 << 12; + dm644x_reg_modify(venc_vmod_reg, val, (7 << 12)); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +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_pinmux = dm644x_vpbe_setup_pinmux, + .setup_clock = dm644x_venc_setup_clock, + .setup_if_config = dm644x_set_if_config, +}; + +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 +977,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 Nov 24 08:10:54 2010 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Wed, 24 Nov 2010 19:40:54 +0530 Subject: [PATCH v2 6/6] davinci vpbe: Build infrastructure for VPBE driver Message-ID: <1290607854-10672-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 ++ 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 bengardiner at nanometrics.ca Wed Nov 24 11:16:46 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 24 Nov 2010 12:16:46 -0500 Subject: [PATCH v4 2/5] da850-evm: add UI Expander pushbuttons In-Reply-To: References: <9e73184bfb5c0a0d47377dd9b0627405dd87ce8a.1290540916.git.bengardiner@nanometrics.ca> Message-ID: Hi Sekhar, On Wed, Nov 24, 2010 at 8:16 AM, Nori, Sekhar wrote: > Hi Ben, > > I have some minor comments on this patch. You could > wait for other pending items to get resolved before > posting a re-spin. Thank you for your continued interest and input. > On Wed, Nov 24, 2010 at 01:10:57, Ben Gardiner wrote: > >> ?arch/arm/mach-davinci/board-da850-evm.c | ? 98 ++++++++++++++++++++++++++++++- >> ?1 files changed, 97 insertions(+), 1 deletions(-) >> >> diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > >> +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), >> + ? ? .rep = 0, /* disable auto-repeat */ >> + ? ? .poll_interval = DA850_GPIO_KEYS_POLL_MS, >> +}; >> + >> +static struct platform_device da850_evm_ui_keys_device = { >> + ? ? .name = "gpio-keys", >> + ? ? .id = 0, >> + ? ? .dev = { >> + ? ? ? ? ? ? .platform_data = &da850_evm_ui_keys_pdata >> + ? ? }, >> +}; > > No need of zero/NULL initialization in structures above > since they are static. It my opinion -- please tell me if it is wrong :) -- that explicit initialization of platform data members is better than implicit initialization; future developers and browsers of the code can see that wakeup events are disabled as are auto-repeats. I also included the .desc = NULL explicitly to indicate that it would be populated at runtime so that future developers who grep the code would know to look for a runtime initialization. The .id = 0 is there since the bb keys get .id = 1. As I said please tell me if you would still like me to remove this expliciti initializations -- I would prefer to leave them as-is. >> @@ -304,15 +388,24 @@ 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" >> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? " device\n"); > > Line-breaking an error message is not preferred since it becomes > difficult to grep in code. Looking at this message, you could drop > " device" altogether. Interesting point. Thank you, I really apreciate all the knowledge you are imparting to me throughout this process. I will remove the " device" string from the error message entirely. >> + ? ? ? ? ? ? goto exp_setup_keys_fail; >> + ? ? } >> + >> ? ? ? ui_card_detected = 1; >> ? ? ? pr_info("DA850/OMAP-L138 EVM UI card detected\n"); >> >> ? ? ? da850_evm_setup_nor_nand(); >> - > > Random white space change? Oops, yes -- I am relying to heavily on checkpatch.pl to tell me about my mistakes. I should be reviewing my patches more closely. Thank you again. Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From bengardiner at nanometrics.ca Wed Nov 24 11:17:52 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 24 Nov 2010 12:17:52 -0500 Subject: [PATCH v2 2/4] da850-evm: add UI Expander pushbuttons In-Reply-To: <20101124060913.GE11705@linux-sh.org> References: <95f48a32a0256ecdb7148aa08d16f64928a7e5d8.1289935504.git.bengardiner@nanometrics.ca> <8739qshwhm.fsf@deeprootsystems.com> <20101124060913.GE11705@linux-sh.org> Message-ID: Hi Paul, On Wed, Nov 24, 2010 at 1:09 AM, Paul Mundt wrote: > On Tue, Nov 23, 2010 at 07:48:21AM -0800, Kevin Hilman wrote: >> [...] >> Agreed. ?In general, we should not have machine/platform specific >> conditionals in generic Kconfigs. ? Generally, this should be handled in >> machine/platform specific Kconfigs. >> > The patch that I originally wrote for this had the select under the > Kconfig option for the driver itself, with the decision to use it or not > being dynamically determined based on the platform data. I maintain that > this is the only sensible way to deal with things, but this was rejected > by the input folks at the time who felt that it was adding in extra > overhead for a corner case. The alternatives then are to either make an > identical copy of the driver that uses a polled interface, come up with > lame INPUT_POLLDEV wrapper shims, or simply accept the fact that drivers > using optional interfaces are going to have to have those interfaces > built in to the kernel. Thanks for weighing-in. >From the statements that Dmitry has made [1] [2], I gather that a separate driver is preferred. But I realized this too late. I've tested the recent re-send of "[PATCH 09/18] input: add input driver for polled GPIO buttons" by Gabor Juhos [3] with this patch series and it works well. The changes needed in this patch series are minimal. I have some review comments for the gpio_buttons patch that I will be posting shortly. The upshot is there will be no need for Kconfig entries for both INPUT_POLLDEV and KEYBOARD_GPIO in arch/arm/mach-davinci, just one to enable the gpio_buttons driver. -- Best Regards, Ben Gardiner [1] http://article.gmane.org/gmane.linux.kernel.input/14044 [2] http://article.gmane.org/gmane.linux.kernel.input/16478 [3] http://article.gmane.org/gmane.linux.kernel.input/16610 --- Nanometrics Inc. http://www.nanometrics.ca From bengardiner at nanometrics.ca Wed Nov 24 15:59:29 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 24 Nov 2010 16:59:29 -0500 Subject: [PATCH v5 0/5] da850-evm: add gpio-{keys, leds} for UI and BB expanders In-Reply-To: References: Message-ID: Note: I must regrettably move on to other projects for awhile and will thus be unavailable to continue this integration effort. I am posting the most recent version of the series with a modified version of Gabor Juho's driver in the hopes that it will make the integration effort easier for whoever picks up the task when the time is right -- whether or not that is me. My apologies for the patchbomb. I will do my best to make time for reviews and testing of any future versions of the series. Best Regards, Ben Gardiner --- 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): [WIP] 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 | 2 + drivers/input/keyboard/gpio_keys_polled.c | 240 ++++++++++++++++++++++ include/linux/gpio_keys_polled.h | 26 +++ 6 files changed, 581 insertions(+), 12 deletions(-) create mode 100644 drivers/input/keyboard/gpio_keys_polled.c create mode 100644 include/linux/gpio_keys_polled.h --- 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 Wed Nov 24 15:59:30 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 24 Nov 2010 16:59:30 -0500 Subject: [PATCH v5 1/5] [WIP] input: add input driver for polled GPIO buttons In-Reply-To: References: Message-ID: <91c06463c8c2afb543c8a22350d2ddd8bf2ef71f.1290635422.git.bengardiner@nanometrics.ca> 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. (WIP: this version has incorporated into it the changes I suggested in review of the original patch by Gabor Junos. I am posting it as part of the da850-evm series in the hopes that the final version produced by Gabor can be substituted in its place when the time is right. I don't mean to imply that this version of the patch should be integrated in the final series) Signed-off-by: Gabor Juhos Cc: Dmitry Torokhov Cc: Mike Frysinger Cc: linux-input at vger.kernel.org Signed-off-by: Ben Gardiner --- drivers/input/keyboard/Kconfig | 16 ++ drivers/input/keyboard/Makefile | 2 + drivers/input/keyboard/gpio_keys_polled.c | 240 +++++++++++++++++++++++++++++ include/linux/gpio_keys_polled.h | 26 +++ 4 files changed, 284 insertions(+), 0 deletions(-) create mode 100644 drivers/input/keyboard/gpio_keys_polled.c create mode 100644 include/linux/gpio_keys_polled.h diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index b8c51b9..9648ff4 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -485,4 +485,20 @@ config KEYBOARD_W90P910 To compile this driver as a module, choose M here: the module will be called w90p910_keypad. +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 of various CPUs (and some other chips). + + 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-buttons. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index a34452e..e6da817 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -44,3 +44,5 @@ obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o +obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.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..390ed93 --- /dev/null +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -0,0 +1,240 @@ +/* + * 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 can_sleep; +}; + +struct gpio_keys_polled_dev { + struct input_polled_dev *poll_dev; + struct gpio_keys_polled_platform_data *pdata; + struct gpio_keys_button_data *data; +}; + +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_polled_platform_data *pdata = bdev->pdata; + struct input_dev *input = dev->input; + int i, threshold; + + for (i = 0; i < bdev->pdata->nbuttons; i++) { + struct gpio_keys_button *button = &pdata->buttons[i]; + struct gpio_keys_button_data *bdata = &bdev->data[i]; + + threshold = round_up(button->debounce_interval, + pdata->poll_interval) / + pdata->poll_interval; + if (bdata->count < threshold) + bdata->count++; + else + gpio_keys_polled_check_state(input, button, bdata); + + } +} + +static int __devinit gpio_keys_polled_probe(struct platform_device *pdev) +{ + struct gpio_keys_polled_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) + return -ENXIO; + + 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; + } + + bdev->data = (struct gpio_keys_button_data *) &bdev[1]; + + 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; + + 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]; + 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"); + 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; + } + + bdev->data[i].can_sleep = gpio_cansleep(gpio); + bdev->data[i].last_state = -1; + + input_set_capability(input, type, button->code); + } + + bdev->poll_dev = poll_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: + for (i = i - 1; i >= 0; i--) + 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_polled_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"); diff --git a/include/linux/gpio_keys_polled.h b/include/linux/gpio_keys_polled.h new file mode 100644 index 0000000..bf7f94a --- /dev/null +++ b/include/linux/gpio_keys_polled.h @@ -0,0 +1,26 @@ +/* + * Definitions for the GPIO buttons interface driver + * + * Copyright (C) 2007-2010 Gabor Juhos + * + * This file was based on: /include/linux/gpio_keys.h + * The original gpio_keys.h seems not to have a license. + * + * 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. + * + */ + +#ifndef _GPIO_BUTTONS_H_ +#define _GPIO_BUTTONS_H_ + +#include + +struct gpio_keys_polled_platform_data { + struct gpio_keys_button *buttons; + int nbuttons; /* number of buttons */ + int poll_interval; /* polling interval in msecs*/ +}; + +#endif /* _GPIO_BUTTONS_H_ */ -- 1.7.0.4 From bengardiner at nanometrics.ca Wed Nov 24 15:59:31 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 24 Nov 2010 16:59:31 -0500 Subject: [PATCH v5 2/5] da850-evm: add UI Expander pushbuttons In-Reply-To: References: Message-ID: <18c52f8d32a0a34a80644c14c9aae57032495019.1290635422.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 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..f6490f8 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_polled_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 Wed Nov 24 15:59:32 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 24 Nov 2010 16:59:32 -0500 Subject: [PATCH v5 3/5] da850-evm: extract defines for SEL{A, B, C} pins in UI expander In-Reply-To: References: Message-ID: 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 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 f6490f8..8f7a605 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 Wed Nov 24 15:59:33 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 24 Nov 2010 16:59:33 -0500 Subject: [PATCH v5 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 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 8f7a605..14d3381 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_polled_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 Wed Nov 24 15:59:34 2010 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Wed, 24 Nov 2010 16:59:34 -0500 Subject: [PATCH v5 5/5] da850-evm: KEYBOARD_GPIO_POLLED Kconfig conditional In-Reply-To: References: Message-ID: <389ef9b19f078dfc6308f77452b8bf596ab22a04.1290635422.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 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 Kloiber at distec.de Thu Nov 25 11:13:26 2010 From: Kloiber at distec.de (DISTEC Kloiber Thomas) Date: Thu, 25 Nov 2010 18:13:26 +0100 Subject: davinci vpbe: How to use 1920 x 1080 mode with osd0 on DM644X? Message-ID: Hi all, I'm currently using MV kernel 2.6.18 from DVSDK 2.0.0.22 on DM6443. I know, that e2e forum is the better place for this kernel version and I already posted my question there as well some days ago. I did see that there is some work going on here on davinci vpbe and may be I can find some help here. I want to use 1920 x 1080 mode for osd0 fb on DM644X. My boot args are ..... video=davincifb:osd0=1920x1080x16,8100K ... But I'm not able to use more than 960 lines. The max. mode I can select with fbset is 1920 x 960. Does anyone know the reason for this limitation? How can 1080 lines be supported? Does the git kernel have the same limitation? Thanks and best regards Thomas From hverkuil at xs4all.nl Fri Nov 26 05:20:55 2010 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Fri, 26 Nov 2010 12:20:55 +0100 Subject: [PATCHi v2 1/6] davinci vpbe: V4L2 display driver for DM644X SoC In-Reply-To: <1290607755-10183-1-git-send-email-manjunath.hadli@ti.com> References: <1290607755-10183-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <201011261220.55748.hverkuil@xs4all.nl> 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 hverkuil at xs4all.nl Fri Nov 26 05:45:26 2010 From: hverkuil at xs4all.nl (Hans Verkuil) Date: Fri, 26 Nov 2010 12:45:26 +0100 Subject: [PATCH v2 0/6] davinci vpbe: display driver for DM644X In-Reply-To: <1290607697-9714-1-git-send-email-manjunath.hadli@ti.com> References: <1290607697-9714-1-git-send-email-manjunath.hadli@ti.com> Message-ID: <201011261245.26801.hverkuil@xs4all.nl> Hi Manju, On Wednesday, November 24, 2010 15:08:17 Manjunath Hadli wrote: > This driver is written for Texas Instruments's DM644X VPBE IP. > This SoC supports 2 video planes and 2 OSD planes as part of its > OSD (On Screen Display) block. > > At present, the patches conatin the basic support of DM644X V4L2 > driver, and subsequent patch sets would add support for external > encoders,other DMXXX family SoC and fbdev support. As you can see, I have only comments on the first patch. The others seem fine to me. I do think that the drivers would benefit from some all round tightening of the code. Often functions have quite a lot of indentation levels, the use of more temporary variables will help, and the driver has a lot of error and debug messages, possibly more than is necessary. Regards, Hans > > 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. > > 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 | 81 +- > arch/arm/mach-davinci/dm644x.c | 238 +++- > 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 | 853 ++++++++++ > drivers/media/video/davinci/vpbe_display.c | 2282 ++++++++++++++++++++++++++ > drivers/media/video/davinci/vpbe_osd.c | 1208 ++++++++++++++ > drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++ > drivers/media/video/davinci/vpbe_venc.c | 575 +++++++ > drivers/media/video/davinci/vpbe_venc_regs.h | 189 +++ > include/media/davinci/vpbe.h | 187 +++ > include/media/davinci/vpbe_display.h | 144 ++ > include/media/davinci/vpbe_osd.h | 397 +++++ > include/media/davinci/vpbe_types.h | 93 ++ > include/media/davinci/vpbe_venc.h | 41 + > 16 files changed, 6686 insertions(+), 19 deletions(-) > 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 mkaricheri at gmail.com Sat Nov 27 08:44:46 2010 From: mkaricheri at gmail.com (Muralidharan Karicheri) Date: Sat, 27 Nov 2010 09:44:46 -0500 Subject: [PATCH v2 0/6] davinci vpbe: display driver for DM644X In-Reply-To: <1290607697-9714-1-git-send-email-manjunath.hadli@ti.com> References: <1290607697-9714-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Manjunath, Could you re-send the patch 1/6? I can't find it either at my inbox or mailing list. On Wed, Nov 24, 2010 at 9:08 AM, Manjunath Hadli wrote: > This driver is written for Texas Instruments's DM644X VPBE IP. > This SoC supports 2 video planes and 2 OSD planes as part of its > OSD (On Screen Display) block. > > At present, the patches conatin the basic support of DM644X V4L2 > driver, and subsequent patch sets would add support for external > encoders,other DMXXX family SoC and fbdev support. > > ? ? ? ? ? ? ? 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. > > 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 ? ? | ? 81 +- > ?arch/arm/mach-davinci/dm644x.c ? ? ? ? ? ? ? | ?238 +++- > ?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 ? ? ? ? ? | ?853 ++++++++++ > ?drivers/media/video/davinci/vpbe_display.c ? | 2282 ++++++++++++++++++++++++++ > ?drivers/media/video/davinci/vpbe_osd.c ? ? ? | 1208 ++++++++++++++ > ?drivers/media/video/davinci/vpbe_osd_regs.h ?| ?389 +++++ > ?drivers/media/video/davinci/vpbe_venc.c ? ? ?| ?575 +++++++ > ?drivers/media/video/davinci/vpbe_venc_regs.h | ?189 +++ > ?include/media/davinci/vpbe.h ? ? ? ? ? ? ? ? | ?187 +++ > ?include/media/davinci/vpbe_display.h ? ? ? ? | ?144 ++ > ?include/media/davinci/vpbe_osd.h ? ? ? ? ? ? | ?397 +++++ > ?include/media/davinci/vpbe_types.h ? ? ? ? ? | ? 93 ++ > ?include/media/davinci/vpbe_venc.h ? ? ? ? ? ?| ? 41 + > ?16 files changed, 6686 insertions(+), 19 deletions(-) > ?create mode 100644 drivers/media/video/davinci/vpbe.c > ?create mode 100644 drivers/media/video/davinci/vpbe_display.c > ?create mode 100644 drivers/media/video/davinci/vpbe_osd.c > ?create mode 100644 drivers/media/video/davinci/vpbe_osd_regs.h > ?create mode 100644 drivers/media/video/davinci/vpbe_venc.c > ?create mode 100644 drivers/media/video/davinci/vpbe_venc_regs.h > ?create mode 100644 include/media/davinci/vpbe.h > ?create mode 100644 include/media/davinci/vpbe_display.h > ?create mode 100644 include/media/davinci/vpbe_osd.h > ?create mode 100644 include/media/davinci/vpbe_types.h > ?create mode 100644 include/media/davinci/vpbe_venc.h > > -- > To unsubscribe from this list: send the line "unsubscribe linux-media" in > the body of a message to majordomo at vger.kernel.org > More majordomo info at ?http://vger.kernel.org/majordomo-info.html > -- Murali Karicheri mkaricheri at gmail.com From mkaricheri at gmail.com Sat Nov 27 09:22:14 2010 From: mkaricheri at gmail.com (Muralidharan Karicheri) Date: Sat, 27 Nov 2010 10:22:14 -0500 Subject: [PATCH v2 5/6] davinci vpbe: platform specific additions In-Reply-To: <1290607835-10612-1-git-send-email-manjunath.hadli@ti.com> References: <1290607835-10612-1-git-send-email-manjunath.hadli@ti.com> Message-ID: > +static int dm644x_set_if_config(enum v4l2_mbus_pixelcode pixcode) > +{ > + ? ? ? unsigned int val = 0; > + ? ? ? int ret = 0; > + > + ? ? ? switch (pixcode) { > + ? ? ? case V4L2_MBUS_FMT_FIXED: > + ? ? ? ? ? ? /* Analog out.do nothing */ > + ? ? ? ? ? ? break; > + ? ? ? case V4L2_MBUS_FMT_YUYV8_2X8: > + ? ? ? ? ? ? ? /* BT656 */ > + ? ? ? ? ? ? ? val = (1<<12); > + ? ? ? ? ? ? ? /*set VDMD in VMOD */ > + ? ? ? ? ? ? ? dm644x_reg_modify(venc_vmod_reg, val, (7 << 12)); > + ? ? ? ? ? ? ? /* Set YCCTL */ > + ? ? ? ? ? ? ? dm644x_reg_modify(venc_ycctl_reg, 1, 1); > + ? ? ? ? ? ? ? break; > + ? ? ? case V4L2_MBUS_FMT_YUYV10_1X20: > + ? ? ? /* This was VPBE_DIGITAL_IF_YCC16.BT656.Replace the enum accordingly > + ? ? ? * when the right one gets into open source */ > + ? ? ? ? ? ? ? val = 0 << 12; > + ? ? ? ? ? ? ? dm644x_reg_modify(venc_vmod_reg, val, (7 << 12)); > + ? ? ? ? ? ? ? dm644x_reg_modify(venc_ycctl_reg, 1, 1); > + ? ? ? ? ? ? ? break; > + ? ? ? case V4L2_MBUS_FMT_SGRBG8_1X8: > + ? ? ? /* This was VPBE_DIGITAL_IF_PRGB/SRGB.Replace the enum accordingly > + ? ? ? * when the right one gets into open source */ > + ? ? ? ? ? ? ? val = 2 << 12; > + ? ? ? ? ? ? ? dm644x_reg_modify(venc_vmod_reg, val, (7 << 12)); > + ? ? ? ? ? ? ? break; > + ? ? ? default: > + ? ? ? ? ? ? ? ret = -EINVAL; > + ? ? ? ? ? ? ? break; > + ? ? ? } > + ? ? ? return ret; > +} The media bus format was added to sub device interface sometime back to configure the media data format (8 bit, 16 bit etc) and there is also a set of APIs to set the pad configuration (done by Laurent) which is being reviewed. In the context of that, I believe the venc VMOD configuration can be handled by implementing s_mbus_fmt() sub device API at venc. Currently OSD sub device has set_layer_config() which consists of setting the pixel format, frame format and sub-frame(crop). I think this can be replaced by s_mbus_fmt(). So on a board file, we could define the pad configuration which will include the mbus code such as V4L2_MBUS_FMT_YUYV8_2X8 + addition pad configurations such as embedded sync enabled etc. So once sub device is selected, vpbe controller will set the pad configuration (obtained from board file for each of the ?). The pad in this case is the link between venc lcd port to external encoder. When S_FMT/S_CROP is called on the video node, the controller calls s_mbus_fmt() on the osd and venc sub device to set the mbus formats. Since the external encoder support is a TBD, I am okay if you implement this after this patch series is merged to the tree. Murali > + > +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_pinmux = dm644x_vpbe_setup_pinmux, > + ? ? ? .setup_clock = dm644x_venc_setup_clock, > + ? ? ? .setup_if_config = dm644x_set_if_config, > +}; > + > +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 +977,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 > > -- > 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 > -- Murali Karicheri mkaricheri at gmail.com From mkaricheri at gmail.com Sat Nov 27 09:24:28 2010 From: mkaricheri at gmail.com (Muralidharan Karicheri) Date: Sat, 27 Nov 2010 10:24:28 -0500 Subject: [PATCH v2 5/6] davinci vpbe: platform specific additions In-Reply-To: References: <1290607835-10612-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Just want to add one more point. Probably it is a good idea to keep this function in venc. Why this function has to be platform specific? On Sat, Nov 27, 2010 at 10:22 AM, Muralidharan Karicheri wrote: >> +static int dm644x_set_if_config(enum v4l2_mbus_pixelcode pixcode) >> +{ >> + ? ? ? unsigned int val = 0; >> + ? ? ? int ret = 0; >> + >> + ? ? ? switch (pixcode) { >> + ? ? ? case V4L2_MBUS_FMT_FIXED: >> + ? ? ? ? ? ? /* Analog out.do nothing */ >> + ? ? ? ? ? ? break; >> + ? ? ? case V4L2_MBUS_FMT_YUYV8_2X8: >> + ? ? ? ? ? ? ? /* BT656 */ >> + ? ? ? ? ? ? ? val = (1<<12); >> + ? ? ? ? ? ? ? /*set VDMD in VMOD */ >> + ? ? ? ? ? ? ? dm644x_reg_modify(venc_vmod_reg, val, (7 << 12)); >> + ? ? ? ? ? ? ? /* Set YCCTL */ >> + ? ? ? ? ? ? ? dm644x_reg_modify(venc_ycctl_reg, 1, 1); >> + ? ? ? ? ? ? ? break; >> + ? ? ? case V4L2_MBUS_FMT_YUYV10_1X20: >> + ? ? ? /* This was VPBE_DIGITAL_IF_YCC16.BT656.Replace the enum accordingly >> + ? ? ? * when the right one gets into open source */ >> + ? ? ? ? ? ? ? val = 0 << 12; >> + ? ? ? ? ? ? ? dm644x_reg_modify(venc_vmod_reg, val, (7 << 12)); >> + ? ? ? ? ? ? ? dm644x_reg_modify(venc_ycctl_reg, 1, 1); >> + ? ? ? ? ? ? ? break; >> + ? ? ? case V4L2_MBUS_FMT_SGRBG8_1X8: >> + ? ? ? /* This was VPBE_DIGITAL_IF_PRGB/SRGB.Replace the enum accordingly >> + ? ? ? * when the right one gets into open source */ >> + ? ? ? ? ? ? ? val = 2 << 12; >> + ? ? ? ? ? ? ? dm644x_reg_modify(venc_vmod_reg, val, (7 << 12)); >> + ? ? ? ? ? ? ? break; >> + ? ? ? default: >> + ? ? ? ? ? ? ? ret = -EINVAL; >> + ? ? ? ? ? ? ? break; >> + ? ? ? } >> + ? ? ? return ret; >> +} > > The media bus format was added to sub device interface sometime back > to configure the media data format (8 bit, 16 bit etc) and there is > also a set of APIs to set the pad configuration (done by Laurent) > which is being reviewed. In the context of that, I believe the venc > VMOD configuration can be handled by implementing s_mbus_fmt() sub > device API at venc. Currently OSD sub device has set_layer_config() > which consists of setting the pixel format, frame format and > sub-frame(crop). I think this can be replaced by s_mbus_fmt(). So on a > board file, we could define the pad configuration which will include > the mbus code such as V4L2_MBUS_FMT_YUYV8_2X8 + addition pad > configurations such as embedded sync enabled etc. So once sub device > is selected, vpbe controller will set the pad configuration (obtained > from board file for each of the ?). The pad in this case is the link > between venc lcd port to external encoder. When S_FMT/S_CROP is called > on the video node, the controller calls s_mbus_fmt() on the osd and > venc sub device to set the mbus formats. > > Since the external encoder support is a TBD, I ?am okay if you > implement this after this patch series is merged to the tree. > > Murali >> + >> +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_pinmux = dm644x_vpbe_setup_pinmux, >> + ? ? ? .setup_clock = dm644x_venc_setup_clock, >> + ? ? ? .setup_if_config = dm644x_set_if_config, >> +}; >> + >> +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 +977,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 >> >> -- >> 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 >> > > > > -- > Murali Karicheri > mkaricheri at gmail.com > -- Murali Karicheri mkaricheri at gmail.com From mkaricheri at gmail.com Sat Nov 27 09:37:24 2010 From: mkaricheri at gmail.com (Muralidharan Karicheri) Date: Sat, 27 Nov 2010 10:37:24 -0500 Subject: [PATCH v2 3/6] davinci vpbe: OSD(On Screen Display) block In-Reply-To: <1290607794-10476-1-git-send-email-manjunath.hadli@ti.com> References: <1290607794-10476-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Manjunath, I think set_layer_config(), get_layer_config and try_layer_config() can be split into standard fmt and crop sub device video ops. Could you investigate this? Murali On Wed, Nov 24, 2010 at 9:09 AM, Manjunath Hadli wrote: > 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 ? ? ?| 1208 +++++++++++++++++++++++++++ > ?drivers/media/video/davinci/vpbe_osd_regs.h | ?389 +++++++++ > ?include/media/davinci/vpbe_osd.h ? ? ? ? ? ?| ?397 +++++++++ > ?3 files changed, 1994 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..64371b5 > --- /dev/null > +++ b/drivers/media/video/davinci/vpbe_osd.c > @@ -0,0 +1,1208 @@ > +/* > + * 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; > + > + ? ? ? 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 || > + ? ? ? ? ? ? ? ? ? ? ? !win->lconfig.line_length || > + ? ? ? ? ? ? ? ? ? ? ? !win->lconfig.xsize || > + ? ? ? ? ? ? ? ? ? ? ? !win->lconfig.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 (win->lconfig.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; > + > + ? ? ? 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, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?&win->lconfig); > + ? ? ? } > + > + ? ? ? 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]; > + ? ? ? 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) > + ? ? ? ? ? ? ? ? ? ?&& (win->lconfig.pixfmt != PIXFMT_OSD_ATTR)) > + ? ? ? ? ? ? ? ? ? || ((lconfig->pixfmt != PIXFMT_OSD_ATTR) > + ? ? ? ? ? ? ? ? ? ? ? && (win->lconfig.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) > + ? ? ? ? ? ? ? ? ? && (win->lconfig.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) > + ? ? ? ? ? ? ? ? ? ? ? ? ?&& (win->lconfig.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 != win->lconfig.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); > + ? ? ? ? ? ? ? ? ? ? ? } > + ? ? ? ? ? ? ? } > + ? ? ? } > + > + ? ? ? win->lconfig = *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, > + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?&win->lconfig); > + ? ? ? } > + > + ? ? ? 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; > + ? ? ? 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); > + > + ? ? ? win->lconfig.line_length = 0; > + ? ? ? win->lconfig.xsize = 0; > + ? ? ? win->lconfig.ysize = 0; > + ? ? ? win->lconfig.xpos = 0; > + ? ? ? win->lconfig.ypos = 0; > + ? ? ? win->lconfig.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. > + ? ? ? ? ? ? ? ?*/ > + ? ? ? ? ? ? ? win->lconfig.pixfmt = PIXFMT_8BPP; > + ? ? ? ? ? ? ? _osd_set_layer_config(sd, layer, &win->lconfig); > + ? ? ? ? ? ? ? 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: > + ? ? ? ? ? ? ? win->lconfig.pixfmt = osd->yc_pixfmt; > + ? ? ? ? ? ? ? _osd_set_layer_config(sd, layer, &win->lconfig); > + ? ? ? ? ? ? ? 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 > > -- > 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 > -- Murali Karicheri mkaricheri at gmail.com From lamiaposta71 at gmail.com Sun Nov 28 12:33:45 2010 From: lamiaposta71 at gmail.com (Raffaele Recalcati) Date: Sun, 28 Nov 2010 19:33:45 +0100 Subject: New sched fantastic patch Message-ID: Looking at http://lkml.org/lkml/2010/10/19/123 I ask if anyone knows if this patch is good for embedded arm frame buffer or X 2.6.32 , or the same with 2 6.36. Thx, Raffaele -------------- next part -------------- An HTML attachment was scrubbed... URL: From ghosh.subhasish at gmail.com Mon Nov 29 01:13:01 2010 From: ghosh.subhasish at gmail.com (Subhasish Ghosh) Date: Mon, 29 Nov 2010 12:43:01 +0530 Subject: [RFC: PATCH 0/1] da850-evm: Support for TI's PRU CAN Emulation. Message-ID: <1291014782-16124-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. Subhasish Ghosh (1): da850-evm: Support for TI's PRU CAN Emulation. arch/arm/mach-davinci/board-da850-evm.c | 27 ++++++++++++++++++ arch/arm/mach-davinci/da850.c | 34 +++++++++++++++++++---- arch/arm/mach-davinci/devices-da8xx.c | 37 +++++++++++++++++++++++++ arch/arm/mach-davinci/include/mach/da8xx.h | 2 + arch/arm/mach-davinci/include/mach/mux.h | 5 +++ include/linux/can/platform/ti_omapl_pru_can.h | 29 +++++++++++++++++++ 6 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 include/linux/can/platform/ti_omapl_pru_can.h -- 1.7.2.3 From ghosh.subhasish at gmail.com Mon Nov 29 01:13:02 2010 From: ghosh.subhasish at gmail.com (Subhasish Ghosh) Date: Mon, 29 Nov 2010 12:43:02 +0530 Subject: [RFC: PATCH 1/1] da850-evm: Support for TI's PRU CAN Emulation. In-Reply-To: <1291014782-16124-1-git-send-email-subhasish@mistralsolutions.com> References: <1291014782-16124-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1291014782-16124-2-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/board-da850-evm.c | 27 ++++++++++++++++++ arch/arm/mach-davinci/da850.c | 34 +++++++++++++++++++---- arch/arm/mach-davinci/devices-da8xx.c | 37 +++++++++++++++++++++++++ arch/arm/mach-davinci/include/mach/da8xx.h | 2 + arch/arm/mach-davinci/include/mach/mux.h | 5 +++ include/linux/can/platform/ti_omapl_pru_can.h | 29 +++++++++++++++++++ 6 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 include/linux/can/platform/ti_omapl_pru_can.h diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index f89b0b7..e907ef5 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,32 @@ static struct platform_device *da850_evm_devices[] __initdata = { &da850_evm_norflash_device, }; +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 = gpio_request(DA850_PRU_CAN_TRX_PIN, "pru_can_en"); + if (ret) + pr_warning("Cannot open GPIO %d\n", DA850_PRU_CAN_TRX_PIN); + + /* value = 0 to enable the can transceiver */ + gpio_direction_output(DA850_PRU_CAN_TRX_PIN, 0); + 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 diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 63916b9..59a3638 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -238,6 +238,13 @@ static struct clk tptc2_clk = { .flags = ALWAYS_ENABLED, }; +static struct clk pru_clk = { + .name = "pru_ck", + .parent = &pll0_sysclk2, + .lpsc = DA8XX_LPSC0_DMAX, + .flags = ALWAYS_ENABLED, +}; + static struct clk uart0_clk = { .name = "uart0", .parent = &pll0_sysclk2, @@ -373,6 +380,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), @@ -390,12 +398,15 @@ static struct clk_lookup da850_clks[] = { CLK(NULL, NULL, NULL), }; -/* - * Device specific mux setup - * - * soc description mux mode mode mux dbg - * reg offset mask mode +/* soc -> DA850 + * desc -> Pin name, which evaluates to soc##_##desc. + * muxreg -> Pin Multiplexing Control n (PINMUXn) Register number. + * mode_offset -> Bit offset in the register PINMUXn. + * mode_mask -> Number of bits for Pin Multiplexing Control n. + * mux_mode -> Multiplexing mode to set. + * dbg -> debug on/off. */ + static const struct mux_config da850_pins[] = { #ifdef CONFIG_DAVINCI_MUX /* UART0 function */ @@ -514,7 +525,7 @@ static const struct mux_config da850_pins[] = { MUX_CFG(DA850, EMA_A_5, 12, 8, 15, 1, false) MUX_CFG(DA850, EMA_A_6, 12, 4, 15, 1, false) MUX_CFG(DA850, EMA_A_7, 12, 0, 15, 1, false) - MUX_CFG(DA850, EMA_A_8, 11, 28, 15, 1, false) + MUX_CFG(DA850, EMA_A_8, 11, 28, 15, 1, false) MUX_CFG(DA850, EMA_A_9, 11, 24, 15, 1, false) MUX_CFG(DA850, EMA_A_10, 11, 20, 15, 1, false) MUX_CFG(DA850, EMA_A_11, 11, 16, 15, 1, false) @@ -542,7 +553,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) @@ -557,6 +573,12 @@ const short da850_uart0_pins[] __initdata = { -1 }; +const short da850_pru_can_pins[] __initdata = { + DA850_GPIO2_0, DA850_PRU0_R31_0, DA850_PRU1_R30_15, + DA850_PRU1_R31_18, + -1 +}; + const short da850_uart1_pins[] __initdata = { DA850_UART1_RXD, DA850_UART1_TXD, -1 diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index 9eec630..11a9b67 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -85,6 +86,42 @@ 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, + }, +}; + +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, +}; + +/* Info specific to CAN conroller */ +#define OMAPL138_PRU_CAN_VERSION 1 + +static struct ti_pru_can_platform_data ti_pru_can_pdata = { + .version = OMAPL138_PRU_CAN_VERSION, +}; + +int __init da8xx_register_pru_can(void) +{ + omapl138_pru_can_device.dev.platform_data = &ti_pru_can_pdata; + 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..1f1fb64 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, diff --git a/include/linux/can/platform/ti_omapl_pru_can.h b/include/linux/can/platform/ti_omapl_pru_can.h new file mode 100644 index 0000000..0dea436 --- /dev/null +++ b/include/linux/can/platform/ti_omapl_pru_can.h @@ -0,0 +1,29 @@ +/* + * TI OMAPL PRU CAN Emulation device driver + * + * This driver supports TI's PRU CAN Emulation and the + * + * Copyright (C) 2009 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. + */ + + +/** + * struct ti_pru_can_platform_data - PRU CAN platform data + * + * @version: version for future use + * + * Platform data structure to get all platform specific settings. + */ + +struct ti_pru_can_platform_data { + u32 version; +}; -- 1.7.2.3 From nsekhar at ti.com Mon Nov 29 01:44:25 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 29 Nov 2010 13:14:25 +0530 Subject: [PATCH v2 0/6] davinci vpbe: display driver for DM644X In-Reply-To: References: <1290607697-9714-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Hi Murali, On Sat, Nov 27, 2010 at 20:14:46, Muralidharan Karicheri wrote: > Manjunath, > > Could you re-send the patch 1/6? I can't find it either at my inbox or > mailing list. I received the 1/6 in my inbox and also see it on patchwork and gmane. https://patchwork.kernel.org/patch/353081/ http://permalink.gmane.org/gmane.linux.drivers.video-input-infrastructure/25692 Can you please check your spam folder just in case... Thanks, Sekhar From nsekhar at ti.com Mon Nov 29 02:47:12 2010 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 29 Nov 2010 14:17:12 +0530 Subject: [RFC: PATCH 0/1] da850-evm: Support for TI's PRU CAN Emulation. In-Reply-To: <1291014782-16124-1-git-send-email-subhasish@mistralsolutions.com> References: <1291014782-16124-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: Hi Subhasish, On Mon, Nov 29, 2010 at 12:43:01, Subhasish Ghosh wrote: > 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. No need for this cover letter since it is just replicating the information present in patch 1/1 Thanks, Sekhar From ghosh.subhasish at gmail.com Mon Nov 29 05:06:27 2010 From: ghosh.subhasish at gmail.com (Subhasish Ghosh) Date: Mon, 29 Nov 2010 16:36:27 +0530 Subject: [RFC: PATCH 1/1] da850_evm: Support for TI's PRU SoftUART Emulation Message-ID: <1291028787-26042-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/board-da850-evm.c | 20 +++++ arch/arm/mach-davinci/da850.c | 35 ++++++++- arch/arm/mach-davinci/devices-da8xx.c | 104 ++++++++++++++++++++++++++- arch/arm/mach-davinci/include/mach/da8xx.h | 2 + arch/arm/mach-davinci/include/mach/memory.h | 1 + include/linux/serial_core.h | 3 + include/linux/ti_omapl_pru_suart.h | 38 ++++++++++ 7 files changed, 198 insertions(+), 5 deletions(-) create mode 100644 include/linux/ti_omapl_pru_suart.h diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index f89b0b7..4ee09ed 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -736,6 +736,26 @@ static struct edma_rsv_info *da850_edma_rsv[2] = { &da850_edma_cc1_rsv, }; +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_pru_suart_pins); + if (ret) + pr_warning("da850_evm_init: da850_pru_suart_pins" + "mux setup failed: %d\n", ret); + ret = da8xx_register_pru_suart(); + if (ret) + pr_warning("da850_evm_init: pru suart registration" + "failed: %d\n", ret); + return ret; +} + +device_initcall(da850_evm_setup_pru_suart); + static __init void da850_evm_init(void) { int ret; diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 63916b9..a5eeb4f 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -238,6 +238,13 @@ static struct clk tptc2_clk = { .flags = ALWAYS_ENABLED, }; +static struct clk pru_clk = { + .name = "pru_ck", + .parent = &pll0_sysclk2, + .lpsc = DA8XX_LPSC0_DMAX, + .flags = ALWAYS_ENABLED, +}; + static struct clk uart0_clk = { .name = "uart0", .parent = &pll0_sysclk2, @@ -318,6 +325,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 +388,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), @@ -382,6 +398,7 @@ static struct clk_lookup da850_clks[] = { CLK(NULL, "arm", &arm_clk), CLK(NULL, "rmii", &rmii_clk), CLK("davinci_emac.1", NULL, &emac_clk), + CLK(NULL, "mcasp_pru", &mcasp_pru_clk), CLK("davinci-mcasp.0", NULL, &mcasp_clk), CLK("da8xx_lcdc.0", NULL, &lcdc_clk), CLK("davinci_mmc.0", NULL, &mmcsd0_clk), @@ -392,9 +409,13 @@ static struct clk_lookup da850_clks[] = { /* * Device specific mux setup - * - * soc description mux mode mode mux dbg - * reg offset mask mode + * soc -> DA850 + * desc -> Pin name, which evaluates to soc##_##desc. + * muxreg -> Pin Multiplexing Control n (PINMUXn) Register number. + * mode_offset -> Bit offset in the register PINMUXn. + * mode_mask -> Number of bits for Pin Multiplexing Control n. + * mux_mode -> Multiplexing mode to set. + * dbg -> debug on/off */ static const struct mux_config da850_pins[] = { #ifdef CONFIG_DAVINCI_MUX @@ -557,6 +578,14 @@ const short da850_uart0_pins[] __initdata = { -1 }; +const short da850_pru_suart_pins[] __initdata = { + 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 +}; + const short da850_uart1_pins[] __initdata = { DA850_UART1_RXD, DA850_UART1_TXD, -1 diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index 9eec630..3ae9c3e 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -14,7 +14,7 @@ #include #include #include - +#include #include #include #include @@ -85,7 +85,107 @@ 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, +}; + +#define OMAPL138_PRU_SUART_VERSION 1 + +static struct ti_pru_suart_platform_data ti_pru_suart_pdata = { + .version = OMAPL138_PRU_SUART_VERSION, +}; + +int __init da8xx_register_pru_suart(void) +{ + omapl_pru_suart_device.dev.platform_data = &ti_pru_suart_pdata; + 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..be9dc25 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); @@ -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_suart_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/memory.h b/arch/arm/mach-davinci/include/mach/memory.h index 22eb97c..bf490b1 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..b831ebc 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 OMAPL_PRU_SUART 97 + #ifdef __KERNEL__ #include diff --git a/include/linux/ti_omapl_pru_suart.h b/include/linux/ti_omapl_pru_suart.h new file mode 100644 index 0000000..9179f2c --- /dev/null +++ b/include/linux/ti_omapl_pru_suart.h @@ -0,0 +1,38 @@ +/* + * linux/include/linux/ti_omapl_pru_suart.h + */ +#ifndef _LINUX_SUART_OMAPL_PRU_H +#define _LINUX_SUART_OMAPL_PRU_H + +#include +#include + +/* + * TI OMAPL PRU SUART Emulation device driver + * + * This driver supports TI's PRU SUART Emulation and the + * + * Copyright (C) 2009 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. + */ + +/* + * struct ti_pru_suart_platform_data - SUART Platform Data + * + * @version: version for future use + * + * Platform data structure to get all platform specific settings. + */ + +struct ti_pru_suart_platform_data { + u32 version; +}; +#endif -- 1.7.2.3 From sshtylyov at mvista.com Mon Nov 29 05:40:24 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Mon, 29 Nov 2010 14:40:24 +0300 Subject: [RFC: PATCH 1/1] da850-evm: Support for TI's PRU CAN Emulation. In-Reply-To: <1291014782-16124-2-git-send-email-subhasish@mistralsolutions.com> References: <1291014782-16124-1-git-send-email-subhasish@mistralsolutions.com> <1291014782-16124-2-git-send-email-subhasish@mistralsolutions.com> Message-ID: <4CF39128.5000805@mvista.com> Hello. On 29-11-2010 10:13, Subhasish Ghosh wrote: > 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 [...] > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index f89b0b7..e907ef5 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c The board file should be in a patch of its own. > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 63916b9..59a3638 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -238,6 +238,13 @@ static struct clk tptc2_clk = { > .flags = ALWAYS_ENABLED, > }; > > +static struct clk pru_clk = { > + .name = "pru_ck", > + .parent = &pll0_sysclk2, > + .lpsc = DA8XX_LPSC0_DMAX, > + .flags = ALWAYS_ENABLED, Why it's always enabled? > +}; > + > static struct clk uart0_clk = { > .name = "uart0", > .parent =&pll0_sysclk2, > @@ -373,6 +380,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), Don't use spaces to indent and align the initializer with the others please. > @@ -390,12 +398,15 @@ static struct clk_lookup da850_clks[] = { > CLK(NULL, NULL, NULL), > }; > > -/* > - * Device specific mux setup > - * > - * soc description mux mode mode mux dbg > - * reg offset mask mode > +/* soc -> DA850 > + * desc -> Pin name, which evaluates to soc##_##desc. > + * muxreg -> Pin Multiplexing Control n (PINMUXn) Register number. > + * mode_offset -> Bit offset in the register PINMUXn. > + * mode_mask -> Number of bits for Pin Multiplexing Control n. > + * mux_mode -> Multiplexing mode to set. > + * dbg -> debug on/off. Why are you changing this at all? > @@ -514,7 +525,7 @@ static const struct mux_config da850_pins[] = { > MUX_CFG(DA850, EMA_A_5, 12, 8, 15, 1, false) > MUX_CFG(DA850, EMA_A_6, 12, 4, 15, 1, false) > MUX_CFG(DA850, EMA_A_7, 12, 0, 15, 1, false) > - MUX_CFG(DA850, EMA_A_8, 11, 28, 15, 1, false) > + MUX_CFG(DA850, EMA_A_8, 11, 28, 15, 1, false) Why touch this at all? > @@ -542,7 +553,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 */ CAN. And please use tab to, not spaces. > + 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) Please align with the other initializers. And are these all PRU pins? > /* 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) > @@ -557,6 +573,12 @@ const short da850_uart0_pins[] __initdata = { > -1 > }; > > +const short da850_pru_can_pins[] __initdata = { > + DA850_GPIO2_0, DA850_PRU0_R31_0, DA850_PRU1_R30_15, > + DA850_PRU1_R31_18, No, lists in da850.c are generic and associated GPIO pins shouldn't be there -- this list should be placed into the board file. > diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c > index 9eec630..11a9b67 100644 > --- a/arch/arm/mach-davinci/devices-da8xx.c > +++ b/arch/arm/mach-davinci/devices-da8xx.c > @@ -85,6 +86,42 @@ struct platform_device da8xx_serial_device = { [...] > +static struct platform_device omapl138_pru_can_device = { > + .name = "davinci_pru_can", > + .id = 1, Why 1? Is there other device? I don't see them... > diff --git a/include/linux/can/platform/ti_omapl_pru_can.h b/include/linux/can/platform/ti_omapl_pru_can.h > new file mode 100644 > index 0000000..0dea436 > --- /dev/null > +++ b/include/linux/can/platform/ti_omapl_pru_can.h > @@ -0,0 +1,29 @@ I think this file belongs to the driver patch... WBR, Sergei From sshtylyov at mvista.com Mon Nov 29 05:57:05 2010 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Mon, 29 Nov 2010 14:57:05 +0300 Subject: [RFC: PATCH 1/1] da850_evm: Support for TI's PRU SoftUART Emulation In-Reply-To: <1291028787-26042-1-git-send-email-subhasish@mistralsolutions.com> References: <1291028787-26042-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <4CF39511.90601@mvista.com> Hello. On 29-11-2010 14:06, 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/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index f89b0b7..4ee09ed 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c The board file should be in a patch of its own. > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 63916b9..a5eeb4f 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -238,6 +238,13 @@ static struct clk tptc2_clk = { > .flags = ALWAYS_ENABLED, > }; > > +static struct clk pru_clk = { > + .name = "pru_ck", > + .parent = &pll0_sysclk2, > + .lpsc = DA8XX_LPSC0_DMAX, > + .flags = ALWAYS_ENABLED, Why it's always enabled? > @@ -392,9 +409,13 @@ static struct clk_lookup da850_clks[] = { > > /* > * Device specific mux setup > - * > - * soc description mux mode mode mux dbg > - * reg offset mask mode > + * soc -> DA850 > + * desc -> Pin name, which evaluates to soc##_##desc. > + * muxreg -> Pin Multiplexing Control n (PINMUXn) Register number. > + * mode_offset -> Bit offset in the register PINMUXn. > + * mode_mask -> Number of bits for Pin Multiplexing Control n. > + * mux_mode -> Multiplexing mode to set. > + * dbg -> debug on/off Why change this at all? And doesn't this intersect with the patch you've just posted? > @@ -557,6 +578,14 @@ const short da850_uart0_pins[] __initdata = { > -1 > }; > > +const short da850_pru_suart_pins[] __initdata = { > + 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, Don't these pins belong to McASP? > diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c > index 9eec630..3ae9c3e 100644 > --- a/arch/arm/mach-davinci/devices-da8xx.c > +++ b/arch/arm/mach-davinci/devices-da8xx.c > @@ -85,7 +85,107 @@ struct platform_device da8xx_serial_device = { > }, > }; > > -static const s8 da8xx_queue_tc_mapping[][2] = { > + > +#define OMAPL138_PRU_MEM_BASE 0x01C30000 Isn't these already #define'd in your first patch? > +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, Why do you need PSCs? > + }, > + { > + .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, > + }, > +}; What kind of device is this, with so many subdevices and IRQs? > +struct platform_device omapl_pru_suart_device = { > + .name = "davinci_pru_suart", > + .id = 1, Why 1? Is there another device? I don't see it... > + .num_resources = ARRAY_SIZE(omapl138_pru_suart_resources), > + .resource = omapl138_pru_suart_resources, Please align the initialzers properly. > diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h > index 212eb4c..b831ebc 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 OMAPL_PRU_SUART 97 Shouldn't these constants have PORT_ as a prefix? > diff --git a/include/linux/ti_omapl_pru_suart.h b/include/linux/ti_omapl_pru_suart.h > new file mode 100644 > index 0000000..9179f2c > --- /dev/null > +++ b/include/linux/ti_omapl_pru_suart.h I think this file belongs with the driver patch. Also, you could place it into arch/arm/mach-davinci/include/mach... > @@ -0,0 +1,38 @@ > +/* > + * linux/include/linux/ti_omapl_pru_suart.h File names in comments have long been frowned upon. > + */ > +#ifndef _LINUX_SUART_OMAPL_PRU_H > +#define _LINUX_SUART_OMAPL_PRU_H > + > +#include > +#include > + > +/* > + * TI OMAPL PRU SUART Emulation device driver > + * > + * This driver supports TI's PRU SUART Emulation and the > + * > + * Copyright (C) 2009 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. Copyright should precede the #include's, not follow them. WBR, Sergei From ghosh.subhasish at gmail.com Mon Nov 29 07:22:18 2010 From: ghosh.subhasish at gmail.com (Subhasish Ghosh) Date: Mon, 29 Nov 2010 05:22:18 -0800 (PST) Subject: [RFC: PATCH 1/1] da850-evm: Support for TI's PRU CAN Emulation. In-Reply-To: <4CF39128.5000805@mvista.com> References: <1291014782-16124-1-git-send-email-subhasish@mistralsolutions.com> <1291014782-16124-2-git-send-email-subhasish@mistralsolutions.com> <4CF39128.5000805@mvista.com> Message-ID: <1291036938296-5784294.post@n2.nabble.com> Sergei Shtylyov-2 wrote: > > Hello. > > On 29-11-2010 10:13, Subhasish Ghosh wrote: > >> 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 > [...] > >> diff --git a/arch/arm/mach-davinci/board-da850-evm.c >> b/arch/arm/mach-davinci/board-da850-evm.c >> index f89b0b7..e907ef5 100644 >> --- a/arch/arm/mach-davinci/board-da850-evm.c >> +++ b/arch/arm/mach-davinci/board-da850-evm.c > > The board file should be in a patch of its own. > > >> diff --git a/arch/arm/mach-davinci/da850.c >> b/arch/arm/mach-davinci/da850.c >> index 63916b9..59a3638 100644 >> --- a/arch/arm/mach-davinci/da850.c >> +++ b/arch/arm/mach-davinci/da850.c >> @@ -238,6 +238,13 @@ static struct clk tptc2_clk = { >> .flags = ALWAYS_ENABLED, >> }; >> >> +static struct clk pru_clk = { >> + .name = "pru_ck", >> + .parent = &pll0_sysclk2, >> + .lpsc = DA8XX_LPSC0_DMAX, >> + .flags = ALWAYS_ENABLED, > > Why it's always enabled? > [SG] - As per the L138 Datasheet, this falls under "Always On" domain. > >> +}; >> + >> static struct clk uart0_clk = { >> .name = "uart0", >> .parent =&pll0_sysclk2, >> @@ -373,6 +380,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), > > Don't use spaces to indent and align the initializer with the others > please. > >> @@ -390,12 +398,15 @@ static struct clk_lookup da850_clks[] = { >> CLK(NULL, NULL, NULL), >> }; >> >> -/* >> - * Device specific mux setup >> - * >> - * soc description mux mode mode mux dbg >> - * reg offset mask mode >> +/* soc -> DA850 >> + * desc -> Pin name, which evaluates to soc##_##desc. >> + * muxreg -> Pin Multiplexing Control n (PINMUXn) Register >> number. >> + * mode_offset -> Bit offset in the register PINMUXn. >> + * mode_mask -> Number of bits for Pin Multiplexing Control n. >> + * mux_mode -> Multiplexing mode to set. >> + * dbg -> debug on/off. > > Why are you changing this at all? > >> @@ -514,7 +525,7 @@ static const struct mux_config da850_pins[] = { >> MUX_CFG(DA850, EMA_A_5, 12, 8, 15, 1, false) >> MUX_CFG(DA850, EMA_A_6, 12, 4, 15, 1, false) >> MUX_CFG(DA850, EMA_A_7, 12, 0, 15, 1, false) >> - MUX_CFG(DA850, EMA_A_8, 11, 28, 15, 1, false) >> + MUX_CFG(DA850, EMA_A_8, 11, 28, 15, 1, false) > > Why touch this at all? > >> @@ -542,7 +553,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 */ > > CAN. And please use tab to, not spaces. > >> + 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) > > Please align with the other initializers. And are these all PRU pins? > [SG] - Yes, all of these are PRU pins. > >> /* 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) >> @@ -557,6 +573,12 @@ const short da850_uart0_pins[] __initdata = { >> -1 >> }; >> >> +const short da850_pru_can_pins[] __initdata = { >> + DA850_GPIO2_0, DA850_PRU0_R31_0, DA850_PRU1_R30_15, >> + DA850_PRU1_R31_18, > > No, lists in da850.c are generic and associated GPIO pins shouldn't be > there -- this list should be placed into the board file. > >> diff --git a/arch/arm/mach-davinci/devices-da8xx.c >> b/arch/arm/mach-davinci/devices-da8xx.c >> index 9eec630..11a9b67 100644 >> --- a/arch/arm/mach-davinci/devices-da8xx.c >> +++ b/arch/arm/mach-davinci/devices-da8xx.c >> @@ -85,6 +86,42 @@ struct platform_device da8xx_serial_device = { > [...] >> +static struct platform_device omapl138_pru_can_device = { >> + .name = "davinci_pru_can", >> + .id = 1, > > Why 1? Is there other device? I don't see them... > >> diff --git a/include/linux/can/platform/ti_omapl_pru_can.h >> b/include/linux/can/platform/ti_omapl_pru_can.h >> new file mode 100644 >> index 0000000..0dea436 >> --- /dev/null >> +++ b/include/linux/can/platform/ti_omapl_pru_can.h >> @@ -0,0 +1,29 @@ > > I think this file belongs to the driver patch... > [SG] -- This header file is included in devices-da8xx.c. > > 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 > > -- View this message in context: http://davinci-linux-open-source.1494791.n2.nabble.com/RFC-PATCH-0-1-da850-evm-Support-for-TI-s-PRU-CAN-Emulation-tp5783456p5784294.html Sent from the davinci-linux-open-source mailing list archive at Nabble.com. From ghosh.subhasish at gmail.com Mon Nov 29 07:42:52 2010 From: ghosh.subhasish at gmail.com (Subhasish Ghosh) Date: Mon, 29 Nov 2010 05:42:52 -0800 (PST) Subject: [RFC: PATCH 1/1] da850_evm: Support for TI's PRU SoftUART Emulation In-Reply-To: <4CF39511.90601@mvista.com> References: <1291028787-26042-1-git-send-email-subhasish@mistralsolutions.com> <4CF39511.90601@mvista.com> Message-ID: <1291038172032-5784351.post@n2.nabble.com> Sergei Shtylyov-2 wrote: > > Hello. > > On 29-11-2010 14:06, 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/board-da850-evm.c >> b/arch/arm/mach-davinci/board-da850-evm.c >> index f89b0b7..4ee09ed 100644 >> --- a/arch/arm/mach-davinci/board-da850-evm.c >> +++ b/arch/arm/mach-davinci/board-da850-evm.c > > The board file should be in a patch of its own. > >> diff --git a/arch/arm/mach-davinci/da850.c >> b/arch/arm/mach-davinci/da850.c >> index 63916b9..a5eeb4f 100644 >> --- a/arch/arm/mach-davinci/da850.c >> +++ b/arch/arm/mach-davinci/da850.c >> @@ -238,6 +238,13 @@ static struct clk tptc2_clk = { >> .flags = ALWAYS_ENABLED, >> }; >> >> +static struct clk pru_clk = { >> + .name = "pru_ck", >> + .parent = &pll0_sysclk2, >> + .lpsc = DA8XX_LPSC0_DMAX, >> + .flags = ALWAYS_ENABLED, > > Why it's always enabled? > [SG] - As per the L138 Datasheet, this falls under "Always On" domain. > >> @@ -392,9 +409,13 @@ static struct clk_lookup da850_clks[] = { >> >> /* >> * Device specific mux setup >> - * >> - * soc description mux mode mode mux dbg >> - * reg offset mask mode >> + * soc -> DA850 >> + * desc -> Pin name, which evaluates to soc##_##desc. >> + * muxreg -> Pin Multiplexing Control n (PINMUXn) Register number. >> + * mode_offset -> Bit offset in the register PINMUXn. >> + * mode_mask -> Number of bits for Pin Multiplexing Control n. >> + * mux_mode -> Multiplexing mode to set. >> + * dbg -> debug on/off > > Why change this at all? And doesn't this intersect with the patch > you've > just posted? > >> @@ -557,6 +578,14 @@ const short da850_uart0_pins[] __initdata = { >> -1 >> }; >> >> +const short da850_pru_suart_pins[] __initdata = { >> + 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, > > Don't these pins belong to McASP? > [SG] -- Yes, To emulate the Soft UART we use the McASP as serializers. > >> diff --git a/arch/arm/mach-davinci/devices-da8xx.c >> b/arch/arm/mach-davinci/devices-da8xx.c >> index 9eec630..3ae9c3e 100644 >> --- a/arch/arm/mach-davinci/devices-da8xx.c >> +++ b/arch/arm/mach-davinci/devices-da8xx.c >> @@ -85,7 +85,107 @@ struct platform_device da8xx_serial_device = { >> }, >> }; >> >> -static const s8 da8xx_queue_tc_mapping[][2] = { >> + >> +#define OMAPL138_PRU_MEM_BASE 0x01C30000 > > Isn't these already #define'd in your first patch? > >> +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, > > Why do you need PSCs? > [SG] - These are required by the PRU firmware to enable McASPs. > >> + }, >> + { >> + .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, >> + }, >> +}; > > What kind of device is this, with so many subdevices and IRQs? > [SG] -- We are emulating eight UARTs on the two PRUs available in OMAPL. > The interrupts are for each of the eight UARTs. For the application, > the visibility is a single controller emulating eight UARTs. > >> +struct platform_device omapl_pru_suart_device = { >> + .name = "davinci_pru_suart", >> + .id = 1, > > Why 1? Is there another device? I don't see it... > >> + .num_resources = ARRAY_SIZE(omapl138_pru_suart_resources), >> + .resource = omapl138_pru_suart_resources, > > Please align the initialzers properly. > >> diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h >> index 212eb4c..b831ebc 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 OMAPL_PRU_SUART 97 > > Shouldn't these constants have PORT_ as a prefix? > >> diff --git a/include/linux/ti_omapl_pru_suart.h >> b/include/linux/ti_omapl_pru_suart.h >> new file mode 100644 >> index 0000000..9179f2c >> --- /dev/null >> +++ b/include/linux/ti_omapl_pru_suart.h > > I think this file belongs with the driver patch. Also, you could place > it > into arch/arm/mach-davinci/include/mach... > [SG] -- This header file is included in devices-da8xx.c. > >> @@ -0,0 +1,38 @@ >> +/* >> + * linux/include/linux/ti_omapl_pru_suart.h > > File names in comments have long been frowned upon. > >> + */ >> +#ifndef _LINUX_SUART_OMAPL_PRU_H >> +#define _LINUX_SUART_OMAPL_PRU_H >> + >> +#include >> +#include >> + >> +/* >> + * TI OMAPL PRU SUART Emulation device driver >> + * >> + * This driver supports TI's PRU SUART Emulation and the >> + * >> + * Copyright (C) 2009 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. > > Copyright should precede the #include's, not follow them. > > 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 > > -- View this message in context: http://davinci-linux-open-source.1494791.n2.nabble.com/RFC-PATCH-1-1-da850-evm-Support-for-TI-s-PRU-SoftUART-Emulation-tp5783929p5784351.html Sent from the davinci-linux-open-source mailing list archive at Nabble.com. From manjunath.hadli at ti.com Mon Nov 29 07:47:07 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Mon, 29 Nov 2010 19:17:07 +0530 Subject: davinci vpbe: How to use 1920 x 1080 mode with osd0 on DM644X? In-Reply-To: References: Message-ID: Hello Thomas, I am not sure if OSD can support 1920 x 1080. If you see the constraints section of the VPBE User guide for Dm644x (http://focus.ti.com/general/docs/lit/getliterature.tsp?literatureNumber=sprue37c&fileType=pdf ), you would see that the total bandwidth cannot exceed 25Mbps, which , with 1920x1080 would easily exceed at even lower refresh rates. Thanks and Regards, -Manju ________________________________________ From: davinci-linux-open-source-bounces at linux.davincidsp.com [davinci-linux-open-source-bounces at linux.davincidsp.com] On Behalf Of DISTEC Kloiber Thomas [Kloiber at distec.de] Sent: Thursday, November 25, 2010 10:43 PM To: davinci-linux-open-source at linux.davincidsp.com Subject: davinci vpbe: How to use 1920 x 1080 mode with osd0 on DM644X? Hi all, I'm currently using MV kernel 2.6.18 from DVSDK 2.0.0.22 on DM6443. I know, that e2e forum is the better place for this kernel version and I already posted my question there as well some days ago. I did see that there is some work going on here on davinci vpbe and may be I can find some help here. I want to use 1920 x 1080 mode for osd0 fb on DM644X. My boot args are ..... video=davincifb:osd0=1920x1080x16,8100K ... But I'm not able to use more than 960 lines. The max. mode I can select with fbset is 1920 x 960. Does anyone know the reason for this limitation? How can 1080 lines be supported? Does the git kernel have the same limitation? Thanks and best regards Thomas _______________________________________________ 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 Mon Nov 29 09:57:38 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Mon, 29 Nov 2010 21:27:38 +0530 Subject: [PATCH v2 5/6] davinci vpbe: platform specific additions In-Reply-To: Message-ID: On Sat, Nov 27, 2010 at 20:52:14, Muralidharan Karicheri wrote: > > +static int dm644x_set_if_config(enum v4l2_mbus_pixelcode pixcode) { > > + ? ? ? unsigned int val = 0; > > + ? ? ? int ret = 0; > > + > > + ? ? ? switch (pixcode) { > > + ? ? ? case V4L2_MBUS_FMT_FIXED: > > + ? ? ? ? ? ? /* Analog out.do nothing */ > > + ? ? ? ? ? ? break; > > + ? ? ? case V4L2_MBUS_FMT_YUYV8_2X8: > > + ? ? ? ? ? ? ? /* BT656 */ > > + ? ? ? ? ? ? ? val = (1<<12); > > + ? ? ? ? ? ? ? /*set VDMD in VMOD */ > > + ? ? ? ? ? ? ? dm644x_reg_modify(venc_vmod_reg, val, (7 << 12)); > > + ? ? ? ? ? ? ? /* Set YCCTL */ > > + ? ? ? ? ? ? ? dm644x_reg_modify(venc_ycctl_reg, 1, 1); > > + ? ? ? ? ? ? ? break; > > + ? ? ? case V4L2_MBUS_FMT_YUYV10_1X20: > > + ? ? ? /* This was VPBE_DIGITAL_IF_YCC16.BT656.Replace the enum > > +accordingly > > + ? ? ? * when the right one gets into open source */ > > + ? ? ? ? ? ? ? val = 0 << 12; > > + ? ? ? ? ? ? ? dm644x_reg_modify(venc_vmod_reg, val, (7 << 12)); > > + ? ? ? ? ? ? ? dm644x_reg_modify(venc_ycctl_reg, 1, 1); > > + ? ? ? ? ? ? ? break; > > + ? ? ? case V4L2_MBUS_FMT_SGRBG8_1X8: > > + ? ? ? /* This was VPBE_DIGITAL_IF_PRGB/SRGB.Replace the enum > > +accordingly > > + ? ? ? * when the right one gets into open source */ > > + ? ? ? ? ? ? ? val = 2 << 12; > > + ? ? ? ? ? ? ? dm644x_reg_modify(venc_vmod_reg, val, (7 << 12)); > > + ? ? ? ? ? ? ? break; > > + ? ? ? default: > > + ? ? ? ? ? ? ? ret = -EINVAL; > > + ? ? ? ? ? ? ? break; > > + ? ? ? } > > + ? ? ? return ret; > > +} > > The media bus format was added to sub device interface sometime back to >configure the media data format (8 bit, 16 bit etc) and there is also a set >of APIs to set the pad configuration (done by Laurent) which is being >reviewed. In the context of that, I believe the venc VMOD configuration can >be handled by implementing s_mbus_fmt() sub device API at venc. Currently >OSD sub device has set_layer_config() which consists of setting the pixel >format, frame format and sub-frame(crop). I think this can be replaced by >s_mbus_fmt(). So on a board file, we could define the pad configuration >which will include the mbus code such as V4L2_MBUS_FMT_YUYV8_2X8 + addition >pad configurations such as embedded sync enabled etc. So once sub device is >selected, vpbe controller will set the pad configuration (obtained from >board file for each of the ?). The pad in this case is the link between >venc lcd port to external encoder. When S_FMT/S_CROP is called on the video >node, the controller calls s_mbus_fmt() on the osd and venc sub device to >set the mbus formats. [Manju] I changed the implementation currently only with the view of reducing the privately defined enumerations which was one of the comments by Hans. I am following Laurent's patch series, and it is a good idea to get to s_mbus_fmt when it gets in. Till then we can keep this. In the same regard, I asked Laurent to suggest the correct enums for the RGB666 interfaces. I have no problem in removing this call now and re-introducing it as part of next patch series. I hope by that time Laurent's patch would get in too. > > Since the external encoder support is a TBD, I am okay if you implement this after this patch series is merged to the tree. > > Murali > > + > > +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_pinmux = dm644x_vpbe_setup_pinmux, > > + ? ? ? .setup_clock = dm644x_venc_setup_clock, > > + ? ? ? .setup_if_config = dm644x_set_if_config, }; > > + > > +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 +977,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 > > > > -- > > 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 > > > > > > -- > Murali Karicheri > mkaricheri at gmail.com > From manjunath.hadli at ti.com Mon Nov 29 10:01:46 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Mon, 29 Nov 2010 21:31:46 +0530 Subject: [PATCH v2 5/6] davinci vpbe: platform specific additions Message-ID: Resending the reply with proper indentation.. On Mon, Nov 29, 2010 at 21:27:39, Hadli, Manjunath wrote: > On Sat, Nov 27, 2010 at 20:52:14, Muralidharan Karicheri wrote: > > > +static int dm644x_set_if_config(enum v4l2_mbus_pixelcode pixcode) { > > > + ? ? ? unsigned int val = 0; > > > + ? ? ? int ret = 0; > > > + > > > + ? ? ? switch (pixcode) { > > > + ? ? ? case V4L2_MBUS_FMT_FIXED: > > > + ? ? ? ? ? ? /* Analog out.do nothing */ > > > + ? ? ? ? ? ? break; > > > + ? ? ? case V4L2_MBUS_FMT_YUYV8_2X8: > > > + ? ? ? ? ? ? ? /* BT656 */ > > > + ? ? ? ? ? ? ? val = (1<<12); > > > + ? ? ? ? ? ? ? /*set VDMD in VMOD */ > > > + ? ? ? ? ? ? ? dm644x_reg_modify(venc_vmod_reg, val, (7 << 12)); > > > + ? ? ? ? ? ? ? /* Set YCCTL */ > > > + ? ? ? ? ? ? ? dm644x_reg_modify(venc_ycctl_reg, 1, 1); > > > + ? ? ? ? ? ? ? break; > > > + ? ? ? case V4L2_MBUS_FMT_YUYV10_1X20: > > > + ? ? ? /* This was VPBE_DIGITAL_IF_YCC16.BT656.Replace the enum > > > +accordingly > > > + ? ? ? * when the right one gets into open source */ > > > + ? ? ? ? ? ? ? val = 0 << 12; > > > + ? ? ? ? ? ? ? dm644x_reg_modify(venc_vmod_reg, val, (7 << 12)); > > > + ? ? ? ? ? ? ? dm644x_reg_modify(venc_ycctl_reg, 1, 1); > > > + ? ? ? ? ? ? ? break; > > > + ? ? ? case V4L2_MBUS_FMT_SGRBG8_1X8: > > > + ? ? ? /* This was VPBE_DIGITAL_IF_PRGB/SRGB.Replace the enum > > > +accordingly > > > + ? ? ? * when the right one gets into open source */ > > > + ? ? ? ? ? ? ? val = 2 << 12; > > > + ? ? ? ? ? ? ? dm644x_reg_modify(venc_vmod_reg, val, (7 << 12)); > > > + ? ? ? ? ? ? ? break; > > > + ? ? ? default: > > > + ? ? ? ? ? ? ? ret = -EINVAL; > > > + ? ? ? ? ? ? ? break; > > > + ? ? ? } > > > + ? ? ? return ret; > > > +} > > > > The media bus format was added to sub device interface sometime back to >configure the media data format (8 bit, 16 bit etc) and there is also a set >of APIs to set the pad configuration (done by Laurent) which is being >reviewed. In the context of that, I believe the venc VMOD configuration can >be handled by implementing s_mbus_fmt() sub device API at venc. Currently >OSD sub device has set_layer_config() which consists of setting the pixel >format, frame format and sub-frame(crop). I think this can be replaced by >s_mbus_fmt(). So on a board file, we could define the pad configuration >which will include the mbus code such as V4L2_MBUS_FMT_YUYV8_2X8 + addition >pad configurations such as embedded sync enabled etc. So once sub device is >selected, vpbe controller will set the pad configuration (obtained from >board file for each of the ?). The pad in this case is the link between >venc lcd port to external encoder. When S_FMT/S_CROP is called on the video >node, the controller calls s_mbus_fmt() on the osd and venc sub device to >set the mbus formats. > [Manju] I changed the implementation currently only with the view of reducing the privately defined enumerations which was one of the comments by Hans. I am following Laurent's patch series, and it is a good idea to get to s_mbus_fmt when it gets in. Till then we can keep this. In the same regard, I asked Laurent to suggest the correct enums for the RGB666 interfaces. I have no problem in removing this call now and re-introducing it as part of next patch series. I hope by that time Laurent's patch would get in too. > > > > > Since the external encoder support is a TBD, I am okay if you implement this after this patch series is merged to the tree. > > > > Murali > > > + > > > +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_pinmux = dm644x_vpbe_setup_pinmux, > > > + ? ? ? .setup_clock = dm644x_venc_setup_clock, > > > + ? ? ? .setup_if_config = dm644x_set_if_config, }; > > > + > > > +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 +977,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 > > > > > > -- > > > 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 > > > > > > > > > > > -- > > Murali Karicheri > > mkaricheri at gmail.com > > > > From manjunath.hadli at ti.com Mon Nov 29 10:03:45 2010 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Mon, 29 Nov 2010 21:33:45 +0530 Subject: [PATCH v2 3/6] davinci vpbe: OSD(On Screen Display) block In-Reply-To: Message-ID: On Sat, Nov 27, 2010 at 21:07:24, Muralidharan Karicheri wrote: > Manjunath, > > I think set_layer_config(), get_layer_config and try_layer_config() > can be split into standard fmt and crop sub device video ops. Could > you investigate this? > > Murali > Murali, Thank you for the comments. I will investigate. > > On Wed, Nov 24, 2010 at 9:09 AM, Manjunath Hadli wrote: > > 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 | 1208 +++++++++++++++++++++++++++ > > drivers/media/video/davinci/vpbe_osd_regs.h | 389 +++++++++ > > include/media/davinci/vpbe_osd.h | 397 +++++++++ > > 3 files changed, 1994 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..64371b5 > > --- /dev/null > > +++ b/drivers/media/video/davinci/vpbe_osd.c > > @@ -0,0 +1,1208 @@ > > +/* > > + * 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_C