diff --git a/projects/RPi/patches/linux/linux-01-RPi_support-a2a4960.patch b/projects/RPi/patches/linux/linux-01-RPi_support-8b2af1c.patch similarity index 90% rename from projects/RPi/patches/linux/linux-01-RPi_support-a2a4960.patch rename to projects/RPi/patches/linux/linux-01-RPi_support-8b2af1c.patch index e9addac07f..3678402bfa 100644 --- a/projects/RPi/patches/linux/linux-01-RPi_support-a2a4960.patch +++ b/projects/RPi/patches/linux/linux-01-RPi_support-8b2af1c.patch @@ -1,11 +1,11 @@ -From 06783b43efa0a0e80f87cfb87f809b04e490bf8b Mon Sep 17 00:00:00 2001 +From a7a41e08412068ce87101b1f2a69d8819177b283 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Sun, 12 May 2013 12:24:19 +0100 -Subject: [PATCH 01/82] Main bcm2708 linux port +Subject: [PATCH 01/91] Main bcm2708 linux port Signed-off-by: popcornmix --- - arch/arm/Kconfig | 16 + + arch/arm/Kconfig | 17 + arch/arm/Kconfig.debug | 8 + arch/arm/Makefile | 1 + arch/arm/configs/bcmrpi_cutdown_defconfig | 474 +++++++ @@ -56,7 +56,7 @@ Signed-off-by: popcornmix drivers/mmc/host/sdhci.h | 37 + drivers/tty/serial/amba-pl011.c | 2 +- include/linux/mmc/sdhci.h | 2 + - 51 files changed, 7794 insertions(+), 72 deletions(-) + 51 files changed, 7795 insertions(+), 72 deletions(-) create mode 100644 arch/arm/configs/bcmrpi_cutdown_defconfig create mode 100644 arch/arm/configs/bcmrpi_defconfig create mode 100644 arch/arm/configs/bcmrpi_emergency_defconfig @@ -97,10 +97,10 @@ Signed-off-by: popcornmix create mode 100644 drivers/mmc/host/sdhci-bcm2708.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index 1ad6fb6..54da673 100644 +index 1ad6fb6..e28d7b2 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig -@@ -368,6 +368,21 @@ config ARCH_AT91 +@@ -368,6 +368,22 @@ config ARCH_AT91 This enables support for systems based on Atmel AT91RM9200 and AT91SAM9* processors. @@ -110,6 +110,7 @@ index 1ad6fb6..54da673 100644 + select ARM_AMBA + select HAVE_CLK + select HAVE_SCHED_CLOCK ++ select NEED_MACH_GPIO_H + select NEED_MACH_MEMORY_H + select CLKDEV_LOOKUP + select GENERIC_CLOCKEVENTS @@ -122,7 +123,7 @@ index 1ad6fb6..54da673 100644 config ARCH_CLPS711X bool "Cirrus Logic CLPS711x/EP721x/EP731x-based" select ARCH_REQUIRE_GPIOLIB -@@ -1043,6 +1058,7 @@ source "arch/arm/mach-virt/Kconfig" +@@ -1043,6 +1059,7 @@ source "arch/arm/mach-virt/Kconfig" source "arch/arm/mach-vt8500/Kconfig" source "arch/arm/mach-w90x900/Kconfig" @@ -1696,7 +1697,7 @@ index 0000000..3b40c49 +CONFIG_CRC_ITU_T=y +CONFIG_LIBCRC32C=y diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c -index 94f6b05..dd67686 100644 +index 92f7b15..7b5ed03 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -176,6 +176,16 @@ void arch_cpu_idle(void) @@ -8590,13 +8591,13 @@ index 3e781b8..aad2393 100644 unsigned int data_early:1; /* Data finished before cmd */ -- -1.8.4 +1.8.5.1 -From 0c2cc902b9a01445585e9715ca2bf36a902467cf Mon Sep 17 00:00:00 2001 +From ffd85339c2b40d74ec1a8cbf7a957884f0a2ac25 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Tue, 7 May 2013 22:20:24 +0100 -Subject: [PATCH 02/82] Add quick config. +Subject: [PATCH 02/91] Add quick config. This is designed for quick compiling when developing. No modules are needed and it includes all Pi specific drivers @@ -8809,13 +8810,13 @@ index 0000000..e5efe75 +CONFIG_CRC_ITU_T=y +CONFIG_LIBCRC32C=y -- -1.8.4 +1.8.5.1 -From f5449b637e752281bb486374b1b13d540375a807 Mon Sep 17 00:00:00 2001 +From ef917ffb8f230dcb50638f3109a0a59288051654 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 1 May 2013 19:46:17 +0100 -Subject: [PATCH 03/82] Add dwc_otg driver +Subject: [PATCH 03/91] Add dwc_otg driver Signed-off-by: popcornmix --- @@ -65884,13 +65885,13 @@ index 0000000..cdc9963 +test_main(); +0; -- -1.8.4 +1.8.5.1 -From 0de58221dbee59bcb4b3ed3b2ddf3ef74bb10b7b Mon Sep 17 00:00:00 2001 +From a6d142fdbb59fc245779df8c5328e21df890fd42 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 1 May 2013 19:54:32 +0100 -Subject: [PATCH 04/82] bcm2708 watchdog driver +Subject: [PATCH 04/91] bcm2708 watchdog driver Signed-off-by: popcornmix --- @@ -66320,13 +66321,13 @@ index 0000000..2f19203 +MODULE_ALIAS_MISCDEV(TEMP_MINOR); +MODULE_LICENSE("GPL"); -- -1.8.4 +1.8.5.1 -From 6108fa91cd84340c79bf7bca8f5505449c017b55 Mon Sep 17 00:00:00 2001 +From 79bf8b376c62ead1b7db73419d08fb0fa80b0c53 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 1 May 2013 19:55:09 +0100 -Subject: [PATCH 05/82] bcm2708 framebuffer driver +Subject: [PATCH 05/91] bcm2708 framebuffer driver Signed-off-by: popcornmix --- @@ -69362,13 +69363,13 @@ index 3c14e43..7626beb 100644 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 -- -1.8.4 +1.8.5.1 -From 4d1525a5865dfc9a110d202b416c5c46de21f01b Mon Sep 17 00:00:00 2001 +From f66a46728dd6561fdb01b1e56829cff8c440ad56 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Tue, 2 Jul 2013 23:42:01 +0100 -Subject: [PATCH 06/82] bcm2708 vchiq driver +Subject: [PATCH 06/91] bcm2708 vchiq driver Signed-off-by: popcornmix --- @@ -81864,13 +81865,13 @@ index 0000000..b6bfa21 + return vchiq_build_time; +} -- -1.8.4 +1.8.5.1 -From 03dde57ea271342aa5012a643263b43ea95c89eb Mon Sep 17 00:00:00 2001 +From b32aca932b5bc99b508a1973e242e308c6174f1a Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 3 Jul 2013 00:31:47 +0100 -Subject: [PATCH 07/82] cma: Add vc_cma driver to enable use of CMA +Subject: [PATCH 07/91] cma: Add vc_cma driver to enable use of CMA Signed-off-by: popcornmix --- @@ -81914,7 +81915,7 @@ index 7ff1d0d..fc85f0c 100644 +obj-$(CONFIG_BRCM_CHAR_DRIVERS) += broadcom/ diff --git a/drivers/char/broadcom/Kconfig b/drivers/char/broadcom/Kconfig new file mode 100644 -index 0000000..e23b440 +index 0000000..f089943 --- /dev/null +++ b/drivers/char/broadcom/Kconfig @@ -0,0 +1,16 @@ @@ -81929,7 +81930,7 @@ index 0000000..e23b440 + +config BCM_VC_CMA + bool "Videocore CMA" -+ depends on CMA && BRCM_CHAR_DRIVERS ++ depends on CMA && BRCM_CHAR_DRIVERS && BCM2708_VCHIQ + default n + help + Helper for videocore CMA access. @@ -83156,13 +83157,13 @@ index 0000000..5325832 + +#endif /* VC_CMA_H */ -- -1.8.4 +1.8.5.1 -From bf751558ab1a40b021d5900966659bfa16ecb335 Mon Sep 17 00:00:00 2001 +From da006e46cfdac4b58647add283488aa71dd97b43 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Mon, 26 Mar 2012 22:15:50 +0100 -Subject: [PATCH 08/82] bcm2708: alsa sound driver +Subject: [PATCH 08/91] bcm2708: alsa sound driver Signed-off-by: popcornmix --- @@ -85481,13 +85482,13 @@ index 0000000..af3e6eb + +#endif // _VC_AUDIO_DEFS_H_ -- -1.8.4 +1.8.5.1 -From 52911e377567e105431b80ea1167df8f58de065d Mon Sep 17 00:00:00 2001 +From 8c0811cfb655295da58469c49c4bb82467398db9 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Tue, 26 Mar 2013 17:26:38 +0000 -Subject: [PATCH 09/82] Allow mac address to be set in smsc95xx +Subject: [PATCH 09/91] Allow mac address to be set in smsc95xx Signed-off-by: popcornmix --- @@ -85578,170 +85579,374 @@ index 3f38ba8..60076fe 100644 if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, dev->net->dev_addr) == 0) { -- -1.8.4 +1.8.5.1 -From afd4b59ba5e3bf9118bf9ea07282707d89d80ba5 Mon Sep 17 00:00:00 2001 +From 2f53106dec15b1d2943d5ebdfe3dc663772c0968 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Tue, 8 May 2012 23:12:13 +0100 -Subject: [PATCH 10/82] possible fix for sdcard missing status. Thank naren +Subject: [PATCH 10/91] possible fix for sdcard missing status. Thank naren ---- - drivers/mmc/host/sdhci-bcm2708.c | 9 +++++++++ - 1 file changed, 9 insertions(+) +sdcard patch improvements from naren -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index d8ef77c..0514bc1 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -1173,6 +1173,14 @@ static unsigned int sdhci_bcm2708_uhs_broken(struct sdhci_host *host) - return 1; - } - -+static unsigned int sdhci_bcm2708_missing_status(struct sdhci_host *host) -+{ -+ if(host->last_cmdop == MMC_SEND_STATUS) -+ return 1; -+ else -+ return 0; -+} -+ - /***************************************************************************** \ - * * - * Device ops * -@@ -1206,6 +1214,7 @@ static unsigned int sdhci_bcm2708_uhs_broken(struct sdhci_host *host) - .spurious_crc_acmd51 = sdhci_bcm2708_quirk_spurious_crc, - .voltage_broken = sdhci_bcm2708_quirk_voltage_broken, - .uhs_broken = sdhci_bcm2708_uhs_broken, -+ .missing_status = sdhci_bcm2708_missing_status, - }; - - /*****************************************************************************\ --- -1.8.4 - - -From addaed5ebe3d27dedfd0cb666e7098eaf58f5612 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Thu, 17 May 2012 14:44:19 +0100 -Subject: [PATCH 11/82] sdcard patch improvements from naren - ---- - drivers/mmc/host/sdhci-bcm2708.c | 23 +++++++---------------- - 1 file changed, 7 insertions(+), 16 deletions(-) - -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index 0514bc1..7ba715b 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -886,8 +886,7 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host, - We get CRC and DEND errors unless we wait for - the SD controller to finish reading/writing to the card. */ - u32 state_mask; -- int timeout=1000000; -- hptime_t now = hptime(); -+ int timeout=1000; - - DBG("PDMA over - sync card\n"); - if (data->flags & MMC_DATA_READ) -@@ -895,17 +894,12 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host, - else - state_mask = SDHCI_DOING_WRITE; - -- while (0 != (sdhci_bcm2708_raw_readl(host, -- SDHCI_PRESENT_STATE) & -- state_mask) && --timeout > 0) -+ while (0 != (sdhci_bcm2708_raw_readl(host, SDHCI_PRESENT_STATE) -+ & state_mask) && --timeout > 0) -+ { -+ udelay(100); - continue; -- -- if (1000000-timeout > 4000) /*ave. is about 3250*/ -- DBG("%s: note - long %s sync %luns - " -- "%d its.\n", -- mmc_hostname(host->mmc), -- data->flags & MMC_DATA_READ? "read": "write", -- since_ns(now), 1000000-timeout); -+ } - if (timeout <= 0) - printk(KERN_ERR"%s: final %s to SD card still " - "running\n", -@@ -1175,10 +1169,7 @@ static unsigned int sdhci_bcm2708_uhs_broken(struct sdhci_host *host) - - static unsigned int sdhci_bcm2708_missing_status(struct sdhci_host *host) - { -- if(host->last_cmdop == MMC_SEND_STATUS) -- return 1; -- else -- return 0; -+ return 1; - } - - /***************************************************************************** \ --- -1.8.4 - - -From 0f1bf0d00853b2f4ea69ee0881fc469040b822df Mon Sep 17 00:00:00 2001 -From: Grigori Goronzy -Date: Mon, 4 Jun 2012 04:27:48 +0200 -Subject: [PATCH 12/82] sdhci-bcm2708: speed up DMA sync +sdhci-bcm2708: speed up DMA sync Experiments show that it doesn't really take that long to sync, so we can reduce the poll interval slightly. Might improve performance a bit. ---- - drivers/mmc/host/sdhci-bcm2708.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index 7ba715b..fe656c0 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -897,7 +897,7 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host, - while (0 != (sdhci_bcm2708_raw_readl(host, SDHCI_PRESENT_STATE) - & state_mask) && --timeout > 0) - { -- udelay(100); -+ udelay(30); - continue; - } - if (timeout <= 0) --- -1.8.4 - - -From e8e2859ee337d238629739097a4a97ddb218bac6 Mon Sep 17 00:00:00 2001 -From: Grigori Goronzy -Date: Mon, 11 Jun 2012 18:52:04 +0200 -Subject: [PATCH 13/82] sdhci-bcm2708: remove custom clock handling +sdhci-bcm2708: remove custom clock handling The custom clock handling code is redundant and buggy. The MMC/SDHCI subsystem does a better job than it, so remove it for good. ---- - drivers/mmc/host/sdhci-bcm2708.c | 65 +--------------------------------------- - 1 file changed, 1 insertion(+), 64 deletions(-) +sdhci-bcm2708: add additional quirks + +Some additional quirks are needed for correct operation. +There's no SDHCI capabilities register documented, and it always reads +zero, so add SDHCI_QUIRK_MISSING_CAPS. Apparently +SDHCI_QUIRK_NO_HISPD_BIT is needed for many cards to work correctly in +high-speed mode, so add it as well. + +sdhci-bcm2708: add allow_highspeed parameter + +Add a parameter to disable high-speed mode for the few cards that +still might have problems. High-speed mode is enabled by default. + +sdhci-bcm2708: assume 50 MHz eMMC clock + +80 MHz clock isnt't suited well to be dividable to get SD clocks of 25 +MHz (default mode) or 50 MHz (high speed mode). 50 MHz are perfect to +drive the SD interface at ideal frequencies. + +Allow emmc clock to be specified as command line parameter + +sdhci-bcm2708: raise DMA sync timeout + +Commit d64b84c by accident reduced the maximum overall DMA sync +timeout. The maximum overall timeout was reduced from 100ms to 30ms, +which isn't enough for many cards. Increase it to 150ms, just to be +extra safe. According to commit 872a8ff in the MMC subsystem, some +cards require crazy long timeouts (3s), but as we're busy-waiting, +and shouldn't delay for such a long time, let's hope 150ms will be +enough for most cards. + +Use ndelay rather than udelay. Thanks lb + +Add sync_after_dma module parameter + +sdhci-bcm2708: use extension FIFO to buffer DMA transfers + +The additional FIFO might speed up transfers in some cases. + +sdhci-bcm2708: use multiblock-type transfers for single blocks + +There are issues with both single block reads (missed completion) +and writes (data loss in some cases!). Just don't do single block +transfers anymore, and treat them like multiblock transfers. This +adds a quirk for this and uses it. + +Add module parameter for missing_status quirk. sdhci-bcm2708.missing_status=0 may improve interrupt latency + +Fix spinlock recursion in sdhci-bcm2708.c + +mmc: Report 3.3V support in caps + +sdhci: Use macros for out spin lock/unlock functions to reduce diffs with upstream code + +sdhci: sdhci_bcm2708_quirk_voltage_broken appears to be a no-op + +sdhci: sdhci_bcm2708_uhs_broken should be handled through caps reported + +Add low-latency mode to sdcard driver. Disable with sdhci-bcm2708.enable_llm=0. Thanks ddv2005. + +Allow the number of cycles delay between sdcard peripheral writes to be specified on command line with sdhci-bcm2708.cycle_delay + +Lazy CRC quirk: Implemented retrying mechanisms for SD SSR and SCR, disabled missing_status and spurious CRC ACMD51 quirks by default (should be fixed by the retrying-mechanishm) + +mmc: suppress sdcard warnings we are happy about by default + +sdhci-bcm2807: Increase sync_after_dma timeout + +The current timeout is being hit with some cards that complete successfully with a longer timeout. +The timeout is not handled well, and is believed to be a code path that causes corruption. +872a8ff suggests that crappy cards can take up to 3 seconds to respond +--- + drivers/mmc/card/block.c | 2 +- + drivers/mmc/core/sd.c | 110 +++++++++++++++-- + drivers/mmc/host/sdhci-bcm2708.c | 248 ++++++++++++++++++--------------------- + drivers/mmc/host/sdhci.c | 179 +++++++++++++++++++++------- + drivers/mmc/host/sdhci.h | 8 +- + include/linux/mmc/host.h | 1 + + include/linux/mmc/sdhci.h | 1 + + 7 files changed, 361 insertions(+), 188 deletions(-) + +diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c +index 1a3163f..b39f38b 100644 +--- a/drivers/mmc/card/block.c ++++ b/drivers/mmc/card/block.c +@@ -1361,7 +1361,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, + brq->data.blocks = 1; + } + +- if (brq->data.blocks > 1 || do_rel_wr) { ++ if (brq->data.blocks > 1 || do_rel_wr || card->host->caps2 & MMC_CAP2_FORCE_MULTIBLOCK) { + /* SPI multiblock writes terminate using a special + * token, not a STOP_TRANSMISSION request. + */ +diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c +index 5e8823d..e0e9afb 100644 +--- a/drivers/mmc/core/sd.c ++++ b/drivers/mmc/core/sd.c +@@ -13,6 +13,8 @@ + #include + #include + #include ++#include ++#include + + #include + #include +@@ -58,6 +60,15 @@ + __res & __mask; \ + }) + ++// timeout for tries ++static const unsigned long retry_timeout_ms= 10*1000; ++ ++// try at least 10 times, even if timeout is reached ++static const int retry_min_tries= 10; ++ ++// delay between tries ++static const unsigned long retry_delay_ms= 10; ++ + /* + * Given the decoded CSD structure, decode the raw CID to our CID structure. + */ +@@ -210,12 +221,63 @@ static int mmc_decode_scr(struct mmc_card *card) + } + + /* +- * Fetch and process SD Status register. ++ * Fetch and process SD Configuration Register. ++ */ ++static int mmc_read_scr(struct mmc_card *card) ++{ ++ unsigned long timeout_at; ++ int err, tries; ++ ++ timeout_at= jiffies + msecs_to_jiffies( retry_timeout_ms ); ++ tries= 0; ++ ++ while( tries < retry_min_tries || time_before( jiffies, timeout_at ) ) ++ { ++ unsigned long delay_at; ++ tries++; ++ ++ err = mmc_app_send_scr(card, card->raw_scr); ++ if( !err ) ++ break; // sucess!!! ++ ++ touch_nmi_watchdog(); // we are still alive! ++ ++ // delay ++ delay_at= jiffies + msecs_to_jiffies( retry_delay_ms ); ++ while( time_before( jiffies, delay_at ) ) ++ { ++ mdelay( 1 ); ++ touch_nmi_watchdog(); // we are still alive! ++ } ++ } ++ ++ if( err) ++ { ++ pr_err("%s: failed to read SD Configuration register (SCR) after %d tries during %lu ms, error %d\n", mmc_hostname(card->host), tries, retry_timeout_ms, err ); ++ return err; ++ } ++ ++ if( tries > 1 ) ++ { ++ pr_info("%s: could read SD Configuration register (SCR) at the %dth attempt\n", mmc_hostname(card->host), tries ); ++ } ++ ++ err = mmc_decode_scr(card); ++ if (err) ++ return err; ++ ++ return err; ++} ++ ++/* ++ * Fetch and process SD Status Register. + */ + static int mmc_read_ssr(struct mmc_card *card) + { ++ unsigned long timeout_at; + unsigned int au, es, et, eo; + int err, i, max_au; ++ int tries; + u32 *ssr; + + if (!(card->csd.cmdclass & CCC_APP_SPEC)) { +@@ -228,14 +290,40 @@ static int mmc_read_ssr(struct mmc_card *card) + if (!ssr) + return -ENOMEM; + +- err = mmc_app_sd_status(card, ssr); +- if (err) { +- pr_warning("%s: problem reading SD Status " +- "register.\n", mmc_hostname(card->host)); +- err = 0; ++ timeout_at= jiffies + msecs_to_jiffies( retry_timeout_ms ); ++ tries= 0; ++ ++ while( tries < retry_min_tries || time_before( jiffies, timeout_at ) ) ++ { ++ unsigned long delay_at; ++ tries++; ++ ++ err= mmc_app_sd_status(card, ssr); ++ if( !err ) ++ break; // sucess!!! ++ ++ touch_nmi_watchdog(); // we are still alive! ++ ++ // delay ++ delay_at= jiffies + msecs_to_jiffies( retry_delay_ms ); ++ while( time_before( jiffies, delay_at ) ) ++ { ++ mdelay( 1 ); ++ touch_nmi_watchdog(); // we are still alive! ++ } ++ } ++ ++ if( err) ++ { ++ pr_err("%s: failed to read SD Status register (SSR) after %d tries during %lu ms, error %d\n", mmc_hostname(card->host), tries, retry_timeout_ms, err ); + goto out; + } + ++ if( tries > 1 ) ++ { ++ pr_info("%s: could read SD Status register (SSR) at the %dth attempt\n", mmc_hostname(card->host), tries ); ++ } ++ + for (i = 0; i < 16; i++) + ssr[i] = be32_to_cpu(ssr[i]); + +@@ -816,14 +904,10 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, + + if (!reinit) { + /* +- * Fetch SCR from card. ++ * Fetch and decode SD Configuration register. + */ +- err = mmc_app_send_scr(card, card->raw_scr); +- if (err) +- return err; +- +- err = mmc_decode_scr(card); +- if (err) ++ err = mmc_read_scr(card); ++ if( err ) + return err; + + /* diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index fe656c0..18be90c 100644 +index d8ef77c..f91be88 100644 --- a/drivers/mmc/host/sdhci-bcm2708.c +++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -353,68 +353,9 @@ void sdhci_bcm2708_writeb(struct sdhci_host *host, u8 val, int reg) +@@ -51,7 +51,6 @@ + #undef CONFIG_MMC_SDHCI_BCM2708_DMA + #define CONFIG_MMC_SDHCI_BCM2708_DMA y + +-#define USE_SYNC_AFTER_DMA + #ifdef CONFIG_MMC_SDHCI_BCM2708_DMA + /* #define CHECK_DMA_USE */ + #endif +@@ -73,7 +72,12 @@ + #define BCM2708_SDHCI_SLEEP_TIMEOUT 1000 /* msecs */ + + /* Mhz clock that the EMMC core is running at. Should match the platform clockman settings */ +-#define BCM2708_EMMC_CLOCK_FREQ 80000000 ++#define BCM2708_EMMC_CLOCK_FREQ 50000000 ++ ++#define REG_EXRDFIFO_EN 0x80 ++#define REG_EXRDFIFO_CFG 0x84 ++ ++int cycle_delay=2; + + /*****************************************************************************\ + * * +@@ -129,6 +133,14 @@ static inline unsigned long int since_ns(hptime_t t) + return (unsigned long)((hptime() - t) * HPTIME_CLK_NS); + } + ++static bool allow_highspeed = 1; ++static int emmc_clock_freq = BCM2708_EMMC_CLOCK_FREQ; ++static bool sync_after_dma = 1; ++static bool missing_status = 1; ++static bool spurious_crc_acmd51 = 0; ++bool enable_llm = 1; ++bool extra_messages = 0; ++ + #if 0 + static void hptime_test(void) + { +@@ -241,19 +253,19 @@ static void sdhci_bcm2708_raw_writel(struct sdhci_host *host, u32 val, int reg) + /* host->clock is the clock freq in Hz */ + static hptime_t last_write_hpt; + hptime_t now = hptime(); +- ns_2clk = 2000000000/host->clock; ++ ns_2clk = cycle_delay*1000000/(host->clock/1000); + + if (now == last_write_hpt || now == last_write_hpt+1) { + /* we can't guarantee any significant time has + * passed - we'll have to wait anyway ! */ +- udelay((ns_2clk+1000-1)/1000); ++ ndelay(ns_2clk); + } else + { + /* we must have waited at least this many ns: */ + unsigned int ns_wait = HPTIME_CLK_NS * + (last_write_hpt - now - 1); + if (ns_wait < ns_2clk) +- udelay((ns_2clk-ns_wait+500)/1000); ++ ndelay(ns_2clk - ns_wait); + } + last_write_hpt = now; + } +@@ -269,13 +281,13 @@ static void sdhci_bcm2708_raw_writel(struct sdhci_host *host, u32 val, int reg) + ier &= ~SDHCI_INT_DATA_TIMEOUT; + writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE); + timeout_disabled = true; +- udelay((ns_2clk+1000-1)/1000); ++ ndelay(ns_2clk); + } else if (timeout_disabled) { + ier = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE); + ier |= SDHCI_INT_DATA_TIMEOUT; + writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE); + timeout_disabled = false; +- udelay((ns_2clk+1000-1)/1000); ++ ndelay(ns_2clk); + } + #endif + writel(val, host->ioaddr + reg); +@@ -353,68 +365,9 @@ void sdhci_bcm2708_writeb(struct sdhci_host *host, u8 val, int reg) static unsigned int sdhci_bcm2708_get_max_clock(struct sdhci_host *host) { - return 20000000; // this value is in Hz (20MHz) -+ return BCM2708_EMMC_CLOCK_FREQ; - } - +-} +- -static unsigned int sdhci_bcm2708_get_timeout_clock(struct sdhci_host *host) -{ - if(host->clock) - return (host->clock / 1000); // this value is in kHz (100MHz) - else - return (sdhci_bcm2708_get_max_clock(host) / 1000); --} -- ++ return emmc_clock_freq; + } + -static void sdhci_bcm2708_set_clock(struct sdhci_host *host, unsigned int clock) -{ - int div = 0; @@ -85796,7 +86001,218 @@ index fe656c0..18be90c 100644 /*****************************************************************************\ * * * DMA Operation * -@@ -1189,11 +1130,7 @@ static unsigned int sdhci_bcm2708_missing_status(struct sdhci_host *host) +@@ -695,11 +648,11 @@ static void schci_bcm2708_dma_go(struct sdhci_host *host) + sdhci_bcm2708_platdma_reset(struct sdhci_host *host, struct mmc_data *data) + { + struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); +- unsigned long flags; ++// unsigned long flags; + + BUG_ON(NULL == host); + +- spin_lock_irqsave(&host->lock, flags); ++// spin_lock_irqsave(&host->lock, flags); + + if (host_priv->dma_wanted) { + if (NULL == data) { +@@ -720,13 +673,16 @@ static void schci_bcm2708_dma_go(struct sdhci_host *host) + cs = readl(host_priv->dma_chan_base + BCM2708_DMA_CS); + + if (!(BCM2708_DMA_ACTIVE & cs)) +- printk(KERN_INFO "%s: missed completion of " ++ { ++ if (extra_messages) ++ printk(KERN_INFO "%s: missed completion of " + "cmd %d DMA (%d/%d [%d]/[%d]) - " + "ignoring it\n", + mmc_hostname(host->mmc), + host->last_cmdop, + host_priv->sg_done, sg_todo, + host_priv->sg_ix+1, sg_len); ++ } + else + printk(KERN_INFO "%s: resetting ongoing cmd %d" + "DMA before %d/%d [%d]/[%d] complete\n", +@@ -779,7 +735,7 @@ static void schci_bcm2708_dma_go(struct sdhci_host *host) + #endif + } + +- spin_unlock_irqrestore(&host->lock, flags); ++// spin_unlock_irqrestore(&host->lock, flags); + } + + +@@ -792,11 +748,11 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host, + int sg_len; + int sg_ix; + int sg_todo; +- unsigned long flags; ++// unsigned long flags; + + BUG_ON(NULL == host); + +- spin_lock_irqsave(&host->lock, flags); ++// spin_lock_irqsave(&host->lock, flags); + data = host->data; + + #ifdef CHECK_DMA_USE +@@ -821,7 +777,7 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host, + + if (NULL == data) { + DBG("PDMA unused completion - status 0x%X\n", dma_cs); +- spin_unlock_irqrestore(&host->lock, flags); ++// spin_unlock_irqrestore(&host->lock, flags); + return; + } + sg = data->sg; +@@ -878,40 +834,34 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host, + SDHCI_INT_SPACE_AVAIL); + } + } else { +-#ifdef USE_SYNC_AFTER_DMA +- /* On the Arasan controller the stop command (which will be +- scheduled after this completes) does not seem to work +- properly if we allow it to be issued when we are +- transferring data to/from the SD card. +- We get CRC and DEND errors unless we wait for +- the SD controller to finish reading/writing to the card. */ +- u32 state_mask; +- int timeout=1000000; +- hptime_t now = hptime(); +- +- DBG("PDMA over - sync card\n"); +- if (data->flags & MMC_DATA_READ) +- state_mask = SDHCI_DOING_READ; +- else +- state_mask = SDHCI_DOING_WRITE; +- +- while (0 != (sdhci_bcm2708_raw_readl(host, +- SDHCI_PRESENT_STATE) & +- state_mask) && --timeout > 0) +- continue; ++ if (sync_after_dma) { ++ /* On the Arasan controller the stop command (which will be ++ scheduled after this completes) does not seem to work ++ properly if we allow it to be issued when we are ++ transferring data to/from the SD card. ++ We get CRC and DEND errors unless we wait for ++ the SD controller to finish reading/writing to the card. */ ++ u32 state_mask; ++ int timeout=3*1000*1000; ++ ++ DBG("PDMA over - sync card\n"); ++ if (data->flags & MMC_DATA_READ) ++ state_mask = SDHCI_DOING_READ; ++ else ++ state_mask = SDHCI_DOING_WRITE; + +- if (1000000-timeout > 4000) /*ave. is about 3250*/ +- DBG("%s: note - long %s sync %luns - " +- "%d its.\n", +- mmc_hostname(host->mmc), +- data->flags & MMC_DATA_READ? "read": "write", +- since_ns(now), 1000000-timeout); +- if (timeout <= 0) +- printk(KERN_ERR"%s: final %s to SD card still " +- "running\n", +- mmc_hostname(host->mmc), +- data->flags & MMC_DATA_READ? "read": "write"); +-#endif ++ while (0 != (sdhci_bcm2708_raw_readl(host, SDHCI_PRESENT_STATE) ++ & state_mask) && --timeout > 0) ++ { ++ udelay(1); ++ continue; ++ } ++ if (timeout <= 0) ++ printk(KERN_ERR"%s: final %s to SD card still " ++ "running\n", ++ mmc_hostname(host->mmc), ++ data->flags & MMC_DATA_READ? "read": "write"); ++ } + if (host_priv->complete) { + (*host_priv->complete)(host); + DBG("PDMA %s complete\n", +@@ -920,7 +870,7 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host, + SDHCI_INT_SPACE_AVAIL); + } + } +- spin_unlock_irqrestore(&host->lock, flags); ++// spin_unlock_irqrestore(&host->lock, flags); + } + + static irqreturn_t sdhci_bcm2708_dma_irq(int irq, void *dev_id) +@@ -929,12 +879,11 @@ static irqreturn_t sdhci_bcm2708_dma_irq(int irq, void *dev_id) + struct sdhci_host *host = dev_id; + struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); + u32 dma_cs; /* control and status register */ +- unsigned long flags; + + BUG_ON(NULL == dev_id); + BUG_ON(NULL == host_priv->dma_chan_base); + +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock(host); + + dma_cs = readl(host_priv->dma_chan_base + BCM2708_DMA_CS); + +@@ -958,7 +907,8 @@ static irqreturn_t sdhci_bcm2708_dma_irq(int irq, void *dev_id) + + if (!host_priv->dma_wanted) { + /* ignore this interrupt - it was reset */ +- printk(KERN_INFO "%s: DMA IRQ %X ignored - " ++ if (extra_messages) ++ printk(KERN_INFO "%s: DMA IRQ %X ignored - " + "results were reset\n", + mmc_hostname(host->mmc), dma_cs); + #ifdef CHECK_DMA_USE +@@ -975,8 +925,7 @@ static irqreturn_t sdhci_bcm2708_dma_irq(int irq, void *dev_id) + + result = IRQ_HANDLED; + } +- +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock(host); + + return result; + } +@@ -1019,10 +968,12 @@ static ssize_t attr_dma_store(struct device *_dev, + int on = simple_strtol(buf, NULL, 0); + if (on) { + host->flags |= SDHCI_USE_PLATDMA; ++ sdhci_bcm2708_writel(host, 1, REG_EXRDFIFO_EN); + printk(KERN_INFO "%s: DMA enabled\n", + mmc_hostname(host->mmc)); + } else { + host->flags &= ~(SDHCI_USE_PLATDMA | SDHCI_REQ_USE_DMA); ++ sdhci_bcm2708_writel(host, 0, REG_EXRDFIFO_EN); + printk(KERN_INFO "%s: DMA disabled\n", + mmc_hostname(host->mmc)); + } +@@ -1158,19 +1109,14 @@ static unsigned int sdhci_bcm2708_quirk_extra_ints(struct sdhci_host *host) + return 1; + } + +-static unsigned int sdhci_bcm2708_quirk_spurious_crc(struct sdhci_host *host) ++static unsigned int sdhci_bcm2708_quirk_spurious_crc_acmd51(struct sdhci_host *host) + { + return 1; + } + +-static unsigned int sdhci_bcm2708_quirk_voltage_broken(struct sdhci_host *host) ++static unsigned int sdhci_bcm2708_missing_status(struct sdhci_host *host) + { +- return 1; +-} +- +-static unsigned int sdhci_bcm2708_uhs_broken(struct sdhci_host *host) +-{ +- return 1; ++ return 1; + } + + /***************************************************************************** \ +@@ -1190,11 +1136,7 @@ static unsigned int sdhci_bcm2708_uhs_broken(struct sdhci_host *host) #else #error The BCM2708 SDHCI driver needs CONFIG_MMC_SDHCI_IO_ACCESSORS to be set #endif @@ -85808,82 +86224,100 @@ index fe656c0..18be90c 100644 #ifdef CONFIG_MMC_SDHCI_BCM2708_DMA // Platform DMA operations --- -1.8.4 - - -From abca27f964c3c0dcb8480f46ba6c380de4fd1a96 Mon Sep 17 00:00:00 2001 -From: Grigori Goronzy -Date: Mon, 11 Jun 2012 18:53:59 +0200 -Subject: [PATCH 14/82] sdhci-bcm2708: add additional quirks - -Some additional quirks are needed for correct operation. -There's no SDHCI capabilities register documented, and it always reads -zero, so add SDHCI_QUIRK_MISSING_CAPS. Apparently -SDHCI_QUIRK_NO_HISPD_BIT is needed for many cards to work correctly in -high-speed mode, so add it as well. ---- - drivers/mmc/host/sdhci-bcm2708.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index 18be90c..1be1785 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -1189,7 +1189,9 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev) +@@ -1203,9 +1145,6 @@ static unsigned int sdhci_bcm2708_uhs_broken(struct sdhci_host *host) + .pdma_reset = sdhci_bcm2708_platdma_reset, + #endif + .extra_ints = sdhci_bcm2708_quirk_extra_ints, +- .spurious_crc_acmd51 = sdhci_bcm2708_quirk_spurious_crc, +- .voltage_broken = sdhci_bcm2708_quirk_voltage_broken, +- .uhs_broken = sdhci_bcm2708_uhs_broken, + }; + + /*****************************************************************************\ +@@ -1244,15 +1183,30 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev) + ret = PTR_ERR(host); + goto err; + } ++ if (missing_status) { ++ sdhci_bcm2708_ops.missing_status = sdhci_bcm2708_missing_status; ++ } ++ ++ if( spurious_crc_acmd51 ) { ++ sdhci_bcm2708_ops.spurious_crc_acmd51 = sdhci_bcm2708_quirk_spurious_crc_acmd51; ++ } ++ ++ ++ printk("sdhci: %s low-latency mode\n",enable_llm?"Enable":"Disable"); + + host->hw_name = "BCM2708_Arasan"; + host->ops = &sdhci_bcm2708_ops; + host->irq = platform_get_irq(pdev, 0); ++ host->second_irq = 0; + host->quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | - SDHCI_QUIRK_NONSTANDARD_CLOCK; + SDHCI_QUIRK_MISSING_CAPS | -+ SDHCI_QUIRK_NO_HISPD_BIT; ++ SDHCI_QUIRK_NO_HISPD_BIT | ++ (sync_after_dma ? 0:SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12); ++ + #ifdef CONFIG_MMC_SDHCI_BCM2708_DMA host->flags = SDHCI_USE_PLATDMA; #endif --- -1.8.4 - - -From e0615f329b8b249540b36c12e41eadf9fd53b666 Mon Sep 17 00:00:00 2001 -From: Grigori Goronzy -Date: Mon, 11 Jun 2012 18:57:13 +0200 -Subject: [PATCH 15/82] sdhci-bcm2708: add allow_highspeed parameter - -Add a parameter to disable high-speed mode for the few cards that -still might have problems. High-speed mode is enabled by default. ---- - drivers/mmc/host/sdhci-bcm2708.c | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index 1be1785..9a6d195 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -129,6 +129,8 @@ static inline unsigned long int since_ns(hptime_t t) - return (unsigned long)((hptime() - t) * HPTIME_CLK_NS); - } +@@ -1305,17 +1259,24 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev) + host_priv->dma_chan = ret; -+static bool allow_highspeed = 1; -+ - #if 0 - static void hptime_test(void) - { -@@ -1254,7 +1256,8 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev) + ret = request_irq(host_priv->dma_irq, sdhci_bcm2708_dma_irq, +- IRQF_SHARED, DRIVER_NAME " (dma)", host); ++ 0 /*IRQF_SHARED*/, DRIVER_NAME " (dma)", host); + if (ret) { + dev_err(&pdev->dev, "cannot set DMA IRQ\n"); + goto err_add_dma_irq; + } ++ host->second_irq = host_priv->dma_irq; + DBG("DMA CBs %p handle %08X DMA%d %p DMA IRQ %d\n", + host_priv->cb_base, (unsigned)host_priv->cb_handle, host_priv->dma_chan, host_priv->dma_chan_base, host_priv->dma_irq); - host->mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; ++ // we support 3.3V ++ host->caps |= SDHCI_CAN_VDD_330; + if (allow_highspeed) + host->mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; ++ ++ /* single block writes cause data loss with some SD cards! */ ++ host->mmc->caps2 |= MMC_CAP2_FORCE_MULTIBLOCK; #endif ret = sdhci_add_host(host); -@@ -1357,7 +1360,11 @@ static void __exit sdhci_drv_exit(void) +@@ -1327,6 +1288,12 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev) + ret = device_create_file(&pdev->dev, &dev_attr_dma_wait); + ret = device_create_file(&pdev->dev, &dev_attr_status); + ++#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA ++ /* enable extension fifo for paced DMA transfers */ ++ sdhci_bcm2708_writel(host, 1, REG_EXRDFIFO_EN); ++ sdhci_bcm2708_writel(host, 4, REG_EXRDFIFO_CFG); ++#endif ++ + printk(KERN_INFO "%s: BCM2708 SDHC host at 0x%08llx DMA %d IRQ %d\n", + mmc_hostname(host->mmc), (unsigned long long)iomem->start, + host_priv->dma_chan, host_priv->dma_irq); +@@ -1418,7 +1385,26 @@ static void __exit sdhci_drv_exit(void) module_init(sdhci_drv_init); module_exit(sdhci_drv_exit); +module_param(allow_highspeed, bool, 0444); ++module_param(emmc_clock_freq, int, 0444); ++module_param(sync_after_dma, bool, 0444); ++module_param(missing_status, bool, 0444); ++module_param(spurious_crc_acmd51, bool, 0444); ++module_param(enable_llm, bool, 0444); ++module_param(cycle_delay, int, 0444); ++module_param(extra_messages, bool, 0444); + MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver"); MODULE_AUTHOR("Broadcom "); @@ -85891,175 +86325,500 @@ index 1be1785..9a6d195 100644 MODULE_ALIAS("platform:"DRIVER_NAME); + +MODULE_PARM_DESC(allow_highspeed, "Allow high speed transfers modes"); --- -1.8.4 - - -From 26f6920b48fd609c051ed4aa89a74530a1295d56 Mon Sep 17 00:00:00 2001 -From: Grigori Goronzy -Date: Mon, 11 Jun 2012 18:58:40 +0200 -Subject: [PATCH 16/82] sdhci-bcm2708: assume 50 MHz eMMC clock - -80 MHz clock isnt't suited well to be dividable to get SD clocks of 25 -MHz (default mode) or 50 MHz (high speed mode). 50 MHz are perfect to -drive the SD interface at ideal frequencies. ---- - drivers/mmc/host/sdhci-bcm2708.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index 9a6d195..8747f5f 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -73,7 +73,7 @@ - #define BCM2708_SDHCI_SLEEP_TIMEOUT 1000 /* msecs */ - - /* Mhz clock that the EMMC core is running at. Should match the platform clockman settings */ --#define BCM2708_EMMC_CLOCK_FREQ 80000000 -+#define BCM2708_EMMC_CLOCK_FREQ 50000000 - - /*****************************************************************************\ - * * --- -1.8.4 - - -From 28aaf7acfa3191852fd6d20629597187bf647e8d Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Sat, 16 Jun 2012 22:31:55 +0100 -Subject: [PATCH 17/82] Allow emmc clock to be specified as command line - parameter - ---- - drivers/mmc/host/sdhci-bcm2708.c | 6 +++++- - 1 file changed, 5 insertions(+), 1 deletion(-) - -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index 8747f5f..fe889d3 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -130,6 +130,7 @@ static inline unsigned long int since_ns(hptime_t t) - } - - static bool allow_highspeed = 1; -+static int emmc_clock_freq = BCM2708_EMMC_CLOCK_FREQ; - - #if 0 - static void hptime_test(void) -@@ -355,7 +356,7 @@ void sdhci_bcm2708_writeb(struct sdhci_host *host, u8 val, int reg) - - static unsigned int sdhci_bcm2708_get_max_clock(struct sdhci_host *host) - { -- return BCM2708_EMMC_CLOCK_FREQ; -+ return emmc_clock_freq; - } - - /*****************************************************************************\ -@@ -1361,6 +1362,7 @@ static void __exit sdhci_drv_exit(void) - module_exit(sdhci_drv_exit); - - module_param(allow_highspeed, bool, 0444); -+module_param(emmc_clock_freq, int, 0444); - - MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver"); - MODULE_AUTHOR("Broadcom "); -@@ -1368,3 +1370,5 @@ static void __exit sdhci_drv_exit(void) - MODULE_ALIAS("platform:"DRIVER_NAME); - - MODULE_PARM_DESC(allow_highspeed, "Allow high speed transfers modes"); +MODULE_PARM_DESC(emmc_clock_freq, "Specify the speed of emmc clock"); ++MODULE_PARM_DESC(sync_after_dma, "Block in driver until dma complete"); ++MODULE_PARM_DESC(missing_status, "Use the missing status quirk"); ++MODULE_PARM_DESC(spurious_crc_acmd51, "Use the spurious crc quirk for reading SCR (ACMD51)"); ++MODULE_PARM_DESC(enable_llm, "Enable low-latency mode"); ++MODULE_PARM_DESC(extra_messages, "Enable more sdcard warning messages"); + --- -1.8.4 - - -From c22f6a2e0ae13c1b37b084784e7383ac650b9b23 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Sat, 16 Jun 2012 22:35:38 +0100 -Subject: [PATCH 18/82] sdhci-bcm2708: raise DMA sync timeout - -Commit d64b84c by accident reduced the maximum overall DMA sync -timeout. The maximum overall timeout was reduced from 100ms to 30ms, -which isn't enough for many cards. Increase it to 150ms, just to be -extra safe. According to commit 872a8ff in the MMC subsystem, some -cards require crazy long timeouts (3s), but as we're busy-waiting, -and shouldn't delay for such a long time, let's hope 150ms will be -enough for most cards. ---- - drivers/mmc/host/sdhci-bcm2708.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index fe889d3..16c8df1 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -830,7 +830,7 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host, - We get CRC and DEND errors unless we wait for - the SD controller to finish reading/writing to the card. */ - u32 state_mask; -- int timeout=1000; -+ int timeout=5000; ++ +diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c +index 4b250ea..a33407e 100644 +--- a/drivers/mmc/host/sdhci.c ++++ b/drivers/mmc/host/sdhci.c +@@ -132,6 +132,99 @@ static void sdhci_dumpregs(struct sdhci_host *host) + * Low level functions * + * * + \*****************************************************************************/ ++extern bool enable_llm; ++static int sdhci_locked=0; ++void sdhci_spin_lock(struct sdhci_host *host) ++{ ++ spin_lock(&host->lock); ++#ifdef CONFIG_PREEMPT ++ if(enable_llm) ++ { ++ disable_irq_nosync(host->irq); ++ if(host->second_irq) ++ disable_irq_nosync(host->second_irq); ++ local_irq_enable(); ++ } ++#endif ++} ++ ++void sdhci_spin_unlock(struct sdhci_host *host) ++{ ++#ifdef CONFIG_PREEMPT ++ if(enable_llm) ++ { ++ local_irq_disable(); ++ if(host->second_irq) ++ enable_irq(host->second_irq); ++ enable_irq(host->irq); ++ } ++#endif ++ spin_unlock(&host->lock); ++} ++ ++void sdhci_spin_lock_irqsave(struct sdhci_host *host,unsigned long *flags) ++{ ++#ifdef CONFIG_PREEMPT ++ if(enable_llm) ++ { ++ while(sdhci_locked) ++ { ++ preempt_schedule(); ++ } ++ spin_lock_irqsave(&host->lock,*flags); ++ disable_irq(host->irq); ++ if(host->second_irq) ++ disable_irq(host->second_irq); ++ local_irq_enable(); ++ } ++ else ++#endif ++ spin_lock_irqsave(&host->lock,*flags); ++} ++ ++void sdhci_spin_unlock_irqrestore(struct sdhci_host *host,unsigned long flags) ++{ ++#ifdef CONFIG_PREEMPT ++ if(enable_llm) ++ { ++ local_irq_disable(); ++ if(host->second_irq) ++ enable_irq(host->second_irq); ++ enable_irq(host->irq); ++ } ++#endif ++ spin_unlock_irqrestore(&host->lock,flags); ++} ++ ++static void sdhci_spin_enable_schedule(struct sdhci_host *host) ++{ ++#ifdef CONFIG_PREEMPT ++ if(enable_llm) ++ { ++ sdhci_locked = 1; ++ preempt_enable(); ++ } ++#endif ++} ++ ++static void sdhci_spin_disable_schedule(struct sdhci_host *host) ++{ ++#ifdef CONFIG_PREEMPT ++ if(enable_llm) ++ { ++ preempt_disable(); ++ sdhci_locked = 0; ++ } ++#endif ++} ++ ++ ++#undef spin_lock_irqsave ++#define spin_lock_irqsave(host_lock, flags) sdhci_spin_lock_irqsave(container_of(host_lock, struct sdhci_host, lock), &flags) ++#define spin_unlock_irqrestore(host_lock, flags) sdhci_spin_unlock_irqrestore(container_of(host_lock, struct sdhci_host, lock), flags) ++ ++#define spin_lock(host_lock) sdhci_spin_lock(container_of(host_lock, struct sdhci_host, lock)) ++#define spin_unlock(host_lock) sdhci_spin_unlock(container_of(host_lock, struct sdhci_host, lock)) - DBG("PDMA over - sync card\n"); - if (data->flags & MMC_DATA_READ) --- -1.8.4 - - -From b410c7b3c6cb2a52b59432ec7167c6b48ef81e9f Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Fri, 22 Jun 2012 12:57:42 +0100 -Subject: [PATCH 19/82] Use ndelay rather than udelay. Thanks lb - ---- - drivers/mmc/host/sdhci-bcm2708.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index 16c8df1..0eb48c9 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -249,14 +249,14 @@ static void sdhci_bcm2708_raw_writel(struct sdhci_host *host, u32 val, int reg) - if (now == last_write_hpt || now == last_write_hpt+1) { - /* we can't guarantee any significant time has - * passed - we'll have to wait anyway ! */ -- udelay((ns_2clk+1000-1)/1000); -+ ndelay(ns_2clk); - } else - { - /* we must have waited at least this many ns: */ - unsigned int ns_wait = HPTIME_CLK_NS * - (last_write_hpt - now - 1); - if (ns_wait < ns_2clk) -- udelay((ns_2clk-ns_wait+500)/1000); -+ ndelay(ns_2clk - ns_wait); - } - last_write_hpt = now; - } -@@ -272,13 +272,13 @@ static void sdhci_bcm2708_raw_writel(struct sdhci_host *host, u32 val, int reg) - ier &= ~SDHCI_INT_DATA_TIMEOUT; - writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE); - timeout_disabled = true; -- udelay((ns_2clk+1000-1)/1000); -+ ndelay(ns_2clk); - } else if (timeout_disabled) { - ier = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE); - ier |= SDHCI_INT_DATA_TIMEOUT; - writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE); - timeout_disabled = false; -- udelay((ns_2clk+1000-1)/1000); -+ ndelay(ns_2clk); - } + static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set) + { +@@ -301,7 +394,7 @@ static void sdhci_led_control(struct led_classdev *led, + struct sdhci_host *host = container_of(led, struct sdhci_host, led); + unsigned long flags; + +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + + if (host->runtime_suspended) + goto out; +@@ -311,7 +404,7 @@ static void sdhci_led_control(struct led_classdev *led, + else + sdhci_activate_led(host); + out: +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + } #endif - writel(val, host->ioaddr + reg); + +@@ -1019,7 +1112,9 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) + return; + } + timeout--; ++ sdhci_spin_enable_schedule(host); + mdelay(1); ++ sdhci_spin_disable_schedule(host); + } + DBG("send cmd %d - wait 0x%X irq 0x%x\n", cmd->opcode, mask, + sdhci_readl(host, SDHCI_INT_STATUS)); +@@ -1246,7 +1341,9 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) + return; + } + timeout--; ++ sdhci_spin_enable_schedule(host); + mdelay(1); ++ sdhci_spin_disable_schedule(host); + } + + clk |= SDHCI_CLOCK_CARD_EN; +@@ -1347,7 +1444,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) + + sdhci_runtime_pm_get(host); + +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + + WARN_ON(host->mrq != NULL); + +@@ -1405,9 +1502,9 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) + mmc->card->type == MMC_TYPE_MMC ? + MMC_SEND_TUNING_BLOCK_HS200 : + MMC_SEND_TUNING_BLOCK; +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + sdhci_execute_tuning(mmc, tuning_opcode); +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + + /* Restore original mmc_request structure */ + host->mrq = mrq; +@@ -1421,7 +1518,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) + } + + mmiowb(); +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + } + + static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) +@@ -1430,10 +1527,10 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) + int vdd_bit = -1; + u8 ctrl; + +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + + if (host->flags & SDHCI_DEVICE_DEAD) { +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + if (host->vmmc && ios->power_mode == MMC_POWER_OFF) + mmc_regulator_set_ocr(host->mmc, host->vmmc, 0); + return; +@@ -1460,9 +1557,9 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) + vdd_bit = sdhci_set_power(host, ios->vdd); + + if (host->vmmc && vdd_bit != -1) { +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit); +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + } + + if (host->ops->platform_send_init_74_clocks) +@@ -1501,7 +1598,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) + else + ctrl &= ~SDHCI_CTRL_HISPD; + +- if (host->version >= SDHCI_SPEC_300 && !(host->ops->uhs_broken)) { ++ if (host->version >= SDHCI_SPEC_300) { + u16 clk, ctrl_2; + + /* In case of UHS-I modes, set High Speed Enable */ +@@ -1599,7 +1696,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) + sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + + mmiowb(); +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + } + + static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +@@ -1647,7 +1744,7 @@ static int sdhci_check_ro(struct sdhci_host *host) + unsigned long flags; + int is_readonly; + +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + + if (host->flags & SDHCI_DEVICE_DEAD) + is_readonly = 0; +@@ -1657,7 +1754,7 @@ static int sdhci_check_ro(struct sdhci_host *host) + is_readonly = !(sdhci_readl(host, SDHCI_PRESENT_STATE) + & SDHCI_WRITE_PROTECT); + +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + + /* This quirk needs to be replaced by a callback-function later */ + return host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT ? +@@ -1730,9 +1827,9 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) + struct sdhci_host *host = mmc_priv(mmc); + unsigned long flags; + +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + sdhci_enable_sdio_irq_nolock(host, enable); +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + } + + static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, +@@ -2080,7 +2177,7 @@ static void sdhci_card_event(struct mmc_host *mmc) + if (host->ops->card_event) + host->ops->card_event(host); + +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + + /* Check host->mrq first in case we are runtime suspended */ + if (host->mrq && !sdhci_do_get_cd(host)) { +@@ -2096,7 +2193,7 @@ static void sdhci_card_event(struct mmc_host *mmc) + tasklet_schedule(&host->finish_tasklet); + } + +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + } + + static const struct mmc_host_ops sdhci_ops = { +@@ -2135,14 +2232,14 @@ static void sdhci_tasklet_finish(unsigned long param) + + host = (struct sdhci_host*)param; + +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + + /* + * If this tasklet gets rescheduled while running, it will + * be run again afterwards but without any active request. + */ + if (!host->mrq) { +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + return; + } + +@@ -2180,7 +2277,7 @@ static void sdhci_tasklet_finish(unsigned long param) + #endif + + mmiowb(); +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + + mmc_request_done(host->mmc, mrq); + sdhci_runtime_pm_put(host); +@@ -2193,7 +2290,7 @@ static void sdhci_timeout_timer(unsigned long data) + + host = (struct sdhci_host*)data; + +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + + if (host->mrq) { + pr_err("%s: Timeout waiting for hardware " +@@ -2214,7 +2311,7 @@ static void sdhci_timeout_timer(unsigned long data) + } + + mmiowb(); +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + } + + static void sdhci_tuning_timer(unsigned long data) +@@ -2224,11 +2321,11 @@ static void sdhci_tuning_timer(unsigned long data) + + host = (struct sdhci_host *)data; + +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + + host->flags |= SDHCI_NEEDS_RETUNING; + +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + } + + /*****************************************************************************\ +@@ -2452,10 +2549,10 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) + u32 intmask, unexpected = 0; + int cardint = 0, max_loops = 16; + +- spin_lock(&host->lock); ++ sdhci_spin_lock(host); + + if (host->runtime_suspended) { +- spin_unlock(&host->lock); ++ sdhci_spin_unlock(host); + pr_warning("%s: got irq while runtime suspended\n", + mmc_hostname(host->mmc)); + return IRQ_HANDLED; +@@ -2559,7 +2656,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) + if (intmask && --max_loops) + goto again; + out: +- spin_unlock(&host->lock); ++ sdhci_spin_unlock(host); + + if (unexpected) { + pr_err("%s: Unexpected interrupt 0x%08x.\n", +@@ -2660,7 +2757,7 @@ int sdhci_resume_host(struct sdhci_host *host) + } + + if (!device_may_wakeup(mmc_dev(host->mmc))) { +- ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, ++ ret = request_irq(host->irq, sdhci_irq, 0 /*IRQF_SHARED*/, + mmc_hostname(host->mmc), host); + if (ret) + return ret; +@@ -2737,15 +2834,15 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) + host->flags &= ~SDHCI_NEEDS_RETUNING; + } + +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + + synchronize_irq(host->irq); + +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + host->runtime_suspended = true; +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + + return ret; + } +@@ -2771,16 +2868,16 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) + sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios); + if ((host_flags & SDHCI_PV_ENABLED) && + !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) { +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + sdhci_enable_preset_value(host, true); +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + } + + /* Set the re-tuning expiration flag */ + if (host->flags & SDHCI_USING_RETUNING_TIMER) + host->flags |= SDHCI_NEEDS_RETUNING; + +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + + host->runtime_suspended = false; + +@@ -2791,7 +2888,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) + /* Enable Card Detection */ + sdhci_enable_card_detection(host); + +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + + return ret; + } +@@ -3286,8 +3383,8 @@ int sdhci_add_host(struct sdhci_host *host) + + sdhci_init(host, 0); + +- ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, +- mmc_hostname(mmc), host); ++ ret = request_irq(host->irq, sdhci_irq, 0 /*IRQF_SHARED*/, ++ mmc_hostname(mmc), host); + if (ret) { + pr_err("%s: Failed to request IRQ %d: %d\n", + mmc_hostname(mmc), host->irq, ret); +@@ -3348,7 +3445,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) + unsigned long flags; + + if (dead) { +- spin_lock_irqsave(&host->lock, flags); ++ sdhci_spin_lock_irqsave(host, &flags); + + host->flags |= SDHCI_DEVICE_DEAD; + +@@ -3360,7 +3457,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) + tasklet_schedule(&host->finish_tasklet); + } + +- spin_unlock_irqrestore(&host->lock, flags); ++ sdhci_spin_unlock_irqrestore(host, flags); + } + + sdhci_disable_card_detection(host); +diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h +index 8a02ec8..e434073 100644 +--- a/drivers/mmc/host/sdhci.h ++++ b/drivers/mmc/host/sdhci.h +@@ -299,8 +299,6 @@ struct sdhci_ops { + struct mmc_data *data); + unsigned int (*extra_ints)(struct sdhci_host *host); + unsigned int (*spurious_crc_acmd51)(struct sdhci_host *host); +- unsigned int (*voltage_broken)(struct sdhci_host *host); +- unsigned int (*uhs_broken)(struct sdhci_host *host); + unsigned int (*missing_status)(struct sdhci_host *host); + + void (*hw_reset)(struct sdhci_host *host); +@@ -442,4 +440,10 @@ static inline void *sdhci_priv(struct sdhci_host *host) + extern int sdhci_runtime_resume_host(struct sdhci_host *host); + #endif + ++extern void sdhci_spin_lock_irqsave(struct sdhci_host *host,unsigned long *flags); ++extern void sdhci_spin_unlock_irqrestore(struct sdhci_host *host,unsigned long flags); ++extern void sdhci_spin_lock(struct sdhci_host *host); ++extern void sdhci_spin_unlock(struct sdhci_host *host); ++ ++ + #endif /* __SDHCI_HW_H */ +diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h +index 3b0c33a..e9ae33c 100644 +--- a/include/linux/mmc/host.h ++++ b/include/linux/mmc/host.h +@@ -281,6 +281,7 @@ struct mmc_host { + MMC_CAP2_PACKED_WR) + #define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */ + #define MMC_CAP2_SANITIZE (1 << 15) /* Support Sanitize */ ++#define MMC_CAP2_FORCE_MULTIBLOCK (1 << 31) /* Always use multiblock transfers */ + + mmc_pm_flag_t pm_caps; /* supported pm features */ + +diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h +index aad2393..0aeeef8 100644 +--- a/include/linux/mmc/sdhci.h ++++ b/include/linux/mmc/sdhci.h +@@ -100,6 +100,7 @@ struct sdhci_host { + #define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5) + + int irq; /* Device IRQ */ ++ int second_irq; /* Additional IRQ to disable/enable in low-latency mode */ + void __iomem *ioaddr; /* Mapped address */ + + const struct sdhci_ops *ops; /* Low level hw interface */ -- -1.8.4 +1.8.5.1 -From 518b44c7249830efcb5e2248dc243bff2fc42b09 Mon Sep 17 00:00:00 2001 +From 638b30f422c07afab22c59eff1e4af387100d18a Mon Sep 17 00:00:00 2001 From: popcornmix Date: Mon, 4 Nov 2013 18:56:10 +0000 -Subject: [PATCH 20/82] Add Chris Boot's i2c and spi drivers. +Subject: [PATCH 11/91] Add Chris Boot's i2c and spi drivers. --- arch/arm/configs/bcmrpi_cutdown_defconfig | 9 + @@ -87420,378 +88179,13 @@ index 0000000..abaa5a6 +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); -- -1.8.4 +1.8.5.1 -From cfa0f75fac23a1595b4bcee2ebda393687d3cb64 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Tue, 17 Jul 2012 00:48:27 +0100 -Subject: [PATCH 21/82] Add sync_after_dma module parameter - ---- - drivers/mmc/host/sdhci-bcm2708.c | 61 ++++++++++++++++++++++------------------ - 1 file changed, 33 insertions(+), 28 deletions(-) - -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index 0eb48c9..984f2cf 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -51,7 +51,6 @@ - #undef CONFIG_MMC_SDHCI_BCM2708_DMA - #define CONFIG_MMC_SDHCI_BCM2708_DMA y - --#define USE_SYNC_AFTER_DMA - #ifdef CONFIG_MMC_SDHCI_BCM2708_DMA - /* #define CHECK_DMA_USE */ - #endif -@@ -131,6 +130,7 @@ static inline unsigned long int since_ns(hptime_t t) - - static bool allow_highspeed = 1; - static int emmc_clock_freq = BCM2708_EMMC_CLOCK_FREQ; -+static bool sync_after_dma = 1; - - #if 0 - static void hptime_test(void) -@@ -822,34 +822,34 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host, - SDHCI_INT_SPACE_AVAIL); - } - } else { --#ifdef USE_SYNC_AFTER_DMA -- /* On the Arasan controller the stop command (which will be -- scheduled after this completes) does not seem to work -- properly if we allow it to be issued when we are -- transferring data to/from the SD card. -- We get CRC and DEND errors unless we wait for -- the SD controller to finish reading/writing to the card. */ -- u32 state_mask; -- int timeout=5000; -- -- DBG("PDMA over - sync card\n"); -- if (data->flags & MMC_DATA_READ) -- state_mask = SDHCI_DOING_READ; -- else -- state_mask = SDHCI_DOING_WRITE; -+ if (sync_after_dma) { -+ /* On the Arasan controller the stop command (which will be -+ scheduled after this completes) does not seem to work -+ properly if we allow it to be issued when we are -+ transferring data to/from the SD card. -+ We get CRC and DEND errors unless we wait for -+ the SD controller to finish reading/writing to the card. */ -+ u32 state_mask; -+ int timeout=30*5000; -+ -+ DBG("PDMA over - sync card\n"); -+ if (data->flags & MMC_DATA_READ) -+ state_mask = SDHCI_DOING_READ; -+ else -+ state_mask = SDHCI_DOING_WRITE; - -- while (0 != (sdhci_bcm2708_raw_readl(host, SDHCI_PRESENT_STATE) -- & state_mask) && --timeout > 0) -- { -- udelay(30); -- continue; -+ while (0 != (sdhci_bcm2708_raw_readl(host, SDHCI_PRESENT_STATE) -+ & state_mask) && --timeout > 0) -+ { -+ udelay(1); -+ continue; -+ } -+ if (timeout <= 0) -+ printk(KERN_ERR"%s: final %s to SD card still " -+ "running\n", -+ mmc_hostname(host->mmc), -+ data->flags & MMC_DATA_READ? "read": "write"); - } -- if (timeout <= 0) -- printk(KERN_ERR"%s: final %s to SD card still " -- "running\n", -- mmc_hostname(host->mmc), -- data->flags & MMC_DATA_READ? "read": "write"); --#endif - if (host_priv->complete) { - (*host_priv->complete)(host); - DBG("PDMA %s complete\n", -@@ -1193,7 +1193,9 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev) - SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | - SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | - SDHCI_QUIRK_MISSING_CAPS | -- SDHCI_QUIRK_NO_HISPD_BIT; -+ SDHCI_QUIRK_NO_HISPD_BIT | -+ (sync_after_dma ? 0:SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12); -+ - - #ifdef CONFIG_MMC_SDHCI_BCM2708_DMA - host->flags = SDHCI_USE_PLATDMA; -@@ -1363,6 +1365,7 @@ static void __exit sdhci_drv_exit(void) - - module_param(allow_highspeed, bool, 0444); - module_param(emmc_clock_freq, int, 0444); -+module_param(sync_after_dma, bool, 0444); - - MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver"); - MODULE_AUTHOR("Broadcom "); -@@ -1371,4 +1374,6 @@ static void __exit sdhci_drv_exit(void) - - MODULE_PARM_DESC(allow_highspeed, "Allow high speed transfers modes"); - MODULE_PARM_DESC(emmc_clock_freq, "Specify the speed of emmc clock"); -+MODULE_PARM_DESC(sync_after_dma, "Block in driver until dma complete"); -+ - --- -1.8.4 - - -From 8dbbed73f54c90394b7d1459cc067b8428f629be Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Sun, 12 May 2013 12:25:52 +0100 -Subject: [PATCH 22/82] sdhci-bcm2708: use extension FIFO to buffer DMA - transfers - -The additional FIFO might speed up transfers in some cases. ---- - drivers/mmc/host/sdhci-bcm2708.c | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index 984f2cf..9c3f304 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -74,6 +74,9 @@ - /* Mhz clock that the EMMC core is running at. Should match the platform clockman settings */ - #define BCM2708_EMMC_CLOCK_FREQ 50000000 - -+#define REG_EXRDFIFO_EN 0x80 -+#define REG_EXRDFIFO_CFG 0x84 -+ - /*****************************************************************************\ - * * - * Debug * -@@ -957,10 +960,12 @@ static ssize_t attr_dma_store(struct device *_dev, - int on = simple_strtol(buf, NULL, 0); - if (on) { - host->flags |= SDHCI_USE_PLATDMA; -+ sdhci_bcm2708_writel(host, 1, REG_EXRDFIFO_EN); - printk(KERN_INFO "%s: DMA enabled\n", - mmc_hostname(host->mmc)); - } else { - host->flags &= ~(SDHCI_USE_PLATDMA | SDHCI_REQ_USE_DMA); -+ sdhci_bcm2708_writel(host, 0, REG_EXRDFIFO_EN); - printk(KERN_INFO "%s: DMA disabled\n", - mmc_hostname(host->mmc)); - } -@@ -1272,6 +1277,12 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev) - ret = device_create_file(&pdev->dev, &dev_attr_dma_wait); - ret = device_create_file(&pdev->dev, &dev_attr_status); - -+#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA -+ /* enable extension fifo for paced DMA transfers */ -+ sdhci_bcm2708_writel(host, 1, REG_EXRDFIFO_EN); -+ sdhci_bcm2708_writel(host, 4, REG_EXRDFIFO_CFG); -+#endif -+ - printk(KERN_INFO "%s: BCM2708 SDHC host at 0x%08llx DMA %d IRQ %d\n", - mmc_hostname(host->mmc), (unsigned long long)iomem->start, - host_priv->dma_chan, host_priv->dma_irq); --- -1.8.4 - - -From dc1889a5c6b2eda3a4b2425b3e7d0896a91e5b2f Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 3 Jul 2013 00:42:49 +0100 -Subject: [PATCH 23/82] sdhci-bcm2708: use multiblock-type transfers for single - blocks - -There are issues with both single block reads (missed completion) -and writes (data loss in some cases!). Just don't do single block -transfers anymore, and treat them like multiblock transfers. This -adds a quirk for this and uses it. ---- - drivers/mmc/card/block.c | 2 +- - drivers/mmc/host/sdhci-bcm2708.c | 3 +++ - include/linux/mmc/host.h | 1 + - 3 files changed, 5 insertions(+), 1 deletion(-) - -diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c -index 1a3163f..b39f38b 100644 ---- a/drivers/mmc/card/block.c -+++ b/drivers/mmc/card/block.c -@@ -1361,7 +1361,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, - brq->data.blocks = 1; - } - -- if (brq->data.blocks > 1 || do_rel_wr) { -+ if (brq->data.blocks > 1 || do_rel_wr || card->host->caps2 & MMC_CAP2_FORCE_MULTIBLOCK) { - /* SPI multiblock writes terminate using a special - * token, not a STOP_TRANSMISSION request. - */ -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index 9c3f304..7600c58 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -1266,6 +1266,9 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev) - - if (allow_highspeed) - host->mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; -+ -+ /* single block writes cause data loss with some SD cards! */ -+ host->mmc->caps2 |= MMC_CAP2_FORCE_MULTIBLOCK; - #endif - - ret = sdhci_add_host(host); -diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h -index 3b0c33a..e9ae33c 100644 ---- a/include/linux/mmc/host.h -+++ b/include/linux/mmc/host.h -@@ -281,6 +281,7 @@ struct mmc_host { - MMC_CAP2_PACKED_WR) - #define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */ - #define MMC_CAP2_SANITIZE (1 << 15) /* Support Sanitize */ -+#define MMC_CAP2_FORCE_MULTIBLOCK (1 << 31) /* Always use multiblock transfers */ - - mmc_pm_flag_t pm_caps; /* supported pm features */ - --- -1.8.4 - - -From 018fa7cb0987a0a51edac7697d4340b44b55c41e Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 1 Aug 2012 19:02:14 +0100 -Subject: [PATCH 24/82] Add module parameter for missing_status quirk. - sdhci-bcm2708.missing_status=0 may improve interrupt latency - ---- - drivers/mmc/host/sdhci-bcm2708.c | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index 7600c58..4dfa669 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -134,6 +134,7 @@ static inline unsigned long int since_ns(hptime_t t) - static bool allow_highspeed = 1; - static int emmc_clock_freq = BCM2708_EMMC_CLOCK_FREQ; - static bool sync_after_dma = 1; -+static bool missing_status = 1; - - #if 0 - static void hptime_test(void) -@@ -1150,7 +1151,6 @@ static unsigned int sdhci_bcm2708_missing_status(struct sdhci_host *host) - .spurious_crc_acmd51 = sdhci_bcm2708_quirk_spurious_crc, - .voltage_broken = sdhci_bcm2708_quirk_voltage_broken, - .uhs_broken = sdhci_bcm2708_uhs_broken, -- .missing_status = sdhci_bcm2708_missing_status, - }; - - /*****************************************************************************\ -@@ -1189,6 +1189,9 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev) - ret = PTR_ERR(host); - goto err; - } -+ if (missing_status) { -+ sdhci_bcm2708_ops.missing_status = sdhci_bcm2708_missing_status; -+ } - - host->hw_name = "BCM2708_Arasan"; - host->ops = &sdhci_bcm2708_ops; -@@ -1380,6 +1383,7 @@ static void __exit sdhci_drv_exit(void) - module_param(allow_highspeed, bool, 0444); - module_param(emmc_clock_freq, int, 0444); - module_param(sync_after_dma, bool, 0444); -+module_param(missing_status, bool, 0444); - - MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver"); - MODULE_AUTHOR("Broadcom "); -@@ -1389,5 +1393,6 @@ static void __exit sdhci_drv_exit(void) - MODULE_PARM_DESC(allow_highspeed, "Allow high speed transfers modes"); - MODULE_PARM_DESC(emmc_clock_freq, "Specify the speed of emmc clock"); - MODULE_PARM_DESC(sync_after_dma, "Block in driver until dma complete"); -+MODULE_PARM_DESC(missing_status, "Use the missing status quirk"); - - --- -1.8.4 - - -From 123e90f8ef43a524a8c411d03b5ce53e927842ad Mon Sep 17 00:00:00 2001 -From: ddv2005 -Date: Sun, 5 Aug 2012 10:42:12 -0400 -Subject: [PATCH 25/82] Fix spinlock recursion in sdhci-bcm2708.c - ---- - drivers/mmc/host/sdhci-bcm2708.c | 14 +++++++------- - 1 file changed, 7 insertions(+), 7 deletions(-) - -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index 4dfa669..dc94b55 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -643,11 +643,11 @@ static void schci_bcm2708_dma_go(struct sdhci_host *host) - sdhci_bcm2708_platdma_reset(struct sdhci_host *host, struct mmc_data *data) - { - struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); -- unsigned long flags; -+// unsigned long flags; - - BUG_ON(NULL == host); - -- spin_lock_irqsave(&host->lock, flags); -+// spin_lock_irqsave(&host->lock, flags); - - if (host_priv->dma_wanted) { - if (NULL == data) { -@@ -727,7 +727,7 @@ static void schci_bcm2708_dma_go(struct sdhci_host *host) - #endif - } - -- spin_unlock_irqrestore(&host->lock, flags); -+// spin_unlock_irqrestore(&host->lock, flags); - } - - -@@ -740,11 +740,11 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host, - int sg_len; - int sg_ix; - int sg_todo; -- unsigned long flags; -+// unsigned long flags; - - BUG_ON(NULL == host); - -- spin_lock_irqsave(&host->lock, flags); -+// spin_lock_irqsave(&host->lock, flags); - data = host->data; - - #ifdef CHECK_DMA_USE -@@ -769,7 +769,7 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host, - - if (NULL == data) { - DBG("PDMA unused completion - status 0x%X\n", dma_cs); -- spin_unlock_irqrestore(&host->lock, flags); -+// spin_unlock_irqrestore(&host->lock, flags); - return; - } - sg = data->sg; -@@ -862,7 +862,7 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host, - SDHCI_INT_SPACE_AVAIL); - } - } -- spin_unlock_irqrestore(&host->lock, flags); -+// spin_unlock_irqrestore(&host->lock, flags); - } - - static irqreturn_t sdhci_bcm2708_dma_irq(int irq, void *dev_id) --- -1.8.4 - - -From ff26575b25f1e4a32036ba70a458c3efcc6ab5ab Mon Sep 17 00:00:00 2001 +From 7d803bf765a77c95eae0874a9fe62d82775d0343 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 8 May 2013 11:46:50 +0100 -Subject: [PATCH 26/82] enabling the realtime clock 1-wire chip DS1307 and +Subject: [PATCH 12/91] enabling the realtime clock 1-wire chip DS1307 and 1-wire on GPIO4 (as a module) --- @@ -87851,548 +88245,13 @@ index dfcc605..5f869e8 100644 bcm_register_device(&bcm2708_fb_device); bcm_register_device(&bcm2708_usb_device); -- -1.8.4 +1.8.5.1 -From 8b21a09d9e687d42173731835ea36e7a1411649e Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Sun, 12 May 2013 12:27:48 +0100 -Subject: [PATCH 27/82] Add low-latency mode to sdcard driver. Disable with - sdhci-bcm2708.enable_llm=0. Thanks ddv2005. - ---- - drivers/mmc/host/sdhci-bcm2708.c | 15 ++-- - drivers/mmc/host/sdhci.c | 169 ++++++++++++++++++++++++++++++--------- - drivers/mmc/host/sdhci.h | 6 ++ - include/linux/mmc/sdhci.h | 1 + - 4 files changed, 146 insertions(+), 45 deletions(-) - -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index dc94b55..6dacdb3 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -135,6 +135,7 @@ static inline unsigned long int since_ns(hptime_t t) - static int emmc_clock_freq = BCM2708_EMMC_CLOCK_FREQ; - static bool sync_after_dma = 1; - static bool missing_status = 1; -+bool enable_llm = 1; - - #if 0 - static void hptime_test(void) -@@ -871,12 +872,11 @@ static irqreturn_t sdhci_bcm2708_dma_irq(int irq, void *dev_id) - struct sdhci_host *host = dev_id; - struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); - u32 dma_cs; /* control and status register */ -- unsigned long flags; - - BUG_ON(NULL == dev_id); - BUG_ON(NULL == host_priv->dma_chan_base); - -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock(host); - - dma_cs = readl(host_priv->dma_chan_base + BCM2708_DMA_CS); - -@@ -917,8 +917,7 @@ static irqreturn_t sdhci_bcm2708_dma_irq(int irq, void *dev_id) - - result = IRQ_HANDLED; - } -- -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock(host); - - return result; - } -@@ -1193,9 +1192,12 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev) - sdhci_bcm2708_ops.missing_status = sdhci_bcm2708_missing_status; - } - -+ printk("sdhci: %s low-latency mode\n",enable_llm?"Enable":"Disable"); -+ - host->hw_name = "BCM2708_Arasan"; - host->ops = &sdhci_bcm2708_ops; - host->irq = platform_get_irq(pdev, 0); -+ host->second_irq = 0; - - host->quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | - SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | -@@ -1257,11 +1259,12 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev) - host_priv->dma_chan = ret; - - ret = request_irq(host_priv->dma_irq, sdhci_bcm2708_dma_irq, -- IRQF_SHARED, DRIVER_NAME " (dma)", host); -+ 0 /*IRQF_SHARED*/, DRIVER_NAME " (dma)", host); - if (ret) { - dev_err(&pdev->dev, "cannot set DMA IRQ\n"); - goto err_add_dma_irq; - } -+ host->second_irq = host_priv->dma_irq; - DBG("DMA CBs %p handle %08X DMA%d %p DMA IRQ %d\n", - host_priv->cb_base, (unsigned)host_priv->cb_handle, - host_priv->dma_chan, host_priv->dma_chan_base, -@@ -1384,6 +1387,7 @@ static void __exit sdhci_drv_exit(void) - module_param(emmc_clock_freq, int, 0444); - module_param(sync_after_dma, bool, 0444); - module_param(missing_status, bool, 0444); -+module_param(enable_llm, bool, 0444); - - MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver"); - MODULE_AUTHOR("Broadcom "); -@@ -1394,5 +1398,6 @@ static void __exit sdhci_drv_exit(void) - MODULE_PARM_DESC(emmc_clock_freq, "Specify the speed of emmc clock"); - MODULE_PARM_DESC(sync_after_dma, "Block in driver until dma complete"); - MODULE_PARM_DESC(missing_status, "Use the missing status quirk"); -+MODULE_PARM_DESC(enable_llm, "Enable low-latency mode"); - - -diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c -index 4b250ea..48b12ac 100644 ---- a/drivers/mmc/host/sdhci.c -+++ b/drivers/mmc/host/sdhci.c -@@ -132,6 +132,91 @@ static void sdhci_dumpregs(struct sdhci_host *host) - * Low level functions * - * * - \*****************************************************************************/ -+extern bool enable_llm; -+static int sdhci_locked=0; -+void sdhci_spin_lock(struct sdhci_host *host) -+{ -+ spin_lock(&host->lock); -+#ifdef CONFIG_PREEMPT -+ if(enable_llm) -+ { -+ disable_irq_nosync(host->irq); -+ if(host->second_irq) -+ disable_irq_nosync(host->second_irq); -+ local_irq_enable(); -+ } -+#endif -+} -+ -+void sdhci_spin_unlock(struct sdhci_host *host) -+{ -+#ifdef CONFIG_PREEMPT -+ if(enable_llm) -+ { -+ local_irq_disable(); -+ if(host->second_irq) -+ enable_irq(host->second_irq); -+ enable_irq(host->irq); -+ } -+#endif -+ spin_unlock(&host->lock); -+} -+ -+void sdhci_spin_lock_irqsave(struct sdhci_host *host,unsigned long *flags) -+{ -+#ifdef CONFIG_PREEMPT -+ if(enable_llm) -+ { -+ while(sdhci_locked) -+ { -+ preempt_schedule(); -+ } -+ spin_lock_irqsave(&host->lock,*flags); -+ disable_irq(host->irq); -+ if(host->second_irq) -+ disable_irq(host->second_irq); -+ local_irq_enable(); -+ } -+ else -+#endif -+ spin_lock_irqsave(&host->lock,*flags); -+} -+ -+void sdhci_spin_unlock_irqrestore(struct sdhci_host *host,unsigned long flags) -+{ -+#ifdef CONFIG_PREEMPT -+ if(enable_llm) -+ { -+ local_irq_disable(); -+ if(host->second_irq) -+ enable_irq(host->second_irq); -+ enable_irq(host->irq); -+ } -+#endif -+ spin_unlock_irqrestore(&host->lock,flags); -+} -+ -+static void sdhci_spin_enable_schedule(struct sdhci_host *host) -+{ -+#ifdef CONFIG_PREEMPT -+ if(enable_llm) -+ { -+ sdhci_locked = 1; -+ preempt_enable(); -+ } -+#endif -+} -+ -+static void sdhci_spin_disable_schedule(struct sdhci_host *host) -+{ -+#ifdef CONFIG_PREEMPT -+ if(enable_llm) -+ { -+ preempt_disable(); -+ sdhci_locked = 0; -+ } -+#endif -+} - - static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set) - { -@@ -301,7 +386,7 @@ static void sdhci_led_control(struct led_classdev *led, - struct sdhci_host *host = container_of(led, struct sdhci_host, led); - unsigned long flags; - -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - - if (host->runtime_suspended) - goto out; -@@ -311,7 +396,7 @@ static void sdhci_led_control(struct led_classdev *led, - else - sdhci_activate_led(host); - out: -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - } - #endif - -@@ -1019,7 +1104,9 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) - return; - } - timeout--; -+ sdhci_spin_enable_schedule(host); - mdelay(1); -+ sdhci_spin_disable_schedule(host); - } - DBG("send cmd %d - wait 0x%X irq 0x%x\n", cmd->opcode, mask, - sdhci_readl(host, SDHCI_INT_STATUS)); -@@ -1246,7 +1333,9 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) - return; - } - timeout--; -+ sdhci_spin_enable_schedule(host); - mdelay(1); -+ sdhci_spin_disable_schedule(host); - } - - clk |= SDHCI_CLOCK_CARD_EN; -@@ -1347,7 +1436,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) - - sdhci_runtime_pm_get(host); - -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - - WARN_ON(host->mrq != NULL); - -@@ -1405,9 +1494,9 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) - mmc->card->type == MMC_TYPE_MMC ? - MMC_SEND_TUNING_BLOCK_HS200 : - MMC_SEND_TUNING_BLOCK; -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - sdhci_execute_tuning(mmc, tuning_opcode); -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - - /* Restore original mmc_request structure */ - host->mrq = mrq; -@@ -1421,7 +1510,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) - } - - mmiowb(); -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - } - - static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) -@@ -1430,10 +1519,10 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) - int vdd_bit = -1; - u8 ctrl; - -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - - if (host->flags & SDHCI_DEVICE_DEAD) { -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - if (host->vmmc && ios->power_mode == MMC_POWER_OFF) - mmc_regulator_set_ocr(host->mmc, host->vmmc, 0); - return; -@@ -1460,9 +1549,9 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) - vdd_bit = sdhci_set_power(host, ios->vdd); - - if (host->vmmc && vdd_bit != -1) { -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit); -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - } - - if (host->ops->platform_send_init_74_clocks) -@@ -1599,7 +1688,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) - sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); - - mmiowb(); -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - } - - static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -@@ -1647,7 +1736,7 @@ static int sdhci_check_ro(struct sdhci_host *host) - unsigned long flags; - int is_readonly; - -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - - if (host->flags & SDHCI_DEVICE_DEAD) - is_readonly = 0; -@@ -1657,7 +1746,7 @@ static int sdhci_check_ro(struct sdhci_host *host) - is_readonly = !(sdhci_readl(host, SDHCI_PRESENT_STATE) - & SDHCI_WRITE_PROTECT); - -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - - /* This quirk needs to be replaced by a callback-function later */ - return host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT ? -@@ -1730,9 +1819,9 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) - struct sdhci_host *host = mmc_priv(mmc); - unsigned long flags; - -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - sdhci_enable_sdio_irq_nolock(host, enable); -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - } - - static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, -@@ -2080,7 +2169,7 @@ static void sdhci_card_event(struct mmc_host *mmc) - if (host->ops->card_event) - host->ops->card_event(host); - -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - - /* Check host->mrq first in case we are runtime suspended */ - if (host->mrq && !sdhci_do_get_cd(host)) { -@@ -2096,7 +2185,7 @@ static void sdhci_card_event(struct mmc_host *mmc) - tasklet_schedule(&host->finish_tasklet); - } - -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - } - - static const struct mmc_host_ops sdhci_ops = { -@@ -2135,14 +2224,14 @@ static void sdhci_tasklet_finish(unsigned long param) - - host = (struct sdhci_host*)param; - -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - - /* - * If this tasklet gets rescheduled while running, it will - * be run again afterwards but without any active request. - */ - if (!host->mrq) { -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - return; - } - -@@ -2180,7 +2269,7 @@ static void sdhci_tasklet_finish(unsigned long param) - #endif - - mmiowb(); -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - - mmc_request_done(host->mmc, mrq); - sdhci_runtime_pm_put(host); -@@ -2193,7 +2282,7 @@ static void sdhci_timeout_timer(unsigned long data) - - host = (struct sdhci_host*)data; - -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - - if (host->mrq) { - pr_err("%s: Timeout waiting for hardware " -@@ -2214,7 +2303,7 @@ static void sdhci_timeout_timer(unsigned long data) - } - - mmiowb(); -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - } - - static void sdhci_tuning_timer(unsigned long data) -@@ -2224,11 +2313,11 @@ static void sdhci_tuning_timer(unsigned long data) - - host = (struct sdhci_host *)data; - -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - - host->flags |= SDHCI_NEEDS_RETUNING; - -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - } - - /*****************************************************************************\ -@@ -2452,10 +2541,10 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) - u32 intmask, unexpected = 0; - int cardint = 0, max_loops = 16; - -- spin_lock(&host->lock); -+ sdhci_spin_lock(host); - - if (host->runtime_suspended) { -- spin_unlock(&host->lock); -+ sdhci_spin_unlock(host); - pr_warning("%s: got irq while runtime suspended\n", - mmc_hostname(host->mmc)); - return IRQ_HANDLED; -@@ -2559,7 +2648,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) - if (intmask && --max_loops) - goto again; - out: -- spin_unlock(&host->lock); -+ sdhci_spin_unlock(host); - - if (unexpected) { - pr_err("%s: Unexpected interrupt 0x%08x.\n", -@@ -2660,7 +2749,7 @@ int sdhci_resume_host(struct sdhci_host *host) - } - - if (!device_may_wakeup(mmc_dev(host->mmc))) { -- ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, -+ ret = request_irq(host->irq, sdhci_irq, 0 /*IRQF_SHARED*/, - mmc_hostname(host->mmc), host); - if (ret) - return ret; -@@ -2737,15 +2826,15 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) - host->flags &= ~SDHCI_NEEDS_RETUNING; - } - -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - - synchronize_irq(host->irq); - -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - host->runtime_suspended = true; -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - - return ret; - } -@@ -2771,16 +2860,16 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) - sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios); - if ((host_flags & SDHCI_PV_ENABLED) && - !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) { -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - sdhci_enable_preset_value(host, true); -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - } - - /* Set the re-tuning expiration flag */ - if (host->flags & SDHCI_USING_RETUNING_TIMER) - host->flags |= SDHCI_NEEDS_RETUNING; - -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - - host->runtime_suspended = false; - -@@ -2791,7 +2880,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) - /* Enable Card Detection */ - sdhci_enable_card_detection(host); - -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - - return ret; - } -@@ -3286,8 +3375,8 @@ int sdhci_add_host(struct sdhci_host *host) - - sdhci_init(host, 0); - -- ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, -- mmc_hostname(mmc), host); -+ ret = request_irq(host->irq, sdhci_irq, 0 /*IRQF_SHARED*/, -+ mmc_hostname(mmc), host); - if (ret) { - pr_err("%s: Failed to request IRQ %d: %d\n", - mmc_hostname(mmc), host->irq, ret); -@@ -3348,7 +3437,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) - unsigned long flags; - - if (dead) { -- spin_lock_irqsave(&host->lock, flags); -+ sdhci_spin_lock_irqsave(host, &flags); - - host->flags |= SDHCI_DEVICE_DEAD; - -@@ -3360,7 +3449,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) - tasklet_schedule(&host->finish_tasklet); - } - -- spin_unlock_irqrestore(&host->lock, flags); -+ sdhci_spin_unlock_irqrestore(host, flags); - } - - sdhci_disable_card_detection(host); -diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h -index 8a02ec8..4218039 100644 ---- a/drivers/mmc/host/sdhci.h -+++ b/drivers/mmc/host/sdhci.h -@@ -442,4 +442,10 @@ static inline void *sdhci_priv(struct sdhci_host *host) - extern int sdhci_runtime_resume_host(struct sdhci_host *host); - #endif - -+extern void sdhci_spin_lock_irqsave(struct sdhci_host *host,unsigned long *flags); -+extern void sdhci_spin_unlock_irqrestore(struct sdhci_host *host,unsigned long flags); -+extern void sdhci_spin_lock(struct sdhci_host *host); -+extern void sdhci_spin_unlock(struct sdhci_host *host); -+ -+ - #endif /* __SDHCI_HW_H */ -diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h -index aad2393..0aeeef8 100644 ---- a/include/linux/mmc/sdhci.h -+++ b/include/linux/mmc/sdhci.h -@@ -100,6 +100,7 @@ struct sdhci_host { - #define SDHCI_QUIRK2_BROKEN_HOST_CONTROL (1<<5) - - int irq; /* Device IRQ */ -+ int second_irq; /* Additional IRQ to disable/enable in low-latency mode */ - void __iomem *ioaddr; /* Mapped address */ - - const struct sdhci_ops *ops; /* Low level hw interface */ --- -1.8.4 - - -From 10573426156ce4a50557fcd3b3e97406f13434d7 Mon Sep 17 00:00:00 2001 +From 025cab40ed0bdccfc6d544e9cb894116cf8ef658 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 3 Jul 2013 00:46:42 +0100 -Subject: [PATCH 28/82] Add FIQ patch to dwc_otg driver. Enable with +Subject: [PATCH 13/91] Add FIQ patch to dwc_otg driver. Enable with dwc_otg.fiq_fix_enable=1. Should give about 10% more ARM performance. Thanks to Gordon and Costas @@ -88423,10 +88282,10 @@ Subject: [PATCH 28/82] Add FIQ patch to dwc_otg driver. Enable with create mode 100755 drivers/usb/host/dwc_otg/dwc_otg_mphi_fix.h diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index 54da673..d605c86 100644 +index e28d7b2..981adf0 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig -@@ -380,6 +380,7 @@ config ARCH_BCM2708 +@@ -381,6 +381,7 @@ config ARCH_BCM2708 select ARM_ERRATA_411920 select MACH_BCM2708 select VC4 @@ -89549,13 +89408,13 @@ index e46d9bb..6b2c7d0 100644 struct lm_device *lmdev; #elif defined(PCI_INTERFACE) -- -1.8.4 +1.8.5.1 -From 07635c35a799cd4b81b18ebf23b38b006a2b0316 Mon Sep 17 00:00:00 2001 +From 9daf0bea7d05908d068aeece8be7e450cac51d55 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Sat, 8 Sep 2012 15:17:53 +0100 -Subject: [PATCH 31/82] Avoid dynamic memory allocation for channel lock in USB +Subject: [PATCH 16/91] Avoid dynamic memory allocation for channel lock in USB driver. Thanks ddv2005. --- @@ -89659,13 +89518,13 @@ index b7b6b0c..76b5085 100644 /** -- -1.8.4 +1.8.5.1 -From 513857d2806c9cdc7405e858f2c0d4404b79e508 Mon Sep 17 00:00:00 2001 +From 734186aa128c8c97ccfe6cb116bf87efc1f3a1ca Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 3 Jul 2013 00:49:20 +0100 -Subject: [PATCH 32/82] Add cpufreq driver +Subject: [PATCH 17/91] Add cpufreq driver --- arch/arm/Kconfig | 1 + @@ -89676,11 +89535,11 @@ Subject: [PATCH 32/82] Add cpufreq driver create mode 100755 drivers/cpufreq/bcm2835-cpufreq.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig -index d605c86..43c34dc 100644 +index 981adf0..0e9efaf 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig -@@ -376,6 +376,7 @@ config ARCH_BCM2708 - select HAVE_SCHED_CLOCK +@@ -377,6 +377,7 @@ config ARCH_BCM2708 + select NEED_MACH_GPIO_H select NEED_MACH_MEMORY_H select CLKDEV_LOOKUP + select ARCH_HAS_CPUFREQ @@ -89964,20 +89823,20 @@ index 0000000..7bc55bd +module_init(bcm2835_cpufreq_module_init); +module_exit(bcm2835_cpufreq_module_exit); -- -1.8.4 +1.8.5.1 -From 48f4a94476f55b5c51c2c51268cf8d7e28247f19 Mon Sep 17 00:00:00 2001 +From e1863ec8de63cba2532880e189c4cbd14a7bbd2d Mon Sep 17 00:00:00 2001 From: popcornmix Date: Fri, 12 Apr 2013 23:58:47 +0100 -Subject: [PATCH 33/82] config: add missing options from 3.6.y kernel +Subject: [PATCH 18/91] config: add missing options from 3.6.y kernel --- - arch/arm/configs/bcmrpi_defconfig | 637 +++++++++++++++++++++++++++++++++----- - 1 file changed, 551 insertions(+), 86 deletions(-) + arch/arm/configs/bcmrpi_defconfig | 733 +++++++++++++++++++++++++++++++++----- + 1 file changed, 636 insertions(+), 97 deletions(-) diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig -index 31f5afaa..1db7746 100644 +index 31f5afaa..f4fba45 100644 --- a/arch/arm/configs/bcmrpi_defconfig +++ b/arch/arm/configs/bcmrpi_defconfig @@ -1,11 +1,17 @@ @@ -90001,10 +89860,11 @@ index 31f5afaa..1db7746 100644 CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_CGROUP_FREEZER=y -@@ -15,22 +21,23 @@ CONFIG_RESOURCE_COUNTERS=y +@@ -15,29 +21,42 @@ CONFIG_RESOURCE_COUNTERS=y CONFIG_BLK_CGROUP=y CONFIG_NAMESPACES=y CONFIG_SCHED_AUTOGROUP=y ++CONFIG_RELAY=y +CONFIG_BLK_DEV_INITRD=y CONFIG_EMBEDDED=y # CONFIG_COMPAT_BRK is not set @@ -90017,7 +89877,7 @@ index 31f5afaa..1db7746 100644 CONFIG_MODULE_UNLOAD=y CONFIG_MODVERSIONS=y CONFIG_MODULE_SRCVERSION_ALL=y - # CONFIG_BLK_DEV_BSG is not set +-# CONFIG_BLK_DEV_BSG is not set CONFIG_BLK_DEV_THROTTLING=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_MAC_PARTITION=y @@ -90025,12 +89885,17 @@ index 31f5afaa..1db7746 100644 CONFIG_ARCH_BCM2708=y -CONFIG_NO_HZ=y -CONFIG_HIGH_RES_TIMERS=y ++CONFIG_PREEMPT=y CONFIG_AEABI=y ++CONFIG_CLEANCACHE=y ++CONFIG_FRONTSWAP=y ++CONFIG_UACCESS_WITH_MEMCPY=y CONFIG_SECCOMP=y CONFIG_CC_STACKPROTECTOR=y -@@ -38,6 +45,14 @@ CONFIG_ZBOOT_ROM_TEXT=0x0 + CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 - CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext3 rootwait" +-CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext3 rootwait" ++CONFIG_CMDLINE="console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait" CONFIG_KEXEC=y +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_STAT=m @@ -90043,9 +89908,24 @@ index 31f5afaa..1db7746 100644 CONFIG_CPU_IDLE=y CONFIG_VFP=y CONFIG_BINFMT_MISC=m -@@ -52,15 +67,257 @@ CONFIG_IP_PNP=y +@@ -48,19 +67,276 @@ CONFIG_XFRM_USER=y + CONFIG_NET_KEY=m + CONFIG_INET=y + CONFIG_IP_MULTICAST=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_ROUTE_MULTIPATH=y ++CONFIG_IP_ROUTE_VERBOSE=y + CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_IP_PNP_RARP=y ++CONFIG_NET_IPIP=m ++CONFIG_NET_IPGRE_DEMUX=m ++CONFIG_NET_IPGRE=m ++CONFIG_IP_MROUTE=y ++CONFIG_IP_MROUTE_MULTIPLE_TABLES=y ++CONFIG_IP_PIMSM_V1=y ++CONFIG_IP_PIMSM_V2=y CONFIG_SYN_COOKIES=y -# CONFIG_INET_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET_XFRM_MODE_TUNNEL is not set @@ -90066,6 +89946,9 @@ index 31f5afaa..1db7746 100644 +CONFIG_INET6_ESP=m +CONFIG_INET6_IPCOMP=m +CONFIG_IPV6_MULTIPLE_TABLES=y ++CONFIG_IPV6_MROUTE=y ++CONFIG_IPV6_MROUTE_MULTIPLE_TABLES=y ++CONFIG_IPV6_PIMSM_V2=y +CONFIG_NETFILTER=y +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_ZONES=y @@ -90121,6 +90004,7 @@ index 31f5afaa..1db7746 100644 +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m ++CONFIG_NETFILTER_XT_MATCH_IPVS=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m @@ -90307,7 +90191,7 @@ index 31f5afaa..1db7746 100644 CONFIG_IRCOMM=m CONFIG_IRDA_ULTRA=y CONFIG_IRDA_CACHE_LAST_LSAP=y -@@ -73,8 +330,6 @@ CONFIG_USB_IRDA=m +@@ -73,8 +349,6 @@ CONFIG_USB_IRDA=m CONFIG_SIGMATEL_FIR=m CONFIG_MCS_FIR=m CONFIG_BT=m @@ -90316,7 +90200,7 @@ index 31f5afaa..1db7746 100644 CONFIG_BT_RFCOMM=m CONFIG_BT_RFCOMM_TTY=y CONFIG_BT_BNEP=m -@@ -89,21 +344,28 @@ CONFIG_BT_HCIVHCI=m +@@ -89,35 +363,99 @@ CONFIG_BT_HCIVHCI=m CONFIG_BT_MRVL=m CONFIG_BT_MRVL_SDIO=m CONFIG_BT_ATH3K=m @@ -90332,7 +90216,7 @@ index 31f5afaa..1db7746 100644 CONFIG_NET_9P=m CONFIG_NFC=m CONFIG_NFC_PN533=m - CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +-CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_CMA=y @@ -90345,15 +90229,31 @@ index 31f5afaa..1db7746 100644 -CONFIG_MISC_DEVICES=y CONFIG_SCSI=y # CONFIG_SCSI_PROC_FS is not set - CONFIG_BLK_DEV_SD=m -@@ -111,13 +373,50 @@ CONFIG_BLK_DEV_SR=m +-CONFIG_BLK_DEV_SD=m ++CONFIG_BLK_DEV_SD=y ++CONFIG_CHR_DEV_ST=m ++CONFIG_CHR_DEV_OSST=m + CONFIG_BLK_DEV_SR=m CONFIG_SCSI_MULTI_LUN=y - # CONFIG_SCSI_LOWLEVEL is not set +-# CONFIG_SCSI_LOWLEVEL is not set ++CONFIG_SCSI_ISCSI_ATTRS=y ++CONFIG_ISCSI_TCP=m ++CONFIG_ISCSI_BOOT_SYSFS=m CONFIG_MD=y ++CONFIG_MD_LINEAR=m ++CONFIG_MD_RAID0=m +CONFIG_BLK_DEV_DM=m +CONFIG_DM_CRYPT=m ++CONFIG_DM_SNAPSHOT=m ++CONFIG_DM_MIRROR=m ++CONFIG_DM_RAID=m ++CONFIG_DM_LOG_USERSPACE=m ++CONFIG_DM_ZERO=m ++CONFIG_DM_DELAY=m CONFIG_NETDEVICES=y ++CONFIG_BONDING=m +CONFIG_DUMMY=m ++CONFIG_MACVLAN=m +CONFIG_NETCONSOLE=m CONFIG_TUN=m -CONFIG_PHYLIB=m @@ -90364,10 +90264,16 @@ index 31f5afaa..1db7746 100644 +CONFIG_PPP=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m ++CONFIG_PPP_FILTER=y ++CONFIG_PPP_MPPE=m ++CONFIG_PPP_MULTILINK=y ++CONFIG_PPPOE=m ++CONFIG_PPPOL2TP=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_SLIP=m +CONFIG_SLIP_COMPRESSED=y ++CONFIG_SLIP_SMART=y +CONFIG_USB_CATC=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m @@ -90377,6 +90283,7 @@ index 31f5afaa..1db7746 100644 +CONFIG_USB_NET_AX8817X=m +CONFIG_USB_NET_CDCETHER=m +CONFIG_USB_NET_CDC_EEM=m ++CONFIG_USB_NET_CDC_NCM=m +CONFIG_USB_NET_CDC_MBIM=m +CONFIG_USB_NET_DM9601=m +CONFIG_USB_NET_SMSC75XX=m @@ -90401,7 +90308,7 @@ index 31f5afaa..1db7746 100644 CONFIG_LIBERTAS_THINFIRM=m CONFIG_LIBERTAS_THINFIRM_USB=m CONFIG_AT76C50X_USB=m -@@ -125,14 +424,18 @@ CONFIG_USB_ZD1201=m +@@ -125,14 +463,17 @@ CONFIG_USB_ZD1201=m CONFIG_USB_NET_RNDIS_WLAN=m CONFIG_RTL8187=m CONFIG_MAC80211_HWSIM=m @@ -90413,7 +90320,6 @@ index 31f5afaa..1db7746 100644 +CONFIG_ATH6KL=m +CONFIG_ATH6KL_USB=m +CONFIG_AR5523=m -+CONFIG_ATH10K=m CONFIG_B43=m +# CONFIG_B43_PHY_N is not set CONFIG_B43LEGACY=m @@ -90422,7 +90328,7 @@ index 31f5afaa..1db7746 100644 CONFIG_LIBERTAS=m CONFIG_LIBERTAS_USB=m CONFIG_LIBERTAS_SDIO=m -@@ -143,56 +446,25 @@ CONFIG_RT2500USB=m +@@ -143,56 +484,25 @@ CONFIG_RT2500USB=m CONFIG_RT73USB=m CONFIG_RT2800USB=m CONFIG_RT2800USB_RT53XX=y @@ -90477,7 +90383,7 @@ index 31f5afaa..1db7746 100644 +CONFIG_INPUT_JOYSTICK=y +CONFIG_JOYSTICK_IFORCE=m +CONFIG_JOYSTICK_IFORCE_USB=y -+CONFIG_JOYSTICK_XPAD=m ++CONFIG_JOYSTICK_XPAD=y +CONFIG_JOYSTICK_XPAD_FF=y CONFIG_INPUT_MISC=y CONFIG_INPUT_AD714X=m @@ -90485,33 +90391,43 @@ index 31f5afaa..1db7746 100644 CONFIG_INPUT_ATI_REMOTE2=m CONFIG_INPUT_KEYSPAN_REMOTE=m CONFIG_INPUT_POWERMATE=m -@@ -202,18 +474,17 @@ CONFIG_INPUT_UINPUT=m - CONFIG_INPUT_GPIO_ROTARY_ENCODER=m - CONFIG_INPUT_ADXL34X=m - CONFIG_INPUT_CMA3000=m --CONFIG_SERIO=m --CONFIG_SERIO_RAW=m --CONFIG_GAMEPORT=m --CONFIG_GAMEPORT_NS558=m --CONFIG_GAMEPORT_L4=m -+# CONFIG_SERIO is not set - CONFIG_VT_HW_CONSOLE_BINDING=y - # CONFIG_LEGACY_PTYS is not set +@@ -212,21 +522,189 @@ CONFIG_VT_HW_CONSOLE_BINDING=y # CONFIG_DEVKMEM is not set CONFIG_SERIAL_AMBA_PL011=y CONFIG_SERIAL_AMBA_PL011_CONSOLE=y -# CONFIG_HW_RANDOM is not set ++CONFIG_TTY_PRINTK=y +CONFIG_HW_RANDOM=y -+CONFIG_HW_RANDOM_BCM2708=y ++CONFIG_HW_RANDOM_BCM2708=m CONFIG_RAW_DRIVER=y +CONFIG_BRCM_CHAR_DRIVERS=y +CONFIG_BCM_VC_CMA=y CONFIG_I2C=y CONFIG_I2C_CHARDEV=m CONFIG_I2C_BCM2708=m -@@ -222,9 +493,152 @@ CONFIG_SPI_BCM2708=m - CONFIG_SPI_SPIDEV=m + CONFIG_SPI=y + CONFIG_SPI_BCM2708=m +-CONFIG_SPI_SPIDEV=m ++CONFIG_SPI_SPIDEV=y CONFIG_GPIO_SYSFS=y ++CONFIG_W1=m ++CONFIG_W1_MASTER_DS2490=m ++CONFIG_W1_MASTER_DS2482=m ++CONFIG_W1_MASTER_DS1WM=m ++CONFIG_W1_MASTER_GPIO=m ++CONFIG_W1_SLAVE_THERM=m ++CONFIG_W1_SLAVE_SMEM=m ++CONFIG_W1_SLAVE_DS2408=m ++CONFIG_W1_SLAVE_DS2413=m ++CONFIG_W1_SLAVE_DS2423=m ++CONFIG_W1_SLAVE_DS2431=m ++CONFIG_W1_SLAVE_DS2433=m ++CONFIG_W1_SLAVE_DS2760=m ++CONFIG_W1_SLAVE_DS2780=m ++CONFIG_W1_SLAVE_DS2781=m ++CONFIG_W1_SLAVE_DS28E04=m ++CONFIG_W1_SLAVE_BQ27000=m ++CONFIG_BATTERY_DS2760=m # CONFIG_HWMON is not set +CONFIG_THERMAL=y +CONFIG_THERMAL_BCM2835=y @@ -90590,7 +90506,6 @@ index 31f5afaa..1db7746 100644 +CONFIG_USB_STKWEBCAM=m +CONFIG_USB_S2255=m +CONFIG_USB_SN9C102=m -+CONFIG_VIDEO_USBTV=m +CONFIG_VIDEO_PVRUSB2=m +CONFIG_VIDEO_HDPVR=m +CONFIG_VIDEO_TLG2300=m @@ -90650,6 +90565,7 @@ index 31f5afaa..1db7746 100644 +CONFIG_VIDEO_EM28XX_DVB=m +CONFIG_RADIO_SI470X=y +CONFIG_USB_SI470X=m ++CONFIG_I2C_SI470X=m +CONFIG_USB_MR800=m +CONFIG_USB_DSBR=m +CONFIG_RADIO_SHARK=m @@ -90657,17 +90573,23 @@ index 31f5afaa..1db7746 100644 +CONFIG_RADIO_SI4713=m +CONFIG_USB_KEENE=m +CONFIG_USB_MA901=m ++CONFIG_RADIO_TEA5764=m +CONFIG_RADIO_SAA7706H=m +CONFIG_RADIO_TEF6862=m ++CONFIG_RADIO_WL1273=m +CONFIG_RADIO_WL128X=m CONFIG_FB=y CONFIG_FB_BCM2708=y ++# CONFIG_BACKLIGHT_GENERIC is not set CONFIG_FRAMEBUFFER_CONSOLE=y -@@ -250,9 +664,9 @@ CONFIG_SND_USB_AUDIO=m + CONFIG_LOGO=y + # CONFIG_LOGO_LINUX_MONO is not set +@@ -249,10 +727,10 @@ CONFIG_SND_BCM2835=m + CONFIG_SND_USB_AUDIO=m CONFIG_SND_USB_UA101=m CONFIG_SND_USB_CAIAQ=m ++CONFIG_SND_USB_CAIAQ_INPUT=y CONFIG_SND_USB_6FIRE=m -+CONFIG_SND_USB_HIFACE=m CONFIG_SOUND_PRIME=m -CONFIG_HID_PID=y -CONFIG_USB_HIDDEV=y @@ -90675,15 +90597,7 @@ index 31f5afaa..1db7746 100644 CONFIG_HID_A4TECH=m CONFIG_HID_ACRUX=m CONFIG_HID_APPLE=m -@@ -263,6 +677,7 @@ CONFIG_HID_CYPRESS=m - CONFIG_HID_DRAGONRISE=m - CONFIG_HID_EMS_FF=m - CONFIG_HID_ELECOM=m -+CONFIG_HID_ELO=m - CONFIG_HID_EZKEY=m - CONFIG_HID_HOLTEK=m - CONFIG_HID_KEYTOUCH=m -@@ -283,7 +698,6 @@ CONFIG_HID_ORTEK=m +@@ -283,7 +761,6 @@ CONFIG_HID_ORTEK=m CONFIG_HID_PANTHERLORD=m CONFIG_HID_PETALYNX=m CONFIG_HID_PICOLCD=m @@ -90691,7 +90605,7 @@ index 31f5afaa..1db7746 100644 CONFIG_HID_ROCCAT=m CONFIG_HID_SAMSUNG=m CONFIG_HID_SONY=m -@@ -292,15 +706,19 @@ CONFIG_HID_SUNPLUS=m +@@ -292,15 +769,18 @@ CONFIG_HID_SUNPLUS=m CONFIG_HID_GREENASIA=m CONFIG_HID_SMARTJOYPLUS=m CONFIG_HID_TOPSEED=m @@ -90701,9 +90615,9 @@ index 31f5afaa..1db7746 100644 CONFIG_HID_WIIMOTE=m CONFIG_HID_ZEROPLUS=m CONFIG_HID_ZYDACRON=m +-CONFIG_USB=y +CONFIG_HID_PID=y +CONFIG_USB_HIDDEV=y - CONFIG_USB=y CONFIG_USB_ANNOUNCE_NEW_DEVICES=y CONFIG_USB_MON=m CONFIG_USB_DWCOTG=y @@ -90711,7 +90625,7 @@ index 31f5afaa..1db7746 100644 CONFIG_USB_STORAGE=y CONFIG_USB_STORAGE_REALTEK=m CONFIG_USB_STORAGE_DATAFAB=m -@@ -315,8 +733,6 @@ CONFIG_USB_STORAGE_ONETOUCH=m +@@ -315,8 +795,6 @@ CONFIG_USB_STORAGE_ONETOUCH=m CONFIG_USB_STORAGE_KARMA=m CONFIG_USB_STORAGE_CYPRESS_ATACB=m CONFIG_USB_STORAGE_ENE_UB6250=m @@ -90720,7 +90634,7 @@ index 31f5afaa..1db7746 100644 CONFIG_USB_MDC800=m CONFIG_USB_MICROTEK=m CONFIG_USB_SERIAL=m -@@ -337,6 +753,7 @@ CONFIG_USB_SERIAL_IPAQ=m +@@ -337,6 +815,7 @@ CONFIG_USB_SERIAL_IPAQ=m CONFIG_USB_SERIAL_IR=m CONFIG_USB_SERIAL_EDGEPORT=m CONFIG_USB_SERIAL_EDGEPORT_TI=m @@ -90728,7 +90642,7 @@ index 31f5afaa..1db7746 100644 CONFIG_USB_SERIAL_GARMIN=m CONFIG_USB_SERIAL_IPW=m CONFIG_USB_SERIAL_IUU=m -@@ -345,6 +762,7 @@ CONFIG_USB_SERIAL_KEYSPAN=m +@@ -345,6 +824,7 @@ CONFIG_USB_SERIAL_KEYSPAN=m CONFIG_USB_SERIAL_KLSI=m CONFIG_USB_SERIAL_KOBIL_SCT=m CONFIG_USB_SERIAL_MCT_U232=m @@ -90736,7 +90650,7 @@ index 31f5afaa..1db7746 100644 CONFIG_USB_SERIAL_MOS7720=m CONFIG_USB_SERIAL_MOS7840=m CONFIG_USB_SERIAL_MOTOROLA=m -@@ -366,8 +784,12 @@ CONFIG_USB_SERIAL_OPTION=m +@@ -366,8 +846,12 @@ CONFIG_USB_SERIAL_OPTION=m CONFIG_USB_SERIAL_OMNINET=m CONFIG_USB_SERIAL_OPTICON=m CONFIG_USB_SERIAL_VIVOPAY_SERIAL=m @@ -90749,7 +90663,7 @@ index 31f5afaa..1db7746 100644 CONFIG_USB_SERIAL_DEBUG=m CONFIG_USB_EMI62=m CONFIG_USB_EMI26=m -@@ -389,17 +811,60 @@ CONFIG_USB_TEST=m +@@ -389,17 +873,71 @@ CONFIG_USB_TEST=m CONFIG_USB_ISIGHTFW=m CONFIG_USB_YUREX=m CONFIG_MMC=y @@ -90762,6 +90676,7 @@ index 31f5afaa..1db7746 100644 -CONFIG_LEDS_TRIGGER_TIMER=m -CONFIG_LEDS_TRIGGER_HEARTBEAT=m -CONFIG_LEDS_TRIGGER_DEFAULT_ON=m ++CONFIG_MMC_SPI=m +CONFIG_LEDS_GPIO=m +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_ONESHOT=y @@ -90782,7 +90697,6 @@ index 31f5afaa..1db7746 100644 +CONFIG_RTC_DRV_ISL1208=m +CONFIG_RTC_DRV_ISL12022=m +CONFIG_RTC_DRV_X1205=m -+CONFIG_RTC_DRV_PCF2127=m +CONFIG_RTC_DRV_PCF8523=m +CONFIG_RTC_DRV_PCF8563=m +CONFIG_RTC_DRV_PCF8583=m @@ -90808,13 +90722,24 @@ index 31f5afaa..1db7746 100644 CONFIG_UIO_PDRV=m CONFIG_UIO_PDRV_GENIRQ=m +CONFIG_STAGING=y ++CONFIG_W35UND=m ++CONFIG_PRISM2_USB=m ++CONFIG_R8712U=m ++CONFIG_VT6656=m ++CONFIG_SPEAKUP=m ++CONFIG_SPEAKUP_SYNTH_SOFT=m +CONFIG_STAGING_MEDIA=y ++CONFIG_DVB_AS102=m +CONFIG_LIRC_STAGING=y ++CONFIG_LIRC_IGORPLUGUSB=m ++CONFIG_LIRC_IMON=m +CONFIG_LIRC_RPI=m ++CONFIG_LIRC_SASEM=m ++CONFIG_LIRC_SERIAL=m # CONFIG_IOMMU_SUPPORT is not set CONFIG_EXT4_FS=y CONFIG_EXT4_FS_POSIX_ACL=y -@@ -422,6 +887,8 @@ CONFIG_BTRFS_FS=m +@@ -422,6 +960,8 @@ CONFIG_BTRFS_FS=m CONFIG_BTRFS_FS_POSIX_ACL=y CONFIG_NILFS2_FS=m CONFIG_FANOTIFY=y @@ -90823,7 +90748,18 @@ index 31f5afaa..1db7746 100644 CONFIG_AUTOFS4_FS=y CONFIG_FUSE_FS=m CONFIG_CUSE=m -@@ -444,21 +911,22 @@ CONFIG_SQUASHFS=m +@@ -437,28 +977,32 @@ CONFIG_MSDOS_FS=y + CONFIG_VFAT_FS=y + CONFIG_FAT_DEFAULT_IOCHARSET="ascii" + CONFIG_NTFS_FS=m ++CONFIG_NTFS_RW=y + CONFIG_TMPFS=y + CONFIG_TMPFS_POSIX_ACL=y + CONFIG_CONFIGFS_FS=y ++CONFIG_ECRYPT_FS=m ++CONFIG_HFS_FS=m ++CONFIG_HFSPLUS_FS=m + CONFIG_SQUASHFS=m CONFIG_SQUASHFS_XATTR=y CONFIG_SQUASHFS_LZO=y CONFIG_SQUASHFS_XZ=y @@ -90846,46 +90782,55 @@ index 31f5afaa..1db7746 100644 -CONFIG_PARTITION_ADVANCED=y -CONFIG_MAC_PARTITION=y -CONFIG_EFI_PARTITION=y -+CONFIG_9P_FS_SECURITY=y CONFIG_NLS_DEFAULT="utf8" CONFIG_NLS_CODEPAGE_437=y CONFIG_NLS_CODEPAGE_737=m -@@ -498,15 +966,15 @@ CONFIG_NLS_ISO8859_15=m +@@ -497,39 +1041,34 @@ CONFIG_NLS_ISO8859_14=m + CONFIG_NLS_ISO8859_15=m CONFIG_NLS_KOI8_R=m CONFIG_NLS_KOI8_U=m - CONFIG_NLS_UTF8=m +-CONFIG_NLS_UTF8=m +CONFIG_DLM=m CONFIG_PRINTK_TIME=y --CONFIG_DETECT_HUNG_TASK=y --CONFIG_TIMER_STATS=y --CONFIG_DEBUG_STACK_USAGE=y +CONFIG_BOOT_PRINTK_DELAY=y - CONFIG_DEBUG_INFO=y -+CONFIG_DEBUG_STACK_USAGE=y - CONFIG_DEBUG_MEMORY_INIT=y ++CONFIG_DEBUG_FS=y ++CONFIG_DEBUG_MEMORY_INIT=y + CONFIG_DETECT_HUNG_TASK=y + CONFIG_TIMER_STATS=y +-CONFIG_DEBUG_STACK_USAGE=y +-CONFIG_DEBUG_INFO=y +-CONFIG_DEBUG_MEMORY_INIT=y -CONFIG_BOOT_PRINTK_DELAY=y -+CONFIG_DETECT_HUNG_TASK=y -+CONFIG_TIMER_STATS=y ++# CONFIG_DEBUG_PREEMPT is not set CONFIG_LATENCYTOP=y -CONFIG_SYSCTL_SYSCALL_CHECK=y - CONFIG_IRQSOFF_TRACER=y - CONFIG_SCHED_TRACER=y - CONFIG_STACK_TRACER=y -@@ -516,20 +984,17 @@ CONFIG_KGDB=y +-CONFIG_IRQSOFF_TRACER=y +-CONFIG_SCHED_TRACER=y +-CONFIG_STACK_TRACER=y +-CONFIG_BLK_DEV_IO_TRACE=y +-CONFIG_FUNCTION_PROFILER=y ++# CONFIG_KPROBE_EVENT is not set + CONFIG_KGDB=y CONFIG_KGDB_KDB=y CONFIG_KDB_KEYBOARD=y CONFIG_STRICT_DEVMEM=y -CONFIG_CRYPTO_AUTHENC=m ++CONFIG_CRYPTO_USER=m ++CONFIG_CRYPTO_NULL=m ++CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_SEQIV=m CONFIG_CRYPTO_CBC=y - CONFIG_CRYPTO_HMAC=y +-CONFIG_CRYPTO_HMAC=y ++CONFIG_CRYPTO_XTS=m CONFIG_CRYPTO_XCBC=m - CONFIG_CRYPTO_MD5=y - CONFIG_CRYPTO_SHA1=y +-CONFIG_CRYPTO_MD5=y +-CONFIG_CRYPTO_SHA1=y -CONFIG_CRYPTO_SHA256=m ++CONFIG_CRYPTO_SHA1_ARM=m CONFIG_CRYPTO_SHA512=m CONFIG_CRYPTO_TGR192=m CONFIG_CRYPTO_WP512=m ++CONFIG_CRYPTO_AES_ARM=m CONFIG_CRYPTO_CAST5=m CONFIG_CRYPTO_DES=y -CONFIG_CRYPTO_DEFLATE=m @@ -90893,13 +90838,13 @@ index 31f5afaa..1db7746 100644 # CONFIG_CRYPTO_HW is not set CONFIG_CRC_ITU_T=y -- -1.8.4 +1.8.5.1 -From 4995fa0e671e5e20d298e46b38cccd0ddff72b8d Mon Sep 17 00:00:00 2001 +From d232364c5eed6e266015d146cbbcab9d0bec325f Mon Sep 17 00:00:00 2001 From: popcornmix Date: Mon, 8 Apr 2013 21:12:48 +0100 -Subject: [PATCH 34/82] Add NAK holdoff scheme. Enabled by default, disable +Subject: [PATCH 19/91] Add NAK holdoff scheme. Enabled by default, disable with dwc_otg.nak_holdoff_enable=0. Thanks gsh --- @@ -91106,13 +91051,13 @@ index a9dea55..ebee73a 100644 if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { /* Add back to inactive non-periodic schedule. */ -- -1.8.4 +1.8.5.1 -From 207d67ce894d7e8040f568ebf557856e19475bee Mon Sep 17 00:00:00 2001 +From 334fc0c90c021a5713c884de618ca3393613d924 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Tue, 26 Mar 2013 19:24:24 +0000 -Subject: [PATCH 35/82] Added hwmon/thermal driver for reporting core +Subject: [PATCH 20/91] Added hwmon/thermal driver for reporting core temperature. Thanks Dorian --- @@ -91657,13 +91602,13 @@ index 0000000..3f9a733 + +module_platform_driver(bcm2835_thermal_driver); -- -1.8.4 +1.8.5.1 -From fe149eba8c774155801f10e8295f1ce4117a3329 Mon Sep 17 00:00:00 2001 +From d73dcb39470b7a79bff68ec7427a0e54037e748a Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 17 Apr 2013 12:16:36 +0100 -Subject: [PATCH 36/82] Enable multiple ALSA channels +Subject: [PATCH 21/91] Enable multiple ALSA channels --- arch/arm/mach-bcm2708/bcm2708.c | 54 ++++++++++++++++++++++++++++++++++++----- @@ -91735,289 +91680,13 @@ index a82d5bd..a388dd8 100644 static struct resource bcm2708_spi_resources[] = { -- -1.8.4 +1.8.5.1 -From a1720b532066d483873e72fa2c597cd9e8e1bfe3 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 3 Oct 2012 21:31:48 +0100 -Subject: [PATCH 37/82] Allow the number of cycles delay between sdcard - peripheral writes to be specified on command line with - sdhci-bcm2708.cycle_delay - ---- - drivers/mmc/host/sdhci-bcm2708.c | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index 6dacdb3..dcaefcd 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -77,6 +77,8 @@ - #define REG_EXRDFIFO_EN 0x80 - #define REG_EXRDFIFO_CFG 0x84 - -+int cycle_delay=2; -+ - /*****************************************************************************\ - * * - * Debug * -@@ -249,7 +251,7 @@ static void sdhci_bcm2708_raw_writel(struct sdhci_host *host, u32 val, int reg) - /* host->clock is the clock freq in Hz */ - static hptime_t last_write_hpt; - hptime_t now = hptime(); -- ns_2clk = 2000000000/host->clock; -+ ns_2clk = cycle_delay*1000000/(host->clock/1000); - - if (now == last_write_hpt || now == last_write_hpt+1) { - /* we can't guarantee any significant time has -@@ -1388,6 +1390,7 @@ static void __exit sdhci_drv_exit(void) - module_param(sync_after_dma, bool, 0444); - module_param(missing_status, bool, 0444); - module_param(enable_llm, bool, 0444); -+module_param(cycle_delay, int, 0444); - - MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver"); - MODULE_AUTHOR("Broadcom "); --- -1.8.4 - - -From 686663ae52d94a9e15ea2cde8171d2b819a5f319 Mon Sep 17 00:00:00 2001 -From: dero -Date: Mon, 19 Nov 2012 12:46:06 +0100 -Subject: [PATCH 38/82] Lazy CRC quirk: Implemented retrying mechanisms for SD - SSR and SCR, disabled missing_status and spurious CRC ACMD51 quirks by - default (should be fixed by the retrying-mechanishm) - ---- - drivers/mmc/core/sd.c | 110 ++++++++++++++++++++++++++++++++++----- - drivers/mmc/host/sdhci-bcm2708.c | 11 +++- - 2 files changed, 106 insertions(+), 15 deletions(-) - -diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c -index 5e8823d..e0e9afb 100644 ---- a/drivers/mmc/core/sd.c -+++ b/drivers/mmc/core/sd.c -@@ -13,6 +13,8 @@ - #include - #include - #include -+#include -+#include - - #include - #include -@@ -58,6 +60,15 @@ - __res & __mask; \ - }) - -+// timeout for tries -+static const unsigned long retry_timeout_ms= 10*1000; -+ -+// try at least 10 times, even if timeout is reached -+static const int retry_min_tries= 10; -+ -+// delay between tries -+static const unsigned long retry_delay_ms= 10; -+ - /* - * Given the decoded CSD structure, decode the raw CID to our CID structure. - */ -@@ -210,12 +221,63 @@ static int mmc_decode_scr(struct mmc_card *card) - } - - /* -- * Fetch and process SD Status register. -+ * Fetch and process SD Configuration Register. -+ */ -+static int mmc_read_scr(struct mmc_card *card) -+{ -+ unsigned long timeout_at; -+ int err, tries; -+ -+ timeout_at= jiffies + msecs_to_jiffies( retry_timeout_ms ); -+ tries= 0; -+ -+ while( tries < retry_min_tries || time_before( jiffies, timeout_at ) ) -+ { -+ unsigned long delay_at; -+ tries++; -+ -+ err = mmc_app_send_scr(card, card->raw_scr); -+ if( !err ) -+ break; // sucess!!! -+ -+ touch_nmi_watchdog(); // we are still alive! -+ -+ // delay -+ delay_at= jiffies + msecs_to_jiffies( retry_delay_ms ); -+ while( time_before( jiffies, delay_at ) ) -+ { -+ mdelay( 1 ); -+ touch_nmi_watchdog(); // we are still alive! -+ } -+ } -+ -+ if( err) -+ { -+ pr_err("%s: failed to read SD Configuration register (SCR) after %d tries during %lu ms, error %d\n", mmc_hostname(card->host), tries, retry_timeout_ms, err ); -+ return err; -+ } -+ -+ if( tries > 1 ) -+ { -+ pr_info("%s: could read SD Configuration register (SCR) at the %dth attempt\n", mmc_hostname(card->host), tries ); -+ } -+ -+ err = mmc_decode_scr(card); -+ if (err) -+ return err; -+ -+ return err; -+} -+ -+/* -+ * Fetch and process SD Status Register. - */ - static int mmc_read_ssr(struct mmc_card *card) - { -+ unsigned long timeout_at; - unsigned int au, es, et, eo; - int err, i, max_au; -+ int tries; - u32 *ssr; - - if (!(card->csd.cmdclass & CCC_APP_SPEC)) { -@@ -228,14 +290,40 @@ static int mmc_read_ssr(struct mmc_card *card) - if (!ssr) - return -ENOMEM; - -- err = mmc_app_sd_status(card, ssr); -- if (err) { -- pr_warning("%s: problem reading SD Status " -- "register.\n", mmc_hostname(card->host)); -- err = 0; -+ timeout_at= jiffies + msecs_to_jiffies( retry_timeout_ms ); -+ tries= 0; -+ -+ while( tries < retry_min_tries || time_before( jiffies, timeout_at ) ) -+ { -+ unsigned long delay_at; -+ tries++; -+ -+ err= mmc_app_sd_status(card, ssr); -+ if( !err ) -+ break; // sucess!!! -+ -+ touch_nmi_watchdog(); // we are still alive! -+ -+ // delay -+ delay_at= jiffies + msecs_to_jiffies( retry_delay_ms ); -+ while( time_before( jiffies, delay_at ) ) -+ { -+ mdelay( 1 ); -+ touch_nmi_watchdog(); // we are still alive! -+ } -+ } -+ -+ if( err) -+ { -+ pr_err("%s: failed to read SD Status register (SSR) after %d tries during %lu ms, error %d\n", mmc_hostname(card->host), tries, retry_timeout_ms, err ); - goto out; - } - -+ if( tries > 1 ) -+ { -+ pr_info("%s: could read SD Status register (SSR) at the %dth attempt\n", mmc_hostname(card->host), tries ); -+ } -+ - for (i = 0; i < 16; i++) - ssr[i] = be32_to_cpu(ssr[i]); - -@@ -816,14 +904,10 @@ int mmc_sd_setup_card(struct mmc_host *host, struct mmc_card *card, - - if (!reinit) { - /* -- * Fetch SCR from card. -+ * Fetch and decode SD Configuration register. - */ -- err = mmc_app_send_scr(card, card->raw_scr); -- if (err) -- return err; -- -- err = mmc_decode_scr(card); -- if (err) -+ err = mmc_read_scr(card); -+ if( err ) - return err; - - /* -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index dcaefcd..a3ecdf2 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -137,6 +137,7 @@ static inline unsigned long int since_ns(hptime_t t) - static int emmc_clock_freq = BCM2708_EMMC_CLOCK_FREQ; - static bool sync_after_dma = 1; - static bool missing_status = 1; -+static bool spurious_crc_acmd51 = 0; - bool enable_llm = 1; - - #if 0 -@@ -1103,7 +1104,7 @@ static unsigned int sdhci_bcm2708_quirk_extra_ints(struct sdhci_host *host) - return 1; - } - --static unsigned int sdhci_bcm2708_quirk_spurious_crc(struct sdhci_host *host) -+static unsigned int sdhci_bcm2708_quirk_spurious_crc_acmd51(struct sdhci_host *host) - { - return 1; - } -@@ -1149,7 +1150,6 @@ static unsigned int sdhci_bcm2708_missing_status(struct sdhci_host *host) - .pdma_reset = sdhci_bcm2708_platdma_reset, - #endif - .extra_ints = sdhci_bcm2708_quirk_extra_ints, -- .spurious_crc_acmd51 = sdhci_bcm2708_quirk_spurious_crc, - .voltage_broken = sdhci_bcm2708_quirk_voltage_broken, - .uhs_broken = sdhci_bcm2708_uhs_broken, - }; -@@ -1194,6 +1194,11 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev) - sdhci_bcm2708_ops.missing_status = sdhci_bcm2708_missing_status; - } - -+ if( spurious_crc_acmd51 ) { -+ sdhci_bcm2708_ops.spurious_crc_acmd51 = sdhci_bcm2708_quirk_spurious_crc_acmd51; -+ } -+ -+ - printk("sdhci: %s low-latency mode\n",enable_llm?"Enable":"Disable"); - - host->hw_name = "BCM2708_Arasan"; -@@ -1389,6 +1394,7 @@ static void __exit sdhci_drv_exit(void) - module_param(emmc_clock_freq, int, 0444); - module_param(sync_after_dma, bool, 0444); - module_param(missing_status, bool, 0444); -+module_param(spurious_crc_acmd51, bool, 0444); - module_param(enable_llm, bool, 0444); - module_param(cycle_delay, int, 0444); - -@@ -1401,6 +1407,7 @@ static void __exit sdhci_drv_exit(void) - MODULE_PARM_DESC(emmc_clock_freq, "Specify the speed of emmc clock"); - MODULE_PARM_DESC(sync_after_dma, "Block in driver until dma complete"); - MODULE_PARM_DESC(missing_status, "Use the missing status quirk"); -+MODULE_PARM_DESC(spurious_crc_acmd51, "Use the spurious crc quirk for reading SCR (ACMD51)"); - MODULE_PARM_DESC(enable_llm, "Enable low-latency mode"); - - --- -1.8.4 - - -From 90e0bc3b54e69a9c746168e978810930dcfc5c7b Mon Sep 17 00:00:00 2001 +From 2e8ef414d4012bac928010d3bb6f3991f34a76ba Mon Sep 17 00:00:00 2001 From: Gordon Hollingworth Date: Sun, 4 Nov 2012 15:55:01 +0000 -Subject: [PATCH 39/82] Make sure we wait for the reset to finish +Subject: [PATCH 22/91] Make sure we wait for the reset to finish --- drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 2 ++ @@ -92037,13 +91706,13 @@ index aff59df..1a23d4b 100644 mphi_int_count = 0; } -- -1.8.4 +1.8.5.1 -From 61d55ded63bf8fd4e946748aa28fe01fbc641f33 Mon Sep 17 00:00:00 2001 +From f40c81b08a1f7fc444af027a96c3918fe2471f72 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Mon, 19 Nov 2012 18:27:05 +0000 -Subject: [PATCH 40/82] Add Simon Hall's dma helper module, useful in future +Subject: [PATCH 23/91] Add Simon Hall's dma helper module, useful in future for X acceleration --- @@ -93377,19 +93046,19 @@ index 0000000..0bc41c4 + } +} -- -1.8.4 +1.8.5.1 -From e3c17da3dffbfb12ff81cb840e1895fbc395906f Mon Sep 17 00:00:00 2001 +From 64c06aa897194fa4bfc9f522c0dc80399bdbbcc3 Mon Sep 17 00:00:00 2001 From: Aron Szabo Date: Sat, 16 Jun 2012 12:15:55 +0200 -Subject: [PATCH 41/82] lirc: added support for RaspberryPi GPIO +Subject: [PATCH 24/91] lirc: added support for RaspberryPi GPIO --- drivers/staging/media/lirc/Kconfig | 6 + drivers/staging/media/lirc/Makefile | 1 + - drivers/staging/media/lirc/lirc_rpi.c | 687 ++++++++++++++++++++++++++++++++++ - 3 files changed, 694 insertions(+) + drivers/staging/media/lirc/lirc_rpi.c | 693 ++++++++++++++++++++++++++++++++++ + 3 files changed, 700 insertions(+) create mode 100644 drivers/staging/media/lirc/lirc_rpi.c diff --git a/drivers/staging/media/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig @@ -93423,10 +93092,10 @@ index b90fcab..2b227fd 100644 obj-$(CONFIG_LIRC_SIR) += lirc_sir.o diff --git a/drivers/staging/media/lirc/lirc_rpi.c b/drivers/staging/media/lirc/lirc_rpi.c new file mode 100644 -index 0000000..96acab0 +index 0000000..c76f696 --- /dev/null +++ b/drivers/staging/media/lirc/lirc_rpi.c -@@ -0,0 +1,687 @@ +@@ -0,0 +1,693 @@ +/* + * lirc_rpi.c + * @@ -93492,11 +93161,13 @@ index 0000000..96acab0 +/* set the default GPIO output pin */ +static int gpio_out_pin = 17; +/* enable debugging messages */ -+static int debug; ++static bool debug; +/* -1 = auto, 0 = active high, 1 = active low */ +static int sense = -1; +/* use softcarrier by default */ -+static int softcarrier = 1; ++static bool softcarrier = 1; ++/* 0 = do not invert output, 1 = invert output */ ++static bool invert = 0; + +struct gpio_chip *gpiochip; +struct irq_chip *irqchip; @@ -93564,10 +93235,10 @@ index 0000000..96acab0 + actual = 0; target = 0; flag = 0; + while (actual < length) { + if (flag) { -+ gpiochip->set(gpiochip, gpio_out_pin, 0); ++ gpiochip->set(gpiochip, gpio_out_pin, invert); + target += space_width; + } else { -+ gpiochip->set(gpiochip, gpio_out_pin, 1); ++ gpiochip->set(gpiochip, gpio_out_pin, !invert); + target += pulse_width; + } + d = (target - actual - @@ -93591,7 +93262,7 @@ index 0000000..96acab0 + if (softcarrier) { + return send_pulse_softcarrier(length); + } else { -+ gpiochip->set(gpiochip, gpio_out_pin, 1); ++ gpiochip->set(gpiochip, gpio_out_pin, !invert); + safe_udelay(length); + return 0; + } @@ -93599,7 +93270,7 @@ index 0000000..96acab0 + +static void send_space(long length) +{ -+ gpiochip->set(gpiochip, gpio_out_pin, 0); ++ gpiochip->set(gpiochip, gpio_out_pin, invert); + if (length <= 0) + return; + safe_udelay(length); @@ -93747,7 +93418,7 @@ index 0000000..96acab0 + + gpiochip->direction_input(gpiochip, gpio_in_pin); + gpiochip->direction_output(gpiochip, gpio_out_pin, 1); -+ gpiochip->set(gpiochip, gpio_out_pin, 0); ++ gpiochip->set(gpiochip, gpio_out_pin, invert); + + irq = gpiochip->to_irq(gpiochip, gpio_in_pin); + dprintk("to_irq %d\n", irq); @@ -93886,7 +93557,7 @@ index 0000000..96acab0 + else + delta = send_pulse(wbuf[i]); + } -+ gpiochip->set(gpiochip, gpio_out_pin, 0); ++ gpiochip->set(gpiochip, gpio_out_pin, invert); + + spin_unlock_irqrestore(&lock, flags); + kfree(wbuf); @@ -94017,8 +93688,6 @@ index 0000000..96acab0 + +static void lirc_rpi_exit(void) +{ -+ gpio_free(gpio_out_pin); -+ gpio_free(gpio_in_pin); + platform_device_unregister(lirc_rpi_dev); + platform_driver_unregister(&lirc_rpi_driver); + lirc_buffer_free(&rbuf); @@ -94050,6 +93719,10 @@ index 0000000..96acab0 + goto exit_rpi; + } + ++ result = init_port(); ++ if (result < 0) ++ goto exit_rpi; ++ + driver.features = LIRC_CAN_SET_SEND_DUTY_CYCLE | + LIRC_CAN_SET_SEND_CARRIER | + LIRC_CAN_SEND_PULSE | @@ -94067,10 +93740,6 @@ index 0000000..96acab0 + + printk(KERN_INFO LIRC_DRIVER_NAME ": driver registered!\n"); + -+ result = init_port(); -+ if (result < 0) -+ goto exit_rpi; -+ + return 0; + + exit_rpi: @@ -94081,6 +93750,9 @@ index 0000000..96acab0 + +static void __exit lirc_rpi_exit_module(void) +{ ++ gpio_free(gpio_out_pin); ++ gpio_free(gpio_in_pin); ++ + lirc_rpi_exit(); + + lirc_unregister_driver(driver.minor); @@ -94105,23 +93777,26 @@ index 0000000..96acab0 + " Valid pin numbers are: 0, 1, 4, 8, 7, 9, 10, 11, 14, 15," + " 17, 18, 21, 22, 23, 24, 25, default 18"); + -+module_param(sense, bool, S_IRUGO); ++module_param(sense, int, S_IRUGO); +MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit" + " (0 = active high, 1 = active low )"); + +module_param(softcarrier, bool, S_IRUGO); +MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); + ++module_param(invert, bool, S_IRUGO); ++MODULE_PARM_DESC(invert, "Invert output (0 = off, 1 = on, default off"); ++ +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging messages"); -- -1.8.4 +1.8.5.1 -From 97f6f722559421387f1a1a1396522ab0c1563ea0 Mon Sep 17 00:00:00 2001 +From 436b2b7ef6e95540a308b3c128b4df447c5452e4 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Tue, 11 Dec 2012 18:23:03 +0000 -Subject: [PATCH 42/82] Default to dwc_otp.lpm_enable=0 +Subject: [PATCH 25/91] Default to dwc_otp.lpm_enable=0 --- drivers/usb/host/dwc_otg/dwc_otg_driver.c | 2 +- @@ -94141,13 +93816,13 @@ index d58ebd7..c2bb596 100644 .ahb_thr_ratio = -1, .power_down = -1, -- -1.8.4 +1.8.5.1 -From 983a0999608be29ebe004fbc0eb11c8243f7573a Mon Sep 17 00:00:00 2001 +From c449c30b7a79cf96c7a35390085824a4d928834f Mon Sep 17 00:00:00 2001 From: P33M Date: Wed, 9 Jan 2013 16:12:04 +0000 -Subject: [PATCH 43/82] dwc_otg: fix bug in dwc_otg_hcd.c resulting in silent +Subject: [PATCH 26/91] dwc_otg: fix bug in dwc_otg_hcd.c resulting in silent kernel memory corruption, escalating to OOPS under high USB load. --- @@ -94181,13 +93856,13 @@ index ebee73a..b3efaf4 100644 DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); -- -1.8.4 +1.8.5.1 -From b035bd5b8416ada559cea222b1bfc49a328357f9 Mon Sep 17 00:00:00 2001 +From 6e862463cba8ff71e8b6611903757416251284ca Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 3 Jul 2013 00:51:55 +0100 -Subject: [PATCH 44/82] Add hwrng (hardware random number generator) driver +Subject: [PATCH 27/91] Add hwrng (hardware random number generator) driver --- arch/arm/mach-bcm2708/include/mach/platform.h | 1 + @@ -94361,13 +94036,13 @@ index 0000000..1ffa7d7 +MODULE_DESCRIPTION("BCM2708 H/W Random Number Generator (RNG) driver"); +MODULE_LICENSE("GPL and additional rights"); -- -1.8.4 +1.8.5.1 -From e7e4126390137ea359cfb76cffc2b69c725e0dc6 Mon Sep 17 00:00:00 2001 +From def9b296a0bb5f462575fe348b5bc4b8db0997b2 Mon Sep 17 00:00:00 2001 From: Technion Date: Mon, 11 Feb 2013 22:08:53 +1100 -Subject: [PATCH 45/82] Changed wording on logging. Previously, we received +Subject: [PATCH 28/91] Changed wording on logging. Previously, we received errors like this: mmc0: could read SD Status register (SSR) at the 3th attempt A more sensible response is now returned. A typo also fixed in comments. @@ -94399,13 +94074,13 @@ index e0e9afb..25b98a9 100644 for (i = 0; i < 16; i++) -- -1.8.4 +1.8.5.1 -From 6c1706493529f0f0dd5c9f944cc01981cd030b47 Mon Sep 17 00:00:00 2001 +From c612839a8f31fb9e27406f4b13e175ca2d1fb525 Mon Sep 17 00:00:00 2001 From: P33M Date: Fri, 15 Feb 2013 22:36:47 +0000 -Subject: [PATCH 46/82] dwc_otg: Fix unsafe access of QTD during URB enqueue +Subject: [PATCH 29/91] dwc_otg: Fix unsafe access of QTD during URB enqueue In dwc_otg_hcd_urb_enqueue during qtd creation, it was possible that the transaction could complete almost immediately after the qtd was assigned @@ -94489,13 +94164,13 @@ index b3efaf4..1554be5 100644 } } -- -1.8.4 +1.8.5.1 -From 9351b307e68fa5389a561537ba695fe6f068c2e8 Mon Sep 17 00:00:00 2001 +From 1595868ecabc0aff6ae2ccb4f3f9bb3a0708e616 Mon Sep 17 00:00:00 2001 From: P33M Date: Fri, 15 Feb 2013 22:38:40 +0000 -Subject: [PATCH 47/82] dwc_otg: Fix incorrect URB allocation error handling +Subject: [PATCH 30/91] dwc_otg: Fix incorrect URB allocation error handling If the memory allocation for a dwc_otg_urb failed, the kernel would OOPS because for some reason a member of the *unallocated* struct was set to @@ -94533,97 +94208,13 @@ index 35d03d1..6fe30e3 100644 } -- -1.8.4 +1.8.5.1 -From 73ceb51a9b51d70305ed1c7677daa3ee807f92b2 Mon Sep 17 00:00:00 2001 -From: pjennings -Date: Wed, 20 Feb 2013 17:51:43 -0600 -Subject: [PATCH 48/82] Added inverted transmitter support - ---- - drivers/staging/media/lirc/lirc_rpi.c | 17 +++++++++++------ - 1 file changed, 11 insertions(+), 6 deletions(-) - -diff --git a/drivers/staging/media/lirc/lirc_rpi.c b/drivers/staging/media/lirc/lirc_rpi.c -index 96acab0..5bb0dfe 100644 ---- a/drivers/staging/media/lirc/lirc_rpi.c -+++ b/drivers/staging/media/lirc/lirc_rpi.c -@@ -68,6 +68,8 @@ - static int sense = -1; - /* use softcarrier by default */ - static int softcarrier = 1; -+/* 0 = do not invert output, 1 = invert output */ -+static int invert = 0; - - struct gpio_chip *gpiochip; - struct irq_chip *irqchip; -@@ -135,10 +137,10 @@ static long send_pulse_softcarrier(unsigned long length) - actual = 0; target = 0; flag = 0; - while (actual < length) { - if (flag) { -- gpiochip->set(gpiochip, gpio_out_pin, 0); -+ gpiochip->set(gpiochip, gpio_out_pin, invert); - target += space_width; - } else { -- gpiochip->set(gpiochip, gpio_out_pin, 1); -+ gpiochip->set(gpiochip, gpio_out_pin, !invert); - target += pulse_width; - } - d = (target - actual - -@@ -162,7 +164,7 @@ static long send_pulse(unsigned long length) - if (softcarrier) { - return send_pulse_softcarrier(length); - } else { -- gpiochip->set(gpiochip, gpio_out_pin, 1); -+ gpiochip->set(gpiochip, gpio_out_pin, !invert); - safe_udelay(length); - return 0; - } -@@ -170,7 +172,7 @@ static long send_pulse(unsigned long length) - - static void send_space(long length) - { -- gpiochip->set(gpiochip, gpio_out_pin, 0); -+ gpiochip->set(gpiochip, gpio_out_pin, invert); - if (length <= 0) - return; - safe_udelay(length); -@@ -318,7 +320,7 @@ static int init_port(void) - - gpiochip->direction_input(gpiochip, gpio_in_pin); - gpiochip->direction_output(gpiochip, gpio_out_pin, 1); -- gpiochip->set(gpiochip, gpio_out_pin, 0); -+ gpiochip->set(gpiochip, gpio_out_pin, invert); - - irq = gpiochip->to_irq(gpiochip, gpio_in_pin); - dprintk("to_irq %d\n", irq); -@@ -457,7 +459,7 @@ static ssize_t lirc_write(struct file *file, const char *buf, - else - delta = send_pulse(wbuf[i]); - } -- gpiochip->set(gpiochip, gpio_out_pin, 0); -+ gpiochip->set(gpiochip, gpio_out_pin, invert); - - spin_unlock_irqrestore(&lock, flags); - kfree(wbuf); -@@ -683,5 +685,8 @@ static void __exit lirc_rpi_exit_module(void) - module_param(softcarrier, bool, S_IRUGO); - MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); - -+module_param(invert, bool, S_IRUGO); -+MODULE_PARM_DESC(invert, "Invert output (0 = off, 1 = on, default off"); -+ - module_param(debug, bool, S_IRUGO | S_IWUSR); - MODULE_PARM_DESC(debug, "Enable debugging messages"); --- -1.8.4 - - -From d9a6d84bd83bcdf8edb9b6add4d59e148dde11d6 Mon Sep 17 00:00:00 2001 +From 832c4dab3dd32fc1b488868e29854c59bf73c765 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Sun, 24 Feb 2013 16:30:57 +0000 -Subject: [PATCH 49/82] Add retry on error and tidy of temperature driver +Subject: [PATCH 31/91] Add retry on error and tidy of temperature driver --- drivers/thermal/bcm2835-thermal.c | 78 ++++++++++++++------------------------- @@ -94747,13 +94338,13 @@ index 3f9a733..85fceb5 100644 static int bcm2835_get_trip_type(struct thermal_zone_device * thermal_dev, int trip_num, enum thermal_trip_type *trip_type) { -- -1.8.4 +1.8.5.1 -From 589b438b9d484e464f6894ab1daab3dce8833bf1 Mon Sep 17 00:00:00 2001 +From 94f9a64587265ad6099afffa7bc6b53089864bad Mon Sep 17 00:00:00 2001 From: P33M Date: Thu, 28 Feb 2013 16:52:51 +0000 -Subject: [PATCH 50/82] dwc_otg: fix potential use-after-free case in interrupt +Subject: [PATCH 32/91] dwc_otg: fix potential use-after-free case in interrupt handler If a transaction had previously aborted, certain interrupts are @@ -94782,13 +94373,13 @@ index 1a23d4b..7af455d 100644 if (hcint.b.nyet) { retval |= handle_hc_nyet_intr(dwc_otg_hcd, hc, hc_regs, qtd); -- -1.8.4 +1.8.5.1 -From ca577be817a50946fe082bbbdb5cde78fdb47a3e Mon Sep 17 00:00:00 2001 +From 6b0e7139c1ab867a8d0f42f679d9dcb46c8bbce7 Mon Sep 17 00:00:00 2001 From: P33M Date: Sun, 3 Mar 2013 14:45:53 +0000 -Subject: [PATCH 51/82] dwc_otg: add handling of SPLIT transaction data toggle +Subject: [PATCH 33/91] dwc_otg: add handling of SPLIT transaction data toggle errors Previously a data toggle error on packets from a USB1.1 device behind @@ -94840,13 +94431,13 @@ index 7af455d..a27dacd 100644 if (hcint.b.nyet) { /* -- -1.8.4 +1.8.5.1 -From 016d8e6a44821e3547e00be48ed83efdc02cc467 Mon Sep 17 00:00:00 2001 +From bcc116e749d59eb5dbcc0f53e3b8ee6096c75892 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 1 May 2013 21:14:28 +0100 -Subject: [PATCH 52/82] Add bitbanging pullups, use them for w1-gpio +Subject: [PATCH 34/91] Add bitbanging pullups, use them for w1-gpio Allows parasite power to work, uses module option pullup=1 --- @@ -94982,13 +94573,13 @@ index e10acc2..667fdd5 100644 } } -- -1.8.4 +1.8.5.1 -From ccf2dee549dfe3a9a365255ef742bed2794f1f87 Mon Sep 17 00:00:00 2001 +From 08827d2ca4ba757ab2404d82661425ba6fa7e107 Mon Sep 17 00:00:00 2001 From: P33M Date: Thu, 21 Mar 2013 19:36:17 +0000 -Subject: [PATCH 53/82] dwc_otg: implement tasklet for returning URBs to +Subject: [PATCH 35/91] dwc_otg: implement tasklet for returning URBs to usbcore hcd layer The dwc_otg driver interrupt handler for transfer completion will spend @@ -95227,13 +94818,13 @@ index 2b4a14e..39787e3 100644 } -- -1.8.4 +1.8.5.1 -From 17181a1c89f20b4e11d6f7d7e461e4d9666b21ce Mon Sep 17 00:00:00 2001 +From aa0f6f833474c39ff74e8cf4a86697282dd351ed Mon Sep 17 00:00:00 2001 From: P33M Date: Mon, 22 Apr 2013 00:08:36 +0100 -Subject: [PATCH 55/82] dwc_otg: fix NAK holdoff and allow on split +Subject: [PATCH 37/91] dwc_otg: fix NAK holdoff and allow on split transactions only This corrects a bug where if a single active non-periodic endpoint @@ -95300,13 +94891,13 @@ index 533b17d..73f7643 100644 DWC_SPINLOCK_IRQSAVE(channel_lock, &flags); if (hcd->available_host_channels < 1) { -- -1.8.4 +1.8.5.1 -From c3e96740bbfb0cde7b181e2871a20412641f8e88 Mon Sep 17 00:00:00 2001 +From 80068caf5e52e9ceff4b779443900ed29d218626 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Fri, 26 Apr 2013 10:08:31 -0700 -Subject: [PATCH 56/82] Merge pull request #286 from +Subject: [PATCH 38/91] Merge pull request #286 from martinezjavier/rpi-3.6.y-dev add mmap support and some cleanups to bcm2835 ALSA driver @@ -95784,82 +95375,13 @@ index 080bd5c..36afee3 100755 struct semaphore buffers_update_sem; struct semaphore control_sem; -- -1.8.4 +1.8.5.1 -From a7e509238c4f108746e4f6567e5e7f2461dd11ef Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Fri, 10 May 2013 19:42:38 +0100 -Subject: [PATCH 57/82] mmc: suppress sdcard warnings we are happy about by - default - ---- - drivers/mmc/host/sdhci-bcm2708.c | 11 +++++++++-- - 1 file changed, 9 insertions(+), 2 deletions(-) - -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index a3ecdf2..71f33ce 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -139,6 +139,7 @@ static inline unsigned long int since_ns(hptime_t t) - static bool missing_status = 1; - static bool spurious_crc_acmd51 = 0; - bool enable_llm = 1; -+bool extra_messages = 0; - - #if 0 - static void hptime_test(void) -@@ -672,13 +673,16 @@ static void schci_bcm2708_dma_go(struct sdhci_host *host) - cs = readl(host_priv->dma_chan_base + BCM2708_DMA_CS); - - if (!(BCM2708_DMA_ACTIVE & cs)) -- printk(KERN_INFO "%s: missed completion of " -+ { -+ if (extra_messages) -+ printk(KERN_INFO "%s: missed completion of " - "cmd %d DMA (%d/%d [%d]/[%d]) - " - "ignoring it\n", - mmc_hostname(host->mmc), - host->last_cmdop, - host_priv->sg_done, sg_todo, - host_priv->sg_ix+1, sg_len); -+ } - else - printk(KERN_INFO "%s: resetting ongoing cmd %d" - "DMA before %d/%d [%d]/[%d] complete\n", -@@ -903,7 +907,8 @@ static irqreturn_t sdhci_bcm2708_dma_irq(int irq, void *dev_id) - - if (!host_priv->dma_wanted) { - /* ignore this interrupt - it was reset */ -- printk(KERN_INFO "%s: DMA IRQ %X ignored - " -+ if (extra_messages) -+ printk(KERN_INFO "%s: DMA IRQ %X ignored - " - "results were reset\n", - mmc_hostname(host->mmc), dma_cs); - #ifdef CHECK_DMA_USE -@@ -1397,6 +1402,7 @@ static void __exit sdhci_drv_exit(void) - module_param(spurious_crc_acmd51, bool, 0444); - module_param(enable_llm, bool, 0444); - module_param(cycle_delay, int, 0444); -+module_param(extra_messages, bool, 0444); - - MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver"); - MODULE_AUTHOR("Broadcom "); -@@ -1409,5 +1415,6 @@ static void __exit sdhci_drv_exit(void) - MODULE_PARM_DESC(missing_status, "Use the missing status quirk"); - MODULE_PARM_DESC(spurious_crc_acmd51, "Use the spurious crc quirk for reading SCR (ACMD51)"); - MODULE_PARM_DESC(enable_llm, "Enable low-latency mode"); -+MODULE_PARM_DESC(extra_messages, "Enable more sdcard warning messages"); - - --- -1.8.4 - - -From 9be1838ab3c08af3421d4ceb89ade82009c527e6 Mon Sep 17 00:00:00 2001 +From 7afbc63a193e40edb5efebb8103139c14d87d25d Mon Sep 17 00:00:00 2001 From: Harm Hanemaaijer Date: Thu, 20 Jun 2013 20:21:39 +0200 -Subject: [PATCH 58/82] Speed up console framebuffer imageblit function +Subject: [PATCH 39/91] Speed up console framebuffer imageblit function Especially on platforms with a slower CPU but a relatively high framebuffer fill bandwidth, like current ARM devices, the existing @@ -96068,13 +95590,13 @@ index baed57d..ce91bf2 100644 start_index, pitch_index); } else -- -1.8.4 +1.8.5.1 -From de56c40c1f5fb19dadd8c5fa488915b95fb6f7ac Mon Sep 17 00:00:00 2001 +From 82a53113b982c9b42ab512d56edcb9e438dc5df0 Mon Sep 17 00:00:00 2001 From: Siarhei Siamashka Date: Mon, 17 Jun 2013 13:32:11 +0300 -Subject: [PATCH 59/82] fbdev: add FBIOCOPYAREA ioctl +Subject: [PATCH 40/91] fbdev: add FBIOCOPYAREA ioctl Based on the patch authored by Ali Gholami Rudi at https://lkml.org/lkml/2009/7/13/153 @@ -96167,13 +95689,13 @@ index fb795c3..fa72af0 100644 #define FB_TYPE_PACKED_PIXELS 0 /* Packed Pixels */ #define FB_TYPE_PLANES 1 /* Non interleaved planes */ -- -1.8.4 +1.8.5.1 -From 4ff866a7add9f47565263bd1004bdcec71894533 Mon Sep 17 00:00:00 2001 +From fa03803af5f3cc084e16042c9376807589bf369d Mon Sep 17 00:00:00 2001 From: Siarhei Siamashka Date: Mon, 17 Jun 2013 16:00:25 +0300 -Subject: [PATCH 60/82] bcm2708_fb: DMA acceleration for fb_copyarea +Subject: [PATCH 41/91] bcm2708_fb: DMA acceleration for fb_copyarea Based on http://www.raspberrypi.org/phpBB3/viewtopic.php?p=62425#p62425 Also used Simon's dmaer_master module as a reference for tweaking DMA @@ -96414,13 +95936,13 @@ index 08d9238..c10c5ee 100644 fb->dma); kfree(fb); -- -1.8.4 +1.8.5.1 -From a649566c8170b418a8bf67eaeeba1fe695e6d663 Mon Sep 17 00:00:00 2001 +From 74b5846025af0222106b1c132de9caf51bbecf1e Mon Sep 17 00:00:00 2001 From: Mike Bradley Date: Mon, 17 Jun 2013 11:31:42 -0700 -Subject: [PATCH 61/82] dwc_otg: Call usb_hcd_unlink_urb_from_ep with lock held +Subject: [PATCH 42/91] dwc_otg: Call usb_hcd_unlink_urb_from_ep with lock held in completion handler usb_hcd_unlink_urb_from_ep must be called with the HCD lock held. Calling it @@ -96504,13 +96026,13 @@ index 39787e3..5e6a26a 100644 return 0; } -- -1.8.4 +1.8.5.1 -From 80007e07fed0871cbcdd0b49122a4d9d5059c5a1 Mon Sep 17 00:00:00 2001 +From de32ba2a349e4115b7a2cd397b5c0502b75d5eed Mon Sep 17 00:00:00 2001 From: Gordon Hollingworth Date: Thu, 4 Apr 2013 11:05:21 +0100 -Subject: [PATCH 62/82] USB fix using a FIQ to implement split transactions +Subject: [PATCH 43/91] USB fix using a FIQ to implement split transactions This commit adds a FIQ implementaion that schedules the split transactions using a FIQ so we don't get @@ -97910,13 +97432,13 @@ index 1b1f83c..c8590b5 100644 if (status.b.sr) { -- -1.8.4 +1.8.5.1 -From 7a5d1f564dc3f0a05fb6e31bbb01040ebc19b983 Mon Sep 17 00:00:00 2001 +From 6190c8c08d0027844d5316623ff6a6878f4b002d Mon Sep 17 00:00:00 2001 From: popcornmix Date: Wed, 3 Jul 2013 11:39:46 +0100 -Subject: [PATCH 63/82] dwc_otg: fix device attributes and avoid kernel +Subject: [PATCH 44/91] dwc_otg: fix device attributes and avoid kernel warnings on boot --- @@ -97982,42 +97504,13 @@ index fab2961..af1cd4d 100644 #ifdef CONFIG_USB_DWC_OTG_LPM -- -1.8.4 +1.8.5.1 -From 6f6b15799bb09733814a03eb0d48e6f2b55e6d4e Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Wed, 10 Jul 2013 23:53:31 +0100 -Subject: [PATCH 64/82] sdhci-bcm2807: Increase sync_after_dma timeout - -The current timeout is being hit with some cards that complete successfully with a longer timeout. -The timeout is not handled well, and is believed to be a code path that causes corruption. -872a8ff suggests that crappy cards can take up to 3 seconds to respond ---- - drivers/mmc/host/sdhci-bcm2708.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index 71f33ce..45499d8 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -842,7 +842,7 @@ static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host, - We get CRC and DEND errors unless we wait for - the SD controller to finish reading/writing to the card. */ - u32 state_mask; -- int timeout=30*5000; -+ int timeout=3*1000*1000; - - DBG("PDMA over - sync card\n"); - if (data->flags & MMC_DATA_READ) --- -1.8.4 - - -From 567e8f771ba1c2a89b7d1f9936af7009e2d7b3f9 Mon Sep 17 00:00:00 2001 +From 179b0e9716d0885cfb6e1ac418eba852b7725926 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Mon, 15 Jul 2013 23:55:52 +0100 -Subject: [PATCH 65/82] dcw_otg: avoid logging function that can cause panics +Subject: [PATCH 45/91] dcw_otg: avoid logging function that can cause panics See: https://github.com/raspberrypi/firmware/issues/21 Thanks to cleverca22 for fix @@ -98039,13 +97532,13 @@ index af1cd4d..9da0c92 100644 return sprintf(buf, "SPRAM Dump\n"); } -- -1.8.4 +1.8.5.1 -From 89b663525d62c59581ff7f6d251191d25cf317c4 Mon Sep 17 00:00:00 2001 +From 7c1335c8ba9800532aeb6fdd532bcffcc8ba5052 Mon Sep 17 00:00:00 2001 From: P33M Date: Sat, 13 Jul 2013 20:41:26 +0100 -Subject: [PATCH 66/82] dwc_otg: mask correct interrupts after transaction +Subject: [PATCH 46/91] dwc_otg: mask correct interrupts after transaction error recovery The dwc_otg driver will unmask certain interrupts on a transaction @@ -98109,13 +97602,13 @@ index 8e5789f..fd73e41 100644 } -- -1.8.4 +1.8.5.1 -From 63e61dfa9152ceac4c7b7a8c316fee0cf10cfad0 Mon Sep 17 00:00:00 2001 +From f55e5ce82797d3052654aedaeec618914d82c28e Mon Sep 17 00:00:00 2001 From: P33M Date: Sat, 13 Jul 2013 21:48:41 +0100 -Subject: [PATCH 67/82] dwc_otg: fiq: prevent FIQ thrash and incorrect state +Subject: [PATCH 47/91] dwc_otg: fiq: prevent FIQ thrash and incorrect state passing to IRQ In the case of a transaction to a device that had previously aborted @@ -98169,13 +97662,13 @@ index fd73e41..2ec0565 100644 // Clear the interrupt, this will also clear the HAINT bit FIQ_WRITE((dwc_regs_base + 0x500 + (channel * 0x20) + 0x8), hcint.d32); -- -1.8.4 +1.8.5.1 -From 16417807fd34069f47d28b4c68f3f55d33204dcc Mon Sep 17 00:00:00 2001 +From 40aebae6b8d0c15c90c9cd3a62259ebf2aa5fae8 Mon Sep 17 00:00:00 2001 From: Gordon Hollingworth Date: Mon, 8 Jul 2013 04:12:19 +0100 -Subject: [PATCH 68/82] Fix function tracing +Subject: [PATCH 48/91] Fix function tracing --- drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 12 ++++++------ @@ -98237,13 +97730,13 @@ index 2ec0565..083b1c3 100644 /* entry takes care to store registers we will be treading on here */ -- -1.8.4 +1.8.5.1 -From 760ef0f0be87a6c12ba2d8a84c507c18ad20d833 Mon Sep 17 00:00:00 2001 +From 09f3f40c3be23ec522078e17df02100d55496267 Mon Sep 17 00:00:00 2001 From: P33M Date: Thu, 18 Jul 2013 16:32:41 +0100 -Subject: [PATCH 69/82] dwc_otg: whitespace cleanup in dwc_otg_urb_enqueue +Subject: [PATCH 49/91] dwc_otg: whitespace cleanup in dwc_otg_urb_enqueue --- drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 53 ++++++++++++++-------------- @@ -98327,13 +97820,13 @@ index d1c5c2b..315c803 100644 } -- -1.8.4 +1.8.5.1 -From 3798869b67bd8d0562ad2ce92cb95a68e3c16cc0 Mon Sep 17 00:00:00 2001 +From e77b98b91ce833a0b3281ed00e4597b4e77b79d8 Mon Sep 17 00:00:00 2001 From: P33M Date: Thu, 18 Jul 2013 17:07:26 +0100 -Subject: [PATCH 70/82] dwc_otg: prevent OOPSes during device disconnects +Subject: [PATCH 50/91] dwc_otg: prevent OOPSes during device disconnects The dwc_otg_urb_enqueue function is thread-unsafe. In particular the access of urb->hcpriv, usb_hcd_link_urb_to_ep, dwc_otg_urb->qtd and @@ -98471,13 +97964,13 @@ index 7b92025..db95851 100644 return retval; -- -1.8.4 +1.8.5.1 -From d9713eaaf34a55fffcf3e2f4c53ba8af74601045 Mon Sep 17 00:00:00 2001 +From 9fd11e7750d06fbbe32016939e0f4fc5fd2601df Mon Sep 17 00:00:00 2001 From: P33M Date: Mon, 22 Jul 2013 14:08:26 +0100 -Subject: [PATCH 71/82] dwc_otg: prevent BUG() in TT allocation if hub address +Subject: [PATCH 51/91] dwc_otg: prevent BUG() in TT allocation if hub address is > 16 A fixed-size array is used to track TT allocation. This was @@ -98564,13 +98057,13 @@ index 083b1c3..c76910d 100644 } -- -1.8.4 +1.8.5.1 -From 6e443da697427a4de231fdd651f9f430e76043bc Mon Sep 17 00:00:00 2001 +From b69bdd06946893871ed9ee49d93e0f46546c17f0 Mon Sep 17 00:00:00 2001 From: P33M Date: Tue, 23 Jul 2013 14:15:32 +0100 -Subject: [PATCH 72/82] dwc_otg: make channel halts with unknown state less +Subject: [PATCH 52/91] dwc_otg: make channel halts with unknown state less damaging If the IRQ received a channel halt interrupt through the FIQ @@ -98612,13 +98105,13 @@ index c76910d..5fd8613 100644 } -- -1.8.4 +1.8.5.1 -From 29bca904c787fbaad871d5cadfd71ce6f9bef590 Mon Sep 17 00:00:00 2001 +From ac8bb33449cf856f0e1d12355dfa6d3674d74454 Mon Sep 17 00:00:00 2001 From: P33M Date: Tue, 30 Jul 2013 09:58:48 +0100 -Subject: [PATCH 73/82] dwc_otg: fiq_split: use TTs with more granularity +Subject: [PATCH 53/91] dwc_otg: fiq_split: use TTs with more granularity This fixes certain issues with split transaction scheduling. @@ -98735,13 +98228,13 @@ index 5fd8613..a959a49 100644 /* Try to queue more transfers now that there's a free channel. */ -- -1.8.4 +1.8.5.1 -From 1471953a4bfae9a23f6a08844f41ff580c21f68a Mon Sep 17 00:00:00 2001 +From 8a027a640d6e2b56e346f6b28abf1b0cf668cc72 Mon Sep 17 00:00:00 2001 From: P33M Date: Fri, 2 Aug 2013 10:04:18 +0100 -Subject: [PATCH 74/82] dwc_otg: fix potential sleep while atomic during urb +Subject: [PATCH 54/91] dwc_otg: fix potential sleep while atomic during urb enqueue Fixes a regression introduced with eb1b482a. Kmalloc called from @@ -98767,13 +98260,13 @@ index 87e517d..88c0544 100644 if (alloc_bandwidth) { allocate_bus_bandwidth(hcd, -- -1.8.4 +1.8.5.1 -From 4fbcf5bf785808166d063f6fd7e889c72e41d7be Mon Sep 17 00:00:00 2001 +From cc678194e329219ddb3f71a86604d45c222e1471 Mon Sep 17 00:00:00 2001 From: P33M Date: Mon, 5 Aug 2013 11:42:12 +0100 -Subject: [PATCH 75/82] dwc_otg: make fiq_split_enable imply fiq_fix_enable +Subject: [PATCH 55/91] dwc_otg: make fiq_split_enable imply fiq_fix_enable Failing to set up the FIQ correctly would result in "IRQ 32: nobody cared" errors in dmesg. @@ -98782,7 +98275,7 @@ Failing to set up the FIQ correctly would result in 1 file changed, 6 insertions(+) diff --git a/drivers/usb/host/dwc_otg/dwc_otg_driver.c b/drivers/usb/host/dwc_otg/dwc_otg_driver.c -index 176dc14..f06c3d2 100644 +index 176dc14..f06c3d22 100644 --- a/drivers/usb/host/dwc_otg/dwc_otg_driver.c +++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c @@ -1070,6 +1070,12 @@ static int __init dwc_otg_driver_init(void) @@ -98799,13 +98292,13 @@ index 176dc14..f06c3d2 100644 DWC_DRIVER_VERSION, #ifdef LM_INTERFACE -- -1.8.4 +1.8.5.1 -From 39b26759190e28cbc3ec49c88232990fbedf4162 Mon Sep 17 00:00:00 2001 +From 110c93cedd588269083f8f2bcc66132896573d7f Mon Sep 17 00:00:00 2001 From: P33M Date: Mon, 5 Aug 2013 11:47:12 +0100 -Subject: [PATCH 76/82] dwc_otg: prevent crashes on host port disconnects +Subject: [PATCH 56/91] dwc_otg: prevent crashes on host port disconnects Fix several issues resulting in crashes or inconsistent state if a Model A root port was disconnected. @@ -98960,13 +98453,13 @@ index 88c0544..ae4271a 100644 if (status) { DWC_PRINTF("Uknown urb status %d\n", status); -- -1.8.4 +1.8.5.1 -From 5e060de08fcc5defa2863c66473541b992c28759 Mon Sep 17 00:00:00 2001 +From fb4384fd5a2c89da24b5641dc43b8ed1f016e099 Mon Sep 17 00:00:00 2001 From: P33M Date: Mon, 5 Aug 2013 13:17:58 +0100 -Subject: [PATCH 77/82] dwc_otg: prevent leaking URBs during enqueue +Subject: [PATCH 57/91] dwc_otg: prevent leaking URBs during enqueue A dwc_otg_urb would get leaked if the HCD enqueue function failed for any reason. Free the URB at the appropriate points. @@ -98999,13 +98492,13 @@ index ae4271a..ee8eec9 100644 return retval; } -- -1.8.4 +1.8.5.1 -From f87c55a6420947fb19f480623712ba5a8c14d272 Mon Sep 17 00:00:00 2001 +From 96b81cca71307dcc2351311c5a2edc166a6f3445 Mon Sep 17 00:00:00 2001 From: P33M Date: Fri, 20 Sep 2013 16:08:27 +0100 -Subject: [PATCH 79/82] dwc_otg: Enable NAK holdoff for control split +Subject: [PATCH 58/91] dwc_otg: Enable NAK holdoff for control split transactions Certain low-speed devices take a very long time to complete a @@ -99035,13 +98528,13 @@ index 3a549a1..f8dc4be 100644 hc->qh->nak_frame = dwc_otg_hcd_get_frame_number(hcd); } -- -1.8.4 +1.8.5.1 -From af6315d795eb6221491046befc8cc593e28b000e Mon Sep 17 00:00:00 2001 +From 2f727e3366339a3f86f865f9f7eb008b45f7a806 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Fri, 20 Sep 2013 19:07:56 +0100 -Subject: [PATCH 80/82] dwc_otg: Fix for occasional lockup on boot when doing a +Subject: [PATCH 59/91] dwc_otg: Fix for occasional lockup on boot when doing a USB reset --- @@ -99064,13 +98557,13 @@ index f8dc4be..64d33a5 100644 return 1; } -- -1.8.4 +1.8.5.1 -From dc8b6fedf169cc022e3729dab011e0b2b770d93b Mon Sep 17 00:00:00 2001 +From 50a082721331f9d9edf6f0fa0e16f031f06dea46 Mon Sep 17 00:00:00 2001 From: P33M Date: Fri, 27 Sep 2013 14:42:24 +0100 -Subject: [PATCH 81/82] dwc_otg: Don't issue traffic to LS devices in FS mode +Subject: [PATCH 60/91] dwc_otg: Don't issue traffic to LS devices in FS mode Issuing low-speed packets when the root port is in full-speed mode causes the root port to stop responding. Explicitly fail when @@ -99109,31 +98602,10408 @@ index 1904f6a..22300f0 100644 if (qtd == NULL) { DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n"); -- -1.8.4 +1.8.5.1 -From c7768e2beacacbef7d6d05f40f45f0aa96cf7082 Mon Sep 17 00:00:00 2001 -From: popcornmix -Date: Sat, 9 Nov 2013 17:42:58 +0000 -Subject: [PATCH 82/82] mmc: Report 3.3V support in caps +From 5c01a5307f6ce5f030450430fed730fb680142d3 Mon Sep 17 00:00:00 2001 +From: Andrey Vagin +Date: Wed, 6 Nov 2013 13:25:20 +0400 +Subject: [PATCH 61/91] ARM: bcm2708: PL01X debug include was moved into + arch/arm/include/debug/ --- - drivers/mmc/host/sdhci-bcm2708.c | 2 ++ - 1 file changed, 2 insertions(+) + arch/arm/mach-bcm2708/include/mach/debug-macro.S | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) -diff --git a/drivers/mmc/host/sdhci-bcm2708.c b/drivers/mmc/host/sdhci-bcm2708.c -index 45499d8..6ef7899 100644 ---- a/drivers/mmc/host/sdhci-bcm2708.c -+++ b/drivers/mmc/host/sdhci-bcm2708.c -@@ -1282,6 +1282,8 @@ static int sdhci_bcm2708_probe(struct platform_device *pdev) - host_priv->dma_chan, host_priv->dma_chan_base, - host_priv->dma_irq); +diff --git a/arch/arm/mach-bcm2708/include/mach/debug-macro.S b/arch/arm/mach-bcm2708/include/mach/debug-macro.S +index 2d0dc1c..b24304a 100644 +--- a/arch/arm/mach-bcm2708/include/mach/debug-macro.S ++++ b/arch/arm/mach-bcm2708/include/mach/debug-macro.S +@@ -19,4 +19,4 @@ + ldr \rv, =IO_ADDRESS(UART0_BASE) + .endm -+ // we support 3.3V -+ host->caps |= SDHCI_CAN_VDD_330; - if (allow_highspeed) - host->mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; +-#include ++#include +-- +1.8.5.1 + + +From ce1424b7ead68cdebae2a8000f9af2d0e832e271 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Sat, 23 Nov 2013 11:47:00 +0000 +Subject: [PATCH 62/91] config: Add CONFIG_IFB + +--- + arch/arm/configs/bcmrpi_defconfig | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig +index f4fba45..408c328 100644 +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -408,6 +408,7 @@ CONFIG_DM_DELAY=m + CONFIG_NETDEVICES=y + CONFIG_BONDING=m + CONFIG_DUMMY=m ++CONFIG_IFB=m + CONFIG_MACVLAN=m + CONFIG_NETCONSOLE=m + CONFIG_TUN=m +-- +1.8.5.1 + + +From 0dc92ec6083156e921a20cdfd57bbfba5a1ed2cb Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 27 Nov 2013 22:52:53 +0000 +Subject: [PATCH 63/91] dvb: Add support for CableStar Device + +--- + drivers/media/dvb-core/dvb-usb-ids.h | 1 + + drivers/media/usb/dvb-usb-v2/az6007.c | 59 +++++++++++++++++++++++++++++++++++ + 2 files changed, 60 insertions(+) + +diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h +index 419a2d6..4a53454 100644 +--- a/drivers/media/dvb-core/dvb-usb-ids.h ++++ b/drivers/media/dvb-core/dvb-usb-ids.h +@@ -365,6 +365,7 @@ + #define USB_PID_TERRATEC_DVBS2CI_V2 0x10ac + #define USB_PID_TECHNISAT_USB2_HDCI_V1 0x0001 + #define USB_PID_TECHNISAT_USB2_HDCI_V2 0x0002 ++#define USB_PID_TECHNISAT_USB2_CABLESTAR_HDCI 0x0003 + #define USB_PID_TECHNISAT_AIRSTAR_TELESTICK_2 0x0004 + #define USB_PID_TECHNISAT_USB2_DVB_S2 0x0500 + #define USB_PID_CPYTO_REDI_PC50A 0xa803 +diff --git a/drivers/media/usb/dvb-usb-v2/az6007.c b/drivers/media/usb/dvb-usb-v2/az6007.c +index 44c64ef3..c1051c3 100644 +--- a/drivers/media/usb/dvb-usb-v2/az6007.c ++++ b/drivers/media/usb/dvb-usb-v2/az6007.c +@@ -68,6 +68,19 @@ struct az6007_device_state { + .microcode_name = "dvb-usb-terratec-h7-drxk.fw", + }; + ++static struct drxk_config cablestar_hdci_drxk = { ++ .adr = 0x29, ++ .parallel_ts = true, ++ .dynamic_clk = true, ++ .single_master = true, ++ .enable_merr_cfg = true, ++ .no_i2c_bridge = false, ++ .chunk_size = 64, ++ .mpeg_out_clk_strength = 0x02, ++ .qam_demod_parameter_count = 2, ++ .microcode_name = "dvb-usb-technisat-cablestar-hdci-drxk.fw", ++}; ++ + static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) + { + struct az6007_device_state *st = fe_to_priv(fe); +@@ -630,6 +643,27 @@ static int az6007_frontend_attach(struct dvb_usb_adapter *adap) + return 0; + } + ++static int az6007_cablestar_hdci_frontend_attach(struct dvb_usb_adapter *adap) ++{ ++ struct az6007_device_state *st = adap_to_priv(adap); ++ struct dvb_usb_device *d = adap_to_d(adap); ++ ++ pr_debug("attaching demod drxk\n"); ++ ++ adap->fe[0] = dvb_attach(drxk_attach, &cablestar_hdci_drxk, ++ &d->i2c_adap); ++ if (!adap->fe[0]) ++ return -EINVAL; ++ ++ adap->fe[0]->sec_priv = adap; ++ st->gate_ctrl = adap->fe[0]->ops.i2c_gate_ctrl; ++ adap->fe[0]->ops.i2c_gate_ctrl = drxk_gate_ctrl; ++ ++ az6007_ci_init(adap); ++ ++ return 0; ++} ++ + static int az6007_tuner_attach(struct dvb_usb_adapter *adap) + { + struct dvb_usb_device *d = adap_to_d(adap); +@@ -868,6 +902,29 @@ static int az6007_download_firmware(struct dvb_usb_device *d, + } + }; + ++static struct dvb_usb_device_properties az6007_cablestar_hdci_props = { ++ .driver_name = KBUILD_MODNAME, ++ .owner = THIS_MODULE, ++ .firmware = AZ6007_FIRMWARE, ++ ++ .adapter_nr = adapter_nr, ++ .size_of_priv = sizeof(struct az6007_device_state), ++ .i2c_algo = &az6007_i2c_algo, ++ .tuner_attach = az6007_tuner_attach, ++ .frontend_attach = az6007_cablestar_hdci_frontend_attach, ++ .streaming_ctrl = az6007_streaming_ctrl, ++/* ditch get_rc_config as it can't work (TS35 remote, I believe it's rc5) */ ++ .get_rc_config = NULL, ++ .read_mac_address = az6007_read_mac_addr, ++ .download_firmware = az6007_download_firmware, ++ .identify_state = az6007_identify_state, ++ .power_ctrl = az6007_power_ctrl, ++ .num_adapters = 1, ++ .adapter = { ++ { .stream = DVB_USB_STREAM_BULK(0x02, 10, 4096), } ++ } ++}; ++ + static struct usb_device_id az6007_usb_table[] = { + {DVB_USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007, + &az6007_props, "Azurewave 6007", RC_MAP_EMPTY)}, +@@ -875,6 +932,8 @@ static int az6007_download_firmware(struct dvb_usb_device *d, + &az6007_props, "Terratec H7", RC_MAP_NEC_TERRATEC_CINERGY_XS)}, + {DVB_USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7_2, + &az6007_props, "Terratec H7", RC_MAP_NEC_TERRATEC_CINERGY_XS)}, ++ {DVB_USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_CABLESTAR_HDCI, ++ &az6007_cablestar_hdci_props, "Technisat CableStar Combo HD CI", RC_MAP_EMPTY)}, + {0}, + }; -- -1.8.4 +1.8.5.1 + + +From 01f442ea1097f5b166699225e63b20d736412e95 Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Wed, 20 Nov 2013 11:22:05 +0000 +Subject: [PATCH 64/91] sdhci: Only do one iteration of PIO reading loop + +--- + drivers/mmc/host/sdhci.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c +index a33407e..3c50449 100644 +--- a/drivers/mmc/host/sdhci.c ++++ b/drivers/mmc/host/sdhci.c +@@ -551,6 +551,7 @@ static void sdhci_transfer_pio(struct sdhci_host *host, u32 intstate) + break; + state = sdhci_readl(host, SDHCI_PRESENT_STATE); + available = state & mask; ++ break; + } + + DBG("PIO transfer complete - %d blocks left.\n", host->blocks); +-- +1.8.5.1 + + +From 44d55a4664c1cd5e6bf19cc4ac61e97bdeeb0bc1 Mon Sep 17 00:00:00 2001 +From: Vincent Sanders +Date: Mon, 2 Sep 2013 16:44:57 +0100 +Subject: [PATCH 65/91] vchiq: create_pagelist copes with vmalloc memory + +Signed-off-by: Daniel Stone +--- + .../interface/vchiq_arm/vchiq_2835_arm.c | 83 ++++++++++++++-------- + 1 file changed, 53 insertions(+), 30 deletions(-) + +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +index 2b5fa56..b3bdaa2 100644 +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +@@ -374,6 +374,7 @@ + unsigned int num_pages, offset, i; + char *addr, *base_addr, *next_addr; + int run, addridx, actual_pages; ++ unsigned long *need_release; + + offset = (unsigned int)buf & (PAGE_SIZE - 1); + num_pages = (count + offset + PAGE_SIZE - 1) / PAGE_SIZE; +@@ -384,9 +385,10 @@ + ** list + */ + pagelist = kmalloc(sizeof(PAGELIST_T) + +- (num_pages * sizeof(unsigned long)) + +- (num_pages * sizeof(pages[0])), +- GFP_KERNEL); ++ (num_pages * sizeof(unsigned long)) + ++ sizeof(unsigned long) + ++ (num_pages * sizeof(pages[0])), ++ GFP_KERNEL); + + vchiq_log_trace(vchiq_arm_log_level, + "create_pagelist - %x", (unsigned int)pagelist); +@@ -394,28 +396,44 @@ + return -ENOMEM; + + addrs = pagelist->addrs; +- pages = (struct page **)(addrs + num_pages); ++ need_release = (unsigned long *)(addrs + num_pages); ++ pages = (struct page **)(addrs + num_pages + 1); + +- down_read(&task->mm->mmap_sem); +- actual_pages = get_user_pages(task, task->mm, +- (unsigned long)buf & ~(PAGE_SIZE - 1), num_pages, +- (type == PAGELIST_READ) /*Write */ , 0 /*Force */ , +- pages, NULL /*vmas */); +- up_read(&task->mm->mmap_sem); +- +- if (actual_pages != num_pages) +- { +- /* This is probably due to the process being killed */ +- while (actual_pages > 0) +- { +- actual_pages--; +- page_cache_release(pages[actual_pages]); +- } +- kfree(pagelist); +- if (actual_pages == 0) +- actual_pages = -ENOMEM; +- return actual_pages; +- } ++ if (is_vmalloc_addr(buf)) { ++ for (actual_pages = 0; actual_pages < num_pages; actual_pages++) { ++ pages[actual_pages] = vmalloc_to_page(buf + (actual_pages * PAGE_SIZE)); ++ } ++ *need_release = 0; /* do not try and release vmalloc pages */ ++ } else { ++ down_read(&task->mm->mmap_sem); ++ actual_pages = get_user_pages(task, task->mm, ++ (unsigned long)buf & ~(PAGE_SIZE - 1), ++ num_pages, ++ (type == PAGELIST_READ) /*Write */ , ++ 0 /*Force */ , ++ pages, ++ NULL /*vmas */); ++ up_read(&task->mm->mmap_sem); ++ ++ if (actual_pages != num_pages) { ++ vchiq_log_info(vchiq_arm_log_level, ++ "create_pagelist - only %d/%d pages locked", ++ actual_pages, ++ num_pages); ++ ++ /* This is probably due to the process being killed */ ++ while (actual_pages > 0) ++ { ++ actual_pages--; ++ page_cache_release(pages[actual_pages]); ++ } ++ kfree(pagelist); ++ if (actual_pages == 0) ++ actual_pages = -ENOMEM; ++ return actual_pages; ++ } ++ *need_release = 1; /* release user pages */ ++ } + + pagelist->length = count; + pagelist->type = type; +@@ -482,6 +500,7 @@ + static void + free_pagelist(PAGELIST_T *pagelist, int actual) + { ++ unsigned long *need_release; + struct page **pages; + unsigned int num_pages, i; + +@@ -492,7 +511,8 @@ + (pagelist->length + pagelist->offset + PAGE_SIZE - 1) / + PAGE_SIZE; + +- pages = (struct page **)(pagelist->addrs + num_pages); ++ need_release = (unsigned long *)(pagelist->addrs + num_pages); ++ pages = (struct page **)(pagelist->addrs + num_pages + 1); + + /* Deal with any partial cache lines (fragments) */ + if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS) { +@@ -528,11 +548,14 @@ + up(&g_free_fragments_sema); + } + +- for (i = 0; i < num_pages; i++) { +- if (pagelist->type != PAGELIST_WRITE) +- set_page_dirty(pages[i]); +- page_cache_release(pages[i]); +- } ++ if (*need_release) { ++ for (i = 0; i < num_pages; i++) { ++ if (pagelist->type != PAGELIST_WRITE) ++ set_page_dirty(pages[i]); ++ ++ page_cache_release(pages[i]); ++ } ++ } + + kfree(pagelist); + } +-- +1.8.5.1 + + +From eee31b397622d0647e721581639248598081b88a Mon Sep 17 00:00:00 2001 +From: Vincent Sanders +Date: Mon, 30 Sep 2013 17:04:55 +0100 +Subject: [PATCH 66/91] vchiq: fix the shim message release + +Signed-off-by: Daniel Stone +--- + .../vc04_services/interface/vchiq_arm/vchiq_shim.c | 47 +++++++++++++--------- + 1 file changed, 29 insertions(+), 18 deletions(-) + +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c +index f752f8d..fe9bd80 100644 +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c +@@ -545,47 +545,58 @@ static VCHIQ_STATUS_T shim_callback(VCHIQ_REASON_T reason, + SHIM_SERVICE_T *service = + (SHIM_SERVICE_T *)VCHIQ_GET_SERVICE_USERDATA(handle); + ++ if (!service->callback) ++ goto release; ++ + switch (reason) { + case VCHIQ_MESSAGE_AVAILABLE: + vchiu_queue_push(&service->queue, header); + +- if (service->callback) +- service->callback(service->callback_param, +- VCHI_CALLBACK_MSG_AVAILABLE, NULL); ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_MSG_AVAILABLE, NULL); ++ ++ goto done; + break; ++ + case VCHIQ_BULK_TRANSMIT_DONE: +- if (service->callback) +- service->callback(service->callback_param, +- VCHI_CALLBACK_BULK_SENT, bulk_user); ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_BULK_SENT, bulk_user); + break; ++ + case VCHIQ_BULK_RECEIVE_DONE: +- if (service->callback) +- service->callback(service->callback_param, +- VCHI_CALLBACK_BULK_RECEIVED, bulk_user); ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_BULK_RECEIVED, bulk_user); + break; ++ + case VCHIQ_SERVICE_CLOSED: +- if (service->callback) +- service->callback(service->callback_param, +- VCHI_CALLBACK_SERVICE_CLOSED, NULL); ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_SERVICE_CLOSED, NULL); + break; ++ + case VCHIQ_SERVICE_OPENED: + /* No equivalent VCHI reason */ + break; ++ + case VCHIQ_BULK_TRANSMIT_ABORTED: +- if (service->callback) +- service->callback(service->callback_param, +- VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, bulk_user); ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, ++ bulk_user); + break; ++ + case VCHIQ_BULK_RECEIVE_ABORTED: +- if (service->callback) +- service->callback(service->callback_param, +- VCHI_CALLBACK_BULK_RECEIVE_ABORTED, bulk_user); ++ service->callback(service->callback_param, ++ VCHI_CALLBACK_BULK_RECEIVE_ABORTED, ++ bulk_user); + break; ++ + default: + WARN(1, "not supported\n"); + break; + } + ++release: ++ vchiq_release_message(service->handle, header); ++done: + return VCHIQ_SUCCESS; + } + +-- +1.8.5.1 + + +From b5326e571bda2df776335cd880f15f646ee52733 Mon Sep 17 00:00:00 2001 +From: Vincent Sanders +Date: Sat, 9 Nov 2013 22:37:21 +0000 +Subject: [PATCH 67/91] vchiq: export additional symbols + +Signed-off-by: Daniel Stone +--- + drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c +index fe9bd80..a0b069d 100644 +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c +@@ -405,6 +405,7 @@ int32_t vchi_held_msg_release(VCHI_HELD_MSG_T *message) + + return 0; + } ++EXPORT_SYMBOL(vchi_held_msg_release); + + /*********************************************************** + * Name: vchi_msg_hold +@@ -450,6 +451,7 @@ int32_t vchi_msg_hold(VCHI_SERVICE_HANDLE_T handle, + + return 0; + } ++EXPORT_SYMBOL(vchi_msg_hold); + + /*********************************************************** + * Name: vchi_initialise +-- +1.8.5.1 + + +From fe35af6dea3097d7589e0afed41fdb9ca23bcc0d Mon Sep 17 00:00:00 2001 +From: Vincent Sanders +Date: Wed, 30 Jan 2013 12:45:18 +0000 +Subject: [PATCH 68/91] bcm2835: add v4l2 camera device + +- Supports raw YUV capture, preview, JPEG and H264. +- Uses videobuf2 for data transfer, using dma_buf. +- Uses 3.6.10 timestamping +- Camera power based on use +- Uses immutable input mode on video encoder + +Signed-off-by: Daniel Stone +Signed-off-by: Luke Diamand +--- + Documentation/video4linux/bcm2835-v4l2.txt | 60 + + drivers/media/platform/Kconfig | 1 + + drivers/media/platform/Makefile | 2 + + drivers/media/platform/bcm2835/Kconfig | 25 + + drivers/media/platform/bcm2835/Makefile | 5 + + drivers/media/platform/bcm2835/bcm2835-camera.c | 1478 +++++++++++++++++ + drivers/media/platform/bcm2835/bcm2835-camera.h | 113 ++ + drivers/media/platform/bcm2835/controls.c | 725 ++++++++ + drivers/media/platform/bcm2835/mmal-common.h | 52 + + drivers/media/platform/bcm2835/mmal-encodings.h | 93 ++ + drivers/media/platform/bcm2835/mmal-msg-common.h | 50 + + drivers/media/platform/bcm2835/mmal-msg-format.h | 81 + + drivers/media/platform/bcm2835/mmal-msg-port.h | 107 ++ + drivers/media/platform/bcm2835/mmal-msg.h | 404 +++++ + drivers/media/platform/bcm2835/mmal-parameters.h | 539 ++++++ + drivers/media/platform/bcm2835/mmal-vchiq.c | 1916 ++++++++++++++++++++++ + drivers/media/platform/bcm2835/mmal-vchiq.h | 178 ++ + 17 files changed, 5829 insertions(+) + create mode 100644 Documentation/video4linux/bcm2835-v4l2.txt + create mode 100644 drivers/media/platform/bcm2835/Kconfig + create mode 100644 drivers/media/platform/bcm2835/Makefile + create mode 100644 drivers/media/platform/bcm2835/bcm2835-camera.c + create mode 100644 drivers/media/platform/bcm2835/bcm2835-camera.h + create mode 100644 drivers/media/platform/bcm2835/controls.c + create mode 100644 drivers/media/platform/bcm2835/mmal-common.h + create mode 100644 drivers/media/platform/bcm2835/mmal-encodings.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg-common.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg-format.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg-port.h + create mode 100644 drivers/media/platform/bcm2835/mmal-msg.h + create mode 100644 drivers/media/platform/bcm2835/mmal-parameters.h + create mode 100644 drivers/media/platform/bcm2835/mmal-vchiq.c + create mode 100644 drivers/media/platform/bcm2835/mmal-vchiq.h + +diff --git a/Documentation/video4linux/bcm2835-v4l2.txt b/Documentation/video4linux/bcm2835-v4l2.txt +new file mode 100644 +index 0000000..c585a8f +--- /dev/null ++++ b/Documentation/video4linux/bcm2835-v4l2.txt +@@ -0,0 +1,60 @@ ++ ++BCM2835 (aka Raspberry Pi) V4L2 driver ++====================================== ++ ++1. Copyright ++============ ++ ++Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ ++2. License ++========== ++ ++This program is free software; you can redistribute it and/or modify ++it under the terms of the GNU General Public License as published by ++the Free Software Foundation; either version 2 of the License, or ++(at your option) any later version. ++ ++This program is distributed in the hope that it will be useful, ++but WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++GNU General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with this program; if not, write to the Free Software ++Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ ++3. Quick Start ++============== ++ ++You need a version 1.0 or later of v4l2-ctl, available from: ++ git://git.linuxtv.org/v4l-utils.git ++ ++$ sudo modprobe bcm2835-v4l2 ++ ++Turn on the overlay: ++ ++$ v4l2-ctl --overlay=1 ++ ++Turn off the overlay: ++ ++$ v4l2-ctl --overlay=0 ++ ++Set the capture format for video: ++ ++$ v4l2-ctl --set-fmt-video=width=1920,height=1088,pixelformat=4 ++ ++(Note: 1088 not 1080). ++ ++Capture: ++ ++$ v4l2-ctl --stream-mmap=3 --stream-count=100 --stream-to=somefile.h264 ++ ++Stills capture: ++ ++$ v4l2-ctl --set-fmt-video=width=2592,height=1944,pixelformat=3 ++$ v4l2-ctl --stream-mmap=3 --stream-count=1 --stream-to=somefile.jpg ++ ++List of available formats: ++ ++$ v4l2-ctl --list-formats +diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig +index c7caf94..64ff97e 100644 +--- a/drivers/media/platform/Kconfig ++++ b/drivers/media/platform/Kconfig +@@ -124,6 +124,7 @@ config VIDEO_S3C_CAMIF + source "drivers/media/platform/soc_camera/Kconfig" + source "drivers/media/platform/exynos4-is/Kconfig" + source "drivers/media/platform/s5p-tv/Kconfig" ++source "drivers/media/platform/bcm2835/Kconfig" + + endif # V4L_PLATFORM_DRIVERS + +diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile +index 4e4da48..4b7b42f 100644 +--- a/drivers/media/platform/Makefile ++++ b/drivers/media/platform/Makefile +@@ -52,4 +52,6 @@ obj-y += davinci/ + + obj-$(CONFIG_ARCH_OMAP) += omap/ + ++obj-$(CONFIG_VIDEO_BCM2835) += bcm2835/ ++ + ccflags-y += -I$(srctree)/drivers/media/i2c +diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/bcm2835/Kconfig +new file mode 100644 +index 0000000..a8fd172 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/Kconfig +@@ -0,0 +1,25 @@ ++# Broadcom VideoCore IV v4l2 camera support ++ ++config VIDEO_BCM2835 ++ bool "Broadcom BCM2835 camera interface driver" ++ depends on VIDEO_V4L2 && ARCH_BCM2708 ++ ---help--- ++ Say Y here to enable camera host interface devices for ++ Broadcom BCM2835 SoC. This operates over the VCHIQ interface ++ to a service running on VideoCore. ++ ++ ++if VIDEO_BCM2835 ++ ++config VIDEO_BCM2835_MMAL ++ tristate "Broadcom BM2835 MMAL camera interface driver" ++ depends on BCM2708_VCHIQ ++ select VIDEOBUF2_VMALLOC ++ ---help--- ++ This is a V4L2 driver for the Broadcom BCM2835 MMAL camera host interface ++ ++ To compile this driver as a module, choose M here: the ++ module will be called bcm2835-v4l2.o ++ ++ ++endif # VIDEO_BM2835 +diff --git a/drivers/media/platform/bcm2835/Makefile b/drivers/media/platform/bcm2835/Makefile +new file mode 100644 +index 0000000..f17c79c +--- /dev/null ++++ b/drivers/media/platform/bcm2835/Makefile +@@ -0,0 +1,5 @@ ++bcm2835-v4l2-objs := bcm2835-camera.o controls.o mmal-vchiq.o ++ ++obj-$(CONFIG_VIDEO_BCM2835_MMAL) += bcm2835-v4l2.o ++ ++ccflags-$(CONFIG_VIDEO_BCM2835) += -Idrivers/misc/vc04_services -Idrivers/misc/vc04_services/interface/vcos/linuxkernel -D__VCCOREVER__=0x04000000 +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c +new file mode 100644 +index 0000000..47fe45d +--- /dev/null ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.c +@@ -0,0 +1,1478 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mmal-common.h" ++#include "mmal-encodings.h" ++#include "mmal-vchiq.h" ++#include "mmal-msg.h" ++#include "mmal-parameters.h" ++#include "bcm2835-camera.h" ++ ++#define BM2835_MMAL_VERSION "0.0.2" ++#define BM2835_MMAL_MODULE_NAME "bcm2835-v4l2" ++ ++#define MAX_WIDTH 2592 ++#define MAX_HEIGHT 1944 ++#define MIN_BUFFER_SIZE (80*1024) ++ ++#define MAX_VIDEO_MODE_WIDTH 1280 ++#define MAX_VIDEO_MODE_HEIGHT 720 ++ ++MODULE_DESCRIPTION("Broadcom 2835 MMAL video capture"); ++MODULE_AUTHOR("Vincent Sanders"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(BM2835_MMAL_VERSION); ++ ++int bcm2835_v4l2_debug; ++module_param_named(debug, bcm2835_v4l2_debug, int, 0644); ++MODULE_PARM_DESC(bcm2835_v4l2_debug, "Debug level 0-2"); ++ ++static struct bm2835_mmal_dev *gdev; /* global device data */ ++ ++/* video formats */ ++static struct mmal_fmt formats[] = { ++ { ++ .name = "4:2:0, packed YUV", ++ .fourcc = V4L2_PIX_FMT_YUV420, ++ .mmal = MMAL_ENCODING_I420, ++ .depth = 12, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "4:2:2, packed, YUYV", ++ .fourcc = V4L2_PIX_FMT_YUYV, ++ .mmal = MMAL_ENCODING_YUYV, ++ .depth = 16, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "RGB24 (BE)", ++ .fourcc = V4L2_PIX_FMT_BGR24, ++ .mmal = MMAL_ENCODING_BGR24, ++ .depth = 24, ++ .mmal_component = MMAL_COMPONENT_CAMERA, ++ }, ++ { ++ .name = "JPEG", ++ .fourcc = V4L2_PIX_FMT_JPEG, ++ .mmal = MMAL_ENCODING_JPEG, ++ .depth = 8, ++ .mmal_component = MMAL_COMPONENT_IMAGE_ENCODE, ++ }, ++ { ++ .name = "H264", ++ .fourcc = V4L2_PIX_FMT_H264, ++ .mmal = MMAL_ENCODING_H264, ++ .depth = 8, ++ .mmal_component = MMAL_COMPONENT_VIDEO_ENCODE, ++ } ++}; ++ ++static struct mmal_fmt *get_format(struct v4l2_format *f) ++{ ++ struct mmal_fmt *fmt; ++ unsigned int k; ++ ++ for (k = 0; k < ARRAY_SIZE(formats); k++) { ++ fmt = &formats[k]; ++ if (fmt->fourcc == f->fmt.pix.pixelformat) ++ break; ++ } ++ ++ if (k == ARRAY_SIZE(formats)) ++ return NULL; ++ ++ return &formats[k]; ++} ++ ++/* ------------------------------------------------------------------ ++ Videobuf queue operations ++ ------------------------------------------------------------------*/ ++ ++static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, ++ unsigned int *nbuffers, unsigned int *nplanes, ++ unsigned int sizes[], void *alloc_ctxs[]) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ unsigned long size; ++ ++ /* refuse queue setup if port is not configured */ ++ if (dev->capture.port == NULL) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: capture port not configured\n", __func__); ++ return -EINVAL; ++ } ++ ++ size = dev->capture.port->current_buffer.size; ++ if (size == 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: capture port buffer size is zero\n", __func__); ++ return -EINVAL; ++ } ++ ++ if (*nbuffers < (dev->capture.port->current_buffer.num + 2)) ++ *nbuffers = (dev->capture.port->current_buffer.num + 2); ++ ++ *nplanes = 1; ++ ++ sizes[0] = size; ++ ++ /* ++ * videobuf2-vmalloc allocator is context-less so no need to set ++ * alloc_ctxs array. ++ */ ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ return 0; ++} ++ ++static int buffer_prepare(struct vb2_buffer *vb) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb->vb2_queue); ++ unsigned long size; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ BUG_ON(dev->capture.port == NULL); ++ BUG_ON(dev->capture.fmt == NULL); ++ ++ size = dev->capture.stride * dev->capture.height; ++ if (vb2_plane_size(vb, 0) < size) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s data will not fit into plane (%lu < %lu)\n", ++ __func__, vb2_plane_size(vb, 0), size); ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static inline bool is_capturing(struct bm2835_mmal_dev *dev) ++{ ++ return dev->capture.camera_port == ++ &dev-> ++ component[MMAL_COMPONENT_CAMERA]->output[MMAL_CAMERA_PORT_CAPTURE]; ++} ++ ++static void buffer_cb(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ int status, ++ struct mmal_buffer *buf, ++ unsigned long length, u32 mmal_flags, s64 dts, s64 pts) ++{ ++ struct bm2835_mmal_dev *dev = port->cb_ctx; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: status:%d, buf:%p, length:%lu, flags %u, pts %lld\n", ++ __func__, status, buf, length, mmal_flags, pts); ++ ++ if (status != 0) { ++ /* error in transfer */ ++ if (buf != NULL) { ++ /* there was a buffer with the error so return it */ ++ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); ++ } ++ return; ++ } else if (length == 0) { ++ /* stream ended */ ++ if (buf != NULL) { ++ /* this should only ever happen if the port is ++ * disabled and there are buffers still queued ++ */ ++ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); ++ pr_debug("Empty buffer"); ++ } else if (dev->capture.frame_count) { ++ /* grab another frame */ ++ if (is_capturing(dev)) { ++ pr_debug("Grab another frame"); ++ vchiq_mmal_port_parameter_set( ++ instance, ++ dev->capture. ++ camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture. ++ frame_count, ++ sizeof(dev->capture.frame_count)); ++ } ++ } else { ++ /* signal frame completion */ ++ complete(&dev->capture.frame_cmplt); ++ } ++ } else { ++ if (dev->capture.frame_count) { ++ if (dev->capture.vc_start_timestamp != -1) { ++ s64 runtime_us = pts - ++ dev->capture.vc_start_timestamp; ++ u32 div = 0; ++ u32 rem = 0; ++ ++ div = ++ div_u64_rem(runtime_us, USEC_PER_SEC, &rem); ++ buf->vb.v4l2_buf.timestamp.tv_sec = ++ dev->capture.kernel_start_ts.tv_sec - 1 + ++ div; ++ buf->vb.v4l2_buf.timestamp.tv_usec = ++ dev->capture.kernel_start_ts.tv_usec + rem; ++ ++ if (buf->vb.v4l2_buf.timestamp.tv_usec >= ++ USEC_PER_SEC) { ++ buf->vb.v4l2_buf.timestamp.tv_sec++; ++ buf->vb.v4l2_buf.timestamp.tv_usec -= ++ USEC_PER_SEC; ++ } ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Convert start time %d.%06d and %llu" ++ "with offset %llu to %d.%06d\n", ++ (int)dev->capture.kernel_start_ts. ++ tv_sec, ++ (int)dev->capture.kernel_start_ts. ++ tv_usec, ++ dev->capture.vc_start_timestamp, pts, ++ (int)buf->vb.v4l2_buf.timestamp.tv_sec, ++ (int)buf->vb.v4l2_buf.timestamp. ++ tv_usec); ++ } else { ++ v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); ++ } ++ ++ vb2_set_plane_payload(&buf->vb, 0, length); ++ vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); ++ ++ if (mmal_flags & MMAL_BUFFER_HEADER_FLAG_EOS && ++ is_capturing(dev)) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Grab another frame as buffer has EOS"); ++ vchiq_mmal_port_parameter_set( ++ instance, ++ dev->capture. ++ camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture. ++ frame_count, ++ sizeof(dev->capture.frame_count)); ++ } ++ } else { ++ /* signal frame completion */ ++ complete(&dev->capture.frame_cmplt); ++ } ++ } ++} ++ ++static int enable_camera(struct bm2835_mmal_dev *dev) ++{ ++ int ret; ++ if (!dev->camera_use_count) { ++ ret = vchiq_mmal_component_enable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed enabling camera, ret %d\n", ret); ++ return -EINVAL; ++ } ++ } ++ dev->camera_use_count++; ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, "enabled camera (refcount %d)\n", ++ dev->camera_use_count); ++ return 0; ++} ++ ++static int disable_camera(struct bm2835_mmal_dev *dev) ++{ ++ int ret; ++ if (!dev->camera_use_count) { ++ v4l2_err(&dev->v4l2_dev, ++ "Disabled the camera when already disabled\n"); ++ return -EINVAL; ++ } ++ dev->camera_use_count--; ++ if (!dev->camera_use_count) { ++ unsigned int i = 0xFFFFFFFF; ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Disabling camera\n"); ++ ret = ++ vchiq_mmal_component_disable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed disabling camera, ret %d\n", ret); ++ return -EINVAL; ++ } ++ vchiq_mmal_port_parameter_set( ++ dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]->control, ++ MMAL_PARAMETER_CAMERA_NUM, &i, ++ sizeof(i)); ++ } ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Camera refcount now %d\n", dev->camera_use_count); ++ return 0; ++} ++ ++static void buffer_queue(struct vb2_buffer *vb) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb->vb2_queue); ++ struct mmal_buffer *buf = container_of(vb, struct mmal_buffer, vb); ++ int ret; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s: dev:%p buf:%p\n", __func__, dev, buf); ++ ++ buf->buffer = vb2_plane_vaddr(&buf->vb, 0); ++ buf->buffer_size = vb2_plane_size(&buf->vb, 0); ++ ++ ret = vchiq_mmal_submit_buffer(dev->instance, dev->capture.port, buf); ++ if (ret < 0) ++ v4l2_err(&dev->v4l2_dev, "%s: error submitting buffer\n", ++ __func__); ++} ++ ++static int start_streaming(struct vb2_queue *vq, unsigned int count) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ int ret; ++ int parameter_size; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ /* ensure a format has actually been set */ ++ if (dev->capture.port == NULL) ++ return -EINVAL; ++ ++ if (enable_camera(dev) < 0) { ++ v4l2_err(&dev->v4l2_dev, "Failed to enable camera\n"); ++ return -EINVAL; ++ } ++ ++ /*init_completion(&dev->capture.frame_cmplt); */ ++ ++ /* enable frame capture */ ++ dev->capture.frame_count = 1; ++ ++ /* if the preview is not already running, wait for a few frames for AGC ++ * to settle down. ++ */ ++ if (!dev->component[MMAL_COMPONENT_PREVIEW]->enabled) ++ msleep(300); ++ ++ /* enable the connection from camera to encoder (if applicable) */ ++ if (dev->capture.camera_port != dev->capture.port ++ && dev->capture.camera_port) { ++ ret = vchiq_mmal_port_enable(dev->instance, ++ dev->capture.camera_port, NULL); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to enable encode tunnel - error %d\n", ++ ret); ++ return -1; ++ } ++ } ++ ++ /* Get VC timestamp at this point in time */ ++ parameter_size = sizeof(dev->capture.vc_start_timestamp); ++ if (vchiq_mmal_port_parameter_get(dev->instance, ++ dev->capture.camera_port, ++ MMAL_PARAMETER_SYSTEM_TIME, ++ &dev->capture.vc_start_timestamp, ++ ¶meter_size)) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to get VC start time - update your VC f/w\n"); ++ ++ /* Flag to indicate just to rely on kernel timestamps */ ++ dev->capture.vc_start_timestamp = -1; ++ } else ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Start time %lld size %d\n", ++ dev->capture.vc_start_timestamp, parameter_size); ++ ++ v4l2_get_timestamp(&dev->capture.kernel_start_ts); ++ ++ /* enable the camera port */ ++ dev->capture.port->cb_ctx = dev; ++ ret = ++ vchiq_mmal_port_enable(dev->instance, dev->capture.port, buffer_cb); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to enable capture port - error %d\n", ret); ++ return -1; ++ } ++ ++ /* capture the first frame */ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ dev->capture.camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture.frame_count, ++ sizeof(dev->capture.frame_count)); ++ return 0; ++} ++ ++/* abort streaming and wait for last buffer */ ++static int stop_streaming(struct vb2_queue *vq) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p\n", ++ __func__, dev); ++ ++ init_completion(&dev->capture.frame_cmplt); ++ dev->capture.frame_count = 0; ++ ++ /* ensure a format has actually been set */ ++ if (dev->capture.port == NULL) ++ return -EINVAL; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "stopping capturing\n"); ++ ++ /* stop capturing frames */ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ dev->capture.camera_port, ++ MMAL_PARAMETER_CAPTURE, ++ &dev->capture.frame_count, ++ sizeof(dev->capture.frame_count)); ++ ++ /* wait for last frame to complete */ ++ ret = wait_for_completion_timeout(&dev->capture.frame_cmplt, HZ); ++ if (ret <= 0) ++ v4l2_err(&dev->v4l2_dev, ++ "error %d waiting for frame completion\n", ret); ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "disabling connection\n"); ++ ++ /* disable the connection from camera to encoder */ ++ ret = vchiq_mmal_port_disable(dev->instance, dev->capture.camera_port); ++ if (!ret && dev->capture.camera_port != dev->capture.port) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "disabling port\n"); ++ ret = vchiq_mmal_port_disable(dev->instance, dev->capture.port); ++ } else if (dev->capture.camera_port != dev->capture.port) { ++ v4l2_err(&dev->v4l2_dev, "port_disable failed, error %d\n", ++ ret); ++ } ++ ++ if (disable_camera(dev) < 0) { ++ v4l2_err(&dev->v4l2_dev, "Failed to disable camera"); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static void bm2835_mmal_lock(struct vb2_queue *vq) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ mutex_lock(&dev->mutex); ++} ++ ++static void bm2835_mmal_unlock(struct vb2_queue *vq) ++{ ++ struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vq); ++ mutex_unlock(&dev->mutex); ++} ++ ++static struct vb2_ops bm2835_mmal_video_qops = { ++ .queue_setup = queue_setup, ++ .buf_prepare = buffer_prepare, ++ .buf_queue = buffer_queue, ++ .start_streaming = start_streaming, ++ .stop_streaming = stop_streaming, ++ .wait_prepare = bm2835_mmal_unlock, ++ .wait_finish = bm2835_mmal_lock, ++}; ++ ++/* ------------------------------------------------------------------ ++ IOCTL operations ++ ------------------------------------------------------------------*/ ++ ++/* overlay ioctl */ ++static int vidioc_enum_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ struct mmal_fmt *fmt; ++ ++ if (f->index >= ARRAY_SIZE(formats)) ++ return -EINVAL; ++ ++ fmt = &formats[f->index]; ++ ++ strlcpy(f->description, fmt->name, sizeof(f->description)); ++ f->pixelformat = fmt->fourcc; ++ ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ ++ f->fmt.win = dev->overlay; ++ ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ /* Only support one format so get the current one. */ ++ vidioc_g_fmt_vid_overlay(file, priv, f); ++ ++ /* todo: allow the size and/or offset to be changed. */ ++ return 0; ++} ++ ++static int vidioc_s_fmt_vid_overlay(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ ++ vidioc_try_fmt_vid_overlay(file, priv, f); ++ ++ dev->overlay = f->fmt.win; ++ ++ /* todo: program the preview port parameters */ ++ return 0; ++} ++ ++static int vidioc_overlay(struct file *file, void *f, unsigned int on) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct vchiq_mmal_port *src; ++ struct vchiq_mmal_port *dst; ++ struct mmal_parameter_displayregion prev_config = { ++ .set = MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_ALPHA | ++ MMAL_DISPLAY_SET_DEST_RECT | MMAL_DISPLAY_SET_FULLSCREEN, ++ .layer = PREVIEW_LAYER, ++ .alpha = 255, ++ .fullscreen = 0, ++ .dest_rect = { ++ .x = dev->overlay.w.left, ++ .y = dev->overlay.w.top, ++ .width = dev->overlay.w.width, ++ .height = dev->overlay.w.height, ++ }, ++ }; ++ ++ if ((on && dev->component[MMAL_COMPONENT_PREVIEW]->enabled) || ++ (!on && !dev->component[MMAL_COMPONENT_PREVIEW]->enabled)) ++ return 0; /* already in requested state */ ++ ++ src = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW]; ++ ++ if (!on) { ++ /* disconnect preview ports and disable component */ ++ ret = vchiq_mmal_port_disable(dev->instance, src); ++ if (!ret) ++ ret = ++ vchiq_mmal_port_connect_tunnel(dev->instance, src, ++ NULL); ++ if (ret >= 0) ++ ret = vchiq_mmal_component_disable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_PREVIEW]); ++ ++ disable_camera(dev); ++ return ret; ++ } ++ ++ /* set preview port format and connect it to output */ ++ dst = &dev->component[MMAL_COMPONENT_PREVIEW]->input[0]; ++ ++ ret = vchiq_mmal_port_set_format(dev->instance, src); ++ if (ret < 0) ++ goto error; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, dst, ++ MMAL_PARAMETER_DISPLAYREGION, ++ &prev_config, sizeof(prev_config)); ++ if (ret < 0) ++ goto error; ++ ++ if (enable_camera(dev) < 0) ++ goto error; ++ ++ ret = vchiq_mmal_component_enable( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_PREVIEW]); ++ if (ret < 0) ++ goto error; ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "connecting %p to %p\n", ++ src, dst); ++ ret = vchiq_mmal_port_connect_tunnel(dev->instance, src, dst); ++ if (!ret) ++ ret = vchiq_mmal_port_enable(dev->instance, src, NULL); ++error: ++ return ret; ++} ++ ++static int vidioc_g_fbuf(struct file *file, void *fh, ++ struct v4l2_framebuffer *a) ++{ ++ /* The video overlay must stay within the framebuffer and can't be ++ positioned independently. */ ++ a->flags = V4L2_FBUF_FLAG_OVERLAY; ++ ++ /* todo: v4l2_framebuffer still needs more info filling in ++ * in order to pass the v4l2-compliance test. */ ++ ++ return 0; ++} ++ ++/* input ioctls */ ++static int vidioc_enum_input(struct file *file, void *priv, ++ struct v4l2_input *inp) ++{ ++ /* only a single camera input */ ++ if (inp->index != 0) ++ return -EINVAL; ++ ++ inp->type = V4L2_INPUT_TYPE_CAMERA; ++ sprintf(inp->name, "Camera %u", inp->index); ++ return 0; ++} ++ ++static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) ++{ ++ *i = 0; ++ return 0; ++} ++ ++static int vidioc_s_input(struct file *file, void *priv, unsigned int i) ++{ ++ if (i != 0) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++/* capture ioctls */ ++static int vidioc_querycap(struct file *file, void *priv, ++ struct v4l2_capability *cap) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ u32 major; ++ u32 minor; ++ ++ vchiq_mmal_version(dev->instance, &major, &minor); ++ ++ strcpy(cap->driver, "bm2835 mmal"); ++ snprintf(cap->card, sizeof(cap->card), "mmal service %d.%d", ++ major, minor); ++ ++ snprintf(cap->bus_info, sizeof(cap->bus_info), ++ "platform:%s", dev->v4l2_dev.name); ++ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | ++ V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; ++ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; ++ ++ return 0; ++} ++ ++static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_fmtdesc *f) ++{ ++ struct mmal_fmt *fmt; ++ ++ if (f->index >= ARRAY_SIZE(formats)) ++ return -EINVAL; ++ ++ fmt = &formats[f->index]; ++ ++ strlcpy(f->description, fmt->name, sizeof(f->description)); ++ f->pixelformat = fmt->fourcc; ++ return 0; ++} ++ ++static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ ++ f->fmt.pix.width = dev->capture.width; ++ f->fmt.pix.height = dev->capture.height; ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ f->fmt.pix.pixelformat = dev->capture.fmt->fourcc; ++ f->fmt.pix.bytesperline = ++ (f->fmt.pix.width * dev->capture.fmt->depth) >> 3; ++ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; ++ if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_JPEG ++ && f->fmt.pix.sizeimage < (100 << 10)) { ++ /* Need a minimum size for JPEG to account for EXIF. */ ++ f->fmt.pix.sizeimage = (100 << 10); ++ } ++ ++ if (dev->capture.fmt->fourcc == V4L2_PIX_FMT_YUYV || ++ dev->capture.fmt->fourcc == V4L2_PIX_FMT_UYVY) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ else ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; ++ f->fmt.pix.priv = 0; ++ ++ v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, ++ __func__); ++ return 0; ++} ++ ++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct mmal_fmt *mfmt; ++ ++ mfmt = get_format(f); ++ if (!mfmt) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Fourcc format (0x%08x) unknown.\n", ++ f->fmt.pix.pixelformat); ++ f->fmt.pix.pixelformat = formats[0].fourcc; ++ mfmt = get_format(f); ++ } ++ ++ f->fmt.pix.field = V4L2_FIELD_NONE; ++ /* image must be a multiple of 32 pixels wide and 16 lines high */ ++ v4l_bound_align_image(&f->fmt.pix.width, 48, MAX_WIDTH, 5, ++ &f->fmt.pix.height, 32, MAX_HEIGHT, 4, 0); ++ f->fmt.pix.bytesperline = (f->fmt.pix.width * mfmt->depth) >> 3; ++ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; ++ if (f->fmt.pix.sizeimage < MIN_BUFFER_SIZE) ++ f->fmt.pix.sizeimage = MIN_BUFFER_SIZE; ++ ++ if (mfmt->fourcc == V4L2_PIX_FMT_YUYV || ++ mfmt->fourcc == V4L2_PIX_FMT_UYVY) ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; ++ else ++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; ++ f->fmt.pix.priv = 0; ++ ++ v4l2_dump_pix_format(1, bcm2835_v4l2_debug, &dev->v4l2_dev, &f->fmt.pix, ++ __func__); ++ return 0; ++} ++ ++static int mmal_setup_components(struct bm2835_mmal_dev *dev, ++ struct v4l2_format *f) ++{ ++ int ret; ++ struct vchiq_mmal_port *port = NULL, *camera_port = NULL; ++ struct vchiq_mmal_component *encode_component = NULL; ++ struct mmal_fmt *mfmt = get_format(f); ++ ++ BUG_ON(!mfmt); ++ ++ if (dev->capture.encode_component) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "vid_cap - disconnect previous tunnel\n"); ++ ++ /* Disconnect any previous connection */ ++ vchiq_mmal_port_connect_tunnel(dev->instance, ++ dev->capture.camera_port, NULL); ++ dev->capture.camera_port = NULL; ++ ret = vchiq_mmal_component_disable(dev->instance, ++ dev->capture. ++ encode_component); ++ if (ret) ++ v4l2_err(&dev->v4l2_dev, ++ "Failed to disable encode component %d\n", ++ ret); ++ ++ dev->capture.encode_component = NULL; ++ } ++ /* format dependant port setup */ ++ switch (mfmt->mmal_component) { ++ case MMAL_COMPONENT_CAMERA: ++ /* Make a further decision on port based on resolution */ ++ if (f->fmt.pix.width <= MAX_VIDEO_MODE_WIDTH ++ && f->fmt.pix.height <= MAX_VIDEO_MODE_HEIGHT) ++ camera_port = port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO]; ++ else ++ camera_port = port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE]; ++ break; ++ case MMAL_COMPONENT_IMAGE_ENCODE: ++ encode_component = dev->component[MMAL_COMPONENT_IMAGE_ENCODE]; ++ port = &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->output[0]; ++ camera_port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE]; ++ break; ++ case MMAL_COMPONENT_VIDEO_ENCODE: ++ encode_component = dev->component[MMAL_COMPONENT_VIDEO_ENCODE]; ++ port = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ camera_port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO]; ++ break; ++ default: ++ break; ++ } ++ ++ if (!port) ++ return -EINVAL; ++ ++ if (encode_component) ++ camera_port->format.encoding = MMAL_ENCODING_OPAQUE; ++ else ++ camera_port->format.encoding = mfmt->mmal; ++ ++ camera_port->format.encoding_variant = 0; ++ camera_port->es.video.width = f->fmt.pix.width; ++ camera_port->es.video.height = f->fmt.pix.height; ++ camera_port->es.video.crop.x = 0; ++ camera_port->es.video.crop.y = 0; ++ camera_port->es.video.crop.width = f->fmt.pix.width; ++ camera_port->es.video.crop.height = f->fmt.pix.height; ++ camera_port->es.video.frame_rate.num = 30; ++ camera_port->es.video.frame_rate.den = 1; ++ ++ ret = vchiq_mmal_port_set_format(dev->instance, camera_port); ++ ++ if (!ret ++ && camera_port == ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO]) { ++ bool overlay_enabled = ++ !!dev->component[MMAL_COMPONENT_PREVIEW]->enabled; ++ struct vchiq_mmal_port *preview_port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW]; ++ /* Preview and encode ports need to match on resolution */ ++ if (overlay_enabled) { ++ /* Need to disable the overlay before we can update ++ * the resolution ++ */ ++ ret = ++ vchiq_mmal_port_disable(dev->instance, ++ preview_port); ++ if (!ret) ++ ret = ++ vchiq_mmal_port_connect_tunnel( ++ dev->instance, ++ preview_port, ++ NULL); ++ } ++ preview_port->es.video.width = f->fmt.pix.width; ++ preview_port->es.video.height = f->fmt.pix.height; ++ preview_port->es.video.crop.x = 0; ++ preview_port->es.video.crop.y = 0; ++ preview_port->es.video.crop.width = f->fmt.pix.width; ++ preview_port->es.video.crop.height = f->fmt.pix.height; ++ preview_port->es.video.frame_rate.num = 30; ++ preview_port->es.video.frame_rate.den = 1; ++ ret = vchiq_mmal_port_set_format(dev->instance, preview_port); ++ if (overlay_enabled) { ++ ret = vchiq_mmal_port_connect_tunnel( ++ dev->instance, ++ preview_port, ++ &dev->component[MMAL_COMPONENT_PREVIEW]->input[0]); ++ if (!ret) ++ ret = vchiq_mmal_port_enable(dev->instance, ++ preview_port, ++ NULL); ++ } ++ } ++ ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s failed to set format\n", __func__); ++ /* ensure capture is not going to be tried */ ++ dev->capture.port = NULL; ++ } else { ++ if (encode_component) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "vid_cap - set up encode comp\n"); ++ ++ /* configure buffering */ ++ camera_port->current_buffer.size = ++ camera_port->recommended_buffer.size; ++ camera_port->current_buffer.num = ++ camera_port->recommended_buffer.num; ++ ++ port->format.encoding = mfmt->mmal; ++ port->format.encoding_variant = 0; ++ /* Set any encoding specific parameters */ ++ switch (mfmt->mmal_component) { ++ case MMAL_COMPONENT_VIDEO_ENCODE: ++ port->format.bitrate = ++ dev->capture.encode_bitrate; ++ break; ++ case MMAL_COMPONENT_IMAGE_ENCODE: ++ /* Could set EXIF parameters here */ ++ break; ++ default: ++ break; ++ } ++ ret = vchiq_mmal_port_set_format(dev->instance, port); ++ ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "%s failed to set format\n", __func__); ++ } else { ++ ret = vchiq_mmal_component_enable( ++ dev->instance, ++ encode_component); ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "%s Failed to enable encode components\n", ++ __func__); ++ } else { ++ /* configure buffering */ ++ port->current_buffer.num = 1; ++ port->current_buffer.size = ++ f->fmt.pix.sizeimage; ++ if (port->format.encoding == ++ MMAL_ENCODING_JPEG) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "JPEG - fiddle buffer size\n"); ++ port->current_buffer.size = ++ (f->fmt.pix.sizeimage < ++ (100 << 10)) ++ ? (100 << 10) : f->fmt.pix. ++ sizeimage; ++ } ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "vid_cap - current_buffer.size being set to %d\n", ++ f->fmt.pix.sizeimage); ++ port->current_buffer.alignment = 0; ++ ret = ++ vchiq_mmal_port_connect_tunnel( ++ dev->instance, ++ camera_port, ++ &encode_component->input[0]); ++ if (ret) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "%s failed to create connection\n", ++ __func__); ++ /* ensure capture is not going to be tried */ ++ dev->capture.port = NULL; ++ } ++ } ++ } ++ } else { ++ /* configure buffering */ ++ camera_port->current_buffer.num = 1; ++ camera_port->current_buffer.size = f->fmt.pix.sizeimage; ++ camera_port->current_buffer.alignment = 0; ++ } ++ ++ if (!ret) { ++ dev->capture.fmt = mfmt; ++ dev->capture.stride = f->fmt.pix.bytesperline; ++ dev->capture.width = port->es.video.crop.width; ++ dev->capture.height = port->es.video.crop.height; ++ ++ /* select port for capture */ ++ dev->capture.port = port; ++ dev->capture.camera_port = camera_port; ++ dev->capture.encode_component = encode_component; ++ } ++ } ++ ++ /* todo: Need to convert the vchiq/mmal error into a v4l2 error. */ ++ return ret; ++} ++ ++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, ++ struct v4l2_format *f) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct mmal_fmt *mfmt; ++ ++ /* try the format to set valid parameters */ ++ ret = vidioc_try_fmt_vid_cap(file, priv, f); ++ if (ret) { ++ v4l2_err(&dev->v4l2_dev, ++ "vid_cap - vidioc_try_fmt_vid_cap failed\n"); ++ return ret; ++ } ++ ++ /* if a capture is running refuse to set format */ ++ if (vb2_is_busy(&dev->capture.vb_vidq)) { ++ v4l2_info(&dev->v4l2_dev, "%s device busy\n", __func__); ++ return -EBUSY; ++ } ++ ++ /* If the format is unsupported v4l2 says we should switch to ++ * a supported one and not return an error. */ ++ mfmt = get_format(f); ++ if (!mfmt) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Fourcc format (0x%08x) unknown.\n", ++ f->fmt.pix.pixelformat); ++ f->fmt.pix.pixelformat = formats[0].fourcc; ++ mfmt = get_format(f); ++ } ++ ++ ret = mmal_setup_components(dev, f); ++ if (ret != 0) ++ v4l2_err(&dev->v4l2_dev, ++ "%s: failed to setup mmal components: %d\n", ++ __func__, ret); ++ ++ return ret; ++} ++ ++static const struct v4l2_ioctl_ops camera0_ioctl_ops = { ++ /* overlay */ ++ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, ++ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, ++ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, ++ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, ++ .vidioc_overlay = vidioc_overlay, ++ .vidioc_g_fbuf = vidioc_g_fbuf, ++ ++ /* inputs */ ++ .vidioc_enum_input = vidioc_enum_input, ++ .vidioc_g_input = vidioc_g_input, ++ .vidioc_s_input = vidioc_s_input, ++ ++ /* capture */ ++ .vidioc_querycap = vidioc_querycap, ++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, ++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, ++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, ++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, ++ ++ /* buffer management */ ++ .vidioc_reqbufs = vb2_ioctl_reqbufs, ++ .vidioc_create_bufs = vb2_ioctl_create_bufs, ++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf, ++ .vidioc_querybuf = vb2_ioctl_querybuf, ++ .vidioc_qbuf = vb2_ioctl_qbuf, ++ .vidioc_dqbuf = vb2_ioctl_dqbuf, ++ .vidioc_streamon = vb2_ioctl_streamon, ++ .vidioc_streamoff = vb2_ioctl_streamoff, ++ ++ .vidioc_log_status = v4l2_ctrl_log_status, ++ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, ++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe, ++}; ++ ++/* ------------------------------------------------------------------ ++ Driver init/finalise ++ ------------------------------------------------------------------*/ ++ ++static const struct v4l2_file_operations camera0_fops = { ++ .owner = THIS_MODULE, ++ .open = v4l2_fh_open, ++ .release = vb2_fop_release, ++ .read = vb2_fop_read, ++ .poll = vb2_fop_poll, ++ .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ ++ .mmap = vb2_fop_mmap, ++}; ++ ++static struct video_device vdev_template = { ++ .name = "camera0", ++ .fops = &camera0_fops, ++ .ioctl_ops = &camera0_ioctl_ops, ++ .release = video_device_release_empty, ++}; ++ ++static int set_camera_parameters(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *camera) ++{ ++ int ret; ++ struct mmal_parameter_camera_config cam_config = { ++ .max_stills_w = MAX_WIDTH, ++ .max_stills_h = MAX_HEIGHT, ++ .stills_yuv422 = 1, ++ .one_shot_stills = 1, ++ .max_preview_video_w = 1920, ++ .max_preview_video_h = 1088, ++ .num_preview_video_frames = 3, ++ .stills_capture_circular_buffer_height = 0, ++ .fast_preview_resume = 0, ++ .use_stc_timestamp = MMAL_PARAM_TIMESTAMP_MODE_RAW_STC ++ }; ++ ++ ret = vchiq_mmal_port_parameter_set(instance, &camera->control, ++ MMAL_PARAMETER_CAMERA_CONFIG, ++ &cam_config, sizeof(cam_config)); ++ return ret; ++} ++ ++/* MMAL instance and component init */ ++static int __init mmal_init(struct bm2835_mmal_dev *dev) ++{ ++ int ret; ++ struct mmal_es_format *format; ++ ++ ret = vchiq_mmal_init(&dev->instance); ++ if (ret < 0) ++ return ret; ++ ++ /* get the camera component ready */ ++ ret = vchiq_mmal_component_init(dev->instance, "ril.camera", ++ &dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) ++ goto unreg_mmal; ++ ++ if (dev->component[MMAL_COMPONENT_CAMERA]->outputs < ++ MMAL_CAMERA_PORT_COUNT) { ++ ret = -EINVAL; ++ goto unreg_camera; ++ } ++ ++ ret = set_camera_parameters(dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ if (ret < 0) ++ goto unreg_camera; ++ ++ format = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW].format; ++ ++ format->encoding = MMAL_ENCODING_OPAQUE; ++ format->encoding_variant = MMAL_ENCODING_I420; ++ ++ format->es->video.width = 1024; ++ format->es->video.height = 768; ++ format->es->video.crop.x = 0; ++ format->es->video.crop.y = 0; ++ format->es->video.crop.width = 1024; ++ format->es->video.crop.height = 768; ++ format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; ++ format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; ++ ++ format = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO].format; ++ ++ format->encoding = MMAL_ENCODING_OPAQUE; ++ format->encoding_variant = MMAL_ENCODING_I420; ++ ++ format->es->video.width = 1024; ++ format->es->video.height = 768; ++ format->es->video.crop.x = 0; ++ format->es->video.crop.y = 0; ++ format->es->video.crop.width = 1024; ++ format->es->video.crop.height = 768; ++ format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; ++ format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; ++ ++ format = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE].format; ++ ++ format->encoding = MMAL_ENCODING_OPAQUE; ++ ++ format->es->video.width = 2592; ++ format->es->video.height = 1944; ++ format->es->video.crop.x = 0; ++ format->es->video.crop.y = 0; ++ format->es->video.crop.width = 2592; ++ format->es->video.crop.height = 1944; ++ format->es->video.frame_rate.num = 30; ++ format->es->video.frame_rate.den = 1; ++ ++ dev->capture.width = format->es->video.width; ++ dev->capture.height = format->es->video.height; ++ dev->capture.fmt = &formats[0]; ++ dev->capture.encode_component = NULL; ++ ++ /* get the preview component ready */ ++ ret = vchiq_mmal_component_init( ++ dev->instance, "ril.video_render", ++ &dev->component[MMAL_COMPONENT_PREVIEW]); ++ if (ret < 0) ++ goto unreg_camera; ++ ++ if (dev->component[MMAL_COMPONENT_PREVIEW]->inputs < 1) { ++ ret = -EINVAL; ++ pr_debug("too few input ports %d needed %d\n", ++ dev->component[MMAL_COMPONENT_PREVIEW]->inputs, 1); ++ goto unreg_preview; ++ } ++ ++ /* get the image encoder component ready */ ++ ret = vchiq_mmal_component_init( ++ dev->instance, "ril.image_encode", ++ &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]); ++ if (ret < 0) ++ goto unreg_preview; ++ ++ if (dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->inputs < 1) { ++ ret = -EINVAL; ++ v4l2_err(&dev->v4l2_dev, "too few input ports %d needed %d\n", ++ dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->inputs, ++ 1); ++ goto unreg_image_encoder; ++ } ++ ++ /* get the video encoder component ready */ ++ ret = vchiq_mmal_component_init(dev->instance, "ril.video_encode", ++ &dev-> ++ component[MMAL_COMPONENT_VIDEO_ENCODE]); ++ if (ret < 0) ++ goto unreg_image_encoder; ++ ++ if (dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->inputs < 1) { ++ ret = -EINVAL; ++ v4l2_err(&dev->v4l2_dev, "too few input ports %d needed %d\n", ++ dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->inputs, ++ 1); ++ goto unreg_vid_encoder; ++ } ++ ++ { ++ unsigned int enable = 1; ++ vchiq_mmal_port_parameter_set( ++ dev->instance, ++ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->control, ++ MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, ++ &enable, sizeof(enable)); ++ ++ vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->control, ++ MMAL_PARAMETER_MINIMISE_FRAGMENTATION, ++ &enable, ++ sizeof(enable)); ++ } ++ ret = bm2835_mmal_set_all_camera_controls(dev); ++ if (ret < 0) ++ goto unreg_vid_encoder; ++ ++ return 0; ++ ++unreg_vid_encoder: ++ pr_err("Cleanup: Destroy video encoder\n"); ++ vchiq_mmal_component_finalise( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_VIDEO_ENCODE]); ++ ++unreg_image_encoder: ++ pr_err("Cleanup: Destroy image encoder\n"); ++ vchiq_mmal_component_finalise( ++ dev->instance, ++ dev->component[MMAL_COMPONENT_IMAGE_ENCODE]); ++ ++unreg_preview: ++ pr_err("Cleanup: Destroy video render\n"); ++ vchiq_mmal_component_finalise(dev->instance, ++ dev->component[MMAL_COMPONENT_PREVIEW]); ++ ++unreg_camera: ++ pr_err("Cleanup: Destroy camera\n"); ++ vchiq_mmal_component_finalise(dev->instance, ++ dev->component[MMAL_COMPONENT_CAMERA]); ++ ++unreg_mmal: ++ vchiq_mmal_finalise(dev->instance); ++ return ret; ++} ++ ++static int __init bm2835_mmal_init_device(struct bm2835_mmal_dev *dev, ++ struct video_device *vfd) ++{ ++ int ret; ++ ++ *vfd = vdev_template; ++ ++ vfd->v4l2_dev = &dev->v4l2_dev; ++ ++ vfd->lock = &dev->mutex; ++ ++ vfd->queue = &dev->capture.vb_vidq; ++ ++ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); ++ ++ /* video device needs to be able to access instance data */ ++ video_set_drvdata(vfd, dev); ++ ++ ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); ++ if (ret < 0) ++ return ret; ++ ++ v4l2_info(vfd->v4l2_dev, "V4L2 device registered as %s\n", ++ video_device_node_name(vfd)); ++ ++ return 0; ++} ++ ++static struct v4l2_format default_v4l2_format = { ++ .fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG, ++ .fmt.pix.width = 1024, ++ .fmt.pix.bytesperline = 1024 * 3 / 2, ++ .fmt.pix.height = 768, ++ .fmt.pix.sizeimage = 1<<18, ++}; ++ ++static int __init bm2835_mmal_init(void) ++{ ++ int ret; ++ struct bm2835_mmal_dev *dev; ++ struct vb2_queue *q; ++ ++ dev = kzalloc(sizeof(*gdev), GFP_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ ++ /* setup device defaults */ ++ dev->overlay.w.left = 150; ++ dev->overlay.w.top = 50; ++ dev->overlay.w.width = 1024; ++ dev->overlay.w.height = 768; ++ dev->overlay.clipcount = 0; ++ dev->overlay.field = V4L2_FIELD_NONE; ++ ++ dev->capture.fmt = &formats[3]; /* JPEG */ ++ ++ /* v4l device registration */ ++ snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), ++ "%s", BM2835_MMAL_MODULE_NAME); ++ ret = v4l2_device_register(NULL, &dev->v4l2_dev); ++ if (ret) ++ goto free_dev; ++ ++ /* setup v4l controls */ ++ ret = bm2835_mmal_init_controls(dev, &dev->ctrl_handler); ++ if (ret < 0) ++ goto unreg_dev; ++ dev->v4l2_dev.ctrl_handler = &dev->ctrl_handler; ++ ++ /* mmal init */ ++ ret = mmal_init(dev); ++ if (ret < 0) ++ goto unreg_dev; ++ ++ /* initialize queue */ ++ q = &dev->capture.vb_vidq; ++ memset(q, 0, sizeof(*q)); ++ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ++ q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; ++ q->drv_priv = dev; ++ q->buf_struct_size = sizeof(struct mmal_buffer); ++ q->ops = &bm2835_mmal_video_qops; ++ q->mem_ops = &vb2_vmalloc_memops; ++ q->timestamp_type = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; ++ ret = vb2_queue_init(q); ++ if (ret < 0) ++ goto unreg_dev; ++ ++ /* v4l2 core mutex used to protect all fops and v4l2 ioctls. */ ++ mutex_init(&dev->mutex); ++ ++ /* initialise video devices */ ++ ret = bm2835_mmal_init_device(dev, &dev->vdev); ++ if (ret < 0) ++ goto unreg_dev; ++ ++ ret = mmal_setup_components(dev, &default_v4l2_format); ++ if (ret < 0) { ++ v4l2_err(&dev->v4l2_dev, ++ "%s: could not setup components\n", __func__); ++ goto unreg_dev; ++ } ++ ++ v4l2_info(&dev->v4l2_dev, ++ "Broadcom 2835 MMAL video capture ver %s loaded.\n", ++ BM2835_MMAL_VERSION); ++ ++ gdev = dev; ++ return 0; ++ ++unreg_dev: ++ v4l2_ctrl_handler_free(&dev->ctrl_handler); ++ v4l2_device_unregister(&dev->v4l2_dev); ++ ++free_dev: ++ kfree(dev); ++ ++ v4l2_err(&dev->v4l2_dev, ++ "%s: error %d while loading driver\n", ++ BM2835_MMAL_MODULE_NAME, ret); ++ ++ return ret; ++} ++ ++static void __exit bm2835_mmal_exit(void) ++{ ++ if (!gdev) ++ return; ++ ++ v4l2_info(&gdev->v4l2_dev, "unregistering %s\n", ++ video_device_node_name(&gdev->vdev)); ++ ++ video_unregister_device(&gdev->vdev); ++ ++ if (gdev->capture.encode_component) { ++ v4l2_dbg(1, bcm2835_v4l2_debug, &gdev->v4l2_dev, ++ "mmal_exit - disconnect tunnel\n"); ++ vchiq_mmal_port_connect_tunnel(gdev->instance, ++ gdev->capture.camera_port, NULL); ++ vchiq_mmal_component_disable(gdev->instance, ++ gdev->capture.encode_component); ++ } ++ vchiq_mmal_component_disable(gdev->instance, ++ gdev->component[MMAL_COMPONENT_CAMERA]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev-> ++ component[MMAL_COMPONENT_VIDEO_ENCODE]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev-> ++ component[MMAL_COMPONENT_IMAGE_ENCODE]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev->component[MMAL_COMPONENT_PREVIEW]); ++ ++ vchiq_mmal_component_finalise(gdev->instance, ++ gdev->component[MMAL_COMPONENT_CAMERA]); ++ ++ vchiq_mmal_finalise(gdev->instance); ++ ++ v4l2_ctrl_handler_free(&gdev->ctrl_handler); ++ ++ v4l2_device_unregister(&gdev->v4l2_dev); ++ ++ kfree(gdev); ++} ++ ++module_init(bm2835_mmal_init); ++module_exit(bm2835_mmal_exit); +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.h b/drivers/media/platform/bcm2835/bcm2835-camera.h +new file mode 100644 +index 0000000..883eab7 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.h +@@ -0,0 +1,113 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ * ++ * core driver device ++ */ ++ ++#define V4L2_CTRL_COUNT 18 /* number of v4l controls */ ++ ++enum { ++ MMAL_COMPONENT_CAMERA = 0, ++ MMAL_COMPONENT_PREVIEW, ++ MMAL_COMPONENT_IMAGE_ENCODE, ++ MMAL_COMPONENT_VIDEO_ENCODE, ++ MMAL_COMPONENT_COUNT ++}; ++ ++enum { ++ MMAL_CAMERA_PORT_PREVIEW = 0, ++ MMAL_CAMERA_PORT_VIDEO, ++ MMAL_CAMERA_PORT_CAPTURE, ++ MMAL_CAMERA_PORT_COUNT ++}; ++ ++#define PREVIEW_FRAME_RATE_NUM 30 ++#define PREVIEW_FRAME_RATE_DEN 1 ++ ++#define PREVIEW_LAYER 2 ++ ++extern int bcm2835_v4l2_debug; ++ ++struct bm2835_mmal_dev { ++ /* v4l2 devices */ ++ struct v4l2_device v4l2_dev; ++ struct video_device vdev; ++ struct mutex mutex; ++ ++ /* controls */ ++ struct v4l2_ctrl_handler ctrl_handler; ++ struct v4l2_ctrl *ctrls[V4L2_CTRL_COUNT]; ++ struct mmal_colourfx colourfx; ++ int hflip; ++ int vflip; ++ ++ /* allocated mmal instance and components */ ++ struct vchiq_mmal_instance *instance; ++ struct vchiq_mmal_component *component[MMAL_COMPONENT_COUNT]; ++ int camera_use_count; ++ ++ struct v4l2_window overlay; ++ ++ struct { ++ unsigned int width; /* width */ ++ unsigned int height; /* height */ ++ unsigned int stride; /* stride */ ++ struct mmal_fmt *fmt; ++ ++ /* H264 encode bitrate */ ++ int encode_bitrate; ++ /* H264 bitrate mode. CBR/VBR */ ++ int encode_bitrate_mode; ++ /* JPEG Q-factor */ ++ int q_factor; ++ ++ struct vb2_queue vb_vidq; ++ ++ /* VC start timestamp for streaming */ ++ s64 vc_start_timestamp; ++ /* Kernel start timestamp for streaming */ ++ struct timeval kernel_start_ts; ++ ++ struct vchiq_mmal_port *port; /* port being used for capture */ ++ /* camera port being used for capture */ ++ struct vchiq_mmal_port *camera_port; ++ /* component being used for encode */ ++ struct vchiq_mmal_component *encode_component; ++ /* number of frames remaining which driver should capture */ ++ unsigned int frame_count; ++ /* last frame completion */ ++ struct completion frame_cmplt; ++ ++ } capture; ++ ++}; ++ ++int bm2835_mmal_init_controls( ++ struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl_handler *hdl); ++ ++int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev); ++ ++ ++/* Debug helpers */ ++ ++#define v4l2_dump_pix_format(level, debug, dev, pix_fmt, desc) \ ++{ \ ++ v4l2_dbg(level, debug, dev, \ ++"%s: w %u h %u field %u pfmt 0x%x bpl %u sz_img %u colorspace 0x%x priv %u\n", \ ++ desc == NULL ? "" : desc, \ ++ (pix_fmt)->width, (pix_fmt)->height, (pix_fmt)->field, \ ++ (pix_fmt)->pixelformat, (pix_fmt)->bytesperline, \ ++ (pix_fmt)->sizeimage, (pix_fmt)->colorspace, (pix_fmt)->priv); \ ++} +diff --git a/drivers/media/platform/bcm2835/controls.c b/drivers/media/platform/bcm2835/controls.c +new file mode 100644 +index 0000000..d1408e5 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/controls.c +@@ -0,0 +1,725 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mmal-common.h" ++#include "mmal-vchiq.h" ++#include "mmal-parameters.h" ++#include "bcm2835-camera.h" ++ ++/* The supported V4L2_CID_AUTO_EXPOSURE_BIAS values are from -24 to +24. ++ * These are in 1/6th increments so the effective range is -4.0EV to +4.0EV. ++ */ ++static const s64 ev_bias_qmenu[] = { ++ -24, -21, -18, -15, -12, -9, -6, -3, 0, 3, 6, 9, 12, 15, 18, 21, 24 ++}; ++ ++/* Supported ISO values ++ * ISOO = auto ISO ++ */ ++static const s64 iso_qmenu[] = { ++ 0, 100, 200, 400, 800, ++}; ++ ++/* Supported video encode modes */ ++static const s64 bitrate_mode_qmenu[] = { ++ (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, ++ (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, ++}; ++ ++ ++enum bm2835_mmal_ctrl_type { ++ MMAL_CONTROL_TYPE_STD, ++ MMAL_CONTROL_TYPE_STD_MENU, ++ MMAL_CONTROL_TYPE_INT_MENU, ++ MMAL_CONTROL_TYPE_CLUSTER, /* special cluster entry */ ++}; ++ ++struct bm2835_mmal_v4l2_ctrl; ++ ++typedef int(bm2835_mmal_v4l2_ctrl_cb)( ++ struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl); ++ ++struct bm2835_mmal_v4l2_ctrl { ++ u32 id; /* v4l2 control identifier */ ++ enum bm2835_mmal_ctrl_type type; ++ /* control minimum value or ++ * mask for MMAL_CONTROL_TYPE_STD_MENU */ ++ s32 min; ++ s32 max; /* maximum value of control */ ++ s32 def; /* default value of control */ ++ s32 step; /* step size of the control */ ++ const s64 *imenu; /* integer menu array */ ++ u32 mmal_id; /* mmal parameter id */ ++ bm2835_mmal_v4l2_ctrl_cb *setter; ++}; ++ ++struct v4l2_to_mmal_effects_setting { ++ u32 v4l2_effect; ++ u32 mmal_effect; ++ s32 col_fx_enable; ++ s32 col_fx_fixed_cbcr; ++ u32 u; ++ u32 v; ++ u32 num_effect_params; ++ u32 effect_params[MMAL_MAX_IMAGEFX_PARAMETERS]; ++}; ++ ++static const struct v4l2_to_mmal_effects_setting ++ v4l2_to_mmal_effects_values[] = { ++ { V4L2_COLORFX_NONE, MMAL_PARAM_IMAGEFX_NONE, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_BW, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 0, 128, 128, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SEPIA, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 0, 87, 151, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_NEGATIVE, MMAL_PARAM_IMAGEFX_NEGATIVE, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_EMBOSS, MMAL_PARAM_IMAGEFX_EMBOSS, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SKETCH, MMAL_PARAM_IMAGEFX_SKETCH, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SKY_BLUE, MMAL_PARAM_IMAGEFX_PASTEL, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_GRASS_GREEN, MMAL_PARAM_IMAGEFX_WATERCOLOUR, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SKIN_WHITEN, MMAL_PARAM_IMAGEFX_WASHEDOUT, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_VIVID, MMAL_PARAM_IMAGEFX_SATURATION, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_AQUA, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 0, 171, 121, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_ART_FREEZE, MMAL_PARAM_IMAGEFX_HATCH, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SILHOUETTE, MMAL_PARAM_IMAGEFX_FILM, ++ 0, 0, 0, 0, 0, {0, 0, 0, 0, 0} }, ++ { V4L2_COLORFX_SOLARIZATION, MMAL_PARAM_IMAGEFX_SOLARIZE, ++ 0, 0, 0, 0, 5, {1, 128, 160, 160, 48} }, ++ { V4L2_COLORFX_ANTIQUE, MMAL_PARAM_IMAGEFX_COLOURBALANCE, ++ 0, 0, 0, 0, 3, {108, 274, 238, 0, 0} }, ++ { V4L2_COLORFX_SET_CBCR, MMAL_PARAM_IMAGEFX_NONE, ++ 1, 1, 0, 0, 0, {0, 0, 0, 0, 0} } ++}; ++ ++ ++/* control handlers*/ ++ ++static int ctrl_set_rational(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ struct { ++ s32 num; /**< Numerator */ ++ s32 den; /**< Denominator */ ++ } rational_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ rational_value.num = ctrl->val; ++ rational_value.den = 100; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &rational_value, ++ sizeof(rational_value)); ++} ++ ++static int ctrl_set_value(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ u32_value = ctrl->val; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_rotate(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret; ++ u32 u32_value; ++ struct vchiq_mmal_component *camera; ++ ++ camera = dev->component[MMAL_COMPONENT_CAMERA]; ++ ++ u32_value = ((ctrl->val % 360) / 90) * 90; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[0], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[1], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[2], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ ++ return ret; ++} ++ ++static int ctrl_set_flip(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret; ++ u32 u32_value; ++ struct vchiq_mmal_component *camera; ++ ++ if (ctrl->id == V4L2_CID_HFLIP) ++ dev->hflip = ctrl->val; ++ else ++ dev->vflip = ctrl->val; ++ ++ camera = dev->component[MMAL_COMPONENT_CAMERA]; ++ ++ if (dev->hflip && dev->vflip) ++ u32_value = MMAL_PARAM_MIRROR_BOTH; ++ else if (dev->hflip) ++ u32_value = MMAL_PARAM_MIRROR_HORIZONTAL; ++ else if (dev->vflip) ++ u32_value = MMAL_PARAM_MIRROR_VERTICAL; ++ else ++ u32_value = MMAL_PARAM_MIRROR_NONE; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[0], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[1], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ if (ret < 0) ++ return ret; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, &camera->output[2], ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++ ++ return ret; ++ ++} ++ ++static int ctrl_set_exposure(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ switch (ctrl->val) { ++ case V4L2_EXPOSURE_AUTO: ++ u32_value = MMAL_PARAM_EXPOSUREMODE_AUTO; ++ break; ++ ++ case V4L2_EXPOSURE_MANUAL: ++ u32_value = MMAL_PARAM_EXPOSUREMODE_OFF; ++ break; ++ ++ case V4L2_EXPOSURE_SHUTTER_PRIORITY: ++ u32_value = MMAL_PARAM_EXPOSUREMODE_SPORTS; ++ break; ++ ++ case V4L2_EXPOSURE_APERTURE_PRIORITY: ++ u32_value = MMAL_PARAM_EXPOSUREMODE_NIGHT; ++ break; ++ ++ } ++ ++ /* todo: what about the other ten modes there are MMAL parameters for */ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_metering_mode(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ switch (ctrl->val) { ++ case V4L2_EXPOSURE_METERING_AVERAGE: ++ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE; ++ break; ++ ++ case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: ++ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT; ++ break; ++ ++ case V4L2_EXPOSURE_METERING_SPOT: ++ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT; ++ break; ++ ++ /* todo matrix weighting not added to Linux API till 3.9 ++ case V4L2_EXPOSURE_METERING_MATRIX: ++ u32_value = MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX; ++ break; ++ */ ++ ++ } ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_awb_mode(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ switch (ctrl->val) { ++ case V4L2_WHITE_BALANCE_MANUAL: ++ u32_value = MMAL_PARAM_AWBMODE_OFF; ++ break; ++ ++ case V4L2_WHITE_BALANCE_AUTO: ++ u32_value = MMAL_PARAM_AWBMODE_AUTO; ++ break; ++ ++ case V4L2_WHITE_BALANCE_INCANDESCENT: ++ u32_value = MMAL_PARAM_AWBMODE_INCANDESCENT; ++ break; ++ ++ case V4L2_WHITE_BALANCE_FLUORESCENT: ++ u32_value = MMAL_PARAM_AWBMODE_FLUORESCENT; ++ break; ++ ++ case V4L2_WHITE_BALANCE_FLUORESCENT_H: ++ u32_value = MMAL_PARAM_AWBMODE_TUNGSTEN; ++ break; ++ ++ case V4L2_WHITE_BALANCE_HORIZON: ++ u32_value = MMAL_PARAM_AWBMODE_HORIZON; ++ break; ++ ++ case V4L2_WHITE_BALANCE_DAYLIGHT: ++ u32_value = MMAL_PARAM_AWBMODE_SUNLIGHT; ++ break; ++ ++ case V4L2_WHITE_BALANCE_FLASH: ++ u32_value = MMAL_PARAM_AWBMODE_FLASH; ++ break; ++ ++ case V4L2_WHITE_BALANCE_CLOUDY: ++ u32_value = MMAL_PARAM_AWBMODE_CLOUDY; ++ break; ++ ++ case V4L2_WHITE_BALANCE_SHADE: ++ u32_value = MMAL_PARAM_AWBMODE_SHADE; ++ break; ++ ++ } ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int ctrl_set_image_effect(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret = -EINVAL; ++ int i, j; ++ struct vchiq_mmal_port *control; ++ struct mmal_parameter_imagefx_parameters imagefx; ++ ++ for (i = 0; i < ARRAY_SIZE(v4l2_to_mmal_effects_values); i++) { ++ if (ctrl->val == v4l2_to_mmal_effects_values[i].v4l2_effect) { ++ ++ imagefx.effect = ++ v4l2_to_mmal_effects_values[i].mmal_effect; ++ imagefx.num_effect_params = ++ v4l2_to_mmal_effects_values[i].num_effect_params; ++ ++ if (imagefx.num_effect_params > MMAL_MAX_IMAGEFX_PARAMETERS) ++ imagefx.num_effect_params = MMAL_MAX_IMAGEFX_PARAMETERS; ++ ++ for (j = 0; j < imagefx.num_effect_params; j++) ++ imagefx.effect_parameter[j] = ++ v4l2_to_mmal_effects_values[i].effect_params[j]; ++ ++ dev->colourfx.enable = ++ v4l2_to_mmal_effects_values[i].col_fx_enable; ++ if (!v4l2_to_mmal_effects_values[i].col_fx_fixed_cbcr) { ++ dev->colourfx.u = ++ v4l2_to_mmal_effects_values[i].u; ++ dev->colourfx.v = ++ v4l2_to_mmal_effects_values[i].v; ++ } ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ ret = vchiq_mmal_port_parameter_set( ++ dev->instance, control, ++ MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, ++ &imagefx, sizeof(imagefx)); ++ if (ret) ++ goto exit; ++ ++ ret = vchiq_mmal_port_parameter_set( ++ dev->instance, control, ++ MMAL_PARAMETER_COLOUR_EFFECT, ++ &dev->colourfx, sizeof(dev->colourfx)); ++ } ++ } ++ ++exit: ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "mmal_ctrl:%p ctrl id:0x%x ctrl val:%d imagefx:0x%x color_effect:%s u:%d v:%d ret %d(%d)\n", ++ mmal_ctrl, ctrl->id, ctrl->val, imagefx.effect, ++ dev->colourfx.enable ? "true" : "false", ++ dev->colourfx.u, dev->colourfx.v, ++ ret, (ret == 0 ? 0 : -EINVAL)); ++ return (ret == 0 ? 0 : EINVAL); ++} ++ ++static int ctrl_set_colfx(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret = -EINVAL; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ dev->colourfx.enable = (ctrl->val & 0xff00) >> 8; ++ dev->colourfx.enable = ctrl->val & 0xff; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, control, ++ MMAL_PARAMETER_COLOUR_EFFECT, ++ &dev->colourfx, sizeof(dev->colourfx)); ++ ++ v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "After: mmal_ctrl:%p ctrl id:0x%x ctrl val:%d ret %d(%d)\n", ++ mmal_ctrl, ctrl->id, ctrl->val, ret, ++ (ret == 0 ? 0 : -EINVAL)); ++ return (ret == 0 ? 0 : EINVAL); ++} ++ ++static int ctrl_set_bitrate(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ int ret; ++ struct vchiq_mmal_port *encoder_out; ++ ++ dev->capture.encode_bitrate = ctrl->val; ++ ++ encoder_out = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, encoder_out, ++ mmal_ctrl->mmal_id, ++ &ctrl->val, sizeof(ctrl->val)); ++ ret = 0; ++ return ret; ++} ++ ++static int ctrl_set_bitrate_mode(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 bitrate_mode; ++ struct vchiq_mmal_port *encoder_out; ++ ++ encoder_out = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ ++ dev->capture.encode_bitrate_mode = ctrl->val; ++ switch (ctrl->val) { ++ default: ++ case V4L2_MPEG_VIDEO_BITRATE_MODE_VBR: ++ bitrate_mode = MMAL_VIDEO_RATECONTROL_VARIABLE; ++ break; ++ case V4L2_MPEG_VIDEO_BITRATE_MODE_CBR: ++ bitrate_mode = MMAL_VIDEO_RATECONTROL_CONSTANT; ++ break; ++ } ++ ++ vchiq_mmal_port_parameter_set(dev->instance, encoder_out, ++ mmal_ctrl->mmal_id, ++ &bitrate_mode, ++ sizeof(bitrate_mode)); ++ return 0; ++} ++ ++static int ctrl_set_q_factor(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *jpeg_out; ++ ++ jpeg_out = &dev->component[MMAL_COMPONENT_IMAGE_ENCODE]->output[0]; ++ ++ u32_value = ctrl->val; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, jpeg_out, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ ++static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) ++{ ++ struct bm2835_mmal_dev *dev = ++ container_of(ctrl->handler, struct bm2835_mmal_dev, ++ ctrl_handler); ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl = ctrl->priv; ++ ++ if ((mmal_ctrl == NULL) || ++ (mmal_ctrl->id != ctrl->id) || ++ (mmal_ctrl->setter == NULL)) { ++ pr_warn("mmal_ctrl:%p ctrl id:%d\n", mmal_ctrl, ctrl->id); ++ return -EINVAL; ++ } ++ ++ return mmal_ctrl->setter(dev, ctrl, mmal_ctrl); ++} ++ ++static const struct v4l2_ctrl_ops bm2835_mmal_ctrl_ops = { ++ .s_ctrl = bm2835_mmal_s_ctrl, ++}; ++ ++ ++ ++static const struct bm2835_mmal_v4l2_ctrl v4l2_ctrls[V4L2_CTRL_COUNT] = { ++ { ++ V4L2_CID_SATURATION, MMAL_CONTROL_TYPE_STD, ++ -100, 100, 0, 1, NULL, ++ MMAL_PARAMETER_SATURATION, &ctrl_set_rational ++ }, ++ { ++ V4L2_CID_SHARPNESS, MMAL_CONTROL_TYPE_STD, ++ -100, 100, 0, 1, NULL, ++ MMAL_PARAMETER_SHARPNESS, &ctrl_set_rational ++ }, ++ { ++ V4L2_CID_CONTRAST, MMAL_CONTROL_TYPE_STD, ++ -100, 100, 0, 1, NULL, ++ MMAL_PARAMETER_CONTRAST, &ctrl_set_rational ++ }, ++ { ++ V4L2_CID_BRIGHTNESS, MMAL_CONTROL_TYPE_STD, ++ 0, 100, 50, 1, NULL, ++ MMAL_PARAMETER_BRIGHTNESS, &ctrl_set_rational ++ }, ++ { ++ V4L2_CID_ISO_SENSITIVITY, MMAL_CONTROL_TYPE_INT_MENU, ++ 0, ARRAY_SIZE(iso_qmenu) - 1, 0, 1, iso_qmenu, ++ MMAL_PARAMETER_ISO, &ctrl_set_value ++ }, ++ { ++ V4L2_CID_IMAGE_STABILIZATION, MMAL_CONTROL_TYPE_STD, ++ 0, 1, 0, 1, NULL, ++ MMAL_PARAMETER_VIDEO_STABILISATION, &ctrl_set_value ++ }, ++/* { ++ 0, MMAL_CONTROL_TYPE_CLUSTER, 3, 1, 0, NULL, 0, NULL ++ }, ++*/ { ++ V4L2_CID_EXPOSURE_AUTO, MMAL_CONTROL_TYPE_STD_MENU, ++ ~0x03, 3, V4L2_EXPOSURE_AUTO, 0, NULL, ++ MMAL_PARAMETER_EXPOSURE_MODE, &ctrl_set_exposure ++ }, ++/* todo this needs mixing in with set exposure ++ { ++ V4L2_CID_SCENE_MODE, MMAL_CONTROL_TYPE_STD_MENU, ++ }, ++ */ ++ { ++ V4L2_CID_AUTO_EXPOSURE_BIAS, MMAL_CONTROL_TYPE_INT_MENU, ++ 0, ARRAY_SIZE(ev_bias_qmenu) - 1, ++ (ARRAY_SIZE(ev_bias_qmenu)+1)/2 - 1, 0, ev_bias_qmenu, ++ MMAL_PARAMETER_EXPOSURE_COMP, &ctrl_set_value ++ }, ++ { ++ V4L2_CID_EXPOSURE_METERING, ++ MMAL_CONTROL_TYPE_STD_MENU, ++ ~0x7, 2, V4L2_EXPOSURE_METERING_AVERAGE, 0, NULL, ++ MMAL_PARAMETER_EXP_METERING_MODE, &ctrl_set_metering_mode ++ }, ++ { ++ V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, ++ MMAL_CONTROL_TYPE_STD_MENU, ++ ~0x3fe, 9, V4L2_WHITE_BALANCE_AUTO, 0, NULL, ++ MMAL_PARAMETER_AWB_MODE, &ctrl_set_awb_mode ++ }, ++ { ++ V4L2_CID_COLORFX, MMAL_CONTROL_TYPE_STD_MENU, ++ 0, 15, V4L2_COLORFX_NONE, 0, NULL, ++ MMAL_PARAMETER_IMAGE_EFFECT, &ctrl_set_image_effect ++ }, ++ { ++ V4L2_CID_COLORFX_CBCR, MMAL_CONTROL_TYPE_STD, ++ 0, 0xffff, 0x8080, 1, NULL, ++ MMAL_PARAMETER_COLOUR_EFFECT, &ctrl_set_colfx ++ }, ++ { ++ V4L2_CID_ROTATE, MMAL_CONTROL_TYPE_STD, ++ 0, 360, 0, 90, NULL, ++ MMAL_PARAMETER_ROTATION, &ctrl_set_rotate ++ }, ++ { ++ V4L2_CID_HFLIP, MMAL_CONTROL_TYPE_STD, ++ 0, 1, 0, 1, NULL, ++ MMAL_PARAMETER_MIRROR, &ctrl_set_flip ++ }, ++ { ++ V4L2_CID_VFLIP, MMAL_CONTROL_TYPE_STD, ++ 0, 1, 0, 1, NULL, ++ MMAL_PARAMETER_MIRROR, &ctrl_set_flip ++ }, ++ { ++ V4L2_CID_MPEG_VIDEO_BITRATE_MODE, MMAL_CONTROL_TYPE_STD_MENU, ++ 0, ARRAY_SIZE(bitrate_mode_qmenu) - 1, ++ 0, 0, bitrate_mode_qmenu, ++ MMAL_PARAMETER_RATECONTROL, &ctrl_set_bitrate_mode ++ }, ++ { ++ V4L2_CID_MPEG_VIDEO_BITRATE, MMAL_CONTROL_TYPE_STD, ++ 25*1000, 25*1000*1000, 10*1000*1000, 25*1000, NULL, ++ MMAL_PARAMETER_VIDEO_BIT_RATE, &ctrl_set_bitrate ++ }, ++ { ++ V4L2_CID_JPEG_COMPRESSION_QUALITY, MMAL_CONTROL_TYPE_STD, ++ 0, 100, ++ 30, 1, NULL, ++ MMAL_PARAMETER_JPEG_Q_FACTOR, &ctrl_set_q_factor ++ }, ++}; ++ ++int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev) ++{ ++ int c; ++ int ret; ++ ++ for (c = 0; c < V4L2_CTRL_COUNT; c++) { ++ if ((dev->ctrls[c]) && (v4l2_ctrls[c].setter)) { ++ ret = v4l2_ctrls[c].setter(dev, dev->ctrls[c], ++ &v4l2_ctrls[c]); ++ if (ret) ++ break; ++ } ++ } ++ return ret; ++} ++ ++int bm2835_mmal_init_controls(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl_handler *hdl) ++{ ++ int c; ++ const struct bm2835_mmal_v4l2_ctrl *ctrl; ++ ++ v4l2_ctrl_handler_init(hdl, V4L2_CTRL_COUNT); ++ ++ for (c = 0; c < V4L2_CTRL_COUNT; c++) { ++ ctrl = &v4l2_ctrls[c]; ++ ++ switch (ctrl->type) { ++ case MMAL_CONTROL_TYPE_STD: ++ dev->ctrls[c] = v4l2_ctrl_new_std(hdl, ++ &bm2835_mmal_ctrl_ops, ctrl->id, ++ ctrl->min, ctrl->max, ctrl->step, ctrl->def); ++ break; ++ ++ case MMAL_CONTROL_TYPE_STD_MENU: ++ dev->ctrls[c] = v4l2_ctrl_new_std_menu(hdl, ++ &bm2835_mmal_ctrl_ops, ctrl->id, ++ ctrl->max, ctrl->min, ctrl->def); ++ break; ++ ++ case MMAL_CONTROL_TYPE_INT_MENU: ++ dev->ctrls[c] = v4l2_ctrl_new_int_menu(hdl, ++ &bm2835_mmal_ctrl_ops, ctrl->id, ++ ctrl->max, ctrl->def, ctrl->imenu); ++ break; ++ ++ case MMAL_CONTROL_TYPE_CLUSTER: ++ /* skip this entry when constructing controls */ ++ continue; ++ } ++ ++ if (hdl->error) ++ break; ++ ++ dev->ctrls[c]->priv = (void *)ctrl; ++ } ++ ++ if (hdl->error) { ++ pr_err("error adding control %d/%d id 0x%x\n", c, ++ V4L2_CTRL_COUNT, ctrl->id); ++ return hdl->error; ++ } ++ ++ for (c = 0; c < V4L2_CTRL_COUNT; c++) { ++ ctrl = &v4l2_ctrls[c]; ++ ++ switch (ctrl->type) { ++ case MMAL_CONTROL_TYPE_CLUSTER: ++ v4l2_ctrl_auto_cluster(ctrl->min, ++ &dev->ctrls[c+1], ++ ctrl->max, ++ ctrl->def); ++ break; ++ ++ case MMAL_CONTROL_TYPE_STD: ++ case MMAL_CONTROL_TYPE_STD_MENU: ++ case MMAL_CONTROL_TYPE_INT_MENU: ++ break; ++ } ++ ++ } ++ ++ return 0; ++} +diff --git a/drivers/media/platform/bcm2835/mmal-common.h b/drivers/media/platform/bcm2835/mmal-common.h +new file mode 100644 +index 0000000..602b4a7 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-common.h +@@ -0,0 +1,52 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ * ++ * MMAL structures ++ * ++ */ ++ ++#define MMAL_FOURCC(a, b, c, d) ((a) | (b << 8) | (c << 16) | (d << 24)) ++#define MMAL_MAGIC MMAL_FOURCC('m', 'm', 'a', 'l') ++ ++/** Special value signalling that time is not known */ ++#define MMAL_TIME_UNKNOWN (1LL<<63) ++ ++/* mapping between v4l and mmal video modes */ ++struct mmal_fmt { ++ char *name; ++ u32 fourcc; /* v4l2 format id */ ++ u32 mmal; ++ int depth; ++ u32 mmal_component; /* MMAL component index to be used to encode */ ++}; ++ ++/* buffer for one video frame */ ++struct mmal_buffer { ++ /* v4l buffer data -- must be first */ ++ struct vb2_buffer vb; ++ ++ /* list of buffers available */ ++ struct list_head list; ++ ++ void *buffer; /* buffer pointer */ ++ unsigned long buffer_size; /* size of allocated buffer */ ++}; ++ ++/* */ ++struct mmal_colourfx { ++ s32 enable; ++ u32 u; ++ u32 v; ++}; ++ +diff --git a/drivers/media/platform/bcm2835/mmal-encodings.h b/drivers/media/platform/bcm2835/mmal-encodings.h +new file mode 100644 +index 0000000..856e80e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-encodings.h +@@ -0,0 +1,93 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++#define MMAL_ENCODING_H264 MMAL_FOURCC('H', '2', '6', '4') ++#define MMAL_ENCODING_H263 MMAL_FOURCC('H', '2', '6', '3') ++#define MMAL_ENCODING_MP4V MMAL_FOURCC('M', 'P', '4', 'V') ++#define MMAL_ENCODING_MP2V MMAL_FOURCC('M', 'P', '2', 'V') ++#define MMAL_ENCODING_MP1V MMAL_FOURCC('M', 'P', '1', 'V') ++#define MMAL_ENCODING_WMV3 MMAL_FOURCC('W', 'M', 'V', '3') ++#define MMAL_ENCODING_WMV2 MMAL_FOURCC('W', 'M', 'V', '2') ++#define MMAL_ENCODING_WMV1 MMAL_FOURCC('W', 'M', 'V', '1') ++#define MMAL_ENCODING_WVC1 MMAL_FOURCC('W', 'V', 'C', '1') ++#define MMAL_ENCODING_VP8 MMAL_FOURCC('V', 'P', '8', ' ') ++#define MMAL_ENCODING_VP7 MMAL_FOURCC('V', 'P', '7', ' ') ++#define MMAL_ENCODING_VP6 MMAL_FOURCC('V', 'P', '6', ' ') ++#define MMAL_ENCODING_THEORA MMAL_FOURCC('T', 'H', 'E', 'O') ++#define MMAL_ENCODING_SPARK MMAL_FOURCC('S', 'P', 'R', 'K') ++ ++#define MMAL_ENCODING_JPEG MMAL_FOURCC('J', 'P', 'E', 'G') ++#define MMAL_ENCODING_GIF MMAL_FOURCC('G', 'I', 'F', ' ') ++#define MMAL_ENCODING_PNG MMAL_FOURCC('P', 'N', 'G', ' ') ++#define MMAL_ENCODING_PPM MMAL_FOURCC('P', 'P', 'M', ' ') ++#define MMAL_ENCODING_TGA MMAL_FOURCC('T', 'G', 'A', ' ') ++#define MMAL_ENCODING_BMP MMAL_FOURCC('B', 'M', 'P', ' ') ++ ++#define MMAL_ENCODING_I420 MMAL_FOURCC('I', '4', '2', '0') ++#define MMAL_ENCODING_I420_SLICE MMAL_FOURCC('S', '4', '2', '0') ++#define MMAL_ENCODING_YV12 MMAL_FOURCC('Y', 'V', '1', '2') ++#define MMAL_ENCODING_I422 MMAL_FOURCC('I', '4', '2', '2') ++#define MMAL_ENCODING_I422_SLICE MMAL_FOURCC('S', '4', '2', '2') ++#define MMAL_ENCODING_YUYV MMAL_FOURCC('Y', 'U', 'Y', 'V') ++#define MMAL_ENCODING_YVYU MMAL_FOURCC('Y', 'V', 'Y', 'U') ++#define MMAL_ENCODING_UYVY MMAL_FOURCC('U', 'Y', 'V', 'Y') ++#define MMAL_ENCODING_VYUY MMAL_FOURCC('V', 'Y', 'U', 'Y') ++#define MMAL_ENCODING_NV12 MMAL_FOURCC('N', 'V', '1', '2') ++#define MMAL_ENCODING_NV21 MMAL_FOURCC('N', 'V', '2', '1') ++#define MMAL_ENCODING_ARGB MMAL_FOURCC('A', 'R', 'G', 'B') ++#define MMAL_ENCODING_RGBA MMAL_FOURCC('R', 'G', 'B', 'A') ++#define MMAL_ENCODING_ABGR MMAL_FOURCC('A', 'B', 'G', 'R') ++#define MMAL_ENCODING_BGRA MMAL_FOURCC('B', 'G', 'R', 'A') ++#define MMAL_ENCODING_RGB16 MMAL_FOURCC('R', 'G', 'B', '2') ++#define MMAL_ENCODING_RGB24 MMAL_FOURCC('R', 'G', 'B', '3') ++#define MMAL_ENCODING_RGB32 MMAL_FOURCC('R', 'G', 'B', '4') ++#define MMAL_ENCODING_BGR16 MMAL_FOURCC('B', 'G', 'R', '2') ++#define MMAL_ENCODING_BGR24 MMAL_FOURCC('B', 'G', 'R', '3') ++#define MMAL_ENCODING_BGR32 MMAL_FOURCC('B', 'G', 'R', '4') ++ ++/** SAND Video (YUVUV128) format, native format understood by VideoCore. ++ * This format is *not* opaque - if requested you will receive full frames ++ * of YUV_UV video. ++ */ ++#define MMAL_ENCODING_YUVUV128 MMAL_FOURCC('S', 'A', 'N', 'D') ++ ++/** VideoCore opaque image format, image handles are returned to ++ * the host but not the actual image data. ++ */ ++#define MMAL_ENCODING_OPAQUE MMAL_FOURCC('O', 'P', 'Q', 'V') ++ ++/** An EGL image handle ++ */ ++#define MMAL_ENCODING_EGL_IMAGE MMAL_FOURCC('E', 'G', 'L', 'I') ++ ++/* }@ */ ++ ++/** \name Pre-defined audio encodings */ ++/* @{ */ ++#define MMAL_ENCODING_PCM_UNSIGNED_BE MMAL_FOURCC('P', 'C', 'M', 'U') ++#define MMAL_ENCODING_PCM_UNSIGNED_LE MMAL_FOURCC('p', 'c', 'm', 'u') ++#define MMAL_ENCODING_PCM_SIGNED_BE MMAL_FOURCC('P', 'C', 'M', 'S') ++#define MMAL_ENCODING_PCM_SIGNED_LE MMAL_FOURCC('p', 'c', 'm', 's') ++#define MMAL_ENCODING_PCM_FLOAT_BE MMAL_FOURCC('P', 'C', 'M', 'F') ++#define MMAL_ENCODING_PCM_FLOAT_LE MMAL_FOURCC('p', 'c', 'm', 'f') ++ ++/* Pre-defined H264 encoding variants */ ++ ++/** ISO 14496-10 Annex B byte stream format */ ++#define MMAL_ENCODING_VARIANT_H264_DEFAULT 0 ++/** ISO 14496-15 AVC stream format */ ++#define MMAL_ENCODING_VARIANT_H264_AVC1 MMAL_FOURCC('A', 'V', 'C', '1') ++/** Implicitly delineated NAL units without emulation prevention */ ++#define MMAL_ENCODING_VARIANT_H264_RAW MMAL_FOURCC('R', 'A', 'W', ' ') +diff --git a/drivers/media/platform/bcm2835/mmal-msg-common.h b/drivers/media/platform/bcm2835/mmal-msg-common.h +new file mode 100644 +index 0000000..66e8a6e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg-common.h +@@ -0,0 +1,50 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++#ifndef MMAL_MSG_COMMON_H ++#define MMAL_MSG_COMMON_H ++ ++enum mmal_msg_status { ++ MMAL_MSG_STATUS_SUCCESS = 0, /**< Success */ ++ MMAL_MSG_STATUS_ENOMEM, /**< Out of memory */ ++ MMAL_MSG_STATUS_ENOSPC, /**< Out of resources other than memory */ ++ MMAL_MSG_STATUS_EINVAL, /**< Argument is invalid */ ++ MMAL_MSG_STATUS_ENOSYS, /**< Function not implemented */ ++ MMAL_MSG_STATUS_ENOENT, /**< No such file or directory */ ++ MMAL_MSG_STATUS_ENXIO, /**< No such device or address */ ++ MMAL_MSG_STATUS_EIO, /**< I/O error */ ++ MMAL_MSG_STATUS_ESPIPE, /**< Illegal seek */ ++ MMAL_MSG_STATUS_ECORRUPT, /**< Data is corrupt \attention */ ++ MMAL_MSG_STATUS_ENOTREADY, /**< Component is not ready */ ++ MMAL_MSG_STATUS_ECONFIG, /**< Component is not configured */ ++ MMAL_MSG_STATUS_EISCONN, /**< Port is already connected */ ++ MMAL_MSG_STATUS_ENOTCONN, /**< Port is disconnected */ ++ MMAL_MSG_STATUS_EAGAIN, /**< Resource temporarily unavailable. */ ++ MMAL_MSG_STATUS_EFAULT, /**< Bad address */ ++}; ++ ++struct mmal_rect { ++ s32 x; /**< x coordinate (from left) */ ++ s32 y; /**< y coordinate (from top) */ ++ s32 width; /**< width */ ++ s32 height; /**< height */ ++}; ++ ++struct mmal_rational { ++ s32 num; /**< Numerator */ ++ s32 den; /**< Denominator */ ++}; ++ ++#endif /* MMAL_MSG_COMMON_H */ +diff --git a/drivers/media/platform/bcm2835/mmal-msg-format.h b/drivers/media/platform/bcm2835/mmal-msg-format.h +new file mode 100644 +index 0000000..123d86e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg-format.h +@@ -0,0 +1,81 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++#ifndef MMAL_MSG_FORMAT_H ++#define MMAL_MSG_FORMAT_H ++ ++#include "mmal-msg-common.h" ++ ++/* MMAL_ES_FORMAT_T */ ++ ++ ++struct mmal_audio_format { ++ u32 channels; /**< Number of audio channels */ ++ u32 sample_rate; /**< Sample rate */ ++ ++ u32 bits_per_sample; /**< Bits per sample */ ++ u32 block_align; /**< Size of a block of data */ ++}; ++ ++struct mmal_video_format { ++ u32 width; /**< Width of frame in pixels */ ++ u32 height; /**< Height of frame in rows of pixels */ ++ struct mmal_rect crop; /**< Visible region of the frame */ ++ struct mmal_rational frame_rate; /**< Frame rate */ ++ struct mmal_rational par; /**< Pixel aspect ratio */ ++ ++ /* FourCC specifying the color space of the video stream. See the ++ * \ref MmalColorSpace "pre-defined color spaces" for some examples. ++ */ ++ u32 color_space; ++}; ++ ++struct mmal_subpicture_format { ++ u32 x_offset; ++ u32 y_offset; ++}; ++ ++union mmal_es_specific_format { ++ struct mmal_audio_format audio; ++ struct mmal_video_format video; ++ struct mmal_subpicture_format subpicture; ++}; ++ ++/** Definition of an elementary stream format (MMAL_ES_FORMAT_T) */ ++struct mmal_es_format { ++ u32 type; /* enum mmal_es_type */ ++ ++ u32 encoding; /* FourCC specifying encoding of the elementary stream.*/ ++ u32 encoding_variant; /* FourCC specifying the specific ++ * encoding variant of the elementary ++ * stream. ++ */ ++ ++ union mmal_es_specific_format *es; /* TODO: pointers in ++ * message serialisation?!? ++ */ ++ /* Type specific ++ * information for the ++ * elementary stream ++ */ ++ ++ u32 bitrate; /**< Bitrate in bits per second */ ++ u32 flags; /**< Flags describing properties of the elementary stream. */ ++ ++ u32 extradata_size; /**< Size of the codec specific data */ ++ u8 *extradata; /**< Codec specific data */ ++}; ++ ++#endif /* MMAL_MSG_FORMAT_H */ +diff --git a/drivers/media/platform/bcm2835/mmal-msg-port.h b/drivers/media/platform/bcm2835/mmal-msg-port.h +new file mode 100644 +index 0000000..a55c1ea +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg-port.h +@@ -0,0 +1,107 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++/* MMAL_PORT_TYPE_T */ ++enum mmal_port_type { ++ MMAL_PORT_TYPE_UNKNOWN = 0, /**< Unknown port type */ ++ MMAL_PORT_TYPE_CONTROL, /**< Control port */ ++ MMAL_PORT_TYPE_INPUT, /**< Input port */ ++ MMAL_PORT_TYPE_OUTPUT, /**< Output port */ ++ MMAL_PORT_TYPE_CLOCK, /**< Clock port */ ++}; ++ ++/** The port is pass-through and doesn't need buffer headers allocated */ ++#define MMAL_PORT_CAPABILITY_PASSTHROUGH 0x01 ++/** The port wants to allocate the buffer payloads. ++ * This signals a preference that payload allocation should be done ++ * on this port for efficiency reasons. */ ++#define MMAL_PORT_CAPABILITY_ALLOCATION 0x02 ++/** The port supports format change events. ++ * This applies to input ports and is used to let the client know ++ * whether the port supports being reconfigured via a format ++ * change event (i.e. without having to disable the port). */ ++#define MMAL_PORT_CAPABILITY_SUPPORTS_EVENT_FORMAT_CHANGE 0x04 ++ ++/* mmal port structure (MMAL_PORT_T) ++ * ++ * most elements are informational only, the pointer values for ++ * interogation messages are generally provided as additional ++ * strucures within the message. When used to set values only teh ++ * buffer_num, buffer_size and userdata parameters are writable. ++ */ ++struct mmal_port { ++ void *priv; /* Private member used by the framework */ ++ const char *name; /* Port name. Used for debugging purposes (RO) */ ++ ++ u32 type; /* Type of the port (RO) enum mmal_port_type */ ++ u16 index; /* Index of the port in its type list (RO) */ ++ u16 index_all; /* Index of the port in the list of all ports (RO) */ ++ ++ u32 is_enabled; /* Indicates whether the port is enabled or not (RO) */ ++ struct mmal_es_format *format; /* Format of the elementary stream */ ++ ++ u32 buffer_num_min; /* Minimum number of buffers the port ++ * requires (RO). This is set by the ++ * component. ++ */ ++ ++ u32 buffer_size_min; /* Minimum size of buffers the port ++ * requires (RO). This is set by the ++ * component. ++ */ ++ ++ u32 buffer_alignment_min; /* Minimum alignment requirement for ++ * the buffers (RO). A value of ++ * zero means no special alignment ++ * requirements. This is set by the ++ * component. ++ */ ++ ++ u32 buffer_num_recommended; /* Number of buffers the port ++ * recommends for optimal ++ * performance (RO). A value of ++ * zero means no special ++ * recommendation. This is set ++ * by the component. ++ */ ++ ++ u32 buffer_size_recommended; /* Size of buffers the port ++ * recommends for optimal ++ * performance (RO). A value of ++ * zero means no special ++ * recommendation. This is set ++ * by the component. ++ */ ++ ++ u32 buffer_num; /* Actual number of buffers the port will use. ++ * This is set by the client. ++ */ ++ ++ u32 buffer_size; /* Actual maximum size of the buffers that ++ * will be sent to the port. This is set by ++ * the client. ++ */ ++ ++ void *component; /* Component this port belongs to (Read Only) */ ++ ++ void *userdata; /* Field reserved for use by the client */ ++ ++ u32 capabilities; /* Flags describing the capabilities of a ++ * port (RO). Bitwise combination of \ref ++ * portcapabilities "Port capabilities" ++ * values. ++ */ ++ ++}; +diff --git a/drivers/media/platform/bcm2835/mmal-msg.h b/drivers/media/platform/bcm2835/mmal-msg.h +new file mode 100644 +index 0000000..67b1076 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-msg.h +@@ -0,0 +1,404 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++/* all the data structures which serialise the MMAL protocol. note ++ * these are directly mapped onto the recived message data. ++ * ++ * BEWARE: They seem to *assume* pointers are u32 and that there is no ++ * structure padding! ++ * ++ * NOTE: this implementation uses kernel types to ensure sizes. Rather ++ * than assigning values to enums to force their size the ++ * implementation uses fixed size types and not the enums (though the ++ * comments have the actual enum type ++ */ ++ ++#define VC_MMAL_VER 15 ++#define VC_MMAL_MIN_VER 10 ++#define VC_MMAL_SERVER_NAME MAKE_FOURCC("mmal") ++ ++/* max total message size is 512 bytes */ ++#define MMAL_MSG_MAX_SIZE 512 ++/* with six 32bit header elements max payload is therefore 488 bytes */ ++#define MMAL_MSG_MAX_PAYLOAD 488 ++ ++#include "mmal-msg-common.h" ++#include "mmal-msg-format.h" ++#include "mmal-msg-port.h" ++ ++enum mmal_msg_type { ++ MMAL_MSG_TYPE_QUIT = 1, ++ MMAL_MSG_TYPE_SERVICE_CLOSED, ++ MMAL_MSG_TYPE_GET_VERSION, ++ MMAL_MSG_TYPE_COMPONENT_CREATE, ++ MMAL_MSG_TYPE_COMPONENT_DESTROY, /* 5 */ ++ MMAL_MSG_TYPE_COMPONENT_ENABLE, ++ MMAL_MSG_TYPE_COMPONENT_DISABLE, ++ MMAL_MSG_TYPE_PORT_INFO_GET, ++ MMAL_MSG_TYPE_PORT_INFO_SET, ++ MMAL_MSG_TYPE_PORT_ACTION, /* 10 */ ++ MMAL_MSG_TYPE_BUFFER_FROM_HOST, ++ MMAL_MSG_TYPE_BUFFER_TO_HOST, ++ MMAL_MSG_TYPE_GET_STATS, ++ MMAL_MSG_TYPE_PORT_PARAMETER_SET, ++ MMAL_MSG_TYPE_PORT_PARAMETER_GET, /* 15 */ ++ MMAL_MSG_TYPE_EVENT_TO_HOST, ++ MMAL_MSG_TYPE_GET_CORE_STATS_FOR_PORT, ++ MMAL_MSG_TYPE_OPAQUE_ALLOCATOR, ++ MMAL_MSG_TYPE_CONSUME_MEM, ++ MMAL_MSG_TYPE_LMK, /* 20 */ ++ MMAL_MSG_TYPE_OPAQUE_ALLOCATOR_DESC, ++ MMAL_MSG_TYPE_DRM_GET_LHS32, ++ MMAL_MSG_TYPE_DRM_GET_TIME, ++ MMAL_MSG_TYPE_BUFFER_FROM_HOST_ZEROLEN, ++ MMAL_MSG_TYPE_PORT_FLUSH, /* 25 */ ++ MMAL_MSG_TYPE_HOST_LOG, ++ MMAL_MSG_TYPE_MSG_LAST ++}; ++ ++/* port action request messages differ depending on the action type */ ++enum mmal_msg_port_action_type { ++ MMAL_MSG_PORT_ACTION_TYPE_UNKNOWN = 0, /* Unkown action */ ++ MMAL_MSG_PORT_ACTION_TYPE_ENABLE, /* Enable a port */ ++ MMAL_MSG_PORT_ACTION_TYPE_DISABLE, /* Disable a port */ ++ MMAL_MSG_PORT_ACTION_TYPE_FLUSH, /* Flush a port */ ++ MMAL_MSG_PORT_ACTION_TYPE_CONNECT, /* Connect ports */ ++ MMAL_MSG_PORT_ACTION_TYPE_DISCONNECT, /* Disconnect ports */ ++ MMAL_MSG_PORT_ACTION_TYPE_SET_REQUIREMENTS, /* Set buffer requirements*/ ++}; ++ ++struct mmal_msg_header { ++ u32 magic; ++ u32 type; /** enum mmal_msg_type */ ++ ++ /* Opaque handle to the control service */ ++ struct mmal_control_service *control_service; ++ ++ struct mmal_msg_context *context; /** a u32 per message context */ ++ u32 status; /** The status of the vchiq operation */ ++ u32 padding; ++}; ++ ++/* Send from VC to host to report version */ ++struct mmal_msg_version { ++ u32 flags; ++ u32 major; ++ u32 minor; ++ u32 minimum; ++}; ++ ++/* request to VC to create component */ ++struct mmal_msg_component_create { ++ void *client_component; /* component context */ ++ char name[128]; ++ u32 pid; /* For debug */ ++}; ++ ++/* reply from VC to component creation request */ ++struct mmal_msg_component_create_reply { ++ u32 status; /** enum mmal_msg_status - how does this differ to ++ * the one in the header? ++ */ ++ u32 component_handle; /* VideoCore handle for component */ ++ u32 input_num; /* Number of input ports */ ++ u32 output_num; /* Number of output ports */ ++ u32 clock_num; /* Number of clock ports */ ++}; ++ ++/* request to VC to destroy a component */ ++struct mmal_msg_component_destroy { ++ u32 component_handle; ++}; ++ ++struct mmal_msg_component_destroy_reply { ++ u32 status; /** The component destruction status */ ++}; ++ ++ ++/* request and reply to VC to enable a component */ ++struct mmal_msg_component_enable { ++ u32 component_handle; ++}; ++ ++struct mmal_msg_component_enable_reply { ++ u32 status; /** The component enable status */ ++}; ++ ++ ++/* request and reply to VC to disable a component */ ++struct mmal_msg_component_disable { ++ u32 component_handle; ++}; ++ ++struct mmal_msg_component_disable_reply { ++ u32 status; /** The component disable status */ ++}; ++ ++/* request to VC to get port information */ ++struct mmal_msg_port_info_get { ++ u32 component_handle; /* component handle port is associated with */ ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 index; /* port index to query */ ++}; ++ ++/* reply from VC to get port info request */ ++struct mmal_msg_port_info_get_reply { ++ u32 status; /** enum mmal_msg_status */ ++ u32 component_handle; /* component handle port is associated with */ ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 port_index; /* port indexed in query */ ++ s32 found; /* unused */ ++ u32 port_handle; /**< Handle to use for this port */ ++ struct mmal_port port; ++ struct mmal_es_format format; /* elementry stream format */ ++ union mmal_es_specific_format es; /* es type specific data */ ++ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; /* es extra data */ ++}; ++ ++/* request to VC to set port information */ ++struct mmal_msg_port_info_set { ++ u32 component_handle; ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 port_index; /* port indexed in query */ ++ struct mmal_port port; ++ struct mmal_es_format format; ++ union mmal_es_specific_format es; ++ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; ++}; ++ ++/* reply from VC to port info set request */ ++struct mmal_msg_port_info_set_reply { ++ u32 status; ++ u32 component_handle; /* component handle port is associated with */ ++ u32 port_type; /* enum mmal_msg_port_type */ ++ u32 index; /* port indexed in query */ ++ s32 found; /* unused */ ++ u32 port_handle; /**< Handle to use for this port */ ++ struct mmal_port port; ++ struct mmal_es_format format; ++ union mmal_es_specific_format es; ++ u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; ++}; ++ ++ ++/* port action requests that take a mmal_port as a parameter */ ++struct mmal_msg_port_action_port { ++ u32 component_handle; ++ u32 port_handle; ++ u32 action; /* enum mmal_msg_port_action_type */ ++ struct mmal_port port; ++}; ++ ++/* port action requests that take handles as a parameter */ ++struct mmal_msg_port_action_handle { ++ u32 component_handle; ++ u32 port_handle; ++ u32 action; /* enum mmal_msg_port_action_type */ ++ u32 connect_component_handle; ++ u32 connect_port_handle; ++}; ++ ++struct mmal_msg_port_action_reply { ++ u32 status; /** The port action operation status */ ++}; ++ ++ ++ ++ ++/* MMAL buffer transfer */ ++ ++/** Size of space reserved in a buffer message for short messages. */ ++#define MMAL_VC_SHORT_DATA 128 ++ ++/** Signals that the current payload is the end of the stream of data */ ++#define MMAL_BUFFER_HEADER_FLAG_EOS (1<<0) ++/** Signals that the start of the current payload starts a frame */ ++#define MMAL_BUFFER_HEADER_FLAG_FRAME_START (1<<1) ++/** Signals that the end of the current payload ends a frame */ ++#define MMAL_BUFFER_HEADER_FLAG_FRAME_END (1<<2) ++/** Signals that the current payload contains only complete frames (>1) */ ++#define MMAL_BUFFER_HEADER_FLAG_FRAME \ ++ (MMAL_BUFFER_HEADER_FLAG_FRAME_START|MMAL_BUFFER_HEADER_FLAG_FRAME_END) ++/** Signals that the current payload is a keyframe (i.e. self decodable) */ ++#define MMAL_BUFFER_HEADER_FLAG_KEYFRAME (1<<3) ++/** Signals a discontinuity in the stream of data (e.g. after a seek). ++ * Can be used for instance by a decoder to reset its state */ ++#define MMAL_BUFFER_HEADER_FLAG_DISCONTINUITY (1<<4) ++/** Signals a buffer containing some kind of config data for the component ++ * (e.g. codec config data) */ ++#define MMAL_BUFFER_HEADER_FLAG_CONFIG (1<<5) ++/** Signals an encrypted payload */ ++#define MMAL_BUFFER_HEADER_FLAG_ENCRYPTED (1<<6) ++/** Signals a buffer containing side information */ ++#define MMAL_BUFFER_HEADER_FLAG_CODECSIDEINFO (1<<7) ++/** Signals a buffer which is the snapshot/postview image from a stills ++ * capture ++ */ ++#define MMAL_BUFFER_HEADER_FLAGS_SNAPSHOT (1<<8) ++/** Signals a buffer which contains data known to be corrupted */ ++#define MMAL_BUFFER_HEADER_FLAG_CORRUPTED (1<<9) ++/** Signals that a buffer failed to be transmitted */ ++#define MMAL_BUFFER_HEADER_FLAG_TRANSMISSION_FAILED (1<<10) ++ ++struct mmal_driver_buffer { ++ u32 magic; ++ u32 component_handle; ++ u32 port_handle; ++ void *client_context; ++}; ++ ++/* buffer header */ ++struct mmal_buffer_header { ++ struct mmal_buffer_header *next; /* next header */ ++ void *priv; /* framework private data */ ++ u32 cmd; ++ void *data; ++ u32 alloc_size; ++ u32 length; ++ u32 offset; ++ u32 flags; ++ s64 pts; ++ s64 dts; ++ void *type; ++ void *user_data; ++}; ++ ++struct mmal_buffer_header_type_specific { ++ union { ++ struct { ++ u32 planes; ++ u32 offset[4]; ++ u32 pitch[4]; ++ u32 flags; ++ } video; ++ } u; ++}; ++ ++struct mmal_msg_buffer_from_host { ++ /* The front 32 bytes of the buffer header are copied ++ * back to us in the reply to allow for context. This ++ * area is used to store two mmal_driver_buffer structures to ++ * allow for multiple concurrent service users. ++ */ ++ /* control data */ ++ struct mmal_driver_buffer drvbuf; ++ ++ /* referenced control data for passthrough buffer management */ ++ struct mmal_driver_buffer drvbuf_ref; ++ struct mmal_buffer_header buffer_header; /* buffer header itself */ ++ struct mmal_buffer_header_type_specific buffer_header_type_specific; ++ s32 is_zero_copy; ++ s32 has_reference; ++ ++ /** allows short data to be xfered in control message */ ++ u32 payload_in_message; ++ u8 short_data[MMAL_VC_SHORT_DATA]; ++}; ++ ++ ++/* port parameter setting */ ++ ++#define MMAL_WORKER_PORT_PARAMETER_SPACE 96 ++ ++struct mmal_msg_port_parameter_set { ++ u32 component_handle; /* component */ ++ u32 port_handle; /* port */ ++ u32 id; /* Parameter ID */ ++ u32 size; /* Parameter size */ ++ uint32_t value[MMAL_WORKER_PORT_PARAMETER_SPACE]; ++}; ++ ++struct mmal_msg_port_parameter_set_reply { ++ u32 status; /** enum mmal_msg_status todo: how does this ++ * differ to the one in the header? ++ */ ++}; ++ ++/* port parameter getting */ ++ ++struct mmal_msg_port_parameter_get { ++ u32 component_handle; /* component */ ++ u32 port_handle; /* port */ ++ u32 id; /* Parameter ID */ ++ u32 size; /* Parameter size */ ++}; ++ ++struct mmal_msg_port_parameter_get_reply { ++ u32 status; /* Status of mmal_port_parameter_get call */ ++ u32 id; /* Parameter ID */ ++ u32 size; /* Parameter size */ ++ uint32_t value[MMAL_WORKER_PORT_PARAMETER_SPACE]; ++}; ++ ++/* event messages */ ++#define MMAL_WORKER_EVENT_SPACE 256 ++ ++struct mmal_msg_event_to_host { ++ void *client_component; /* component context */ ++ ++ u32 port_type; ++ u32 port_num; ++ ++ u32 cmd; ++ u32 length; ++ u8 data[MMAL_WORKER_EVENT_SPACE]; ++ struct mmal_buffer_header *delayed_buffer; ++}; ++ ++/* all mmal messages are serialised through this structure */ ++struct mmal_msg { ++ /* header */ ++ struct mmal_msg_header h; ++ /* payload */ ++ union { ++ struct mmal_msg_version version; ++ ++ struct mmal_msg_component_create component_create; ++ struct mmal_msg_component_create_reply component_create_reply; ++ ++ struct mmal_msg_component_destroy component_destroy; ++ struct mmal_msg_component_destroy_reply component_destroy_reply; ++ ++ struct mmal_msg_component_enable component_enable; ++ struct mmal_msg_component_enable_reply component_enable_reply; ++ ++ struct mmal_msg_component_disable component_disable; ++ struct mmal_msg_component_disable_reply component_disable_reply; ++ ++ struct mmal_msg_port_info_get port_info_get; ++ struct mmal_msg_port_info_get_reply port_info_get_reply; ++ ++ struct mmal_msg_port_info_set port_info_set; ++ struct mmal_msg_port_info_set_reply port_info_set_reply; ++ ++ struct mmal_msg_port_action_port port_action_port; ++ struct mmal_msg_port_action_handle port_action_handle; ++ struct mmal_msg_port_action_reply port_action_reply; ++ ++ struct mmal_msg_buffer_from_host buffer_from_host; ++ ++ struct mmal_msg_port_parameter_set port_parameter_set; ++ struct mmal_msg_port_parameter_set_reply ++ port_parameter_set_reply; ++ struct mmal_msg_port_parameter_get ++ port_parameter_get; ++ struct mmal_msg_port_parameter_get_reply ++ port_parameter_get_reply; ++ ++ struct mmal_msg_event_to_host event_to_host; ++ ++ u8 payload[MMAL_MSG_MAX_PAYLOAD]; ++ } u; ++}; +diff --git a/drivers/media/platform/bcm2835/mmal-parameters.h b/drivers/media/platform/bcm2835/mmal-parameters.h +new file mode 100644 +index 0000000..c611b58 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-parameters.h +@@ -0,0 +1,539 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ */ ++ ++/* common parameters */ ++ ++/** @name Parameter groups ++ * Parameters are divided into groups, and then allocated sequentially within ++ * a group using an enum. ++ * @{ ++ */ ++ ++/** Common parameter ID group, used with many types of component. */ ++#define MMAL_PARAMETER_GROUP_COMMON (0<<16) ++/** Camera-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_CAMERA (1<<16) ++/** Video-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_VIDEO (2<<16) ++/** Audio-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_AUDIO (3<<16) ++/** Clock-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_CLOCK (4<<16) ++/** Miracast-specific parameter ID group. */ ++#define MMAL_PARAMETER_GROUP_MIRACAST (5<<16) ++ ++/* Common parameters */ ++enum mmal_parameter_common_type { ++ MMAL_PARAMETER_UNUSED /**< Never a valid parameter ID */ ++ = MMAL_PARAMETER_GROUP_COMMON, ++ MMAL_PARAMETER_SUPPORTED_ENCODINGS, /**< MMAL_PARAMETER_ENCODING_T */ ++ MMAL_PARAMETER_URI, /**< MMAL_PARAMETER_URI_T */ ++ ++ /** MMAL_PARAMETER_CHANGE_EVENT_REQUEST_T */ ++ MMAL_PARAMETER_CHANGE_EVENT_REQUEST, ++ ++ /** MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_ZERO_COPY, ++ ++ /**< MMAL_PARAMETER_BUFFER_REQUIREMENTS_T */ ++ MMAL_PARAMETER_BUFFER_REQUIREMENTS, ++ ++ MMAL_PARAMETER_STATISTICS, /**< MMAL_PARAMETER_STATISTICS_T */ ++ MMAL_PARAMETER_CORE_STATISTICS, /**< MMAL_PARAMETER_CORE_STATISTICS_T */ ++ MMAL_PARAMETER_MEM_USAGE, /**< MMAL_PARAMETER_MEM_USAGE_T */ ++ MMAL_PARAMETER_BUFFER_FLAG_FILTER, /**< MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_SEEK, /**< MMAL_PARAMETER_SEEK_T */ ++ MMAL_PARAMETER_POWERMON_ENABLE, /**< MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_LOGGING, /**< MMAL_PARAMETER_LOGGING_T */ ++ MMAL_PARAMETER_SYSTEM_TIME /**< MMAL_PARAMETER_UINT64_T */ ++}; ++ ++/* camera parameters */ ++ ++enum mmal_parameter_camera_type { ++ /* 0 */ ++ /** @ref MMAL_PARAMETER_THUMBNAIL_CONFIG_T */ ++ MMAL_PARAMETER_THUMBNAIL_CONFIGURATION ++ = MMAL_PARAMETER_GROUP_CAMERA, ++ MMAL_PARAMETER_CAPTURE_QUALITY, /**< Unused? */ ++ MMAL_PARAMETER_ROTATION, /**< @ref MMAL_PARAMETER_INT32_T */ ++ MMAL_PARAMETER_EXIF_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_EXIF, /**< @ref MMAL_PARAMETER_EXIF_T */ ++ MMAL_PARAMETER_AWB_MODE, /**< @ref MMAL_PARAM_AWBMODE_T */ ++ MMAL_PARAMETER_IMAGE_EFFECT, /**< @ref MMAL_PARAMETER_IMAGEFX_T */ ++ MMAL_PARAMETER_COLOUR_EFFECT, /**< @ref MMAL_PARAMETER_COLOURFX_T */ ++ MMAL_PARAMETER_FLICKER_AVOID, /**< @ref MMAL_PARAMETER_FLICKERAVOID_T */ ++ MMAL_PARAMETER_FLASH, /**< @ref MMAL_PARAMETER_FLASH_T */ ++ MMAL_PARAMETER_REDEYE, /**< @ref MMAL_PARAMETER_REDEYE_T */ ++ MMAL_PARAMETER_FOCUS, /**< @ref MMAL_PARAMETER_FOCUS_T */ ++ MMAL_PARAMETER_FOCAL_LENGTHS, /**< Unused? */ ++ MMAL_PARAMETER_EXPOSURE_COMP, /**< @ref MMAL_PARAMETER_INT32_T */ ++ MMAL_PARAMETER_ZOOM, /**< @ref MMAL_PARAMETER_SCALEFACTOR_T */ ++ MMAL_PARAMETER_MIRROR, /**< @ref MMAL_PARAMETER_MIRROR_T */ ++ ++ /* 0x10 */ ++ MMAL_PARAMETER_CAMERA_NUM, /**< @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_CAPTURE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_EXPOSURE_MODE, /**< @ref MMAL_PARAMETER_EXPOSUREMODE_T */ ++ MMAL_PARAMETER_EXP_METERING_MODE, /**< @ref MMAL_PARAMETER_EXPOSUREMETERINGMODE_T */ ++ MMAL_PARAMETER_FOCUS_STATUS, /**< @ref MMAL_PARAMETER_FOCUS_STATUS_T */ ++ MMAL_PARAMETER_CAMERA_CONFIG, /**< @ref MMAL_PARAMETER_CAMERA_CONFIG_T */ ++ MMAL_PARAMETER_CAPTURE_STATUS, /**< @ref MMAL_PARAMETER_CAPTURE_STATUS_T */ ++ MMAL_PARAMETER_FACE_TRACK, /**< @ref MMAL_PARAMETER_FACE_TRACK_T */ ++ MMAL_PARAMETER_DRAW_BOX_FACES_AND_FOCUS, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_JPEG_Q_FACTOR, /**< @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_FRAME_RATE, /**< @ref MMAL_PARAMETER_FRAME_RATE_T */ ++ MMAL_PARAMETER_USE_STC, /**< @ref MMAL_PARAMETER_CAMERA_STC_MODE_T */ ++ MMAL_PARAMETER_CAMERA_INFO, /**< @ref MMAL_PARAMETER_CAMERA_INFO_T */ ++ MMAL_PARAMETER_VIDEO_STABILISATION, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_FACE_TRACK_RESULTS, /**< @ref MMAL_PARAMETER_FACE_TRACK_RESULTS_T */ ++ MMAL_PARAMETER_ENABLE_RAW_CAPTURE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ ++ /* 0x20 */ ++ MMAL_PARAMETER_DPF_FILE, /**< @ref MMAL_PARAMETER_URI_T */ ++ MMAL_PARAMETER_ENABLE_DPF_FILE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_DPF_FAIL_IS_FATAL, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_CAPTURE_MODE, /**< @ref MMAL_PARAMETER_CAPTUREMODE_T */ ++ MMAL_PARAMETER_FOCUS_REGIONS, /**< @ref MMAL_PARAMETER_FOCUS_REGIONS_T */ ++ MMAL_PARAMETER_INPUT_CROP, /**< @ref MMAL_PARAMETER_INPUT_CROP_T */ ++ MMAL_PARAMETER_SENSOR_INFORMATION, /**< @ref MMAL_PARAMETER_SENSOR_INFORMATION_T */ ++ MMAL_PARAMETER_FLASH_SELECT, /**< @ref MMAL_PARAMETER_FLASH_SELECT_T */ ++ MMAL_PARAMETER_FIELD_OF_VIEW, /**< @ref MMAL_PARAMETER_FIELD_OF_VIEW_T */ ++ MMAL_PARAMETER_HIGH_DYNAMIC_RANGE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_DYNAMIC_RANGE_COMPRESSION, /**< @ref MMAL_PARAMETER_DRC_T */ ++ MMAL_PARAMETER_ALGORITHM_CONTROL, /**< @ref MMAL_PARAMETER_ALGORITHM_CONTROL_T */ ++ MMAL_PARAMETER_SHARPNESS, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ MMAL_PARAMETER_CONTRAST, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ MMAL_PARAMETER_BRIGHTNESS, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ MMAL_PARAMETER_SATURATION, /**< @ref MMAL_PARAMETER_RATIONAL_T */ ++ ++ /* 0x30 */ ++ MMAL_PARAMETER_ISO, /**< @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_ANTISHAKE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ ++ /** @ref MMAL_PARAMETER_IMAGEFX_PARAMETERS_T */ ++ MMAL_PARAMETER_IMAGE_EFFECT_PARAMETERS, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_CAMERA_BURST_CAPTURE, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_CAMERA_MIN_ISO, ++ ++ /** @ref MMAL_PARAMETER_CAMERA_USE_CASE_T */ ++ MMAL_PARAMETER_CAMERA_USE_CASE, ++ ++ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_CAPTURE_STATS_PASS, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_CAMERA_CUSTOM_SENSOR_CONFIG, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_ENABLE_REGISTER_FILE, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_REGISTER_FAIL_IS_FATAL, ++ ++ /** @ref MMAL_PARAMETER_CONFIGFILE_T */ ++ MMAL_PARAMETER_CONFIGFILE_REGISTERS, ++ ++ /** @ref MMAL_PARAMETER_CONFIGFILE_CHUNK_T */ ++ MMAL_PARAMETER_CONFIGFILE_CHUNK_REGISTERS, ++ MMAL_PARAMETER_JPEG_ATTACH_LOG, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_ZERO_SHUTTER_LAG, /**< @ref MMAL_PARAMETER_ZEROSHUTTERLAG_T */ ++ MMAL_PARAMETER_FPS_RANGE, /**< @ref MMAL_PARAMETER_FPS_RANGE_T */ ++ MMAL_PARAMETER_CAPTURE_EXPOSURE_COMP, /**< @ref MMAL_PARAMETER_INT32_T */ ++ ++ /* 0x40 */ ++ MMAL_PARAMETER_SW_SHARPEN_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_FLASH_REQUIRED, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_SW_SATURATION_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++}; ++ ++enum mmal_parameter_camera_config_timestamp_mode { ++ MMAL_PARAM_TIMESTAMP_MODE_ZERO = 0, /* Always timestamp frames as 0 */ ++ MMAL_PARAM_TIMESTAMP_MODE_RAW_STC, /* Use the raw STC value ++ * for the frame timestamp ++ */ ++ MMAL_PARAM_TIMESTAMP_MODE_RESET_STC, /* Use the STC timestamp ++ * but subtract the ++ * timestamp of the first ++ * frame sent to give a ++ * zero based timestamp. ++ */ ++}; ++ ++/* camera configuration parameter */ ++struct mmal_parameter_camera_config { ++ /* Parameters for setting up the image pools */ ++ u32 max_stills_w; /* Max size of stills capture */ ++ u32 max_stills_h; ++ u32 stills_yuv422; /* Allow YUV422 stills capture */ ++ u32 one_shot_stills; /* Continuous or one shot stills captures. */ ++ ++ u32 max_preview_video_w; /* Max size of the preview or video ++ * capture frames ++ */ ++ u32 max_preview_video_h; ++ u32 num_preview_video_frames; ++ ++ /** Sets the height of the circular buffer for stills capture. */ ++ u32 stills_capture_circular_buffer_height; ++ ++ /** Allows preview/encode to resume as fast as possible after the stills ++ * input frame has been received, and then processes the still frame in ++ * the background whilst preview/encode has resumed. ++ * Actual mode is controlled by MMAL_PARAMETER_CAPTURE_MODE. ++ */ ++ u32 fast_preview_resume; ++ ++ /** Selects algorithm for timestamping frames if ++ * there is no clock component connected. ++ * enum mmal_parameter_camera_config_timestamp_mode ++ */ ++ s32 use_stc_timestamp; ++}; ++ ++ ++enum mmal_parameter_exposuremode { ++ MMAL_PARAM_EXPOSUREMODE_OFF, ++ MMAL_PARAM_EXPOSUREMODE_AUTO, ++ MMAL_PARAM_EXPOSUREMODE_NIGHT, ++ MMAL_PARAM_EXPOSUREMODE_NIGHTPREVIEW, ++ MMAL_PARAM_EXPOSUREMODE_BACKLIGHT, ++ MMAL_PARAM_EXPOSUREMODE_SPOTLIGHT, ++ MMAL_PARAM_EXPOSUREMODE_SPORTS, ++ MMAL_PARAM_EXPOSUREMODE_SNOW, ++ MMAL_PARAM_EXPOSUREMODE_BEACH, ++ MMAL_PARAM_EXPOSUREMODE_VERYLONG, ++ MMAL_PARAM_EXPOSUREMODE_FIXEDFPS, ++ MMAL_PARAM_EXPOSUREMODE_ANTISHAKE, ++ MMAL_PARAM_EXPOSUREMODE_FIREWORKS, ++}; ++ ++enum mmal_parameter_exposuremeteringmode { ++ MMAL_PARAM_EXPOSUREMETERINGMODE_AVERAGE, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_SPOT, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_BACKLIT, ++ MMAL_PARAM_EXPOSUREMETERINGMODE_MATRIX, ++}; ++ ++enum mmal_parameter_awbmode { ++ MMAL_PARAM_AWBMODE_OFF, ++ MMAL_PARAM_AWBMODE_AUTO, ++ MMAL_PARAM_AWBMODE_SUNLIGHT, ++ MMAL_PARAM_AWBMODE_CLOUDY, ++ MMAL_PARAM_AWBMODE_SHADE, ++ MMAL_PARAM_AWBMODE_TUNGSTEN, ++ MMAL_PARAM_AWBMODE_FLUORESCENT, ++ MMAL_PARAM_AWBMODE_INCANDESCENT, ++ MMAL_PARAM_AWBMODE_FLASH, ++ MMAL_PARAM_AWBMODE_HORIZON, ++}; ++ ++enum mmal_parameter_imagefx { ++ MMAL_PARAM_IMAGEFX_NONE, ++ MMAL_PARAM_IMAGEFX_NEGATIVE, ++ MMAL_PARAM_IMAGEFX_SOLARIZE, ++ MMAL_PARAM_IMAGEFX_POSTERIZE, ++ MMAL_PARAM_IMAGEFX_WHITEBOARD, ++ MMAL_PARAM_IMAGEFX_BLACKBOARD, ++ MMAL_PARAM_IMAGEFX_SKETCH, ++ MMAL_PARAM_IMAGEFX_DENOISE, ++ MMAL_PARAM_IMAGEFX_EMBOSS, ++ MMAL_PARAM_IMAGEFX_OILPAINT, ++ MMAL_PARAM_IMAGEFX_HATCH, ++ MMAL_PARAM_IMAGEFX_GPEN, ++ MMAL_PARAM_IMAGEFX_PASTEL, ++ MMAL_PARAM_IMAGEFX_WATERCOLOUR, ++ MMAL_PARAM_IMAGEFX_FILM, ++ MMAL_PARAM_IMAGEFX_BLUR, ++ MMAL_PARAM_IMAGEFX_SATURATION, ++ MMAL_PARAM_IMAGEFX_COLOURSWAP, ++ MMAL_PARAM_IMAGEFX_WASHEDOUT, ++ MMAL_PARAM_IMAGEFX_POSTERISE, ++ MMAL_PARAM_IMAGEFX_COLOURPOINT, ++ MMAL_PARAM_IMAGEFX_COLOURBALANCE, ++ MMAL_PARAM_IMAGEFX_CARTOON, ++}; ++ ++/** Manner of video rate control */ ++enum mmal_parameter_rate_control_mode { ++ MMAL_VIDEO_RATECONTROL_DEFAULT, ++ MMAL_VIDEO_RATECONTROL_VARIABLE, ++ MMAL_VIDEO_RATECONTROL_CONSTANT, ++ MMAL_VIDEO_RATECONTROL_VARIABLE_SKIP_FRAMES, ++ MMAL_VIDEO_RATECONTROL_CONSTANT_SKIP_FRAMES ++}; ++ ++/* video parameters */ ++ ++enum mmal_parameter_video_type { ++ /** @ref MMAL_DISPLAYREGION_T */ ++ MMAL_PARAMETER_DISPLAYREGION = MMAL_PARAMETER_GROUP_VIDEO, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_PROFILE_T */ ++ MMAL_PARAMETER_SUPPORTED_PROFILES, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_PROFILE_T */ ++ MMAL_PARAMETER_PROFILE, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_INTRAPERIOD, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_RATECONTROL_T */ ++ MMAL_PARAMETER_RATECONTROL, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_NALUNITFORMAT_T */ ++ MMAL_PARAMETER_NALUNITFORMAT, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_MINIMISE_FRAGMENTATION, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. ++ * Setting the value to zero resets to the default (one slice per frame). ++ */ ++ MMAL_PARAMETER_MB_ROWS_PER_SLICE, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_LEVEL_EXTENSION_T */ ++ MMAL_PARAMETER_VIDEO_LEVEL_EXTENSION, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_EEDE_ENABLE_T */ ++ MMAL_PARAMETER_VIDEO_EEDE_ENABLE, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_EEDE_LOSSRATE_T */ ++ MMAL_PARAMETER_VIDEO_EEDE_LOSSRATE, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. Request an I-frame. */ ++ MMAL_PARAMETER_VIDEO_REQUEST_I_FRAME, ++ /** @ref MMAL_PARAMETER_VIDEO_INTRA_REFRESH_T */ ++ MMAL_PARAMETER_VIDEO_INTRA_REFRESH, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_IMMUTABLE_INPUT, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. Run-time bit rate control */ ++ MMAL_PARAMETER_VIDEO_BIT_RATE, ++ ++ /** @ref MMAL_PARAMETER_FRAME_RATE_T */ ++ MMAL_PARAMETER_VIDEO_FRAME_RATE, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_MIN_QUANT, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_MAX_QUANT, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_ENCODE_RC_MODEL_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_RC_MODEL, ++ ++ MMAL_PARAMETER_EXTRA_BUFFERS, /**< @ref MMAL_PARAMETER_UINT32_T. */ ++ /** @ref MMAL_PARAMETER_UINT32_T. ++ * Changing this parameter from the default can reduce frame rate ++ * because image buffers need to be re-pitched. ++ */ ++ MMAL_PARAMETER_VIDEO_ALIGN_HORIZ, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. ++ * Changing this parameter from the default can reduce frame rate ++ * because image buffers need to be re-pitched. ++ */ ++ MMAL_PARAMETER_VIDEO_ALIGN_VERT, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_DROPPABLE_PFRAMES, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_INITIAL_QUANT, ++ ++ /**< @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_QP_P, ++ ++ /**< @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_RC_SLICE_DQUANT, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_FRAME_LIMIT_BITS, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_PEAK_RATE, ++ ++ /* H264 specific parameters */ ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_DISABLE_CABAC, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_LOW_LATENCY, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_AU_DELIMITERS, ++ ++ /** @ref MMAL_PARAMETER_UINT32_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_DEBLOCK_IDC, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_ENCODER_H264_MB_INTRA_MODES_T. */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_MB_INTRA_MODE, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_HEADER_ON_OPEN, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_PRECODE_FOR_QP, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_DRM_INIT_INFO_T. */ ++ MMAL_PARAMETER_VIDEO_DRM_INIT_INFO, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_TIMESTAMP_FIFO, ++ ++ /** @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_DECODE_ERROR_CONCEALMENT, ++ ++ /** @ref MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER_T. */ ++ MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER, ++ ++ /** @ref MMAL_PARAMETER_BYTES_T */ ++ MMAL_PARAMETER_VIDEO_DECODE_CONFIG_VD3 ++}; ++ ++/** Valid mirror modes */ ++enum mmal_parameter_mirror { ++ MMAL_PARAM_MIRROR_NONE, ++ MMAL_PARAM_MIRROR_VERTICAL, ++ MMAL_PARAM_MIRROR_HORIZONTAL, ++ MMAL_PARAM_MIRROR_BOTH, ++}; ++ ++enum mmal_parameter_displaytransform { ++ MMAL_DISPLAY_ROT0 = 0, ++ MMAL_DISPLAY_MIRROR_ROT0 = 1, ++ MMAL_DISPLAY_MIRROR_ROT180 = 2, ++ MMAL_DISPLAY_ROT180 = 3, ++ MMAL_DISPLAY_MIRROR_ROT90 = 4, ++ MMAL_DISPLAY_ROT270 = 5, ++ MMAL_DISPLAY_ROT90 = 6, ++ MMAL_DISPLAY_MIRROR_ROT270 = 7, ++}; ++ ++enum mmal_parameter_displaymode { ++ MMAL_DISPLAY_MODE_FILL = 0, ++ MMAL_DISPLAY_MODE_LETTERBOX = 1, ++}; ++ ++enum mmal_parameter_displayset { ++ MMAL_DISPLAY_SET_NONE = 0, ++ MMAL_DISPLAY_SET_NUM = 1, ++ MMAL_DISPLAY_SET_FULLSCREEN = 2, ++ MMAL_DISPLAY_SET_TRANSFORM = 4, ++ MMAL_DISPLAY_SET_DEST_RECT = 8, ++ MMAL_DISPLAY_SET_SRC_RECT = 0x10, ++ MMAL_DISPLAY_SET_MODE = 0x20, ++ MMAL_DISPLAY_SET_PIXEL = 0x40, ++ MMAL_DISPLAY_SET_NOASPECT = 0x80, ++ MMAL_DISPLAY_SET_LAYER = 0x100, ++ MMAL_DISPLAY_SET_COPYPROTECT = 0x200, ++ MMAL_DISPLAY_SET_ALPHA = 0x400, ++}; ++ ++struct mmal_parameter_displayregion { ++ /** Bitfield that indicates which fields are set and should be ++ * used. All other fields will maintain their current value. ++ * \ref MMAL_DISPLAYSET_T defines the bits that can be ++ * combined. ++ */ ++ u32 set; ++ ++ /** Describes the display output device, with 0 typically ++ * being a directly connected LCD display. The actual values ++ * will depend on the hardware. Code using hard-wired numbers ++ * (e.g. 2) is certain to fail. ++ */ ++ ++ u32 display_num; ++ /** Indicates that we are using the full device screen area, ++ * rather than a window of the display. If zero, then ++ * dest_rect is used to specify a region of the display to ++ * use. ++ */ ++ ++ s32 fullscreen; ++ /** Indicates any rotation or flipping used to map frames onto ++ * the natural display orientation. ++ */ ++ u32 transform; /* enum mmal_parameter_displaytransform */ ++ ++ /** Where to display the frame within the screen, if ++ * fullscreen is zero. ++ */ ++ struct vchiq_mmal_rect dest_rect; ++ ++ /** Indicates which area of the frame to display. If all ++ * values are zero, the whole frame will be used. ++ */ ++ struct vchiq_mmal_rect src_rect; ++ ++ /** If set to non-zero, indicates that any display scaling ++ * should disregard the aspect ratio of the frame region being ++ * displayed. ++ */ ++ s32 noaspect; ++ ++ /** Indicates how the image should be scaled to fit the ++ * display. \code MMAL_DISPLAY_MODE_FILL \endcode indicates ++ * that the image should fill the screen by potentially ++ * cropping the frames. Setting \code mode \endcode to \code ++ * MMAL_DISPLAY_MODE_LETTERBOX \endcode indicates that all the ++ * source region should be displayed and black bars added if ++ * necessary. ++ */ ++ u32 mode; /* enum mmal_parameter_displaymode */ ++ ++ /** If non-zero, defines the width of a source pixel relative ++ * to \code pixel_y \endcode. If zero, then pixels default to ++ * being square. ++ */ ++ u32 pixel_x; ++ ++ /** If non-zero, defines the height of a source pixel relative ++ * to \code pixel_x \endcode. If zero, then pixels default to ++ * being square. ++ */ ++ u32 pixel_y; ++ ++ /** Sets the relative depth of the images, with greater values ++ * being in front of smaller values. ++ */ ++ u32 layer; ++ ++ /** Set to non-zero to ensure copy protection is used on ++ * output. ++ */ ++ s32 copyprotect_required; ++ ++ /** Level of opacity of the layer, where zero is fully ++ * transparent and 255 is fully opaque. ++ */ ++ u32 alpha; ++}; ++ ++#define MMAL_MAX_IMAGEFX_PARAMETERS 5 ++ ++struct mmal_parameter_imagefx_parameters { ++ enum mmal_parameter_imagefx effect; ++ u32 num_effect_params; ++ u32 effect_parameter[MMAL_MAX_IMAGEFX_PARAMETERS]; ++}; +diff --git a/drivers/media/platform/bcm2835/mmal-vchiq.c b/drivers/media/platform/bcm2835/mmal-vchiq.c +new file mode 100644 +index 0000000..a06fb44 +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-vchiq.c +@@ -0,0 +1,1916 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ * ++ * V4L2 driver MMAL vchiq interface code ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "mmal-common.h" ++#include "mmal-vchiq.h" ++#include "mmal-msg.h" ++ ++#define USE_VCHIQ_ARM ++#include "interface/vchi/vchi.h" ++ ++/* maximum number of components supported */ ++#define VCHIQ_MMAL_MAX_COMPONENTS 4 ++ ++/*#define FULL_MSG_DUMP 1*/ ++ ++#ifdef DEBUG ++static const char *const msg_type_names[] = { ++ "UNKNOWN", ++ "QUIT", ++ "SERVICE_CLOSED", ++ "GET_VERSION", ++ "COMPONENT_CREATE", ++ "COMPONENT_DESTROY", ++ "COMPONENT_ENABLE", ++ "COMPONENT_DISABLE", ++ "PORT_INFO_GET", ++ "PORT_INFO_SET", ++ "PORT_ACTION", ++ "BUFFER_FROM_HOST", ++ "BUFFER_TO_HOST", ++ "GET_STATS", ++ "PORT_PARAMETER_SET", ++ "PORT_PARAMETER_GET", ++ "EVENT_TO_HOST", ++ "GET_CORE_STATS_FOR_PORT", ++ "OPAQUE_ALLOCATOR", ++ "CONSUME_MEM", ++ "LMK", ++ "OPAQUE_ALLOCATOR_DESC", ++ "DRM_GET_LHS32", ++ "DRM_GET_TIME", ++ "BUFFER_FROM_HOST_ZEROLEN", ++ "PORT_FLUSH", ++ "HOST_LOG", ++}; ++#endif ++ ++static const char *const port_action_type_names[] = { ++ "UNKNOWN", ++ "ENABLE", ++ "DISABLE", ++ "FLUSH", ++ "CONNECT", ++ "DISCONNECT", ++ "SET_REQUIREMENTS", ++}; ++ ++#if defined(DEBUG) ++#if defined(FULL_MSG_DUMP) ++#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE) \ ++ do { \ ++ pr_debug(TITLE" type:%s(%d) length:%d\n", \ ++ msg_type_names[(MSG)->h.type], \ ++ (MSG)->h.type, (MSG_LEN)); \ ++ print_hex_dump(KERN_DEBUG, "<h.type], \ ++ (MSG)->h.type, (MSG_LEN)); \ ++ } ++#endif ++#else ++#define DBG_DUMP_MSG(MSG, MSG_LEN, TITLE) ++#endif ++ ++/* normal message context */ ++struct mmal_msg_context { ++ union { ++ struct { ++ /* work struct for defered callback - must come first */ ++ struct work_struct work; ++ /* mmal instance */ ++ struct vchiq_mmal_instance *instance; ++ /* mmal port */ ++ struct vchiq_mmal_port *port; ++ /* actual buffer used to store bulk reply */ ++ struct mmal_buffer *buffer; ++ /* amount of buffer used */ ++ unsigned long buffer_used; ++ /* MMAL buffer flags */ ++ u32 mmal_flags; ++ /* Presentation and Decode timestamps */ ++ s64 pts; ++ s64 dts; ++ ++ int status; /* context status */ ++ ++ } bulk; /* bulk data */ ++ ++ struct { ++ /* message handle to release */ ++ VCHI_HELD_MSG_T msg_handle; ++ /* pointer to received message */ ++ struct mmal_msg *msg; ++ /* received message length */ ++ u32 msg_len; ++ /* completion upon reply */ ++ struct completion cmplt; ++ } sync; /* synchronous response */ ++ } u; ++ ++}; ++ ++struct vchiq_mmal_instance { ++ VCHI_SERVICE_HANDLE_T handle; ++ ++ /* ensure serialised access to service */ ++ struct mutex vchiq_mutex; ++ ++ /* ensure serialised access to bulk operations */ ++ struct mutex bulk_mutex; ++ ++ /* vmalloc page to receive scratch bulk xfers into */ ++ void *bulk_scratch; ++ ++ /* component to use next */ ++ int component_idx; ++ struct vchiq_mmal_component component[VCHIQ_MMAL_MAX_COMPONENTS]; ++}; ++ ++static struct mmal_msg_context *get_msg_context(struct vchiq_mmal_instance ++ *instance) ++{ ++ struct mmal_msg_context *msg_context; ++ ++ /* todo: should this be allocated from a pool to avoid kmalloc */ ++ msg_context = kmalloc(sizeof(*msg_context), GFP_KERNEL); ++ memset(msg_context, 0, sizeof(*msg_context)); ++ ++ return msg_context; ++} ++ ++static void release_msg_context(struct mmal_msg_context *msg_context) ++{ ++ kfree(msg_context); ++} ++ ++/* deals with receipt of event to host message */ ++static void event_to_host_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, u32 msg_len) ++{ ++ pr_debug("unhandled event\n"); ++ pr_debug("component:%p port type:%d num:%d cmd:0x%x length:%d\n", ++ msg->u.event_to_host.client_component, ++ msg->u.event_to_host.port_type, ++ msg->u.event_to_host.port_num, ++ msg->u.event_to_host.cmd, msg->u.event_to_host.length); ++} ++ ++/* workqueue scheduled callback ++ * ++ * we do this because it is important we do not call any other vchiq ++ * sync calls from witin the message delivery thread ++ */ ++static void buffer_work_cb(struct work_struct *work) ++{ ++ struct mmal_msg_context *msg_context = (struct mmal_msg_context *)work; ++ ++ msg_context->u.bulk.port->buffer_cb(msg_context->u.bulk.instance, ++ msg_context->u.bulk.port, ++ msg_context->u.bulk.status, ++ msg_context->u.bulk.buffer, ++ msg_context->u.bulk.buffer_used, ++ msg_context->u.bulk.mmal_flags, ++ msg_context->u.bulk.dts, ++ msg_context->u.bulk.pts); ++ ++ /* release message context */ ++ release_msg_context(msg_context); ++} ++ ++/* enqueue a bulk receive for a given message context */ ++static int bulk_receive(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, ++ struct mmal_msg_context *msg_context) ++{ ++ unsigned long rd_len; ++ unsigned long flags = 0; ++ int ret; ++ ++ /* bulk mutex stops other bulk operations while we have a ++ * receive in progress - released in callback ++ */ ++ ret = mutex_lock_interruptible(&instance->bulk_mutex); ++ if (ret != 0) ++ return ret; ++ ++ rd_len = msg->u.buffer_from_host.buffer_header.length; ++ ++ /* take buffer from queue */ ++ spin_lock_irqsave(&msg_context->u.bulk.port->slock, flags); ++ if (list_empty(&msg_context->u.bulk.port->buffers)) { ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ pr_err("buffer list empty trying to submit bulk receive\n"); ++ ++ /* todo: this is a serious error, we should never have ++ * commited a buffer_to_host operation to the mmal ++ * port without the buffer to back it up (underflow ++ * handling) and there is no obvious way to deal with ++ * this - how is the mmal servie going to react when ++ * we fail to do the xfer and reschedule a buffer when ++ * it arrives? perhaps a starved flag to indicate a ++ * waiting bulk receive? ++ */ ++ ++ mutex_unlock(&instance->bulk_mutex); ++ ++ return -EINVAL; ++ } ++ ++ msg_context->u.bulk.buffer = ++ list_entry(msg_context->u.bulk.port->buffers.next, ++ struct mmal_buffer, list); ++ list_del(&msg_context->u.bulk.buffer->list); ++ ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ ++ /* ensure we do not overrun the available buffer */ ++ if (rd_len > msg_context->u.bulk.buffer->buffer_size) { ++ rd_len = msg_context->u.bulk.buffer->buffer_size; ++ pr_warn("short read as not enough receive buffer space\n"); ++ /* todo: is this the correct response, what happens to ++ * the rest of the message data? ++ */ ++ } ++ ++ /* store length */ ++ msg_context->u.bulk.buffer_used = rd_len; ++ msg_context->u.bulk.mmal_flags = ++ msg->u.buffer_from_host.buffer_header.flags; ++ msg_context->u.bulk.dts = msg->u.buffer_from_host.buffer_header.dts; ++ msg_context->u.bulk.pts = msg->u.buffer_from_host.buffer_header.pts; ++ ++ // only need to flush L1 cache here, as VCHIQ takes care of the L2 ++ // cache. ++ __cpuc_flush_dcache_area(msg_context->u.bulk.buffer->buffer, rd_len); ++ ++ /* queue the bulk submission */ ++ vchi_service_use(instance->handle); ++ ret = vchi_bulk_queue_receive(instance->handle, ++ msg_context->u.bulk.buffer->buffer, ++ /* Actual receive needs to be a multiple ++ * of 4 bytes ++ */ ++ (rd_len + 3) & ~3, ++ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, ++ msg_context); ++ ++ vchi_service_release(instance->handle); ++ ++ if (ret != 0) { ++ /* callback will not be clearing the mutex */ ++ mutex_unlock(&instance->bulk_mutex); ++ } ++ ++ return ret; ++} ++ ++/* enque a dummy bulk receive for a given message context */ ++static int dummy_bulk_receive(struct vchiq_mmal_instance *instance, ++ struct mmal_msg_context *msg_context) ++{ ++ int ret; ++ ++ /* bulk mutex stops other bulk operations while we have a ++ * receive in progress - released in callback ++ */ ++ ret = mutex_lock_interruptible(&instance->bulk_mutex); ++ if (ret != 0) ++ return ret; ++ ++ /* zero length indicates this was a dummy transfer */ ++ msg_context->u.bulk.buffer_used = 0; ++ ++ /* queue the bulk submission */ ++ vchi_service_use(instance->handle); ++ ++ ret = vchi_bulk_queue_receive(instance->handle, ++ instance->bulk_scratch, ++ 8, ++ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, ++ msg_context); ++ ++ vchi_service_release(instance->handle); ++ ++ if (ret != 0) { ++ /* callback will not be clearing the mutex */ ++ mutex_unlock(&instance->bulk_mutex); ++ } ++ ++ return ret; ++} ++ ++/* data in message, memcpy from packet into output buffer */ ++static int inline_receive(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, ++ struct mmal_msg_context *msg_context) ++{ ++ unsigned long flags = 0; ++ ++ /* take buffer from queue */ ++ spin_lock_irqsave(&msg_context->u.bulk.port->slock, flags); ++ if (list_empty(&msg_context->u.bulk.port->buffers)) { ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ pr_err("buffer list empty trying to receive inline\n"); ++ ++ /* todo: this is a serious error, we should never have ++ * commited a buffer_to_host operation to the mmal ++ * port without the buffer to back it up (with ++ * underflow handling) and there is no obvious way to ++ * deal with this. Less bad than the bulk case as we ++ * can just drop this on the floor but...unhelpful ++ */ ++ return -EINVAL; ++ } ++ ++ msg_context->u.bulk.buffer = ++ list_entry(msg_context->u.bulk.port->buffers.next, ++ struct mmal_buffer, list); ++ list_del(&msg_context->u.bulk.buffer->list); ++ ++ spin_unlock_irqrestore(&msg_context->u.bulk.port->slock, flags); ++ ++ memcpy(msg_context->u.bulk.buffer->buffer, ++ msg->u.buffer_from_host.short_data, ++ msg->u.buffer_from_host.payload_in_message); ++ ++ msg_context->u.bulk.buffer_used = ++ msg->u.buffer_from_host.payload_in_message; ++ ++ return 0; ++} ++ ++/* queue the buffer availability with MMAL_MSG_TYPE_BUFFER_FROM_HOST */ ++static int ++buffer_from_host(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, struct mmal_buffer *buf) ++{ ++ struct mmal_msg_context *msg_context; ++ struct mmal_msg m; ++ int ret; ++ ++ pr_debug("instance:%p buffer:%p\n", instance->handle, buf); ++ ++ /* bulk mutex stops other bulk operations while we ++ * have a receive in progress ++ */ ++ if (mutex_lock_interruptible(&instance->bulk_mutex)) ++ return -EINTR; ++ ++ /* get context */ ++ msg_context = get_msg_context(instance); ++ if (msg_context == NULL) ++ return -ENOMEM; ++ ++ /* store bulk message context for when data arrives */ ++ msg_context->u.bulk.instance = instance; ++ msg_context->u.bulk.port = port; ++ msg_context->u.bulk.buffer = NULL; /* not valid until bulk xfer */ ++ msg_context->u.bulk.buffer_used = 0; ++ ++ /* initialise work structure ready to schedule callback */ ++ INIT_WORK(&msg_context->u.bulk.work, buffer_work_cb); ++ ++ /* prep the buffer from host message */ ++ memset(&m, 0xbc, sizeof(m)); /* just to make debug clearer */ ++ ++ m.h.type = MMAL_MSG_TYPE_BUFFER_FROM_HOST; ++ m.h.magic = MMAL_MAGIC; ++ m.h.context = msg_context; ++ m.h.status = 0; ++ ++ /* drvbuf is our private data passed back */ ++ m.u.buffer_from_host.drvbuf.magic = MMAL_MAGIC; ++ m.u.buffer_from_host.drvbuf.component_handle = port->component->handle; ++ m.u.buffer_from_host.drvbuf.port_handle = port->handle; ++ m.u.buffer_from_host.drvbuf.client_context = msg_context; ++ ++ /* buffer header */ ++ m.u.buffer_from_host.buffer_header.cmd = 0; ++ m.u.buffer_from_host.buffer_header.data = buf->buffer; ++ m.u.buffer_from_host.buffer_header.alloc_size = buf->buffer_size; ++ m.u.buffer_from_host.buffer_header.length = 0; /* nothing used yet */ ++ m.u.buffer_from_host.buffer_header.offset = 0; /* no offset */ ++ m.u.buffer_from_host.buffer_header.flags = 0; /* no flags */ ++ m.u.buffer_from_host.buffer_header.pts = MMAL_TIME_UNKNOWN; ++ m.u.buffer_from_host.buffer_header.dts = MMAL_TIME_UNKNOWN; ++ ++ /* clear buffer type sepecific data */ ++ memset(&m.u.buffer_from_host.buffer_header_type_specific, 0, ++ sizeof(m.u.buffer_from_host.buffer_header_type_specific)); ++ ++ /* no payload in message */ ++ m.u.buffer_from_host.payload_in_message = 0; ++ ++ vchi_service_use(instance->handle); ++ ++ ret = vchi_msg_queue(instance->handle, &m, ++ sizeof(struct mmal_msg_header) + ++ sizeof(m.u.buffer_from_host), ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (ret != 0) { ++ release_msg_context(msg_context); ++ /* todo: is this correct error value? */ ++ } ++ ++ vchi_service_release(instance->handle); ++ ++ mutex_unlock(&instance->bulk_mutex); ++ ++ return ret; ++} ++ ++/* submit a buffer to the mmal sevice ++ * ++ * the buffer_from_host uses size data from the ports next available ++ * mmal_buffer and deals with there being no buffer available by ++ * incrementing the underflow for later ++ */ ++static int port_buffer_from_host(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct mmal_buffer *buf; ++ unsigned long flags = 0; ++ ++ if (!port->enabled) ++ return -EINVAL; ++ ++ /* peek buffer from queue */ ++ spin_lock_irqsave(&port->slock, flags); ++ if (list_empty(&port->buffers)) { ++ port->buffer_underflow++; ++ spin_unlock_irqrestore(&port->slock, flags); ++ return -ENOSPC; ++ } ++ ++ buf = list_entry(port->buffers.next, struct mmal_buffer, list); ++ ++ spin_unlock_irqrestore(&port->slock, flags); ++ ++ /* issue buffer to mmal service */ ++ ret = buffer_from_host(instance, port, buf); ++ if (ret) { ++ pr_err("adding buffer header failed\n"); ++ /* todo: how should this be dealt with */ ++ } ++ ++ return ret; ++} ++ ++/* deals with receipt of buffer to host message */ ++static void buffer_to_host_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, u32 msg_len) ++{ ++ struct mmal_msg_context *msg_context; ++ ++ pr_debug("buffer_to_host_cb: instance:%p msg:%p msg_len:%d\n", ++ instance, msg, msg_len); ++ ++ if (msg->u.buffer_from_host.drvbuf.magic == MMAL_MAGIC) { ++ msg_context = msg->u.buffer_from_host.drvbuf.client_context; ++ } else { ++ pr_err("MMAL_MSG_TYPE_BUFFER_TO_HOST with bad magic\n"); ++ return; ++ } ++ ++ if (msg->h.status != MMAL_MSG_STATUS_SUCCESS) { ++ /* message reception had an error */ ++ pr_warn("error %d in reply\n", msg->h.status); ++ ++ msg_context->u.bulk.status = msg->h.status; ++ ++ } else if (msg->u.buffer_from_host.buffer_header.length == 0) { ++ /* empty buffer */ ++ if (msg->u.buffer_from_host.buffer_header.flags & ++ MMAL_BUFFER_HEADER_FLAG_EOS) { ++ msg_context->u.bulk.status = ++ dummy_bulk_receive(instance, msg_context); ++ if (msg_context->u.bulk.status == 0) ++ return; /* successful bulk submission, bulk ++ * completion will trigger callback ++ */ ++ } else { ++ /* do callback with empty buffer - not EOS though */ ++ msg_context->u.bulk.status = 0; ++ msg_context->u.bulk.buffer_used = 0; ++ } ++ } else if (msg->u.buffer_from_host.payload_in_message == 0) { ++ /* data is not in message, queue a bulk receive */ ++ msg_context->u.bulk.status = ++ bulk_receive(instance, msg, msg_context); ++ if (msg_context->u.bulk.status == 0) ++ return; /* successful bulk submission, bulk ++ * completion will trigger callback ++ */ ++ ++ /* failed to submit buffer, this will end badly */ ++ pr_err("error %d on bulk submission\n", ++ msg_context->u.bulk.status); ++ ++ } else if (msg->u.buffer_from_host.payload_in_message <= ++ MMAL_VC_SHORT_DATA) { ++ /* data payload within message */ ++ msg_context->u.bulk.status = inline_receive(instance, msg, ++ msg_context); ++ } else { ++ pr_err("message with invalid short payload\n"); ++ ++ /* signal error */ ++ msg_context->u.bulk.status = -EINVAL; ++ msg_context->u.bulk.buffer_used = ++ msg->u.buffer_from_host.payload_in_message; ++ } ++ ++ /* replace the buffer header */ ++ port_buffer_from_host(instance, msg_context->u.bulk.port); ++ ++ /* schedule the port callback */ ++ schedule_work(&msg_context->u.bulk.work); ++} ++ ++static void bulk_receive_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg_context *msg_context) ++{ ++ /* bulk receive operation complete */ ++ mutex_unlock(&msg_context->u.bulk.instance->bulk_mutex); ++ ++ /* replace the buffer header */ ++ port_buffer_from_host(msg_context->u.bulk.instance, ++ msg_context->u.bulk.port); ++ ++ msg_context->u.bulk.status = 0; ++ ++ /* schedule the port callback */ ++ schedule_work(&msg_context->u.bulk.work); ++} ++ ++static void bulk_abort_cb(struct vchiq_mmal_instance *instance, ++ struct mmal_msg_context *msg_context) ++{ ++ pr_err("%s: bulk ABORTED msg_context:%p\n", __func__, msg_context); ++ ++ /* bulk receive operation complete */ ++ mutex_unlock(&msg_context->u.bulk.instance->bulk_mutex); ++ ++ /* replace the buffer header */ ++ port_buffer_from_host(msg_context->u.bulk.instance, ++ msg_context->u.bulk.port); ++ ++ msg_context->u.bulk.status = -EINTR; ++ ++ schedule_work(&msg_context->u.bulk.work); ++} ++ ++/* incoming event service callback */ ++static void service_callback(void *param, ++ const VCHI_CALLBACK_REASON_T reason, ++ void *bulk_ctx) ++{ ++ struct vchiq_mmal_instance *instance = param; ++ int status; ++ u32 msg_len; ++ struct mmal_msg *msg; ++ VCHI_HELD_MSG_T msg_handle; ++ ++ if (!instance) { ++ pr_err("Message callback passed NULL instance\n"); ++ return; ++ } ++ ++ switch (reason) { ++ case VCHI_CALLBACK_MSG_AVAILABLE: ++ status = vchi_msg_hold(instance->handle, (void **)&msg, ++ &msg_len, VCHI_FLAGS_NONE, &msg_handle); ++ if (status) { ++ pr_err("Unable to dequeue a message (%d)\n", status); ++ break; ++ } ++ ++ DBG_DUMP_MSG(msg, msg_len, "<<< reply message"); ++ ++ /* handling is different for buffer messages */ ++ switch (msg->h.type) { ++ ++ case MMAL_MSG_TYPE_BUFFER_FROM_HOST: ++ vchi_held_msg_release(&msg_handle); ++ break; ++ ++ case MMAL_MSG_TYPE_EVENT_TO_HOST: ++ event_to_host_cb(instance, msg, msg_len); ++ vchi_held_msg_release(&msg_handle); ++ ++ break; ++ ++ case MMAL_MSG_TYPE_BUFFER_TO_HOST: ++ buffer_to_host_cb(instance, msg, msg_len); ++ vchi_held_msg_release(&msg_handle); ++ break; ++ ++ default: ++ /* messages dependant on header context to complete */ ++ ++ /* todo: the msg.context really ought to be sanity ++ * checked before we just use it, afaict it comes back ++ * and is used raw from the videocore. Perhaps it ++ * should be verified the address lies in the kernel ++ * address space. ++ */ ++ if (msg->h.context == NULL) { ++ pr_err("received message context was null!\n"); ++ vchi_held_msg_release(&msg_handle); ++ break; ++ } ++ ++ /* fill in context values */ ++ msg->h.context->u.sync.msg_handle = msg_handle; ++ msg->h.context->u.sync.msg = msg; ++ msg->h.context->u.sync.msg_len = msg_len; ++ ++ /* todo: should this check (completion_done() ++ * == 1) for no one waiting? or do we need a ++ * flag to tell us the completion has been ++ * interrupted so we can free the message and ++ * its context. This probably also solves the ++ * message arriving after interruption todo ++ * below ++ */ ++ ++ /* complete message so caller knows it happened */ ++ complete(&msg->h.context->u.sync.cmplt); ++ break; ++ } ++ ++ break; ++ ++ case VCHI_CALLBACK_BULK_RECEIVED: ++ bulk_receive_cb(instance, bulk_ctx); ++ break; ++ ++ case VCHI_CALLBACK_BULK_RECEIVE_ABORTED: ++ bulk_abort_cb(instance, bulk_ctx); ++ break; ++ ++ case VCHI_CALLBACK_SERVICE_CLOSED: ++ /* TODO: consider if this requires action if received when ++ * driver is not explicitly closing the service ++ */ ++ break; ++ ++ default: ++ pr_err("Received unhandled message reason %d\n", reason); ++ break; ++ } ++} ++ ++static int send_synchronous_mmal_msg(struct vchiq_mmal_instance *instance, ++ struct mmal_msg *msg, ++ unsigned int payload_len, ++ struct mmal_msg **msg_out, ++ VCHI_HELD_MSG_T *msg_handle_out) ++{ ++ struct mmal_msg_context msg_context; ++ int ret; ++ ++ /* payload size must not cause message to exceed max size */ ++ if (payload_len > ++ (MMAL_MSG_MAX_SIZE - sizeof(struct mmal_msg_header))) { ++ pr_err("payload length %d exceeds max:%d\n", payload_len, ++ (MMAL_MSG_MAX_SIZE - sizeof(struct mmal_msg_header))); ++ return -EINVAL; ++ } ++ ++ init_completion(&msg_context.u.sync.cmplt); ++ ++ msg->h.magic = MMAL_MAGIC; ++ msg->h.context = &msg_context; ++ msg->h.status = 0; ++ ++ DBG_DUMP_MSG(msg, (sizeof(struct mmal_msg_header) + payload_len), ++ ">>> sync message"); ++ ++ vchi_service_use(instance->handle); ++ ++ ret = vchi_msg_queue(instance->handle, ++ msg, ++ sizeof(struct mmal_msg_header) + payload_len, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ vchi_service_release(instance->handle); ++ ++ if (ret) { ++ pr_err("error %d queuing message\n", ret); ++ return ret; ++ } ++ ++ ret = wait_for_completion_timeout(&msg_context.u.sync.cmplt, HZ); ++ if (ret <= 0) { ++ pr_err("error %d waiting for sync completion\n", ret); ++ if (ret == 0) ++ ret = -ETIME; ++ /* todo: what happens if the message arrives after aborting */ ++ return ret; ++ } ++ ++ *msg_out = msg_context.u.sync.msg; ++ *msg_handle_out = msg_context.u.sync.msg_handle; ++ ++ return 0; ++} ++ ++static void dump_port_info(struct vchiq_mmal_port *port) ++{ ++ pr_debug("port handle:0x%x enabled:%d\n", port->handle, port->enabled); ++ ++ pr_debug("buffer minimum num:%d size:%d align:%d\n", ++ port->minimum_buffer.num, ++ port->minimum_buffer.size, port->minimum_buffer.alignment); ++ ++ pr_debug("buffer recommended num:%d size:%d align:%d\n", ++ port->recommended_buffer.num, ++ port->recommended_buffer.size, ++ port->recommended_buffer.alignment); ++ ++ pr_debug("buffer current values num:%d size:%d align:%d\n", ++ port->current_buffer.num, ++ port->current_buffer.size, port->current_buffer.alignment); ++ ++ pr_debug("elementry stream: type:%d encoding:0x%x varient:0x%x\n", ++ port->format.type, ++ port->format.encoding, port->format.encoding_variant); ++ ++ pr_debug(" bitrate:%d flags:0x%x\n", ++ port->format.bitrate, port->format.flags); ++ ++ if (port->format.type == MMAL_ES_TYPE_VIDEO) { ++ pr_debug ++ ("es video format: width:%d height:%d colourspace:0x%x\n", ++ port->es.video.width, port->es.video.height, ++ port->es.video.color_space); ++ ++ pr_debug(" : crop xywh %d,%d,%d,%d\n", ++ port->es.video.crop.x, ++ port->es.video.crop.y, ++ port->es.video.crop.width, port->es.video.crop.height); ++ pr_debug(" : framerate %d/%d aspect %d/%d\n", ++ port->es.video.frame_rate.num, ++ port->es.video.frame_rate.den, ++ port->es.video.par.num, port->es.video.par.den); ++ } ++} ++ ++static void port_to_mmal_msg(struct vchiq_mmal_port *port, struct mmal_port *p) ++{ ++ ++ /* todo do readonly fields need setting at all? */ ++ p->type = port->type; ++ p->index = port->index; ++ p->index_all = 0; ++ p->is_enabled = port->enabled; ++ p->buffer_num_min = port->minimum_buffer.num; ++ p->buffer_size_min = port->minimum_buffer.size; ++ p->buffer_alignment_min = port->minimum_buffer.alignment; ++ p->buffer_num_recommended = port->recommended_buffer.num; ++ p->buffer_size_recommended = port->recommended_buffer.size; ++ ++ /* only three writable fields in a port */ ++ p->buffer_num = port->current_buffer.num; ++ p->buffer_size = port->current_buffer.size; ++ p->userdata = port; ++} ++ ++static int port_info_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ pr_debug("setting port info port %p\n", port); ++ if (!port) ++ return -1; ++ dump_port_info(port); ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_INFO_SET; ++ ++ m.u.port_info_set.component_handle = port->component->handle; ++ m.u.port_info_set.port_type = port->type; ++ m.u.port_info_set.port_index = port->index; ++ ++ port_to_mmal_msg(port, &m.u.port_info_set.port); ++ ++ /* elementry stream format setup */ ++ m.u.port_info_set.format.type = port->format.type; ++ m.u.port_info_set.format.encoding = port->format.encoding; ++ m.u.port_info_set.format.encoding_variant = ++ port->format.encoding_variant; ++ m.u.port_info_set.format.bitrate = port->format.bitrate; ++ m.u.port_info_set.format.flags = port->format.flags; ++ ++ memcpy(&m.u.port_info_set.es, &port->es, ++ sizeof(union mmal_es_specific_format)); ++ ++ m.u.port_info_set.format.extradata_size = port->format.extradata_size; ++ memcpy(rmsg->u.port_info_set.extradata, port->format.extradata, ++ port->format.extradata_size); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_info_set), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_INFO_SET) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ /* return operation status */ ++ ret = -rmsg->u.port_info_get_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d\n", __func__, ret, ++ port->component->handle, port->handle); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++ ++} ++ ++/* use port info get message to retrive port information */ ++static int port_info_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ /* port info time */ ++ m.h.type = MMAL_MSG_TYPE_PORT_INFO_GET; ++ m.u.port_info_get.component_handle = port->component->handle; ++ m.u.port_info_get.port_type = port->type; ++ m.u.port_info_get.index = port->index; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_info_get), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_INFO_GET) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ /* return operation status */ ++ ret = -rmsg->u.port_info_get_reply.status; ++ if (ret != MMAL_MSG_STATUS_SUCCESS) ++ goto release_msg; ++ ++ if (rmsg->u.port_info_get_reply.port.is_enabled == 0) ++ port->enabled = false; ++ else ++ port->enabled = true; ++ ++ /* copy the values out of the message */ ++ port->handle = rmsg->u.port_info_get_reply.port_handle; ++ ++ /* port type and index cached to use on port info set becuase ++ * it does not use a port handle ++ */ ++ port->type = rmsg->u.port_info_get_reply.port_type; ++ port->index = rmsg->u.port_info_get_reply.port_index; ++ ++ port->minimum_buffer.num = ++ rmsg->u.port_info_get_reply.port.buffer_num_min; ++ port->minimum_buffer.size = ++ rmsg->u.port_info_get_reply.port.buffer_size_min; ++ port->minimum_buffer.alignment = ++ rmsg->u.port_info_get_reply.port.buffer_alignment_min; ++ ++ port->recommended_buffer.alignment = ++ rmsg->u.port_info_get_reply.port.buffer_alignment_min; ++ port->recommended_buffer.num = ++ rmsg->u.port_info_get_reply.port.buffer_num_recommended; ++ ++ port->current_buffer.num = rmsg->u.port_info_get_reply.port.buffer_num; ++ port->current_buffer.size = ++ rmsg->u.port_info_get_reply.port.buffer_size; ++ ++ /* stream format */ ++ port->format.type = rmsg->u.port_info_get_reply.format.type; ++ port->format.encoding = rmsg->u.port_info_get_reply.format.encoding; ++ port->format.encoding_variant = ++ rmsg->u.port_info_get_reply.format.encoding_variant; ++ port->format.bitrate = rmsg->u.port_info_get_reply.format.bitrate; ++ port->format.flags = rmsg->u.port_info_get_reply.format.flags; ++ ++ /* elementry stream format */ ++ memcpy(&port->es, ++ &rmsg->u.port_info_get_reply.es, ++ sizeof(union mmal_es_specific_format)); ++ port->format.es = &port->es; ++ ++ port->format.extradata_size = ++ rmsg->u.port_info_get_reply.format.extradata_size; ++ memcpy(port->format.extradata, ++ rmsg->u.port_info_get_reply.extradata, ++ port->format.extradata_size); ++ ++ pr_debug("received port info\n"); ++ dump_port_info(port); ++ ++release_msg: ++ ++ pr_debug("%s:result:%d component:0x%x port:%d\n", ++ __func__, ret, port->component->handle, port->handle); ++ ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* create comonent on vc */ ++static int create_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component, ++ const char *name) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ /* build component create message */ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_CREATE; ++ m.u.component_create.client_component = component; ++ strncpy(m.u.component_create.name, name, ++ sizeof(m.u.component_create.name)); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_create), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_create_reply.status; ++ if (ret != MMAL_MSG_STATUS_SUCCESS) ++ goto release_msg; ++ ++ /* a valid component response received */ ++ component->handle = rmsg->u.component_create_reply.component_handle; ++ component->inputs = rmsg->u.component_create_reply.input_num; ++ component->outputs = rmsg->u.component_create_reply.output_num; ++ component->clocks = rmsg->u.component_create_reply.clock_num; ++ ++ pr_debug("Component handle:0x%x in:%d out:%d clock:%d\n", ++ component->handle, ++ component->inputs, component->outputs, component->clocks); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* destroys a component on vc */ ++static int destroy_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_DESTROY; ++ m.u.component_destroy.component_handle = component->handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_destroy), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_destroy_reply.status; ++ ++release_msg: ++ ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* enable a component on vc */ ++static int enable_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_ENABLE; ++ m.u.component_enable.component_handle = component->handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_enable), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_enable_reply.status; ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* disable a component on vc */ ++static int disable_component(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_COMPONENT_DISABLE; ++ m.u.component_disable.component_handle = component->handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.component_disable), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.component_disable_reply.status; ++ ++release_msg: ++ ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* get version of mmal implementation */ ++static int get_version(struct vchiq_mmal_instance *instance, ++ u32 *major_out, u32 *minor_out) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_GET_VERSION; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.version), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != m.h.type) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ *major_out = rmsg->u.version.major; ++ *minor_out = rmsg->u.version.minor; ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* do a port action with a port as a parameter */ ++static int port_action_port(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ enum mmal_msg_port_action_type action_type) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_ACTION; ++ m.u.port_action_port.component_handle = port->component->handle; ++ m.u.port_action_port.port_handle = port->handle; ++ m.u.port_action_port.action = action_type; ++ ++ port_to_mmal_msg(port, &m.u.port_action_port.port); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_action_port), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_ACTION) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_action_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d)\n", ++ __func__, ++ ret, port->component->handle, port->handle, ++ port_action_type_names[action_type], action_type); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* do a port action with handles as parameters */ ++static int port_action_handle(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ enum mmal_msg_port_action_type action_type, ++ u32 connect_component_handle, ++ u32 connect_port_handle) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_ACTION; ++ ++ m.u.port_action_handle.component_handle = port->component->handle; ++ m.u.port_action_handle.port_handle = port->handle; ++ m.u.port_action_handle.action = action_type; ++ ++ m.u.port_action_handle.connect_component_handle = ++ connect_component_handle; ++ m.u.port_action_handle.connect_port_handle = connect_port_handle; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(m.u.port_action_handle), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_ACTION) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_action_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d action:%s(%d)" \ ++ " connect component:0x%x connect port:%d\n", ++ __func__, ++ ret, port->component->handle, port->handle, ++ port_action_type_names[action_type], ++ action_type, connect_component_handle, connect_port_handle); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++static int port_parameter_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter_id, void *value, u32 value_size) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_PARAMETER_SET; ++ ++ m.u.port_parameter_set.component_handle = port->component->handle; ++ m.u.port_parameter_set.port_handle = port->handle; ++ m.u.port_parameter_set.id = parameter_id; ++ m.u.port_parameter_set.size = (2 * sizeof(u32)) + value_size; ++ memcpy(&m.u.port_parameter_set.value, value, value_size); ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ (4 * sizeof(u32)) + value_size, ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_PARAMETER_SET) { ++ /* got an unexpected message type in reply */ ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_parameter_set_reply.status; ++ ++ pr_debug("%s:result:%d component:0x%x port:%d parameter:%d\n", ++ __func__, ++ ret, port->component->handle, port->handle, parameter_id); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++static int port_parameter_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter_id, void *value, u32 *value_size) ++{ ++ int ret; ++ struct mmal_msg m; ++ struct mmal_msg *rmsg; ++ VCHI_HELD_MSG_T rmsg_handle; ++ ++ m.h.type = MMAL_MSG_TYPE_PORT_PARAMETER_GET; ++ ++ m.u.port_parameter_get.component_handle = port->component->handle; ++ m.u.port_parameter_get.port_handle = port->handle; ++ m.u.port_parameter_get.id = parameter_id; ++ m.u.port_parameter_get.size = (2 * sizeof(u32)) + *value_size; ++ ++ ret = send_synchronous_mmal_msg(instance, &m, ++ sizeof(struct ++ mmal_msg_port_parameter_get), ++ &rmsg, &rmsg_handle); ++ if (ret) ++ return ret; ++ ++ if (rmsg->h.type != MMAL_MSG_TYPE_PORT_PARAMETER_GET) { ++ /* got an unexpected message type in reply */ ++ pr_err("Incorrect reply type %d\n", rmsg->h.type); ++ ret = -EINVAL; ++ goto release_msg; ++ } ++ ++ ret = -rmsg->u.port_parameter_get_reply.status; ++ if (ret) { ++ /* Copy only as much as we have space for ++ * but report true size of parameter ++ */ ++ memcpy(value, &rmsg->u.port_parameter_get_reply.value, ++ *value_size); ++ *value_size = rmsg->u.port_parameter_get_reply.size; ++ } else ++ memcpy(value, &rmsg->u.port_parameter_get_reply.value, ++ rmsg->u.port_parameter_get_reply.size); ++ ++ pr_info("%s:result:%d component:0x%x port:%d parameter:%d\n", __func__, ++ ret, port->component->handle, port->handle, parameter_id); ++ ++release_msg: ++ vchi_held_msg_release(&rmsg_handle); ++ ++ return ret; ++} ++ ++/* disables a port and drains buffers from it */ ++static int port_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ struct list_head *q, *buf_head; ++ unsigned long flags = 0; ++ ++ if (!port->enabled) ++ return 0; ++ ++ port->enabled = false; ++ ++ ret = port_action_port(instance, port, ++ MMAL_MSG_PORT_ACTION_TYPE_DISABLE); ++ if (ret == 0) { ++ ++ /* drain all queued buffers on port */ ++ spin_lock_irqsave(&port->slock, flags); ++ ++ list_for_each_safe(buf_head, q, &port->buffers) { ++ struct mmal_buffer *mmalbuf; ++ mmalbuf = list_entry(buf_head, struct mmal_buffer, ++ list); ++ list_del(buf_head); ++ if (port->buffer_cb) ++ port->buffer_cb(instance, ++ port, 0, mmalbuf, 0, 0, ++ MMAL_TIME_UNKNOWN, ++ MMAL_TIME_UNKNOWN); ++ } ++ ++ spin_unlock_irqrestore(&port->slock, flags); ++ ++ ret = port_info_get(instance, port); ++ } ++ ++ return ret; ++} ++ ++/* enable a port */ ++static int port_enable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ unsigned int hdr_count; ++ struct list_head *buf_head; ++ int ret; ++ ++ if (port->enabled) ++ return 0; ++ ++ /* ensure there are enough buffers queued to cover the buffer headers */ ++ if (port->buffer_cb != NULL) { ++ hdr_count = 0; ++ list_for_each(buf_head, &port->buffers) { ++ hdr_count++; ++ } ++ if (hdr_count < port->current_buffer.num) ++ return -ENOSPC; ++ } ++ ++ ret = port_action_port(instance, port, ++ MMAL_MSG_PORT_ACTION_TYPE_ENABLE); ++ if (ret) ++ goto done; ++ ++ port->enabled = true; ++ ++ if (port->buffer_cb) { ++ /* send buffer headers to videocore */ ++ hdr_count = 1; ++ list_for_each(buf_head, &port->buffers) { ++ struct mmal_buffer *mmalbuf; ++ mmalbuf = list_entry(buf_head, struct mmal_buffer, ++ list); ++ ret = buffer_from_host(instance, port, mmalbuf); ++ if (ret) ++ goto done; ++ ++ hdr_count++; ++ if (hdr_count > port->current_buffer.num) ++ break; ++ } ++ } ++ ++ ret = port_info_get(instance, port); ++ ++done: ++ return ret; ++} ++ ++/* ------------------------------------------------------------------ ++ * Exported API ++ *------------------------------------------------------------------*/ ++ ++int vchiq_mmal_port_set_format(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = port_info_set(instance, port); ++ if (ret) ++ goto release_unlock; ++ ++ /* read what has actually been set */ ++ ret = port_info_get(instance, port); ++ ++release_unlock: ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++ ++} ++ ++int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, void *value, u32 value_size) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = port_parameter_set(instance, port, parameter, value, value_size); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_port_parameter_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, void *value, u32 *value_size) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = port_parameter_get(instance, port, parameter, value, value_size); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* enable a port ++ * ++ * enables a port and queues buffers for satisfying callbacks if we ++ * provide a callback handler ++ */ ++int vchiq_mmal_port_enable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ vchiq_mmal_buffer_cb buffer_cb) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ /* already enabled - noop */ ++ if (port->enabled) { ++ ret = 0; ++ goto unlock; ++ } ++ ++ port->buffer_cb = buffer_cb; ++ ++ ret = port_enable(instance, port); ++ ++unlock: ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_port_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (!port->enabled) { ++ mutex_unlock(&instance->vchiq_mutex); ++ return 0; ++ } ++ ++ ret = port_disable(instance, port); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ports will be connected in a tunneled manner so data buffers ++ * are not handled by client. ++ */ ++int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *src, ++ struct vchiq_mmal_port *dst) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ /* disconnect ports if connected */ ++ if (src->connected != NULL) { ++ ret = port_disable(instance, src); ++ if (ret) { ++ pr_err("failed disabling src port(%d)\n", ret); ++ goto release_unlock; ++ } ++ ++ /* do not need to disable the destination port as they ++ * are connected and it is done automatically ++ */ ++ ++ ret = port_action_handle(instance, src, ++ MMAL_MSG_PORT_ACTION_TYPE_DISCONNECT, ++ src->connected->component->handle, ++ src->connected->handle); ++ if (ret < 0) { ++ pr_err("failed disconnecting src port\n"); ++ goto release_unlock; ++ } ++ src->connected->enabled = false; ++ src->connected = NULL; ++ } ++ ++ if (dst == NULL) { ++ /* do not make new connection */ ++ ret = 0; ++ pr_debug("not making new connection\n"); ++ goto release_unlock; ++ } ++ ++ /* copy src port format to dst */ ++ dst->format.encoding = src->format.encoding; ++ dst->es.video.width = src->es.video.width; ++ dst->es.video.height = src->es.video.height; ++ dst->es.video.crop.x = src->es.video.crop.x; ++ dst->es.video.crop.y = src->es.video.crop.y; ++ dst->es.video.crop.width = src->es.video.crop.width; ++ dst->es.video.crop.height = src->es.video.crop.height; ++ dst->es.video.frame_rate.num = src->es.video.frame_rate.num; ++ dst->es.video.frame_rate.den = src->es.video.frame_rate.den; ++ ++ /* set new format */ ++ ret = port_info_set(instance, dst); ++ if (ret) { ++ pr_debug("setting port info failed\n"); ++ goto release_unlock; ++ } ++ ++ /* read what has actually been set */ ++ ret = port_info_get(instance, dst); ++ if (ret) { ++ pr_debug("read back port info failed\n"); ++ goto release_unlock; ++ } ++ ++ /* connect two ports together */ ++ ret = port_action_handle(instance, src, ++ MMAL_MSG_PORT_ACTION_TYPE_CONNECT, ++ dst->component->handle, dst->handle); ++ if (ret < 0) { ++ pr_debug("connecting port %d:%d to %d:%d failed\n", ++ src->component->handle, src->handle, ++ dst->component->handle, dst->handle); ++ goto release_unlock; ++ } ++ src->connected = dst; ++ ++release_unlock: ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ struct mmal_buffer *buffer) ++{ ++ unsigned long flags = 0; ++ ++ spin_lock_irqsave(&port->slock, flags); ++ list_add_tail(&buffer->list, &port->buffers); ++ spin_unlock_irqrestore(&port->slock, flags); ++ ++ /* the port previously underflowed because it was missing a ++ * mmal_buffer which has just been added, submit that buffer ++ * to the mmal service. ++ */ ++ if (port->buffer_underflow) { ++ port_buffer_from_host(instance, port); ++ port->buffer_underflow--; ++ } ++ ++ return 0; ++} ++ ++/* Initialise a mmal component and its ports ++ * ++ */ ++int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, ++ const char *name, ++ struct vchiq_mmal_component **component_out) ++{ ++ int ret; ++ int idx; /* port index */ ++ struct vchiq_mmal_component *component; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (instance->component_idx == VCHIQ_MMAL_MAX_COMPONENTS) { ++ ret = -EINVAL; /* todo is this correct error? */ ++ goto unlock; ++ } ++ ++ component = &instance->component[instance->component_idx]; ++ ++ ret = create_component(instance, component, name); ++ if (ret < 0) ++ goto unlock; ++ ++ /* ports info needs gathering */ ++ component->control.type = MMAL_PORT_TYPE_CONTROL; ++ component->control.index = 0; ++ component->control.component = component; ++ spin_lock_init(&component->control.slock); ++ INIT_LIST_HEAD(&component->control.buffers); ++ ret = port_info_get(instance, &component->control); ++ if (ret < 0) ++ goto release_component; ++ ++ for (idx = 0; idx < component->inputs; idx++) { ++ component->input[idx].type = MMAL_PORT_TYPE_INPUT; ++ component->input[idx].index = idx; ++ component->input[idx].component = component; ++ spin_lock_init(&component->input[idx].slock); ++ INIT_LIST_HEAD(&component->input[idx].buffers); ++ ret = port_info_get(instance, &component->input[idx]); ++ if (ret < 0) ++ goto release_component; ++ } ++ ++ for (idx = 0; idx < component->outputs; idx++) { ++ component->output[idx].type = MMAL_PORT_TYPE_OUTPUT; ++ component->output[idx].index = idx; ++ component->output[idx].component = component; ++ spin_lock_init(&component->output[idx].slock); ++ INIT_LIST_HEAD(&component->output[idx].buffers); ++ ret = port_info_get(instance, &component->output[idx]); ++ if (ret < 0) ++ goto release_component; ++ } ++ ++ for (idx = 0; idx < component->clocks; idx++) { ++ component->clock[idx].type = MMAL_PORT_TYPE_CLOCK; ++ component->clock[idx].index = idx; ++ component->clock[idx].component = component; ++ spin_lock_init(&component->clock[idx].slock); ++ INIT_LIST_HEAD(&component->clock[idx].buffers); ++ ret = port_info_get(instance, &component->clock[idx]); ++ if (ret < 0) ++ goto release_component; ++ } ++ ++ instance->component_idx++; ++ ++ *component_out = component; ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return 0; ++ ++release_component: ++ destroy_component(instance, component); ++unlock: ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ++ * cause a mmal component to be destroyed ++ */ ++int vchiq_mmal_component_finalise(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (component->enabled) ++ ret = disable_component(instance, component); ++ ++ ret = destroy_component(instance, component); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ++ * cause a mmal component to be enabled ++ */ ++int vchiq_mmal_component_enable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (component->enabled) { ++ mutex_unlock(&instance->vchiq_mutex); ++ return 0; ++ } ++ ++ ret = enable_component(instance, component); ++ if (ret == 0) ++ component->enabled = true; ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++/* ++ * cause a mmal component to be enabled ++ */ ++int vchiq_mmal_component_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ if (!component->enabled) { ++ mutex_unlock(&instance->vchiq_mutex); ++ return 0; ++ } ++ ++ ret = disable_component(instance, component); ++ if (ret == 0) ++ component->enabled = false; ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_version(struct vchiq_mmal_instance *instance, ++ u32 *major_out, u32 *minor_out) ++{ ++ int ret; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ ret = get_version(instance, major_out, minor_out); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ return ret; ++} ++ ++int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance) ++{ ++ int status = 0; ++ ++ if (instance == NULL) ++ return -EINVAL; ++ ++ if (mutex_lock_interruptible(&instance->vchiq_mutex)) ++ return -EINTR; ++ ++ vchi_service_use(instance->handle); ++ ++ status = vchi_service_close(instance->handle); ++ if (status != 0) ++ pr_err("mmal-vchiq: VCHIQ close failed"); ++ ++ mutex_unlock(&instance->vchiq_mutex); ++ ++ vfree(instance->bulk_scratch); ++ ++ kfree(instance); ++ ++ return status; ++} ++ ++int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) ++{ ++ int status; ++ struct vchiq_mmal_instance *instance; ++ static VCHI_CONNECTION_T *vchi_connection; ++ static VCHI_INSTANCE_T vchi_instance; ++ SERVICE_CREATION_T params = { ++ VCHI_VERSION_EX(VC_MMAL_VER, VC_MMAL_MIN_VER), ++ VC_MMAL_SERVER_NAME, ++ vchi_connection, ++ 0, /* rx fifo size (unused) */ ++ 0, /* tx fifo size (unused) */ ++ service_callback, ++ NULL, /* service callback parameter */ ++ 1, /* unaligned bulk receives */ ++ 1, /* unaligned bulk transmits */ ++ 0 /* want crc check on bulk transfers */ ++ }; ++ ++ /* compile time checks to ensure structure size as they are ++ * directly (de)serialised from memory. ++ */ ++ ++ /* ensure the header structure has packed to the correct size */ ++ BUILD_BUG_ON(sizeof(struct mmal_msg_header) != 24); ++ ++ /* ensure message structure does not exceed maximum length */ ++ BUILD_BUG_ON(sizeof(struct mmal_msg) > MMAL_MSG_MAX_SIZE); ++ ++ /* mmal port struct is correct size */ ++ BUILD_BUG_ON(sizeof(struct mmal_port) != 64); ++ ++ /* create a vchi instance */ ++ status = vchi_initialise(&vchi_instance); ++ if (status) { ++ pr_err("Failed to initialise VCHI instance (status=%d)\n", ++ status); ++ return -EIO; ++ } ++ ++ status = vchi_connect(NULL, 0, vchi_instance); ++ if (status) { ++ pr_err("Failed to connect VCHI instance (status=%d)\n", status); ++ return -EIO; ++ } ++ ++ instance = kmalloc(sizeof(*instance), GFP_KERNEL); ++ memset(instance, 0, sizeof(*instance)); ++ ++ mutex_init(&instance->vchiq_mutex); ++ mutex_init(&instance->bulk_mutex); ++ ++ instance->bulk_scratch = vmalloc(PAGE_SIZE); ++ ++ params.callback_param = instance; ++ ++ status = vchi_service_open(vchi_instance, ¶ms, &instance->handle); ++ if (status) { ++ pr_err("Failed to open VCHI service connection (status=%d)\n", ++ status); ++ goto err_close_services; ++ } ++ ++ vchi_service_release(instance->handle); ++ ++ *out_instance = instance; ++ ++ return 0; ++ ++err_close_services: ++ ++ vchi_service_close(instance->handle); ++ vfree(instance->bulk_scratch); ++ kfree(instance); ++ return -ENODEV; ++} +diff --git a/drivers/media/platform/bcm2835/mmal-vchiq.h b/drivers/media/platform/bcm2835/mmal-vchiq.h +new file mode 100644 +index 0000000..9d1d11e +--- /dev/null ++++ b/drivers/media/platform/bcm2835/mmal-vchiq.h +@@ -0,0 +1,178 @@ ++/* ++ * Broadcom BM2835 V4L2 driver ++ * ++ * Copyright © 2013 Raspberry Pi (Trading) Ltd. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Authors: Vincent Sanders ++ * Dave Stevenson ++ * Simon Mellor ++ * Luke Diamand ++ * ++ * MMAL interface to VCHIQ message passing ++ */ ++ ++#ifndef MMAL_VCHIQ_H ++#define MMAL_VCHIQ_H ++ ++#include "mmal-msg-format.h" ++ ++#define MAX_PORT_COUNT 4 ++ ++/* Maximum size of the format extradata. */ ++#define MMAL_FORMAT_EXTRADATA_MAX_SIZE 128 ++ ++struct vchiq_mmal_instance; ++ ++enum vchiq_mmal_es_type { ++ MMAL_ES_TYPE_UNKNOWN, /**< Unknown elementary stream type */ ++ MMAL_ES_TYPE_CONTROL, /**< Elementary stream of control commands */ ++ MMAL_ES_TYPE_AUDIO, /**< Audio elementary stream */ ++ MMAL_ES_TYPE_VIDEO, /**< Video elementary stream */ ++ MMAL_ES_TYPE_SUBPICTURE /**< Sub-picture elementary stream */ ++}; ++ ++/* rectangle, used lots so it gets its own struct */ ++struct vchiq_mmal_rect { ++ s32 x; ++ s32 y; ++ s32 width; ++ s32 height; ++}; ++ ++struct vchiq_mmal_port_buffer { ++ unsigned int num; /* number of buffers */ ++ u32 size; /* size of buffers */ ++ u32 alignment; /* alignment of buffers */ ++}; ++ ++struct vchiq_mmal_port; ++ ++typedef void (*vchiq_mmal_buffer_cb)( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ int status, struct mmal_buffer *buffer, ++ unsigned long length, u32 mmal_flags, s64 dts, s64 pts); ++ ++struct vchiq_mmal_port { ++ bool enabled; ++ u32 handle; ++ u32 type; /* port type, cached to use on port info set */ ++ u32 index; /* port index, cached to use on port info set */ ++ ++ /* component port belongs to, allows simple deref */ ++ struct vchiq_mmal_component *component; ++ ++ struct vchiq_mmal_port *connected; /* port conencted to */ ++ ++ /* buffer info */ ++ struct vchiq_mmal_port_buffer minimum_buffer; ++ struct vchiq_mmal_port_buffer recommended_buffer; ++ struct vchiq_mmal_port_buffer current_buffer; ++ ++ /* stream format */ ++ struct mmal_es_format format; ++ /* elementry stream format */ ++ union mmal_es_specific_format es; ++ ++ /* data buffers to fill */ ++ struct list_head buffers; ++ /* lock to serialise adding and removing buffers from list */ ++ spinlock_t slock; ++ /* count of how many buffer header refils have failed because ++ * there was no buffer to satisfy them ++ */ ++ int buffer_underflow; ++ /* callback on buffer completion */ ++ vchiq_mmal_buffer_cb buffer_cb; ++ /* callback context */ ++ void *cb_ctx; ++}; ++ ++struct vchiq_mmal_component { ++ bool enabled; ++ u32 handle; /* VideoCore handle for component */ ++ u32 inputs; /* Number of input ports */ ++ u32 outputs; /* Number of output ports */ ++ u32 clocks; /* Number of clock ports */ ++ struct vchiq_mmal_port control; /* control port */ ++ struct vchiq_mmal_port input[MAX_PORT_COUNT]; /* input ports */ ++ struct vchiq_mmal_port output[MAX_PORT_COUNT]; /* output ports */ ++ struct vchiq_mmal_port clock[MAX_PORT_COUNT]; /* clock ports */ ++}; ++ ++ ++int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance); ++int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance); ++ ++/* Initialise a mmal component and its ports ++* ++*/ ++int vchiq_mmal_component_init( ++ struct vchiq_mmal_instance *instance, ++ const char *name, ++ struct vchiq_mmal_component **component_out); ++ ++int vchiq_mmal_component_finalise( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component); ++ ++int vchiq_mmal_component_enable( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component); ++ ++int vchiq_mmal_component_disable( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_component *component); ++ ++ ++ ++/* enable a mmal port ++ * ++ * enables a port and if a buffer callback provided enque buffer ++ * headers as apropriate for the port. ++ */ ++int vchiq_mmal_port_enable( ++ struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ vchiq_mmal_buffer_cb buffer_cb); ++ ++/* disable a port ++ * ++ * disable a port will dequeue any pending buffers ++ */ ++int vchiq_mmal_port_disable(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port); ++ ++ ++int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, ++ void *value, ++ u32 value_size); ++ ++int vchiq_mmal_port_parameter_get(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ u32 parameter, ++ void *value, ++ u32 *value_size); ++ ++int vchiq_mmal_port_set_format(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port); ++ ++int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *src, ++ struct vchiq_mmal_port *dst); ++ ++int vchiq_mmal_version(struct vchiq_mmal_instance *instance, ++ u32 *major_out, ++ u32 *minor_out); ++ ++int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, ++ struct vchiq_mmal_port *port, ++ struct mmal_buffer *buf); ++ ++#endif /* MMAL_VCHIQ_H */ +-- +1.8.5.1 + + +From a179a6fd38ef256bad3b372c14b272d63eb07203 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 9 Dec 2013 10:58:01 +0000 +Subject: [PATCH 69/91] V4L2: Fix EV values. Add manual shutter speed control + +V4L2 EV values should be in units of 1/1000. Corrected. +Add support for V4L2_CID_EXPOSURE_ABSOLUTE which should +give manual shutter control. Requires manual exposure mode +to be selected first. + +Signed-off-by: Dave Stevenson +--- + drivers/media/platform/bcm2835/bcm2835-camera.h | 4 +- + drivers/media/platform/bcm2835/controls.c | 94 ++++++++++++++++++------ + drivers/media/platform/bcm2835/mmal-parameters.h | 1 + + 3 files changed, 76 insertions(+), 23 deletions(-) + +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.h b/drivers/media/platform/bcm2835/bcm2835-camera.h +index 883eab7..5640492 100644 +--- a/drivers/media/platform/bcm2835/bcm2835-camera.h ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.h +@@ -15,7 +15,7 @@ + * core driver device + */ + +-#define V4L2_CTRL_COUNT 18 /* number of v4l controls */ ++#define V4L2_CTRL_COUNT 19 /* number of v4l controls */ + + enum { + MMAL_COMPONENT_CAMERA = 0, +@@ -51,6 +51,8 @@ struct bm2835_mmal_dev { + struct mmal_colourfx colourfx; + int hflip; + int vflip; ++ enum mmal_parameter_exposuremode exposure_mode; ++ unsigned int manual_shutter_speed; + + /* allocated mmal instance and components */ + struct vchiq_mmal_instance *instance; +diff --git a/drivers/media/platform/bcm2835/controls.c b/drivers/media/platform/bcm2835/controls.c +index d1408e5..481d1f6 100644 +--- a/drivers/media/platform/bcm2835/controls.c ++++ b/drivers/media/platform/bcm2835/controls.c +@@ -30,11 +30,23 @@ + #include "mmal-parameters.h" + #include "bcm2835-camera.h" + +-/* The supported V4L2_CID_AUTO_EXPOSURE_BIAS values are from -24 to +24. +- * These are in 1/6th increments so the effective range is -4.0EV to +4.0EV. ++/* The supported V4L2_CID_AUTO_EXPOSURE_BIAS values are from -4.0 to +4.0. ++ * MMAL values are in 1/6th increments so the MMAL range is -24 to +24. ++ * V4L2 docs say value "is expressed in terms of EV, drivers should interpret ++ * the values as 0.001 EV units, where the value 1000 stands for +1 EV." ++ * V4L2 is limited to a max of 32 values in a menu, so count in 1/3rds from ++ * -4 to +4 + */ + static const s64 ev_bias_qmenu[] = { +- -24, -21, -18, -15, -12, -9, -6, -3, 0, 3, 6, 9, 12, 15, 18, 21, 24 ++ -4000, -3667, -3333, ++ -3000, -2667, -2333, ++ -2000, -1667, -1333, ++ -1000, -667, -333, ++ 0, 333, 667, ++ 1000, 1333, 1667, ++ 2000, 2333, 2667, ++ 3000, 3333, 3667, ++ 4000 + }; + + /* Supported ISO values +@@ -166,6 +178,22 @@ static int ctrl_set_value(struct bm2835_mmal_dev *dev, + &u32_value, sizeof(u32_value)); + } + ++static int ctrl_set_value_ev(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ s32 s32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ s32_value = (ctrl->val-12)*2; /* Convert from index to 1/6ths */ ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &s32_value, sizeof(s32_value)); ++} ++ + static int ctrl_set_rotate(struct bm2835_mmal_dev *dev, + struct v4l2_ctrl *ctrl, + const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) +@@ -245,34 +273,50 @@ static int ctrl_set_exposure(struct bm2835_mmal_dev *dev, + struct v4l2_ctrl *ctrl, + const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) + { +- u32 u32_value; ++ enum mmal_parameter_exposuremode exp_mode = dev->exposure_mode; ++ u32 shutter_speed = 0; + struct vchiq_mmal_port *control; ++ int ret = 0; + + control = &dev->component[MMAL_COMPONENT_CAMERA]->control; + +- switch (ctrl->val) { +- case V4L2_EXPOSURE_AUTO: +- u32_value = MMAL_PARAM_EXPOSUREMODE_AUTO; +- break; ++ if (mmal_ctrl->mmal_id == MMAL_PARAMETER_SHUTTER_SPEED) { ++ /* V4L2 is in 100usec increments. ++ * MMAL is 1usec. ++ */ ++ dev->manual_shutter_speed = ctrl->val * 100; ++ } else if (mmal_ctrl->mmal_id == MMAL_PARAMETER_EXPOSURE_MODE) { ++ switch (ctrl->val) { ++ case V4L2_EXPOSURE_AUTO: ++ exp_mode = MMAL_PARAM_EXPOSUREMODE_AUTO; ++ break; + +- case V4L2_EXPOSURE_MANUAL: +- u32_value = MMAL_PARAM_EXPOSUREMODE_OFF; +- break; ++ case V4L2_EXPOSURE_MANUAL: ++ exp_mode = MMAL_PARAM_EXPOSUREMODE_OFF; ++ break; + +- case V4L2_EXPOSURE_SHUTTER_PRIORITY: +- u32_value = MMAL_PARAM_EXPOSUREMODE_SPORTS; +- break; ++ case V4L2_EXPOSURE_SHUTTER_PRIORITY: ++ exp_mode = MMAL_PARAM_EXPOSUREMODE_SPORTS; ++ break; + +- case V4L2_EXPOSURE_APERTURE_PRIORITY: +- u32_value = MMAL_PARAM_EXPOSUREMODE_NIGHT; +- break; ++ case V4L2_EXPOSURE_APERTURE_PRIORITY: ++ exp_mode = MMAL_PARAM_EXPOSUREMODE_NIGHT; ++ break; + ++ } ++ dev->exposure_mode = exp_mode; + } + +- /* todo: what about the other ten modes there are MMAL parameters for */ +- return vchiq_mmal_port_parameter_set(dev->instance, control, +- mmal_ctrl->mmal_id, +- &u32_value, sizeof(u32_value)); ++ if (dev->exposure_mode == MMAL_PARAM_EXPOSUREMODE_OFF) ++ shutter_speed = dev->manual_shutter_speed; ++ ++ ret = vchiq_mmal_port_parameter_set(dev->instance, control, ++ MMAL_PARAMETER_SHUTTER_SPEED, ++ &shutter_speed, sizeof(shutter_speed)); ++ ret += vchiq_mmal_port_parameter_set(dev->instance, control, ++ MMAL_PARAMETER_EXPOSURE_MODE, ++ &exp_mode, sizeof(u32)); ++ return ret; + } + + static int ctrl_set_metering_mode(struct bm2835_mmal_dev *dev, +@@ -578,10 +622,16 @@ static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) + }, + */ + { ++ V4L2_CID_EXPOSURE_ABSOLUTE, MMAL_CONTROL_TYPE_STD, ++ /* Units of 100usecs */ ++ 1, 1*1000*10, 100*10, 1, NULL, ++ MMAL_PARAMETER_SHUTTER_SPEED, &ctrl_set_exposure ++ }, ++ { + V4L2_CID_AUTO_EXPOSURE_BIAS, MMAL_CONTROL_TYPE_INT_MENU, + 0, ARRAY_SIZE(ev_bias_qmenu) - 1, + (ARRAY_SIZE(ev_bias_qmenu)+1)/2 - 1, 0, ev_bias_qmenu, +- MMAL_PARAMETER_EXPOSURE_COMP, &ctrl_set_value ++ MMAL_PARAMETER_EXPOSURE_COMP, &ctrl_set_value_ev + }, + { + V4L2_CID_EXPOSURE_METERING, +diff --git a/drivers/media/platform/bcm2835/mmal-parameters.h b/drivers/media/platform/bcm2835/mmal-parameters.h +index c611b58..d8aace5 100644 +--- a/drivers/media/platform/bcm2835/mmal-parameters.h ++++ b/drivers/media/platform/bcm2835/mmal-parameters.h +@@ -161,6 +161,7 @@ enum mmal_parameter_camera_type { + MMAL_PARAMETER_SW_SHARPEN_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ + MMAL_PARAMETER_FLASH_REQUIRED, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ + MMAL_PARAMETER_SW_SATURATION_DISABLE, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_SHUTTER_SPEED /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ + }; + + enum mmal_parameter_camera_config_timestamp_mode { +-- +1.8.5.1 + + +From c1924621838eefe313c98f58df1659a9b5548afa Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 9 Dec 2013 11:01:53 +0000 +Subject: [PATCH 70/91] V4L2: Correct JPEG Q-factor range + +Should be 1-100, not 0-100 + +Signed-off-by: Dave Stevenson +--- + drivers/media/platform/bcm2835/controls.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/media/platform/bcm2835/controls.c b/drivers/media/platform/bcm2835/controls.c +index 481d1f6..c2e4c64 100644 +--- a/drivers/media/platform/bcm2835/controls.c ++++ b/drivers/media/platform/bcm2835/controls.c +@@ -538,7 +538,7 @@ static int ctrl_set_bitrate_mode(struct bm2835_mmal_dev *dev, + return 0; + } + +-static int ctrl_set_q_factor(struct bm2835_mmal_dev *dev, ++static int ctrl_set_image_encode_output(struct bm2835_mmal_dev *dev, + struct v4l2_ctrl *ctrl, + const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) + { +@@ -683,9 +683,9 @@ static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) + }, + { + V4L2_CID_JPEG_COMPRESSION_QUALITY, MMAL_CONTROL_TYPE_STD, +- 0, 100, ++ 1, 100, + 30, 1, NULL, +- MMAL_PARAMETER_JPEG_Q_FACTOR, &ctrl_set_q_factor ++ MMAL_PARAMETER_JPEG_Q_FACTOR, &ctrl_set_image_encode_output + }, + }; + +-- +1.8.5.1 + + +From 3784a0fe579d50598135f9b219fce838ac3e5817 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 9 Dec 2013 11:05:24 +0000 +Subject: [PATCH 71/91] V4L2: Fix issue of driver jamming if STREAMON failed. + +Fix issue where the driver was left in a partially enabled +state if STREAMON failed, and would then reject many IOCTLs +as it thought it was streaming. + +Signed-off-by: Dave Stevenson +--- + drivers/media/platform/bcm2835/bcm2835-camera.c | 10 +++++++++- + 1 file changed, 9 insertions(+), 1 deletion(-) + +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c +index 47fe45d..2743074 100644 +--- a/drivers/media/platform/bcm2835/bcm2835-camera.c ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.c +@@ -425,7 +425,15 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) + vchiq_mmal_port_enable(dev->instance, dev->capture.port, buffer_cb); + if (ret) { + v4l2_err(&dev->v4l2_dev, +- "Failed to enable capture port - error %d\n", ret); ++ "Failed to enable capture port - error %d. " ++ "Disabling camera port again\n", ret); ++ ++ vchiq_mmal_port_disable(dev->instance, ++ dev->capture.camera_port); ++ if (disable_camera(dev) < 0) { ++ v4l2_err(&dev->v4l2_dev, "Failed to disable camera"); ++ return -EINVAL; ++ } + return -1; + } + +-- +1.8.5.1 + + +From e97e7f975bf19987152e18dc68c8ffc203f1d1e3 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 9 Dec 2013 15:30:48 +0000 +Subject: [PATCH 72/91] V4L2: Fix ISO controls. + +Driver was passing the index to the GPU, and not the desired +ISO value. + +Signed-off-by: Dave Stevenson +--- + drivers/media/platform/bcm2835/controls.c | 21 ++++++++++++++++++++- + 1 file changed, 20 insertions(+), 1 deletion(-) + +diff --git a/drivers/media/platform/bcm2835/controls.c b/drivers/media/platform/bcm2835/controls.c +index c2e4c64..92863f7 100644 +--- a/drivers/media/platform/bcm2835/controls.c ++++ b/drivers/media/platform/bcm2835/controls.c +@@ -178,6 +178,25 @@ static int ctrl_set_value(struct bm2835_mmal_dev *dev, + &u32_value, sizeof(u32_value)); + } + ++static int ctrl_set_value_menu(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ if (ctrl->val > mmal_ctrl->max || ctrl->val < mmal_ctrl->min) ++ return 1; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ u32_value = mmal_ctrl->imenu[ctrl->val]; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ + static int ctrl_set_value_ev(struct bm2835_mmal_dev *dev, + struct v4l2_ctrl *ctrl, + const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) +@@ -601,7 +620,7 @@ static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) + { + V4L2_CID_ISO_SENSITIVITY, MMAL_CONTROL_TYPE_INT_MENU, + 0, ARRAY_SIZE(iso_qmenu) - 1, 0, 1, iso_qmenu, +- MMAL_PARAMETER_ISO, &ctrl_set_value ++ MMAL_PARAMETER_ISO, &ctrl_set_value_menu + }, + { + V4L2_CID_IMAGE_STABILIZATION, MMAL_CONTROL_TYPE_STD, +-- +1.8.5.1 + + +From 0ecbed516b524f49213d089aada558d92b2e28db Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 9 Dec 2013 16:40:24 +0000 +Subject: [PATCH 73/91] V4L2: Add flicker avoidance controls + +Add support for V4L2_CID_POWER_LINE_FREQUENCY to set flicker +avoidance frequencies. + +Signed-off-by: Dave Stevenson +--- + drivers/media/platform/bcm2835/bcm2835-camera.h | 2 +- + drivers/media/platform/bcm2835/controls.c | 42 ++++++++++++++++++++++++ + drivers/media/platform/bcm2835/mmal-parameters.h | 8 +++++ + 3 files changed, 51 insertions(+), 1 deletion(-) + +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.h b/drivers/media/platform/bcm2835/bcm2835-camera.h +index 5640492..a53c3bd 100644 +--- a/drivers/media/platform/bcm2835/bcm2835-camera.h ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.h +@@ -15,7 +15,7 @@ + * core driver device + */ + +-#define V4L2_CTRL_COUNT 19 /* number of v4l controls */ ++#define V4L2_CTRL_COUNT 20 /* number of v4l controls */ + + enum { + MMAL_COMPONENT_CAMERA = 0, +diff --git a/drivers/media/platform/bcm2835/controls.c b/drivers/media/platform/bcm2835/controls.c +index 92863f7..7cc97c8 100644 +--- a/drivers/media/platform/bcm2835/controls.c ++++ b/drivers/media/platform/bcm2835/controls.c +@@ -56,6 +56,13 @@ + 0, 100, 200, 400, 800, + }; + ++static const s64 mains_freq_qmenu[] = { ++ V4L2_CID_POWER_LINE_FREQUENCY_DISABLED, ++ V4L2_CID_POWER_LINE_FREQUENCY_50HZ, ++ V4L2_CID_POWER_LINE_FREQUENCY_60HZ, ++ V4L2_CID_POWER_LINE_FREQUENCY_AUTO ++}; ++ + /* Supported video encode modes */ + static const s64 bitrate_mode_qmenu[] = { + (s64)V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, +@@ -373,6 +380,35 @@ static int ctrl_set_metering_mode(struct bm2835_mmal_dev *dev, + &u32_value, sizeof(u32_value)); + } + ++static int ctrl_set_flicker_avoidance(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *control; ++ ++ control = &dev->component[MMAL_COMPONENT_CAMERA]->control; ++ ++ switch (ctrl->val) { ++ case V4L2_CID_POWER_LINE_FREQUENCY_DISABLED: ++ u32_value = MMAL_PARAM_FLICKERAVOID_OFF; ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_50HZ: ++ u32_value = MMAL_PARAM_FLICKERAVOID_50HZ; ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_60HZ: ++ u32_value = MMAL_PARAM_FLICKERAVOID_60HZ; ++ break; ++ case V4L2_CID_POWER_LINE_FREQUENCY_AUTO: ++ u32_value = MMAL_PARAM_FLICKERAVOID_AUTO; ++ break; ++ } ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, control, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ + static int ctrl_set_awb_mode(struct bm2835_mmal_dev *dev, + struct v4l2_ctrl *ctrl, + const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) +@@ -706,6 +742,12 @@ static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) + 30, 1, NULL, + MMAL_PARAMETER_JPEG_Q_FACTOR, &ctrl_set_image_encode_output + }, ++ { ++ V4L2_CID_POWER_LINE_FREQUENCY, MMAL_CONTROL_TYPE_STD_MENU, ++ 0, ARRAY_SIZE(mains_freq_qmenu) - 1, ++ 1, 1, NULL, ++ MMAL_PARAMETER_FLICKER_AVOID, &ctrl_set_flicker_avoidance ++ }, + }; + + int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev) +diff --git a/drivers/media/platform/bcm2835/mmal-parameters.h b/drivers/media/platform/bcm2835/mmal-parameters.h +index d8aace5..b3d2c39 100644 +--- a/drivers/media/platform/bcm2835/mmal-parameters.h ++++ b/drivers/media/platform/bcm2835/mmal-parameters.h +@@ -271,6 +271,14 @@ enum mmal_parameter_imagefx { + MMAL_PARAM_IMAGEFX_CARTOON, + }; + ++enum MMAL_PARAM_FLICKERAVOID_T { ++ MMAL_PARAM_FLICKERAVOID_OFF, ++ MMAL_PARAM_FLICKERAVOID_AUTO, ++ MMAL_PARAM_FLICKERAVOID_50HZ, ++ MMAL_PARAM_FLICKERAVOID_60HZ, ++ MMAL_PARAM_FLICKERAVOID_MAX = 0x7FFFFFFF ++}; ++ + /** Manner of video rate control */ + enum mmal_parameter_rate_control_mode { + MMAL_VIDEO_RATECONTROL_DEFAULT, +-- +1.8.5.1 + + +From c3718a2d49ea53c0e03599aa5e15c1ebc1c50326 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Fri, 13 Dec 2013 15:54:13 +0000 +Subject: [PATCH 74/91] V4L2: Add support for frame rate control. + +Add support for frame rate (or time per frame as V4L2 +inverts it) control via s_parm. + +Signed-off-by: Dave Stevenson +--- + drivers/media/platform/bcm2835/bcm2835-camera.c | 115 +++++++++++++++++++++-- + drivers/media/platform/bcm2835/bcm2835-camera.h | 4 +- + drivers/media/platform/bcm2835/controls.c | 5 +- + drivers/media/platform/bcm2835/mmal-parameters.h | 5 + + 4 files changed, 116 insertions(+), 13 deletions(-) + +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c +index 2743074..8c38d03 100644 +--- a/drivers/media/platform/bcm2835/bcm2835-camera.c ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.c +@@ -55,6 +55,15 @@ + + static struct bm2835_mmal_dev *gdev; /* global device data */ + ++#define FPS_MIN 1 ++#define FPS_MAX 30 ++ ++/* timeperframe: min/max and default */ ++static const struct v4l2_fract ++ tpf_min = {.numerator = 1, .denominator = FPS_MAX}, ++ tpf_max = {.numerator = 1, .denominator = FPS_MIN}, ++ tpf_default = {.numerator = 1000, .denominator = 30000}; ++ + /* video formats */ + static struct mmal_fmt formats[] = { + { +@@ -869,8 +878,10 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, + camera_port->es.video.crop.y = 0; + camera_port->es.video.crop.width = f->fmt.pix.width; + camera_port->es.video.crop.height = f->fmt.pix.height; +- camera_port->es.video.frame_rate.num = 30; +- camera_port->es.video.frame_rate.den = 1; ++ camera_port->es.video.frame_rate.num = ++ dev->capture.timeperframe.denominator; ++ camera_port->es.video.frame_rate.den = ++ dev->capture.timeperframe.numerator; + + ret = vchiq_mmal_port_set_format(dev->instance, camera_port); + +@@ -1064,6 +1075,90 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + return ret; + } + ++/* timeperframe is arbitrary and continous */ ++static int vidioc_enum_frameintervals(struct file *file, void *priv, ++ struct v4l2_frmivalenum *fival) ++{ ++ if (fival->index) ++ return -EINVAL; ++ ++ /* regarding width & height - we support any */ ++ ++ fival->type = V4L2_FRMIVAL_TYPE_CONTINUOUS; ++ ++ /* fill in stepwise (step=1.0 is requred by V4L2 spec) */ ++ fival->stepwise.min = tpf_min; ++ fival->stepwise.max = tpf_max; ++ fival->stepwise.step = (struct v4l2_fract) {1, 1}; ++ ++ return 0; ++} ++ ++static int vidioc_g_parm(struct file *file, void *priv, ++ struct v4l2_streamparm *parm) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ ++ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; ++ parm->parm.capture.timeperframe = dev->capture.timeperframe; ++ parm->parm.capture.readbuffers = 1; ++ return 0; ++} ++ ++#define FRACT_CMP(a, OP, b) \ ++ ((u64)(a).numerator * (b).denominator OP \ ++ (u64)(b).numerator * (a).denominator) ++ ++static int vidioc_s_parm(struct file *file, void *priv, ++ struct v4l2_streamparm *parm) ++{ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct v4l2_fract tpf; ++ struct mmal_parameter_rational fps_param; ++ int ret; ++ ++ if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ++ return -EINVAL; ++ ++ tpf = parm->parm.capture.timeperframe; ++ ++ /* tpf: {*, 0} resets timing; clip to [min, max]*/ ++ tpf = tpf.denominator ? tpf : tpf_default; ++ tpf = FRACT_CMP(tpf, <, tpf_min) ? tpf_min : tpf; ++ tpf = FRACT_CMP(tpf, >, tpf_max) ? tpf_max : tpf; ++ ++ dev->capture.timeperframe = tpf; ++ parm->parm.capture.timeperframe = tpf; ++ parm->parm.capture.readbuffers = 1; ++ ++ fps_param.num = dev->capture.timeperframe.denominator; ++ fps_param.den = dev->capture.timeperframe.numerator; ++ ret = vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW], ++ MMAL_PARAMETER_VIDEO_FRAME_RATE, ++ &fps_param, sizeof(fps_param)); ++ ret += vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_VIDEO], ++ MMAL_PARAMETER_VIDEO_FRAME_RATE, ++ &fps_param, sizeof(fps_param)); ++ ret += vchiq_mmal_port_parameter_set(dev->instance, ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_CAPTURE], ++ MMAL_PARAMETER_VIDEO_FRAME_RATE, ++ &fps_param, sizeof(fps_param)); ++ if (ret) ++ v4l2_dbg(0, bcm2835_v4l2_debug, &dev->v4l2_dev, ++ "Failed to set fps ret %d\n", ++ ret); ++ ++ return 0; ++} ++ + static const struct v4l2_ioctl_ops camera0_ioctl_ops = { + /* overlay */ + .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay, +@@ -1092,6 +1187,9 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, ++ .vidioc_enum_frameintervals = vidioc_enum_frameintervals, ++ .vidioc_g_parm = vidioc_g_parm, ++ .vidioc_s_parm = vidioc_s_parm, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + +@@ -1184,8 +1282,10 @@ static int __init mmal_init(struct bm2835_mmal_dev *dev) + format->es->video.crop.y = 0; + format->es->video.crop.width = 1024; + format->es->video.crop.height = 768; +- format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; +- format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; ++ format->es->video.frame_rate.num = ++ dev->capture.timeperframe.denominator; ++ format->es->video.frame_rate.den = ++ dev->capture.timeperframe.numerator; + + format = + &dev->component[MMAL_COMPONENT_CAMERA]-> +@@ -1200,8 +1300,10 @@ static int __init mmal_init(struct bm2835_mmal_dev *dev) + format->es->video.crop.y = 0; + format->es->video.crop.width = 1024; + format->es->video.crop.height = 768; +- format->es->video.frame_rate.num = PREVIEW_FRAME_RATE_NUM; +- format->es->video.frame_rate.den = PREVIEW_FRAME_RATE_DEN; ++ format->es->video.frame_rate.num = ++ dev->capture.timeperframe.denominator; ++ format->es->video.frame_rate.den = ++ dev->capture.timeperframe.numerator; + + format = + &dev->component[MMAL_COMPONENT_CAMERA]-> +@@ -1222,6 +1324,7 @@ static int __init mmal_init(struct bm2835_mmal_dev *dev) + dev->capture.height = format->es->video.height; + dev->capture.fmt = &formats[0]; + dev->capture.encode_component = NULL; ++ dev->capture.timeperframe = tpf_default; + + /* get the preview component ready */ + ret = vchiq_mmal_component_init( +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.h b/drivers/media/platform/bcm2835/bcm2835-camera.h +index a53c3bd..0f29b1a 100644 +--- a/drivers/media/platform/bcm2835/bcm2835-camera.h ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.h +@@ -32,9 +32,6 @@ enum { + MMAL_CAMERA_PORT_COUNT + }; + +-#define PREVIEW_FRAME_RATE_NUM 30 +-#define PREVIEW_FRAME_RATE_DEN 1 +- + #define PREVIEW_LAYER 2 + + extern int bcm2835_v4l2_debug; +@@ -66,6 +63,7 @@ struct bm2835_mmal_dev { + unsigned int height; /* height */ + unsigned int stride; /* stride */ + struct mmal_fmt *fmt; ++ struct v4l2_fract timeperframe; + + /* H264 encode bitrate */ + int encode_bitrate; +diff --git a/drivers/media/platform/bcm2835/controls.c b/drivers/media/platform/bcm2835/controls.c +index 7cc97c8..e965ca3 100644 +--- a/drivers/media/platform/bcm2835/controls.c ++++ b/drivers/media/platform/bcm2835/controls.c +@@ -152,10 +152,7 @@ static int ctrl_set_rational(struct bm2835_mmal_dev *dev, + struct v4l2_ctrl *ctrl, + const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) + { +- struct { +- s32 num; /**< Numerator */ +- s32 den; /**< Denominator */ +- } rational_value; ++ struct mmal_parameter_rational rational_value; + struct vchiq_mmal_port *control; + + control = &dev->component[MMAL_COMPONENT_CAMERA]->control; +diff --git a/drivers/media/platform/bcm2835/mmal-parameters.h b/drivers/media/platform/bcm2835/mmal-parameters.h +index b3d2c39..0f2bd50 100644 +--- a/drivers/media/platform/bcm2835/mmal-parameters.h ++++ b/drivers/media/platform/bcm2835/mmal-parameters.h +@@ -164,6 +164,11 @@ enum mmal_parameter_camera_type { + MMAL_PARAMETER_SHUTTER_SPEED /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ + }; + ++struct mmal_parameter_rational { ++ s32 num; /**< Numerator */ ++ s32 den; /**< Denominator */ ++}; ++ + enum mmal_parameter_camera_config_timestamp_mode { + MMAL_PARAM_TIMESTAMP_MODE_ZERO = 0, /* Always timestamp frames as 0 */ + MMAL_PARAM_TIMESTAMP_MODE_RAW_STC, /* Use the raw STC value +-- +1.8.5.1 + + +From 7c1e1f7b2f1a3990955b16888b6d571a6a117b0d Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 9 Dec 2013 17:30:09 +0000 +Subject: [PATCH 75/91] V4L2: Improve G_FBUF handling so we pass conformance + +Return some sane numbers for get framebuffer so that +we pass conformance. + +Signed-off-by: Dave Stevenson +--- + drivers/media/platform/bcm2835/bcm2835-camera.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c +index 8c38d03..c8d8742 100644 +--- a/drivers/media/platform/bcm2835/bcm2835-camera.c ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.c +@@ -664,10 +664,18 @@ static int vidioc_g_fbuf(struct file *file, void *fh, + { + /* The video overlay must stay within the framebuffer and can't be + positioned independently. */ ++ struct bm2835_mmal_dev *dev = video_drvdata(file); ++ struct vchiq_mmal_port *preview_port = ++ &dev->component[MMAL_COMPONENT_CAMERA]-> ++ output[MMAL_CAMERA_PORT_PREVIEW]; + a->flags = V4L2_FBUF_FLAG_OVERLAY; +- +- /* todo: v4l2_framebuffer still needs more info filling in +- * in order to pass the v4l2-compliance test. */ ++ a->fmt.width = preview_port->es.video.width; ++ a->fmt.height = preview_port->es.video.height; ++ a->fmt.pixelformat = V4L2_PIX_FMT_YUV420; ++ a->fmt.bytesperline = (preview_port->es.video.width * 3)>>1; ++ a->fmt.sizeimage = (preview_port->es.video.width * ++ preview_port->es.video.height * 3)>>1; ++ a->fmt.colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; + } +-- +1.8.5.1 + + +From 1a6a4ed1d0064a1012fec01813ee1ed88f827489 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 9 Dec 2013 17:29:39 +0000 +Subject: [PATCH 76/91] V4L2: Fix information advertised through g_vidfmt + +Width and height were being stored based on incorrect +values. + +Signed-off-by: Dave Stevenson +--- + drivers/media/platform/bcm2835/bcm2835-camera.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c +index c8d8742..4766a9c 100644 +--- a/drivers/media/platform/bcm2835/bcm2835-camera.c ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.c +@@ -1028,13 +1028,19 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, + if (!ret) { + dev->capture.fmt = mfmt; + dev->capture.stride = f->fmt.pix.bytesperline; +- dev->capture.width = port->es.video.crop.width; +- dev->capture.height = port->es.video.crop.height; ++ dev->capture.width = camera_port->es.video.crop.width; ++ dev->capture.height = camera_port->es.video.crop.height; + + /* select port for capture */ + dev->capture.port = port; + dev->capture.camera_port = camera_port; + dev->capture.encode_component = encode_component; ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "Set dev->capture.fmt %08X, %dx%d, stride %d", ++ port->format.encoding, ++ dev->capture.width, dev->capture.height, ++ dev->capture.stride); + } + } + +-- +1.8.5.1 + + +From 3069a8f246ac819fd188920f4e512e47b1f612e0 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 9 Dec 2013 11:03:54 +0000 +Subject: [PATCH 77/91] V4L2: Add support for inline H264 headers + +Add support for V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER +to control H264 inline headers. +Requires firmware fix to work correctly, otherwise format +has to be set to H264 before this parameter is set. + +Signed-off-by: Dave Stevenson +--- + drivers/media/platform/bcm2835/bcm2835-camera.h | 2 +- + drivers/media/platform/bcm2835/controls.c | 115 ++++++++++++++++++----- + drivers/media/platform/bcm2835/mmal-parameters.h | 11 ++- + 3 files changed, 103 insertions(+), 25 deletions(-) + +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.h b/drivers/media/platform/bcm2835/bcm2835-camera.h +index 0f29b1a..25aa91f 100644 +--- a/drivers/media/platform/bcm2835/bcm2835-camera.h ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.h +@@ -15,7 +15,7 @@ + * core driver device + */ + +-#define V4L2_CTRL_COUNT 20 /* number of v4l controls */ ++#define V4L2_CTRL_COUNT 21 /* number of v4l controls */ + + enum { + MMAL_COMPONENT_CAMERA = 0, +diff --git a/drivers/media/platform/bcm2835/controls.c b/drivers/media/platform/bcm2835/controls.c +index e965ca3..cb062a9 100644 +--- a/drivers/media/platform/bcm2835/controls.c ++++ b/drivers/media/platform/bcm2835/controls.c +@@ -96,6 +96,7 @@ struct bm2835_mmal_v4l2_ctrl { + const s64 *imenu; /* integer menu array */ + u32 mmal_id; /* mmal parameter id */ + bm2835_mmal_v4l2_ctrl_cb *setter; ++ bool ignore_errors; + }; + + struct v4l2_to_mmal_effects_setting { +@@ -606,12 +607,29 @@ static int ctrl_set_image_encode_output(struct bm2835_mmal_dev *dev, + &u32_value, sizeof(u32_value)); + } + ++static int ctrl_set_video_encode_param_output(struct bm2835_mmal_dev *dev, ++ struct v4l2_ctrl *ctrl, ++ const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl) ++{ ++ u32 u32_value; ++ struct vchiq_mmal_port *vid_enc_ctl; ++ ++ vid_enc_ctl = &dev->component[MMAL_COMPONENT_VIDEO_ENCODE]->output[0]; ++ ++ u32_value = ctrl->val; ++ ++ return vchiq_mmal_port_parameter_set(dev->instance, vid_enc_ctl, ++ mmal_ctrl->mmal_id, ++ &u32_value, sizeof(u32_value)); ++} ++ + static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) + { + struct bm2835_mmal_dev *dev = + container_of(ctrl->handler, struct bm2835_mmal_dev, + ctrl_handler); + const struct bm2835_mmal_v4l2_ctrl *mmal_ctrl = ctrl->priv; ++ int ret; + + if ((mmal_ctrl == NULL) || + (mmal_ctrl->id != ctrl->id) || +@@ -620,7 +638,10 @@ static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) + return -EINVAL; + } + +- return mmal_ctrl->setter(dev, ctrl, mmal_ctrl); ++ ret = mmal_ctrl->setter(dev, ctrl, mmal_ctrl); ++ if (mmal_ctrl->ignore_errors) ++ ret = 0; ++ return ret; + } + + static const struct v4l2_ctrl_ops bm2835_mmal_ctrl_ops = { +@@ -633,32 +654,44 @@ static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) + { + V4L2_CID_SATURATION, MMAL_CONTROL_TYPE_STD, + -100, 100, 0, 1, NULL, +- MMAL_PARAMETER_SATURATION, &ctrl_set_rational ++ MMAL_PARAMETER_SATURATION, ++ &ctrl_set_rational, ++ false + }, + { + V4L2_CID_SHARPNESS, MMAL_CONTROL_TYPE_STD, + -100, 100, 0, 1, NULL, +- MMAL_PARAMETER_SHARPNESS, &ctrl_set_rational ++ MMAL_PARAMETER_SHARPNESS, ++ &ctrl_set_rational, ++ false + }, + { + V4L2_CID_CONTRAST, MMAL_CONTROL_TYPE_STD, + -100, 100, 0, 1, NULL, +- MMAL_PARAMETER_CONTRAST, &ctrl_set_rational ++ MMAL_PARAMETER_CONTRAST, ++ &ctrl_set_rational, ++ false + }, + { + V4L2_CID_BRIGHTNESS, MMAL_CONTROL_TYPE_STD, + 0, 100, 50, 1, NULL, +- MMAL_PARAMETER_BRIGHTNESS, &ctrl_set_rational ++ MMAL_PARAMETER_BRIGHTNESS, ++ &ctrl_set_rational, ++ false + }, + { + V4L2_CID_ISO_SENSITIVITY, MMAL_CONTROL_TYPE_INT_MENU, + 0, ARRAY_SIZE(iso_qmenu) - 1, 0, 1, iso_qmenu, +- MMAL_PARAMETER_ISO, &ctrl_set_value_menu ++ MMAL_PARAMETER_ISO, ++ &ctrl_set_value_menu, ++ false + }, + { + V4L2_CID_IMAGE_STABILIZATION, MMAL_CONTROL_TYPE_STD, + 0, 1, 0, 1, NULL, +- MMAL_PARAMETER_VIDEO_STABILISATION, &ctrl_set_value ++ MMAL_PARAMETER_VIDEO_STABILISATION, ++ &ctrl_set_value, ++ false + }, + /* { + 0, MMAL_CONTROL_TYPE_CLUSTER, 3, 1, 0, NULL, 0, NULL +@@ -666,7 +699,9 @@ static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) + */ { + V4L2_CID_EXPOSURE_AUTO, MMAL_CONTROL_TYPE_STD_MENU, + ~0x03, 3, V4L2_EXPOSURE_AUTO, 0, NULL, +- MMAL_PARAMETER_EXPOSURE_MODE, &ctrl_set_exposure ++ MMAL_PARAMETER_EXPOSURE_MODE, ++ &ctrl_set_exposure, ++ false + }, + /* todo this needs mixing in with set exposure + { +@@ -677,86 +712,120 @@ static int bm2835_mmal_s_ctrl(struct v4l2_ctrl *ctrl) + V4L2_CID_EXPOSURE_ABSOLUTE, MMAL_CONTROL_TYPE_STD, + /* Units of 100usecs */ + 1, 1*1000*10, 100*10, 1, NULL, +- MMAL_PARAMETER_SHUTTER_SPEED, &ctrl_set_exposure ++ MMAL_PARAMETER_SHUTTER_SPEED, ++ &ctrl_set_exposure, ++ false + }, + { + V4L2_CID_AUTO_EXPOSURE_BIAS, MMAL_CONTROL_TYPE_INT_MENU, + 0, ARRAY_SIZE(ev_bias_qmenu) - 1, + (ARRAY_SIZE(ev_bias_qmenu)+1)/2 - 1, 0, ev_bias_qmenu, +- MMAL_PARAMETER_EXPOSURE_COMP, &ctrl_set_value_ev ++ MMAL_PARAMETER_EXPOSURE_COMP, ++ &ctrl_set_value_ev, ++ false + }, + { + V4L2_CID_EXPOSURE_METERING, + MMAL_CONTROL_TYPE_STD_MENU, + ~0x7, 2, V4L2_EXPOSURE_METERING_AVERAGE, 0, NULL, +- MMAL_PARAMETER_EXP_METERING_MODE, &ctrl_set_metering_mode ++ MMAL_PARAMETER_EXP_METERING_MODE, ++ &ctrl_set_metering_mode, ++ false + }, + { + V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, + MMAL_CONTROL_TYPE_STD_MENU, + ~0x3fe, 9, V4L2_WHITE_BALANCE_AUTO, 0, NULL, +- MMAL_PARAMETER_AWB_MODE, &ctrl_set_awb_mode ++ MMAL_PARAMETER_AWB_MODE, ++ &ctrl_set_awb_mode, ++ false + }, + { + V4L2_CID_COLORFX, MMAL_CONTROL_TYPE_STD_MENU, + 0, 15, V4L2_COLORFX_NONE, 0, NULL, +- MMAL_PARAMETER_IMAGE_EFFECT, &ctrl_set_image_effect ++ MMAL_PARAMETER_IMAGE_EFFECT, ++ &ctrl_set_image_effect, ++ false + }, + { + V4L2_CID_COLORFX_CBCR, MMAL_CONTROL_TYPE_STD, + 0, 0xffff, 0x8080, 1, NULL, +- MMAL_PARAMETER_COLOUR_EFFECT, &ctrl_set_colfx ++ MMAL_PARAMETER_COLOUR_EFFECT, ++ &ctrl_set_colfx, ++ false + }, + { + V4L2_CID_ROTATE, MMAL_CONTROL_TYPE_STD, + 0, 360, 0, 90, NULL, +- MMAL_PARAMETER_ROTATION, &ctrl_set_rotate ++ MMAL_PARAMETER_ROTATION, ++ &ctrl_set_rotate, ++ false + }, + { + V4L2_CID_HFLIP, MMAL_CONTROL_TYPE_STD, + 0, 1, 0, 1, NULL, +- MMAL_PARAMETER_MIRROR, &ctrl_set_flip ++ MMAL_PARAMETER_MIRROR, ++ &ctrl_set_flip, ++ false + }, + { + V4L2_CID_VFLIP, MMAL_CONTROL_TYPE_STD, + 0, 1, 0, 1, NULL, +- MMAL_PARAMETER_MIRROR, &ctrl_set_flip ++ MMAL_PARAMETER_MIRROR, ++ &ctrl_set_flip, ++ false + }, + { + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, MMAL_CONTROL_TYPE_STD_MENU, + 0, ARRAY_SIZE(bitrate_mode_qmenu) - 1, + 0, 0, bitrate_mode_qmenu, +- MMAL_PARAMETER_RATECONTROL, &ctrl_set_bitrate_mode ++ MMAL_PARAMETER_RATECONTROL, ++ &ctrl_set_bitrate_mode, ++ false + }, + { + V4L2_CID_MPEG_VIDEO_BITRATE, MMAL_CONTROL_TYPE_STD, + 25*1000, 25*1000*1000, 10*1000*1000, 25*1000, NULL, +- MMAL_PARAMETER_VIDEO_BIT_RATE, &ctrl_set_bitrate ++ MMAL_PARAMETER_VIDEO_BIT_RATE, ++ &ctrl_set_bitrate, ++ false + }, + { + V4L2_CID_JPEG_COMPRESSION_QUALITY, MMAL_CONTROL_TYPE_STD, + 1, 100, + 30, 1, NULL, +- MMAL_PARAMETER_JPEG_Q_FACTOR, &ctrl_set_image_encode_output ++ MMAL_PARAMETER_JPEG_Q_FACTOR, ++ &ctrl_set_image_encode_output, ++ false + }, + { + V4L2_CID_POWER_LINE_FREQUENCY, MMAL_CONTROL_TYPE_STD_MENU, + 0, ARRAY_SIZE(mains_freq_qmenu) - 1, + 1, 1, NULL, +- MMAL_PARAMETER_FLICKER_AVOID, &ctrl_set_flicker_avoidance ++ MMAL_PARAMETER_FLICKER_AVOID, ++ &ctrl_set_flicker_avoidance, ++ false ++ }, ++ { ++ V4L2_CID_MPEG_VIDEO_REPEAT_SEQ_HEADER, MMAL_CONTROL_TYPE_STD, ++ 0, 1, ++ 0, 1, NULL, ++ MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, ++ &ctrl_set_video_encode_param_output, ++ true /* Errors ignored as requires latest firmware to work */ + }, + }; + + int bm2835_mmal_set_all_camera_controls(struct bm2835_mmal_dev *dev) + { + int c; +- int ret; ++ int ret = 0; + + for (c = 0; c < V4L2_CTRL_COUNT; c++) { + if ((dev->ctrls[c]) && (v4l2_ctrls[c].setter)) { + ret = v4l2_ctrls[c].setter(dev, dev->ctrls[c], + &v4l2_ctrls[c]); +- if (ret) ++ if (!v4l2_ctrls[c]. ignore_errors && ret) + break; + } + } +diff --git a/drivers/media/platform/bcm2835/mmal-parameters.h b/drivers/media/platform/bcm2835/mmal-parameters.h +index 0f2bd50..b08a4b0 100644 +--- a/drivers/media/platform/bcm2835/mmal-parameters.h ++++ b/drivers/media/platform/bcm2835/mmal-parameters.h +@@ -421,7 +421,16 @@ enum mmal_parameter_video_type { + MMAL_PARAMETER_VIDEO_DRM_PROTECT_BUFFER, + + /** @ref MMAL_PARAMETER_BYTES_T */ +- MMAL_PARAMETER_VIDEO_DECODE_CONFIG_VD3 ++ MMAL_PARAMETER_VIDEO_DECODE_CONFIG_VD3, ++ ++ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_VCL_HRD_PARAMETERS, ++ ++ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_H264_LOW_DELAY_HRD_FLAG, ++ ++ /**< @ref MMAL_PARAMETER_BOOLEAN_T */ ++ MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER + }; + + /** Valid mirror modes */ +-- +1.8.5.1 + + +From 1113263a44cf1985ab786805fb728e796d5e17fd Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Thu, 19 Dec 2013 17:33:02 +0000 +Subject: [PATCH 78/91] V4L2: Fix JPEG timestamp issue + +JPEG images were coming through from the GPU with timestamp +of 0. Detect this and give current system time instead +of some invalid value. + +Signed-off-by: Dave Stevenson +--- + drivers/media/platform/bcm2835/bcm2835-camera.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c +index 4766a9c..9fc90a2 100644 +--- a/drivers/media/platform/bcm2835/bcm2835-camera.c ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.c +@@ -238,7 +238,8 @@ static void buffer_cb(struct vchiq_mmal_instance *instance, + } + } else { + if (dev->capture.frame_count) { +- if (dev->capture.vc_start_timestamp != -1) { ++ if (dev->capture.vc_start_timestamp != -1 && ++ pts != 0) { + s64 runtime_us = pts - + dev->capture.vc_start_timestamp; + u32 div = 0; +@@ -259,7 +260,7 @@ static void buffer_cb(struct vchiq_mmal_instance *instance, + USEC_PER_SEC; + } + v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, +- "Convert start time %d.%06d and %llu" ++ "Convert start time %d.%06d and %llu " + "with offset %llu to %d.%06d\n", + (int)dev->capture.kernel_start_ts. + tv_sec, +-- +1.8.5.1 + + +From ed18624b29ff137afcfce1fb485ad1af671f6875 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Mon, 9 Dec 2013 11:24:55 +0000 +Subject: [PATCH 79/91] V4L2: Fix issue when switching down JPEG resolution. + +JPEG buffer size calculation is based on input resolution. +Input resolution was being configured after output port +format. Caused failures if switching from one JPEG resolution +to a smaller one. + +Signed-off-by: Dave Stevenson +--- + drivers/media/platform/bcm2835/bcm2835-camera.c | 126 ++++++++++++++---------- + 1 file changed, 72 insertions(+), 54 deletions(-) + +diff --git a/drivers/media/platform/bcm2835/bcm2835-camera.c b/drivers/media/platform/bcm2835/bcm2835-camera.c +index 9fc90a2..4780107 100644 +--- a/drivers/media/platform/bcm2835/bcm2835-camera.c ++++ b/drivers/media/platform/bcm2835/bcm2835-camera.c +@@ -955,69 +955,87 @@ static int mmal_setup_components(struct bm2835_mmal_dev *dev, + camera_port->current_buffer.num = + camera_port->recommended_buffer.num; + +- port->format.encoding = mfmt->mmal; +- port->format.encoding_variant = 0; +- /* Set any encoding specific parameters */ +- switch (mfmt->mmal_component) { +- case MMAL_COMPONENT_VIDEO_ENCODE: +- port->format.bitrate = +- dev->capture.encode_bitrate; +- break; +- case MMAL_COMPONENT_IMAGE_ENCODE: +- /* Could set EXIF parameters here */ +- break; +- default: +- break; +- } +- ret = vchiq_mmal_port_set_format(dev->instance, port); +- ++ ret = ++ vchiq_mmal_port_connect_tunnel( ++ dev->instance, ++ camera_port, ++ &encode_component->input[0]); + if (ret) { +- v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, +- "%s failed to set format\n", __func__); ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "%s failed to create connection\n", ++ __func__); ++ /* ensure capture is not going to be tried */ ++ dev->capture.port = NULL; + } else { ++ port->es.video.width = f->fmt.pix.width; ++ port->es.video.height = f->fmt.pix.height; ++ port->es.video.crop.x = 0; ++ port->es.video.crop.y = 0; ++ port->es.video.crop.width = f->fmt.pix.width; ++ port->es.video.crop.height = f->fmt.pix.height; ++ port->es.video.frame_rate.num = ++ dev->capture.timeperframe.denominator; ++ port->es.video.frame_rate.den = ++ dev->capture.timeperframe.numerator; ++ ++ port->format.encoding = mfmt->mmal; ++ port->format.encoding_variant = 0; ++ /* Set any encoding specific parameters */ ++ switch (mfmt->mmal_component) { ++ case MMAL_COMPONENT_VIDEO_ENCODE: ++ port->format.bitrate = ++ dev->capture.encode_bitrate; ++ break; ++ case MMAL_COMPONENT_IMAGE_ENCODE: ++ /* Could set EXIF parameters here */ ++ break; ++ default: ++ break; ++ } ++ ret = vchiq_mmal_port_set_format(dev->instance, ++ port); ++ if (ret) ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "%s failed to set format\n", ++ __func__); ++ } ++ ++ if (!ret) { + ret = vchiq_mmal_component_enable( + dev->instance, + encode_component); + if (ret) { + v4l2_dbg(1, bcm2835_v4l2_debug, +- &dev->v4l2_dev, +- "%s Failed to enable encode components\n", +- __func__); +- } else { +- /* configure buffering */ +- port->current_buffer.num = 1; +- port->current_buffer.size = +- f->fmt.pix.sizeimage; +- if (port->format.encoding == +- MMAL_ENCODING_JPEG) { +- v4l2_dbg(1, bcm2835_v4l2_debug, +- &dev->v4l2_dev, +- "JPEG - fiddle buffer size\n"); +- port->current_buffer.size = +- (f->fmt.pix.sizeimage < +- (100 << 10)) +- ? (100 << 10) : f->fmt.pix. +- sizeimage; +- } ++ &dev->v4l2_dev, ++ "%s Failed to enable encode components\n", ++ __func__); ++ } ++ } ++ if (!ret) { ++ /* configure buffering */ ++ port->current_buffer.num = 1; ++ port->current_buffer.size = ++ f->fmt.pix.sizeimage; ++ if (port->format.encoding == ++ MMAL_ENCODING_JPEG) { + v4l2_dbg(1, bcm2835_v4l2_debug, +- &dev->v4l2_dev, +- "vid_cap - current_buffer.size being set to %d\n", +- f->fmt.pix.sizeimage); +- port->current_buffer.alignment = 0; +- ret = +- vchiq_mmal_port_connect_tunnel( +- dev->instance, +- camera_port, +- &encode_component->input[0]); +- if (ret) { +- v4l2_dbg(1, bcm2835_v4l2_debug, +- &dev->v4l2_dev, +- "%s failed to create connection\n", +- __func__); +- /* ensure capture is not going to be tried */ +- dev->capture.port = NULL; +- } ++ &dev->v4l2_dev, ++ "JPG - buf size now %d was %d\n", ++ f->fmt.pix.sizeimage, ++ port->current_buffer.size); ++ port->current_buffer.size = ++ (f->fmt.pix.sizeimage < ++ (100 << 10)) ++ ? (100 << 10) : f->fmt.pix. ++ sizeimage; + } ++ v4l2_dbg(1, bcm2835_v4l2_debug, ++ &dev->v4l2_dev, ++ "vid_cap - cur_buf.size set to %d\n", ++ f->fmt.pix.sizeimage); ++ port->current_buffer.alignment = 0; + } + } else { + /* configure buffering */ +-- +1.8.5.1 + + +From 1d6db295e852282024f17378f582d5dd9c42743f Mon Sep 17 00:00:00 2001 +From: popcornmix +Date: Mon, 2 Dec 2013 16:57:44 +0000 +Subject: [PATCH 80/91] config: Enable V4L / MMAL driver + +--- + arch/arm/configs/bcmrpi_defconfig | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig +index 408c328..4032d53 100644 +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -688,6 +688,9 @@ CONFIG_DVB_B2C2_FLEXCOP_USB=m + CONFIG_VIDEO_EM28XX=m + CONFIG_VIDEO_EM28XX_ALSA=m + CONFIG_VIDEO_EM28XX_DVB=m ++CONFIG_V4L_PLATFORM_DRIVERS=y ++CONFIG_VIDEO_BCM2835=y ++CONFIG_VIDEO_BCM2835_MMAL=m + CONFIG_RADIO_SI470X=y + CONFIG_USB_SI470X=m + CONFIG_I2C_SI470X=m +-- +1.8.5.1 + + +From e30d2a13103489f3d1fd563215c5a4bffe3889c7 Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 14:22:53 +0100 +Subject: [PATCH 81/91] dmaengine: Add support for BCM2708 + +Add support for DMA controller of BCM2708 as used in the Raspberry Pi. +Currently it only supports cyclic DMA. + +Signed-off-by: Florian Meier +--- + drivers/dma/Kconfig | 6 + + drivers/dma/Makefile | 1 + + drivers/dma/bcm2708-dmaengine.c | 588 ++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 595 insertions(+) + create mode 100644 drivers/dma/bcm2708-dmaengine.c + +diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig +index f238cfd..66201d9 100644 +--- a/drivers/dma/Kconfig ++++ b/drivers/dma/Kconfig +@@ -288,6 +288,12 @@ config DMA_OMAP + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + ++config DMA_BCM2708 ++ tristate "BCM2708 DMA engine support" ++ depends on MACH_BCM2708 ++ select DMA_ENGINE ++ select DMA_VIRTUAL_CHANNELS ++ + config TI_CPPI41 + tristate "AM33xx CPPI41 DMA support" + depends on ARCH_OMAP +diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile +index db89035..74432a4 100644 +--- a/drivers/dma/Makefile ++++ b/drivers/dma/Makefile +@@ -37,6 +37,7 @@ obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o + obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o + obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o + obj-$(CONFIG_DMA_OMAP) += omap-dma.o ++obj-$(CONFIG_DMA_BCM2708) += bcm2708-dmaengine.o + obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o + obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o + obj-$(CONFIG_TI_CPPI41) += cppi41.o +diff --git a/drivers/dma/bcm2708-dmaengine.c b/drivers/dma/bcm2708-dmaengine.c +new file mode 100644 +index 0000000..7d5ed19 +--- /dev/null ++++ b/drivers/dma/bcm2708-dmaengine.c +@@ -0,0 +1,588 @@ ++/* ++ * BCM2708 DMA engine support ++ * ++ * This driver only supports cyclic DMA transfers ++ * as needed for the I2S module. ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * Based on ++ * OMAP DMAengine support by Russell King ++ * ++ * BCM2708 DMA Driver ++ * Copyright (C) 2010 Broadcom ++ * ++ * Raspberry Pi PCM I2S ALSA Driver ++ * Copyright (c) by Phil Poole 2013 ++ * ++ * MARVELL MMP Peripheral DMA Driver ++ * Copyright 2012 Marvell International Ltd. ++ * ++ * 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. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "virt-dma.h" ++ ++#include ++#include ++ ++struct bcm2708_dmadev { ++ struct dma_device ddev; ++ spinlock_t lock; ++ void __iomem *base; ++ struct device_dma_parameters dma_parms; ++}; ++ ++struct bcm2708_chan { ++ struct virt_dma_chan vc; ++ struct list_head node; ++ ++ struct dma_slave_config cfg; ++ bool cyclic; ++ ++ int ch; ++ struct bcm2708_desc *desc; ++ ++ void __iomem *chan_base; ++ int irq_number; ++}; ++ ++struct bcm2708_desc { ++ struct virt_dma_desc vd; ++ enum dma_transfer_direction dir; ++ ++ unsigned int control_block_size; ++ struct bcm2708_dma_cb *control_block_base; ++ dma_addr_t control_block_base_phys; ++ ++ unsigned frames; ++ size_t size; ++}; ++ ++#define BCM2708_DMA_DATA_TYPE_S8 1 ++#define BCM2708_DMA_DATA_TYPE_S16 2 ++#define BCM2708_DMA_DATA_TYPE_S32 4 ++#define BCM2708_DMA_DATA_TYPE_S128 16 ++ ++static inline struct bcm2708_dmadev *to_bcm2708_dma_dev(struct dma_device *d) ++{ ++ return container_of(d, struct bcm2708_dmadev, ddev); ++} ++ ++static inline struct bcm2708_chan *to_bcm2708_dma_chan(struct dma_chan *c) ++{ ++ return container_of(c, struct bcm2708_chan, vc.chan); ++} ++ ++static inline struct bcm2708_desc *to_bcm2708_dma_desc( ++ struct dma_async_tx_descriptor *t) ++{ ++ return container_of(t, struct bcm2708_desc, vd.tx); ++} ++ ++static void bcm2708_dma_desc_free(struct virt_dma_desc *vd) ++{ ++ struct bcm2708_desc *desc = container_of(vd, struct bcm2708_desc, vd); ++ dma_free_coherent(desc->vd.tx.chan->device->dev, ++ desc->control_block_size, ++ desc->control_block_base, ++ desc->control_block_base_phys); ++ kfree(desc); ++} ++ ++static void bcm2708_dma_start_desc(struct bcm2708_chan *c) ++{ ++ struct virt_dma_desc *vd = vchan_next_desc(&c->vc); ++ struct bcm2708_desc *d; ++ ++ if (!vd) { ++ c->desc = NULL; ++ return; ++ } ++ ++ list_del(&vd->node); ++ ++ c->desc = d = to_bcm2708_dma_desc(&vd->tx); ++ ++ bcm_dma_start(c->chan_base, d->control_block_base_phys); ++} ++ ++static irqreturn_t bcm2708_dma_callback(int irq, void *data) ++{ ++ struct bcm2708_chan *c = data; ++ struct bcm2708_desc *d; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&c->vc.lock, flags); ++ ++ /* Acknowledge interrupt */ ++ writel(BCM2708_DMA_INT, c->chan_base + BCM2708_DMA_CS); ++ ++ d = c->desc; ++ ++ if (d) { ++ /* TODO Only works for cyclic DMA */ ++ vchan_cyclic_callback(&d->vd); ++ } ++ ++ /* Keep the DMA engine running */ ++ dsb(); /* ARM synchronization barrier */ ++ writel(BCM2708_DMA_ACTIVE, c->chan_base + BCM2708_DMA_CS); ++ ++ spin_unlock_irqrestore(&c->vc.lock, flags); ++ ++ return IRQ_HANDLED; ++} ++ ++static int bcm2708_dma_alloc_chan_resources(struct dma_chan *chan) ++{ ++ struct bcm2708_chan *c = to_bcm2708_dma_chan(chan); ++ ++ return request_irq(c->irq_number, ++ bcm2708_dma_callback, 0, "DMA IRQ", c); ++} ++ ++static void bcm2708_dma_free_chan_resources(struct dma_chan *chan) ++{ ++ struct bcm2708_chan *c = to_bcm2708_dma_chan(chan); ++ ++ vchan_free_chan_resources(&c->vc); ++ free_irq(c->irq_number, c); ++ ++ dev_dbg(c->vc.chan.device->dev, "Freeing DMA channel %u\n", c->ch); ++} ++ ++static size_t bcm2708_dma_desc_size(struct bcm2708_desc *d) ++{ ++ return d->size; ++} ++ ++static size_t bcm2708_dma_desc_size_pos(struct bcm2708_desc *d, dma_addr_t addr) ++{ ++ unsigned i; ++ size_t size; ++ ++ for (size = i = 0; i < d->frames; i++) { ++ struct bcm2708_dma_cb *control_block = ++ &d->control_block_base[i]; ++ size_t this_size = control_block->length; ++ dma_addr_t dma; ++ ++ if (d->dir == DMA_DEV_TO_MEM) ++ dma = control_block->dst; ++ else ++ dma = control_block->src; ++ ++ if (size) ++ size += this_size; ++ else if (addr >= dma && addr < dma + this_size) ++ size += dma + this_size - addr; ++ } ++ ++ return size; ++} ++ ++static enum dma_status bcm2708_dma_tx_status(struct dma_chan *chan, ++ dma_cookie_t cookie, struct dma_tx_state *txstate) ++{ ++ struct bcm2708_chan *c = to_bcm2708_dma_chan(chan); ++ struct virt_dma_desc *vd; ++ enum dma_status ret; ++ unsigned long flags; ++ ++ ret = dma_cookie_status(chan, cookie, txstate); ++ if (ret == DMA_SUCCESS || !txstate) ++ return ret; ++ ++ spin_lock_irqsave(&c->vc.lock, flags); ++ vd = vchan_find_desc(&c->vc, cookie); ++ if (vd) { ++ txstate->residue = ++ bcm2708_dma_desc_size(to_bcm2708_dma_desc(&vd->tx)); ++ } else if (c->desc && c->desc->vd.tx.cookie == cookie) { ++ struct bcm2708_desc *d = c->desc; ++ dma_addr_t pos; ++ ++ if (d->dir == DMA_MEM_TO_DEV) ++ pos = readl(c->chan_base + BCM2708_DMA_SOURCE_AD); ++ else if (d->dir == DMA_DEV_TO_MEM) ++ pos = readl(c->chan_base + BCM2708_DMA_DEST_AD); ++ else ++ pos = 0; ++ ++ txstate->residue = bcm2708_dma_desc_size_pos(d, pos); ++ } else { ++ txstate->residue = 0; ++ } ++ ++ spin_unlock_irqrestore(&c->vc.lock, flags); ++ ++ return ret; ++} ++ ++static void bcm2708_dma_issue_pending(struct dma_chan *chan) ++{ ++ struct bcm2708_chan *c = to_bcm2708_dma_chan(chan); ++ unsigned long flags; ++ ++ c->cyclic = true; /* Nothing else is implemented */ ++ ++ spin_lock_irqsave(&c->vc.lock, flags); ++ if (vchan_issue_pending(&c->vc) && !c->desc) ++ bcm2708_dma_start_desc(c); ++ ++ spin_unlock_irqrestore(&c->vc.lock, flags); ++} ++ ++static struct dma_async_tx_descriptor *bcm2708_dma_prep_dma_cyclic( ++ struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, ++ size_t period_len, enum dma_transfer_direction direction, ++ unsigned long flags, void *context) ++{ ++ struct bcm2708_chan *c = to_bcm2708_dma_chan(chan); ++ enum dma_slave_buswidth dev_width; ++ struct bcm2708_desc *d; ++ dma_addr_t dev_addr; ++ unsigned es, sync_type; ++ unsigned frame; ++ ++ /* Grab configuration */ ++ if (direction == DMA_DEV_TO_MEM) { ++ dev_addr = c->cfg.src_addr; ++ dev_width = c->cfg.src_addr_width; ++ sync_type = BCM2708_DMA_S_DREQ; ++ } else if (direction == DMA_MEM_TO_DEV) { ++ dev_addr = c->cfg.dst_addr; ++ dev_width = c->cfg.dst_addr_width; ++ sync_type = BCM2708_DMA_D_DREQ; ++ } else { ++ dev_err(chan->device->dev, "%s: bad direction?\n", __func__); ++ return NULL; ++ } ++ ++ /* Bus width translates to the element size (ES) */ ++ switch (dev_width) { ++ case DMA_SLAVE_BUSWIDTH_4_BYTES: ++ es = BCM2708_DMA_DATA_TYPE_S32; ++ break; ++ default: ++ return NULL; ++ } ++ ++ /* Now allocate and setup the descriptor. */ ++ d = kzalloc(sizeof(*d), GFP_NOWAIT); ++ if (!d) ++ return NULL; ++ ++ d->dir = direction; ++ d->frames = buf_len / period_len; ++ ++ /* Allocate memory for control blocks */ ++ d->control_block_size = d->frames * sizeof(struct bcm2708_dma_cb); ++ d->control_block_base = dma_zalloc_coherent(chan->device->dev, ++ d->control_block_size, &d->control_block_base_phys, ++ GFP_NOWAIT); ++ ++ if (!d->control_block_base) { ++ kfree(d); ++ return NULL; ++ } ++ ++ /* ++ * Iterate over all frames, create a control block ++ * for each frame and link them together. ++ */ ++ for (frame = 0; frame < d->frames; frame++) { ++ struct bcm2708_dma_cb *control_block = ++ &d->control_block_base[frame]; ++ ++ /* Setup adresses */ ++ if (d->dir == DMA_DEV_TO_MEM) { ++ control_block->info = BCM2708_DMA_D_INC; ++ control_block->src = dev_addr; ++ control_block->dst = buf_addr + frame * period_len; ++ } else { ++ control_block->info = BCM2708_DMA_S_INC; ++ control_block->src = buf_addr + frame * period_len; ++ control_block->dst = dev_addr; ++ } ++ ++ /* Enable interrupt */ ++ control_block->info |= BCM2708_DMA_INT_EN; ++ ++ /* Setup synchronization */ ++ if (sync_type != 0) ++ control_block->info |= sync_type; ++ ++ /* Setup DREQ channel */ ++ if (c->cfg.slave_id != 0) ++ control_block->info |= ++ BCM2708_DMA_PER_MAP(c->cfg.slave_id); ++ ++ /* Length of a frame */ ++ control_block->length = period_len; ++ d->size += control_block->length; ++ ++ /* ++ * Next block is the next frame. ++ * This DMA engine driver currently only supports cyclic DMA. ++ * Therefore, wrap around at number of frames. ++ */ ++ control_block->next = d->control_block_base_phys + ++ sizeof(struct bcm2708_dma_cb) ++ * ((frame + 1) % d->frames); ++ } ++ ++ return vchan_tx_prep(&c->vc, &d->vd, flags); ++} ++ ++static int bcm2708_dma_slave_config(struct bcm2708_chan *c, ++ struct dma_slave_config *cfg) ++{ ++ if ((cfg->direction == DMA_DEV_TO_MEM && ++ cfg->src_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) || ++ (cfg->direction == DMA_MEM_TO_DEV && ++ cfg->dst_addr_width != DMA_SLAVE_BUSWIDTH_4_BYTES) || ++ !is_slave_direction(cfg->direction)) { ++ return -EINVAL; ++ } ++ ++ c->cfg = *cfg; ++ ++ return 0; ++} ++ ++static int bcm2708_dma_terminate_all(struct bcm2708_chan *c) ++{ ++ struct bcm2708_dmadev *d = to_bcm2708_dma_dev(c->vc.chan.device); ++ unsigned long flags; ++ int timeout = 10000; ++ LIST_HEAD(head); ++ ++ spin_lock_irqsave(&c->vc.lock, flags); ++ ++ /* Prevent this channel being scheduled */ ++ spin_lock(&d->lock); ++ list_del_init(&c->node); ++ spin_unlock(&d->lock); ++ ++ /* ++ * Stop DMA activity: we assume the callback will not be called ++ * after bcm_dma_abort() returns (even if it does, it will see ++ * c->desc is NULL and exit.) ++ */ ++ if (c->desc) { ++ c->desc = NULL; ++ bcm_dma_abort(c->chan_base); ++ ++ /* Wait for stopping */ ++ while (timeout > 0) { ++ timeout--; ++ if (!(readl(c->chan_base + BCM2708_DMA_CS) & ++ BCM2708_DMA_ACTIVE)) ++ break; ++ ++ cpu_relax(); ++ } ++ ++ if (timeout <= 0) ++ dev_err(d->ddev.dev, "DMA transfer could not be terminated\n"); ++ } ++ ++ vchan_get_all_descriptors(&c->vc, &head); ++ spin_unlock_irqrestore(&c->vc.lock, flags); ++ vchan_dma_desc_free_list(&c->vc, &head); ++ ++ return 0; ++} ++ ++static int bcm2708_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ++ unsigned long arg) ++{ ++ struct bcm2708_chan *c = to_bcm2708_dma_chan(chan); ++ ++ switch (cmd) { ++ case DMA_SLAVE_CONFIG: ++ return bcm2708_dma_slave_config(c, ++ (struct dma_slave_config *)arg); ++ ++ case DMA_TERMINATE_ALL: ++ return bcm2708_dma_terminate_all(c); ++ ++ default: ++ return -ENXIO; ++ } ++} ++ ++static int bcm2708_dma_chan_init(struct bcm2708_dmadev *d, void __iomem* chan_base, ++ int chan_id, int irq) ++{ ++ struct bcm2708_chan *c; ++ ++ c = devm_kzalloc(d->ddev.dev, sizeof(*c), GFP_KERNEL); ++ if (!c) ++ return -ENOMEM; ++ ++ c->vc.desc_free = bcm2708_dma_desc_free; ++ vchan_init(&c->vc, &d->ddev); ++ INIT_LIST_HEAD(&c->node); ++ ++ d->ddev.chancnt++; ++ ++ c->chan_base = chan_base; ++ c->ch = chan_id; ++ c->irq_number = irq; ++ ++ return 0; ++} ++ ++static void bcm2708_dma_free(struct bcm2708_dmadev *od) ++{ ++ while (!list_empty(&od->ddev.channels)) { ++ struct bcm2708_chan *c = list_first_entry(&od->ddev.channels, ++ struct bcm2708_chan, vc.chan.device_node); ++ ++ list_del(&c->vc.chan.device_node); ++ tasklet_kill(&c->vc.task); ++ } ++} ++ ++static int bcm2708_dma_probe(struct platform_device *pdev) ++{ ++ struct bcm2708_dmadev *od; ++ int rc, i; ++ ++ if (!pdev->dev.dma_mask) ++ pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; ++ ++ rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); ++ if (rc) ++ return rc; ++ dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); ++ ++ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL); ++ if (!od) ++ return -ENOMEM; ++ ++ pdev->dev.dma_parms = &od->dma_parms; ++ dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF); ++ ++ dma_cap_set(DMA_SLAVE, od->ddev.cap_mask); ++ dma_cap_set(DMA_CYCLIC, od->ddev.cap_mask); ++ od->ddev.device_alloc_chan_resources = bcm2708_dma_alloc_chan_resources; ++ od->ddev.device_free_chan_resources = bcm2708_dma_free_chan_resources; ++ od->ddev.device_tx_status = bcm2708_dma_tx_status; ++ od->ddev.device_issue_pending = bcm2708_dma_issue_pending; ++ od->ddev.device_prep_dma_cyclic = bcm2708_dma_prep_dma_cyclic; ++ od->ddev.device_control = bcm2708_dma_control; ++ od->ddev.dev = &pdev->dev; ++ INIT_LIST_HEAD(&od->ddev.channels); ++ spin_lock_init(&od->lock); ++ ++ platform_set_drvdata(pdev, od); ++ ++ for (i = 0; i < 16; i++) { ++ void __iomem* chan_base; ++ int chan_id, irq; ++ ++ chan_id = bcm_dma_chan_alloc(BCM_DMA_FEATURE_FAST, ++ &chan_base, ++ &irq); ++ ++ if (chan_id < 0) ++ break; ++ ++ rc = bcm2708_dma_chan_init(od, chan_base, chan_id, irq); ++ if (rc) { ++ bcm2708_dma_free(od); ++ return rc; ++ } ++ } ++ ++ rc = dma_async_device_register(&od->ddev); ++ if (rc) { ++ dev_err(&pdev->dev, ++ "Failed to register slave DMA engine device: %d\n", rc); ++ bcm2708_dma_free(od); ++ return rc; ++ } ++ ++ dev_dbg(&pdev->dev, "Load BCM2708 DMA engine driver\n"); ++ ++ return rc; ++} ++ ++static int bcm2708_dma_remove(struct platform_device *pdev) ++{ ++ struct bcm2708_dmadev *od = platform_get_drvdata(pdev); ++ ++ dma_async_device_unregister(&od->ddev); ++ bcm2708_dma_free(od); ++ ++ return 0; ++} ++ ++static struct platform_driver bcm2708_dma_driver = { ++ .probe = bcm2708_dma_probe, ++ .remove = bcm2708_dma_remove, ++ .driver = { ++ .name = "bcm2708-dmaengine", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static struct platform_device *pdev; ++ ++static const struct platform_device_info bcm2708_dma_dev_info = { ++ .name = "bcm2708-dmaengine", ++ .id = -1, ++}; ++ ++static int bcm2708_dma_init(void) ++{ ++ int rc = platform_driver_register(&bcm2708_dma_driver); ++ ++ if (rc == 0) { ++ pdev = platform_device_register_full(&bcm2708_dma_dev_info); ++ if (IS_ERR(pdev)) { ++ platform_driver_unregister(&bcm2708_dma_driver); ++ rc = PTR_ERR(pdev); ++ } ++ } ++ ++ return rc; ++} ++subsys_initcall(bcm2708_dma_init); ++ ++static void __exit bcm2708_dma_exit(void) ++{ ++ platform_device_unregister(pdev); ++ platform_driver_unregister(&bcm2708_dma_driver); ++} ++module_exit(bcm2708_dma_exit); ++ ++MODULE_ALIAS("platform:bcm2708-dma"); ++MODULE_DESCRIPTION("BCM2708 DMA engine driver"); ++MODULE_AUTHOR("Florian Meier "); ++MODULE_LICENSE("GPL v2"); +-- +1.8.5.1 + + +From 34f35bc2a8ea0856f25679a197b91462ec5df1ba Mon Sep 17 00:00:00 2001 +From: Liam Girdwood +Date: Mon, 16 Sep 2013 13:01:46 +0100 +Subject: [PATCH 82/91] ASoC: core: Add API for configuration of DAI BCLK ratio + +Some codec drivers when running in slave mode require that BCLK to sample rate ratio +is explicitly set by the machine driver as it may not be exactly rate * frame size. + +Extend the DAI API by adding :- + +int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio); + +Signed-off-by: Liam Girdwood +Signed-off-by: Mark Brown +--- + include/sound/soc-dai.h | 3 +++ + sound/soc/soc-core.c | 16 ++++++++++++++++ + 2 files changed, 19 insertions(+) + +diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h +index ae9a227..d8acf0c 100644 +--- a/include/sound/soc-dai.h ++++ b/include/sound/soc-dai.h +@@ -105,6 +105,8 @@ int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, + int snd_soc_dai_set_pll(struct snd_soc_dai *dai, + int pll_id, int source, unsigned int freq_in, unsigned int freq_out); + ++int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio); ++ + /* Digital Audio interface formatting */ + int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt); + +@@ -131,6 +133,7 @@ struct snd_soc_dai_ops { + int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out); + int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div); ++ int (*set_bclk_ratio)(struct snd_soc_dai *dai, unsigned int ratio); + + /* + * DAI format configuration +diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c +index 1a38be0..09e47c1 100644 +--- a/sound/soc/soc-core.c ++++ b/sound/soc/soc-core.c +@@ -3576,6 +3576,22 @@ int snd_soc_codec_set_pll(struct snd_soc_codec *codec, int pll_id, int source, + EXPORT_SYMBOL_GPL(snd_soc_codec_set_pll); + + /** ++ * snd_soc_dai_set_bclk_ratio - configure BCLK to sample rate ratio. ++ * @dai: DAI ++ * @ratio Ratio of BCLK to Sample rate. ++ * ++ * Configures the DAI for a preset BCLK to sample rate ratio. ++ */ ++int snd_soc_dai_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio) ++{ ++ if (dai->driver && dai->driver->ops->set_bclk_ratio) ++ return dai->driver->ops->set_bclk_ratio(dai, ratio); ++ else ++ return -EINVAL; ++} ++EXPORT_SYMBOL_GPL(snd_soc_dai_set_bclk_ratio); ++ ++/** + * snd_soc_dai_set_fmt - configure DAI hardware audio format. + * @dai: DAI + * @fmt: SND_SOC_DAIFMT_ format value. +-- +1.8.5.1 + + +From 851732349365674e6557a87c935bad26c624236a Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 14:33:38 +0100 +Subject: [PATCH 83/91] ASoC: Add support for BCM2708 + +This driver adds support for digital audio (I2S) +for the BCM2708 SoC that is used by the +Raspberry Pi. External audio codecs can be +connected to the Raspberry Pi via P5 header. + +It relies on cyclic DMA engine support for BCM2708. + +Signed-off-by: Florian Meier +--- + sound/soc/Kconfig | 1 + + sound/soc/Makefile | 1 + + sound/soc/bcm/Kconfig | 10 + + sound/soc/bcm/Makefile | 4 + + sound/soc/bcm/bcm2708-i2s.c | 940 ++++++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 956 insertions(+) + create mode 100644 sound/soc/bcm/Kconfig + create mode 100644 sound/soc/bcm/Makefile + create mode 100644 sound/soc/bcm/bcm2708-i2s.c + +diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig +index 5138b84..a5e3a70 100644 +--- a/sound/soc/Kconfig ++++ b/sound/soc/Kconfig +@@ -33,6 +33,7 @@ config SND_SOC_GENERIC_DMAENGINE_PCM + # All the supported SoCs + source "sound/soc/atmel/Kconfig" + source "sound/soc/au1x/Kconfig" ++source "sound/soc/bcm/Kconfig" + source "sound/soc/blackfin/Kconfig" + source "sound/soc/cirrus/Kconfig" + source "sound/soc/davinci/Kconfig" +diff --git a/sound/soc/Makefile b/sound/soc/Makefile +index 61a64d2..fe6f122 100644 +--- a/sound/soc/Makefile ++++ b/sound/soc/Makefile +@@ -10,6 +10,7 @@ obj-$(CONFIG_SND_SOC) += codecs/ + obj-$(CONFIG_SND_SOC) += generic/ + obj-$(CONFIG_SND_SOC) += atmel/ + obj-$(CONFIG_SND_SOC) += au1x/ ++obj-$(CONFIG_SND_SOC) += bcm/ + obj-$(CONFIG_SND_SOC) += blackfin/ + obj-$(CONFIG_SND_SOC) += cirrus/ + obj-$(CONFIG_SND_SOC) += davinci/ +diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig +new file mode 100644 +index 0000000..37c8f8c +--- /dev/null ++++ b/sound/soc/bcm/Kconfig +@@ -0,0 +1,10 @@ ++config SND_BCM2708_SOC_I2S ++ tristate "SoC Audio support for the Broadcom BCM2708 I2S module" ++ depends on MACH_BCM2708 ++ select REGMAP_MMIO ++ select SND_SOC_DMAENGINE_PCM ++ select SND_SOC_GENERIC_DMAENGINE_PCM ++ help ++ Say Y or M if you want to add support for codecs attached to ++ the BCM2708 I2S interface. You will also need ++ to select the audio interfaces to support below. +diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile +new file mode 100644 +index 0000000..486ea09 +--- /dev/null ++++ b/sound/soc/bcm/Makefile +@@ -0,0 +1,4 @@ ++# BCM2708 Platform Support ++snd-soc-bcm2708-i2s-objs := bcm2708-i2s.o ++ ++obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o +diff --git a/sound/soc/bcm/bcm2708-i2s.c b/sound/soc/bcm/bcm2708-i2s.c +new file mode 100644 +index 0000000..ebaf3d6 +--- /dev/null ++++ b/sound/soc/bcm/bcm2708-i2s.c +@@ -0,0 +1,940 @@ ++/* ++ * ALSA SoC I2S Audio Layer for Broadcom BCM2708 SoC ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * Based on ++ * Raspberry Pi PCM I2S ALSA Driver ++ * Copyright (c) by Phil Poole 2013 ++ * ++ * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor ++ * Vladimir Barinov, ++ * Copyright (C) 2007 MontaVista Software, Inc., ++ * ++ * OMAP ALSA SoC DAI driver using McBSP port ++ * Copyright (C) 2008 Nokia Corporation ++ * Contact: Jarkko Nikula ++ * Peter Ujfalusi ++ * ++ * Freescale SSI ALSA SoC Digital Audio Interface (DAI) driver ++ * Author: Timur Tabi ++ * Copyright 2007-2010 Freescale Semiconductor, Inc. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Clock registers */ ++#define BCM2708_CLK_PCMCTL_REG 0x00 ++#define BCM2708_CLK_PCMDIV_REG 0x04 ++ ++/* Clock register settings */ ++#define BCM2708_CLK_PASSWD (0x5a000000) ++#define BCM2708_CLK_PASSWD_MASK (0xff000000) ++#define BCM2708_CLK_MASH(v) ((v) << 9) ++#define BCM2708_CLK_FLIP BIT(8) ++#define BCM2708_CLK_BUSY BIT(7) ++#define BCM2708_CLK_KILL BIT(5) ++#define BCM2708_CLK_ENAB BIT(4) ++#define BCM2708_CLK_SRC(v) (v) ++ ++#define BCM2708_CLK_SHIFT (12) ++#define BCM2708_CLK_DIVI(v) ((v) << BCM2708_CLK_SHIFT) ++#define BCM2708_CLK_DIVF(v) (v) ++#define BCM2708_CLK_DIVF_MASK (0xFFF) ++ ++enum { ++ BCM2708_CLK_MASH_0 = 0, ++ BCM2708_CLK_MASH_1, ++ BCM2708_CLK_MASH_2, ++ BCM2708_CLK_MASH_3, ++}; ++ ++enum { ++ BCM2708_CLK_SRC_GND = 0, ++ BCM2708_CLK_SRC_OSC, ++ BCM2708_CLK_SRC_DBG0, ++ BCM2708_CLK_SRC_DBG1, ++ BCM2708_CLK_SRC_PLLA, ++ BCM2708_CLK_SRC_PLLC, ++ BCM2708_CLK_SRC_PLLD, ++ BCM2708_CLK_SRC_HDMI, ++}; ++ ++/* Most clocks are not useable (freq = 0) */ ++static const unsigned int bcm2708_clk_freq[BCM2708_CLK_SRC_HDMI+1] = { ++ [BCM2708_CLK_SRC_GND] = 0, ++ [BCM2708_CLK_SRC_OSC] = 19200000, ++ [BCM2708_CLK_SRC_DBG0] = 0, ++ [BCM2708_CLK_SRC_DBG1] = 0, ++ [BCM2708_CLK_SRC_PLLA] = 0, ++ [BCM2708_CLK_SRC_PLLC] = 0, ++ [BCM2708_CLK_SRC_PLLD] = 500000000, ++ [BCM2708_CLK_SRC_HDMI] = 0, ++}; ++ ++/* I2S registers */ ++#define BCM2708_I2S_CS_A_REG 0x00 ++#define BCM2708_I2S_FIFO_A_REG 0x04 ++#define BCM2708_I2S_MODE_A_REG 0x08 ++#define BCM2708_I2S_RXC_A_REG 0x0c ++#define BCM2708_I2S_TXC_A_REG 0x10 ++#define BCM2708_I2S_DREQ_A_REG 0x14 ++#define BCM2708_I2S_INTEN_A_REG 0x18 ++#define BCM2708_I2S_INTSTC_A_REG 0x1c ++#define BCM2708_I2S_GRAY_REG 0x20 ++ ++/* I2S register settings */ ++#define BCM2708_I2S_STBY BIT(25) ++#define BCM2708_I2S_SYNC BIT(24) ++#define BCM2708_I2S_RXSEX BIT(23) ++#define BCM2708_I2S_RXF BIT(22) ++#define BCM2708_I2S_TXE BIT(21) ++#define BCM2708_I2S_RXD BIT(20) ++#define BCM2708_I2S_TXD BIT(19) ++#define BCM2708_I2S_RXR BIT(18) ++#define BCM2708_I2S_TXW BIT(17) ++#define BCM2708_I2S_CS_RXERR BIT(16) ++#define BCM2708_I2S_CS_TXERR BIT(15) ++#define BCM2708_I2S_RXSYNC BIT(14) ++#define BCM2708_I2S_TXSYNC BIT(13) ++#define BCM2708_I2S_DMAEN BIT(9) ++#define BCM2708_I2S_RXTHR(v) ((v) << 7) ++#define BCM2708_I2S_TXTHR(v) ((v) << 5) ++#define BCM2708_I2S_RXCLR BIT(4) ++#define BCM2708_I2S_TXCLR BIT(3) ++#define BCM2708_I2S_TXON BIT(2) ++#define BCM2708_I2S_RXON BIT(1) ++#define BCM2708_I2S_EN (1) ++ ++#define BCM2708_I2S_CLKDIS BIT(28) ++#define BCM2708_I2S_PDMN BIT(27) ++#define BCM2708_I2S_PDME BIT(26) ++#define BCM2708_I2S_FRXP BIT(25) ++#define BCM2708_I2S_FTXP BIT(24) ++#define BCM2708_I2S_CLKM BIT(23) ++#define BCM2708_I2S_CLKI BIT(22) ++#define BCM2708_I2S_FSM BIT(21) ++#define BCM2708_I2S_FSI BIT(20) ++#define BCM2708_I2S_FLEN(v) ((v) << 10) ++#define BCM2708_I2S_FSLEN(v) (v) ++ ++#define BCM2708_I2S_CHWEX BIT(15) ++#define BCM2708_I2S_CHEN BIT(14) ++#define BCM2708_I2S_CHPOS(v) ((v) << 4) ++#define BCM2708_I2S_CHWID(v) (v) ++#define BCM2708_I2S_CH1(v) ((v) << 16) ++#define BCM2708_I2S_CH2(v) (v) ++ ++#define BCM2708_I2S_TX_PANIC(v) ((v) << 24) ++#define BCM2708_I2S_RX_PANIC(v) ((v) << 16) ++#define BCM2708_I2S_TX(v) ((v) << 8) ++#define BCM2708_I2S_RX(v) (v) ++ ++#define BCM2708_I2S_INT_RXERR BIT(3) ++#define BCM2708_I2S_INT_TXERR BIT(2) ++#define BCM2708_I2S_INT_RXR BIT(1) ++#define BCM2708_I2S_INT_TXW BIT(0) ++ ++/* I2S DMA interface */ ++#define BCM2708_I2S_FIFO_PHYSICAL_ADDR 0x7E203004 ++#define BCM2708_DMA_DREQ_PCM_TX 2 ++#define BCM2708_DMA_DREQ_PCM_RX 3 ++ ++/* General device struct */ ++struct bcm2708_i2s_dev { ++ struct device *dev; ++ struct snd_dmaengine_dai_dma_data dma_data[2]; ++ unsigned int fmt; ++ unsigned int bclk_ratio; ++ ++ struct regmap *i2s_regmap; ++ struct regmap *clk_regmap; ++}; ++ ++static void bcm2708_i2s_start_clock(struct bcm2708_i2s_dev *dev) ++{ ++ /* Start the clock if in master mode */ ++ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; ++ ++ switch (master) { ++ case SND_SOC_DAIFMT_CBS_CFS: ++ case SND_SOC_DAIFMT_CBS_CFM: ++ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, ++ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB, ++ BCM2708_CLK_PASSWD | BCM2708_CLK_ENAB); ++ break; ++ default: ++ break; ++ } ++} ++ ++static void bcm2708_i2s_stop_clock(struct bcm2708_i2s_dev *dev) ++{ ++ uint32_t clkreg; ++ int timeout = 1000; ++ ++ /* Stop clock */ ++ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, ++ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB, ++ BCM2708_CLK_PASSWD); ++ ++ /* Wait for the BUSY flag going down */ ++ while (--timeout) { ++ regmap_read(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, &clkreg); ++ if (!(clkreg & BCM2708_CLK_BUSY)) ++ break; ++ } ++ ++ if (!timeout) { ++ /* KILL the clock */ ++ dev_err(dev->dev, "I2S clock didn't stop. Kill the clock!\n"); ++ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, ++ BCM2708_CLK_KILL | BCM2708_CLK_PASSWD_MASK, ++ BCM2708_CLK_KILL | BCM2708_CLK_PASSWD); ++ } ++} ++ ++static void bcm2708_i2s_clear_fifos(struct bcm2708_i2s_dev *dev, ++ bool tx, bool rx) ++{ ++ int timeout = 1000; ++ uint32_t syncval; ++ uint32_t csreg; ++ uint32_t i2s_active_state; ++ uint32_t clkreg; ++ uint32_t clk_active_state; ++ uint32_t off; ++ uint32_t clr; ++ ++ off = tx ? BCM2708_I2S_TXON : 0; ++ off |= rx ? BCM2708_I2S_RXON : 0; ++ ++ clr = tx ? BCM2708_I2S_TXCLR : 0; ++ clr |= rx ? BCM2708_I2S_RXCLR : 0; ++ ++ /* Backup the current state */ ++ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg); ++ i2s_active_state = csreg & (BCM2708_I2S_RXON | BCM2708_I2S_TXON); ++ ++ regmap_read(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, &clkreg); ++ clk_active_state = clkreg & BCM2708_CLK_ENAB; ++ ++ /* Start clock if not running */ ++ if (!clk_active_state) { ++ regmap_update_bits(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, ++ BCM2708_CLK_PASSWD_MASK | BCM2708_CLK_ENAB, ++ BCM2708_CLK_PASSWD | BCM2708_CLK_ENAB); ++ } ++ ++ /* Stop I2S module */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, off, 0); ++ ++ /* ++ * Clear the FIFOs ++ * Requires at least 2 PCM clock cycles to take effect ++ */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, clr, clr); ++ ++ /* Wait for 2 PCM clock cycles */ ++ ++ /* ++ * Toggle the SYNC flag. After 2 PCM clock cycles it can be read back ++ * FIXME: This does not seem to work for slave mode! ++ */ ++ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &syncval); ++ syncval &= BCM2708_I2S_SYNC; ++ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_SYNC, ~syncval); ++ ++ /* Wait for the SYNC flag changing it's state */ ++ while (--timeout) { ++ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg); ++ if ((csreg & BCM2708_I2S_SYNC) != syncval) ++ break; ++ } ++ ++ if (!timeout) ++ dev_err(dev->dev, "I2S SYNC error!\n"); ++ ++ /* Stop clock if it was not running before */ ++ if (!clk_active_state) ++ bcm2708_i2s_stop_clock(dev); ++ ++ /* Restore I2S state */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_RXON | BCM2708_I2S_TXON, i2s_active_state); ++} ++ ++static int bcm2708_i2s_set_dai_fmt(struct snd_soc_dai *dai, ++ unsigned int fmt) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ dev->fmt = fmt; ++ return 0; ++} ++ ++static int bcm2708_i2s_set_dai_bclk_ratio(struct snd_soc_dai *dai, ++ unsigned int ratio) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ dev->bclk_ratio = ratio; ++ return 0; ++} ++ ++static int bcm2708_i2s_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params, ++ struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ ++ unsigned int sampling_rate = params_rate(params); ++ unsigned int data_length, data_delay, bclk_ratio; ++ unsigned int ch1pos, ch2pos, mode, format; ++ unsigned int mash = BCM2708_CLK_MASH_1; ++ unsigned int divi, divf, target_frequency; ++ int clk_src = -1; ++ unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; ++ bool bit_master = (master == SND_SOC_DAIFMT_CBS_CFS ++ || master == SND_SOC_DAIFMT_CBS_CFM); ++ ++ bool frame_master = (master == SND_SOC_DAIFMT_CBS_CFS ++ || master == SND_SOC_DAIFMT_CBM_CFS); ++ uint32_t csreg; ++ ++ /* ++ * If a stream is already enabled, ++ * the registers are already set properly. ++ */ ++ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &csreg); ++ ++ if (csreg & (BCM2708_I2S_TXON | BCM2708_I2S_RXON)) ++ return 0; ++ ++ /* ++ * Adjust the data length according to the format. ++ * We prefill the half frame length with an integer ++ * divider of 2400 as explained at the clock settings. ++ * Maybe it is overwritten there, if the Integer mode ++ * does not apply. ++ */ ++ switch (params_format(params)) { ++ case SNDRV_PCM_FORMAT_S16_LE: ++ data_length = 16; ++ bclk_ratio = 40; ++ break; ++ case SNDRV_PCM_FORMAT_S32_LE: ++ data_length = 32; ++ bclk_ratio = 80; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* If bclk_ratio already set, use that one. */ ++ if (dev->bclk_ratio) ++ bclk_ratio = dev->bclk_ratio; ++ ++ /* ++ * Clock Settings ++ * ++ * The target frequency of the bit clock is ++ * sampling rate * frame length ++ * ++ * Integer mode: ++ * Sampling rates that are multiples of 8000 kHz ++ * can be driven by the oscillator of 19.2 MHz ++ * with an integer divider as long as the frame length ++ * is an integer divider of 19200000/8000=2400 as set up above. ++ * This is no longer possible if the sampling rate ++ * is too high (e.g. 192 kHz), because the oscillator is too slow. ++ * ++ * MASH mode: ++ * For all other sampling rates, it is not possible to ++ * have an integer divider. Approximate the clock ++ * with the MASH module that induces a slight frequency ++ * variance. To minimize that it is best to have the fastest ++ * clock here. That is PLLD with 500 MHz. ++ */ ++ target_frequency = sampling_rate * bclk_ratio; ++ clk_src = BCM2708_CLK_SRC_OSC; ++ mash = BCM2708_CLK_MASH_0; ++ ++ if (bcm2708_clk_freq[clk_src] % target_frequency == 0 ++ && bit_master && frame_master) { ++ divi = bcm2708_clk_freq[clk_src] / target_frequency; ++ divf = 0; ++ } else { ++ uint64_t dividend; ++ ++ if (!dev->bclk_ratio) { ++ /* ++ * Overwrite bclk_ratio, because the ++ * above trick is not needed or can ++ * not be used. ++ */ ++ bclk_ratio = 2 * data_length; ++ } ++ ++ target_frequency = sampling_rate * bclk_ratio; ++ ++ clk_src = BCM2708_CLK_SRC_PLLD; ++ mash = BCM2708_CLK_MASH_1; ++ ++ dividend = bcm2708_clk_freq[clk_src]; ++ dividend <<= BCM2708_CLK_SHIFT; ++ do_div(dividend, target_frequency); ++ divi = dividend >> BCM2708_CLK_SHIFT; ++ divf = dividend & BCM2708_CLK_DIVF_MASK; ++ } ++ ++ /* Set clock divider */ ++ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMDIV_REG, BCM2708_CLK_PASSWD ++ | BCM2708_CLK_DIVI(divi) ++ | BCM2708_CLK_DIVF(divf)); ++ ++ /* Setup clock, but don't start it yet */ ++ regmap_write(dev->clk_regmap, BCM2708_CLK_PCMCTL_REG, BCM2708_CLK_PASSWD ++ | BCM2708_CLK_MASH(mash) ++ | BCM2708_CLK_SRC(clk_src)); ++ ++ /* Setup the frame format */ ++ format = BCM2708_I2S_CHEN; ++ ++ if (data_length > 24) ++ format |= BCM2708_I2S_CHWEX; ++ ++ format |= BCM2708_I2S_CHWID((data_length-8)&0xf); ++ ++ switch (dev->fmt & SND_SOC_DAIFMT_FORMAT_MASK) { ++ case SND_SOC_DAIFMT_I2S: ++ data_delay = 1; ++ break; ++ default: ++ /* ++ * TODO ++ * Others are possible but are not implemented at the moment. ++ */ ++ dev_err(dev->dev, "%s:bad format\n", __func__); ++ return -EINVAL; ++ } ++ ++ ch1pos = data_delay; ++ ch2pos = bclk_ratio / 2 + data_delay; ++ ++ switch (params_channels(params)) { ++ case 2: ++ format = BCM2708_I2S_CH1(format) | BCM2708_I2S_CH2(format); ++ format |= BCM2708_I2S_CH1(BCM2708_I2S_CHPOS(ch1pos)); ++ format |= BCM2708_I2S_CH2(BCM2708_I2S_CHPOS(ch2pos)); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ /* ++ * Set format for both streams. ++ * We cannot set another frame length ++ * (and therefore word length) anyway, ++ * so the format will be the same. ++ */ ++ regmap_write(dev->i2s_regmap, BCM2708_I2S_RXC_A_REG, format); ++ regmap_write(dev->i2s_regmap, BCM2708_I2S_TXC_A_REG, format); ++ ++ /* Setup the I2S mode */ ++ mode = 0; ++ ++ if (data_length <= 16) { ++ /* ++ * Use frame packed mode (2 channels per 32 bit word) ++ * We cannot set another frame length in the second stream ++ * (and therefore word length) anyway, ++ * so the format will be the same. ++ */ ++ mode |= BCM2708_I2S_FTXP | BCM2708_I2S_FRXP; ++ } ++ ++ mode |= BCM2708_I2S_FLEN(bclk_ratio - 1); ++ mode |= BCM2708_I2S_FSLEN(bclk_ratio / 2); ++ ++ /* Master or slave? */ ++ switch (dev->fmt & SND_SOC_DAIFMT_MASTER_MASK) { ++ case SND_SOC_DAIFMT_CBS_CFS: ++ /* CPU is master */ ++ break; ++ case SND_SOC_DAIFMT_CBM_CFS: ++ /* ++ * CODEC is bit clock master ++ * CPU is frame master ++ */ ++ mode |= BCM2708_I2S_CLKM; ++ break; ++ case SND_SOC_DAIFMT_CBS_CFM: ++ /* ++ * CODEC is frame master ++ * CPU is bit clock master ++ */ ++ mode |= BCM2708_I2S_FSM; ++ break; ++ case SND_SOC_DAIFMT_CBM_CFM: ++ /* CODEC is master */ ++ mode |= BCM2708_I2S_CLKM; ++ mode |= BCM2708_I2S_FSM; ++ break; ++ default: ++ dev_err(dev->dev, "%s:bad master\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* ++ * Invert clocks? ++ * ++ * The BCM approach seems to be inverted to the classical I2S approach. ++ */ ++ switch (dev->fmt & SND_SOC_DAIFMT_INV_MASK) { ++ case SND_SOC_DAIFMT_NB_NF: ++ /* None. Therefore, both for BCM */ ++ mode |= BCM2708_I2S_CLKI; ++ mode |= BCM2708_I2S_FSI; ++ break; ++ case SND_SOC_DAIFMT_IB_IF: ++ /* Both. Therefore, none for BCM */ ++ break; ++ case SND_SOC_DAIFMT_NB_IF: ++ /* ++ * Invert only frame sync. Therefore, ++ * invert only bit clock for BCM ++ */ ++ mode |= BCM2708_I2S_CLKI; ++ break; ++ case SND_SOC_DAIFMT_IB_NF: ++ /* ++ * Invert only bit clock. Therefore, ++ * invert only frame sync for BCM ++ */ ++ mode |= BCM2708_I2S_FSI; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ regmap_write(dev->i2s_regmap, BCM2708_I2S_MODE_A_REG, mode); ++ ++ /* Setup the DMA parameters */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_RXTHR(1) ++ | BCM2708_I2S_TXTHR(1) ++ | BCM2708_I2S_DMAEN, 0xffffffff); ++ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_DREQ_A_REG, ++ BCM2708_I2S_TX_PANIC(0x10) ++ | BCM2708_I2S_RX_PANIC(0x30) ++ | BCM2708_I2S_TX(0x30) ++ | BCM2708_I2S_RX(0x20), 0xffffffff); ++ ++ /* Clear FIFOs */ ++ bcm2708_i2s_clear_fifos(dev, true, true); ++ ++ return 0; ++} ++ ++static int bcm2708_i2s_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ uint32_t cs_reg; ++ ++ bcm2708_i2s_start_clock(dev); ++ ++ /* ++ * Clear both FIFOs if the one that should be started ++ * is not empty at the moment. This should only happen ++ * after overrun. Otherwise, hw_params would have cleared ++ * the FIFO. ++ */ ++ regmap_read(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, &cs_reg); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ++ && !(cs_reg & BCM2708_I2S_TXE)) ++ bcm2708_i2s_clear_fifos(dev, true, false); ++ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE ++ && (cs_reg & BCM2708_I2S_RXD)) ++ bcm2708_i2s_clear_fifos(dev, false, true); ++ ++ return 0; ++} ++ ++static void bcm2708_i2s_stop(struct bcm2708_i2s_dev *dev, ++ struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ uint32_t mask; ++ ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ mask = BCM2708_I2S_RXON; ++ else ++ mask = BCM2708_I2S_TXON; ++ ++ regmap_update_bits(dev->i2s_regmap, ++ BCM2708_I2S_CS_A_REG, mask, 0); ++ ++ /* Stop also the clock when not SND_SOC_DAIFMT_CONT */ ++ if (!dai->active && !(dev->fmt & SND_SOC_DAIFMT_CONT)) ++ bcm2708_i2s_stop_clock(dev); ++} ++ ++static int bcm2708_i2s_trigger(struct snd_pcm_substream *substream, int cmd, ++ struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ uint32_t mask; ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ case SNDRV_PCM_TRIGGER_RESUME: ++ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: ++ bcm2708_i2s_start_clock(dev); ++ ++ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ++ mask = BCM2708_I2S_RXON; ++ else ++ mask = BCM2708_I2S_TXON; ++ ++ regmap_update_bits(dev->i2s_regmap, ++ BCM2708_I2S_CS_A_REG, mask, mask); ++ break; ++ ++ case SNDRV_PCM_TRIGGER_STOP: ++ case SNDRV_PCM_TRIGGER_SUSPEND: ++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH: ++ bcm2708_i2s_stop(dev, substream, dai); ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++static int bcm2708_i2s_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ ++ if (dai->active) ++ return 0; ++ ++ /* Should this still be running stop it */ ++ bcm2708_i2s_stop_clock(dev); ++ ++ /* Enable PCM block */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_EN, BCM2708_I2S_EN); ++ ++ /* ++ * Disable STBY. ++ * Requires at least 4 PCM clock cycles to take effect. ++ */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_STBY, BCM2708_I2S_STBY); ++ ++ return 0; ++} ++ ++static void bcm2708_i2s_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ ++ bcm2708_i2s_stop(dev, substream, dai); ++ ++ /* If both streams are stopped, disable module and clock */ ++ if (dai->active) ++ return; ++ ++ /* Disable the module */ ++ regmap_update_bits(dev->i2s_regmap, BCM2708_I2S_CS_A_REG, ++ BCM2708_I2S_EN, 0); ++ ++ /* ++ * Stopping clock is necessary, because stop does ++ * not stop the clock when SND_SOC_DAIFMT_CONT ++ */ ++ bcm2708_i2s_stop_clock(dev); ++} ++ ++static const struct snd_soc_dai_ops bcm2708_i2s_dai_ops = { ++ .startup = bcm2708_i2s_startup, ++ .shutdown = bcm2708_i2s_shutdown, ++ .prepare = bcm2708_i2s_prepare, ++ .trigger = bcm2708_i2s_trigger, ++ .hw_params = bcm2708_i2s_hw_params, ++ .set_fmt = bcm2708_i2s_set_dai_fmt, ++ .set_bclk_ratio = bcm2708_i2s_set_dai_bclk_ratio ++}; ++ ++static int bcm2708_i2s_dai_probe(struct snd_soc_dai *dai) ++{ ++ struct bcm2708_i2s_dev *dev = snd_soc_dai_get_drvdata(dai); ++ ++ dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; ++ dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; ++ ++ return 0; ++} ++ ++static struct snd_soc_dai_driver bcm2708_i2s_dai = { ++ .name = "bcm2708-i2s", ++ .probe = bcm2708_i2s_dai_probe, ++ .playback = { ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE ++ | SNDRV_PCM_FMTBIT_S32_LE ++ }, ++ .capture = { ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE ++ | SNDRV_PCM_FMTBIT_S32_LE ++ }, ++ .ops = &bcm2708_i2s_dai_ops, ++ .symmetric_rates = 1 ++}; ++ ++static bool bcm2708_i2s_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case BCM2708_I2S_CS_A_REG: ++ case BCM2708_I2S_FIFO_A_REG: ++ case BCM2708_I2S_INTSTC_A_REG: ++ case BCM2708_I2S_GRAY_REG: ++ return true; ++ default: ++ return false; ++ }; ++} ++ ++static bool bcm2708_i2s_precious_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case BCM2708_I2S_FIFO_A_REG: ++ return true; ++ default: ++ return false; ++ }; ++} ++ ++static bool bcm2708_clk_volatile_reg(struct device *dev, unsigned int reg) ++{ ++ switch (reg) { ++ case BCM2708_CLK_PCMCTL_REG: ++ return true; ++ default: ++ return false; ++ }; ++} ++ ++static const struct regmap_config bcm2708_regmap_config[] = { ++ { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = BCM2708_I2S_GRAY_REG, ++ .precious_reg = bcm2708_i2s_precious_reg, ++ .volatile_reg = bcm2708_i2s_volatile_reg, ++ .cache_type = REGCACHE_RBTREE, ++ }, ++ { ++ .reg_bits = 32, ++ .reg_stride = 4, ++ .val_bits = 32, ++ .max_register = BCM2708_CLK_PCMDIV_REG, ++ .volatile_reg = bcm2708_clk_volatile_reg, ++ .cache_type = REGCACHE_RBTREE, ++ }, ++}; ++ ++static const struct snd_soc_component_driver bcm2708_i2s_component = { ++ .name = "bcm2708-i2s-comp", ++}; ++ ++ ++static void bcm2708_i2s_setup_gpio(void) ++{ ++ /* ++ * This is the common way to handle the GPIO pins for ++ * the Raspberry Pi. ++ * TODO Better way would be to handle ++ * this in the device tree! ++ */ ++#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3)) ++#define SET_GPIO_ALT(g,a) *(gpio+(((g)/10))) |= (((a)<=3?(a)+4:(a)==4?3:2)<<(((g)%10)*3)) ++ ++ unsigned int *gpio; ++ int pin; ++ gpio = ioremap(GPIO_BASE, SZ_16K); ++ ++ /* SPI is on GPIO 7..11 */ ++ for (pin = 28; pin <= 31; pin++) { ++ INP_GPIO(pin); /* set mode to GPIO input first */ ++ SET_GPIO_ALT(pin, 2); /* set mode to ALT 0 */ ++ } ++#undef INP_GPIO ++#undef SET_GPIO_ALT ++} ++ ++static const struct snd_pcm_hardware bcm2708_pcm_hardware = { ++ .info = SNDRV_PCM_INFO_MMAP | ++ SNDRV_PCM_INFO_MMAP_VALID | ++ SNDRV_PCM_INFO_INTERLEAVED | ++ SNDRV_PCM_INFO_JOINT_DUPLEX, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S32_LE, ++ .period_bytes_min = 32, ++ .period_bytes_max = 64 * PAGE_SIZE, ++ .periods_min = 2, ++ .periods_max = 255, ++ .buffer_bytes_max = 128 * PAGE_SIZE, ++}; ++ ++static const struct snd_dmaengine_pcm_config bcm2708_dmaengine_pcm_config = { ++ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, ++ .pcm_hardware = &bcm2708_pcm_hardware, ++ .prealloc_buffer_size = 256 * PAGE_SIZE, ++}; ++ ++ ++static int bcm2708_i2s_probe(struct platform_device *pdev) ++{ ++ struct bcm2708_i2s_dev *dev; ++ int i; ++ int ret; ++ struct regmap *regmap[2]; ++ struct resource *mem[2]; ++ ++ /* Request both ioareas */ ++ for (i = 0; i <= 1; i++) { ++ void __iomem *base; ++ ++ mem[i] = platform_get_resource(pdev, IORESOURCE_MEM, i); ++ base = devm_ioremap_resource(&pdev->dev, mem[i]); ++ if (IS_ERR(base)) ++ return PTR_ERR(base); ++ ++ regmap[i] = devm_regmap_init_mmio(&pdev->dev, base, ++ &bcm2708_regmap_config[i]); ++ if (IS_ERR(regmap[i])) { ++ dev_err(&pdev->dev, "I2S probe: regmap init failed\n"); ++ return PTR_ERR(regmap[i]); ++ } ++ } ++ ++ dev = devm_kzalloc(&pdev->dev, sizeof(*dev), ++ GFP_KERNEL); ++ if (IS_ERR(dev)) ++ return PTR_ERR(dev); ++ ++ bcm2708_i2s_setup_gpio(); ++ ++ dev->i2s_regmap = regmap[0]; ++ dev->clk_regmap = regmap[1]; ++ ++ /* Set the DMA address */ ++ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = ++ (dma_addr_t)BCM2708_I2S_FIFO_PHYSICAL_ADDR; ++ ++ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = ++ (dma_addr_t)BCM2708_I2S_FIFO_PHYSICAL_ADDR; ++ ++ /* Set the DREQ */ ++ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].slave_id = ++ BCM2708_DMA_DREQ_PCM_TX; ++ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].slave_id = ++ BCM2708_DMA_DREQ_PCM_RX; ++ ++ /* Set the bus width */ ++ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width = ++ DMA_SLAVE_BUSWIDTH_4_BYTES; ++ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr_width = ++ DMA_SLAVE_BUSWIDTH_4_BYTES; ++ ++ /* Set burst */ ++ dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].maxburst = 2; ++ dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].maxburst = 2; ++ ++ /* BCLK ratio - use default */ ++ dev->bclk_ratio = 0; ++ ++ /* Store the pdev */ ++ dev->dev = &pdev->dev; ++ dev_set_drvdata(&pdev->dev, dev); ++ ++ ret = snd_soc_register_component(&pdev->dev, ++ &bcm2708_i2s_component, &bcm2708_i2s_dai, 1); ++ ++ if (ret) { ++ dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); ++ ret = -ENOMEM; ++ return ret; ++ } ++ ++ ret = snd_dmaengine_pcm_register(&pdev->dev, ++ &bcm2708_dmaengine_pcm_config, ++ SND_DMAENGINE_PCM_FLAG_COMPAT); ++ if (ret) { ++ dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); ++ snd_soc_unregister_component(&pdev->dev); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int bcm2708_i2s_remove(struct platform_device *pdev) ++{ ++ snd_dmaengine_pcm_unregister(&pdev->dev); ++ snd_soc_unregister_component(&pdev->dev); ++ return 0; ++} ++ ++static struct platform_driver bcm2708_i2s_driver = { ++ .probe = bcm2708_i2s_probe, ++ .remove = bcm2708_i2s_remove, ++ .driver = { ++ .name = "bcm2708-i2s", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++module_platform_driver(bcm2708_i2s_driver); ++ ++MODULE_ALIAS("platform:bcm2708-i2s"); ++MODULE_DESCRIPTION("BCM2708 I2S interface"); ++MODULE_AUTHOR("Florian Meier "); ++MODULE_LICENSE("GPL v2"); +-- +1.8.5.1 + + +From d702b3d54869d8d0f2b76647840fec336e4ed00b Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 14:37:51 +0100 +Subject: [PATCH 84/91] BCM2708: Extend mach header + +Extend the headers of the mach-bcm2708 +in order to support I2S and DMA engine. + +Signed-off-by: Florian Meier +--- + arch/arm/mach-bcm2708/include/mach/dma.h | 2 ++ + arch/arm/mach-bcm2708/include/mach/platform.h | 2 ++ + 2 files changed, 4 insertions(+) + +diff --git a/arch/arm/mach-bcm2708/include/mach/dma.h b/arch/arm/mach-bcm2708/include/mach/dma.h +index ac7a4a0..f2568d4 100644 +--- a/arch/arm/mach-bcm2708/include/mach/dma.h ++++ b/arch/arm/mach-bcm2708/include/mach/dma.h +@@ -45,6 +45,8 @@ + #define BCM2708_DMA_ADDR 0x04 + /* the current control block appears in the following registers - read only */ + #define BCM2708_DMA_INFO 0x08 ++#define BCM2708_DMA_SOURCE_AD 0x0c ++#define BCM2708_DMA_DEST_AD 0x10 + #define BCM2708_DMA_NEXTCB 0x1C + #define BCM2708_DMA_DEBUG 0x20 + +diff --git a/arch/arm/mach-bcm2708/include/mach/platform.h b/arch/arm/mach-bcm2708/include/mach/platform.h +index 992a630..2e7e1bb 100644 +--- a/arch/arm/mach-bcm2708/include/mach/platform.h ++++ b/arch/arm/mach-bcm2708/include/mach/platform.h +@@ -62,10 +62,12 @@ + #define DMA_BASE (BCM2708_PERI_BASE + 0x7000) /* DMA controller */ + #define ARM_BASE (BCM2708_PERI_BASE + 0xB000) /* BCM2708 ARM control block */ + #define PM_BASE (BCM2708_PERI_BASE + 0x100000) /* Power Management, Reset controller and Watchdog registers */ ++#define PCM_CLOCK_BASE (BCM2708_PERI_BASE + 0x101098) /* PCM Clock */ + #define RNG_BASE (BCM2708_PERI_BASE + 0x104000) /* Hardware RNG */ + #define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO */ + #define UART0_BASE (BCM2708_PERI_BASE + 0x201000) /* Uart 0 */ + #define MMCI0_BASE (BCM2708_PERI_BASE + 0x202000) /* MMC interface */ ++#define I2S_BASE (BCM2708_PERI_BASE + 0x203000) /* I2S */ + #define SPI0_BASE (BCM2708_PERI_BASE + 0x204000) /* SPI0 */ + #define BSC0_BASE (BCM2708_PERI_BASE + 0x205000) /* BSC0 I2C/TWI */ + #define UART1_BASE (BCM2708_PERI_BASE + 0x215000) /* Uart 1 */ +-- +1.8.5.1 + + +From bad2eb3fb3e365ab242754ba2842700b37bf4343 Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 14:59:51 +0100 +Subject: [PATCH 85/91] ASoC: Add support for PCM5102A codec + +Some definitions to support the PCM5102A codec +by Texas Instruments. + +Signed-off-by: Florian Meier +--- + sound/soc/codecs/Kconfig | 4 +++ + sound/soc/codecs/Makefile | 2 ++ + sound/soc/codecs/pcm5102a.c | 63 +++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 69 insertions(+) + create mode 100644 sound/soc/codecs/pcm5102a.c + +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index b33b45d..34d4d55 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -59,6 +59,7 @@ config SND_SOC_ALL_CODECS + select SND_SOC_PCM1681 if I2C + select SND_SOC_PCM1792A if SPI_MASTER + select SND_SOC_PCM3008 ++ select SND_SOC_PCM5102A + select SND_SOC_RT5631 if I2C + select SND_SOC_RT5640 if I2C + select SND_SOC_SGTL5000 if I2C +@@ -311,6 +312,9 @@ config SND_SOC_PCM1792A + config SND_SOC_PCM3008 + tristate + ++config SND_SOC_PCM5102A ++ tristate ++ + config SND_SOC_RT5631 + tristate + +diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile +index bc12676..612f414 100644 +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -46,6 +46,7 @@ snd-soc-hdmi-codec-objs := hdmi.o + snd-soc-pcm1681-objs := pcm1681.o + snd-soc-pcm1792a-codec-objs := pcm1792a.o + snd-soc-pcm3008-objs := pcm3008.o ++snd-soc-pcm5102a-objs := pcm5102a.o + snd-soc-rt5631-objs := rt5631.o + snd-soc-rt5640-objs := rt5640.o + snd-soc-sgtl5000-objs := sgtl5000.o +@@ -179,6 +180,7 @@ obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o + obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o + obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o + obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o ++obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o + obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o + obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o + obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o +diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c +new file mode 100644 +index 0000000..126f1e9 +--- /dev/null ++++ b/sound/soc/codecs/pcm5102a.c +@@ -0,0 +1,63 @@ ++/* ++ * Driver for the PCM5102A codec ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++ ++#include ++#include ++#include ++ ++#include ++ ++static struct snd_soc_dai_driver pcm5102a_dai = { ++ .name = "pcm5102a-hifi", ++ .playback = { ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S24_LE | ++ SNDRV_PCM_FMTBIT_S32_LE ++ }, ++}; ++ ++static struct snd_soc_codec_driver soc_codec_dev_pcm5102a; ++ ++static int pcm5102a_probe(struct platform_device *pdev) ++{ ++ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm5102a, ++ &pcm5102a_dai, 1); ++} ++ ++static int pcm5102a_remove(struct platform_device *pdev) ++{ ++ snd_soc_unregister_codec(&pdev->dev); ++ return 0; ++} ++ ++static struct platform_driver pcm5102a_codec_driver = { ++ .probe = pcm5102a_probe, ++ .remove = pcm5102a_remove, ++ .driver = { ++ .name = "pcm5102a-codec", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++module_platform_driver(pcm5102a_codec_driver); ++ ++MODULE_DESCRIPTION("ASoC PCM5102A codec driver"); ++MODULE_AUTHOR("Florian Meier "); ++MODULE_LICENSE("GPL v2"); +-- +1.8.5.1 + + +From 990742b03a3a107ff9239b677014f6cd3f0b72d8 Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 19:04:54 +0100 +Subject: [PATCH 86/91] BCM2708: Add I2S support to board file + +Adds the required initializations for I2S +to the board file of mach-bcm2708. + +Signed-off-by: Florian Meier +--- + arch/arm/mach-bcm2708/bcm2708.c | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index a388dd8..c3e36fe 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -617,6 +617,28 @@ struct platform_device bcm2708_powerman_device = { + .name = "bcm2835_thermal", + }; + ++#ifdef CONFIG_SND_BCM2708_SOC_I2S_MODULE ++static struct resource bcm2708_i2s_resources[] = { ++ { ++ .start = I2S_BASE, ++ .end = I2S_BASE + 0x20, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = PCM_CLOCK_BASE, ++ .end = PCM_CLOCK_BASE + 0x02, ++ .flags = IORESOURCE_MEM, ++ } ++}; ++ ++static struct platform_device bcm2708_i2s_device = { ++ .name = "bcm2708-i2s", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_i2s_resources), ++ .resource = bcm2708_i2s_resources, ++}; ++#endif ++ + int __init bcm_register_device(struct platform_device *pdev) + { + int ret; +@@ -743,6 +765,10 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2835_hwmon_device); + bcm_register_device(&bcm2835_thermal_device); + ++#ifdef CONFIG_SND_BCM2708_SOC_I2S_MODULE ++ bcm_register_device(&bcm2708_i2s_device); ++#endif ++ + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); +-- +1.8.5.1 + + +From 0ac91e6dda915e220af648c746dae7be7619bd18 Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 19:19:08 +0100 +Subject: [PATCH 87/91] ASoC: Add support for HifiBerry DAC + +This adds a machine driver for the HifiBerry DAC. +It is a sound card that can +be stacked onto the Raspberry Pi. + +Signed-off-by: Florian Meier +--- + sound/soc/bcm/Kconfig | 7 +++ + sound/soc/bcm/Makefile | 5 +++ + sound/soc/bcm/hifiberry_dac.c | 100 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 112 insertions(+) + create mode 100644 sound/soc/bcm/hifiberry_dac.c + +diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig +index 37c8f8c..8b14e37 100644 +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -8,3 +8,10 @@ config SND_BCM2708_SOC_I2S + Say Y or M if you want to add support for codecs attached to + the BCM2708 I2S interface. You will also need + to select the audio interfaces to support below. ++ ++config SND_BCM2708_SOC_HIFIBERRY_DAC ++ tristate "Support for HifiBerry DAC" ++ depends on SND_BCM2708_SOC_I2S ++ select SND_SOC_PCM5102A ++ help ++ Say Y or M if you want to add support for HifiBerry DAC. +diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile +index 486ea09..6834b4d 100644 +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -2,3 +2,8 @@ + snd-soc-bcm2708-i2s-objs := bcm2708-i2s.o + + obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o ++ ++# BCM2708 Machine Support ++snd-soc-hifiberry-dac-objs := hifiberry_dac.o ++ ++obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o +diff --git a/sound/soc/bcm/hifiberry_dac.c b/sound/soc/bcm/hifiberry_dac.c +new file mode 100644 +index 0000000..4b70b45 +--- /dev/null ++++ b/sound/soc/bcm/hifiberry_dac.c +@@ -0,0 +1,100 @@ ++/* ++ * ASoC Driver for HifiBerry DAC ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++static int snd_rpi_hifiberry_dac_init(struct snd_soc_pcm_runtime *rtd) ++{ ++ return 0; ++} ++ ++static int snd_rpi_hifiberry_dac_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; ++ ++ unsigned int sample_bits = ++ snd_pcm_format_physical_width(params_format(params)); ++ ++ return snd_soc_dai_set_bclk_ratio(cpu_dai, sample_bits * 2); ++} ++ ++/* machine stream operations */ ++static struct snd_soc_ops snd_rpi_hifiberry_dac_ops = { ++ .hw_params = snd_rpi_hifiberry_dac_hw_params, ++}; ++ ++static struct snd_soc_dai_link snd_rpi_hifiberry_dac_dai[] = { ++{ ++ .name = "HifiBerry DAC", ++ .stream_name = "HifiBerry DAC HiFi", ++ .cpu_dai_name = "bcm2708-i2s.0", ++ .codec_dai_name = "pcm5102a-hifi", ++ .platform_name = "bcm2708-i2s.0", ++ .codec_name = "pcm5102a-codec", ++ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBS_CFS, ++ .ops = &snd_rpi_hifiberry_dac_ops, ++ .init = snd_rpi_hifiberry_dac_init, ++}, ++}; ++ ++/* audio machine driver */ ++static struct snd_soc_card snd_rpi_hifiberry_dac = { ++ .name = "snd_rpi_hifiberry_dac", ++ .dai_link = snd_rpi_hifiberry_dac_dai, ++ .num_links = ARRAY_SIZE(snd_rpi_hifiberry_dac_dai), ++}; ++ ++static int snd_rpi_hifiberry_dac_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ snd_rpi_hifiberry_dac.dev = &pdev->dev; ++ ret = snd_soc_register_card(&snd_rpi_hifiberry_dac); ++ if (ret) ++ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int snd_rpi_hifiberry_dac_remove(struct platform_device *pdev) ++{ ++ return snd_soc_unregister_card(&snd_rpi_hifiberry_dac); ++} ++ ++static struct platform_driver snd_rpi_hifiberry_dac_driver = { ++ .driver = { ++ .name = "snd-hifiberry-dac", ++ .owner = THIS_MODULE, ++ }, ++ .probe = snd_rpi_hifiberry_dac_probe, ++ .remove = snd_rpi_hifiberry_dac_remove, ++}; ++ ++module_platform_driver(snd_rpi_hifiberry_dac_driver); ++ ++MODULE_AUTHOR("Florian Meier "); ++MODULE_DESCRIPTION("ASoC Driver for HifiBerry DAC"); ++MODULE_LICENSE("GPL v2"); +-- +1.8.5.1 + + +From db129e91b4bc3a2c9ee496637c06158618e94b70 Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 22 Nov 2013 19:21:34 +0100 +Subject: [PATCH 88/91] BCM2708: Add HifiBerry DAC to board file + +This adds the initalization of the HifiBerry DAC +to the mach-bcm2708 board file. + +Signed-off-by: Florian Meier +--- + arch/arm/mach-bcm2708/bcm2708.c | 19 +++++++++++++++++++ + 1 file changed, 19 insertions(+) + +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index c3e36fe..ce4acad 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -639,6 +639,20 @@ struct platform_device bcm2708_powerman_device = { + }; + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) ++static struct platform_device snd_hifiberry_dac_device = { ++ .name = "snd-hifiberry-dac", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct platform_device snd_pcm5102a_codec_device = { ++ .name = "pcm5102a-codec", ++ .id = -1, ++ .num_resources = 0, ++}; ++#endif ++ + int __init bcm_register_device(struct platform_device *pdev) + { + int ret; +@@ -769,6 +783,11 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2708_i2s_device); + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) || defined(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC_MODULE) ++ bcm_register_device(&snd_hifiberry_dac_device); ++ bcm_register_device(&snd_pcm5102a_codec_device); ++#endif ++ + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); +-- +1.8.5.1 + + +From 8945296d2a662728b1b114bcb0fc37771da47cad Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 6 Dec 2013 18:55:53 +0100 +Subject: [PATCH 89/91] ASoC: BCM2708: Add 24 bit support + +This adds 24 bit support to the I2S driver of the BCM2708. +Besides enabling the 24 bit flags, it includes two bug fixes: + +MMAP is not supported. Claiming this leads to strange issues +when the format of driver and file do not match. + +The datasheet states that the width extension bit should be set +for widths greater than 24, but greater or equal would be correct. +This follows from the definition of the width field. + +Signed-off-by: Florian Meier +--- + sound/soc/bcm/bcm2708-i2s.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/sound/soc/bcm/bcm2708-i2s.c b/sound/soc/bcm/bcm2708-i2s.c +index ebaf3d6..a179216 100644 +--- a/sound/soc/bcm/bcm2708-i2s.c ++++ b/sound/soc/bcm/bcm2708-i2s.c +@@ -346,6 +346,10 @@ static int bcm2708_i2s_hw_params(struct snd_pcm_substream *substream, + data_length = 16; + bclk_ratio = 40; + break; ++ case SNDRV_PCM_FORMAT_S24_LE: ++ data_length = 24; ++ bclk_ratio = 40; ++ break; + case SNDRV_PCM_FORMAT_S32_LE: + data_length = 32; + bclk_ratio = 80; +@@ -424,7 +428,7 @@ static int bcm2708_i2s_hw_params(struct snd_pcm_substream *substream, + /* Setup the frame format */ + format = BCM2708_I2S_CHEN; + +- if (data_length > 24) ++ if (data_length >= 24) + format |= BCM2708_I2S_CHWEX; + + format |= BCM2708_I2S_CHWID((data_length-8)&0xf); +@@ -714,6 +718,7 @@ static int bcm2708_i2s_dai_probe(struct snd_soc_dai *dai) + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE ++ | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S32_LE + }, + .capture = { +@@ -721,6 +726,7 @@ static int bcm2708_i2s_dai_probe(struct snd_soc_dai *dai) + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE ++ | SNDRV_PCM_FMTBIT_S24_LE + | SNDRV_PCM_FMTBIT_S32_LE + }, + .ops = &bcm2708_i2s_dai_ops, +@@ -810,11 +816,10 @@ static void bcm2708_i2s_setup_gpio(void) + } + + static const struct snd_pcm_hardware bcm2708_pcm_hardware = { +- .info = SNDRV_PCM_INFO_MMAP | +- SNDRV_PCM_INFO_MMAP_VALID | +- SNDRV_PCM_INFO_INTERLEAVED | ++ .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_JOINT_DUPLEX, + .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .period_bytes_min = 32, + .period_bytes_max = 64 * PAGE_SIZE, +-- +1.8.5.1 + + +From 07132c6890d9f6eefe217177962f9ba09b038666 Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Mon, 2 Dec 2013 20:28:22 +0100 +Subject: [PATCH 90/91] BCM2708: Add I2S and DMA support to default config + +This commit adds several modules that are needed for +I2S support for the Raspberry Pi to the defconfig. + +Signed-off-by: Florian Meier +--- + arch/arm/configs/bcmrpi_defconfig | 11 +++++++++++ + 1 file changed, 11 insertions(+) + +diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig +index 4032d53..d60ba43 100644 +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -733,6 +733,13 @@ CONFIG_SND_USB_UA101=m + CONFIG_SND_USB_CAIAQ=m + CONFIG_SND_USB_CAIAQ_INPUT=y + CONFIG_SND_USB_6FIRE=m ++CONFIG_SND_SOC=m ++CONFIG_SND_SOC_DMAENGINE_PCM=y ++CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y ++CONFIG_SND_BCM2708_SOC_I2S=m ++CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m ++CONFIG_SND_SOC_I2C_AND_SPI=m ++CONFIG_SND_SOC_PCM5102A=m + CONFIG_SOUND_PRIME=m + CONFIG_HIDRAW=y + CONFIG_HID_A4TECH=m +@@ -924,6 +931,10 @@ CONFIG_RTC_DRV_RS5C348=m + CONFIG_RTC_DRV_DS3234=m + CONFIG_RTC_DRV_PCF2123=m + CONFIG_RTC_DRV_RX4581=m ++CONFIG_DMADEVICES=y ++CONFIG_DMA_BCM2708=m ++CONFIG_DMA_ENGINE=y ++CONFIG_DMA_VIRTUAL_CHANNELS=m + CONFIG_UIO=m + CONFIG_UIO_PDRV=m + CONFIG_UIO_PDRV_GENIRQ=m +-- +1.8.5.1 + + +From 8b2af1c65260f579a54b2826d5a364fa22dfac90 Mon Sep 17 00:00:00 2001 +From: Florian Meier +Date: Fri, 6 Dec 2013 20:50:28 +0100 +Subject: [PATCH 91/91] ASoC: BCM2708: Add support for RPi-DAC + +This adds a machine driver for the RPi-DAC. + +Signed-off-by: Florian Meier +--- + arch/arm/configs/bcmrpi_defconfig | 2 + + arch/arm/mach-bcm2708/bcm2708.c | 19 ++++++++ + sound/soc/bcm/Kconfig | 7 +++ + sound/soc/bcm/Makefile | 2 + + sound/soc/bcm/rpi-dac.c | 97 +++++++++++++++++++++++++++++++++++++++ + sound/soc/codecs/Kconfig | 4 ++ + sound/soc/codecs/Makefile | 2 + + sound/soc/codecs/pcm1794a.c | 62 +++++++++++++++++++++++++ + 8 files changed, 195 insertions(+) + create mode 100644 sound/soc/bcm/rpi-dac.c + create mode 100644 sound/soc/codecs/pcm1794a.c + +diff --git a/arch/arm/configs/bcmrpi_defconfig b/arch/arm/configs/bcmrpi_defconfig +index d60ba43..74628c9 100644 +--- a/arch/arm/configs/bcmrpi_defconfig ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -738,8 +738,10 @@ CONFIG_SND_SOC_DMAENGINE_PCM=y + CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y + CONFIG_SND_BCM2708_SOC_I2S=m + CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC=m ++CONFIG_SND_BCM2708_SOC_RPI_DAC=m + CONFIG_SND_SOC_I2C_AND_SPI=m + CONFIG_SND_SOC_PCM5102A=m ++CONFIG_SND_SOC_PCM1794A=m + CONFIG_SOUND_PRIME=m + CONFIG_HIDRAW=y + CONFIG_HID_A4TECH=m +diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c +index ce4acad..4ab2609 100644 +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -653,6 +653,20 @@ struct platform_device bcm2708_powerman_device = { + }; + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) ++static struct platform_device snd_rpi_dac_device = { ++ .name = "snd-rpi-dac", ++ .id = 0, ++ .num_resources = 0, ++}; ++ ++static struct platform_device snd_pcm1794a_codec_device = { ++ .name = "pcm1794a-codec", ++ .id = -1, ++ .num_resources = 0, ++}; ++#endif ++ + int __init bcm_register_device(struct platform_device *pdev) + { + int ret; +@@ -788,6 +802,11 @@ void __init bcm2708_init(void) + bcm_register_device(&snd_pcm5102a_codec_device); + #endif + ++#if defined(CONFIG_SND_BCM2708_SOC_RPI_DAC) || defined(CONFIG_SND_BCM2708_SOC_RPI_DAC_MODULE) ++ bcm_register_device(&snd_rpi_dac_device); ++ bcm_register_device(&snd_pcm1794a_codec_device); ++#endif ++ + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); +diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig +index 8b14e37..1c1f5cf 100644 +--- a/sound/soc/bcm/Kconfig ++++ b/sound/soc/bcm/Kconfig +@@ -15,3 +15,10 @@ config SND_BCM2708_SOC_HIFIBERRY_DAC + select SND_SOC_PCM5102A + help + Say Y or M if you want to add support for HifiBerry DAC. ++ ++config SND_BCM2708_SOC_RPI_DAC ++ tristate "Support for RPi-DAC" ++ depends on SND_BCM2708_SOC_I2S ++ select SND_SOC_PCM1794A ++ help ++ Say Y or M if you want to add support for RPi-DAC. +\ No newline at end of file +diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile +index 6834b4d..95a9d44 100644 +--- a/sound/soc/bcm/Makefile ++++ b/sound/soc/bcm/Makefile +@@ -5,5 +5,7 @@ obj-$(CONFIG_SND_BCM2708_SOC_I2S) += snd-soc-bcm2708-i2s.o + + # BCM2708 Machine Support + snd-soc-hifiberry-dac-objs := hifiberry_dac.o ++snd-soc-rpi-dac-objs := rpi-dac.o + + obj-$(CONFIG_SND_BCM2708_SOC_HIFIBERRY_DAC) += snd-soc-hifiberry-dac.o ++obj-$(CONFIG_SND_BCM2708_SOC_RPI_DAC) += snd-soc-rpi-dac.o +diff --git a/sound/soc/bcm/rpi-dac.c b/sound/soc/bcm/rpi-dac.c +new file mode 100644 +index 0000000..ef3cd93 +--- /dev/null ++++ b/sound/soc/bcm/rpi-dac.c +@@ -0,0 +1,97 @@ ++/* ++ * ASoC Driver for RPi-DAC. ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++static int snd_rpi_rpi_dac_init(struct snd_soc_pcm_runtime *rtd) ++{ ++ return 0; ++} ++ ++static int snd_rpi_rpi_dac_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ struct snd_soc_pcm_runtime *rtd = substream->private_data; ++ struct snd_soc_dai *cpu_dai = rtd->cpu_dai; ++ ++ return snd_soc_dai_set_bclk_ratio(cpu_dai, 32*2); ++} ++ ++/* machine stream operations */ ++static struct snd_soc_ops snd_rpi_rpi_dac_ops = { ++ .hw_params = snd_rpi_rpi_dac_hw_params, ++}; ++ ++static struct snd_soc_dai_link snd_rpi_rpi_dac_dai[] = { ++{ ++ .name = "HifiBerry Mini", ++ .stream_name = "HifiBerry Mini HiFi", ++ .cpu_dai_name = "bcm2708-i2s.0", ++ .codec_dai_name = "pcm1794a-hifi", ++ .platform_name = "bcm2708-i2s.0", ++ .codec_name = "pcm1794a-codec", ++ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | ++ SND_SOC_DAIFMT_CBS_CFS, ++ .ops = &snd_rpi_rpi_dac_ops, ++ .init = snd_rpi_rpi_dac_init, ++}, ++}; ++ ++/* audio machine driver */ ++static struct snd_soc_card snd_rpi_rpi_dac = { ++ .name = "snd_rpi_rpi_dac", ++ .dai_link = snd_rpi_rpi_dac_dai, ++ .num_links = ARRAY_SIZE(snd_rpi_rpi_dac_dai), ++}; ++ ++static int snd_rpi_rpi_dac_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ ++ snd_rpi_rpi_dac.dev = &pdev->dev; ++ ret = snd_soc_register_card(&snd_rpi_rpi_dac); ++ if (ret) ++ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", ret); ++ ++ return ret; ++} ++ ++static int snd_rpi_rpi_dac_remove(struct platform_device *pdev) ++{ ++ return snd_soc_unregister_card(&snd_rpi_rpi_dac); ++} ++ ++static struct platform_driver snd_rpi_rpi_dac_driver = { ++ .driver = { ++ .name = "snd-rpi-dac", ++ .owner = THIS_MODULE, ++ }, ++ .probe = snd_rpi_rpi_dac_probe, ++ .remove = snd_rpi_rpi_dac_remove, ++}; ++ ++module_platform_driver(snd_rpi_rpi_dac_driver); ++ ++MODULE_AUTHOR("Florian Meier "); ++MODULE_DESCRIPTION("ASoC Driver for RPi-DAC"); ++MODULE_LICENSE("GPL v2"); +diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig +index 34d4d55..4a1dd7e 100644 +--- a/sound/soc/codecs/Kconfig ++++ b/sound/soc/codecs/Kconfig +@@ -59,6 +59,7 @@ config SND_SOC_ALL_CODECS + select SND_SOC_PCM1681 if I2C + select SND_SOC_PCM1792A if SPI_MASTER + select SND_SOC_PCM3008 ++ select SND_SOC_PCM1794A + select SND_SOC_PCM5102A + select SND_SOC_RT5631 if I2C + select SND_SOC_RT5640 if I2C +@@ -312,6 +313,9 @@ config SND_SOC_PCM1792A + config SND_SOC_PCM3008 + tristate + ++config SND_SOC_PCM1794A ++ tristate ++ + config SND_SOC_PCM5102A + tristate + +diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile +index 612f414..9b806a2 100644 +--- a/sound/soc/codecs/Makefile ++++ b/sound/soc/codecs/Makefile +@@ -46,6 +46,7 @@ snd-soc-hdmi-codec-objs := hdmi.o + snd-soc-pcm1681-objs := pcm1681.o + snd-soc-pcm1792a-codec-objs := pcm1792a.o + snd-soc-pcm3008-objs := pcm3008.o ++snd-soc-pcm1794a-objs := pcm1794a.o + snd-soc-pcm5102a-objs := pcm5102a.o + snd-soc-rt5631-objs := rt5631.o + snd-soc-rt5640-objs := rt5640.o +@@ -180,6 +181,7 @@ obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o + obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o + obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o + obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o ++obj-$(CONFIG_SND_SOC_PCM1794A) += snd-soc-pcm1794a.o + obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o + obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o + obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o +diff --git a/sound/soc/codecs/pcm1794a.c b/sound/soc/codecs/pcm1794a.c +new file mode 100644 +index 0000000..b4eaa44 +--- /dev/null ++++ b/sound/soc/codecs/pcm1794a.c +@@ -0,0 +1,62 @@ ++/* ++ * Driver for the PCM1794A codec ++ * ++ * Author: Florian Meier ++ * Copyright 2013 ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * version 2 as published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ */ ++ ++ ++#include ++#include ++#include ++ ++#include ++ ++static struct snd_soc_dai_driver pcm1794a_dai = { ++ .name = "pcm1794a-hifi", ++ .playback = { ++ .channels_min = 2, ++ .channels_max = 2, ++ .rates = SNDRV_PCM_RATE_8000_192000, ++ .formats = SNDRV_PCM_FMTBIT_S16_LE | ++ SNDRV_PCM_FMTBIT_S24_LE ++ }, ++}; ++ ++static struct snd_soc_codec_driver soc_codec_dev_pcm1794a; ++ ++static int pcm1794a_probe(struct platform_device *pdev) ++{ ++ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_pcm1794a, ++ &pcm1794a_dai, 1); ++} ++ ++static int pcm1794a_remove(struct platform_device *pdev) ++{ ++ snd_soc_unregister_codec(&pdev->dev); ++ return 0; ++} ++ ++static struct platform_driver pcm1794a_codec_driver = { ++ .probe = pcm1794a_probe, ++ .remove = pcm1794a_remove, ++ .driver = { ++ .name = "pcm1794a-codec", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++module_platform_driver(pcm1794a_codec_driver); ++ ++MODULE_DESCRIPTION("ASoC PCM1794A codec driver"); ++MODULE_AUTHOR("Florian Meier "); ++MODULE_LICENSE("GPL v2"); +-- +1.8.5.1