diff options
286 files changed, 8872 insertions, 2371 deletions
diff --git a/Documentation/devicetree/bindings/sound/ak4642.txt b/Documentation/devicetree/bindings/sound/ak4642.txt new file mode 100644 index 000000000000..623d4e70ae11 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/ak4642.txt @@ -0,0 +1,17 @@ +AK4642 I2C transmitter + +This device supports I2C mode only. + +Required properties: + + - compatible : "asahi-kasei,ak4642" or "asahi-kasei,ak4643" or "asahi-kasei,ak4648" + - reg : The chip select number on the I2C bus + +Example: + +&i2c { + ak4648: ak4648@0x12 { + compatible = "asahi-kasei,ak4642"; + reg = <0x12>; + }; +}; diff --git a/Documentation/devicetree/bindings/sound/cs4271.txt b/Documentation/devicetree/bindings/sound/cs4271.txt index a850fb9c88ea..e2cd1d7539e5 100644 --- a/Documentation/devicetree/bindings/sound/cs4271.txt +++ b/Documentation/devicetree/bindings/sound/cs4271.txt @@ -20,6 +20,18 @@ Optional properties: !RESET pin - cirrus,amuteb-eq-bmutec: When given, the Codec's AMUTEB=BMUTEC flag is enabled. + - cirrus,enable-soft-reset: + The CS4271 requires its LRCLK and MCLK to be stable before its RESET + line is de-asserted. That also means that clocks cannot be changed + without putting the chip back into hardware reset, which also requires + a complete re-initialization of all registers. + + One (undocumented) workaround is to assert and de-assert the PDN bit + in the MODE2 register. This workaround can be enabled with this DT + property. + + Note that this is not needed in case the clocks are stable + throughout the entire runtime of the codec. Examples: diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt new file mode 100644 index 000000000000..be35d34e8b26 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm9712.txt @@ -0,0 +1,51 @@ +NVIDIA Tegra audio complex + +Required properties: +- compatible : "nvidia,tegra-audio-wm9712" +- nvidia,model : The user-visible name of this sound complex. +- nvidia,audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names for sources and + sinks are the WM9712's pins, and the jacks on the board: + + WM9712 pins: + + * MONOOUT + * HPOUTL + * HPOUTR + * LOUT2 + * ROUT2 + * OUT3 + * LINEINL + * LINEINR + * PHONE + * PCBEEP + * MIC1 + * MIC2 + * Mic Bias + + Board connectors: + + * Headphone + * LineIn + * Mic + +- nvidia,ac97-controller : The phandle of the Tegra AC97 controller + + +Example: + +sound { + compatible = "nvidia,tegra-audio-wm9712-colibri_t20", + "nvidia,tegra-audio-wm9712"; + nvidia,model = "Toradex Colibri T20"; + + nvidia,audio-routing = + "Headphone", "HPOUTL", + "Headphone", "HPOUTR", + "LineIn", "LINEINL", + "LineIn", "LINEINR", + "Mic", "MIC1"; + + nvidia,ac97-controller = <&ac97>; +}; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt new file mode 100644 index 000000000000..c1454979c1ef --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra20-ac97.txt @@ -0,0 +1,22 @@ +NVIDIA Tegra 20 AC97 controller + +Required properties: +- compatible : "nvidia,tegra20-ac97" +- reg : Should contain AC97 controller registers location and length +- interrupts : Should contain AC97 interrupt +- nvidia,dma-request-selector : The Tegra DMA controller's phandle and + request selector for the AC97 controller +- nvidia,codec-reset-gpio : The Tegra GPIO controller's phandle and the number + of the GPIO used to reset the external AC97 codec +- nvidia,codec-sync-gpio : The Tegra GPIO controller's phandle and the number + of the GPIO corresponding with the AC97 DAP _FS line +Example: + +ac97@70002000 { + compatible = "nvidia,tegra20-ac97"; + reg = <0x70002000 0x200>; + interrupts = <0 81 0x04>; + nvidia,dma-request-selector = <&apbdma 12>; + nvidia,codec-reset-gpio = <&gpio 170 0>; + nvidia,codec-sync-gpio = <&gpio 120 0>; +}; diff --git a/Documentation/devicetree/bindings/sound/renesas,fsi.txt b/Documentation/devicetree/bindings/sound/renesas,fsi.txt new file mode 100644 index 000000000000..c5be003f413e --- /dev/null +++ b/Documentation/devicetree/bindings/sound/renesas,fsi.txt @@ -0,0 +1,26 @@ +Renesas FSI + +Required properties: +- compatible : "renesas,sh_fsi2" or "renesas,sh_fsi" +- reg : Should contain the register physical address and length +- interrupts : Should contain FSI interrupt + +- fsia,spdif-connection : FSI is connected by S/PDFI +- fsia,stream-mode-support : FSI supports 16bit stream mode. +- fsia,use-internal-clock : FSI uses internal clock when master mode. + +- fsib,spdif-connection : same as fsia +- fsib,stream-mode-support : same as fsia +- fsib,use-internal-clock : same as fsia + +Example: + +sh_fsi2: sh_fsi2@0xec230000 { + compatible = "renesas,sh_fsi2"; + reg = <0xec230000 0x400>; + interrupts = <0 146 0x4>; + + fsia,spdif-connection; + fsia,stream-mode-support; + fsia,use-internal-clock; +}; diff --git a/Documentation/devicetree/bindings/sound/samsung,smdk-wm8994.txt b/Documentation/devicetree/bindings/sound/samsung,smdk-wm8994.txt new file mode 100644 index 000000000000..4686646fb122 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/samsung,smdk-wm8994.txt @@ -0,0 +1,14 @@ +Samsung SMDK audio complex + +Required properties: +- compatible : "samsung,smdk-wm8994" +- samsung,i2s-controller: The phandle of the Samsung I2S0 controller +- samsung,audio-codec: The phandle of the WM8994 audio codec +Example: + +sound { + compatible = "samsung,smdk-wm8994"; + + samsung,i2s-controller = <&i2s0>; + samsung,audio-codec = <&wm8994>; +}; diff --git a/Documentation/devicetree/bindings/sound/samsung-i2s.txt b/Documentation/devicetree/bindings/sound/samsung-i2s.txt new file mode 100644 index 000000000000..3070046da2e5 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/samsung-i2s.txt @@ -0,0 +1,63 @@ +* Samsung I2S controller + +Required SoC Specific Properties: + +- compatible : "samsung,i2s-v5" +- reg: physical base address of the controller and length of memory mapped + region. +- dmas: list of DMA controller phandle and DMA request line ordered pairs. +- dma-names: identifier string for each DMA request line in the dmas property. + These strings correspond 1:1 with the ordered pairs in dmas. + +Optional SoC Specific Properties: + +- samsung,supports-6ch: If the I2S Primary sound source has 5.1 Channel + support, this flag is enabled. +- samsung,supports-rstclr: This flag should be set if I2S software reset bit + control is required. When this flag is set I2S software reset bit will be + enabled or disabled based on need. +- samsung,supports-secdai:If I2S block has a secondary FIFO and internal DMA, + then this flag is enabled. +- samsung,idma-addr: Internal DMA register base address of the audio + sub system(used in secondary sound source). + +Required Board Specific Properties: + +- gpios: The gpio specifier for data out,data in, LRCLK, CDCLK and SCLK + interface lines. The format of the gpio specifier depends on the gpio + controller. + The syntax of samsung gpio specifier is + <[phandle of the gpio controller node] + [pin number within the gpio controller] + [mux function] + [flags and pull up/down] + [drive strength]> + +Example: + +- SoC Specific Portion: + +i2s@03830000 { + compatible = "samsung,i2s-v5"; + reg = <0x03830000 0x100>; + dmas = <&pdma0 10 + &pdma0 9 + &pdma0 8>; + dma-names = "tx", "rx", "tx-sec"; + samsung,supports-6ch; + samsung,supports-rstclr; + samsung,supports-secdai; + samsung,idma-addr = <0x03000000>; +}; + +- Board Specific Portion: + +i2s@03830000 { + gpios = <&gpz 0 2 0 0>, /* I2S_0_SCLK */ + <&gpz 1 2 0 0>, /* I2S_0_CDCLK */ + <&gpz 2 2 0 0>, /* I2S_0_LRCK */ + <&gpz 3 2 0 0>, /* I2S_0_SDI */ + <&gpz 4 2 0 0>, /* I2S_0_SDO[1] */ + <&gpz 5 2 0 0>, /* I2S_0_SDO[2] */ + <&gpz 6 2 0 0>; /* I2S_0_SDO[3] */ +}; diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 363e348bff9b..6c723811c0a0 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2438,7 +2438,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. real-time workloads. It can also improve energy efficiency for asymmetric multiprocessors. - rcu_nocbs_poll [KNL,BOOT] + rcu_nocb_poll [KNL,BOOT] Rather than requiring that offloaded CPUs (specified by rcu_nocbs= above) explicitly awaken the corresponding "rcuoN" kthreads, diff --git a/Documentation/x86/boot.txt b/Documentation/x86/boot.txt index 3edb4c2887a1..e540fd67f767 100644 --- a/Documentation/x86/boot.txt +++ b/Documentation/x86/boot.txt @@ -57,7 +57,7 @@ Protocol 2.10: (Kernel 2.6.31) Added a protocol for relaxed alignment Protocol 2.11: (Kernel 3.6) Added a field for offset of EFI handover protocol entry point. -Protocol 2.12: (Kernel 3.9) Added the xloadflags field and extension fields +Protocol 2.12: (Kernel 3.8) Added the xloadflags field and extension fields to struct boot_params for for loading bzImage and ramdisk above 4G in 64bit. diff --git a/MAINTAINERS b/MAINTAINERS index 212c255b9347..35a56bcd5e75 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1489,7 +1489,7 @@ AVR32 ARCHITECTURE M: Haavard Skinnemoen <[email protected]> M: Hans-Christian Egtvedt <[email protected]> W: http://www.atmel.com/products/AVR32/ -W: http://avr32linux.org/ +W: http://mirror.egtvedt.no/avr32linux.org/ W: http://avrfreaks.net/ S: Maintained F: arch/avr32/ @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 8 SUBLEVEL = 0 -EXTRAVERSION = -rc6 +EXTRAVERSION = -rc7 NAME = Unicycling Gorilla # *DOCUMENTATION* diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts index e05b18f3c33d..4db9db0a8443 100644 --- a/arch/arm/boot/dts/exynos5250-smdk5250.dts +++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts @@ -49,6 +49,11 @@ compatible = "samsung,s524ad0xd1"; reg = <0x51>; }; + + wm8994: wm8994@1a { + compatible = "wlf,wm8994"; + reg = <0x1a>; + }; }; i2c@121D0000 { @@ -204,4 +209,25 @@ samsung,mfc-r = <0x43000000 0x800000>; samsung,mfc-l = <0x51000000 0x800000>; }; + + i2s0: i2s@03830000 { + gpios = <&gpz 0 2 0 0>, <&gpz 1 2 0 0>, <&gpz 2 2 0 0>, + <&gpz 3 2 0 0>, <&gpz 4 2 0 0>, <&gpz 5 2 0 0>, + <&gpz 6 2 0 0>; + }; + + i2s1: i2s@12D60000 { + status = "disabled"; + }; + + i2s2: i2s@12D70000 { + status = "disabled"; + }; + + sound { + compatible = "samsung,smdk-wm8994"; + + samsung,i2s-controller = <&i2s0>; + samsung,audio-codec = <&wm8994>; + }; }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index 3acf594ea60b..f50b4e854355 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -211,8 +211,9 @@ compatible = "samsung,exynos4210-spi"; reg = <0x12d20000 0x100>; interrupts = <0 66 0>; - tx-dma-channel = <&pdma0 5>; /* preliminary */ - rx-dma-channel = <&pdma0 4>; /* preliminary */ + dmas = <&pdma0 5 + &pdma0 4>; + dma-names = "tx", "rx"; #address-cells = <1>; #size-cells = <0>; }; @@ -221,8 +222,9 @@ compatible = "samsung,exynos4210-spi"; reg = <0x12d30000 0x100>; interrupts = <0 67 0>; - tx-dma-channel = <&pdma1 5>; /* preliminary */ - rx-dma-channel = <&pdma1 4>; /* preliminary */ + dmas = <&pdma1 5 + &pdma1 4>; + dma-names = "tx", "rx"; #address-cells = <1>; #size-cells = <0>; }; @@ -231,8 +233,9 @@ compatible = "samsung,exynos4210-spi"; reg = <0x12d40000 0x100>; interrupts = <0 68 0>; - tx-dma-channel = <&pdma0 7>; /* preliminary */ - rx-dma-channel = <&pdma0 6>; /* preliminary */ + dmas = <&pdma0 7 + &pdma0 6>; + dma-names = "tx", "rx"; #address-cells = <1>; #size-cells = <0>; }; @@ -269,6 +272,35 @@ #size-cells = <0>; }; + i2s0: i2s@03830000 { + compatible = "samsung,i2s-v5"; + reg = <0x03830000 0x100>; + dmas = <&pdma0 10 + &pdma0 9 + &pdma0 8>; + dma-names = "tx", "rx", "tx-sec"; + samsung,supports-6ch; + samsung,supports-rstclr; + samsung,supports-secdai; + samsung,idma-addr = <0x03000000>; + }; + + i2s1: i2s@12D60000 { + compatible = "samsung,i2s-v5"; + reg = <0x12D60000 0x100>; + dmas = <&pdma1 12 + &pdma1 11>; + dma-names = "tx", "rx"; + }; + + i2s2: i2s@12D70000 { + compatible = "samsung,i2s-v5"; + reg = <0x12D70000 0x100>; + dmas = <&pdma0 12 + &pdma0 11>; + dma-names = "tx", "rx"; + }; + amba { #address-cells = <1>; #size-cells = <1>; diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 36ae03a3f5d1..87dfa9026c5b 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -351,6 +351,25 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq) irq_set_chained_handler(irq, gic_handle_cascade_irq); } +static u8 gic_get_cpumask(struct gic_chip_data *gic) +{ + void __iomem *base = gic_data_dist_base(gic); + u32 mask, i; + + for (i = mask = 0; i < 32; i += 4) { + mask = readl_relaxed(base + GIC_DIST_TARGET + i); + mask |= mask >> 16; + mask |= mask >> 8; + if (mask) + break; + } + + if (!mask) + pr_crit("GIC CPU mask not found - kernel will fail to boot.\n"); + + return mask; +} + static void __init gic_dist_init(struct gic_chip_data *gic) { unsigned int i; @@ -369,7 +388,9 @@ static void __init gic_dist_init(struct gic_chip_data *gic) /* * Set all global interrupts to this CPU only. */ - cpumask = readl_relaxed(base + GIC_DIST_TARGET + 0); + cpumask = gic_get_cpumask(gic); + cpumask |= cpumask << 8; + cpumask |= cpumask << 16; for (i = 32; i < gic_irqs; i += 4) writel_relaxed(cpumask, base + GIC_DIST_TARGET + i * 4 / 4); @@ -400,7 +421,7 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) * Get what the GIC says our CPU mask is. */ BUG_ON(cpu >= NR_GIC_CPU_IF); - cpu_mask = readl_relaxed(dist_base + GIC_DIST_TARGET + 0); + cpu_mask = gic_get_cpumask(gic); gic_cpu_map[cpu] = cpu_mask; /* diff --git a/arch/arm/include/asm/memory.h b/arch/arm/include/asm/memory.h index 73cf03aa981e..1c4df27f9332 100644 --- a/arch/arm/include/asm/memory.h +++ b/arch/arm/include/asm/memory.h @@ -37,7 +37,7 @@ */ #define PAGE_OFFSET UL(CONFIG_PAGE_OFFSET) #define TASK_SIZE (UL(CONFIG_PAGE_OFFSET) - UL(0x01000000)) -#define TASK_UNMAPPED_BASE (UL(CONFIG_PAGE_OFFSET) / 3) +#define TASK_UNMAPPED_BASE ALIGN(TASK_SIZE / 3, SZ_16M) /* * The maximum size of a 26-bit user space task. diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index e103c290bc9e..85afb031b676 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -414,7 +414,7 @@ config MACH_EXYNOS4_DT select CPU_EXYNOS4210 select HAVE_SAMSUNG_KEYPAD if INPUT_KEYBOARD select PINCTRL - select PINCTRL_EXYNOS4 + select PINCTRL_EXYNOS select USE_OF help Machine support for Samsung Exynos4 machine with device tree enabled. diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index e99d3d8f2bcf..ea9e3020972d 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -104,6 +104,12 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = { OF_DEV_AUXDATA("samsung,mfc-v6", 0x11000000, "s5p-mfc-v6", NULL), OF_DEV_AUXDATA("samsung,exynos5250-tmu", 0x10060000, "exynos-tmu", NULL), + OF_DEV_AUXDATA("samsung,i2s-v5", 0x03830000, + "samsung-i2s.0", NULL), + OF_DEV_AUXDATA("samsung,i2s-v5", 0x12D60000, + "samsung-i2s.1", NULL), + OF_DEV_AUXDATA("samsung,i2s-v5", 0x12D70000, + "samsung-i2s.2", NULL), {}, }; diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c index 616cb87b6179..69985b06c0da 100644 --- a/arch/arm/mach-pxa/pxa27x.c +++ b/arch/arm/mach-pxa/pxa27x.c @@ -53,17 +53,25 @@ static unsigned long ac97_reset_config[] = { GPIO95_AC97_nRESET, }; -void pxa27x_assert_ac97reset(int reset_gpio, int on) +void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio) { + /* + * This helper function is used to work around a bug in the pxa27x's + * ac97 controller during a warm reset. The configuration of the + * reset_gpio is changed as follows: + * to_gpio == true: configured to generic output gpio and driven high + * to_gpio == false: configured to ac97 controller alt fn AC97_nRESET + */ + if (reset_gpio == 113) - pxa2xx_mfp_config(on ? &ac97_reset_config[0] : - &ac97_reset_config[1], 1); + pxa2xx_mfp_config(to_gpio ? &ac97_reset_config[0] : + &ac97_reset_config[1], 1); if (reset_gpio == 95) - pxa2xx_mfp_config(on ? &ac97_reset_config[2] : - &ac97_reset_config[3], 1); + pxa2xx_mfp_config(to_gpio ? &ac97_reset_config[2] : + &ac97_reset_config[3], 1); } -EXPORT_SYMBOL_GPL(pxa27x_assert_ac97reset); +EXPORT_SYMBOL_GPL(pxa27x_configure_ac97reset); /* Crystal clock: 13MHz */ #define BASE_CLK 13000000 diff --git a/arch/arm/mach-realview/include/mach/irqs-eb.h b/arch/arm/mach-realview/include/mach/irqs-eb.h index d6b5073692d2..44754230fdcc 100644 --- a/arch/arm/mach-realview/include/mach/irqs-eb.h +++ b/arch/arm/mach-realview/include/mach/irqs-eb.h @@ -115,7 +115,7 @@ /* * Only define NR_IRQS if less than NR_IRQS_EB */ -#define NR_IRQS_EB (IRQ_EB_GIC_START + 96) +#define NR_IRQS_EB (IRQ_EB_GIC_START + 128) #if defined(CONFIG_MACH_REALVIEW_EB) \ && (!defined(NR_IRQS) || (NR_IRQS < NR_IRQS_EB)) diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c index 99ef190d0909..08294fa9e0d4 100644 --- a/arch/arm/mach-shmobile/board-ap4evb.c +++ b/arch/arm/mach-shmobile/board-ap4evb.c @@ -657,14 +657,8 @@ static struct platform_device lcdc_device = { /* FSI */ #define IRQ_FSI evt2irq(0x1840) static struct sh_fsi_platform_info fsi_info = { - .port_a = { - .flags = SH_FSI_BRS_INV, - }, .port_b = { - .flags = SH_FSI_BRS_INV | - SH_FSI_BRM_INV | - SH_FSI_LRS_INV | - SH_FSI_CLK_CPG | + .flags = SH_FSI_CLK_CPG | SH_FSI_FMT_SPDIF, }, }; @@ -692,21 +686,21 @@ static struct platform_device fsi_device = { }, }; -static struct asoc_simple_dai_init_info fsi2_ak4643_init_info = { - .fmt = SND_SOC_DAIFMT_LEFT_J, - .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, - .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, - .sysclk = 11289600, -}; - static struct asoc_simple_card_info fsi2_ak4643_info = { .name = "AK4643", .card = "FSI2A-AK4643", - .cpu_dai = "fsia-dai", .codec = "ak4642-codec.0-0013", .platform = "sh_fsi2", - .codec_dai = "ak4642-hifi", - .init = &fsi2_ak4643_init_info, + .daifmt = SND_SOC_DAIFMT_LEFT_J, + .cpu_dai = { + .name = "fsia-dai", + .fmt = SND_SOC_DAIFMT_CBS_CFS, + }, + .codec_dai = { + .name = "ak4642-hifi", + .fmt = SND_SOC_DAIFMT_CBM_CFM, + .sysclk = 11289600, + }, }; static struct platform_device fsi_ak4643_device = { @@ -815,18 +809,18 @@ static struct platform_device lcdc1_device = { }, }; -static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = { - .cpu_daifmt = SND_SOC_DAIFMT_CBM_CFM, -}; - static struct asoc_simple_card_info fsi2_hdmi_info = { .name = "HDMI", .card = "FSI2B-HDMI", - .cpu_dai = "fsib-dai", .codec = "sh-mobile-hdmi", .platform = "sh_fsi2", - .codec_dai = "sh_mobile_hdmi-hifi", - .init = &fsi2_hdmi_init_info, + .cpu_dai = { + .name = "fsib-dai", + .fmt = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF, + }, + .codec_dai = { + .name = "sh_mobile_hdmi-hifi", + }, }; static struct platform_device fsi_hdmi_device = { diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c index 5353adf6b828..0679ca6bf1f6 100644 --- a/arch/arm/mach-shmobile/board-armadillo800eva.c +++ b/arch/arm/mach-shmobile/board-armadillo800eva.c @@ -806,21 +806,21 @@ static struct platform_device fsi_device = { }; /* FSI-WM8978 */ -static struct asoc_simple_dai_init_info fsi_wm8978_init_info = { - .fmt = SND_SOC_DAIFMT_I2S, - .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_NB_NF, - .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, - .sysclk = 12288000, -}; - static struct asoc_simple_card_info fsi_wm8978_info = { .name = "wm8978", .card = "FSI2A-WM8978", - .cpu_dai = "fsia-dai", .codec = "wm8978.0-001a", .platform = "sh_fsi2", - .codec_dai = "wm8978-hifi", - .init = &fsi_wm8978_init_info, + .daifmt = SND_SOC_DAIFMT_I2S, + .cpu_dai = { + .name = "fsia-dai", + .fmt = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_IB_NF, + }, + .codec_dai = { + .name = "wm8978-hifi", + .fmt = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_NB_NF, + .sysclk = 12288000, + }, }; static struct platform_device fsi_wm8978_device = { @@ -832,18 +832,18 @@ static struct platform_device fsi_wm8978_device = { }; /* FSI-HDMI */ -static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = { - .cpu_daifmt = SND_SOC_DAIFMT_CBM_CFM, -}; - static struct asoc_simple_card_info fsi2_hdmi_info = { .name = "HDMI", .card = "FSI2B-HDMI", - .cpu_dai = "fsib-dai", .codec = "sh-mobile-hdmi", .platform = "sh_fsi2", - .codec_dai = "sh_mobile_hdmi-hifi", - .init = &fsi2_hdmi_init_info, + .cpu_dai = { + .name = "fsib-dai", + .fmt = SND_SOC_DAIFMT_CBM_CFM, + }, + .codec_dai = { + .name = "sh_mobile_hdmi-hifi", + }, }; static struct platform_device fsi_hdmi_device = { diff --git a/arch/arm/mach-shmobile/board-kzm9g.c b/arch/arm/mach-shmobile/board-kzm9g.c index c02448d6847f..f41b71e8df3e 100644 --- a/arch/arm/mach-shmobile/board-kzm9g.c +++ b/arch/arm/mach-shmobile/board-kzm9g.c @@ -525,21 +525,21 @@ static struct platform_device fsi_device = { }, }; -static struct asoc_simple_dai_init_info fsi2_ak4648_init_info = { - .fmt = SND_SOC_DAIFMT_LEFT_J, - .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, - .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, - .sysclk = 11289600, -}; - static struct asoc_simple_card_info fsi2_ak4648_info = { .name = "AK4648", .card = "FSI2A-AK4648", - .cpu_dai = "fsia-dai", .codec = "ak4642-codec.0-0012", .platform = "sh_fsi2", - .codec_dai = "ak4642-hifi", - .init = &fsi2_ak4648_init_info, + .daifmt = SND_SOC_DAIFMT_LEFT_J, + .cpu_dai = { + .name = "fsia-dai", + .fmt = SND_SOC_DAIFMT_CBS_CFS, + }, + .codec_dai = { + .name = "ak4642-hifi", + .fmt = SND_SOC_DAIFMT_CBM_CFM, + .sysclk = 11289600, + }, }; static struct platform_device fsi_ak4648_device = { diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c index 2fed62f66045..3fd716dae405 100644 --- a/arch/arm/mach-shmobile/board-mackerel.c +++ b/arch/arm/mach-shmobile/board-mackerel.c @@ -502,18 +502,18 @@ static struct platform_device hdmi_lcdc_device = { }, }; -static struct asoc_simple_dai_init_info fsi2_hdmi_init_info = { - .cpu_daifmt = SND_SOC_DAIFMT_CBM_CFM, -}; - static struct asoc_simple_card_info fsi2_hdmi_info = { .name = "HDMI", .card = "FSI2B-HDMI", - .cpu_dai = "fsib-dai", .codec = "sh-mobile-hdmi", .platform = "sh_fsi2", - .codec_dai = "sh_mobile_hdmi-hifi", - .init = &fsi2_hdmi_init_info, + .cpu_dai = { + .name = "fsib-dai", + .fmt = SND_SOC_DAIFMT_CBM_CFM | SND_SOC_DAIFMT_IB_NF, + }, + .codec_dai = { + .name = "sh_mobile_hdmi-hifi", + }, }; static struct platform_device fsi_hdmi_device = { @@ -858,16 +858,12 @@ static struct platform_device leds_device = { #define IRQ_FSI evt2irq(0x1840) static struct sh_fsi_platform_info fsi_info = { .port_a = { - .flags = SH_FSI_BRS_INV, .tx_id = SHDMA_SLAVE_FSIA_TX, .rx_id = SHDMA_SLAVE_FSIA_RX, }, .port_b = { - .flags = SH_FSI_BRS_INV | - SH_FSI_BRM_INV | - SH_FSI_LRS_INV | - SH_FSI_CLK_CPG | - SH_FSI_FMT_SPDIF, + .flags = SH_FSI_CLK_CPG | + SH_FSI_FMT_SPDIF, } }; @@ -896,21 +892,21 @@ static struct platform_device fsi_device = { }, }; -static struct asoc_simple_dai_init_info fsi2_ak4643_init_info = { - .fmt = SND_SOC_DAIFMT_LEFT_J, - .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, - .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, - .sysclk = 11289600, -}; - static struct asoc_simple_card_info fsi2_ak4643_info = { .name = "AK4643", .card = "FSI2A-AK4643", - .cpu_dai = "fsia-dai", .codec = "ak4642-codec.0-0013", .platform = "sh_fsi2", - .codec_dai = "ak4642-hifi", - .init = &fsi2_ak4643_init_info, + .daifmt = SND_SOC_DAIFMT_LEFT_J, + .cpu_dai = { + .name = "fsia-dai", + .fmt = SND_SOC_DAIFMT_CBS_CFS, + }, + .codec_dai = { + .name = "ak4642-hifi", + .fmt = SND_SOC_DAIFMT_CBM_CFM, + .sysclk = 11289600, + }, }; static struct platform_device fsi_ak4643_device = { diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 076c26d43864..dda3904dc64c 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -640,7 +640,7 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, if (is_coherent || nommu()) addr = __alloc_simple_buffer(dev, size, gfp, &page); - else if (gfp & GFP_ATOMIC) + else if (!(gfp & __GFP_WAIT)) addr = __alloc_from_pool(size, &page); else if (!IS_ENABLED(CONFIG_CMA)) addr = __alloc_remap_buffer(dev, size, gfp, prot, &page, caller); diff --git a/arch/arm/plat-samsung/dma-ops.c b/arch/arm/plat-samsung/dma-ops.c index d088afa034e8..71d58ddea9c1 100644 --- a/arch/arm/plat-samsung/dma-ops.c +++ b/arch/arm/plat-samsung/dma-ops.c @@ -19,7 +19,8 @@ #include <mach/dma.h> static unsigned samsung_dmadev_request(enum dma_ch dma_ch, - struct samsung_dma_req *param) + struct samsung_dma_req *param, + struct device *dev, char *ch_name) { dma_cap_mask_t mask; void *filter_param; @@ -33,7 +34,12 @@ static unsigned samsung_dmadev_request(enum dma_ch dma_ch, */ filter_param = (dma_ch == DMACH_DT_PROP) ? (void *)param->dt_dmach_prop : (void *)dma_ch; - return (unsigned)dma_request_channel(mask, pl330_filter, filter_param); + + if (dev->of_node) + return (unsigned)dma_request_slave_channel(dev, ch_name); + else + return (unsigned)dma_request_channel(mask, pl330_filter, + filter_param); } static int samsung_dmadev_release(unsigned ch, void *param) diff --git a/arch/arm/plat-samsung/include/plat/dma-ops.h b/arch/arm/plat-samsung/include/plat/dma-ops.h index f5144cdd3001..114178268b75 100644 --- a/arch/arm/plat-samsung/include/plat/dma-ops.h +++ b/arch/arm/plat-samsung/include/plat/dma-ops.h @@ -39,7 +39,8 @@ struct samsung_dma_config { }; struct samsung_dma_ops { - unsigned (*request)(enum dma_ch ch, struct samsung_dma_req *param); + unsigned (*request)(enum dma_ch ch, struct samsung_dma_req *param, + struct device *dev, char *ch_name); int (*release)(unsigned ch, void *param); int (*config)(unsigned ch, struct samsung_dma_config *param); int (*prepare)(unsigned ch, struct samsung_dma_prep *param); diff --git a/arch/arm/plat-samsung/s3c-dma-ops.c b/arch/arm/plat-samsung/s3c-dma-ops.c index f99448c48d30..0cc40aea3f5a 100644 --- a/arch/arm/plat-samsung/s3c-dma-ops.c +++ b/arch/arm/plat-samsung/s3c-dma-ops.c @@ -36,7 +36,8 @@ static void s3c_dma_cb(struct s3c2410_dma_chan *channel, void *param, } static unsigned s3c_dma_request(enum dma_ch dma_ch, - struct samsung_dma_req *param) + struct samsung_dma_req *param, + struct device *dev, char *ch_name) { struct cb_data *data; diff --git a/arch/avr32/include/asm/dma-mapping.h b/arch/avr32/include/asm/dma-mapping.h index aaf5199d8fcb..b3d18f9f3e8d 100644 --- a/arch/avr32/include/asm/dma-mapping.h +++ b/arch/avr32/include/asm/dma-mapping.h @@ -336,4 +336,14 @@ dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent(d, s, h, f) #define dma_free_noncoherent(d, s, v, h) dma_free_coherent(d, s, v, h) +/* drivers/base/dma-mapping.c */ +extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size); +extern int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t dma_addr, + size_t size); + +#define dma_mmap_coherent(d, v, c, h, s) dma_common_mmap(d, v, c, h, s) +#define dma_get_sgtable(d, t, v, h, s) dma_common_get_sgtable(d, t, v, h, s) + #endif /* __ASM_AVR32_DMA_MAPPING_H */ diff --git a/arch/blackfin/include/asm/dma-mapping.h b/arch/blackfin/include/asm/dma-mapping.h index bbf461076a0a..054d9ec57d9d 100644 --- a/arch/blackfin/include/asm/dma-mapping.h +++ b/arch/blackfin/include/asm/dma-mapping.h @@ -154,4 +154,14 @@ dma_cache_sync(struct device *dev, void *vaddr, size_t size, _dma_sync((dma_addr_t)vaddr, size, dir); } +/* drivers/base/dma-mapping.c */ +extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size); +extern int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t dma_addr, + size_t size); + +#define dma_mmap_coherent(d, v, c, h, s) dma_common_mmap(d, v, c, h, s) +#define dma_get_sgtable(d, t, v, h, s) dma_common_get_sgtable(d, t, v, h, s) + #endif /* _BLACKFIN_DMA_MAPPING_H */ diff --git a/arch/c6x/include/asm/dma-mapping.h b/arch/c6x/include/asm/dma-mapping.h index 3c694065030f..88bd0d899bdb 100644 --- a/arch/c6x/include/asm/dma-mapping.h +++ b/arch/c6x/include/asm/dma-mapping.h @@ -89,4 +89,19 @@ extern void dma_free_coherent(struct device *, size_t, void *, dma_addr_t); #define dma_alloc_noncoherent(d, s, h, f) dma_alloc_coherent((d), (s), (h), (f)) #define dma_free_noncoherent(d, s, v, h) dma_free_coherent((d), (s), (v), (h)) +/* Not supported for now */ +static inline int dma_mmap_coherent(struct device *dev, + struct vm_area_struct *vma, void *cpu_addr, + dma_addr_t dma_addr, size_t size) +{ + return -EINVAL; +} + +static inline int dma_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t dma_addr, + size_t size) +{ + return -EINVAL; +} + #endif /* _ASM_C6X_DMA_MAPPING_H */ diff --git a/arch/cris/include/asm/dma-mapping.h b/arch/cris/include/asm/dma-mapping.h index 8588b2ccf854..2f0f654f1b44 100644 --- a/arch/cris/include/asm/dma-mapping.h +++ b/arch/cris/include/asm/dma-mapping.h @@ -158,5 +158,15 @@ dma_cache_sync(struct device *dev, void *vaddr, size_t size, { } +/* drivers/base/dma-mapping.c */ +extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size); +extern int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t dma_addr, + size_t size); + +#define dma_mmap_coherent(d, v, c, h, s) dma_common_mmap(d, v, c, h, s) +#define dma_get_sgtable(d, t, v, h, s) dma_common_get_sgtable(d, t, v, h, s) + #endif diff --git a/arch/frv/include/asm/dma-mapping.h b/arch/frv/include/asm/dma-mapping.h index dfb811002c64..1746a2b8e6e7 100644 --- a/arch/frv/include/asm/dma-mapping.h +++ b/arch/frv/include/asm/dma-mapping.h @@ -132,4 +132,19 @@ void dma_cache_sync(struct device *dev, void *vaddr, size_t size, flush_write_buffers(); } +/* Not supported for now */ +static inline int dma_mmap_coherent(struct device *dev, + struct vm_area_struct *vma, void *cpu_addr, + dma_addr_t dma_addr, size_t size) +{ + return -EINVAL; +} + +static inline int dma_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t dma_addr, + size_t size) +{ + return -EINVAL; +} + #endif /* _ASM_DMA_MAPPING_H */ diff --git a/arch/m68k/include/asm/dma-mapping.h b/arch/m68k/include/asm/dma-mapping.h index 3e6b8445af6a..292805f0762e 100644 --- a/arch/m68k/include/asm/dma-mapping.h +++ b/arch/m68k/include/asm/dma-mapping.h @@ -115,4 +115,14 @@ static inline int dma_mapping_error(struct device *dev, dma_addr_t handle) #include <asm-generic/dma-mapping-broken.h> #endif +/* drivers/base/dma-mapping.c */ +extern int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, + void *cpu_addr, dma_addr_t dma_addr, size_t size); +extern int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t dma_addr, + size_t size); + +#define dma_mmap_coherent(d, v, c, h, s) dma_common_mmap(d, v, c, h, s) +#define dma_get_sgtable(d, t, v, h, s) dma_common_get_sgtable(d, t, v, h, s) + #endif /* _M68K_DMA_MAPPING_H */ diff --git a/arch/mn10300/include/asm/dma-mapping.h b/arch/mn10300/include/asm/dma-mapping.h index c1be4397b1ed..a18abfc558eb 100644 --- a/arch/mn10300/include/asm/dma-mapping.h +++ b/arch/mn10300/include/asm/dma-mapping.h @@ -168,4 +168,19 @@ void dma_cache_sync(void *vaddr, size_t size, mn10300_dcache_flush_inv(); } +/* Not supported for now */ +static inline int dma_mmap_coherent(struct device *dev, + struct vm_area_struct *vma, void *cpu_addr, + dma_addr_t dma_addr, size_t size) +{ + return -EINVAL; +} + +static inline int dma_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t dma_addr, + size_t size) +{ + return -EINVAL; +} + #endif diff --git a/arch/parisc/include/asm/dma-mapping.h b/arch/parisc/include/asm/dma-mapping.h index 467bbd510eac..106b395688e1 100644 --- a/arch/parisc/include/asm/dma-mapping.h +++ b/arch/parisc/include/asm/dma-mapping.h @@ -238,4 +238,19 @@ void * sba_get_iommu(struct parisc_device *dev); /* At the moment, we panic on error for IOMMU resource exaustion */ #define dma_mapping_error(dev, x) 0 +/* This API cannot be supported on PA-RISC */ +static inline int dma_mmap_coherent(struct device *dev, + struct vm_area_struct *vma, void *cpu_addr, + dma_addr_t dma_addr, size_t size) +{ + return -EINVAL; +} + +static inline int dma_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t dma_addr, + size_t size) +{ + return -EINVAL; +} + #endif diff --git a/arch/powerpc/mm/hash_low_64.S b/arch/powerpc/mm/hash_low_64.S index 56585086413a..7443481a315c 100644 --- a/arch/powerpc/mm/hash_low_64.S +++ b/arch/powerpc/mm/hash_low_64.S @@ -115,11 +115,13 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT) sldi r29,r5,SID_SHIFT - VPN_SHIFT rldicl r28,r3,64 - VPN_SHIFT,64 - (SID_SHIFT - VPN_SHIFT) or r29,r28,r29 - - /* Calculate hash value for primary slot and store it in r28 */ - rldicl r5,r5,0,25 /* vsid & 0x0000007fffffffff */ - rldicl r0,r3,64-12,48 /* (ea >> 12) & 0xffff */ - xor r28,r5,r0 + /* + * Calculate hash value for primary slot and store it in r28 + * r3 = va, r5 = vsid + * r0 = (va >> 12) & ((1ul << (28 - 12)) -1) + */ + rldicl r0,r3,64-12,48 + xor r28,r5,r0 /* hash */ b 4f 3: /* Calc vpn and put it in r29 */ @@ -130,11 +132,12 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT) /* * calculate hash value for primary slot and * store it in r28 for 1T segment + * r3 = va, r5 = vsid */ - rldic r28,r5,25,25 /* (vsid << 25) & 0x7fffffffff */ - clrldi r5,r5,40 /* vsid & 0xffffff */ - rldicl r0,r3,64-12,36 /* (ea >> 12) & 0xfffffff */ - xor r28,r28,r5 + sldi r28,r5,25 /* vsid << 25 */ + /* r0 = (va >> 12) & ((1ul << (40 - 12)) -1) */ + rldicl r0,r3,64-12,36 + xor r28,r28,r5 /* vsid ^ ( vsid << 25) */ xor r28,r28,r0 /* hash */ /* Convert linux PTE bits into HW equivalents */ @@ -407,11 +410,13 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT) */ rldicl r28,r3,64 - VPN_SHIFT,64 - (SID_SHIFT - VPN_SHIFT) or r29,r28,r29 - - /* Calculate hash value for primary slot and store it in r28 */ - rldicl r5,r5,0,25 /* vsid & 0x0000007fffffffff */ - rldicl r0,r3,64-12,48 /* (ea >> 12) & 0xffff */ - xor r28,r5,r0 + /* + * Calculate hash value for primary slot and store it in r28 + * r3 = va, r5 = vsid + * r0 = (va >> 12) & ((1ul << (28 - 12)) -1) + */ + rldicl r0,r3,64-12,48 + xor r28,r5,r0 /* hash */ b 4f 3: /* Calc vpn and put it in r29 */ @@ -426,11 +431,12 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT) /* * Calculate hash value for primary slot and * store it in r28 for 1T segment + * r3 = va, r5 = vsid */ - rldic r28,r5,25,25 /* (vsid << 25) & 0x7fffffffff */ - clrldi r5,r5,40 /* vsid & 0xffffff */ - rldicl r0,r3,64-12,36 /* (ea >> 12) & 0xfffffff */ - xor r28,r28,r5 + sldi r28,r5,25 /* vsid << 25 */ + /* r0 = (va >> 12) & ((1ul << (40 - 12)) -1) */ + rldicl r0,r3,64-12,36 + xor r28,r28,r5 /* vsid ^ ( vsid << 25) */ xor r28,r28,r0 /* hash */ /* Convert linux PTE bits into HW equivalents */ @@ -752,25 +758,27 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT) rldicl r28,r3,64 - VPN_SHIFT,64 - (SID_SHIFT - VPN_SHIFT) or r29,r28,r29 - /* Calculate hash value for primary slot and store it in r28 */ - rldicl r5,r5,0,25 /* vsid & 0x0000007fffffffff */ - rldicl r0,r3,64-16,52 /* (ea >> 16) & 0xfff */ - xor r28,r5,r0 + /* Calculate hash value for primary slot and store it in r28 + * r3 = va, r5 = vsid + * r0 = (va >> 16) & ((1ul << (28 - 16)) -1) + */ + rldicl r0,r3,64-16,52 + xor r28,r5,r0 /* hash */ b 4f 3: /* Calc vpn and put it in r29 */ sldi r29,r5,SID_SHIFT_1T - VPN_SHIFT rldicl r28,r3,64 - VPN_SHIFT,64 - (SID_SHIFT_1T - VPN_SHIFT) or r29,r28,r29 - /* * calculate hash value for primary slot and * store it in r28 for 1T segment + * r3 = va, r5 = vsid */ - rldic r28,r5,25,25 /* (vsid << 25) & 0x7fffffffff */ - clrldi r5,r5,40 /* vsid & 0xffffff */ - rldicl r0,r3,64-16,40 /* (ea >> 16) & 0xffffff */ - xor r28,r28,r5 + sldi r28,r5,25 /* vsid << 25 */ + /* r0 = (va >> 16) & ((1ul << (40 - 16)) -1) */ + rldicl r0,r3,64-16,40 + xor r28,r28,r5 /* vsid ^ ( vsid << 25) */ xor r28,r28,r0 /* hash */ /* Convert linux PTE bits into HW equivalents */ diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c index a0fa5791cd44..aaff7671101b 100644 --- a/arch/sh/boards/mach-ecovec24/setup.c +++ b/arch/sh/boards/mach-ecovec24/setup.c @@ -887,12 +887,6 @@ static struct platform_device camera_devices[] = { }; /* FSI */ -static struct sh_fsi_platform_info fsi_info = { - .port_b = { - .flags = SH_FSI_BRS_INV, - }, -}; - static struct resource fsi_resources[] = { [0] = { .name = "FSI", @@ -911,25 +905,22 @@ static struct platform_device fsi_device = { .id = 0, .num_resources = ARRAY_SIZE(fsi_resources), .resource = fsi_resources, - .dev = { - .platform_data = &fsi_info, - }, -}; - -static struct asoc_simple_dai_init_info fsi_da7210_init_info = { - .fmt = SND_SOC_DAIFMT_I2S, - .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, - .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, }; static struct asoc_simple_card_info fsi_da7210_info = { .name = "DA7210", .card = "FSIB-DA7210", - .cpu_dai = "fsib-dai", .codec = "da7210.0-001a", .platform = "sh_fsi.0", - .codec_dai = "da7210-hifi", - .init = &fsi_da7210_init_info, + .daifmt = SND_SOC_DAIFMT_I2S, + .cpu_dai = { + .name = "fsib-dai", + .fmt = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_IB_NF, + }, + .codec_dai = { + .name = "da7210-hifi", + .fmt = SND_SOC_DAIFMT_CBM_CFM, + }, }; static struct platform_device fsi_da7210_device = { diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c index 35f6efa3ac0e..4010e63e82d8 100644 --- a/arch/sh/boards/mach-se/7724/setup.c +++ b/arch/sh/boards/mach-se/7724/setup.c @@ -279,12 +279,6 @@ static struct platform_device ceu1_device = { /* FSI */ /* change J20, J21, J22 pin to 1-2 connection to use slave mode */ -static struct sh_fsi_platform_info fsi_info = { - .port_a = { - .flags = SH_FSI_BRS_INV, - }, -}; - static struct resource fsi_resources[] = { [0] = { .name = "FSI", @@ -303,26 +297,23 @@ static struct platform_device fsi_device = { .id = 0, .num_resources = ARRAY_SIZE(fsi_resources), .resource = fsi_resources, - .dev = { - .platform_data = &fsi_info, - }, -}; - -static struct asoc_simple_dai_init_info fsi2_ak4642_init_info = { - .fmt = SND_SOC_DAIFMT_LEFT_J, - .codec_daifmt = SND_SOC_DAIFMT_CBM_CFM, - .cpu_daifmt = SND_SOC_DAIFMT_CBS_CFS, - .sysclk = 11289600, }; static struct asoc_simple_card_info fsi_ak4642_info = { .name = "AK4642", .card = "FSIA-AK4642", - .cpu_dai = "fsia-dai", .codec = "ak4642-codec.0-0012", .platform = "sh_fsi.0", - .codec_dai = "ak4642-hifi", - .init = &fsi2_ak4642_init_info, + .daifmt = SND_SOC_DAIFMT_LEFT_J, + .cpu_dai = { + .name = "fsia-dai", + .fmt = SND_SOC_DAIFMT_CBS_CFS | SND_SOC_DAIFMT_IB_NF, + }, + .codec_dai = { + .name = "ak4642-hifi", + .fmt = SND_SOC_DAIFMT_CBM_CFM, + .sysclk = 11289600, + }, }; static struct platform_device fsi_ak4642_device = { diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S index 102ff7cb3e41..142c4ceff112 100644 --- a/arch/x86/ia32/ia32entry.S +++ b/arch/x86/ia32/ia32entry.S @@ -207,7 +207,7 @@ sysexit_from_sys_call: testl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT),TI_flags+THREAD_INFO(%rsp,RIP-ARGOFFSET) jnz ia32_ret_from_sys_call TRACE_IRQS_ON - sti + ENABLE_INTERRUPTS(CLBR_NONE) movl %eax,%esi /* second arg, syscall return value */ cmpl $-MAX_ERRNO,%eax /* is it an error ? */ jbe 1f @@ -217,7 +217,7 @@ sysexit_from_sys_call: call __audit_syscall_exit movq RAX-ARGOFFSET(%rsp),%rax /* reload syscall return value */ movl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT),%edi - cli + DISABLE_INTERRUPTS(CLBR_NONE) TRACE_IRQS_OFF testl %edi,TI_flags+THREAD_INFO(%rsp,RIP-ARGOFFSET) jz \exit diff --git a/arch/x86/kernel/cpu/intel_cacheinfo.c b/arch/x86/kernel/cpu/intel_cacheinfo.c index fe9edec6698a..84c1309c4c0c 100644 --- a/arch/x86/kernel/cpu/intel_cacheinfo.c +++ b/arch/x86/kernel/cpu/intel_cacheinfo.c @@ -298,8 +298,7 @@ struct _cache_attr { unsigned int); }; -#ifdef CONFIG_AMD_NB - +#if defined(CONFIG_AMD_NB) && defined(CONFIG_SYSFS) /* * L3 cache descriptors */ @@ -524,9 +523,9 @@ store_subcaches(struct _cpuid4_info *this_leaf, const char *buf, size_t count, static struct _cache_attr subcaches = __ATTR(subcaches, 0644, show_subcaches, store_subcaches); -#else /* CONFIG_AMD_NB */ +#else #define amd_init_l3_cache(x, y) -#endif /* CONFIG_AMD_NB */ +#endif /* CONFIG_AMD_NB && CONFIG_SYSFS */ static int __cpuinit cpuid4_cache_lookup_regs(int index, diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index 93b9e1181f83..4914e94ad6e8 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -2019,7 +2019,10 @@ __init int intel_pmu_init(void) break; case 28: /* Atom */ - case 54: /* Cedariew */ + case 38: /* Lincroft */ + case 39: /* Penwell */ + case 53: /* Cloverview */ + case 54: /* Cedarview */ memcpy(hw_cache_event_ids, atom_hw_cache_event_ids, sizeof(hw_cache_event_ids)); @@ -2084,6 +2087,7 @@ __init int intel_pmu_init(void) pr_cont("SandyBridge events, "); break; case 58: /* IvyBridge */ + case 62: /* IvyBridge EP */ memcpy(hw_cache_event_ids, snb_hw_cache_event_ids, sizeof(hw_cache_event_ids)); memcpy(hw_cache_extra_regs, snb_hw_cache_extra_regs, diff --git a/arch/x86/kernel/cpu/perf_event_p6.c b/arch/x86/kernel/cpu/perf_event_p6.c index f2af39f5dc3d..4820c232a0b9 100644 --- a/arch/x86/kernel/cpu/perf_event_p6.c +++ b/arch/x86/kernel/cpu/perf_event_p6.c @@ -19,7 +19,7 @@ static const u64 p6_perfmon_event_map[] = }; -static __initconst u64 p6_hw_cache_event_ids +static u64 p6_hw_cache_event_ids [PERF_COUNT_HW_CACHE_MAX] [PERF_COUNT_HW_CACHE_OP_MAX] [PERF_COUNT_HW_CACHE_RESULT_MAX] = diff --git a/arch/x86/tools/insn_sanity.c b/arch/x86/tools/insn_sanity.c index cc2f8c131286..872eb60e7806 100644 --- a/arch/x86/tools/insn_sanity.c +++ b/arch/x86/tools/insn_sanity.c @@ -55,7 +55,7 @@ static FILE *input_file; /* Input file name */ static void usage(const char *err) { if (err) - fprintf(stderr, "Error: %s\n\n", err); + fprintf(stderr, "%s: Error: %s\n\n", prog, err); fprintf(stderr, "Usage: %s [-y|-n|-v] [-s seed[,no]] [-m max] [-i input]\n", prog); fprintf(stderr, "\t-y 64bit mode\n"); fprintf(stderr, "\t-n 32bit mode\n"); @@ -269,7 +269,13 @@ int main(int argc, char **argv) insns++; } - fprintf(stdout, "%s: decoded and checked %d %s instructions with %d errors (seed:0x%x)\n", (errors) ? "Failure" : "Success", insns, (input_file) ? "given" : "random", errors, seed); + fprintf(stdout, "%s: %s: decoded and checked %d %s instructions with %d errors (seed:0x%x)\n", + prog, + (errors) ? "Failure" : "Success", + insns, + (input_file) ? "given" : "random", + errors, + seed); return errors ? 1 : 0; } diff --git a/arch/xtensa/include/asm/dma-mapping.h b/arch/xtensa/include/asm/dma-mapping.h index 4acb5feba1fb..172a02a6ad14 100644 --- a/arch/xtensa/include/asm/dma-mapping.h +++ b/arch/xtensa/include/asm/dma-mapping.h @@ -170,4 +170,19 @@ dma_cache_sync(struct device *dev, void *vaddr, size_t size, consistent_sync(vaddr, size, direction); } +/* Not supported for now */ +static inline int dma_mmap_coherent(struct device *dev, + struct vm_area_struct *vma, void *cpu_addr, + dma_addr_t dma_addr, size_t size) +{ + return -EINVAL; +} + +static inline int dma_get_sgtable(struct device *dev, struct sg_table *sgt, + void *cpu_addr, dma_addr_t dma_addr, + size_t size) +{ + return -EINVAL; +} + #endif /* _XTENSA_DMA_MAPPING_H */ diff --git a/block/genhd.c b/block/genhd.c index 9a289d7c84bb..3993ebf4135f 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -35,6 +35,8 @@ static DEFINE_IDR(ext_devt_idr); static struct device_type disk_type; +static void disk_check_events(struct disk_events *ev, + unsigned int *clearing_ptr); static void disk_alloc_events(struct gendisk *disk); static void disk_add_events(struct gendisk *disk); static void disk_del_events(struct gendisk *disk); @@ -1549,6 +1551,7 @@ unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask) const struct block_device_operations *bdops = disk->fops; struct disk_events *ev = disk->ev; unsigned int pending; + unsigned int clearing = mask; if (!ev) { /* for drivers still using the old ->media_changed method */ @@ -1558,34 +1561,53 @@ unsigned int disk_clear_events(struct gendisk *disk, unsigned int mask) return 0; } - /* tell the workfn about the events being cleared */ + disk_block_events(disk); + + /* + * store the union of mask and ev->clearing on the stack so that the + * race with disk_flush_events does not cause ambiguity (ev->clearing + * can still be modified even if events are blocked). + */ spin_lock_irq(&ev->lock); - ev->clearing |= mask; + clearing |= ev->clearing; + ev->clearing = 0; spin_unlock_irq(&ev->lock); - /* uncondtionally schedule event check and wait for it to finish */ - disk_block_events(disk); - queue_delayed_work(system_freezable_wq, &ev->dwork, 0); - flush_delayed_work(&ev->dwork); - __disk_unblock_events(disk, false); + disk_check_events(ev, &clearing); + /* + * if ev->clearing is not 0, the disk_flush_events got called in the + * middle of this function, so we want to run the workfn without delay. + */ + __disk_unblock_events(disk, ev->clearing ? true : false); /* then, fetch and clear pending events */ spin_lock_irq(&ev->lock); - WARN_ON_ONCE(ev->clearing & mask); /* cleared by workfn */ pending = ev->pending & mask; ev->pending &= ~mask; spin_unlock_irq(&ev->lock); + WARN_ON_ONCE(clearing & mask); return pending; } +/* + * Separate this part out so that a different pointer for clearing_ptr can be + * passed in for disk_clear_events. + */ static void disk_events_workfn(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); struct disk_events *ev = container_of(dwork, struct disk_events, dwork); + + disk_check_events(ev, &ev->clearing); +} + +static void disk_check_events(struct disk_events *ev, + unsigned int *clearing_ptr) +{ struct gendisk *disk = ev->disk; char *envp[ARRAY_SIZE(disk_uevents) + 1] = { }; - unsigned int clearing = ev->clearing; + unsigned int clearing = *clearing_ptr; unsigned int events; unsigned long intv; int nr_events = 0, i; @@ -1598,7 +1620,7 @@ static void disk_events_workfn(struct work_struct *work) events &= ~ev->pending; ev->pending |= events; - ev->clearing &= ~clearing; + *clearing_ptr &= ~clearing; intv = disk_events_poll_jiffies(disk); if (!ev->block && intv) diff --git a/drivers/atm/iphase.h b/drivers/atm/iphase.h index 6a0955e6d4fc..53ecac5a2161 100644 --- a/drivers/atm/iphase.h +++ b/drivers/atm/iphase.h @@ -636,82 +636,82 @@ struct rx_buf_desc { #define SEG_BASE IPHASE5575_FRAG_CONTROL_REG_BASE #define REASS_BASE IPHASE5575_REASS_CONTROL_REG_BASE -typedef volatile u_int freg_t; +typedef volatile u_int ffreg_t; typedef u_int rreg_t; typedef struct _ffredn_t { - freg_t idlehead_high; /* Idle cell header (high) */ - freg_t idlehead_low; /* Idle cell header (low) */ - freg_t maxrate; /* Maximum rate */ - freg_t stparms; /* Traffic Management Parameters */ - freg_t abrubr_abr; /* ABRUBR Priority Byte 1, TCR Byte 0 */ - freg_t rm_type; /* */ - u_int filler5[0x17 - 0x06]; - freg_t cmd_reg; /* Command register */ - u_int filler18[0x20 - 0x18]; - freg_t cbr_base; /* CBR Pointer Base */ - freg_t vbr_base; /* VBR Pointer Base */ - freg_t abr_base; /* ABR Pointer Base */ - freg_t ubr_base; /* UBR Pointer Base */ - u_int filler24; - freg_t vbrwq_base; /* VBR Wait Queue Base */ - freg_t abrwq_base; /* ABR Wait Queue Base */ - freg_t ubrwq_base; /* UBR Wait Queue Base */ - freg_t vct_base; /* Main VC Table Base */ - freg_t vcte_base; /* Extended Main VC Table Base */ - u_int filler2a[0x2C - 0x2A]; - freg_t cbr_tab_beg; /* CBR Table Begin */ - freg_t cbr_tab_end; /* CBR Table End */ - freg_t cbr_pointer; /* CBR Pointer */ - u_int filler2f[0x30 - 0x2F]; - freg_t prq_st_adr; /* Packet Ready Queue Start Address */ - freg_t prq_ed_adr; /* Packet Ready Queue End Address */ - freg_t prq_rd_ptr; /* Packet Ready Queue read pointer */ - freg_t prq_wr_ptr; /* Packet Ready Queue write pointer */ - freg_t tcq_st_adr; /* Transmit Complete Queue Start Address*/ - freg_t tcq_ed_adr; /* Transmit Complete Queue End Address */ - freg_t tcq_rd_ptr; /* Transmit Complete Queue read pointer */ - freg_t tcq_wr_ptr; /* Transmit Complete Queue write pointer*/ - u_int filler38[0x40 - 0x38]; - freg_t queue_base; /* Base address for PRQ and TCQ */ - freg_t desc_base; /* Base address of descriptor table */ - u_int filler42[0x45 - 0x42]; - freg_t mode_reg_0; /* Mode register 0 */ - freg_t mode_reg_1; /* Mode register 1 */ - freg_t intr_status_reg;/* Interrupt Status register */ - freg_t mask_reg; /* Mask Register */ - freg_t cell_ctr_high1; /* Total cell transfer count (high) */ - freg_t cell_ctr_lo1; /* Total cell transfer count (low) */ - freg_t state_reg; /* Status register */ - u_int filler4c[0x58 - 0x4c]; - freg_t curr_desc_num; /* Contains the current descriptor num */ - freg_t next_desc; /* Next descriptor */ - freg_t next_vc; /* Next VC */ - u_int filler5b[0x5d - 0x5b]; - freg_t present_slot_cnt;/* Present slot count */ - u_int filler5e[0x6a - 0x5e]; - freg_t new_desc_num; /* New descriptor number */ - freg_t new_vc; /* New VC */ - freg_t sched_tbl_ptr; /* Schedule table pointer */ - freg_t vbrwq_wptr; /* VBR wait queue write pointer */ - freg_t vbrwq_rptr; /* VBR wait queue read pointer */ - freg_t abrwq_wptr; /* ABR wait queue write pointer */ - freg_t abrwq_rptr; /* ABR wait queue read pointer */ - freg_t ubrwq_wptr; /* UBR wait queue write pointer */ - freg_t ubrwq_rptr; /* UBR wait queue read pointer */ - freg_t cbr_vc; /* CBR VC */ - freg_t vbr_sb_vc; /* VBR SB VC */ - freg_t abr_sb_vc; /* ABR SB VC */ - freg_t ubr_sb_vc; /* UBR SB VC */ - freg_t vbr_next_link; /* VBR next link */ - freg_t abr_next_link; /* ABR next link */ - freg_t ubr_next_link; /* UBR next link */ - u_int filler7a[0x7c-0x7a]; - freg_t out_rate_head; /* Out of rate head */ - u_int filler7d[0xca-0x7d]; /* pad out to full address space */ - freg_t cell_ctr_high1_nc;/* Total cell transfer count (high) */ - freg_t cell_ctr_lo1_nc;/* Total cell transfer count (low) */ - u_int fillercc[0x100-0xcc]; /* pad out to full address space */ + ffreg_t idlehead_high; /* Idle cell header (high) */ + ffreg_t idlehead_low; /* Idle cell header (low) */ + ffreg_t maxrate; /* Maximum rate */ + ffreg_t stparms; /* Traffic Management Parameters */ + ffreg_t abrubr_abr; /* ABRUBR Priority Byte 1, TCR Byte 0 */ + ffreg_t rm_type; /* */ + u_int filler5[0x17 - 0x06]; + ffreg_t cmd_reg; /* Command register */ + u_int filler18[0x20 - 0x18]; + ffreg_t cbr_base; /* CBR Pointer Base */ + ffreg_t vbr_base; /* VBR Pointer Base */ + ffreg_t abr_base; /* ABR Pointer Base */ + ffreg_t ubr_base; /* UBR Pointer Base */ + u_int filler24; + ffreg_t vbrwq_base; /* VBR Wait Queue Base */ + ffreg_t abrwq_base; /* ABR Wait Queue Base */ + ffreg_t ubrwq_base; /* UBR Wait Queue Base */ + ffreg_t vct_base; /* Main VC Table Base */ + ffreg_t vcte_base; /* Extended Main VC Table Base */ + u_int filler2a[0x2C - 0x2A]; + ffreg_t cbr_tab_beg; /* CBR Table Begin */ + ffreg_t cbr_tab_end; /* CBR Table End */ + ffreg_t cbr_pointer; /* CBR Pointer */ + u_int filler2f[0x30 - 0x2F]; + ffreg_t prq_st_adr; /* Packet Ready Queue Start Address */ + ffreg_t prq_ed_adr; /* Packet Ready Queue End Address */ + ffreg_t prq_rd_ptr; /* Packet Ready Queue read pointer */ + ffreg_t prq_wr_ptr; /* Packet Ready Queue write pointer */ + ffreg_t tcq_st_adr; /* Transmit Complete Queue Start Address*/ + ffreg_t tcq_ed_adr; /* Transmit Complete Queue End Address */ + ffreg_t tcq_rd_ptr; /* Transmit Complete Queue read pointer */ + ffreg_t tcq_wr_ptr; /* Transmit Complete Queue write pointer*/ + u_int filler38[0x40 - 0x38]; + ffreg_t queue_base; /* Base address for PRQ and TCQ */ + ffreg_t desc_base; /* Base address of descriptor table */ + u_int filler42[0x45 - 0x42]; + ffreg_t mode_reg_0; /* Mode register 0 */ + ffreg_t mode_reg_1; /* Mode register 1 */ + ffreg_t intr_status_reg;/* Interrupt Status register */ + ffreg_t mask_reg; /* Mask Register */ + ffreg_t cell_ctr_high1; /* Total cell transfer count (high) */ + ffreg_t cell_ctr_lo1; /* Total cell transfer count (low) */ + ffreg_t state_reg; /* Status register */ + u_int filler4c[0x58 - 0x4c]; + ffreg_t curr_desc_num; /* Contains the current descriptor num */ + ffreg_t next_desc; /* Next descriptor */ + ffreg_t next_vc; /* Next VC */ + u_int filler5b[0x5d - 0x5b]; + ffreg_t present_slot_cnt;/* Present slot count */ + u_int filler5e[0x6a - 0x5e]; + ffreg_t new_desc_num; /* New descriptor number */ + ffreg_t new_vc; /* New VC */ + ffreg_t sched_tbl_ptr; /* Schedule table pointer */ + ffreg_t vbrwq_wptr; /* VBR wait queue write pointer */ + ffreg_t vbrwq_rptr; /* VBR wait queue read pointer */ + ffreg_t abrwq_wptr; /* ABR wait queue write pointer */ + ffreg_t abrwq_rptr; /* ABR wait queue read pointer */ + ffreg_t ubrwq_wptr; /* UBR wait queue write pointer */ + ffreg_t ubrwq_rptr; /* UBR wait queue read pointer */ + ffreg_t cbr_vc; /* CBR VC */ + ffreg_t vbr_sb_vc; /* VBR SB VC */ + ffreg_t abr_sb_vc; /* ABR SB VC */ + ffreg_t ubr_sb_vc; /* UBR SB VC */ + ffreg_t vbr_next_link; /* VBR next link */ + ffreg_t abr_next_link; /* ABR next link */ + ffreg_t ubr_next_link; /* UBR next link */ + u_int filler7a[0x7c-0x7a]; + ffreg_t out_rate_head; /* Out of rate head */ + u_int filler7d[0xca-0x7d]; /* pad out to full address space */ + ffreg_t cell_ctr_high1_nc;/* Total cell transfer count (high) */ + ffreg_t cell_ctr_lo1_nc;/* Total cell transfer count (low) */ + u_int fillercc[0x100-0xcc]; /* pad out to full address space */ } ffredn_t; typedef struct _rfredn_t { diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 401d1919635a..202518641779 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -16,6 +16,7 @@ #include <linux/regmap.h> #include <linux/fs.h> #include <linux/list.h> +#include <linux/wait.h> struct regmap; struct regcache_ops; @@ -39,6 +40,13 @@ struct regmap_format { unsigned int (*parse_val)(void *buf); }; +struct regmap_async { + struct list_head list; + struct work_struct cleanup; + struct regmap *map; + void *work_buf; +}; + struct regmap { struct mutex mutex; spinlock_t spinlock; @@ -53,6 +61,11 @@ struct regmap { void *bus_context; const char *name; + spinlock_t async_lock; + wait_queue_head_t async_waitq; + struct list_head async_list; + int async_ret; + #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; const char *debugfs_name; @@ -74,6 +87,9 @@ struct regmap { const struct regmap_access_table *volatile_table; const struct regmap_access_table *precious_table; + int (*reg_read)(void *context, unsigned int reg, unsigned int *val); + int (*reg_write)(void *context, unsigned int reg, unsigned int val); + u8 read_flag_mask; u8 write_flag_mask; @@ -175,6 +191,8 @@ bool regcache_set_val(void *base, unsigned int idx, unsigned int val, unsigned int word_size); int regcache_lookup_reg(struct regmap *map, unsigned int reg); +void regmap_async_complete_cb(struct regmap_async *async, int ret); + extern struct regcache_ops regcache_rbtree_ops; extern struct regcache_ops regcache_lzo_ops; diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index ffa46a92ad33..913274b5f00a 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -15,6 +15,21 @@ #include <linux/init.h> #include <linux/module.h> +#include "internal.h" + +struct regmap_async_spi { + struct regmap_async core; + struct spi_message m; + struct spi_transfer t[2]; +}; + +static void regmap_spi_complete(void *data) +{ + struct regmap_async_spi *async = data; + + regmap_async_complete_cb(&async->core, async->m.status); +} + static int regmap_spi_write(void *context, const void *data, size_t count) { struct device *dev = context; @@ -40,6 +55,41 @@ static int regmap_spi_gather_write(void *context, return spi_sync(spi, &m); } +static int regmap_spi_async_write(void *context, + const void *reg, size_t reg_len, + const void *val, size_t val_len, + struct regmap_async *a) +{ + struct regmap_async_spi *async = container_of(a, + struct regmap_async_spi, + core); + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + + async->t[0].tx_buf = reg; + async->t[0].len = reg_len; + async->t[1].tx_buf = val; + async->t[1].len = val_len; + + spi_message_init(&async->m); + spi_message_add_tail(&async->t[0], &async->m); + spi_message_add_tail(&async->t[1], &async->m); + + async->m.complete = regmap_spi_complete; + async->m.context = async; + + return spi_async(spi, &async->m); +} + +static struct regmap_async *regmap_spi_async_alloc(void) +{ + struct regmap_async_spi *async_spi; + + async_spi = kzalloc(sizeof(*async_spi), GFP_KERNEL); + + return &async_spi->core; +} + static int regmap_spi_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) @@ -53,6 +103,8 @@ static int regmap_spi_read(void *context, static struct regmap_bus regmap_spi = { .write = regmap_spi_write, .gather_write = regmap_spi_gather_write, + .async_write = regmap_spi_async_write, + .async_alloc = regmap_spi_async_alloc, .read = regmap_spi_read, .read_flag_mask = 0x80, }; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index f00b059c057a..47825095ebfd 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -16,6 +16,7 @@ #include <linux/mutex.h> #include <linux/err.h> #include <linux/rbtree.h> +#include <linux/sched.h> #define CREATE_TRACE_POINTS #include <trace/events/regmap.h> @@ -34,6 +35,22 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); +static int _regmap_bus_read(void *context, unsigned int reg, + unsigned int *val); +static int _regmap_bus_formatted_write(void *context, unsigned int reg, + unsigned int val); +static int _regmap_bus_raw_write(void *context, unsigned int reg, + unsigned int val); + +static void async_cleanup(struct work_struct *work) +{ + struct regmap_async *async = container_of(work, struct regmap_async, + cleanup); + + kfree(async->work_buf); + kfree(async); +} + bool regmap_reg_in_ranges(unsigned int reg, const struct regmap_range *ranges, unsigned int nranges) @@ -423,6 +440,10 @@ struct regmap *regmap_init(struct device *dev, map->cache_type = config->cache_type; map->name = config->name; + spin_lock_init(&map->async_lock); + INIT_LIST_HEAD(&map->async_list); + init_waitqueue_head(&map->async_waitq); + if (config->read_flag_mask || config->write_flag_mask) { map->read_flag_mask = config->read_flag_mask; map->write_flag_mask = config->write_flag_mask; @@ -430,6 +451,8 @@ struct regmap *regmap_init(struct device *dev, map->read_flag_mask = bus->read_flag_mask; } + map->reg_read = _regmap_bus_read; + reg_endian = config->reg_format_endian; if (reg_endian == REGMAP_ENDIAN_DEFAULT) reg_endian = bus->reg_format_endian_default; @@ -575,6 +598,11 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } + if (map->format.format_write) + map->reg_write = _regmap_bus_formatted_write; + else if (map->format.format_val) + map->reg_write = _regmap_bus_raw_write; + map->range_tree = RB_ROOT; for (i = 0; i < config->num_ranges; i++) { const struct regmap_range_cfg *range_cfg = &config->ranges[i]; @@ -870,10 +898,13 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg, } static int _regmap_raw_write(struct regmap *map, unsigned int reg, - const void *val, size_t val_len) + const void *val, size_t val_len, bool async) { struct regmap_range_node *range; + unsigned long flags; u8 *u8 = map->work_buf; + void *work_val = map->work_buf + map->format.reg_bytes + + map->format.pad_bytes; void *buf; int ret = -ENOTSUPP; size_t len; @@ -918,7 +949,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, dev_dbg(map->dev, "Writing window %d/%zu\n", win_residue, val_len / map->format.val_bytes); ret = _regmap_raw_write(map, reg, val, win_residue * - map->format.val_bytes); + map->format.val_bytes, async); if (ret != 0) return ret; @@ -941,6 +972,50 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, u8[0] |= map->write_flag_mask; + if (async && map->bus->async_write) { + struct regmap_async *async = map->bus->async_alloc(); + if (!async) + return -ENOMEM; + + async->work_buf = kzalloc(map->format.buf_size, + GFP_KERNEL | GFP_DMA); + if (!async->work_buf) { + kfree(async); + return -ENOMEM; + } + + INIT_WORK(&async->cleanup, async_cleanup); + async->map = map; + + /* If the caller supplied the value we can use it safely. */ + memcpy(async->work_buf, map->work_buf, map->format.pad_bytes + + map->format.reg_bytes + map->format.val_bytes); + if (val == work_val) + val = async->work_buf + map->format.pad_bytes + + map->format.reg_bytes; + + spin_lock_irqsave(&map->async_lock, flags); + list_add_tail(&async->list, &map->async_list); + spin_unlock_irqrestore(&map->async_lock, flags); + + ret = map->bus->async_write(map->bus_context, async->work_buf, + map->format.reg_bytes + + map->format.pad_bytes, + val, val_len, async); + + if (ret != 0) { + dev_err(map->dev, "Failed to schedule write: %d\n", + ret); + + spin_lock_irqsave(&map->async_lock, flags); + list_del(&async->list); + spin_unlock_irqrestore(&map->async_lock, flags); + + kfree(async->work_buf); + kfree(async); + } + } + trace_regmap_hw_write_start(map->dev, reg, val_len / map->format.val_bytes); @@ -948,8 +1023,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, * send the work_buf directly, otherwise try to do a gather * write. */ - if (val == (map->work_buf + map->format.pad_bytes + - map->format.reg_bytes)) + if (val == work_val) ret = map->bus->write(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes + @@ -981,12 +1055,54 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, return ret; } +static int _regmap_bus_formatted_write(void *context, unsigned int reg, + unsigned int val) +{ + int ret; + struct regmap_range_node *range; + struct regmap *map = context; + + BUG_ON(!map->format.format_write); + + range = _regmap_range_lookup(map, reg); + if (range) { + ret = _regmap_select_page(map, ®, range, 1); + if (ret != 0) + return ret; + } + + map->format.format_write(map, reg, val); + + trace_regmap_hw_write_start(map->dev, reg, 1); + + ret = map->bus->write(map->bus_context, map->work_buf, + map->format.buf_size); + + trace_regmap_hw_write_done(map->dev, reg, 1); + + return ret; +} + +static int _regmap_bus_raw_write(void *context, unsigned int reg, + unsigned int val) +{ + struct regmap *map = context; + + BUG_ON(!map->format.format_val); + + map->format.format_val(map->work_buf + map->format.reg_bytes + + map->format.pad_bytes, val, 0); + return _regmap_raw_write(map, reg, + map->work_buf + + map->format.reg_bytes + + map->format.pad_bytes, + map->format.val_bytes, false); +} + int _regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { - struct regmap_range_node *range; int ret; - BUG_ON(!map->format.format_write && !map->format.format_val); if (!map->cache_bypass && map->format.format_write) { ret = regcache_write(map, reg, val); @@ -1005,33 +1121,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, trace_regmap_reg_write(map->dev, reg, val); - if (map->format.format_write) { - range = _regmap_range_lookup(map, reg); - if (range) { - ret = _regmap_select_page(map, ®, range, 1); - if (ret != 0) - return ret; - } - - map->format.format_write(map, reg, val); - - trace_regmap_hw_write_start(map->dev, reg, 1); - - ret = map->bus->write(map->bus_context, map->work_buf, - map->format.buf_size); - - trace_regmap_hw_write_done(map->dev, reg, 1); - - return ret; - } else { - map->format.format_val(map->work_buf + map->format.reg_bytes - + map->format.pad_bytes, val, 0); - return _regmap_raw_write(map, reg, - map->work_buf + - map->format.reg_bytes + - map->format.pad_bytes, - map->format.val_bytes); - } + return map->reg_write(map, reg, val); } /** @@ -1089,7 +1179,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, map->lock(map->lock_arg); - ret = _regmap_raw_write(map, reg, val, val_len); + ret = _regmap_raw_write(map, reg, val, val_len, false); map->unlock(map->lock_arg); @@ -1145,14 +1235,15 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, if (map->use_single_rw) { for (i = 0; i < val_count; i++) { ret = regmap_raw_write(map, - reg + (i * map->reg_stride), - val + (i * val_bytes), - val_bytes); + reg + (i * map->reg_stride), + val + (i * val_bytes), + val_bytes); if (ret != 0) return ret; } } else { - ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); + ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count, + false); } if (val_bytes != 1) @@ -1164,6 +1255,48 @@ out: } EXPORT_SYMBOL_GPL(regmap_bulk_write); +/** + * regmap_raw_write_async(): Write raw values to one or more registers + * asynchronously + * + * @map: Register map to write to + * @reg: Initial register to write to + * @val: Block of data to be written, laid out for direct transmission to the + * device. Must be valid until regmap_async_complete() is called. + * @val_len: Length of data pointed to by val. + * + * This function is intended to be used for things like firmware + * download where a large block of data needs to be transferred to the + * device. No formatting will be done on the data provided. + * + * If supported by the underlying bus the write will be scheduled + * asynchronously, helping maximise I/O speed on higher speed buses + * like SPI. regmap_async_complete() can be called to ensure that all + * asynchrnous writes have been completed. + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_raw_write_async(struct regmap *map, unsigned int reg, + const void *val, size_t val_len) +{ + int ret; + + if (val_len % map->format.val_bytes) + return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; + + map->lock(map->lock_arg); + + ret = _regmap_raw_write(map, reg, val, val_len, true); + + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_raw_write_async); + static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int val_len) { @@ -1202,10 +1335,27 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, return ret; } +static int _regmap_bus_read(void *context, unsigned int reg, + unsigned int *val) +{ + int ret; + struct regmap *map = context; + + if (!map->format.parse_val) + return -EINVAL; + + ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes); + if (ret == 0) + *val = map->format.parse_val(map->work_buf); + + return ret; +} + static int _regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; + BUG_ON(!map->reg_read); if (!map->cache_bypass) { ret = regcache_read(map, reg, val); @@ -1213,26 +1363,21 @@ static int _regmap_read(struct regmap *map, unsigned int reg, return 0; } - if (!map->format.parse_val) - return -EINVAL; - if (map->cache_only) return -EBUSY; - ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes); + ret = map->reg_read(map, reg, val); if (ret == 0) { - *val = map->format.parse_val(map->work_buf); - #ifdef LOG_DEVICE if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0) dev_info(map->dev, "%x => %x\n", reg, *val); #endif trace_regmap_reg_read(map->dev, reg, *val); - } - if (ret == 0 && !map->cache_bypass) - regcache_write(map, reg, *val); + if (!map->cache_bypass) + regcache_write(map, reg, *val); + } return ret; } @@ -1450,6 +1595,68 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_update_bits_check); +void regmap_async_complete_cb(struct regmap_async *async, int ret) +{ + struct regmap *map = async->map; + bool wake; + + spin_lock(&map->async_lock); + + list_del(&async->list); + wake = list_empty(&map->async_list); + + if (ret != 0) + map->async_ret = ret; + + spin_unlock(&map->async_lock); + + schedule_work(&async->cleanup); + + if (wake) + wake_up(&map->async_waitq); +} +EXPORT_SYMBOL_GPL(regmap_async_complete_cb); + +static int regmap_async_is_done(struct regmap *map) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&map->async_lock, flags); + ret = list_empty(&map->async_list); + spin_unlock_irqrestore(&map->async_lock, flags); + + return ret; +} + +/** + * regmap_async_complete: Ensure all asynchronous I/O has completed. + * + * @map: Map to operate on. + * + * Blocks until any pending asynchronous I/O has completed. Returns + * an error code for any failed I/O operations. + */ +int regmap_async_complete(struct regmap *map) +{ + unsigned long flags; + int ret; + + /* Nothing to do with no async support */ + if (!map->bus->async_write) + return 0; + + wait_event(map->async_waitq, regmap_async_is_done(map)); + + spin_lock_irqsave(&map->async_lock, flags); + ret = map->async_ret; + map->async_ret = 0; + spin_unlock_irqrestore(&map->async_lock, flags); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_async_complete); + /** * regmap_register_patch: Register and apply register updates to be applied * on device initialistion diff --git a/drivers/bcma/bcma_private.h b/drivers/bcma/bcma_private.h index 19e3fbfd5757..cb0c45488572 100644 --- a/drivers/bcma/bcma_private.h +++ b/drivers/bcma/bcma_private.h @@ -94,11 +94,16 @@ void bcma_core_pci_hostmode_init(struct bcma_drv_pci *pc); #ifdef CONFIG_BCMA_DRIVER_GPIO /* driver_gpio.c */ int bcma_gpio_init(struct bcma_drv_cc *cc); +int bcma_gpio_unregister(struct bcma_drv_cc *cc); #else static inline int bcma_gpio_init(struct bcma_drv_cc *cc) { return -ENOTSUPP; } +static inline int bcma_gpio_unregister(struct bcma_drv_cc *cc) +{ + return 0; +} #endif /* CONFIG_BCMA_DRIVER_GPIO */ #endif diff --git a/drivers/bcma/driver_chipcommon_nflash.c b/drivers/bcma/driver_chipcommon_nflash.c index dbda91e4dff5..1f0b83e18f68 100644 --- a/drivers/bcma/driver_chipcommon_nflash.c +++ b/drivers/bcma/driver_chipcommon_nflash.c @@ -21,7 +21,7 @@ int bcma_nflash_init(struct bcma_drv_cc *cc) struct bcma_bus *bus = cc->core->bus; if (bus->chipinfo.id != BCMA_CHIP_ID_BCM4706 && - cc->core->id.rev != 0x38) { + cc->core->id.rev != 38) { bcma_err(bus, "NAND flash on unsupported board!\n"); return -ENOTSUPP; } diff --git a/drivers/bcma/driver_gpio.c b/drivers/bcma/driver_gpio.c index 9a6f585da2d9..71f755c06fc6 100644 --- a/drivers/bcma/driver_gpio.c +++ b/drivers/bcma/driver_gpio.c @@ -96,3 +96,8 @@ int bcma_gpio_init(struct bcma_drv_cc *cc) return gpiochip_add(chip); } + +int bcma_gpio_unregister(struct bcma_drv_cc *cc) +{ + return gpiochip_remove(&cc->gpio); +} diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 4a92f647b58b..324f9debda88 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -268,6 +268,13 @@ int bcma_bus_register(struct bcma_bus *bus) void bcma_bus_unregister(struct bcma_bus *bus) { struct bcma_device *cores[3]; + int err; + + err = bcma_gpio_unregister(&bus->drv_cc); + if (err == -EBUSY) + bcma_err(bus, "Some GPIOs are still in use.\n"); + else if (err) + bcma_err(bus, "Can not unregister GPIO driver: %i\n", err); cores[0] = bcma_find_core(bus, BCMA_CORE_MIPS_74K); cores[1] = bcma_find_core(bus, BCMA_CORE_PCIE); diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index f58a4a4b4dfb..2b8303ad63c9 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -168,7 +168,7 @@ static void wake_all_senders(struct drbd_tconn *tconn) { } /* must hold resource->req_lock */ -static void start_new_tl_epoch(struct drbd_tconn *tconn) +void start_new_tl_epoch(struct drbd_tconn *tconn) { /* no point closing an epoch, if it is empty, anyways. */ if (tconn->current_tle_writes == 0) diff --git a/drivers/block/drbd/drbd_req.h b/drivers/block/drbd/drbd_req.h index 016de6b8bb57..c08d22964d06 100644 --- a/drivers/block/drbd/drbd_req.h +++ b/drivers/block/drbd/drbd_req.h @@ -267,6 +267,7 @@ struct bio_and_error { int error; }; +extern void start_new_tl_epoch(struct drbd_tconn *tconn); extern void drbd_req_destroy(struct kref *kref); extern void _req_may_be_done(struct drbd_request *req, struct bio_and_error *m); diff --git a/drivers/block/drbd/drbd_state.c b/drivers/block/drbd/drbd_state.c index 53bf6182bac4..0fe220cfb9e9 100644 --- a/drivers/block/drbd/drbd_state.c +++ b/drivers/block/drbd/drbd_state.c @@ -931,6 +931,7 @@ __drbd_set_state(struct drbd_conf *mdev, union drbd_state ns, enum drbd_state_rv rv = SS_SUCCESS; enum sanitize_state_warnings ssw; struct after_state_chg_work *ascw; + bool did_remote, should_do_remote; os = drbd_read_state(mdev); @@ -981,11 +982,17 @@ __drbd_set_state(struct drbd_conf *mdev, union drbd_state ns, (os.disk != D_DISKLESS && ns.disk == D_DISKLESS)) atomic_inc(&mdev->local_cnt); + did_remote = drbd_should_do_remote(mdev->state); mdev->state.i = ns.i; + should_do_remote = drbd_should_do_remote(mdev->state); mdev->tconn->susp = ns.susp; mdev->tconn->susp_nod = ns.susp_nod; mdev->tconn->susp_fen = ns.susp_fen; + /* put replicated vs not-replicated requests in seperate epochs */ + if (did_remote != should_do_remote) + start_new_tl_epoch(mdev->tconn); + if (os.disk == D_ATTACHING && ns.disk >= D_NEGOTIATING) drbd_print_uuids(mdev, "attached to UUIDs"); diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 9694dd99bbbc..3fd100990453 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -626,12 +626,13 @@ static void mtip_timeout_function(unsigned long int data) } } - if (cmdto_cnt && !test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags)) { + if (cmdto_cnt) { print_tags(port->dd, "timed out", tagaccum, cmdto_cnt); - - mtip_restart_port(port); + if (!test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags)) { + mtip_restart_port(port); + wake_up_interruptible(&port->svc_wait); + } clear_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags); - wake_up_interruptible(&port->svc_wait); } if (port->ic_pause_timer) { @@ -3887,7 +3888,12 @@ static int mtip_block_remove(struct driver_data *dd) * Delete our gendisk structure. This also removes the device * from /dev */ - del_gendisk(dd->disk); + if (dd->disk) { + if (dd->disk->queue) + del_gendisk(dd->disk); + else + put_disk(dd->disk); + } spin_lock(&rssd_index_lock); ida_remove(&rssd_index_ida, dd->index); @@ -3921,7 +3927,13 @@ static int mtip_block_shutdown(struct driver_data *dd) "Shutting down %s ...\n", dd->disk->disk_name); /* Delete our gendisk structure, and cleanup the blk queue. */ - del_gendisk(dd->disk); + if (dd->disk) { + if (dd->disk->queue) + del_gendisk(dd->disk); + else + put_disk(dd->disk); + } + spin_lock(&rssd_index_lock); ida_remove(&rssd_index_ida, dd->index); diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index 74374fb762aa..5ac841ff6cc7 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -161,10 +161,12 @@ static int dispatch_rw_block_io(struct xen_blkif *blkif, static void make_response(struct xen_blkif *blkif, u64 id, unsigned short op, int st); -#define foreach_grant(pos, rbtree, node) \ - for ((pos) = container_of(rb_first((rbtree)), typeof(*(pos)), node); \ +#define foreach_grant_safe(pos, n, rbtree, node) \ + for ((pos) = container_of(rb_first((rbtree)), typeof(*(pos)), node), \ + (n) = rb_next(&(pos)->node); \ &(pos)->node != NULL; \ - (pos) = container_of(rb_next(&(pos)->node), typeof(*(pos)), node)) + (pos) = container_of(n, typeof(*(pos)), node), \ + (n) = (&(pos)->node != NULL) ? rb_next(&(pos)->node) : NULL) static void add_persistent_gnt(struct rb_root *root, @@ -217,10 +219,11 @@ static void free_persistent_gnts(struct rb_root *root, unsigned int num) struct gnttab_unmap_grant_ref unmap[BLKIF_MAX_SEGMENTS_PER_REQUEST]; struct page *pages[BLKIF_MAX_SEGMENTS_PER_REQUEST]; struct persistent_gnt *persistent_gnt; + struct rb_node *n; int ret = 0; int segs_to_unmap = 0; - foreach_grant(persistent_gnt, root, node) { + foreach_grant_safe(persistent_gnt, n, root, node) { BUG_ON(persistent_gnt->handle == BLKBACK_INVALID_HANDLE); gnttab_set_unmap_op(&unmap[segs_to_unmap], @@ -230,9 +233,6 @@ static void free_persistent_gnts(struct rb_root *root, unsigned int num) persistent_gnt->handle); pages[segs_to_unmap] = persistent_gnt->page; - rb_erase(&persistent_gnt->node, root); - kfree(persistent_gnt); - num--; if (++segs_to_unmap == BLKIF_MAX_SEGMENTS_PER_REQUEST || !rb_next(&persistent_gnt->node)) { @@ -241,6 +241,10 @@ static void free_persistent_gnts(struct rb_root *root, unsigned int num) BUG_ON(ret); segs_to_unmap = 0; } + + rb_erase(&persistent_gnt->node, root); + kfree(persistent_gnt); + num--; } BUG_ON(num != 0); } diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 96e9b00db081..11043c18ac5a 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -792,6 +792,7 @@ static void blkif_free(struct blkfront_info *info, int suspend) { struct llist_node *all_gnts; struct grant *persistent_gnt; + struct llist_node *n; /* Prevent new requests being issued until we fix things up. */ spin_lock_irq(&info->io_lock); @@ -804,7 +805,7 @@ static void blkif_free(struct blkfront_info *info, int suspend) /* Remove all persistent grants */ if (info->persistent_gnts_c) { all_gnts = llist_del_all(&info->persistent_gnts); - llist_for_each_entry(persistent_gnt, all_gnts, node) { + llist_for_each_entry_safe(persistent_gnt, n, all_gnts, node) { gnttab_end_foreign_access(persistent_gnt->gref, 0, 0UL); __free_page(pfn_to_page(persistent_gnt->pfn)); kfree(persistent_gnt); @@ -835,7 +836,7 @@ static void blkif_free(struct blkfront_info *info, int suspend) static void blkif_completion(struct blk_shadow *s, struct blkfront_info *info, struct blkif_response *bret) { - int i; + int i = 0; struct bio_vec *bvec; struct req_iterator iter; unsigned long flags; @@ -852,7 +853,8 @@ static void blkif_completion(struct blk_shadow *s, struct blkfront_info *info, */ rq_for_each_segment(bvec, s->request, iter) { BUG_ON((bvec->bv_offset + bvec->bv_len) > PAGE_SIZE); - i = offset >> PAGE_SHIFT; + if (bvec->bv_offset < offset) + i++; BUG_ON(i >= s->req.u.rw.nr_segments); shared_data = kmap_atomic( pfn_to_page(s->grants_used[i]->pfn)); @@ -861,7 +863,7 @@ static void blkif_completion(struct blk_shadow *s, struct blkfront_info *info, bvec->bv_len); bvec_kunmap_irq(bvec_data, &flags); kunmap_atomic(shared_data); - offset += bvec->bv_len; + offset = bvec->bv_offset + bvec->bv_len; } } /* Add the persistent grant into the list of free grants */ diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 684b0d53764f..ee4dbeafb377 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -2062,7 +2062,8 @@ static void virtcons_remove(struct virtio_device *vdev) /* Disable interrupts for vqs */ vdev->config->reset(vdev); /* Finish up work that's lined up */ - cancel_work_sync(&portdev->control_work); + if (use_multiport(portdev)) + cancel_work_sync(&portdev->control_work); list_for_each_entry_safe(port, port2, &portdev->ports, list) unplug_port(port); diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c index 4d0e60adbc6d..a2d478e8692a 100644 --- a/drivers/gpu/drm/radeon/evergreen.c +++ b/drivers/gpu/drm/radeon/evergreen.c @@ -1313,14 +1313,18 @@ void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *sav if (!(tmp & EVERGREEN_CRTC_BLANK_DATA_EN)) { radeon_wait_for_vblank(rdev, i); tmp |= EVERGREEN_CRTC_BLANK_DATA_EN; + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1); WREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i], tmp); + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0); } } else { tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]); if (!(tmp & EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE)) { radeon_wait_for_vblank(rdev, i); tmp |= EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE; + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1); WREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i], tmp); + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0); } } /* wait for the next frame */ @@ -1345,6 +1349,8 @@ void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *sav blackout &= ~BLACKOUT_MODE_MASK; WREG32(MC_SHARED_BLACKOUT_CNTL, blackout | 1); } + /* wait for the MC to settle */ + udelay(100); } void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save) @@ -1378,11 +1384,15 @@ void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *s if (ASIC_IS_DCE6(rdev)) { tmp = RREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i]); tmp |= EVERGREEN_CRTC_BLANK_DATA_EN; + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1); WREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i], tmp); + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0); } else { tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]); tmp &= ~EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE; + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1); WREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i], tmp); + WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0); } /* wait for the next frame */ frame_count = radeon_get_vblank_counter(rdev, i); @@ -2036,9 +2046,20 @@ static void evergreen_gpu_init(struct radeon_device *rdev) WREG32(HDP_ADDR_CONFIG, gb_addr_config); WREG32(DMA_TILING_CONFIG, gb_addr_config); - tmp = gb_addr_config & NUM_PIPES_MASK; - tmp = r6xx_remap_render_backend(rdev, tmp, rdev->config.evergreen.max_backends, - EVERGREEN_MAX_BACKENDS, disabled_rb_mask); + if ((rdev->config.evergreen.max_backends == 1) && + (rdev->flags & RADEON_IS_IGP)) { + if ((disabled_rb_mask & 3) == 1) { + /* RB0 disabled, RB1 enabled */ + tmp = 0x11111111; + } else { + /* RB1 disabled, RB0 enabled */ + tmp = 0x00000000; + } + } else { + tmp = gb_addr_config & NUM_PIPES_MASK; + tmp = r6xx_remap_render_backend(rdev, tmp, rdev->config.evergreen.max_backends, + EVERGREEN_MAX_BACKENDS, disabled_rb_mask); + } WREG32(GB_BACKEND_MAP, tmp); WREG32(CGTS_SYS_TCC_DISABLE, 0); diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index bc2540b17c5e..becb03e8b32f 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -1462,12 +1462,15 @@ u32 r6xx_remap_render_backend(struct radeon_device *rdev, u32 disabled_rb_mask) { u32 rendering_pipe_num, rb_num_width, req_rb_num; - u32 pipe_rb_ratio, pipe_rb_remain; + u32 pipe_rb_ratio, pipe_rb_remain, tmp; u32 data = 0, mask = 1 << (max_rb_num - 1); unsigned i, j; /* mask out the RBs that don't exist on that asic */ - disabled_rb_mask |= (0xff << max_rb_num) & 0xff; + tmp = disabled_rb_mask | ((0xff << max_rb_num) & 0xff); + /* make sure at least one RB is available */ + if ((tmp & 0xff) != 0xff) + disabled_rb_mask = tmp; rendering_pipe_num = 1 << tiling_pipe_num; req_rb_num = total_max_rb_num - r600_count_pipe_bits(disabled_rb_mask); diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c index 9056fafb00ea..0b202c07fe50 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.c +++ b/drivers/gpu/drm/radeon/radeon_asic.c @@ -1445,7 +1445,7 @@ static struct radeon_asic cayman_asic = { .vm = { .init = &cayman_vm_init, .fini = &cayman_vm_fini, - .pt_ring_index = R600_RING_TYPE_DMA_INDEX, + .pt_ring_index = RADEON_RING_TYPE_GFX_INDEX, .set_page = &cayman_vm_set_page, }, .ring = { @@ -1572,7 +1572,7 @@ static struct radeon_asic trinity_asic = { .vm = { .init = &cayman_vm_init, .fini = &cayman_vm_fini, - .pt_ring_index = R600_RING_TYPE_DMA_INDEX, + .pt_ring_index = RADEON_RING_TYPE_GFX_INDEX, .set_page = &cayman_vm_set_page, }, .ring = { @@ -1699,7 +1699,7 @@ static struct radeon_asic si_asic = { .vm = { .init = &si_vm_init, .fini = &si_vm_fini, - .pt_ring_index = R600_RING_TYPE_DMA_INDEX, + .pt_ring_index = RADEON_RING_TYPE_GFX_INDEX, .set_page = &si_vm_set_page, }, .ring = { diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index 33a56a09ff10..3e403bdda58f 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -2470,6 +2470,14 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev) 1), ATOM_DEVICE_CRT1_SUPPORT); } + /* RV100 board with external TDMS bit mis-set. + * Actually uses internal TMDS, clear the bit. + */ + if (dev->pdev->device == 0x5159 && + dev->pdev->subsystem_vendor == 0x1014 && + dev->pdev->subsystem_device == 0x029A) { + tmp &= ~(1 << 4); + } if ((tmp >> 4) & 0x1) { devices |= ATOM_DEVICE_DFP2_SUPPORT; radeon_add_legacy_encoder(dev, diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index ff3def784619..05c96fa0b051 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -1115,8 +1115,10 @@ radeon_user_framebuffer_create(struct drm_device *dev, } radeon_fb = kzalloc(sizeof(*radeon_fb), GFP_KERNEL); - if (radeon_fb == NULL) + if (radeon_fb == NULL) { + drm_gem_object_unreference_unlocked(obj); return ERR_PTR(-ENOMEM); + } ret = radeon_framebuffer_init(dev, radeon_fb, mode_cmd, obj); if (ret) { diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c index 2430d80b1871..cd72062d5a91 100644 --- a/drivers/gpu/drm/radeon/radeon_ring.c +++ b/drivers/gpu/drm/radeon/radeon_ring.c @@ -377,6 +377,9 @@ int radeon_ring_alloc(struct radeon_device *rdev, struct radeon_ring *ring, unsi { int r; + /* make sure we aren't trying to allocate more space than there is on the ring */ + if (ndw > (ring->ring_size / 4)) + return -ENOMEM; /* Align requested size with padding so unlock_commit can * pad safely */ ndw = (ndw + ring->align_mask) & ~ring->align_mask; diff --git a/drivers/gpu/drm/radeon/reg_srcs/cayman b/drivers/gpu/drm/radeon/reg_srcs/cayman index 0f656b111c15..a072fa8c46b0 100644 --- a/drivers/gpu/drm/radeon/reg_srcs/cayman +++ b/drivers/gpu/drm/radeon/reg_srcs/cayman @@ -1,5 +1,6 @@ cayman 0x9400 0x0000802C GRBM_GFX_INDEX +0x00008040 WAIT_UNTIL 0x000084FC CP_STRMOUT_CNTL 0x000085F0 CP_COHER_CNTL 0x000085F4 CP_COHER_SIZE diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c index 2bb6d0e84b3d..435ed3551364 100644 --- a/drivers/gpu/drm/radeon/rv515.c +++ b/drivers/gpu/drm/radeon/rv515.c @@ -336,6 +336,8 @@ void rv515_mc_stop(struct radeon_device *rdev, struct rv515_mc_save *save) WREG32(R600_CITF_CNTL, blackout); } } + /* wait for the MC to settle */ + udelay(100); } void rv515_mc_resume(struct radeon_device *rdev, struct rv515_mc_save *save) diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c index 44420fca7dfa..8be35c809c7b 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_util.c +++ b/drivers/gpu/drm/ttm/ttm_bo_util.c @@ -429,7 +429,7 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo, struct ttm_bo_device *bdev = bo->bdev; struct ttm_bo_driver *driver = bdev->driver; - fbo = kzalloc(sizeof(*fbo), GFP_KERNEL); + fbo = kmalloc(sizeof(*fbo), GFP_KERNEL); if (!fbo) return -ENOMEM; @@ -448,7 +448,12 @@ static int ttm_buffer_object_transfer(struct ttm_buffer_object *bo, fbo->vm_node = NULL; atomic_set(&fbo->cpu_writers, 0); - fbo->sync_obj = driver->sync_obj_ref(bo->sync_obj); + spin_lock(&bdev->fence_lock); + if (bo->sync_obj) + fbo->sync_obj = driver->sync_obj_ref(bo->sync_obj); + else + fbo->sync_obj = NULL; + spin_unlock(&bdev->fence_lock); kref_init(&fbo->list_kref); kref_init(&fbo->kref); fbo->destroy = &ttm_transfered_destroy; @@ -661,13 +666,11 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo, */ set_bit(TTM_BO_PRIV_FLAG_MOVING, &bo->priv_flags); - - /* ttm_buffer_object_transfer accesses bo->sync_obj */ - ret = ttm_buffer_object_transfer(bo, &ghost_obj); spin_unlock(&bdev->fence_lock); if (tmp_obj) driver->sync_obj_unref(&tmp_obj); + ret = ttm_buffer_object_transfer(bo, &ghost_obj); if (ret) return ret; diff --git a/drivers/infiniband/hw/qib/qib_qp.c b/drivers/infiniband/hw/qib/qib_qp.c index 4850d03870c2..35275099cafd 100644 --- a/drivers/infiniband/hw/qib/qib_qp.c +++ b/drivers/infiniband/hw/qib/qib_qp.c @@ -263,20 +263,15 @@ static void remove_qp(struct qib_ibdev *dev, struct qib_qp *qp) struct qib_qp __rcu **qpp; qpp = &dev->qp_table[n]; - q = rcu_dereference_protected(*qpp, - lockdep_is_held(&dev->qpt_lock)); - for (; q; qpp = &q->next) { + for (; (q = rcu_dereference_protected(*qpp, + lockdep_is_held(&dev->qpt_lock))) != NULL; + qpp = &q->next) if (q == qp) { atomic_dec(&qp->refcount); *qpp = qp->next; rcu_assign_pointer(qp->next, NULL); - q = rcu_dereference_protected(*qpp, - lockdep_is_held(&dev->qpt_lock)); break; } - q = rcu_dereference_protected(*qpp, - lockdep_is_held(&dev->qpt_lock)); - } } spin_unlock_irqrestore(&dev->qpt_lock, flags); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index 03103d2bd641..67b0c1d23678 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -741,6 +741,9 @@ void ipoib_cm_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_cm_ tx_req->mapping = addr; + skb_orphan(skb); + skb_dst_drop(skb); + rc = post_send(priv, tx, tx->tx_head & (ipoib_sendq_size - 1), addr, skb->len); if (unlikely(rc)) { @@ -752,9 +755,6 @@ void ipoib_cm_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_cm_ dev->trans_start = jiffies; ++tx->tx_head; - skb_orphan(skb); - skb_dst_drop(skb); - if (++priv->tx_outstanding == ipoib_sendq_size) { ipoib_dbg(priv, "TX ring 0x%x full, stopping kernel net queue\n", tx->qp->qp_num); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index a1bca70e20aa..2cfa76f5d99e 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -600,6 +600,9 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb, netif_stop_queue(dev); } + skb_orphan(skb); + skb_dst_drop(skb); + rc = post_send(priv, priv->tx_head & (ipoib_sendq_size - 1), address->ah, qpn, tx_req, phead, hlen); if (unlikely(rc)) { @@ -615,9 +618,6 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb, address->last_send = priv->tx_head; ++priv->tx_head; - - skb_orphan(skb); - skb_dst_drop(skb); } if (unlikely(priv->tx_outstanding > MAX_SEND_CQE)) diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c index e10e525f33e5..296941a9ae25 100644 --- a/drivers/media/radio/radio-keene.c +++ b/drivers/media/radio/radio-keene.c @@ -374,6 +374,7 @@ static int usb_keene_probe(struct usb_interface *intf, radio->vdev.ioctl_ops = &usb_keene_ioctl_ops; radio->vdev.lock = &radio->lock; radio->vdev.release = video_device_release_empty; + radio->vdev.vfl_dir = VFL_DIR_TX; radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; diff --git a/drivers/media/radio/radio-si4713.c b/drivers/media/radio/radio-si4713.c index a082e400ed0f..1507c9d508d7 100644 --- a/drivers/media/radio/radio-si4713.c +++ b/drivers/media/radio/radio-si4713.c @@ -250,6 +250,7 @@ static struct video_device radio_si4713_vdev_template = { .name = "radio-si4713", .release = video_device_release, .ioctl_ops = &radio_si4713_ioctl_ops, + .vfl_dir = VFL_DIR_TX, }; /* Platform driver interface */ diff --git a/drivers/media/radio/radio-wl1273.c b/drivers/media/radio/radio-wl1273.c index c48be195bbad..cabbe3adf435 100644 --- a/drivers/media/radio/radio-wl1273.c +++ b/drivers/media/radio/radio-wl1273.c @@ -1971,6 +1971,7 @@ static struct video_device wl1273_viddev_template = { .ioctl_ops = &wl1273_ioctl_ops, .name = WL1273_FM_DRIVER_NAME, .release = wl1273_vdev_release, + .vfl_dir = VFL_DIR_TX, }; static int wl1273_fm_radio_remove(struct platform_device *pdev) diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c index 048de4536036..0a8ee8fab924 100644 --- a/drivers/media/radio/wl128x/fmdrv_v4l2.c +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c @@ -518,6 +518,16 @@ static struct video_device fm_viddev_template = { .ioctl_ops = &fm_drv_ioctl_ops, .name = FM_DRV_NAME, .release = video_device_release, + /* + * To ensure both the tuner and modulator ioctls are accessible we + * set the vfl_dir to M2M to indicate this. + * + * It is not really a mem2mem device of course, but it can both receive + * and transmit using the same radio device. It's the only radio driver + * that does this and it should really be split in two radio devices, + * but that would affect applications using this driver. + */ + .vfl_dir = VFL_DIR_M2M, }; int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index b151b7c1bd59..8f59d88897e7 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -192,7 +192,7 @@ config ICS932S401 config ATMEL_SSC tristate "Device driver for Atmel SSC peripheral" - depends on AVR32 || ARCH_AT91 + depends on HAS_IOMEM ---help--- This option enables device driver support for Atmel Synchronized Serial Communication peripheral (SSC). diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index 3c09cbb70b1d..bd90dd23242e 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -175,7 +175,7 @@ static int ssc_probe(struct platform_device *pdev) /* disable all interrupts */ clk_enable(ssc->clk); - ssc_writel(ssc->regs, IDR, ~0UL); + ssc_writel(ssc->regs, IDR, -1); ssc_readl(ssc->regs, SR); clk_disable(ssc->clk); diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 27f80cd8aef3..46dcb54c32ec 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -272,6 +272,7 @@ config MTD_DOCG3 tristate "M-Systems Disk-On-Chip G3" select BCH select BCH_CONST_PARAMS + select BITREVERSE ---help--- This provides an MTD device driver for the M-Systems DiskOnChip G3 devices. diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 67cc73c18ddd..7901d72c9242 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -170,7 +170,7 @@ static int of_flash_probe(struct platform_device *dev) resource_size_t res_size; struct mtd_part_parser_data ppdata; bool map_indirect; - const char *mtd_name; + const char *mtd_name = NULL; match = of_match_device(of_flash_match, &dev->dev); if (!match) diff --git a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c index 86c9a79b89b3..595de4012e71 100644 --- a/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c +++ b/drivers/mtd/nand/bcm47xxnflash/ops_bcm4706.c @@ -17,8 +17,8 @@ #include "bcm47xxnflash.h" /* Broadcom uses 1'000'000 but it seems to be too many. Tests on WNDR4500 has - * shown 164 retries as maxiumum. */ -#define NFLASH_READY_RETRIES 1000 + * shown ~1000 retries as maxiumum. */ +#define NFLASH_READY_RETRIES 10000 #define NFLASH_SECTOR_SIZE 512 diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 3502606f6480..feae55c7b880 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -523,7 +523,7 @@ static struct nand_ecclayout hwecc4_2048 __initconst = { static const struct of_device_id davinci_nand_of_match[] = { {.compatible = "ti,davinci-nand", }, {}, -} +}; MODULE_DEVICE_TABLE(of, davinci_nand_of_match); static struct davinci_nand_pdata diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8323ac991ad1..3766682a0289 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2857,8 +2857,11 @@ static int nand_flash_detect_onfi(struct mtd_info *mtd, struct nand_chip *chip, int i; int val; - /* ONFI need to be probed in 8 bits mode */ - WARN_ON(chip->options & NAND_BUSWIDTH_16); + /* ONFI need to be probed in 8 bits mode, and 16 bits should be selected with NAND_BUSWIDTH_AUTO */ + if (chip->options & NAND_BUSWIDTH_16) { + pr_err("Trying ONFI probe in 16 bits mode, aborting !\n"); + return 0; + } /* Try ONFI for unknown chip or LP */ chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1); if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' || diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c index 1877ed7ca086..1c9e09fbdff8 100644 --- a/drivers/net/bonding/bond_sysfs.c +++ b/drivers/net/bonding/bond_sysfs.c @@ -1053,6 +1053,7 @@ static ssize_t bonding_store_primary(struct device *d, pr_info("%s: Setting primary slave to None.\n", bond->dev->name); bond->primary_slave = NULL; + memset(bond->params.primary, 0, sizeof(bond->params.primary)); bond_select_active_slave(bond); goto out; } diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c index 58607f196c9e..2282b1ae9765 100644 --- a/drivers/net/can/c_can/c_can.c +++ b/drivers/net/can/c_can/c_can.c @@ -488,8 +488,12 @@ static void c_can_setup_receive_object(struct net_device *dev, int iface, priv->write_reg(priv, C_CAN_IFACE(MASK1_REG, iface), IFX_WRITE_LOW_16BIT(mask)); + + /* According to C_CAN documentation, the reserved bit + * in IFx_MASK2 register is fixed 1 + */ priv->write_reg(priv, C_CAN_IFACE(MASK2_REG, iface), - IFX_WRITE_HIGH_16BIT(mask)); + IFX_WRITE_HIGH_16BIT(mask) | BIT(13)); priv->write_reg(priv, C_CAN_IFACE(ARB1_REG, iface), IFX_WRITE_LOW_16BIT(id)); diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index 4eba17b83ba8..f1b3df167ff2 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -36,13 +36,13 @@ #define DRV_VER "4.4.161.0u" #define DRV_NAME "be2net" -#define BE_NAME "ServerEngines BladeEngine2 10Gbps NIC" -#define BE3_NAME "ServerEngines BladeEngine3 10Gbps NIC" -#define OC_NAME "Emulex OneConnect 10Gbps NIC" +#define BE_NAME "Emulex BladeEngine2" +#define BE3_NAME "Emulex BladeEngine3" +#define OC_NAME "Emulex OneConnect" #define OC_NAME_BE OC_NAME "(be3)" #define OC_NAME_LANCER OC_NAME "(Lancer)" #define OC_NAME_SH OC_NAME "(Skyhawk)" -#define DRV_DESC "ServerEngines BladeEngine 10Gbps NIC Driver" +#define DRV_DESC "Emulex OneConnect 10Gbps NIC Driver" #define BE_VENDOR_ID 0x19a2 #define EMULEX_VENDOR_ID 0x10df diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 5c995700e534..4d6f3c54427a 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -25,7 +25,7 @@ MODULE_VERSION(DRV_VER); MODULE_DEVICE_TABLE(pci, be_dev_ids); MODULE_DESCRIPTION(DRV_DESC " " DRV_VER); -MODULE_AUTHOR("ServerEngines Corporation"); +MODULE_AUTHOR("Emulex Corporation"); MODULE_LICENSE("GPL"); static unsigned int num_vfs; diff --git a/drivers/net/ethernet/intel/e1000e/defines.h b/drivers/net/ethernet/intel/e1000e/defines.h index 02a12b69555f..4dab6fc265a2 100644 --- a/drivers/net/ethernet/intel/e1000e/defines.h +++ b/drivers/net/ethernet/intel/e1000e/defines.h @@ -232,6 +232,7 @@ #define E1000_CTRL_FRCDPX 0x00001000 /* Force Duplex */ #define E1000_CTRL_LANPHYPC_OVERRIDE 0x00010000 /* SW control of LANPHYPC */ #define E1000_CTRL_LANPHYPC_VALUE 0x00020000 /* SW value of LANPHYPC */ +#define E1000_CTRL_MEHE 0x00080000 /* Memory Error Handling Enable */ #define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */ #define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */ #define E1000_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */ @@ -389,6 +390,12 @@ #define E1000_PBS_16K E1000_PBA_16K +/* Uncorrectable/correctable ECC Error counts and enable bits */ +#define E1000_PBECCSTS_CORR_ERR_CNT_MASK 0x000000FF +#define E1000_PBECCSTS_UNCORR_ERR_CNT_MASK 0x0000FF00 +#define E1000_PBECCSTS_UNCORR_ERR_CNT_SHIFT 8 +#define E1000_PBECCSTS_ECC_ENABLE 0x00010000 + #define IFS_MAX 80 #define IFS_MIN 40 #define IFS_RATIO 4 @@ -408,6 +415,7 @@ #define E1000_ICR_RXSEQ 0x00000008 /* Rx sequence error */ #define E1000_ICR_RXDMT0 0x00000010 /* Rx desc min. threshold (0) */ #define E1000_ICR_RXT0 0x00000080 /* Rx timer intr (ring 0) */ +#define E1000_ICR_ECCER 0x00400000 /* Uncorrectable ECC Error */ #define E1000_ICR_INT_ASSERTED 0x80000000 /* If this bit asserted, the driver should claim the interrupt */ #define E1000_ICR_RXQ0 0x00100000 /* Rx Queue 0 Interrupt */ #define E1000_ICR_RXQ1 0x00200000 /* Rx Queue 1 Interrupt */ @@ -443,6 +451,7 @@ #define E1000_IMS_RXSEQ E1000_ICR_RXSEQ /* Rx sequence error */ #define E1000_IMS_RXDMT0 E1000_ICR_RXDMT0 /* Rx desc min. threshold */ #define E1000_IMS_RXT0 E1000_ICR_RXT0 /* Rx timer intr */ +#define E1000_IMS_ECCER E1000_ICR_ECCER /* Uncorrectable ECC Error */ #define E1000_IMS_RXQ0 E1000_ICR_RXQ0 /* Rx Queue 0 Interrupt */ #define E1000_IMS_RXQ1 E1000_ICR_RXQ1 /* Rx Queue 1 Interrupt */ #define E1000_IMS_TXQ0 E1000_ICR_TXQ0 /* Tx Queue 0 Interrupt */ diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index 6782a2eea1bc..7e95f221d60b 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -309,6 +309,8 @@ struct e1000_adapter { struct napi_struct napi; + unsigned int uncorr_errors; /* uncorrectable ECC errors */ + unsigned int corr_errors; /* correctable ECC errors */ unsigned int restart_queue; u32 txd_cmd; diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index f95bc6ee1c22..fd4772a2691c 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -108,6 +108,8 @@ static const struct e1000_stats e1000_gstrings_stats[] = { E1000_STAT("dropped_smbus", stats.mgpdc), E1000_STAT("rx_dma_failed", rx_dma_failed), E1000_STAT("tx_dma_failed", tx_dma_failed), + E1000_STAT("uncorr_ecc_errors", uncorr_errors), + E1000_STAT("corr_ecc_errors", corr_errors), }; #define E1000_GLOBAL_STATS_LEN ARRAY_SIZE(e1000_gstrings_stats) diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h index cf217777586c..b88676ff3d86 100644 --- a/drivers/net/ethernet/intel/e1000e/hw.h +++ b/drivers/net/ethernet/intel/e1000e/hw.h @@ -77,6 +77,7 @@ enum e1e_registers { #define E1000_POEMB E1000_PHY_CTRL /* PHY OEM Bits */ E1000_PBA = 0x01000, /* Packet Buffer Allocation - RW */ E1000_PBS = 0x01008, /* Packet Buffer Size */ + E1000_PBECCSTS = 0x0100C, /* Packet Buffer ECC Status - RW */ E1000_EEMNGCTL = 0x01010, /* MNG EEprom Control */ E1000_EEWR = 0x0102C, /* EEPROM Write Register - RW */ E1000_FLOP = 0x0103C, /* FLASH Opcode Register */ diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index 976336547607..24d9f61956f0 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -3624,6 +3624,17 @@ static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw) if (hw->mac.type == e1000_ich8lan) reg |= (E1000_RFCTL_IPV6_EX_DIS | E1000_RFCTL_NEW_IPV6_EXT_DIS); ew32(RFCTL, reg); + + /* Enable ECC on Lynxpoint */ + if (hw->mac.type == e1000_pch_lpt) { + reg = er32(PBECCSTS); + reg |= E1000_PBECCSTS_ECC_ENABLE; + ew32(PBECCSTS, reg); + + reg = er32(CTRL); + reg |= E1000_CTRL_MEHE; + ew32(CTRL, reg); + } } /** diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index fbf75fdca994..643c883dd795 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -1678,6 +1678,23 @@ static irqreturn_t e1000_intr_msi(int irq, void *data) mod_timer(&adapter->watchdog_timer, jiffies + 1); } + /* Reset on uncorrectable ECC error */ + if ((icr & E1000_ICR_ECCER) && (hw->mac.type == e1000_pch_lpt)) { + u32 pbeccsts = er32(PBECCSTS); + + adapter->corr_errors += + pbeccsts & E1000_PBECCSTS_CORR_ERR_CNT_MASK; + adapter->uncorr_errors += + (pbeccsts & E1000_PBECCSTS_UNCORR_ERR_CNT_MASK) >> + E1000_PBECCSTS_UNCORR_ERR_CNT_SHIFT; + + /* Do the reset outside of interrupt context */ + schedule_work(&adapter->reset_task); + + /* return immediately since reset is imminent */ + return IRQ_HANDLED; + } + if (napi_schedule_prep(&adapter->napi)) { adapter->total_tx_bytes = 0; adapter->total_tx_packets = 0; @@ -1741,6 +1758,23 @@ static irqreturn_t e1000_intr(int irq, void *data) mod_timer(&adapter->watchdog_timer, jiffies + 1); } + /* Reset on uncorrectable ECC error */ + if ((icr & E1000_ICR_ECCER) && (hw->mac.type == e1000_pch_lpt)) { + u32 pbeccsts = er32(PBECCSTS); + + adapter->corr_errors += + pbeccsts & E1000_PBECCSTS_CORR_ERR_CNT_MASK; + adapter->uncorr_errors += + (pbeccsts & E1000_PBECCSTS_UNCORR_ERR_CNT_MASK) >> + E1000_PBECCSTS_UNCORR_ERR_CNT_SHIFT; + + /* Do the reset outside of interrupt context */ + schedule_work(&adapter->reset_task); + + /* return immediately since reset is imminent */ + return IRQ_HANDLED; + } + if (napi_schedule_prep(&adapter->napi)) { adapter->total_tx_bytes = 0; adapter->total_tx_packets = 0; @@ -2104,6 +2138,8 @@ static void e1000_irq_enable(struct e1000_adapter *adapter) if (adapter->msix_entries) { ew32(EIAC_82574, adapter->eiac_mask & E1000_EIAC_MASK_82574); ew32(IMS, adapter->eiac_mask | E1000_IMS_OTHER | E1000_IMS_LSC); + } else if (hw->mac.type == e1000_pch_lpt) { + ew32(IMS, IMS_ENABLE_MASK | E1000_IMS_ECCER); } else { ew32(IMS, IMS_ENABLE_MASK); } @@ -4251,6 +4287,16 @@ static void e1000e_update_stats(struct e1000_adapter *adapter) adapter->stats.mgptc += er32(MGTPTC); adapter->stats.mgprc += er32(MGTPRC); adapter->stats.mgpdc += er32(MGTPDC); + + /* Correctable ECC Errors */ + if (hw->mac.type == e1000_pch_lpt) { + u32 pbeccsts = er32(PBECCSTS); + adapter->corr_errors += + pbeccsts & E1000_PBECCSTS_CORR_ERR_CNT_MASK; + adapter->uncorr_errors += + (pbeccsts & E1000_PBECCSTS_UNCORR_ERR_CNT_MASK) >> + E1000_PBECCSTS_UNCORR_ERR_CNT_SHIFT; + } } /** diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index a6542d75374c..5163af314990 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -380,7 +380,7 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap) } } - if ((dev_cap->flags & + if ((dev->caps.flags & (MLX4_DEV_CAP_FLAG_64B_CQE | MLX4_DEV_CAP_FLAG_64B_EQE)) && mlx4_is_master(dev)) dev->caps.function_caps |= MLX4_FUNC_CAP_64B_EQE_CQE; diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c index 7992b3e05d3d..78ace59efd29 100644 --- a/drivers/net/ethernet/via/via-rhine.c +++ b/drivers/net/ethernet/via/via-rhine.c @@ -1801,7 +1801,7 @@ static void rhine_tx(struct net_device *dev) rp->tx_skbuff[entry]->len, PCI_DMA_TODEVICE); } - dev_kfree_skb_irq(rp->tx_skbuff[entry]); + dev_kfree_skb(rp->tx_skbuff[entry]); rp->tx_skbuff[entry] = NULL; entry = (++rp->dirty_tx) % TX_RING_SIZE; } @@ -2010,11 +2010,7 @@ static void rhine_slow_event_task(struct work_struct *work) if (intr_status & IntrPCIErr) netif_warn(rp, hw, dev, "PCI error\n"); - napi_disable(&rp->napi); - rhine_irq_disable(rp); - /* Slow and safe. Consider __napi_schedule as a replacement ? */ - napi_enable(&rp->napi); - napi_schedule(&rp->napi); + iowrite16(RHINE_EVENT & 0xffff, rp->base + IntrEnable); out_unlock: mutex_unlock(&rp->task_lock); diff --git a/drivers/net/tun.c b/drivers/net/tun.c index cc09b67c23bc..2917a86f4c43 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -298,11 +298,12 @@ static void tun_flow_cleanup(unsigned long data) } static void tun_flow_update(struct tun_struct *tun, u32 rxhash, - u16 queue_index) + struct tun_file *tfile) { struct hlist_head *head; struct tun_flow_entry *e; unsigned long delay = tun->ageing_time; + u16 queue_index = tfile->queue_index; if (!rxhash) return; @@ -311,7 +312,9 @@ static void tun_flow_update(struct tun_struct *tun, u32 rxhash, rcu_read_lock(); - if (tun->numqueues == 1) + /* We may get a very small possibility of OOO during switching, not + * worth to optimize.*/ + if (tun->numqueues == 1 || tfile->detached) goto unlock; e = tun_flow_find(head, rxhash); @@ -411,21 +414,21 @@ static void __tun_detach(struct tun_file *tfile, bool clean) tun = rtnl_dereference(tfile->tun); - if (tun) { + if (tun && !tfile->detached) { u16 index = tfile->queue_index; BUG_ON(index >= tun->numqueues); dev = tun->dev; rcu_assign_pointer(tun->tfiles[index], tun->tfiles[tun->numqueues - 1]); - rcu_assign_pointer(tfile->tun, NULL); ntfile = rtnl_dereference(tun->tfiles[index]); ntfile->queue_index = index; --tun->numqueues; - if (clean) + if (clean) { + rcu_assign_pointer(tfile->tun, NULL); sock_put(&tfile->sk); - else + } else tun_disable_queue(tun, tfile); synchronize_net(); @@ -439,10 +442,13 @@ static void __tun_detach(struct tun_file *tfile, bool clean) } if (clean) { - if (tun && tun->numqueues == 0 && tun->numdisabled == 0 && - !(tun->flags & TUN_PERSIST)) - if (tun->dev->reg_state == NETREG_REGISTERED) + if (tun && tun->numqueues == 0 && tun->numdisabled == 0) { + netif_carrier_off(tun->dev); + + if (!(tun->flags & TUN_PERSIST) && + tun->dev->reg_state == NETREG_REGISTERED) unregister_netdevice(tun->dev); + } BUG_ON(!test_bit(SOCK_EXTERNALLY_ALLOCATED, &tfile->socket.flags)); @@ -470,6 +476,10 @@ static void tun_detach_all(struct net_device *dev) rcu_assign_pointer(tfile->tun, NULL); --tun->numqueues; } + list_for_each_entry(tfile, &tun->disabled, next) { + wake_up_all(&tfile->wq.wait); + rcu_assign_pointer(tfile->tun, NULL); + } BUG_ON(tun->numqueues != 0); synchronize_net(); @@ -500,7 +510,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file) goto out; err = -EINVAL; - if (rtnl_dereference(tfile->tun)) + if (rtnl_dereference(tfile->tun) && !tfile->detached) goto out; err = -EBUSY; @@ -1199,7 +1209,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile, tun->dev->stats.rx_packets++; tun->dev->stats.rx_bytes += len; - tun_flow_update(tun, rxhash, tfile->queue_index); + tun_flow_update(tun, rxhash, tfile); return total_len; } @@ -1658,10 +1668,10 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) device_create_file(&tun->dev->dev, &dev_attr_owner) || device_create_file(&tun->dev->dev, &dev_attr_group)) pr_err("Failed to create tun sysfs files\n"); - - netif_carrier_on(tun->dev); } + netif_carrier_on(tun->dev); + tun_debug(KERN_INFO, tun, "tun_set_iff\n"); if (ifr->ifr_flags & IFF_NO_PI) @@ -1813,7 +1823,7 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr) ret = tun_attach(tun, file); } else if (ifr->ifr_flags & IFF_DETACH_QUEUE) { tun = rtnl_dereference(tfile->tun); - if (!tun || !(tun->flags & TUN_TAP_MQ)) + if (!tun || !(tun->flags & TUN_TAP_MQ) || tfile->detached) ret = -EINVAL; else __tun_detach(tfile, false); diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 9197b2c72ca3..00d3b2d37828 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -1215,6 +1215,9 @@ static const struct usb_device_id cdc_devs[] = { { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x46), .driver_info = (unsigned long)&wwan_info, }, + { USB_VENDOR_AND_INTERFACE_INFO(0x12d1, 0xff, 0x02, 0x76), + .driver_info = (unsigned long)&wwan_info, + }, /* Infineon(now Intel) HSPA Modem platform */ { USB_DEVICE_AND_INTERFACE_INFO(0x1519, 0x0443, diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 575a5839ee34..c8e05e27f38c 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -351,6 +351,10 @@ static const struct usb_device_id products[] = { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 57), .driver_info = (unsigned long)&qmi_wwan_info, }, + { /* HUAWEI_INTERFACE_NDIS_CONTROL_QUALCOMM */ + USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 0x01, 0x69), + .driver_info = (unsigned long)&qmi_wwan_info, + }, /* 2. Combined interface devices matching on class+protocol */ { /* Huawei E367 and possibly others in "Windows mode" */ @@ -361,6 +365,14 @@ static const struct usb_device_id products[] = { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 17), .driver_info = (unsigned long)&qmi_wwan_info, }, + { /* HUAWEI_NDIS_SINGLE_INTERFACE_VDF */ + USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 0x01, 0x37), + .driver_info = (unsigned long)&qmi_wwan_info, + }, + { /* HUAWEI_INTERFACE_NDIS_HW_QUALCOMM */ + USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 0x01, 0x67), + .driver_info = (unsigned long)&qmi_wwan_info, + }, { /* Pantech UML290, P4200 and more */ USB_VENDOR_AND_INTERFACE_INFO(0x106c, USB_CLASS_VENDOR_SPEC, 0xf0, 0xff), .driver_info = (unsigned long)&qmi_wwan_info, @@ -461,6 +473,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x1199, 0x901c, 8)}, /* Sierra Wireless EM7700 */ {QMI_FIXED_INTF(0x1bbb, 0x011e, 4)}, /* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */ {QMI_FIXED_INTF(0x2357, 0x0201, 4)}, /* TP-LINK HSUPA Modem MA180 */ + {QMI_FIXED_INTF(0x1bc7, 0x1200, 5)}, /* Telit LE920 */ /* 4. Gobi 1000 devices */ {QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */ diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index f34b2ebee815..5e33606c1366 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -380,6 +380,12 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags) unsigned long lockflags; size_t size = dev->rx_urb_size; + /* prevent rx skb allocation when error ratio is high */ + if (test_bit(EVENT_RX_KILL, &dev->flags)) { + usb_free_urb(urb); + return -ENOLINK; + } + skb = __netdev_alloc_skb_ip_align(dev->net, size, flags); if (!skb) { netif_dbg(dev, rx_err, dev->net, "no rx skb\n"); @@ -539,6 +545,17 @@ block: break; } + /* stop rx if packet error rate is high */ + if (++dev->pkt_cnt > 30) { + dev->pkt_cnt = 0; + dev->pkt_err = 0; + } else { + if (state == rx_cleanup) + dev->pkt_err++; + if (dev->pkt_err > 20) + set_bit(EVENT_RX_KILL, &dev->flags); + } + state = defer_bh(dev, skb, &dev->rxq, state); if (urb) { @@ -791,6 +808,11 @@ int usbnet_open (struct net_device *net) (dev->driver_info->flags & FLAG_FRAMING_AX) ? "ASIX" : "simple"); + /* reset rx error state */ + dev->pkt_cnt = 0; + dev->pkt_err = 0; + clear_bit(EVENT_RX_KILL, &dev->flags); + // delay posting reads until we're fully open tasklet_schedule (&dev->bh); if (info->manage_power) { @@ -1103,13 +1125,11 @@ netdev_tx_t usbnet_start_xmit (struct sk_buff *skb, if (info->tx_fixup) { skb = info->tx_fixup (dev, skb, GFP_ATOMIC); if (!skb) { - if (netif_msg_tx_err(dev)) { - netif_dbg(dev, tx_err, dev->net, "can't tx_fixup skb\n"); - goto drop; - } else { - /* cdc_ncm collected packet; waits for more */ + /* packet collected; minidriver waiting for more */ + if (info->flags & FLAG_MULTI_PACKET) goto not_drop; - } + netif_dbg(dev, tx_err, dev->net, "can't tx_fixup skb\n"); + goto drop; } } length = skb->len; @@ -1254,6 +1274,9 @@ static void usbnet_bh (unsigned long param) } } + /* restart RX again after disabling due to high error rate */ + clear_bit(EVENT_RX_KILL, &dev->flags); + // waiting for all pending urbs to complete? if (dev->wait) { if ((dev->txq.qlen + dev->rxq.qlen + dev->done.qlen) == 0) { diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c index dc8913c6238c..12c6440d1649 100644 --- a/drivers/net/vmxnet3/vmxnet3_drv.c +++ b/drivers/net/vmxnet3/vmxnet3_drv.c @@ -154,8 +154,7 @@ vmxnet3_check_link(struct vmxnet3_adapter *adapter, bool affectTxQueue) if (ret & 1) { /* Link is up. */ printk(KERN_INFO "%s: NIC Link is Up %d Mbps\n", adapter->netdev->name, adapter->link_speed); - if (!netif_carrier_ok(adapter->netdev)) - netif_carrier_on(adapter->netdev); + netif_carrier_on(adapter->netdev); if (affectTxQueue) { for (i = 0; i < adapter->num_tx_queues; i++) @@ -165,8 +164,7 @@ vmxnet3_check_link(struct vmxnet3_adapter *adapter, bool affectTxQueue) } else { printk(KERN_INFO "%s: NIC Link is Down\n", adapter->netdev->name); - if (netif_carrier_ok(adapter->netdev)) - netif_carrier_off(adapter->netdev); + netif_carrier_off(adapter->netdev); if (affectTxQueue) { for (i = 0; i < adapter->num_tx_queues; i++) @@ -3061,6 +3059,7 @@ vmxnet3_probe_device(struct pci_dev *pdev, netif_set_real_num_tx_queues(adapter->netdev, adapter->num_tx_queues); netif_set_real_num_rx_queues(adapter->netdev, adapter->num_rx_queues); + netif_carrier_off(netdev); err = register_netdev(netdev); if (err) { diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index 0f71d1d4339d..e5fd20994bec 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -36,6 +36,7 @@ #include "debug.h" #define N_TX_QUEUES 4 /* #tx queues on mac80211<->driver interface */ +#define BRCMS_FLUSH_TIMEOUT 500 /* msec */ /* Flags we support */ #define MAC_FILTERS (FIF_PROMISC_IN_BSS | \ @@ -708,16 +709,29 @@ static void brcms_ops_rfkill_poll(struct ieee80211_hw *hw) wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked); } +static bool brcms_tx_flush_completed(struct brcms_info *wl) +{ + bool result; + + spin_lock_bh(&wl->lock); + result = brcms_c_tx_flush_completed(wl->wlc); + spin_unlock_bh(&wl->lock); + return result; +} + static void brcms_ops_flush(struct ieee80211_hw *hw, bool drop) { struct brcms_info *wl = hw->priv; + int ret; no_printk("%s: drop = %s\n", __func__, drop ? "true" : "false"); - /* wait for packet queue and dma fifos to run empty */ - spin_lock_bh(&wl->lock); - brcms_c_wait_for_tx_completion(wl->wlc, drop); - spin_unlock_bh(&wl->lock); + ret = wait_event_timeout(wl->tx_flush_wq, + brcms_tx_flush_completed(wl), + msecs_to_jiffies(BRCMS_FLUSH_TIMEOUT)); + + brcms_dbg_mac80211(wl->wlc->hw->d11core, + "ret=%d\n", jiffies_to_msecs(ret)); } static const struct ieee80211_ops brcms_ops = { @@ -772,6 +786,7 @@ void brcms_dpc(unsigned long data) done: spin_unlock_bh(&wl->lock); + wake_up(&wl->tx_flush_wq); } /* @@ -1020,6 +1035,8 @@ static struct brcms_info *brcms_attach(struct bcma_device *pdev) atomic_set(&wl->callbacks, 0); + init_waitqueue_head(&wl->tx_flush_wq); + /* setup the bottom half handler */ tasklet_init(&wl->tasklet, brcms_dpc, (unsigned long) wl); @@ -1609,13 +1626,3 @@ bool brcms_rfkill_set_hw_state(struct brcms_info *wl) spin_lock_bh(&wl->lock); return blocked; } - -/* - * precondition: perimeter lock has been acquired - */ -void brcms_msleep(struct brcms_info *wl, uint ms) -{ - spin_unlock_bh(&wl->lock); - msleep(ms); - spin_lock_bh(&wl->lock); -} diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h index 9358bd5ebd35..947ccacf43e6 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h @@ -68,6 +68,8 @@ struct brcms_info { spinlock_t lock; /* per-device perimeter lock */ spinlock_t isr_lock; /* per-device ISR synchronization lock */ + /* tx flush */ + wait_queue_head_t tx_flush_wq; /* timer related fields */ atomic_t callbacks; /* # outstanding callback functions */ @@ -100,7 +102,6 @@ extern struct brcms_timer *brcms_init_timer(struct brcms_info *wl, extern void brcms_free_timer(struct brcms_timer *timer); extern void brcms_add_timer(struct brcms_timer *timer, uint ms, int periodic); extern bool brcms_del_timer(struct brcms_timer *timer); -extern void brcms_msleep(struct brcms_info *wl, uint ms); extern void brcms_dpc(unsigned long data); extern void brcms_timer(struct brcms_timer *t); extern void brcms_fatal_error(struct brcms_info *wl); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 17594de4199e..8b5839008af3 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -1027,7 +1027,6 @@ brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) static bool brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal) { - bool morepending = false; struct bcma_device *core; struct tx_status txstatus, *txs; u32 s1, s2; @@ -1041,23 +1040,20 @@ brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal) txs = &txstatus; core = wlc_hw->d11core; *fatal = false; - s1 = bcma_read32(core, D11REGOFFS(frmtxstatus)); - while (!(*fatal) - && (s1 & TXS_V)) { - /* !give others some time to run! */ - if (n >= max_tx_num) { - morepending = true; - break; - } + while (n < max_tx_num) { + s1 = bcma_read32(core, D11REGOFFS(frmtxstatus)); if (s1 == 0xffffffff) { brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, __func__); *fatal = true; return false; } - s2 = bcma_read32(core, D11REGOFFS(frmtxstatus2)); + /* only process when valid */ + if (!(s1 & TXS_V)) + break; + s2 = bcma_read32(core, D11REGOFFS(frmtxstatus2)); txs->status = s1 & TXS_STATUS_MASK; txs->frameid = (s1 & TXS_FID_MASK) >> TXS_FID_SHIFT; txs->sequence = s2 & TXS_SEQ_MASK; @@ -1065,15 +1061,12 @@ brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal) txs->lasttxtime = 0; *fatal = brcms_c_dotxstatus(wlc_hw->wlc, txs); - - s1 = bcma_read32(core, D11REGOFFS(frmtxstatus)); + if (*fatal == true) + return false; n++; } - if (*fatal) - return false; - - return morepending; + return n >= max_tx_num; } static void brcms_c_tbtt(struct brcms_c_info *wlc) @@ -7518,25 +7511,16 @@ int brcms_c_get_curband(struct brcms_c_info *wlc) return wlc->band->bandunit; } -void brcms_c_wait_for_tx_completion(struct brcms_c_info *wlc, bool drop) +bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc) { - int timeout = 20; int i; /* Kick DMA to send any pending AMPDU */ for (i = 0; i < ARRAY_SIZE(wlc->hw->di); i++) if (wlc->hw->di[i]) - dma_txflush(wlc->hw->di[i]); - - /* wait for queue and DMA fifos to run dry */ - while (brcms_txpktpendtot(wlc) > 0) { - brcms_msleep(wlc->wl, 1); - - if (--timeout == 0) - break; - } + dma_kick_tx(wlc->hw->di[i]); - WARN_ON_ONCE(timeout == 0); + return !brcms_txpktpendtot(wlc); } void brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval) diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pub.h b/drivers/net/wireless/brcm80211/brcmsmac/pub.h index 4fb2834f4e64..b0f14b7b8616 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/pub.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/pub.h @@ -314,8 +314,6 @@ extern void brcms_c_associate_upd(struct brcms_c_info *wlc, bool state); extern void brcms_c_scan_start(struct brcms_c_info *wlc); extern void brcms_c_scan_stop(struct brcms_c_info *wlc); extern int brcms_c_get_curband(struct brcms_c_info *wlc); -extern void brcms_c_wait_for_tx_completion(struct brcms_c_info *wlc, - bool drop); extern int brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel); extern int brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl); extern void brcms_c_get_current_rateset(struct brcms_c_info *wlc, @@ -332,5 +330,6 @@ extern int brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr); extern int brcms_c_get_tx_power(struct brcms_c_info *wlc); extern bool brcms_c_check_radio_disabled(struct brcms_c_info *wlc); extern void brcms_c_mute(struct brcms_c_info *wlc, bool on); +extern bool brcms_c_tx_flush_completed(struct brcms_c_info *wlc); #endif /* _BRCM_PUB_H_ */ diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index 31534f7c0548..279796419ea0 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -1153,6 +1153,13 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, next_reclaimed = ssn; } + if (tid != IWL_TID_NON_QOS) { + priv->tid_data[sta_id][tid].next_reclaimed = + next_reclaimed; + IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", + next_reclaimed); + } + iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs); iwlagn_check_ratid_empty(priv, sta_id, tid); @@ -1203,28 +1210,11 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, if (!is_agg) iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); - /* - * W/A for FW bug - the seq_ctl isn't updated when the - * queues are flushed. Fetch it from the packet itself - */ - if (!is_agg && status == TX_STATUS_FAIL_FIFO_FLUSHED) { - next_reclaimed = le16_to_cpu(hdr->seq_ctrl); - next_reclaimed = - SEQ_TO_SN(next_reclaimed + 0x10); - } - is_offchannel_skb = (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN); freed++; } - if (tid != IWL_TID_NON_QOS) { - priv->tid_data[sta_id][tid].next_reclaimed = - next_reclaimed; - IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", - next_reclaimed); - } - WARN_ON(!is_agg && freed != 1); /* diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c index 9189a32b7844..973a9d90e9ea 100644 --- a/drivers/net/wireless/mwifiex/scan.c +++ b/drivers/net/wireless/mwifiex/scan.c @@ -1563,7 +1563,7 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, dev_err(adapter->dev, "SCAN_RESP: too many AP returned (%d)\n", scan_rsp->number_of_sets); ret = -1; - goto done; + goto check_next_scan; } bytes_left = le16_to_cpu(scan_rsp->bss_descript_size); @@ -1634,7 +1634,8 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, if (!beacon_size || beacon_size > bytes_left) { bss_info += bytes_left; bytes_left = 0; - return -1; + ret = -1; + goto check_next_scan; } /* Initialize the current working beacon pointer for this BSS @@ -1690,7 +1691,7 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, dev_err(priv->adapter->dev, "%s: bytes left < IE length\n", __func__); - goto done; + goto check_next_scan; } if (element_id == WLAN_EID_DS_PARAMS) { channel = *(current_ptr + sizeof(struct ieee_types_header)); @@ -1753,6 +1754,7 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, } } +check_next_scan: spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); if (list_empty(&adapter->scan_pending_q)) { spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); @@ -1813,7 +1815,6 @@ int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, } } -done: return ret; } diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 4494d130b37c..0f8b05185eda 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -1004,7 +1004,8 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) is_tx ? "Tx" : "Rx"); if (is_tx) { - rtl_lps_leave(hw); + schedule_work(&rtlpriv-> + works.lps_leave_work); ppsc->last_delaylps_stamp_jiffies = jiffies; } @@ -1014,7 +1015,7 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) } } else if (ETH_P_ARP == ether_type) { if (is_tx) { - rtl_lps_leave(hw); + schedule_work(&rtlpriv->works.lps_leave_work); ppsc->last_delaylps_stamp_jiffies = jiffies; } @@ -1024,7 +1025,7 @@ u8 rtl_is_special_data(struct ieee80211_hw *hw, struct sk_buff *skb, u8 is_tx) "802.1X %s EAPOL pkt!!\n", is_tx ? "Tx" : "Rx"); if (is_tx) { - rtl_lps_leave(hw); + schedule_work(&rtlpriv->works.lps_leave_work); ppsc->last_delaylps_stamp_jiffies = jiffies; } diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c index f2ecdeb3a90d..1535efda3d52 100644 --- a/drivers/net/wireless/rtlwifi/usb.c +++ b/drivers/net/wireless/rtlwifi/usb.c @@ -542,8 +542,8 @@ static void _rtl_rx_pre_process(struct ieee80211_hw *hw, struct sk_buff *skb) WARN_ON(skb_queue_empty(&rx_queue)); while (!skb_queue_empty(&rx_queue)) { _skb = skb_dequeue(&rx_queue); - _rtl_usb_rx_process_agg(hw, skb); - ieee80211_rx_irqsafe(hw, skb); + _rtl_usb_rx_process_agg(hw, _skb); + ieee80211_rx_irqsafe(hw, _skb); } } diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h index 94b79c3338c4..9d7f1723dd8f 100644 --- a/drivers/net/xen-netback/common.h +++ b/drivers/net/xen-netback/common.h @@ -151,6 +151,9 @@ void xen_netbk_queue_tx_skb(struct xenvif *vif, struct sk_buff *skb); /* Notify xenvif that ring now has space to send an skb to the frontend */ void xenvif_notify_tx_completion(struct xenvif *vif); +/* Prevent the device from generating any further traffic. */ +void xenvif_carrier_off(struct xenvif *vif); + /* Returns number of ring slots required to send an skb to the frontend */ unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb); diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index b7d41f8c338a..b8c5193bd420 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -343,17 +343,22 @@ err: return err; } -void xenvif_disconnect(struct xenvif *vif) +void xenvif_carrier_off(struct xenvif *vif) { struct net_device *dev = vif->dev; - if (netif_carrier_ok(dev)) { - rtnl_lock(); - netif_carrier_off(dev); /* discard queued packets */ - if (netif_running(dev)) - xenvif_down(vif); - rtnl_unlock(); - xenvif_put(vif); - } + + rtnl_lock(); + netif_carrier_off(dev); /* discard queued packets */ + if (netif_running(dev)) + xenvif_down(vif); + rtnl_unlock(); + xenvif_put(vif); +} + +void xenvif_disconnect(struct xenvif *vif) +{ + if (netif_carrier_ok(vif->dev)) + xenvif_carrier_off(vif); atomic_dec(&vif->refcnt); wait_event(vif->waiting_to_free, atomic_read(&vif->refcnt) == 0); diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index f2d6b78d901d..2b9520c46e97 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -147,7 +147,8 @@ void xen_netbk_remove_xenvif(struct xenvif *vif) atomic_dec(&netbk->netfront_count); } -static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx); +static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx, + u8 status); static void make_tx_response(struct xenvif *vif, struct xen_netif_tx_request *txp, s8 st); @@ -879,7 +880,7 @@ static void netbk_tx_err(struct xenvif *vif, do { make_tx_response(vif, txp, XEN_NETIF_RSP_ERROR); - if (cons >= end) + if (cons == end) break; txp = RING_GET_REQUEST(&vif->tx, cons++); } while (1); @@ -888,6 +889,13 @@ static void netbk_tx_err(struct xenvif *vif, xenvif_put(vif); } +static void netbk_fatal_tx_err(struct xenvif *vif) +{ + netdev_err(vif->dev, "fatal error; disabling device\n"); + xenvif_carrier_off(vif); + xenvif_put(vif); +} + static int netbk_count_requests(struct xenvif *vif, struct xen_netif_tx_request *first, struct xen_netif_tx_request *txp, @@ -901,19 +909,22 @@ static int netbk_count_requests(struct xenvif *vif, do { if (frags >= work_to_do) { - netdev_dbg(vif->dev, "Need more frags\n"); + netdev_err(vif->dev, "Need more frags\n"); + netbk_fatal_tx_err(vif); return -frags; } if (unlikely(frags >= MAX_SKB_FRAGS)) { - netdev_dbg(vif->dev, "Too many frags\n"); + netdev_err(vif->dev, "Too many frags\n"); + netbk_fatal_tx_err(vif); return -frags; } memcpy(txp, RING_GET_REQUEST(&vif->tx, cons + frags), sizeof(*txp)); if (txp->size > first->size) { - netdev_dbg(vif->dev, "Frags galore\n"); + netdev_err(vif->dev, "Frag is bigger than frame.\n"); + netbk_fatal_tx_err(vif); return -frags; } @@ -921,8 +932,9 @@ static int netbk_count_requests(struct xenvif *vif, frags++; if (unlikely((txp->offset + txp->size) > PAGE_SIZE)) { - netdev_dbg(vif->dev, "txp->offset: %x, size: %u\n", + netdev_err(vif->dev, "txp->offset: %x, size: %u\n", txp->offset, txp->size); + netbk_fatal_tx_err(vif); return -frags; } } while ((txp++)->flags & XEN_NETTXF_more_data); @@ -966,7 +978,7 @@ static struct gnttab_copy *xen_netbk_get_requests(struct xen_netbk *netbk, pending_idx = netbk->pending_ring[index]; page = xen_netbk_alloc_page(netbk, skb, pending_idx); if (!page) - return NULL; + goto err; gop->source.u.ref = txp->gref; gop->source.domid = vif->domid; @@ -988,6 +1000,17 @@ static struct gnttab_copy *xen_netbk_get_requests(struct xen_netbk *netbk, } return gop; +err: + /* Unwind, freeing all pages and sending error responses. */ + while (i-- > start) { + xen_netbk_idx_release(netbk, frag_get_pending_idx(&frags[i]), + XEN_NETIF_RSP_ERROR); + } + /* The head too, if necessary. */ + if (start) + xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_ERROR); + + return NULL; } static int xen_netbk_tx_check_gop(struct xen_netbk *netbk, @@ -996,30 +1019,20 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk, { struct gnttab_copy *gop = *gopp; u16 pending_idx = *((u16 *)skb->data); - struct pending_tx_info *pending_tx_info = netbk->pending_tx_info; - struct xenvif *vif = pending_tx_info[pending_idx].vif; - struct xen_netif_tx_request *txp; struct skb_shared_info *shinfo = skb_shinfo(skb); int nr_frags = shinfo->nr_frags; int i, err, start; /* Check status of header. */ err = gop->status; - if (unlikely(err)) { - pending_ring_idx_t index; - index = pending_index(netbk->pending_prod++); - txp = &pending_tx_info[pending_idx].req; - make_tx_response(vif, txp, XEN_NETIF_RSP_ERROR); - netbk->pending_ring[index] = pending_idx; - xenvif_put(vif); - } + if (unlikely(err)) + xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_ERROR); /* Skip first skb fragment if it is on same page as header fragment. */ start = (frag_get_pending_idx(&shinfo->frags[0]) == pending_idx); for (i = start; i < nr_frags; i++) { int j, newerr; - pending_ring_idx_t index; pending_idx = frag_get_pending_idx(&shinfo->frags[i]); @@ -1028,16 +1041,12 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk, if (likely(!newerr)) { /* Had a previous error? Invalidate this fragment. */ if (unlikely(err)) - xen_netbk_idx_release(netbk, pending_idx); + xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY); continue; } /* Error on this fragment: respond to client with an error. */ - txp = &netbk->pending_tx_info[pending_idx].req; - make_tx_response(vif, txp, XEN_NETIF_RSP_ERROR); - index = pending_index(netbk->pending_prod++); - netbk->pending_ring[index] = pending_idx; - xenvif_put(vif); + xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_ERROR); /* Not the first error? Preceding frags already invalidated. */ if (err) @@ -1045,10 +1054,10 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk, /* First error: invalidate header and preceding fragments. */ pending_idx = *((u16 *)skb->data); - xen_netbk_idx_release(netbk, pending_idx); + xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY); for (j = start; j < i; j++) { pending_idx = frag_get_pending_idx(&shinfo->frags[j]); - xen_netbk_idx_release(netbk, pending_idx); + xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY); } /* Remember the error: invalidate all subsequent fragments. */ @@ -1082,7 +1091,7 @@ static void xen_netbk_fill_frags(struct xen_netbk *netbk, struct sk_buff *skb) /* Take an extra reference to offset xen_netbk_idx_release */ get_page(netbk->mmap_pages[pending_idx]); - xen_netbk_idx_release(netbk, pending_idx); + xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY); } } @@ -1095,7 +1104,8 @@ static int xen_netbk_get_extras(struct xenvif *vif, do { if (unlikely(work_to_do-- <= 0)) { - netdev_dbg(vif->dev, "Missing extra info\n"); + netdev_err(vif->dev, "Missing extra info\n"); + netbk_fatal_tx_err(vif); return -EBADR; } @@ -1104,8 +1114,9 @@ static int xen_netbk_get_extras(struct xenvif *vif, if (unlikely(!extra.type || extra.type >= XEN_NETIF_EXTRA_TYPE_MAX)) { vif->tx.req_cons = ++cons; - netdev_dbg(vif->dev, + netdev_err(vif->dev, "Invalid extra type: %d\n", extra.type); + netbk_fatal_tx_err(vif); return -EINVAL; } @@ -1121,13 +1132,15 @@ static int netbk_set_skb_gso(struct xenvif *vif, struct xen_netif_extra_info *gso) { if (!gso->u.gso.size) { - netdev_dbg(vif->dev, "GSO size must not be zero.\n"); + netdev_err(vif->dev, "GSO size must not be zero.\n"); + netbk_fatal_tx_err(vif); return -EINVAL; } /* Currently only TCPv4 S.O. is supported. */ if (gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV4) { - netdev_dbg(vif->dev, "Bad GSO type %d.\n", gso->u.gso.type); + netdev_err(vif->dev, "Bad GSO type %d.\n", gso->u.gso.type); + netbk_fatal_tx_err(vif); return -EINVAL; } @@ -1264,9 +1277,25 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk) /* Get a netif from the list with work to do. */ vif = poll_net_schedule_list(netbk); + /* This can sometimes happen because the test of + * list_empty(net_schedule_list) at the top of the + * loop is unlocked. Just go back and have another + * look. + */ if (!vif) continue; + if (vif->tx.sring->req_prod - vif->tx.req_cons > + XEN_NETIF_TX_RING_SIZE) { + netdev_err(vif->dev, + "Impossible number of requests. " + "req_prod %d, req_cons %d, size %ld\n", + vif->tx.sring->req_prod, vif->tx.req_cons, + XEN_NETIF_TX_RING_SIZE); + netbk_fatal_tx_err(vif); + continue; + } + RING_FINAL_CHECK_FOR_REQUESTS(&vif->tx, work_to_do); if (!work_to_do) { xenvif_put(vif); @@ -1294,17 +1323,14 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk) work_to_do = xen_netbk_get_extras(vif, extras, work_to_do); idx = vif->tx.req_cons; - if (unlikely(work_to_do < 0)) { - netbk_tx_err(vif, &txreq, idx); + if (unlikely(work_to_do < 0)) continue; - } } ret = netbk_count_requests(vif, &txreq, txfrags, work_to_do); - if (unlikely(ret < 0)) { - netbk_tx_err(vif, &txreq, idx - ret); + if (unlikely(ret < 0)) continue; - } + idx += ret; if (unlikely(txreq.size < ETH_HLEN)) { @@ -1316,11 +1342,11 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk) /* No crossing a page as the payload mustn't fragment. */ if (unlikely((txreq.offset + txreq.size) > PAGE_SIZE)) { - netdev_dbg(vif->dev, + netdev_err(vif->dev, "txreq.offset: %x, size: %u, end: %lu\n", txreq.offset, txreq.size, (txreq.offset&~PAGE_MASK) + txreq.size); - netbk_tx_err(vif, &txreq, idx); + netbk_fatal_tx_err(vif); continue; } @@ -1348,8 +1374,8 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk) gso = &extras[XEN_NETIF_EXTRA_TYPE_GSO - 1]; if (netbk_set_skb_gso(vif, skb, gso)) { + /* Failure in netbk_set_skb_gso is fatal. */ kfree_skb(skb); - netbk_tx_err(vif, &txreq, idx); continue; } } @@ -1448,7 +1474,7 @@ static void xen_netbk_tx_submit(struct xen_netbk *netbk) txp->size -= data_len; } else { /* Schedule a response immediately. */ - xen_netbk_idx_release(netbk, pending_idx); + xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY); } if (txp->flags & XEN_NETTXF_csum_blank) @@ -1500,7 +1526,8 @@ static void xen_netbk_tx_action(struct xen_netbk *netbk) xen_netbk_tx_submit(netbk); } -static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx) +static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx, + u8 status) { struct xenvif *vif; struct pending_tx_info *pending_tx_info; @@ -1514,7 +1541,7 @@ static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx) vif = pending_tx_info->vif; - make_tx_response(vif, &pending_tx_info->req, XEN_NETIF_RSP_OKAY); + make_tx_response(vif, &pending_tx_info->req, status); index = pending_index(netbk->pending_prod++); netbk->pending_ring[index] = pending_idx; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index efaecefe3f8c..a5f3c8ca480e 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -184,8 +184,8 @@ config PINCTRL_SAMSUNG select PINMUX select PINCONF -config PINCTRL_EXYNOS4 - bool "Pinctrl driver data for Exynos4 SoC" +config PINCTRL_EXYNOS + bool "Pinctrl driver data for Samsung EXYNOS SoCs" depends on OF && GPIOLIB select PINCTRL_SAMSUNG diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index fc4606f27dc7..6e87e52eab5d 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -36,7 +36,7 @@ obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o obj-$(CONFIG_PINCTRL_U300) += pinctrl-u300.o obj-$(CONFIG_PINCTRL_COH901) += pinctrl-coh901.o obj-$(CONFIG_PINCTRL_SAMSUNG) += pinctrl-samsung.o -obj-$(CONFIG_PINCTRL_EXYNOS4) += pinctrl-exynos.o +obj-$(CONFIG_PINCTRL_EXYNOS) += pinctrl-exynos.o obj-$(CONFIG_PINCTRL_EXYNOS5440) += pinctrl-exynos5440.o obj-$(CONFIG_PINCTRL_XWAY) += pinctrl-xway.o obj-$(CONFIG_PINCTRL_LANTIQ) += pinctrl-lantiq.o diff --git a/drivers/pinctrl/pinctrl-sirf.c b/drivers/pinctrl/pinctrl-sirf.c index 498b2ba905de..d02498b30c6e 100644 --- a/drivers/pinctrl/pinctrl-sirf.c +++ b/drivers/pinctrl/pinctrl-sirf.c @@ -1246,6 +1246,22 @@ static void __iomem *sirfsoc_rsc_of_iomap(void) return of_iomap(np, 0); } +static int sirfsoc_gpio_of_xlate(struct gpio_chip *gc, + const struct of_phandle_args *gpiospec, + u32 *flags) +{ + if (gpiospec->args[0] > SIRFSOC_GPIO_NO_OF_BANKS * SIRFSOC_GPIO_BANK_SIZE) + return -EINVAL; + + if (gc != &sgpio_bank[gpiospec->args[0] / SIRFSOC_GPIO_BANK_SIZE].chip.gc) + return -EINVAL; + + if (flags) + *flags = gpiospec->args[1]; + + return gpiospec->args[0] % SIRFSOC_GPIO_BANK_SIZE; +} + static int sirfsoc_pinmux_probe(struct platform_device *pdev) { int ret; @@ -1736,6 +1752,8 @@ static int sirfsoc_gpio_probe(struct device_node *np) bank->chip.gc.ngpio = SIRFSOC_GPIO_BANK_SIZE; bank->chip.gc.label = kstrdup(np->full_name, GFP_KERNEL); bank->chip.gc.of_node = np; + bank->chip.gc.of_xlate = sirfsoc_gpio_of_xlate; + bank->chip.gc.of_gpio_n_cells = 2; bank->chip.regs = regs; bank->id = i; bank->is_marco = is_marco; diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c index b85040caaea3..cca18a3c0294 100644 --- a/drivers/regulator/max77686.c +++ b/drivers/regulator/max77686.c @@ -379,9 +379,10 @@ static struct regulator_desc regulators[] = { }; #ifdef CONFIG_OF -static int max77686_pmic_dt_parse_pdata(struct max77686_dev *iodev, +static int max77686_pmic_dt_parse_pdata(struct platform_device *pdev, struct max77686_platform_data *pdata) { + struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct device_node *pmic_np, *regulators_np; struct max77686_regulator_data *rdata; struct of_regulator_match rmatch; @@ -390,15 +391,15 @@ static int max77686_pmic_dt_parse_pdata(struct max77686_dev *iodev, pmic_np = iodev->dev->of_node; regulators_np = of_find_node_by_name(pmic_np, "voltage-regulators"); if (!regulators_np) { - dev_err(iodev->dev, "could not find regulators sub-node\n"); + dev_err(&pdev->dev, "could not find regulators sub-node\n"); return -EINVAL; } pdata->num_regulators = ARRAY_SIZE(regulators); - rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) * + rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) * pdata->num_regulators, GFP_KERNEL); if (!rdata) { - dev_err(iodev->dev, + dev_err(&pdev->dev, "could not allocate memory for regulator data\n"); return -ENOMEM; } @@ -407,7 +408,7 @@ static int max77686_pmic_dt_parse_pdata(struct max77686_dev *iodev, rmatch.name = regulators[i].name; rmatch.init_data = NULL; rmatch.of_node = NULL; - of_regulator_match(iodev->dev, regulators_np, &rmatch, 1); + of_regulator_match(&pdev->dev, regulators_np, &rmatch, 1); rdata[i].initdata = rmatch.init_data; rdata[i].of_node = rmatch.of_node; } @@ -417,7 +418,7 @@ static int max77686_pmic_dt_parse_pdata(struct max77686_dev *iodev, return 0; } #else -static int max77686_pmic_dt_parse_pdata(struct max77686_dev *iodev, +static int max77686_pmic_dt_parse_pdata(struct platform_device *pdev, struct max77686_platform_data *pdata) { return 0; @@ -440,7 +441,7 @@ static int max77686_pmic_probe(struct platform_device *pdev) } if (iodev->dev->of_node) { - ret = max77686_pmic_dt_parse_pdata(iodev, pdata); + ret = max77686_pmic_dt_parse_pdata(pdev, pdata); if (ret) return ret; } diff --git a/drivers/regulator/max8907-regulator.c b/drivers/regulator/max8907-regulator.c index d1a77512d83e..d40cf7fdb546 100644 --- a/drivers/regulator/max8907-regulator.c +++ b/drivers/regulator/max8907-regulator.c @@ -237,8 +237,7 @@ static int max8907_regulator_parse_dt(struct platform_device *pdev) return -EINVAL; } - ret = of_regulator_match(pdev->dev.parent, regulators, - max8907_matches, + ret = of_regulator_match(&pdev->dev, regulators, max8907_matches, ARRAY_SIZE(max8907_matches)); if (ret < 0) { dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", diff --git a/drivers/regulator/max8997.c b/drivers/regulator/max8997.c index 02be7fcae32f..836908ce505e 100644 --- a/drivers/regulator/max8997.c +++ b/drivers/regulator/max8997.c @@ -934,7 +934,7 @@ static struct regulator_desc regulators[] = { }; #ifdef CONFIG_OF -static int max8997_pmic_dt_parse_dvs_gpio(struct max8997_dev *iodev, +static int max8997_pmic_dt_parse_dvs_gpio(struct platform_device *pdev, struct max8997_platform_data *pdata, struct device_node *pmic_np) { @@ -944,7 +944,7 @@ static int max8997_pmic_dt_parse_dvs_gpio(struct max8997_dev *iodev, gpio = of_get_named_gpio(pmic_np, "max8997,pmic-buck125-dvs-gpios", i); if (!gpio_is_valid(gpio)) { - dev_err(iodev->dev, "invalid gpio[%d]: %d\n", i, gpio); + dev_err(&pdev->dev, "invalid gpio[%d]: %d\n", i, gpio); return -EINVAL; } pdata->buck125_gpios[i] = gpio; @@ -952,22 +952,23 @@ static int max8997_pmic_dt_parse_dvs_gpio(struct max8997_dev *iodev, return 0; } -static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev, +static int max8997_pmic_dt_parse_pdata(struct platform_device *pdev, struct max8997_platform_data *pdata) { + struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct device_node *pmic_np, *regulators_np, *reg_np; struct max8997_regulator_data *rdata; unsigned int i, dvs_voltage_nr = 1, ret; pmic_np = iodev->dev->of_node; if (!pmic_np) { - dev_err(iodev->dev, "could not find pmic sub-node\n"); + dev_err(&pdev->dev, "could not find pmic sub-node\n"); return -ENODEV; } regulators_np = of_find_node_by_name(pmic_np, "regulators"); if (!regulators_np) { - dev_err(iodev->dev, "could not find regulators sub-node\n"); + dev_err(&pdev->dev, "could not find regulators sub-node\n"); return -EINVAL; } @@ -976,11 +977,10 @@ static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev, for_each_child_of_node(regulators_np, reg_np) pdata->num_regulators++; - rdata = devm_kzalloc(iodev->dev, sizeof(*rdata) * + rdata = devm_kzalloc(&pdev->dev, sizeof(*rdata) * pdata->num_regulators, GFP_KERNEL); if (!rdata) { - dev_err(iodev->dev, "could not allocate memory for " - "regulator data\n"); + dev_err(&pdev->dev, "could not allocate memory for regulator data\n"); return -ENOMEM; } @@ -991,14 +991,14 @@ static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev, break; if (i == ARRAY_SIZE(regulators)) { - dev_warn(iodev->dev, "don't know how to configure " - "regulator %s\n", reg_np->name); + dev_warn(&pdev->dev, "don't know how to configure regulator %s\n", + reg_np->name); continue; } rdata->id = i; - rdata->initdata = of_get_regulator_init_data( - iodev->dev, reg_np); + rdata->initdata = of_get_regulator_init_data(&pdev->dev, + reg_np); rdata->reg_node = reg_np; rdata++; } @@ -1014,7 +1014,7 @@ static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev, if (pdata->buck1_gpiodvs || pdata->buck2_gpiodvs || pdata->buck5_gpiodvs) { - ret = max8997_pmic_dt_parse_dvs_gpio(iodev, pdata, pmic_np); + ret = max8997_pmic_dt_parse_dvs_gpio(pdev, pdata, pmic_np); if (ret) return -EINVAL; @@ -1025,8 +1025,7 @@ static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev, } else { if (pdata->buck125_default_idx >= 8) { pdata->buck125_default_idx = 0; - dev_info(iodev->dev, "invalid value for " - "default dvs index, using 0 instead\n"); + dev_info(&pdev->dev, "invalid value for default dvs index, using 0 instead\n"); } } @@ -1040,28 +1039,28 @@ static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev, if (of_property_read_u32_array(pmic_np, "max8997,pmic-buck1-dvs-voltage", pdata->buck1_voltage, dvs_voltage_nr)) { - dev_err(iodev->dev, "buck1 voltages not specified\n"); + dev_err(&pdev->dev, "buck1 voltages not specified\n"); return -EINVAL; } if (of_property_read_u32_array(pmic_np, "max8997,pmic-buck2-dvs-voltage", pdata->buck2_voltage, dvs_voltage_nr)) { - dev_err(iodev->dev, "buck2 voltages not specified\n"); + dev_err(&pdev->dev, "buck2 voltages not specified\n"); return -EINVAL; } if (of_property_read_u32_array(pmic_np, "max8997,pmic-buck5-dvs-voltage", pdata->buck5_voltage, dvs_voltage_nr)) { - dev_err(iodev->dev, "buck5 voltages not specified\n"); + dev_err(&pdev->dev, "buck5 voltages not specified\n"); return -EINVAL; } return 0; } #else -static int max8997_pmic_dt_parse_pdata(struct max8997_dev *iodev, +static int max8997_pmic_dt_parse_pdata(struct platform_device *pdev, struct max8997_platform_data *pdata) { return 0; @@ -1085,7 +1084,7 @@ static int max8997_pmic_probe(struct platform_device *pdev) } if (iodev->dev->of_node) { - ret = max8997_pmic_dt_parse_pdata(iodev, pdata); + ret = max8997_pmic_dt_parse_pdata(pdev, pdata); if (ret) return ret; } diff --git a/drivers/regulator/max8998.c b/drivers/regulator/max8998.c index 1f0df4046b86..0a8dd1cbee6f 100644 --- a/drivers/regulator/max8998.c +++ b/drivers/regulator/max8998.c @@ -65,7 +65,7 @@ static const struct voltage_map_desc ldo9_voltage_map_desc = { .min = 2800000, .step = 100000, .max = 3100000, }; static const struct voltage_map_desc ldo10_voltage_map_desc = { - .min = 95000, .step = 50000, .max = 1300000, + .min = 950000, .step = 50000, .max = 1300000, }; static const struct voltage_map_desc ldo1213_voltage_map_desc = { .min = 800000, .step = 100000, .max = 3300000, diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 6f684916fd79..66ca769287ab 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -120,6 +120,12 @@ int of_regulator_match(struct device *dev, struct device_node *node, if (!dev || !node) return -EINVAL; + for (i = 0; i < num_matches; i++) { + struct of_regulator_match *match = &matches[i]; + match->init_data = NULL; + match->of_node = NULL; + } + for_each_child_of_node(node, child) { name = of_get_property(child, "regulator-compatible", NULL); diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index bd062a2ffbe2..cd9ea2ea1826 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -174,9 +174,9 @@ static struct regulator_ops s2mps11_buck_ops = { .min_uV = S2MPS11_BUCK_MIN2, \ .uV_step = S2MPS11_BUCK_STEP2, \ .n_voltages = S2MPS11_BUCK_N_VOLTAGES, \ - .vsel_reg = S2MPS11_REG_B9CTRL2, \ + .vsel_reg = S2MPS11_REG_B10CTRL2, \ .vsel_mask = S2MPS11_BUCK_VSEL_MASK, \ - .enable_reg = S2MPS11_REG_B9CTRL1, \ + .enable_reg = S2MPS11_REG_B10CTRL1, \ .enable_mask = S2MPS11_ENABLE_MASK \ } diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 73dce7664126..df395187c063 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -305,8 +305,8 @@ static struct tps65217_board *tps65217_parse_dt(struct platform_device *pdev) if (!regs) return NULL; - count = of_regulator_match(pdev->dev.parent, regs, - reg_matches, TPS65217_NUM_REGULATOR); + count = of_regulator_match(&pdev->dev, regs, reg_matches, + TPS65217_NUM_REGULATOR); of_node_put(regs); if ((count < 0) || (count > TPS65217_NUM_REGULATOR)) return NULL; diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 59c3770fa77d..b0e4c0bc85c3 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -998,7 +998,7 @@ static struct tps65910_board *tps65910_parse_dt_reg_data( return NULL; } - ret = of_regulator_match(pdev->dev.parent, regulators, matches, count); + ret = of_regulator_match(&pdev->dev, regulators, matches, count); if (ret < 0) { dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret); diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index afb7cfa85ccc..c016ad81767a 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -506,6 +506,7 @@ isl1208_rtc_interrupt(int irq, void *data) { unsigned long timeout = jiffies + msecs_to_jiffies(1000); struct i2c_client *client = data; + struct rtc_device *rtc = i2c_get_clientdata(client); int handled = 0, sr, err; /* @@ -528,6 +529,8 @@ isl1208_rtc_interrupt(int irq, void *data) if (sr & ISL1208_REG_SR_ALM) { dev_dbg(&client->dev, "alarm!\n"); + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + /* Clear the alarm */ sr &= ~ISL1208_REG_SR_ALM; sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr); diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index 08378e3cc21c..10c1a3454e48 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -44,6 +44,7 @@ #define RTC_YMR 0x34 /* Year match register */ #define RTC_YLR 0x38 /* Year data load register */ +#define RTC_CR_EN (1 << 0) /* counter enable bit */ #define RTC_CR_CWEN (1 << 26) /* Clockwatch enable bit */ #define RTC_TCR_EN (1 << 1) /* Periodic timer enable bit */ @@ -320,7 +321,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) struct pl031_local *ldata; struct pl031_vendor_data *vendor = id->data; struct rtc_class_ops *ops = &vendor->ops; - unsigned long time; + unsigned long time, data; ret = amba_request_regions(adev, NULL); if (ret) @@ -345,10 +346,11 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) dev_dbg(&adev->dev, "designer ID = 0x%02x\n", amba_manf(adev)); dev_dbg(&adev->dev, "revision = 0x%01x\n", amba_rev(adev)); + data = readl(ldata->base + RTC_CR); /* Enable the clockwatch on ST Variants */ if (vendor->clockwatch) - writel(readl(ldata->base + RTC_CR) | RTC_CR_CWEN, - ldata->base + RTC_CR); + data |= RTC_CR_CWEN; + writel(data | RTC_CR_EN, ldata->base + RTC_CR); /* * On ST PL031 variants, the RTC reset value does not provide correct diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c index 00c930f4b6f3..2730533e2d2d 100644 --- a/drivers/rtc/rtc-vt8500.c +++ b/drivers/rtc/rtc-vt8500.c @@ -137,7 +137,7 @@ static int vt8500_rtc_set_time(struct device *dev, struct rtc_time *tm) return -EINVAL; } - writel((bin2bcd(tm->tm_year - 100) << DATE_YEAR_S) + writel((bin2bcd(tm->tm_year % 100) << DATE_YEAR_S) | (bin2bcd(tm->tm_mon + 1) << DATE_MONTH_S) | (bin2bcd(tm->tm_mday)) | ((tm->tm_year >= 200) << DATE_CENTURY_S), diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index ad93231a8038..51a8c4216ebb 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -134,7 +134,6 @@ struct s3c64xx_spi_dma_data { unsigned ch; enum dma_transfer_direction direction; enum dma_ch dmach; - struct property *dma_prop; }; /** @@ -319,16 +318,15 @@ static void prepare_dma(struct s3c64xx_spi_dma_data *dma, static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) { struct samsung_dma_req req; + struct device *dev = &sdd->pdev->dev; sdd->ops = samsung_dma_get_ops(); req.cap = DMA_SLAVE; req.client = &s3c64xx_spi_dma_client; - req.dt_dmach_prop = sdd->rx_dma.dma_prop; - sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &req); - req.dt_dmach_prop = sdd->tx_dma.dma_prop; - sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &req); + sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &req, dev, "rx"); + sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &req, dev, "tx"); return 1; } @@ -1054,49 +1052,6 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) flush_fifo(sdd); } -static int s3c64xx_spi_get_dmares( - struct s3c64xx_spi_driver_data *sdd, bool tx) -{ - struct platform_device *pdev = sdd->pdev; - struct s3c64xx_spi_dma_data *dma_data; - struct property *prop; - struct resource *res; - char prop_name[15], *chan_str; - - if (tx) { - dma_data = &sdd->tx_dma; - dma_data->direction = DMA_MEM_TO_DEV; - chan_str = "tx"; - } else { - dma_data = &sdd->rx_dma; - dma_data->direction = DMA_DEV_TO_MEM; - chan_str = "rx"; - } - - if (!sdd->pdev->dev.of_node) { - res = platform_get_resource(pdev, IORESOURCE_DMA, tx ? 0 : 1); - if (!res) { - dev_err(&pdev->dev, "Unable to get SPI-%s dma " - "resource\n", chan_str); - return -ENXIO; - } - dma_data->dmach = res->start; - return 0; - } - - sprintf(prop_name, "%s-dma-channel", chan_str); - prop = of_find_property(pdev->dev.of_node, prop_name, NULL); - if (!prop) { - dev_err(&pdev->dev, "%s dma channel property not specified\n", - chan_str); - return -ENXIO; - } - - dma_data->dmach = DMACH_DT_PROP; - dma_data->dma_prop = prop; - return 0; -} - #ifdef CONFIG_OF static int s3c64xx_spi_parse_dt_gpio(struct s3c64xx_spi_driver_data *sdd) { @@ -1198,6 +1153,7 @@ static inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config( static int __init s3c64xx_spi_probe(struct platform_device *pdev) { struct resource *mem_res; + struct resource *res; struct s3c64xx_spi_driver_data *sdd; struct s3c64xx_spi_info *sci = pdev->dev.platform_data; struct spi_master *master; @@ -1256,13 +1212,26 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) sdd->cur_bpw = 8; - ret = s3c64xx_spi_get_dmares(sdd, true); - if (ret) - goto err0; + if (!sdd->pdev->dev.of_node) { + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(&pdev->dev, "Unable to get SPI tx dma " + "resource\n"); + return -ENXIO; + } + sdd->tx_dma.dmach = res->start; + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!res) { + dev_err(&pdev->dev, "Unable to get SPI rx dma " + "resource\n"); + return -ENXIO; + } + sdd->rx_dma.dmach = res->start; + } - ret = s3c64xx_spi_get_dmares(sdd, false); - if (ret) - goto err0; + sdd->tx_dma.direction = DMA_MEM_TO_DEV; + sdd->rx_dma.direction = DMA_DEV_TO_MEM; master->dev.of_node = pdev->dev.of_node; master->bus_num = sdd->port_id; diff --git a/drivers/ssb/driver_gpio.c b/drivers/ssb/driver_gpio.c index 97ac0a38e3d0..eb2753008ef0 100644 --- a/drivers/ssb/driver_gpio.c +++ b/drivers/ssb/driver_gpio.c @@ -174,3 +174,15 @@ int ssb_gpio_init(struct ssb_bus *bus) return -1; } + +int ssb_gpio_unregister(struct ssb_bus *bus) +{ + if (ssb_chipco_available(&bus->chipco) || + ssb_extif_available(&bus->extif)) { + return gpiochip_remove(&bus->gpio); + } else { + SSB_WARN_ON(1); + } + + return -1; +} diff --git a/drivers/ssb/main.c b/drivers/ssb/main.c index 772ad9b5c304..24dc331b4701 100644 --- a/drivers/ssb/main.c +++ b/drivers/ssb/main.c @@ -443,6 +443,15 @@ static void ssb_devices_unregister(struct ssb_bus *bus) void ssb_bus_unregister(struct ssb_bus *bus) { + int err; + + err = ssb_gpio_unregister(bus); + if (err == -EBUSY) + ssb_dprintk(KERN_ERR PFX "Some GPIOs are still in use.\n"); + else if (err) + ssb_dprintk(KERN_ERR PFX + "Can not unregister GPIO driver: %i\n", err); + ssb_buses_lock(); ssb_devices_unregister(bus); list_del(&bus->list); diff --git a/drivers/ssb/ssb_private.h b/drivers/ssb/ssb_private.h index 6c10b66c796c..da38305a2d22 100644 --- a/drivers/ssb/ssb_private.h +++ b/drivers/ssb/ssb_private.h @@ -252,11 +252,16 @@ static inline void ssb_extif_init(struct ssb_extif *extif) #ifdef CONFIG_SSB_DRIVER_GPIO extern int ssb_gpio_init(struct ssb_bus *bus); +extern int ssb_gpio_unregister(struct ssb_bus *bus); #else /* CONFIG_SSB_DRIVER_GPIO */ static inline int ssb_gpio_init(struct ssb_bus *bus) { return -ENOTSUPP; } +static inline int ssb_gpio_unregister(struct ssb_bus *bus) +{ + return 0; +} #endif /* CONFIG_SSB_DRIVER_GPIO */ #endif /* LINUX_SSB_PRIVATE_H_ */ diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index e2695101bb99..f2aa7543d20a 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -941,6 +941,8 @@ int se_dev_set_queue_depth(struct se_device *dev, u32 queue_depth) int se_dev_set_fabric_max_sectors(struct se_device *dev, u32 fabric_max_sectors) { + int block_size = dev->dev_attrib.block_size; + if (dev->export_count) { pr_err("dev[%p]: Unable to change SE Device" " fabric_max_sectors while export_count is %d\n", @@ -978,8 +980,12 @@ int se_dev_set_fabric_max_sectors(struct se_device *dev, u32 fabric_max_sectors) /* * Align max_sectors down to PAGE_SIZE to follow transport_allocate_data_tasks() */ + if (!block_size) { + block_size = 512; + pr_warn("Defaulting to 512 for zero block_size\n"); + } fabric_max_sectors = se_dev_align_max_sectors(fabric_max_sectors, - dev->dev_attrib.block_size); + block_size); dev->dev_attrib.fabric_max_sectors = fabric_max_sectors; pr_debug("dev[%p]: SE Device max_sectors changed to %u\n", diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c index 810263dfa4a1..c57bbbc7a7d1 100644 --- a/drivers/target/target_core_fabric_configfs.c +++ b/drivers/target/target_core_fabric_configfs.c @@ -754,6 +754,11 @@ static int target_fabric_port_link( return -EFAULT; } + if (!(dev->dev_flags & DF_CONFIGURED)) { + pr_err("se_device not configured yet, cannot port link\n"); + return -ENODEV; + } + tpg_ci = &lun_ci->ci_parent->ci_group->cg_item; se_tpg = container_of(to_config_group(tpg_ci), struct se_portal_group, tpg_group); diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 26a6d183ccb1..a664c664a31a 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -58,11 +58,10 @@ sbc_emulate_readcapacity(struct se_cmd *cmd) buf[7] = dev->dev_attrib.block_size & 0xff; rbuf = transport_kmap_data_sg(cmd); - if (!rbuf) - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - - memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); - transport_kunmap_data_sg(cmd); + if (rbuf) { + memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); + transport_kunmap_data_sg(cmd); + } target_complete_cmd(cmd, GOOD); return 0; @@ -97,11 +96,10 @@ sbc_emulate_readcapacity_16(struct se_cmd *cmd) buf[14] = 0x80; rbuf = transport_kmap_data_sg(cmd); - if (!rbuf) - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - - memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); - transport_kunmap_data_sg(cmd); + if (rbuf) { + memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); + transport_kunmap_data_sg(cmd); + } target_complete_cmd(cmd, GOOD); return 0; diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 84f9e96e8ace..2d88f087d961 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -641,11 +641,10 @@ spc_emulate_inquiry(struct se_cmd *cmd) out: rbuf = transport_kmap_data_sg(cmd); - if (!rbuf) - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - - memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); - transport_kunmap_data_sg(cmd); + if (rbuf) { + memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); + transport_kunmap_data_sg(cmd); + } if (!ret) target_complete_cmd(cmd, GOOD); @@ -851,7 +850,7 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) { struct se_device *dev = cmd->se_dev; char *cdb = cmd->t_task_cdb; - unsigned char *buf, *map_buf; + unsigned char buf[SE_MODE_PAGE_BUF], *rbuf; int type = dev->transport->get_device_type(dev); int ten = (cmd->t_task_cdb[0] == MODE_SENSE_10); bool dbd = !!(cdb[1] & 0x08); @@ -863,26 +862,8 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) int ret; int i; - map_buf = transport_kmap_data_sg(cmd); - if (!map_buf) - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - /* - * If SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC is not set, then we - * know we actually allocated a full page. Otherwise, if the - * data buffer is too small, allocate a temporary buffer so we - * don't have to worry about overruns in all our INQUIRY - * emulation handling. - */ - if (cmd->data_length < SE_MODE_PAGE_BUF && - (cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC)) { - buf = kzalloc(SE_MODE_PAGE_BUF, GFP_KERNEL); - if (!buf) { - transport_kunmap_data_sg(cmd); - return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; - } - } else { - buf = map_buf; - } + memset(buf, 0, SE_MODE_PAGE_BUF); + /* * Skip over MODE DATA LENGTH + MEDIUM TYPE fields to byte 3 for * MODE_SENSE_10 and byte 2 for MODE_SENSE (6). @@ -934,8 +915,6 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) if (page == 0x3f) { if (subpage != 0x00 && subpage != 0xff) { pr_warn("MODE_SENSE: Invalid subpage code: 0x%02x\n", subpage); - kfree(buf); - transport_kunmap_data_sg(cmd); return TCM_INVALID_CDB_FIELD; } @@ -972,7 +951,6 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) pr_err("MODE SENSE: unimplemented page/subpage: 0x%02x/0x%02x\n", page, subpage); - transport_kunmap_data_sg(cmd); return TCM_UNKNOWN_MODE_PAGE; set_length: @@ -981,12 +959,12 @@ set_length: else buf[0] = length - 1; - if (buf != map_buf) { - memcpy(map_buf, buf, cmd->data_length); - kfree(buf); + rbuf = transport_kmap_data_sg(cmd); + if (rbuf) { + memcpy(rbuf, buf, min_t(u32, SE_MODE_PAGE_BUF, cmd->data_length)); + transport_kunmap_data_sg(cmd); } - transport_kunmap_data_sg(cmd); target_complete_cmd(cmd, GOOD); return 0; } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 4225d5e72131..8e64adf8e4d5 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -39,6 +39,7 @@ #include <asm/unaligned.h> #include <linux/platform_device.h> #include <linux/workqueue.h> +#include <linux/pm_runtime.h> #include <linux/usb.h> #include <linux/usb/hcd.h> @@ -1025,6 +1026,49 @@ static int register_root_hub(struct usb_hcd *hcd) return retval; } +/* + * usb_hcd_start_port_resume - a root-hub port is sending a resume signal + * @bus: the bus which the root hub belongs to + * @portnum: the port which is being resumed + * + * HCDs should call this function when they know that a resume signal is + * being sent to a root-hub port. The root hub will be prevented from + * going into autosuspend until usb_hcd_end_port_resume() is called. + * + * The bus's private lock must be held by the caller. + */ +void usb_hcd_start_port_resume(struct usb_bus *bus, int portnum) +{ + unsigned bit = 1 << portnum; + + if (!(bus->resuming_ports & bit)) { + bus->resuming_ports |= bit; + pm_runtime_get_noresume(&bus->root_hub->dev); + } +} +EXPORT_SYMBOL_GPL(usb_hcd_start_port_resume); + +/* + * usb_hcd_end_port_resume - a root-hub port has stopped sending a resume signal + * @bus: the bus which the root hub belongs to + * @portnum: the port which is being resumed + * + * HCDs should call this function when they know that a resume signal has + * stopped being sent to a root-hub port. The root hub will be allowed to + * autosuspend again. + * + * The bus's private lock must be held by the caller. + */ +void usb_hcd_end_port_resume(struct usb_bus *bus, int portnum) +{ + unsigned bit = 1 << portnum; + + if (bus->resuming_ports & bit) { + bus->resuming_ports &= ~bit; + pm_runtime_put_noidle(&bus->root_hub->dev); + } +} +EXPORT_SYMBOL_GPL(usb_hcd_end_port_resume); /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 957ed2c41482..cbf7168e3ce7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2838,6 +2838,23 @@ void usb_enable_ltm(struct usb_device *udev) EXPORT_SYMBOL_GPL(usb_enable_ltm); #ifdef CONFIG_USB_SUSPEND +/* + * usb_disable_function_remotewakeup - disable usb3.0 + * device's function remote wakeup + * @udev: target device + * + * Assume there's only one function on the USB 3.0 + * device and disable remote wake for the first + * interface. FIXME if the interface association + * descriptor shows there's more than one function. + */ +static int usb_disable_function_remotewakeup(struct usb_device *udev) +{ + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_INTERFACE, + USB_INTRF_FUNC_SUSPEND, 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); +} /* * usb_port_suspend - suspend a usb device's upstream port @@ -2955,12 +2972,19 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", port1, status); /* paranoia: "should not happen" */ - if (udev->do_remote_wakeup) - (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, - USB_DEVICE_REMOTE_WAKEUP, 0, - NULL, 0, - USB_CTRL_SET_TIMEOUT); + if (udev->do_remote_wakeup) { + if (!hub_is_superspeed(hub->hdev)) { + (void) usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, + USB_RECIP_DEVICE, + USB_DEVICE_REMOTE_WAKEUP, 0, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + } else + (void) usb_disable_function_remotewakeup(udev); + + } /* Try to enable USB2 hardware LPM again */ if (udev->usb2_hw_lpm_capable == 1) @@ -3052,20 +3076,30 @@ static int finish_port_resume(struct usb_device *udev) * udev->reset_resume */ } else if (udev->actconfig && !udev->reset_resume) { - le16_to_cpus(&devstatus); - if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) { - status = usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - USB_REQ_CLEAR_FEATURE, + if (!hub_is_superspeed(udev->parent)) { + le16_to_cpus(&devstatus); + if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) + status = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, - USB_DEVICE_REMOTE_WAKEUP, 0, - NULL, 0, - USB_CTRL_SET_TIMEOUT); - if (status) - dev_dbg(&udev->dev, - "disable remote wakeup, status %d\n", - status); + USB_DEVICE_REMOTE_WAKEUP, 0, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + } else { + status = usb_get_status(udev, USB_RECIP_INTERFACE, 0, + &devstatus); + le16_to_cpus(&devstatus); + if (!status && devstatus & (USB_INTRF_STAT_FUNC_RW_CAP + | USB_INTRF_STAT_FUNC_RW)) + status = + usb_disable_function_remotewakeup(udev); } + + if (status) + dev_dbg(&udev->dev, + "disable remote wakeup, status %d\n", + status); status = 0; } return status; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 09537b2f1002..b416a3fc9959 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -797,6 +797,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) ehci->reset_done[i] = jiffies + msecs_to_jiffies(25); set_bit(i, &ehci->resuming_ports); ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); + usb_hcd_start_port_resume(&hcd->self, i); mod_timer(&hcd->rh_timer, ehci->reset_done[i]); } } diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 4ccb97c0678f..4d3b294f203e 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -649,7 +649,11 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) status = STS_PCD; } } - /* FIXME autosuspend idle root hubs */ + + /* If a resume is in progress, make sure it can finish */ + if (ehci->resuming_ports) + mod_timer(&hcd->rh_timer, jiffies + msecs_to_jiffies(25)); + spin_unlock_irqrestore (&ehci->lock, flags); return status ? retval : 0; } @@ -851,6 +855,7 @@ static int ehci_hub_control ( /* resume signaling for 20 msec */ ehci->reset_done[wIndex] = jiffies + msecs_to_jiffies(20); + usb_hcd_start_port_resume(&hcd->self, wIndex); /* check the port again */ mod_timer(&ehci_to_hcd(ehci)->rh_timer, ehci->reset_done[wIndex]); @@ -862,6 +867,7 @@ static int ehci_hub_control ( clear_bit(wIndex, &ehci->suspended_ports); set_bit(wIndex, &ehci->port_c_suspend); ehci->reset_done[wIndex] = 0; + usb_hcd_end_port_resume(&hcd->self, wIndex); /* stop resume signaling */ temp = ehci_readl(ehci, status_reg); @@ -950,6 +956,7 @@ static int ehci_hub_control ( ehci->reset_done[wIndex] = 0; if (temp & PORT_PE) set_bit(wIndex, &ehci->port_c_suspend); + usb_hcd_end_port_resume(&hcd->self, wIndex); } if (temp & PORT_OC) diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 3d989028c836..fd252f0cfb3a 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1197,17 +1197,26 @@ static void start_iaa_cycle(struct ehci_hcd *ehci, bool nested) if (ehci->async_iaa || ehci->async_unlinking) return; - /* Do all the waiting QHs at once */ - ehci->async_iaa = ehci->async_unlink; - ehci->async_unlink = NULL; - /* If the controller isn't running, we don't have to wait for it */ if (unlikely(ehci->rh_state < EHCI_RH_RUNNING)) { + + /* Do all the waiting QHs */ + ehci->async_iaa = ehci->async_unlink; + ehci->async_unlink = NULL; + if (!nested) /* Avoid recursion */ end_unlink_async(ehci); /* Otherwise start a new IAA cycle */ } else if (likely(ehci->rh_state == EHCI_RH_RUNNING)) { + struct ehci_qh *qh; + + /* Do only the first waiting QH (nVidia bug?) */ + qh = ehci->async_unlink; + ehci->async_iaa = qh; + ehci->async_unlink = qh->unlink_next; + qh->unlink_next = NULL; + /* Make sure the unlinks are all visible to the hardware */ wmb(); @@ -1255,34 +1264,35 @@ static void end_unlink_async(struct ehci_hcd *ehci) } } +static void start_unlink_async(struct ehci_hcd *ehci, struct ehci_qh *qh); + static void unlink_empty_async(struct ehci_hcd *ehci) { - struct ehci_qh *qh, *next; - bool stopped = (ehci->rh_state < EHCI_RH_RUNNING); + struct ehci_qh *qh; + struct ehci_qh *qh_to_unlink = NULL; bool check_unlinks_later = false; + int count = 0; - /* Unlink all the async QHs that have been empty for a timer cycle */ - next = ehci->async->qh_next.qh; - while (next) { - qh = next; - next = qh->qh_next.qh; - + /* Find the last async QH which has been empty for a timer cycle */ + for (qh = ehci->async->qh_next.qh; qh; qh = qh->qh_next.qh) { if (list_empty(&qh->qtd_list) && qh->qh_state == QH_STATE_LINKED) { - if (!stopped && qh->unlink_cycle == - ehci->async_unlink_cycle) + ++count; + if (qh->unlink_cycle == ehci->async_unlink_cycle) check_unlinks_later = true; else - single_unlink_async(ehci, qh); + qh_to_unlink = qh; } } - /* Start a new IAA cycle if any QHs are waiting for it */ - if (ehci->async_unlink) - start_iaa_cycle(ehci, false); + /* If nothing else is being unlinked, unlink the last empty QH */ + if (!ehci->async_iaa && !ehci->async_unlink && qh_to_unlink) { + start_unlink_async(ehci, qh_to_unlink); + --count; + } - /* QHs that haven't been empty for long enough will be handled later */ - if (check_unlinks_later) { + /* Other QHs will be handled later */ + if (count > 0) { ehci_enable_event(ehci, EHCI_HRTIMER_ASYNC_UNLINKS, true); ++ehci->async_unlink_cycle; } diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 69ebee73c0c1..b476daf49f6f 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -213,7 +213,7 @@ static inline unsigned char tt_start_uframe(struct ehci_hcd *ehci, __hc32 mask) } static const unsigned char -max_tt_usecs[] = { 125, 125, 125, 125, 125, 125, 30, 0 }; +max_tt_usecs[] = { 125, 125, 125, 125, 125, 125, 125, 25 }; /* carryover low/fullspeed bandwidth that crosses uframe boundries */ static inline void carryover_tt_bandwidth(unsigned short tt_usecs[8]) @@ -2212,11 +2212,11 @@ static void scan_isoc(struct ehci_hcd *ehci) } ehci->now_frame = now_frame; + frame = ehci->last_iso_frame; for (;;) { union ehci_shadow q, *q_p; __hc32 type, *hw_p; - frame = ehci->last_iso_frame; restart: /* scan each element in frame's queue for completions */ q_p = &ehci->pshadow [frame]; @@ -2321,6 +2321,9 @@ restart: /* Stop when we have reached the current frame */ if (frame == now_frame) break; - ehci->last_iso_frame = (frame + 1) & fmask; + + /* The last frame may still have active siTDs */ + ehci->last_iso_frame = frame; + frame = (frame + 1) & fmask; } } diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c index 20dbdcbe9b0f..f904071d70df 100644 --- a/drivers/usb/host/ehci-timer.c +++ b/drivers/usb/host/ehci-timer.c @@ -113,14 +113,15 @@ static void ehci_poll_ASS(struct ehci_hcd *ehci) if (want != actual) { - /* Poll again later, but give up after about 20 ms */ - if (ehci->ASS_poll_count++ < 20) { - ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true); - return; - } - ehci_dbg(ehci, "Waited too long for the async schedule status (%x/%x), giving up\n", - want, actual); + /* Poll again later */ + ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true); + ++ehci->ASS_poll_count; + return; } + + if (ehci->ASS_poll_count > 20) + ehci_dbg(ehci, "ASS poll count reached %d\n", + ehci->ASS_poll_count); ehci->ASS_poll_count = 0; /* The status is up-to-date; restart or stop the schedule as needed */ @@ -159,14 +160,14 @@ static void ehci_poll_PSS(struct ehci_hcd *ehci) if (want != actual) { - /* Poll again later, but give up after about 20 ms */ - if (ehci->PSS_poll_count++ < 20) { - ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); - return; - } - ehci_dbg(ehci, "Waited too long for the periodic schedule status (%x/%x), giving up\n", - want, actual); + /* Poll again later */ + ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true); + return; } + + if (ehci->PSS_poll_count > 20) + ehci_dbg(ehci, "PSS poll count reached %d\n", + ehci->PSS_poll_count); ehci->PSS_poll_count = 0; /* The status is up-to-date; restart or stop the schedule as needed */ diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index a3b6d7104ae2..4c338ec03a07 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -780,6 +780,7 @@ void usb_enable_xhci_ports(struct pci_dev *xhci_pdev) "defaulting to EHCI.\n"); dev_warn(&xhci_pdev->dev, "USB 3.0 devices will work at USB 2.0 speeds.\n"); + usb_disable_xhci_ports(xhci_pdev); return; } diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 768d54295a20..15d13229ddbb 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -116,6 +116,7 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, } } clear_bit(port, &uhci->resuming_ports); + usb_hcd_end_port_resume(&uhci_to_hcd(uhci)->self, port); } /* Wait for the UHCI controller in HP's iLO2 server management chip. @@ -167,6 +168,8 @@ static void uhci_check_ports(struct uhci_hcd *uhci) set_bit(port, &uhci->resuming_ports); uhci->ports_timeout = jiffies + msecs_to_jiffies(25); + usb_hcd_start_port_resume( + &uhci_to_hcd(uhci)->self, port); /* Make sure we see the port again * after the resuming period is over. */ diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 59fb5c677dbe..7f76a49e90d3 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1698,7 +1698,7 @@ static void handle_port_status(struct xhci_hcd *xhci, faked_port_index + 1); if (slot_id && xhci->devs[slot_id]) xhci_ring_device(xhci, slot_id); - if (bus_state->port_remote_wakeup && (1 << faked_port_index)) { + if (bus_state->port_remote_wakeup & (1 << faked_port_index)) { bus_state->port_remote_wakeup &= ~(1 << faked_port_index); xhci_test_and_clear_bit(xhci, port_array, @@ -2589,6 +2589,8 @@ cleanup: (trb_comp_code != COMP_STALL && trb_comp_code != COMP_BABBLE)) xhci_urb_free_priv(xhci, urb_priv); + else + kfree(urb_priv); usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb); if ((urb->actual_length != urb->transfer_buffer_length && @@ -3108,7 +3110,7 @@ static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len, * running_total. */ packets_transferred = (running_total + trb_buff_len) / - usb_endpoint_maxp(&urb->ep->desc); + GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc)); if ((total_packet_count - packets_transferred) > 31) return 31 << 17; @@ -3642,7 +3644,8 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, td_len = urb->iso_frame_desc[i].length; td_remain_len = td_len; total_packet_count = DIV_ROUND_UP(td_len, - usb_endpoint_maxp(&urb->ep->desc)); + GET_MAX_PACKET( + usb_endpoint_maxp(&urb->ep->desc))); /* A zero-length transfer still involves at least one packet. */ if (total_packet_count == 0) total_packet_count++; @@ -3664,9 +3667,11 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, td = urb_priv->td[i]; for (j = 0; j < trbs_per_td; j++) { u32 remainder = 0; - field = TRB_TBC(burst_count) | TRB_TLBPC(residue); + field = 0; if (first_trb) { + field = TRB_TBC(burst_count) | + TRB_TLBPC(residue); /* Queue the isoc TRB */ field |= TRB_TYPE(TRB_ISOC); /* Assume URB_ISO_ASAP is set */ diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index f14736f647ff..edc0f0dcad83 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -60,6 +60,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x0FCF, 0x1003) }, /* Dynastream ANT development board */ { USB_DEVICE(0x0FCF, 0x1004) }, /* Dynastream ANT2USB */ { USB_DEVICE(0x0FCF, 0x1006) }, /* Dynastream ANT development board */ + { USB_DEVICE(0x0FDE, 0xCA05) }, /* OWL Wireless Electricity Monitor CM-160 */ { USB_DEVICE(0x10A6, 0xAA26) }, /* Knock-off DCU-11 cable */ { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */ { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */ diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index ba68835d06a6..90ceef1776c3 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -584,6 +584,7 @@ static struct usb_device_id id_table_combined [] = { /* * ELV devices: */ + { USB_DEVICE(FTDI_ELV_VID, FTDI_ELV_WS300_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ELV_USR_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ELV_MSM1_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ELV_KL100_PID) }, @@ -670,6 +671,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_5_PID) }, { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_6_PID) }, { USB_DEVICE(FTDI_VID, XSENS_CONVERTER_7_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_OMNI1509) }, { USB_DEVICE(MOBILITY_VID, MOBILITY_USB_SERIAL_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ACTIVE_ROBOTS_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MHAM_KW_PID) }, diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index fa5d56038276..9d359e189a64 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -147,6 +147,11 @@ #define XSENS_CONVERTER_6_PID 0xD38E #define XSENS_CONVERTER_7_PID 0xD38F +/** + * Zolix (www.zolix.com.cb) product ids + */ +#define FTDI_OMNI1509 0xD491 /* Omni1509 embedded USB-serial */ + /* * NDI (www.ndigital.com) product ids */ @@ -204,7 +209,7 @@ /* * ELV USB devices submitted by Christian Abt of ELV (www.elv.de). - * All of these devices use FTDI's vendor ID (0x0403). + * Almost all of these devices use FTDI's vendor ID (0x0403). * Further IDs taken from ELV Windows .inf file. * * The previously included PID for the UO 100 module was incorrect. @@ -212,6 +217,8 @@ * * Armin Laeuger originally sent the PID for the UM 100 module. */ +#define FTDI_ELV_VID 0x1B1F /* ELV AG */ +#define FTDI_ELV_WS300_PID 0xC006 /* eQ3 WS 300 PC II */ #define FTDI_ELV_USR_PID 0xE000 /* ELV Universal-Sound-Recorder */ #define FTDI_ELV_MSM1_PID 0xE001 /* ELV Mini-Sound-Modul */ #define FTDI_ELV_KL100_PID 0xE002 /* ELV Kfz-Leistungsmesser KL 100 */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 0d9dac9e7f93..567bc77d6397 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -242,6 +242,7 @@ static void option_instat_callback(struct urb *urb); #define TELIT_PRODUCT_CC864_DUAL 0x1005 #define TELIT_PRODUCT_CC864_SINGLE 0x1006 #define TELIT_PRODUCT_DE910_DUAL 0x1010 +#define TELIT_PRODUCT_LE920 0x1200 /* ZTE PRODUCTS */ #define ZTE_VENDOR_ID 0x19d2 @@ -453,6 +454,10 @@ static void option_instat_callback(struct urb *urb); #define TPLINK_VENDOR_ID 0x2357 #define TPLINK_PRODUCT_MA180 0x0201 +/* Changhong products */ +#define CHANGHONG_VENDOR_ID 0x2077 +#define CHANGHONG_PRODUCT_CH690 0x7001 + /* some devices interfaces need special handling due to a number of reasons */ enum option_blacklist_reason { OPTION_BLACKLIST_NONE = 0, @@ -534,6 +539,11 @@ static const struct option_blacklist_info zte_1255_blacklist = { .reserved = BIT(3) | BIT(4), }; +static const struct option_blacklist_info telit_le920_blacklist = { + .sendsetup = BIT(0), + .reserved = BIT(1) | BIT(5), +}; + static const struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -784,6 +794,8 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_DUAL) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_SINGLE) }, { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_DE910_DUAL) }, + { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE920), + .driver_info = (kernel_ulong_t)&telit_le920_blacklist }, { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, ZTE_PRODUCT_MF622, 0xff, 0xff, 0xff) }, /* ZTE WCDMA products */ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0002, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&net_intf1_blacklist }, @@ -1318,6 +1330,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T) }, { USB_DEVICE(TPLINK_VENDOR_ID, TPLINK_PRODUCT_MA180), .driver_info = (kernel_ulong_t)&net_intf4_blacklist }, + { USB_DEVICE(CHANGHONG_VENDOR_ID, CHANGHONG_PRODUCT_CH690) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index aa148c21ea40..24662547dc5b 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -53,6 +53,7 @@ static const struct usb_device_id id_table[] = { {DEVICE_G1K(0x05c6, 0x9221)}, /* Generic Gobi QDL device */ {DEVICE_G1K(0x05c6, 0x9231)}, /* Generic Gobi QDL device */ {DEVICE_G1K(0x1f45, 0x0001)}, /* Unknown Gobi QDL device */ + {DEVICE_G1K(0x1bc7, 0x900e)}, /* Telit Gobi QDL device */ /* Gobi 2000 devices */ {USB_DEVICE(0x1410, 0xa010)}, /* Novatel Gobi 2000 QDL device */ diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c index 105d900150c1..16b0bf055eeb 100644 --- a/drivers/usb/storage/initializers.c +++ b/drivers/usb/storage/initializers.c @@ -92,8 +92,8 @@ int usb_stor_ucr61s2b_init(struct us_data *us) return 0; } -/* This places the HUAWEI E220 devices in multi-port mode */ -int usb_stor_huawei_e220_init(struct us_data *us) +/* This places the HUAWEI usb dongles in multi-port mode */ +static int usb_stor_huawei_feature_init(struct us_data *us) { int result; @@ -104,3 +104,75 @@ int usb_stor_huawei_e220_init(struct us_data *us) US_DEBUGP("Huawei mode set result is %d\n", result); return 0; } + +/* + * It will send a scsi switch command called rewind' to huawei dongle. + * When the dongle receives this command at the first time, + * it will reboot immediately. After rebooted, it will ignore this command. + * So it is unnecessary to read its response. + */ +static int usb_stor_huawei_scsi_init(struct us_data *us) +{ + int result = 0; + int act_len = 0; + struct bulk_cb_wrap *bcbw = (struct bulk_cb_wrap *) us->iobuf; + char rewind_cmd[] = {0x11, 0x06, 0x20, 0x00, 0x00, 0x01, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + bcbw->Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcbw->Tag = 0; + bcbw->DataTransferLength = 0; + bcbw->Flags = bcbw->Lun = 0; + bcbw->Length = sizeof(rewind_cmd); + memset(bcbw->CDB, 0, sizeof(bcbw->CDB)); + memcpy(bcbw->CDB, rewind_cmd, sizeof(rewind_cmd)); + + result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe, bcbw, + US_BULK_CB_WRAP_LEN, &act_len); + US_DEBUGP("transfer actual length=%d, result=%d\n", act_len, result); + return result; +} + +/* + * It tries to find the supported Huawei USB dongles. + * In Huawei, they assign the following product IDs + * for all of their mobile broadband dongles, + * including the new dongles in the future. + * So if the product ID is not included in this list, + * it means it is not Huawei's mobile broadband dongles. + */ +static int usb_stor_huawei_dongles_pid(struct us_data *us) +{ + struct usb_interface_descriptor *idesc; + int idProduct; + + idesc = &us->pusb_intf->cur_altsetting->desc; + idProduct = us->pusb_dev->descriptor.idProduct; + /* The first port is CDROM, + * means the dongle in the single port mode, + * and a switch command is required to be sent. */ + if (idesc && idesc->bInterfaceNumber == 0) { + if ((idProduct == 0x1001) + || (idProduct == 0x1003) + || (idProduct == 0x1004) + || (idProduct >= 0x1401 && idProduct <= 0x1500) + || (idProduct >= 0x1505 && idProduct <= 0x1600) + || (idProduct >= 0x1c02 && idProduct <= 0x2202)) { + return 1; + } + } + return 0; +} + +int usb_stor_huawei_init(struct us_data *us) +{ + int result = 0; + + if (usb_stor_huawei_dongles_pid(us)) { + if (us->pusb_dev->descriptor.idProduct >= 0x1446) + result = usb_stor_huawei_scsi_init(us); + else + result = usb_stor_huawei_feature_init(us); + } + return result; +} diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h index 529327fbb06b..5376d4fc76f0 100644 --- a/drivers/usb/storage/initializers.h +++ b/drivers/usb/storage/initializers.h @@ -46,5 +46,5 @@ int usb_stor_euscsi_init(struct us_data *us); * flash reader */ int usb_stor_ucr61s2b_init(struct us_data *us); -/* This places the HUAWEI E220 devices in multi-port mode */ -int usb_stor_huawei_e220_init(struct us_data *us); +/* This places the HUAWEI usb dongles in multi-port mode */ +int usb_stor_huawei_init(struct us_data *us); diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index d305a5aa3a5d..72923b56bbf6 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -1527,335 +1527,10 @@ UNUSUAL_DEV( 0x1210, 0x0003, 0x0100, 0x0100, /* Reported by fangxiaozhi <[email protected]> * This brings the HUAWEI data card devices into multi-port mode */ -UNUSUAL_DEV( 0x12d1, 0x1001, 0x0000, 0x0000, +UNUSUAL_VENDOR_INTF(0x12d1, 0x08, 0x06, 0x50, "HUAWEI MOBILE", "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1003, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1004, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1401, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1402, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1403, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1404, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1405, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1406, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1407, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1408, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1409, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x140A, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x140B, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x140C, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x140D, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x140E, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x140F, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1410, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1411, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1412, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1413, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1414, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1415, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1416, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1417, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1418, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1419, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x141A, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x141B, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x141C, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x141D, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x141E, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x141F, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1420, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1421, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1422, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1423, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1424, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1425, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1426, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1427, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1428, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1429, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x142A, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x142B, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x142C, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x142D, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x142E, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x142F, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1430, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1431, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1432, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1433, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1434, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1435, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1436, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1437, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1438, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x1439, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x143A, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x143B, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x143C, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x143D, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x143E, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, - 0), -UNUSUAL_DEV( 0x12d1, 0x143F, 0x0000, 0x0000, - "HUAWEI MOBILE", - "Mass Storage", - USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_e220_init, + USB_SC_DEVICE, USB_PR_DEVICE, usb_stor_huawei_init, 0), /* Reported by Vilius Bilinkevicius <vilisas AT xxx DOT lt) */ diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 31b3e1a61bbd..cf09b6ba71ff 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -120,6 +120,17 @@ MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks"); .useTransport = use_transport, \ } +#define UNUSUAL_VENDOR_INTF(idVendor, cl, sc, pr, \ + vendor_name, product_name, use_protocol, use_transport, \ + init_function, Flags) \ +{ \ + .vendorName = vendor_name, \ + .productName = product_name, \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ + .initFunction = init_function, \ +} + static struct us_unusual_dev us_unusual_dev_list[] = { # include "unusual_devs.h" { } /* Terminating entry */ @@ -131,6 +142,7 @@ static struct us_unusual_dev for_dynamic_ids = #undef UNUSUAL_DEV #undef COMPLIANT_DEV #undef USUAL_DEV +#undef UNUSUAL_VENDOR_INTF #ifdef CONFIG_LOCKDEP diff --git a/drivers/usb/storage/usual-tables.c b/drivers/usb/storage/usual-tables.c index b78a526910fb..5ef8ce74aae4 100644 --- a/drivers/usb/storage/usual-tables.c +++ b/drivers/usb/storage/usual-tables.c @@ -41,6 +41,20 @@ #define USUAL_DEV(useProto, useTrans) \ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans) } +/* Define the device is matched with Vendor ID and interface descriptors */ +#define UNUSUAL_VENDOR_INTF(id_vendor, cl, sc, pr, \ + vendorName, productName, useProtocol, useTransport, \ + initFunction, flags) \ +{ \ + .match_flags = USB_DEVICE_ID_MATCH_INT_INFO \ + | USB_DEVICE_ID_MATCH_VENDOR, \ + .idVendor = (id_vendor), \ + .bInterfaceClass = (cl), \ + .bInterfaceSubClass = (sc), \ + .bInterfaceProtocol = (pr), \ + .driver_info = (flags) \ +} + struct usb_device_id usb_storage_usb_ids[] = { # include "unusual_devs.h" { } /* Terminating entry */ @@ -50,6 +64,7 @@ MODULE_DEVICE_TABLE(usb, usb_storage_usb_ids); #undef UNUSUAL_DEV #undef COMPLIANT_DEV #undef USUAL_DEV +#undef UNUSUAL_VENDOR_INTF /* * The table of devices to ignore diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index ebd08b21b234..959b1cd89e6a 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -165,12 +165,16 @@ static void tx_poll_stop(struct vhost_net *net) } /* Caller must have TX VQ lock */ -static void tx_poll_start(struct vhost_net *net, struct socket *sock) +static int tx_poll_start(struct vhost_net *net, struct socket *sock) { + int ret; + if (unlikely(net->tx_poll_state != VHOST_NET_POLL_STOPPED)) - return; - vhost_poll_start(net->poll + VHOST_NET_VQ_TX, sock->file); - net->tx_poll_state = VHOST_NET_POLL_STARTED; + return 0; + ret = vhost_poll_start(net->poll + VHOST_NET_VQ_TX, sock->file); + if (!ret) + net->tx_poll_state = VHOST_NET_POLL_STARTED; + return ret; } /* In case of DMA done not in order in lower device driver for some reason. @@ -642,20 +646,23 @@ static void vhost_net_disable_vq(struct vhost_net *n, vhost_poll_stop(n->poll + VHOST_NET_VQ_RX); } -static void vhost_net_enable_vq(struct vhost_net *n, +static int vhost_net_enable_vq(struct vhost_net *n, struct vhost_virtqueue *vq) { struct socket *sock; + int ret; sock = rcu_dereference_protected(vq->private_data, lockdep_is_held(&vq->mutex)); if (!sock) - return; + return 0; if (vq == n->vqs + VHOST_NET_VQ_TX) { n->tx_poll_state = VHOST_NET_POLL_STOPPED; - tx_poll_start(n, sock); + ret = tx_poll_start(n, sock); } else - vhost_poll_start(n->poll + VHOST_NET_VQ_RX, sock->file); + ret = vhost_poll_start(n->poll + VHOST_NET_VQ_RX, sock->file); + + return ret; } static struct socket *vhost_net_stop_vq(struct vhost_net *n, @@ -827,15 +834,18 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd) r = PTR_ERR(ubufs); goto err_ubufs; } - oldubufs = vq->ubufs; - vq->ubufs = ubufs; + vhost_net_disable_vq(n, vq); rcu_assign_pointer(vq->private_data, sock); - vhost_net_enable_vq(n, vq); - r = vhost_init_used(vq); if (r) - goto err_vq; + goto err_used; + r = vhost_net_enable_vq(n, vq); + if (r) + goto err_used; + + oldubufs = vq->ubufs; + vq->ubufs = ubufs; n->tx_packets = 0; n->tx_zcopy_err = 0; @@ -859,6 +869,11 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd) mutex_unlock(&n->dev.mutex); return 0; +err_used: + rcu_assign_pointer(vq->private_data, oldsock); + vhost_net_enable_vq(n, vq); + if (ubufs) + vhost_ubuf_put_and_wait(ubufs); err_ubufs: fput(sock->file); err_vq: diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c index b20df5c829f5..22321cf84fbe 100644 --- a/drivers/vhost/tcm_vhost.c +++ b/drivers/vhost/tcm_vhost.c @@ -575,10 +575,8 @@ static void vhost_scsi_handle_vq(struct vhost_scsi *vs) /* Must use ioctl VHOST_SCSI_SET_ENDPOINT */ tv_tpg = vs->vs_tpg; - if (unlikely(!tv_tpg)) { - pr_err("%s endpoint not set\n", __func__); + if (unlikely(!tv_tpg)) return; - } mutex_lock(&vq->mutex); vhost_disable_notify(&vs->dev, vq); diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 34389f75fe65..9759249e6d90 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -77,26 +77,38 @@ void vhost_poll_init(struct vhost_poll *poll, vhost_work_fn_t fn, init_poll_funcptr(&poll->table, vhost_poll_func); poll->mask = mask; poll->dev = dev; + poll->wqh = NULL; vhost_work_init(&poll->work, fn); } /* Start polling a file. We add ourselves to file's wait queue. The caller must * keep a reference to a file until after vhost_poll_stop is called. */ -void vhost_poll_start(struct vhost_poll *poll, struct file *file) +int vhost_poll_start(struct vhost_poll *poll, struct file *file) { unsigned long mask; + int ret = 0; mask = file->f_op->poll(file, &poll->table); if (mask) vhost_poll_wakeup(&poll->wait, 0, 0, (void *)mask); + if (mask & POLLERR) { + if (poll->wqh) + remove_wait_queue(poll->wqh, &poll->wait); + ret = -EINVAL; + } + + return ret; } /* Stop polling a file. After this function returns, it becomes safe to drop the * file reference. You must also flush afterwards. */ void vhost_poll_stop(struct vhost_poll *poll) { - remove_wait_queue(poll->wqh, &poll->wait); + if (poll->wqh) { + remove_wait_queue(poll->wqh, &poll->wait); + poll->wqh = NULL; + } } static bool vhost_work_seq_done(struct vhost_dev *dev, struct vhost_work *work, @@ -792,7 +804,7 @@ long vhost_vring_ioctl(struct vhost_dev *d, int ioctl, void __user *argp) fput(filep); if (pollstart && vq->handle_kick) - vhost_poll_start(&vq->poll, vq->kick); + r = vhost_poll_start(&vq->poll, vq->kick); mutex_unlock(&vq->mutex); diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h index 2639c58b23ab..17261e277c02 100644 --- a/drivers/vhost/vhost.h +++ b/drivers/vhost/vhost.h @@ -42,7 +42,7 @@ void vhost_work_queue(struct vhost_dev *dev, struct vhost_work *work); void vhost_poll_init(struct vhost_poll *poll, vhost_work_fn_t fn, unsigned long mask, struct vhost_dev *dev); -void vhost_poll_start(struct vhost_poll *poll, struct file *file); +int vhost_poll_start(struct vhost_poll *poll, struct file *file); void vhost_poll_stop(struct vhost_poll *poll); void vhost_poll_flush(struct vhost_poll *poll); void vhost_poll_queue(struct vhost_poll *poll); diff --git a/drivers/xen/events.c b/drivers/xen/events.c index 0be4df39e953..74d77dfa5f63 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c @@ -840,7 +840,7 @@ int bind_evtchn_to_irq(unsigned int evtchn) if (irq == -1) { irq = xen_allocate_irq_dynamic(); - if (irq == -1) + if (irq < 0) goto out; irq_set_chip_and_handler_name(irq, &xen_dynamic_chip, @@ -944,7 +944,7 @@ int bind_virq_to_irq(unsigned int virq, unsigned int cpu) if (irq == -1) { irq = xen_allocate_irq_dynamic(); - if (irq == -1) + if (irq < 0) goto out; irq_set_chip_and_handler_name(irq, &xen_percpu_chip, diff --git a/drivers/xen/xen-pciback/pciback_ops.c b/drivers/xen/xen-pciback/pciback_ops.c index 97f5d264c31e..37c1f825f513 100644 --- a/drivers/xen/xen-pciback/pciback_ops.c +++ b/drivers/xen/xen-pciback/pciback_ops.c @@ -135,7 +135,6 @@ int xen_pcibk_enable_msi(struct xen_pcibk_device *pdev, struct pci_dev *dev, struct xen_pci_op *op) { struct xen_pcibk_dev_data *dev_data; - int otherend = pdev->xdev->otherend_id; int status; if (unlikely(verbose_request)) @@ -144,8 +143,9 @@ int xen_pcibk_enable_msi(struct xen_pcibk_device *pdev, status = pci_enable_msi(dev); if (status) { - printk(KERN_ERR "error enable msi for guest %x status %x\n", - otherend, status); + pr_warn_ratelimited(DRV_NAME ": %s: error enabling MSI for guest %u: err %d\n", + pci_name(dev), pdev->xdev->otherend_id, + status); op->value = 0; return XEN_PCI_ERR_op_failed; } @@ -223,10 +223,10 @@ int xen_pcibk_enable_msix(struct xen_pcibk_device *pdev, pci_name(dev), i, op->msix_entries[i].vector); } - } else { - printk(KERN_WARNING DRV_NAME ": %s: failed to enable MSI-X: err %d!\n", - pci_name(dev), result); - } + } else + pr_warn_ratelimited(DRV_NAME ": %s: error enabling MSI-X for guest %u: err %d!\n", + pci_name(dev), pdev->xdev->otherend_id, + result); kfree(entries); op->value = result; diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index a8b8adc05070..5a3327b8f90d 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -4534,7 +4534,7 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes) unsigned nr_extents = 0; int extra_reserve = 0; enum btrfs_reserve_flush_enum flush = BTRFS_RESERVE_FLUSH_ALL; - int ret; + int ret = 0; bool delalloc_lock = true; /* If we are a free space inode we need to not flush since we will be in @@ -4579,20 +4579,18 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes) csum_bytes = BTRFS_I(inode)->csum_bytes; spin_unlock(&BTRFS_I(inode)->lock); - if (root->fs_info->quota_enabled) { + if (root->fs_info->quota_enabled) ret = btrfs_qgroup_reserve(root, num_bytes + nr_extents * root->leafsize); - if (ret) { - spin_lock(&BTRFS_I(inode)->lock); - calc_csum_metadata_size(inode, num_bytes, 0); - spin_unlock(&BTRFS_I(inode)->lock); - if (delalloc_lock) - mutex_unlock(&BTRFS_I(inode)->delalloc_mutex); - return ret; - } - } - ret = reserve_metadata_bytes(root, block_rsv, to_reserve, flush); + /* + * ret != 0 here means the qgroup reservation failed, we go straight to + * the shared error handling then. + */ + if (ret == 0) + ret = reserve_metadata_bytes(root, block_rsv, + to_reserve, flush); + if (ret) { u64 to_free = 0; unsigned dropped; diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c index 2e8cae63d247..fdb7a8db3b57 100644 --- a/fs/btrfs/extent_map.c +++ b/fs/btrfs/extent_map.c @@ -288,7 +288,8 @@ out: void clear_em_logging(struct extent_map_tree *tree, struct extent_map *em) { clear_bit(EXTENT_FLAG_LOGGING, &em->flags); - try_merge_map(tree, em); + if (em->in_tree) + try_merge_map(tree, em); } /** diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index f76b1fd160d4..aeb84469d2c4 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -293,15 +293,24 @@ static int __btrfs_run_defrag_inode(struct btrfs_fs_info *fs_info, struct btrfs_key key; struct btrfs_ioctl_defrag_range_args range; int num_defrag; + int index; + int ret; /* get the inode */ key.objectid = defrag->root; btrfs_set_key_type(&key, BTRFS_ROOT_ITEM_KEY); key.offset = (u64)-1; + + index = srcu_read_lock(&fs_info->subvol_srcu); + inode_root = btrfs_read_fs_root_no_name(fs_info, &key); if (IS_ERR(inode_root)) { - kmem_cache_free(btrfs_inode_defrag_cachep, defrag); - return PTR_ERR(inode_root); + ret = PTR_ERR(inode_root); + goto cleanup; + } + if (btrfs_root_refs(&inode_root->root_item) == 0) { + ret = -ENOENT; + goto cleanup; } key.objectid = defrag->ino; @@ -309,9 +318,10 @@ static int __btrfs_run_defrag_inode(struct btrfs_fs_info *fs_info, key.offset = 0; inode = btrfs_iget(fs_info->sb, &key, inode_root, NULL); if (IS_ERR(inode)) { - kmem_cache_free(btrfs_inode_defrag_cachep, defrag); - return PTR_ERR(inode); + ret = PTR_ERR(inode); + goto cleanup; } + srcu_read_unlock(&fs_info->subvol_srcu, index); /* do a chunk of defrag */ clear_bit(BTRFS_INODE_IN_DEFRAG, &BTRFS_I(inode)->runtime_flags); @@ -346,6 +356,10 @@ static int __btrfs_run_defrag_inode(struct btrfs_fs_info *fs_info, iput(inode); return 0; +cleanup: + srcu_read_unlock(&fs_info->subvol_srcu, index); + kmem_cache_free(btrfs_inode_defrag_cachep, defrag); + return ret; } /* @@ -1594,9 +1608,10 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb, if (err < 0 && num_written > 0) num_written = err; } -out: + if (sync) atomic_dec(&BTRFS_I(inode)->sync_writers); +out: sb_end_write(inode->i_sb); current->backing_dev_info = NULL; return num_written ? num_written : err; diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 5b22d45d3c6a..338f2597bf7f 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -515,7 +515,6 @@ static noinline int create_subvol(struct btrfs_root *root, BUG_ON(ret); - d_instantiate(dentry, btrfs_lookup_dentry(dir, dentry)); fail: if (async_transid) { *async_transid = trans->transid; @@ -525,6 +524,10 @@ fail: } if (err && !ret) ret = err; + + if (!ret) + d_instantiate(dentry, btrfs_lookup_dentry(dir, dentry)); + return ret; } diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index f10731297040..e5ed56729607 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -836,9 +836,16 @@ int btrfs_ordered_update_i_size(struct inode *inode, u64 offset, * if the disk i_size is already at the inode->i_size, or * this ordered extent is inside the disk i_size, we're done */ - if (disk_i_size == i_size || offset <= disk_i_size) { + if (disk_i_size == i_size) + goto out; + + /* + * We still need to update disk_i_size if outstanding_isize is greater + * than disk_i_size. + */ + if (offset <= disk_i_size && + (!ordered || ordered->outstanding_isize <= disk_i_size)) goto out; - } /* * walk backward from this ordered extent to disk_i_size. @@ -870,7 +877,7 @@ int btrfs_ordered_update_i_size(struct inode *inode, u64 offset, break; if (test->file_offset >= i_size) break; - if (test->file_offset >= disk_i_size) { + if (entry_end(test) > disk_i_size) { /* * we don't update disk_i_size now, so record this * undealt i_size. Or we will not know the real diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index bdbb94f245c9..67783e03d121 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c @@ -580,20 +580,29 @@ static int scrub_fixup_readpage(u64 inum, u64 offset, u64 root, void *fixup_ctx) int corrected = 0; struct btrfs_key key; struct inode *inode = NULL; + struct btrfs_fs_info *fs_info; u64 end = offset + PAGE_SIZE - 1; struct btrfs_root *local_root; + int srcu_index; key.objectid = root; key.type = BTRFS_ROOT_ITEM_KEY; key.offset = (u64)-1; - local_root = btrfs_read_fs_root_no_name(fixup->root->fs_info, &key); - if (IS_ERR(local_root)) + + fs_info = fixup->root->fs_info; + srcu_index = srcu_read_lock(&fs_info->subvol_srcu); + + local_root = btrfs_read_fs_root_no_name(fs_info, &key); + if (IS_ERR(local_root)) { + srcu_read_unlock(&fs_info->subvol_srcu, srcu_index); return PTR_ERR(local_root); + } key.type = BTRFS_INODE_ITEM_KEY; key.objectid = inum; key.offset = 0; - inode = btrfs_iget(fixup->root->fs_info->sb, &key, local_root, NULL); + inode = btrfs_iget(fs_info->sb, &key, local_root, NULL); + srcu_read_unlock(&fs_info->subvol_srcu, srcu_index); if (IS_ERR(inode)) return PTR_ERR(inode); @@ -606,7 +615,6 @@ static int scrub_fixup_readpage(u64 inum, u64 offset, u64 root, void *fixup_ctx) } if (PageUptodate(page)) { - struct btrfs_fs_info *fs_info; if (PageDirty(page)) { /* * we need to write the data to the defect sector. the @@ -3180,18 +3188,25 @@ static int copy_nocow_pages_for_inode(u64 inum, u64 offset, u64 root, void *ctx) u64 physical_for_dev_replace; u64 len; struct btrfs_fs_info *fs_info = nocow_ctx->sctx->dev_root->fs_info; + int srcu_index; key.objectid = root; key.type = BTRFS_ROOT_ITEM_KEY; key.offset = (u64)-1; + + srcu_index = srcu_read_lock(&fs_info->subvol_srcu); + local_root = btrfs_read_fs_root_no_name(fs_info, &key); - if (IS_ERR(local_root)) + if (IS_ERR(local_root)) { + srcu_read_unlock(&fs_info->subvol_srcu, srcu_index); return PTR_ERR(local_root); + } key.type = BTRFS_INODE_ITEM_KEY; key.objectid = inum; key.offset = 0; inode = btrfs_iget(fs_info->sb, &key, local_root, NULL); + srcu_read_unlock(&fs_info->subvol_srcu, srcu_index); if (IS_ERR(inode)) return PTR_ERR(inode); diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index f15494699f3b..fc03aa60b684 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -333,12 +333,14 @@ start_transaction(struct btrfs_root *root, u64 num_items, int type, &root->fs_info->trans_block_rsv, num_bytes, flush); if (ret) - return ERR_PTR(ret); + goto reserve_fail; } again: h = kmem_cache_alloc(btrfs_trans_handle_cachep, GFP_NOFS); - if (!h) - return ERR_PTR(-ENOMEM); + if (!h) { + ret = -ENOMEM; + goto alloc_fail; + } /* * If we are JOIN_NOLOCK we're already committing a transaction and @@ -365,11 +367,7 @@ again: if (ret < 0) { /* We must get the transaction if we are JOIN_NOLOCK. */ BUG_ON(type == TRANS_JOIN_NOLOCK); - - if (type < TRANS_JOIN_NOLOCK) - sb_end_intwrite(root->fs_info->sb); - kmem_cache_free(btrfs_trans_handle_cachep, h); - return ERR_PTR(ret); + goto join_fail; } cur_trans = root->fs_info->running_transaction; @@ -410,6 +408,19 @@ got_it: if (!current->journal_info && type != TRANS_USERSPACE) current->journal_info = h; return h; + +join_fail: + if (type < TRANS_JOIN_NOLOCK) + sb_end_intwrite(root->fs_info->sb); + kmem_cache_free(btrfs_trans_handle_cachep, h); +alloc_fail: + if (num_bytes) + btrfs_block_rsv_release(root, &root->fs_info->trans_block_rsv, + num_bytes); +reserve_fail: + if (qgroup_reserved) + btrfs_qgroup_free(root, qgroup_reserved); + return ERR_PTR(ret); } struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root, diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 15f6efdf6463..5cbb7f4b1672 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1556,7 +1556,8 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) ret = 0; /* Notify udev that device has changed */ - btrfs_kobject_uevent(bdev, KOBJ_CHANGE); + if (bdev) + btrfs_kobject_uevent(bdev, KOBJ_CHANGE); error_brelse: brelse(bh); diff --git a/fs/dlm/user.c b/fs/dlm/user.c index 7ff49852b0cb..911649a47dd5 100644 --- a/fs/dlm/user.c +++ b/fs/dlm/user.c @@ -503,11 +503,11 @@ static ssize_t device_write(struct file *file, const char __user *buf, #endif return -EINVAL; -#ifdef CONFIG_COMPAT - if (count > sizeof(struct dlm_write_request32) + DLM_RESNAME_MAXLEN) -#else + /* + * can't compare against COMPAT/dlm_write_request32 because + * we don't yet know if is64bit is zero + */ if (count > sizeof(struct dlm_write_request) + DLM_RESNAME_MAXLEN) -#endif return -EINVAL; kbuf = kzalloc(count + 1, GFP_NOFS); diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c index fdb180769485..f3859354e41a 100644 --- a/fs/nilfs2/ioctl.c +++ b/fs/nilfs2/ioctl.c @@ -664,8 +664,11 @@ static int nilfs_ioctl_clean_segments(struct inode *inode, struct file *filp, if (ret < 0) printk(KERN_ERR "NILFS: GC failed during preparation: " "cannot read source blocks: err=%d\n", ret); - else + else { + if (nilfs_sb_need_update(nilfs)) + set_nilfs_discontinued(nilfs); ret = nilfs_clean_segments(inode->i_sb, argv, kbufs); + } nilfs_remove_all_gcinodes(nilfs); clear_nilfs_gc_running(nilfs); diff --git a/include/linux/llist.h b/include/linux/llist.h index a5199f6d0e82..d0ab98f73d38 100644 --- a/include/linux/llist.h +++ b/include/linux/llist.h @@ -125,6 +125,31 @@ static inline void init_llist_head(struct llist_head *list) (pos) = llist_entry((pos)->member.next, typeof(*(pos)), member)) /** + * llist_for_each_entry_safe - iterate safely against remove over some entries + * of lock-less list of given type. + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as a temporary storage. + * @node: the fist entry of deleted list entries. + * @member: the name of the llist_node with the struct. + * + * In general, some entries of the lock-less list can be traversed + * safely only after being removed from list, so start with an entry + * instead of list head. This variant allows removal of entries + * as we iterate. + * + * If being used on entries deleted from lock-less list directly, the + * traverse order is from the newest to the oldest added entry. If + * you want to traverse from the oldest to the newest, you must + * reverse the order by yourself before traversing. + */ +#define llist_for_each_entry_safe(pos, n, node, member) \ + for ((pos) = llist_entry((node), typeof(*(pos)), member), \ + (n) = (pos)->member.next; \ + &(pos)->member != NULL; \ + (pos) = llist_entry(n, typeof(*(pos)), member), \ + (n) = (&(pos)->member != NULL) ? (pos)->member.next : NULL) + +/** * llist_empty - tests whether a lock-less list is empty * @head: the list to test * diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 0108a56f814e..28bd5fa2ff2e 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -429,7 +429,7 @@ extern int memcg_limited_groups_array_size; * the slab_mutex must be held when looping through those caches */ #define for_each_memcg_cache_index(_idx) \ - for ((_idx) = 0; i < memcg_limited_groups_array_size; (_idx)++) + for ((_idx) = 0; (_idx) < memcg_limited_groups_array_size; (_idx)++) static inline bool memcg_kmem_enabled(void) { diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 8b1d1daaae16..ec3e2a2a6d77 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -62,6 +62,8 @@ #define ARIZONA_MAX_OUTPUT 6 +#define ARIZONA_MAX_AIF 3 + #define ARIZONA_HAP_ACT_ERM 0 #define ARIZONA_HAP_ACT_LRA 2 @@ -96,6 +98,13 @@ struct arizona_pdata { /** Pin state for GPIO pins */ int gpio_defaults[ARIZONA_MAX_GPIO]; + /** + * Maximum number of channels clocks will be generated for, + * useful for systems where and I2S bus with multiple data + * lines is mastered. + */ + int max_channels_clocked[ARIZONA_MAX_AIF]; + /** GPIO for mic detection polarity */ int micd_pol_gpio; diff --git a/include/linux/mmu_notifier.h b/include/linux/mmu_notifier.h index bc823c4c028b..deca87452528 100644 --- a/include/linux/mmu_notifier.h +++ b/include/linux/mmu_notifier.h @@ -151,7 +151,7 @@ struct mmu_notifier_ops { * Therefore notifier chains can only be traversed when either * * 1. mmap_sem is held. - * 2. One of the reverse map locks is held (i_mmap_mutex or anon_vma->mutex). + * 2. One of the reverse map locks is held (i_mmap_mutex or anon_vma->rwsem). * 3. No other concurrent thread can access the list (release) */ struct mmu_notifier { diff --git a/include/linux/regmap.h b/include/linux/regmap.h index b7e95bf942c9..f9b7fbe35ab1 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -235,14 +235,21 @@ struct regmap_range_cfg { unsigned int window_len; }; +struct regmap_async; + typedef int (*regmap_hw_write)(void *context, const void *data, size_t count); typedef int (*regmap_hw_gather_write)(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len); +typedef int (*regmap_hw_async_write)(void *context, + const void *reg, size_t reg_len, + const void *val, size_t val_len, + struct regmap_async *async); typedef int (*regmap_hw_read)(void *context, const void *reg_buf, size_t reg_size, void *val_buf, size_t val_size); +typedef struct regmap_async *(*regmap_hw_async_alloc)(void); typedef void (*regmap_hw_free_context)(void *context); /** @@ -255,8 +262,11 @@ typedef void (*regmap_hw_free_context)(void *context); * @write: Write operation. * @gather_write: Write operation with split register/value, return -ENOTSUPP * if not implemented on a given device. + * @async_write: Write operation which completes asynchronously, optional and + * must serialise with respect to non-async I/O. * @read: Read operation. Data is returned in the buffer used to transmit * data. + * @async_alloc: Allocate a regmap_async() structure. * @read_flag_mask: Mask to be set in the top byte of the register when doing * a read. * @reg_format_endian_default: Default endianness for formatted register @@ -265,13 +275,16 @@ typedef void (*regmap_hw_free_context)(void *context); * @val_format_endian_default: Default endianness for formatted register * values. Used when the regmap_config specifies DEFAULT. If this is * DEFAULT, BIG is assumed. + * @async_size: Size of struct used for async work. */ struct regmap_bus { bool fast_io; regmap_hw_write write; regmap_hw_gather_write gather_write; + regmap_hw_async_write async_write; regmap_hw_read read; regmap_hw_free_context free_context; + regmap_hw_async_alloc async_alloc; u8 read_flag_mask; enum regmap_endian reg_format_endian_default; enum regmap_endian val_format_endian_default; @@ -310,6 +323,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len); int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count); +int regmap_raw_write_async(struct regmap *map, unsigned int reg, + const void *val, size_t val_len); int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val); int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, size_t val_len); @@ -321,6 +336,7 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); int regmap_get_val_bytes(struct regmap *map); +int regmap_async_complete(struct regmap *map); int regcache_sync(struct regmap *map); int regcache_sync_region(struct regmap *map, unsigned int min, @@ -422,6 +438,13 @@ static inline int regmap_raw_write(struct regmap *map, unsigned int reg, return -EINVAL; } +static inline int regmap_raw_write_async(struct regmap *map, unsigned int reg, + const void *val, size_t val_len) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count) { @@ -500,6 +523,11 @@ static inline void regcache_mark_dirty(struct regmap *map) WARN_ONCE(1, "regmap API is disabled"); } +static inline void regmap_async_complete(struct regmap *map) +{ + WARN_ONCE(1, "regmap API is disabled"); +} + static inline int regmap_register_patch(struct regmap *map, const struct reg_default *regs, int num_regs) diff --git a/include/linux/usb.h b/include/linux/usb.h index 689b14b26c8d..4d22d0f6167a 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -357,6 +357,8 @@ struct usb_bus { int bandwidth_int_reqs; /* number of Interrupt requests */ int bandwidth_isoc_reqs; /* number of Isoc. requests */ + unsigned resuming_ports; /* bit array: resuming root-hub ports */ + #if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE) struct mon_bus *mon_bus; /* non-null when associated */ int monitored; /* non-zero when monitored */ diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 608050b2545f..0a78df5f6cfd 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -430,6 +430,9 @@ extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd); extern void usb_wakeup_notification(struct usb_device *hdev, unsigned int portnum); +extern void usb_hcd_start_port_resume(struct usb_bus *bus, int portnum); +extern void usb_hcd_end_port_resume(struct usb_bus *bus, int portnum); + /* The D0/D1 toggle bits ... USE WITH CAUTION (they're almost hcd-internal) */ #define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1) #define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << (ep))) diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index 5de7a220e986..0e5ac93bab10 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -33,6 +33,7 @@ struct usbnet { wait_queue_head_t *wait; struct mutex phy_mutex; unsigned char suspend_count; + unsigned char pkt_cnt, pkt_err; /* i/o info: pipes etc */ unsigned in, out; @@ -70,6 +71,7 @@ struct usbnet { # define EVENT_DEV_OPEN 7 # define EVENT_DEVICE_REPORT_IDLE 8 # define EVENT_NO_RUNTIME_PM 9 +# define EVENT_RX_KILL 10 }; static inline struct usb_driver *driver_of(struct usb_interface *intf) @@ -100,7 +102,6 @@ struct driver_info { #define FLAG_LINK_INTR 0x0800 /* updates link (carrier) status */ #define FLAG_POINTTOPOINT 0x1000 /* possibly use "usb%d" names */ -#define FLAG_NOARP 0x2000 /* device can't do ARP */ /* * Indicates to usbnet, that USB driver accumulates multiple IP packets. @@ -108,6 +109,7 @@ struct driver_info { */ #define FLAG_MULTI_PACKET 0x2000 #define FLAG_RX_ASSEMBLE 0x4000 /* rx packets may span >1 frames */ +#define FLAG_NOARP 0x8000 /* device can't do ARP */ /* init device ... can sleep, or cause probe() failure */ int (*bind)(struct usbnet *, struct usb_interface *); diff --git a/include/net/transp_v6.h b/include/net/transp_v6.h index 498433dd067d..938b7fd11204 100644 --- a/include/net/transp_v6.h +++ b/include/net/transp_v6.h @@ -34,17 +34,17 @@ extern int udpv6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); -extern int datagram_recv_ctl(struct sock *sk, - struct msghdr *msg, - struct sk_buff *skb); - -extern int datagram_send_ctl(struct net *net, - struct sock *sk, - struct msghdr *msg, - struct flowi6 *fl6, - struct ipv6_txoptions *opt, - int *hlimit, int *tclass, - int *dontfrag); +extern int ip6_datagram_recv_ctl(struct sock *sk, + struct msghdr *msg, + struct sk_buff *skb); + +extern int ip6_datagram_send_ctl(struct net *net, + struct sock *sk, + struct msghdr *msg, + struct flowi6 *fl6, + struct ipv6_txoptions *opt, + int *hlimit, int *tclass, + int *dontfrag); #define LOOPBACK4_IPV6 cpu_to_be32(0x7f000006) diff --git a/include/sound/cs4271.h b/include/sound/cs4271.h index dd8c48d14ed9..70f45355acaa 100644 --- a/include/sound/cs4271.h +++ b/include/sound/cs4271.h @@ -20,6 +20,21 @@ struct cs4271_platform_data { int gpio_nreset; /* GPIO driving Reset pin, if any */ bool amutec_eq_bmutec; /* flag to enable AMUTEC=BMUTEC */ + + /* + * The CS4271 requires its LRCLK and MCLK to be stable before its RESET + * line is de-asserted. That also means that clocks cannot be changed + * without putting the chip back into hardware reset, which also requires + * a complete re-initialization of all registers. + * + * One (undocumented) workaround is to assert and de-assert the PDN bit + * in the MODE2 register. This workaround can be enabled with the + * following flag. + * + * Note that this is not needed in case the clocks are stable + * throughout the entire runtime of the codec. + */ + bool enable_soft_reset; }; #endif /* __CS4271_H */ diff --git a/include/sound/max98090.h b/include/sound/max98090.h new file mode 100755 index 000000000000..95efb13f8478 --- /dev/null +++ b/include/sound/max98090.h @@ -0,0 +1,29 @@ +/* + * Platform data for MAX98090 + * + * Copyright 2011-2012 Maxim Integrated Products + * + * 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. + * + */ + +#ifndef __SOUND_MAX98090_PDATA_H__ +#define __SOUND_MAX98090_PDATA_H__ + +/* codec platform data */ +struct max98090_pdata { + + /* Analog/digital microphone configuration: + * 0 = analog microphone input (normal setting) + * 1 = digital microphone input + */ + unsigned int digmic_left_mode:1; + unsigned int digmic_right_mode:1; + unsigned int digmic_3_mode:1; + unsigned int digmic_4_mode:1; +}; + +#endif diff --git a/include/sound/saif.h b/include/sound/saif.h deleted file mode 100644 index f22f3e16edf4..000000000000 --- a/include/sound/saif.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __SOUND_SAIF_H__ -#define __SOUND_SAIF_H__ - -struct mxs_saif_platform_data { - bool master_mode; /* if true use master mode */ - int master_id; /* id of the master if in slave mode */ -}; -#endif diff --git a/include/sound/sh_fsi.h b/include/sound/sh_fsi.h index cc1c919c6436..7a9710b4b799 100644 --- a/include/sound/sh_fsi.h +++ b/include/sound/sh_fsi.h @@ -11,82 +11,20 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ - -#define FSI_PORT_A 0 -#define FSI_PORT_B 1 - #include <linux/clk.h> #include <sound/soc.h> /* - * flags format - * - * 0x00000CBA - * - * A: inversion - * B: format mode - * C: chip specific - * D: clock selecter if master mode + * flags */ - -/* A: clock inversion */ -#define SH_FSI_INVERSION_MASK 0x0000000F -#define SH_FSI_LRM_INV (1 << 0) -#define SH_FSI_BRM_INV (1 << 1) -#define SH_FSI_LRS_INV (1 << 2) -#define SH_FSI_BRS_INV (1 << 3) - -/* B: format mode */ -#define SH_FSI_FMT_MASK 0x000000F0 -#define SH_FSI_FMT_DAI (0 << 4) -#define SH_FSI_FMT_SPDIF (1 << 4) - -/* C: chip specific */ -#define SH_FSI_OPTION_MASK 0x00000F00 -#define SH_FSI_ENABLE_STREAM_MODE (1 << 8) /* for 16bit data */ - -/* D: clock selecter if master mode */ -#define SH_FSI_CLK_MASK 0x0000F000 -#define SH_FSI_CLK_EXTERNAL (0 << 12) -#define SH_FSI_CLK_CPG (1 << 12) /* FSIxCK + FSI-DIV */ - -/* - * set_rate return value - * - * see ACKMD/BPFMD on - * ACK_MD (FSI2) - * CKG1 (FSI) - * - * err : return value < 0 - * no change : return value == 0 - * change xMD : return value > 0 - * - * 0x-00000AB - * - * A: ACKMD value - * B: BPFMD value - */ - -#define SH_FSI_ACKMD_MASK (0xF << 0) -#define SH_FSI_ACKMD_512 (1 << 0) -#define SH_FSI_ACKMD_256 (2 << 0) -#define SH_FSI_ACKMD_128 (3 << 0) -#define SH_FSI_ACKMD_64 (4 << 0) -#define SH_FSI_ACKMD_32 (5 << 0) - -#define SH_FSI_BPFMD_MASK (0xF << 4) -#define SH_FSI_BPFMD_512 (1 << 4) -#define SH_FSI_BPFMD_256 (2 << 4) -#define SH_FSI_BPFMD_128 (3 << 4) -#define SH_FSI_BPFMD_64 (4 << 4) -#define SH_FSI_BPFMD_32 (5 << 4) -#define SH_FSI_BPFMD_16 (6 << 4) +#define SH_FSI_FMT_SPDIF (1 << 0) /* spdif for HDMI */ +#define SH_FSI_ENABLE_STREAM_MODE (1 << 1) /* for 16bit data */ +#define SH_FSI_CLK_CPG (1 << 2) /* FSIxCK + FSI-DIV */ struct sh_fsi_port_info { unsigned long flags; int tx_id; int rx_id; - int (*set_rate)(struct device *dev, int rate, int enable); }; struct sh_fsi_platform_info { diff --git a/include/sound/simple_card.h b/include/sound/simple_card.h index 4b62b8dc6a4f..6c74527d4926 100644 --- a/include/sound/simple_card.h +++ b/include/sound/simple_card.h @@ -14,21 +14,21 @@ #include <sound/soc.h> -struct asoc_simple_dai_init_info { +struct asoc_simple_dai { + const char *name; unsigned int fmt; - unsigned int cpu_daifmt; - unsigned int codec_daifmt; unsigned int sysclk; }; struct asoc_simple_card_info { const char *name; const char *card; - const char *cpu_dai; const char *codec; const char *platform; - const char *codec_dai; - struct asoc_simple_dai_init_info *init; /* for snd_link.init */ + + unsigned int daifmt; + struct asoc_simple_dai cpu_dai; + struct asoc_simple_dai codec_dai; /* used in simple-card.c */ struct snd_soc_dai_link snd_link; diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h index 3953cea0ecfb..3d84808952b9 100644 --- a/include/sound/soc-dai.h +++ b/include/sound/soc-dai.h @@ -45,7 +45,7 @@ struct snd_compr_stream; * sending or receiving PCM data in a frame. This can be used to save power. */ #define SND_SOC_DAIFMT_CONT (1 << 4) /* continuous clock */ -#define SND_SOC_DAIFMT_GATED (2 << 4) /* clock is gated */ +#define SND_SOC_DAIFMT_GATED (0 << 4) /* clock is gated */ /* * DAI hardware signal inversions. @@ -53,7 +53,7 @@ struct snd_compr_stream; * Specifies whether the DAI can also support inverted clocks for the specified * format. */ -#define SND_SOC_DAIFMT_NB_NF (1 << 8) /* normal bit clock + frame */ +#define SND_SOC_DAIFMT_NB_NF (0 << 8) /* normal bit clock + frame */ #define SND_SOC_DAIFMT_NB_IF (2 << 8) /* normal BCLK + inv FRM */ #define SND_SOC_DAIFMT_IB_NF (3 << 8) /* invert BCLK + nor FRM */ #define SND_SOC_DAIFMT_IB_IF (4 << 8) /* invert BCLK + FRM */ @@ -126,7 +126,8 @@ int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai, int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate); /* Digital Audio Interface mute */ -int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute); +int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, + int direction); struct snd_soc_dai_ops { /* @@ -157,6 +158,7 @@ struct snd_soc_dai_ops { * Called by soc-core to minimise any pops. */ int (*digital_mute)(struct snd_soc_dai *dai, int mute); + int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream); /* * ALSA PCM audio operations - all optional. diff --git a/include/sound/soc.h b/include/sound/soc.h index bc56738cb109..a6a059ca3874 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -906,8 +906,8 @@ struct snd_soc_dai_link { struct snd_pcm_hw_params *params); /* machine stream operations */ - struct snd_soc_ops *ops; - struct snd_soc_compr_ops *compr_ops; + const struct snd_soc_ops *ops; + const struct snd_soc_compr_ops *compr_ops; }; struct snd_soc_codec_conf { @@ -1171,6 +1171,8 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card, const char *propname); int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname); +unsigned int snd_soc_of_parse_daifmt(struct device_node *np, + const char *prefix); #include <sound/soc-dai.h> diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index 50598472dc41..f738e25377ff 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -152,6 +152,12 @@ #define USB_INTRF_FUNC_SUSPEND_LP (1 << (8 + 0)) #define USB_INTRF_FUNC_SUSPEND_RW (1 << (8 + 1)) +/* + * Interface status, Figure 9-5 USB 3.0 spec + */ +#define USB_INTRF_STAT_FUNC_RW_CAP 1 +#define USB_INTRF_STAT_FUNC_RW 2 + #define USB_ENDPOINT_HALT 0 /* IN/OUT will STALL */ /* Bit array elements as returned by the USB_REQ_GET_STATUS request. */ diff --git a/kernel/events/core.c b/kernel/events/core.c index 301079d06f24..7b6646a8c067 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -908,6 +908,15 @@ list_add_event(struct perf_event *event, struct perf_event_context *ctx) } /* + * Initialize event state based on the perf_event_attr::disabled. + */ +static inline void perf_event__state_init(struct perf_event *event) +{ + event->state = event->attr.disabled ? PERF_EVENT_STATE_OFF : + PERF_EVENT_STATE_INACTIVE; +} + +/* * Called at perf_event creation and when events are attached/detached from a * group. */ @@ -6179,8 +6188,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, event->overflow_handler = overflow_handler; event->overflow_handler_context = context; - if (attr->disabled) - event->state = PERF_EVENT_STATE_OFF; + perf_event__state_init(event); pmu = NULL; @@ -6609,9 +6617,17 @@ SYSCALL_DEFINE5(perf_event_open, mutex_lock(&gctx->mutex); perf_remove_from_context(group_leader); + + /* + * Removing from the context ends up with disabled + * event. What we want here is event in the initial + * startup state, ready to be add into new context. + */ + perf_event__state_init(group_leader); list_for_each_entry(sibling, &group_leader->sibling_list, group_entry) { perf_remove_from_context(sibling); + perf_event__state_init(sibling); put_ctx(gctx); } mutex_unlock(&gctx->mutex); diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index f6e5ec2932b4..c1cc7e17ff9d 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h @@ -40,8 +40,7 @@ #ifdef CONFIG_RCU_NOCB_CPU static cpumask_var_t rcu_nocb_mask; /* CPUs to have callbacks offloaded. */ static bool have_rcu_nocb_mask; /* Was rcu_nocb_mask allocated? */ -static bool rcu_nocb_poll; /* Offload kthread are to poll. */ -module_param(rcu_nocb_poll, bool, 0444); +static bool __read_mostly rcu_nocb_poll; /* Offload kthread are to poll. */ static char __initdata nocb_buf[NR_CPUS * 5]; #endif /* #ifdef CONFIG_RCU_NOCB_CPU */ @@ -2159,6 +2158,13 @@ static int __init rcu_nocb_setup(char *str) } __setup("rcu_nocbs=", rcu_nocb_setup); +static int __init parse_rcu_nocb_poll(char *arg) +{ + rcu_nocb_poll = 1; + return 0; +} +early_param("rcu_nocb_poll", parse_rcu_nocb_poll); + /* Is the specified CPU a no-CPUs CPU? */ static bool is_nocb_cpu(int cpu) { @@ -2366,10 +2372,11 @@ static int rcu_nocb_kthread(void *arg) for (;;) { /* If not polling, wait for next batch of callbacks. */ if (!rcu_nocb_poll) - wait_event(rdp->nocb_wq, rdp->nocb_head); + wait_event_interruptible(rdp->nocb_wq, rdp->nocb_head); list = ACCESS_ONCE(rdp->nocb_head); if (!list) { schedule_timeout_interruptible(1); + flush_signals(current); continue; } diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c index 2cd3c1b4e582..7ae4c4c5420e 100644 --- a/kernel/sched/debug.c +++ b/kernel/sched/debug.c @@ -222,8 +222,8 @@ void print_cfs_rq(struct seq_file *m, int cpu, struct cfs_rq *cfs_rq) cfs_rq->runnable_load_avg); SEQ_printf(m, " .%-30s: %lld\n", "blocked_load_avg", cfs_rq->blocked_load_avg); - SEQ_printf(m, " .%-30s: %ld\n", "tg_load_avg", - atomic64_read(&cfs_rq->tg->load_avg)); + SEQ_printf(m, " .%-30s: %lld\n", "tg_load_avg", + (unsigned long long)atomic64_read(&cfs_rq->tg->load_avg)); SEQ_printf(m, " .%-30s: %lld\n", "tg_load_contrib", cfs_rq->tg_load_contrib); SEQ_printf(m, " .%-30s: %d\n", "tg_runnable_contrib", diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 5eea8707234a..81fa53643409 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -2663,7 +2663,7 @@ static void destroy_cfs_bandwidth(struct cfs_bandwidth *cfs_b) hrtimer_cancel(&cfs_b->slack_timer); } -static void unthrottle_offline_cfs_rqs(struct rq *rq) +static void __maybe_unused unthrottle_offline_cfs_rqs(struct rq *rq) { struct cfs_rq *cfs_rq; diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c index 418feb01344e..4f02b2847357 100644 --- a/kernel/sched/rt.c +++ b/kernel/sched/rt.c @@ -566,7 +566,7 @@ static inline struct rt_bandwidth *sched_rt_bandwidth(struct rt_rq *rt_rq) static int do_balance_runtime(struct rt_rq *rt_rq) { struct rt_bandwidth *rt_b = sched_rt_bandwidth(rt_rq); - struct root_domain *rd = cpu_rq(smp_processor_id())->rd; + struct root_domain *rd = rq_of_rt_rq(rt_rq)->rd; int i, weight, more = 0; u64 rt_period; diff --git a/lib/digsig.c b/lib/digsig.c index 8c0e62975c88..dc2be7ed1765 100644 --- a/lib/digsig.c +++ b/lib/digsig.c @@ -162,6 +162,8 @@ static int digsig_verify_rsa(struct key *key, memset(out1, 0, head); memcpy(out1 + head, p, l); + kfree(p); + err = pkcs_1_v1_5_decode_emsa(out1, len, mblen, out2, &len); if (err) goto err; diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 6001ee6347a9..b5783d81eda9 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1257,6 +1257,10 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct *vma, if (flags & FOLL_WRITE && !pmd_write(*pmd)) goto out; + /* Avoid dumping huge zero page */ + if ((flags & FOLL_DUMP) && is_huge_zero_pmd(*pmd)) + return ERR_PTR(-EFAULT); + page = pmd_page(*pmd); VM_BUG_ON(!PageHead(page)); if (flags & FOLL_TOUCH) { diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 4f3ea0b1e57c..546db81820e4 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -3033,6 +3033,7 @@ unsigned long hugetlb_change_protection(struct vm_area_struct *vma, if (!huge_pte_none(huge_ptep_get(ptep))) { pte = huge_ptep_get_and_clear(mm, address, ptep); pte = pte_mkhuge(pte_modify(pte, newprot)); + pte = arch_make_huge_pte(pte, vma, NULL, 0); set_huge_pte_at(mm, address, ptep, pte); pages++; } diff --git a/mm/migrate.c b/mm/migrate.c index c38778610aa8..2fd8b4af4744 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -160,8 +160,10 @@ static int remove_migration_pte(struct page *new, struct vm_area_struct *vma, if (is_write_migration_entry(entry)) pte = pte_mkwrite(pte); #ifdef CONFIG_HUGETLB_PAGE - if (PageHuge(new)) + if (PageHuge(new)) { pte = pte_mkhuge(pte); + pte = arch_make_huge_pte(pte, vma, new, 0); + } #endif flush_cache_page(vma, addr, pte_pfn(pte)); set_pte_at(mm, addr, ptep, pte); diff --git a/mm/mmap.c b/mm/mmap.c index 35730ee9d515..d1e4124f3d0e 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2943,7 +2943,7 @@ static void vm_lock_mapping(struct mm_struct *mm, struct address_space *mapping) * vma in this mm is backed by the same anon_vma or address_space. * * We can take all the locks in random order because the VM code - * taking i_mmap_mutex or anon_vma->mutex outside the mmap_sem never + * taking i_mmap_mutex or anon_vma->rwsem outside the mmap_sem never * takes more than one of them in a row. Secondly we're protected * against a concurrent mm_take_all_locks() by the mm_all_locks_mutex. * diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 25bfce0666eb..4925a02ae7e4 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -249,12 +249,12 @@ static void hci_conn_disconnect(struct hci_conn *conn) __u8 reason = hci_proto_disconn_ind(conn); switch (conn->type) { - case ACL_LINK: - hci_acl_disconn(conn, reason); - break; case AMP_LINK: hci_amp_disconn(conn, reason); break; + default: + hci_acl_disconn(conn, reason); + break; } } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 68a9587c9694..5abefb12891d 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -859,6 +859,19 @@ int smp_sig_channel(struct l2cap_conn *conn, struct sk_buff *skb) skb_pull(skb, sizeof(code)); + /* + * The SMP context must be initialized for all other PDUs except + * pairing and security requests. If we get any other PDU when + * not initialized simply disconnect (done if this function + * returns an error). + */ + if (code != SMP_CMD_PAIRING_REQ && code != SMP_CMD_SECURITY_REQ && + !conn->smp_chan) { + BT_ERR("Unexpected SMP command 0x%02x. Disconnecting.", code); + kfree_skb(skb); + return -ENOTSUPP; + } + switch (code) { case SMP_CMD_PAIRING_REQ: reason = smp_cmd_pairing_req(conn, skb); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index b29dacf900f9..e6e1cbe863f5 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -1781,10 +1781,13 @@ static ssize_t pktgen_thread_write(struct file *file, return -EFAULT; i += len; mutex_lock(&pktgen_thread_lock); - pktgen_add_device(t, f); + ret = pktgen_add_device(t, f); mutex_unlock(&pktgen_thread_lock); - ret = count; - sprintf(pg_result, "OK: add_device=%s", f); + if (!ret) { + ret = count; + sprintf(pg_result, "OK: add_device=%s", f); + } else + sprintf(pg_result, "ERROR: can not add device %s", f); goto out; } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index a9a2ae3e2213..32443ebc3e89 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -683,7 +683,7 @@ static void __copy_skb_header(struct sk_buff *new, const struct sk_buff *old) new->network_header = old->network_header; new->mac_header = old->mac_header; new->inner_transport_header = old->inner_transport_header; - new->inner_network_header = old->inner_transport_header; + new->inner_network_header = old->inner_network_header; skb_dst_copy(new, old); new->rxhash = old->rxhash; new->ooo_okay = old->ooo_okay; diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index 291f2ed7cc31..cdf2e707bb10 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -310,6 +310,12 @@ void tcp_slow_start(struct tcp_sock *tp) { int cnt; /* increase in packets */ unsigned int delta = 0; + u32 snd_cwnd = tp->snd_cwnd; + + if (unlikely(!snd_cwnd)) { + pr_err_once("snd_cwnd is nul, please report this bug.\n"); + snd_cwnd = 1U; + } /* RFC3465: ABC Slow start * Increase only after a full MSS of bytes is acked @@ -324,7 +330,7 @@ void tcp_slow_start(struct tcp_sock *tp) if (sysctl_tcp_max_ssthresh > 0 && tp->snd_cwnd > sysctl_tcp_max_ssthresh) cnt = sysctl_tcp_max_ssthresh >> 1; /* limited slow start */ else - cnt = tp->snd_cwnd; /* exponential increase */ + cnt = snd_cwnd; /* exponential increase */ /* RFC3465: ABC * We MAY increase by 2 if discovered delayed ack @@ -334,11 +340,11 @@ void tcp_slow_start(struct tcp_sock *tp) tp->bytes_acked = 0; tp->snd_cwnd_cnt += cnt; - while (tp->snd_cwnd_cnt >= tp->snd_cwnd) { - tp->snd_cwnd_cnt -= tp->snd_cwnd; + while (tp->snd_cwnd_cnt >= snd_cwnd) { + tp->snd_cwnd_cnt -= snd_cwnd; delta++; } - tp->snd_cwnd = min(tp->snd_cwnd + delta, tp->snd_cwnd_clamp); + tp->snd_cwnd = min(snd_cwnd + delta, tp->snd_cwnd_clamp); } EXPORT_SYMBOL_GPL(tcp_slow_start); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 18f97ca76b00..ad70a962c20e 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3504,6 +3504,11 @@ static bool tcp_process_frto(struct sock *sk, int flag) } } else { if (!(flag & FLAG_DATA_ACKED) && (tp->frto_counter == 1)) { + if (!tcp_packets_in_flight(tp)) { + tcp_enter_frto_loss(sk, 2, flag); + return true; + } + /* Prevent sending of new data. */ tp->snd_cwnd = min(tp->snd_cwnd, tcp_packets_in_flight(tp)); @@ -5649,8 +5654,7 @@ static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, * the remote receives only the retransmitted (regular) SYNs: either * the original SYN-data or the corresponding SYN-ACK is lost. */ - syn_drop = (cookie->len <= 0 && data && - inet_csk(sk)->icsk_retransmits); + syn_drop = (cookie->len <= 0 && data && tp->total_retrans); tcp_fastopen_cache_set(sk, mss, cookie, syn_drop); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 70b09ef2463b..eadb693eef55 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -496,6 +496,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) * errors returned from accept(). */ inet_csk_reqsk_queue_drop(sk, req, prev); + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); goto out; case TCP_SYN_SENT: @@ -1500,8 +1501,10 @@ int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) * clogging syn queue with openreqs with exponentially increasing * timeout. */ - if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) + if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) { + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); goto drop; + } req = inet_reqsk_alloc(&tcp_request_sock_ops); if (!req) @@ -1666,6 +1669,7 @@ drop_and_release: drop_and_free: reqsk_free(req); drop: + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); return 0; } EXPORT_SYMBOL(tcp_v4_conn_request); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 420e56326384..1b5d8cb9b123 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -1660,6 +1660,7 @@ static int addrconf_ifid_eui64(u8 *eui, struct net_device *dev) if (dev->addr_len != IEEE802154_ADDR_LEN) return -1; memcpy(eui, dev->dev_addr, 8); + eui[0] ^= 2; return 0; } diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 8edf2601065a..7a778b9a7b85 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -380,7 +380,7 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len) if (skb->protocol == htons(ETH_P_IPV6)) { sin->sin6_addr = ipv6_hdr(skb)->saddr; if (np->rxopt.all) - datagram_recv_ctl(sk, msg, skb); + ip6_datagram_recv_ctl(sk, msg, skb); if (ipv6_addr_type(&sin->sin6_addr) & IPV6_ADDR_LINKLOCAL) sin->sin6_scope_id = IP6CB(skb)->iif; } else { @@ -468,7 +468,8 @@ out: } -int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) +int ip6_datagram_recv_ctl(struct sock *sk, struct msghdr *msg, + struct sk_buff *skb) { struct ipv6_pinfo *np = inet6_sk(sk); struct inet6_skb_parm *opt = IP6CB(skb); @@ -597,11 +598,12 @@ int datagram_recv_ctl(struct sock *sk, struct msghdr *msg, struct sk_buff *skb) } return 0; } +EXPORT_SYMBOL_GPL(ip6_datagram_recv_ctl); -int datagram_send_ctl(struct net *net, struct sock *sk, - struct msghdr *msg, struct flowi6 *fl6, - struct ipv6_txoptions *opt, - int *hlimit, int *tclass, int *dontfrag) +int ip6_datagram_send_ctl(struct net *net, struct sock *sk, + struct msghdr *msg, struct flowi6 *fl6, + struct ipv6_txoptions *opt, + int *hlimit, int *tclass, int *dontfrag) { struct in6_pktinfo *src_info; struct cmsghdr *cmsg; @@ -871,4 +873,4 @@ int datagram_send_ctl(struct net *net, struct sock *sk, exit_f: return err; } -EXPORT_SYMBOL_GPL(datagram_send_ctl); +EXPORT_SYMBOL_GPL(ip6_datagram_send_ctl); diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c index 29124b7a04c8..d6de4b447250 100644 --- a/net/ipv6/ip6_flowlabel.c +++ b/net/ipv6/ip6_flowlabel.c @@ -365,8 +365,8 @@ fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq, msg.msg_control = (void*)(fl->opt+1); memset(&flowi6, 0, sizeof(flowi6)); - err = datagram_send_ctl(net, sk, &msg, &flowi6, fl->opt, &junk, - &junk, &junk); + err = ip6_datagram_send_ctl(net, sk, &msg, &flowi6, fl->opt, + &junk, &junk, &junk); if (err) goto done; err = -EINVAL; diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index c727e4712751..131dd097736d 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -960,7 +960,7 @@ static netdev_tx_t ip6gre_tunnel_xmit(struct sk_buff *skb, int ret; if (!ip6_tnl_xmit_ctl(t)) - return -1; + goto tx_err; switch (skb->protocol) { case htons(ETH_P_IP): diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index ee94d31c9d4d..d1e2e8ef29c5 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c @@ -476,8 +476,8 @@ sticky_done: msg.msg_controllen = optlen; msg.msg_control = (void*)(opt+1); - retv = datagram_send_ctl(net, sk, &msg, &fl6, opt, &junk, &junk, - &junk); + retv = ip6_datagram_send_ctl(net, sk, &msg, &fl6, opt, &junk, + &junk, &junk); if (retv) goto done; update: @@ -1002,7 +1002,7 @@ static int do_ipv6_getsockopt(struct sock *sk, int level, int optname, release_sock(sk); if (skb) { - int err = datagram_recv_ctl(sk, &msg, skb); + int err = ip6_datagram_recv_ctl(sk, &msg, skb); kfree_skb(skb); if (err) return err; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 6cd29b1e8b92..70fa81449997 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -507,7 +507,7 @@ static int rawv6_recvmsg(struct kiocb *iocb, struct sock *sk, sock_recv_ts_and_drops(msg, sk, skb); if (np->rxopt.all) - datagram_recv_ctl(sk, msg, skb); + ip6_datagram_recv_ctl(sk, msg, skb); err = copied; if (flags & MSG_TRUNC) @@ -822,8 +822,8 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, memset(opt, 0, sizeof(struct ipv6_txoptions)); opt->tot_len = sizeof(struct ipv6_txoptions); - err = datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt, - &hlimit, &tclass, &dontfrag); + err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt, + &hlimit, &tclass, &dontfrag); if (err < 0) { fl6_sock_release(flowlabel); return err; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index e229a3bc345d..363d8b7772e8 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -928,7 +928,7 @@ restart: dst_hold(&rt->dst); read_unlock_bh(&table->tb6_lock); - if (!rt->n && !(rt->rt6i_flags & RTF_NONEXTHOP)) + if (!rt->n && !(rt->rt6i_flags & (RTF_NONEXTHOP | RTF_LOCAL))) nrt = rt6_alloc_cow(rt, &fl6->daddr, &fl6->saddr); else if (!(rt->dst.flags & DST_HOST)) nrt = rt6_alloc_clone(rt, &fl6->daddr); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 93825dd3a7c0..4f43537197ef 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -423,6 +423,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, } inet_csk_reqsk_queue_drop(sk, req, prev); + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); goto out; case TCP_SYN_SENT: @@ -958,8 +959,10 @@ static int tcp_v6_conn_request(struct sock *sk, struct sk_buff *skb) goto drop; } - if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) + if (sk_acceptq_is_full(sk) && inet_csk_reqsk_queue_young(sk) > 1) { + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENOVERFLOWS); goto drop; + } req = inet6_reqsk_alloc(&tcp6_request_sock_ops); if (req == NULL) @@ -1108,6 +1111,7 @@ drop_and_release: drop_and_free: reqsk_free(req); drop: + NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_LISTENDROPS); return 0; /* don't send reset */ } diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index dfaa29b8b293..fb083295ff0b 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -443,7 +443,7 @@ try_again: ip_cmsg_recv(msg, skb); } else { if (np->rxopt.all) - datagram_recv_ctl(sk, msg, skb); + ip6_datagram_recv_ctl(sk, msg, skb); } err = copied; @@ -1153,8 +1153,8 @@ do_udp_sendmsg: memset(opt, 0, sizeof(struct ipv6_txoptions)); opt->tot_len = sizeof(*opt); - err = datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt, - &hlimit, &tclass, &dontfrag); + err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt, + &hlimit, &tclass, &dontfrag); if (err < 0) { fl6_sock_release(flowlabel); return err; diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 1a9f3723c13c..2ac884d0e89b 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -168,6 +168,51 @@ l2tp_session_id_hash_2(struct l2tp_net *pn, u32 session_id) } +/* Lookup the tunnel socket, possibly involving the fs code if the socket is + * owned by userspace. A struct sock returned from this function must be + * released using l2tp_tunnel_sock_put once you're done with it. + */ +struct sock *l2tp_tunnel_sock_lookup(struct l2tp_tunnel *tunnel) +{ + int err = 0; + struct socket *sock = NULL; + struct sock *sk = NULL; + + if (!tunnel) + goto out; + + if (tunnel->fd >= 0) { + /* Socket is owned by userspace, who might be in the process + * of closing it. Look the socket up using the fd to ensure + * consistency. + */ + sock = sockfd_lookup(tunnel->fd, &err); + if (sock) + sk = sock->sk; + } else { + /* Socket is owned by kernelspace */ + sk = tunnel->sock; + } + +out: + return sk; +} +EXPORT_SYMBOL_GPL(l2tp_tunnel_sock_lookup); + +/* Drop a reference to a tunnel socket obtained via. l2tp_tunnel_sock_put */ +void l2tp_tunnel_sock_put(struct sock *sk) +{ + struct l2tp_tunnel *tunnel = l2tp_sock_to_tunnel(sk); + if (tunnel) { + if (tunnel->fd >= 0) { + /* Socket is owned by userspace */ + sockfd_put(sk->sk_socket); + } + sock_put(sk); + } +} +EXPORT_SYMBOL_GPL(l2tp_tunnel_sock_put); + /* Lookup a session by id in the global session list */ static struct l2tp_session *l2tp_session_find_2(struct net *net, u32 session_id) @@ -1123,8 +1168,6 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len struct udphdr *uh; struct inet_sock *inet; __wsum csum; - int old_headroom; - int new_headroom; int headroom; int uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0; int udp_len; @@ -1136,16 +1179,12 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len */ headroom = NET_SKB_PAD + sizeof(struct iphdr) + uhlen + hdr_len; - old_headroom = skb_headroom(skb); if (skb_cow_head(skb, headroom)) { kfree_skb(skb); return NET_XMIT_DROP; } - new_headroom = skb_headroom(skb); skb_orphan(skb); - skb->truesize += new_headroom - old_headroom; - /* Setup L2TP header */ session->build_header(session, __skb_push(skb, hdr_len)); @@ -1607,6 +1646,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 tunnel->old_sk_destruct = sk->sk_destruct; sk->sk_destruct = &l2tp_tunnel_destruct; tunnel->sock = sk; + tunnel->fd = fd; lockdep_set_class_and_name(&sk->sk_lock.slock, &l2tp_socket_class, "l2tp_sock"); sk->sk_allocation = GFP_ATOMIC; @@ -1642,24 +1682,32 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_create); */ int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) { - int err = 0; - struct socket *sock = tunnel->sock ? tunnel->sock->sk_socket : NULL; + int err = -EBADF; + struct socket *sock = NULL; + struct sock *sk = NULL; + + sk = l2tp_tunnel_sock_lookup(tunnel); + if (!sk) + goto out; + + sock = sk->sk_socket; + BUG_ON(!sock); /* Force the tunnel socket to close. This will eventually * cause the tunnel to be deleted via the normal socket close * mechanisms when userspace closes the tunnel socket. */ - if (sock != NULL) { - err = inet_shutdown(sock, 2); + err = inet_shutdown(sock, 2); - /* If the tunnel's socket was created by the kernel, - * close the socket here since the socket was not - * created by userspace. - */ - if (sock->file == NULL) - err = inet_release(sock); - } + /* If the tunnel's socket was created by the kernel, + * close the socket here since the socket was not + * created by userspace. + */ + if (sock->file == NULL) + err = inet_release(sock); + l2tp_tunnel_sock_put(sk); +out: return err; } EXPORT_SYMBOL_GPL(l2tp_tunnel_delete); diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index 56d583e083a7..e62204cad4fe 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -188,7 +188,8 @@ struct l2tp_tunnel { int (*recv_payload_hook)(struct sk_buff *skb); void (*old_sk_destruct)(struct sock *); struct sock *sock; /* Parent socket */ - int fd; + int fd; /* Parent fd, if tunnel socket + * was created by userspace */ uint8_t priv[0]; /* private data */ }; @@ -228,6 +229,8 @@ out: return tunnel; } +extern struct sock *l2tp_tunnel_sock_lookup(struct l2tp_tunnel *tunnel); +extern void l2tp_tunnel_sock_put(struct sock *sk); extern struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunnel, u32 session_id); extern struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth); extern struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname); diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index 927547171bc7..8ee4a86ae996 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -554,8 +554,8 @@ static int l2tp_ip6_sendmsg(struct kiocb *iocb, struct sock *sk, memset(opt, 0, sizeof(struct ipv6_txoptions)); opt->tot_len = sizeof(struct ipv6_txoptions); - err = datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt, - &hlimit, &tclass, &dontfrag); + err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt, + &hlimit, &tclass, &dontfrag); if (err < 0) { fl6_sock_release(flowlabel); return err; @@ -646,7 +646,7 @@ static int l2tp_ip6_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len, int noblock, int flags, int *addr_len) { - struct inet_sock *inet = inet_sk(sk); + struct ipv6_pinfo *np = inet6_sk(sk); struct sockaddr_l2tpip6 *lsa = (struct sockaddr_l2tpip6 *)msg->msg_name; size_t copied = 0; int err = -EOPNOTSUPP; @@ -688,8 +688,8 @@ static int l2tp_ip6_recvmsg(struct kiocb *iocb, struct sock *sk, lsa->l2tp_scope_id = IP6CB(skb)->iif; } - if (inet->cmsg_flags) - ip_cmsg_recv(msg, skb); + if (np->rxopt.all) + ip6_datagram_recv_ctl(sk, msg, skb); if (flags & MSG_TRUNC) copied = skb->len; diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 286366ef8930..716605c241f4 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -388,8 +388,6 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) struct l2tp_session *session; struct l2tp_tunnel *tunnel; struct pppol2tp_session *ps; - int old_headroom; - int new_headroom; int uhlen, headroom; if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) @@ -408,7 +406,6 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) if (tunnel == NULL) goto abort_put_sess; - old_headroom = skb_headroom(skb); uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0; headroom = NET_SKB_PAD + sizeof(struct iphdr) + /* IP header */ @@ -418,9 +415,6 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) if (skb_cow_head(skb, headroom)) goto abort_put_sess_tun; - new_headroom = skb_headroom(skb); - skb->truesize += new_headroom - old_headroom; - /* Setup PPP header */ __skb_push(skb, sizeof(ppph)); skb->data[0] = ppph[0]; diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index a9327e2e48ce..670cbc3518de 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -35,10 +35,11 @@ /* Must be called with rcu_read_lock. */ static void netdev_port_receive(struct vport *vport, struct sk_buff *skb) { - if (unlikely(!vport)) { - kfree_skb(skb); - return; - } + if (unlikely(!vport)) + goto error; + + if (unlikely(skb_warn_if_lro(skb))) + goto error; /* Make our own copy of the packet. Otherwise we will mangle the * packet for anyone who came before us (e.g. tcpdump via AF_PACKET). @@ -50,6 +51,10 @@ static void netdev_port_receive(struct vport *vport, struct sk_buff *skb) skb_push(skb, ETH_HLEN); ovs_vport_receive(vport, skb); + return; + +error: + kfree_skb(skb); } /* Called with rcu_read_lock and bottom-halves disabled. */ @@ -169,9 +174,6 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb) goto error; } - if (unlikely(skb_warn_if_lro(skb))) - goto error; - skb->dev = netdev_vport->dev; len = skb->len; dev_queue_xmit(skb); diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index e639645e8fec..c111bd0e083a 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2361,13 +2361,15 @@ static int packet_release(struct socket *sock) packet_flush_mclist(sk); - memset(&req_u, 0, sizeof(req_u)); - - if (po->rx_ring.pg_vec) + if (po->rx_ring.pg_vec) { + memset(&req_u, 0, sizeof(req_u)); packet_set_ring(sk, &req_u, 1, 0); + } - if (po->tx_ring.pg_vec) + if (po->tx_ring.pg_vec) { + memset(&req_u, 0, sizeof(req_u)); packet_set_ring(sk, &req_u, 1, 1); + } fanout_release(sk); diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 298c0ddfb57e..3d2acc7a9c80 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -438,18 +438,18 @@ static int netem_enqueue(struct sk_buff *skb, struct Qdisc *sch) if (q->rate) { struct sk_buff_head *list = &sch->q; - delay += packet_len_2_sched_time(skb->len, q); - if (!skb_queue_empty(list)) { /* - * Last packet in queue is reference point (now). - * First packet in queue is already in flight, - * calculate this time bonus and substract + * Last packet in queue is reference point (now), + * calculate this time bonus and subtract * from delay. */ - delay -= now - netem_skb_cb(skb_peek(list))->time_to_send; + delay -= netem_skb_cb(skb_peek_tail(list))->time_to_send - now; + delay = max_t(psched_tdiff_t, 0, delay); now = netem_skb_cb(skb_peek_tail(list))->time_to_send; } + + delay += packet_len_2_sched_time(skb->len, q); } cb->time_to_send = now + delay; diff --git a/net/sctp/auth.c b/net/sctp/auth.c index 159b9bc5d633..d8420ae614dc 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -71,7 +71,7 @@ void sctp_auth_key_put(struct sctp_auth_bytes *key) return; if (atomic_dec_and_test(&key->refcnt)) { - kfree(key); + kzfree(key); SCTP_DBG_OBJCNT_DEC(keys); } } diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 17a001bac2cc..1a9c5fb77310 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -249,6 +249,8 @@ void sctp_endpoint_free(struct sctp_endpoint *ep) /* Final destructor for endpoint. */ static void sctp_endpoint_destroy(struct sctp_endpoint *ep) { + int i; + SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return); /* Free up the HMAC transform. */ @@ -271,6 +273,9 @@ static void sctp_endpoint_destroy(struct sctp_endpoint *ep) sctp_inq_free(&ep->base.inqueue); sctp_bind_addr_free(&ep->base.bind_addr); + for (i = 0; i < SCTP_HOW_MANY_SECRETS; ++i) + memset(&ep->secret_key[i], 0, SCTP_SECRET_SIZE); + /* Remove and free the port */ if (sctp_sk(ep->base.sk)->bind_hash) sctp_put_port(ep->base.sk); diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 9e65758cb038..cedd9bf67b8c 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -3390,7 +3390,7 @@ static int sctp_setsockopt_auth_key(struct sock *sk, ret = sctp_auth_set_key(sctp_sk(sk)->ep, asoc, authkey); out: - kfree(authkey); + kzfree(authkey); return ret; } diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 0a148c9d2a5c..0f679df7d072 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -465,7 +465,7 @@ static int svc_udp_get_dest_address4(struct svc_rqst *rqstp, } /* - * See net/ipv6/datagram.c : datagram_recv_ctl + * See net/ipv6/datagram.c : ip6_datagram_recv_ctl */ static int svc_udp_get_dest_address6(struct svc_rqst *rqstp, struct cmsghdr *cmh) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 01592d7d4789..45f1618c8e23 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -1358,7 +1358,7 @@ ieee80211_bss(struct wiphy *wiphy, struct iw_request_info *info, &iwe, IW_EV_UINT_LEN); } - buf = kmalloc(30, GFP_ATOMIC); + buf = kmalloc(31, GFP_ATOMIC); if (buf) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; diff --git a/samples/seccomp/Makefile b/samples/seccomp/Makefile index bbbd276659ba..7203e66dcd6f 100644 --- a/samples/seccomp/Makefile +++ b/samples/seccomp/Makefile @@ -19,6 +19,7 @@ bpf-direct-objs := bpf-direct.o # Try to match the kernel target. ifndef CONFIG_64BIT +ifndef CROSS_COMPILE # s390 has -m31 flag to build 31 bit binaries ifndef CONFIG_S390 @@ -35,6 +36,7 @@ HOSTLOADLIBES_bpf-direct += $(MFLAG) HOSTLOADLIBES_bpf-fancy += $(MFLAG) HOSTLOADLIBES_dropper += $(MFLAG) endif +endif # Tell kbuild to always build the programs always := $(hostprogs-y) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 4d2c7dfdaabd..2bb08a962ce3 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -230,12 +230,12 @@ our $Inline = qr{inline|__always_inline|noinline}; our $Member = qr{->$Ident|\.$Ident|\[[^]]*\]}; our $Lval = qr{$Ident(?:$Member)*}; -our $Float_hex = qr{(?i:0x[0-9a-f]+p-?[0-9]+[fl]?)}; -our $Float_dec = qr{(?i:((?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?))}; -our $Float_int = qr{(?i:[0-9]+e-?[0-9]+[fl]?)}; +our $Float_hex = qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?}; +our $Float_dec = qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?}; +our $Float_int = qr{(?i)[0-9]+e-?[0-9]+[fl]?}; our $Float = qr{$Float_hex|$Float_dec|$Float_int}; -our $Constant = qr{(?:$Float|(?i:(?:0x[0-9a-f]+|[0-9]+)[ul]*))}; -our $Assignment = qr{(?:\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=)}; +our $Constant = qr{$Float|(?i)(?:0x[0-9a-f]+|[0-9]+)[ul]*}; +our $Assignment = qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=}; our $Compare = qr{<=|>=|==|!=|<|>}; our $Operators = qr{ <=|>=|==|!=| diff --git a/sound/arm/pxa2xx-ac97-lib.c b/sound/arm/pxa2xx-ac97-lib.c index fff7753e35c1..e6f4633b8dd5 100644 --- a/sound/arm/pxa2xx-ac97-lib.c +++ b/sound/arm/pxa2xx-ac97-lib.c @@ -34,7 +34,7 @@ static struct clk *ac97_clk; static struct clk *ac97conf_clk; static int reset_gpio; -extern void pxa27x_assert_ac97reset(int reset_gpio, int on); +extern void pxa27x_configure_ac97reset(int reset_gpio, bool to_gpio); /* * Beware PXA27x bugs: @@ -140,10 +140,10 @@ static inline void pxa_ac97_warm_pxa27x(void) gsr_bits = 0; /* warm reset broken on Bulverde, so manually keep AC97 reset high */ - pxa27x_assert_ac97reset(reset_gpio, 1); + pxa27x_configure_ac97reset(reset_gpio, true); udelay(10); GCR |= GCR_WARM_RST; - pxa27x_assert_ac97reset(reset_gpio, 0); + pxa27x_configure_ac97reset(reset_gpio, false); udelay(500); } @@ -358,7 +358,7 @@ int pxa2xx_ac97_hw_probe(struct platform_device *dev) __func__, ret); goto err_conf; } - pxa27x_assert_ac97reset(reset_gpio, 0); + pxa27x_configure_ac97reset(reset_gpio, false); ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK"); if (IS_ERR(ac97conf_clk)) { diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index d1b691bf8e2d..3fdd87fa18a9 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -1,6 +1,6 @@ config SND_ATMEL_SOC tristate "SoC Audio for the Atmel System-on-Chip" - depends on ARCH_AT91 + depends on HAS_IOMEM help Say Y or M if you want to add support for codecs attached to the ATMEL SSC interface. You will also need @@ -24,7 +24,7 @@ config SND_ATMEL_SOC_SSC config SND_AT91_SOC_SAM9G20_WM8731 tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board" - depends on ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS + depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS select SND_ATMEL_SOC_PDC select SND_ATMEL_SOC_SSC select SND_SOC_WM8731 @@ -34,7 +34,7 @@ config SND_AT91_SOC_SAM9G20_WM8731 config SND_AT91_SOC_AFEB9260 tristate "SoC Audio support for AFEB9260 board" - depends on ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC + depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC select SND_ATMEL_SOC_PDC select SND_ATMEL_SOC_SSC select SND_SOC_TLV320AIC23 diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c index 6a293c713a38..054ea4d9326a 100644 --- a/sound/soc/atmel/atmel-pcm-pdc.c +++ b/sound/soc/atmel/atmel-pcm-pdc.c @@ -159,7 +159,7 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream, pr_debug("atmel-pcm: " "hw_params: DMA for %s initialized " - "(dma_bytes=%u, period_size=%u)\n", + "(dma_bytes=%zu, period_size=%zu)\n", prtd->params->name, runtime->dma_bytes, prtd->period_size); @@ -201,7 +201,7 @@ static int atmel_pcm_trigger(struct snd_pcm_substream *substream, int ret = 0; pr_debug("atmel-pcm:buffer_size = %ld," - "dma_area = %p, dma_bytes = %u\n", + "dma_area = %p, dma_bytes = %zu\n", rtd->buffer_size, rtd->dma_area, rtd->dma_bytes); switch (cmd) { diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c index e99f1811300a..3109db7b9017 100644 --- a/sound/soc/atmel/atmel-pcm.c +++ b/sound/soc/atmel/atmel-pcm.c @@ -49,7 +49,7 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, buf->private_data = NULL; buf->area = dma_alloc_coherent(pcm->card->dev, size, &buf->addr, GFP_KERNEL); - pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%d\n", + pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%zu\n", (void *)buf->area, (void *)buf->addr, size); if (!buf->area) diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h index bb45d20e7250..12ae814eff21 100644 --- a/sound/soc/atmel/atmel-pcm.h +++ b/sound/soc/atmel/atmel-pcm.h @@ -88,7 +88,8 @@ void atmel_pcm_free(struct snd_pcm *pcm); int atmel_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma); -#ifdef CONFIG_SND_ATMEL_SOC_PDC +#if defined(CONFIG_SND_ATMEL_SOC_PDC) || \ + defined(CONFIG_SND_ATMEL_SOC_PDC_MODULE) int atmel_pcm_pdc_platform_register(struct device *dev); void atmel_pcm_pdc_platform_unregister(struct device *dev); #else @@ -101,7 +102,8 @@ static inline void atmel_pcm_pdc_platform_unregister(struct device *dev) } #endif -#ifdef CONFIG_SND_ATMEL_SOC_DMA +#if defined(CONFIG_SND_ATMEL_SOC_DMA) || \ + defined(CONFIG_SND_ATMEL_SOC_DMA_MODULE) int atmel_pcm_dma_platform_register(struct device *dev); void atmel_pcm_dma_platform_unregister(struct device *dev); #else diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c index 1c7663422054..e13580d6c476 100644 --- a/sound/soc/atmel/atmel_ssc_dai.c +++ b/sound/soc/atmel/atmel_ssc_dai.c @@ -42,8 +42,6 @@ #include <sound/initval.h> #include <sound/soc.h> -#include <mach/hardware.h> - #include "atmel-pcm.h" #include "atmel_ssc_dai.h" @@ -679,15 +677,6 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai) # define atmel_ssc_resume NULL #endif /* CONFIG_PM */ -static int atmel_ssc_probe(struct snd_soc_dai *dai) -{ - struct atmel_ssc_info *ssc_p = &ssc_info[dai->id]; - - snd_soc_dai_set_drvdata(dai, ssc_p); - - return 0; -} - #define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000) #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\ @@ -703,7 +692,6 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = { }; static struct snd_soc_dai_driver atmel_ssc_dai = { - .probe = atmel_ssc_probe, .suspend = atmel_ssc_suspend, .resume = atmel_ssc_resume, .playback = { @@ -790,8 +778,8 @@ void atmel_ssc_put_audio(int ssc_id) { struct ssc_device *ssc = ssc_info[ssc_id].ssc; - ssc_free(ssc); asoc_ssc_exit(&ssc->pdev->dev); + ssc_free(ssc); } EXPORT_SYMBOL_GPL(atmel_ssc_put_audio); diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c index da976291da9e..2d6fbd0125b9 100644 --- a/sound/soc/atmel/sam9g20_wm8731.c +++ b/sound/soc/atmel/sam9g20_wm8731.c @@ -305,10 +305,10 @@ static int at91sam9g20ek_audio_remove(struct platform_device *pdev) { struct snd_soc_card *card = platform_get_drvdata(pdev); - atmel_ssc_put_audio(0); - snd_soc_unregister_card(card); - clk_put(mclk); + clk_disable(mclk); mclk = NULL; + snd_soc_unregister_card(card); + atmel_ssc_put_audio(0); return 0; } diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 1f0cdab03294..2d0378709702 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -26,6 +26,7 @@ #include <linux/delay.h> #include <linux/i2c.h> #include <linux/slab.h> +#include <linux/of_device.h> #include <linux/module.h> #include <sound/soc.h> #include <sound/initval.h> @@ -513,12 +514,31 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4648 = { }; #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static struct of_device_id ak4642_of_match[]; static int ak4642_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct device_node *np = i2c->dev.of_node; + const struct snd_soc_codec_driver *driver; + + driver = NULL; + if (np) { + const struct of_device_id *of_id; + + of_id = of_match_device(ak4642_of_match, &i2c->dev); + if (of_id) + driver = of_id->data; + } else { + driver = (struct snd_soc_codec_driver *)id->driver_data; + } + + if (!driver) { + dev_err(&i2c->dev, "no driver\n"); + return -EINVAL; + } + return snd_soc_register_codec(&i2c->dev, - (struct snd_soc_codec_driver *)id->driver_data, - &ak4642_dai, 1); + driver, &ak4642_dai, 1); } static int ak4642_i2c_remove(struct i2c_client *client) @@ -527,6 +547,14 @@ static int ak4642_i2c_remove(struct i2c_client *client) return 0; } +static struct of_device_id ak4642_of_match[] = { + { .compatible = "asahi-kasei,ak4642", .data = &soc_codec_dev_ak4642}, + { .compatible = "asahi-kasei,ak4643", .data = &soc_codec_dev_ak4642}, + { .compatible = "asahi-kasei,ak4648", .data = &soc_codec_dev_ak4648}, + {}, +}; +MODULE_DEVICE_TABLE(of, ak4642_of_match); + static const struct i2c_device_id ak4642_i2c_id[] = { { "ak4642", (kernel_ulong_t)&soc_codec_dev_ak4642 }, { "ak4643", (kernel_ulong_t)&soc_codec_dev_ak4642 }, @@ -539,6 +567,7 @@ static struct i2c_driver ak4642_i2c_driver = { .driver = { .name = "ak4642-codec", .owner = THIS_MODULE, + .of_match_table = ak4642_of_match, }, .probe = ak4642_i2c_probe, .remove = ak4642_i2c_remove, diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c index ef62c435848e..d824c984c8a4 100644 --- a/sound/soc/codecs/arizona.c +++ b/sound/soc/codecs/arizona.c @@ -56,14 +56,14 @@ #define arizona_fll_warn(_fll, fmt, ...) \ dev_warn(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) #define arizona_fll_dbg(_fll, fmt, ...) \ - dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) + dev_dbg(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__) #define arizona_aif_err(_dai, fmt, ...) \ dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) #define arizona_aif_warn(_dai, fmt, ...) \ dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) #define arizona_aif_dbg(_dai, fmt, ...) \ - dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) + dev_dbg(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__) const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "None", @@ -141,6 +141,30 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = { "ASRC1R", "ASRC2L", "ASRC2R", + "ISRC1INT1", + "ISRC1INT2", + "ISRC1INT3", + "ISRC1INT4", + "ISRC1DEC1", + "ISRC1DEC2", + "ISRC1DEC3", + "ISRC1DEC4", + "ISRC2INT1", + "ISRC2INT2", + "ISRC2INT3", + "ISRC2INT4", + "ISRC2DEC1", + "ISRC2DEC2", + "ISRC2DEC3", + "ISRC2DEC4", + "ISRC3INT1", + "ISRC3INT2", + "ISRC3INT3", + "ISRC3INT4", + "ISRC3DEC1", + "ISRC3DEC2", + "ISRC3DEC3", + "ISRC3DEC4", }; EXPORT_SYMBOL_GPL(arizona_mixer_texts); @@ -220,6 +244,30 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = { 0x91, 0x92, 0x93, + 0xa0, /* ISRC1INT1 */ + 0xa1, + 0xa2, + 0xa3, + 0xa4, /* ISRC1DEC1 */ + 0xa5, + 0xa6, + 0xa7, + 0xa8, /* ISRC2DEC1 */ + 0xa9, + 0xaa, + 0xab, + 0xac, /* ISRC2INT1 */ + 0xad, + 0xae, + 0xaf, + 0xb0, /* ISRC3DEC1 */ + 0xb1, + 0xb2, + 0xb3, + 0xb4, /* ISRC3INT1 */ + 0xb5, + 0xb6, + 0xb7, }; EXPORT_SYMBOL_GPL(arizona_mixer_values); @@ -275,6 +323,15 @@ const struct soc_enum arizona_lhpf4_mode = arizona_lhpf_mode_text); EXPORT_SYMBOL_GPL(arizona_lhpf4_mode); +static const char *arizona_ng_hold_text[] = { + "30ms", "120ms", "250ms", "500ms", +}; + +const struct soc_enum arizona_ng_hold = + SOC_ENUM_SINGLE(ARIZONA_NOISE_GATE_CONTROL, ARIZONA_NGATE_HOLD_SHIFT, + 4, arizona_ng_hold_text); +EXPORT_SYMBOL_GPL(arizona_ng_hold); + int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -417,6 +474,10 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, case 147456000: val |= 6 << ARIZONA_SYSCLK_FREQ_SHIFT; break; + case 0: + dev_dbg(arizona->dev, "%s cleared\n", name); + *clk = freq; + return 0; default: return -EINVAL; } @@ -635,6 +696,9 @@ static int arizona_startup(struct snd_pcm_substream *substream, return 0; } + if (base_rate == 0) + return 0; + if (base_rate % 8000) constraint = &arizona_44k1_constraint; else @@ -645,25 +709,81 @@ static int arizona_startup(struct snd_pcm_substream *substream, constraint); } +static int arizona_hw_params_rate(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); + struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + int base = dai->driver->base; + int i, sr_val; + + /* + * We will need to be more flexible than this in future, + * currently we use a single sample rate for SYSCLK. + */ + for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) + if (arizona_sr_vals[i] == params_rate(params)) + break; + if (i == ARRAY_SIZE(arizona_sr_vals)) { + arizona_aif_err(dai, "Unsupported sample rate %dHz\n", + params_rate(params)); + return -EINVAL; + } + sr_val = i; + + switch (dai_priv->clk) { + case ARIZONA_CLK_SYSCLK: + snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, + ARIZONA_SAMPLE_RATE_1_MASK, sr_val); + if (base) + snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_RATE_MASK, 0); + break; + case ARIZONA_CLK_ASYNCCLK: + snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1, + ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val); + if (base) + snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_RATE_MASK, + 8 << ARIZONA_AIF1_RATE_SHIFT); + break; + default: + arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); + return -EINVAL; + } + + return 0; +} + static int arizona_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec); - struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1]; + struct arizona *arizona = priv->arizona; int base = dai->driver->base; const int *rates; - int i; - int bclk, lrclk, wl, frame, sr_val; + int i, ret; + int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1]; + int bclk, lrclk, wl, frame, bclk_target; if (params_rate(params) % 8000) rates = &arizona_44k1_bclk_rates[0]; else rates = &arizona_48k_bclk_rates[0]; + bclk_target = snd_soc_params_to_bclk(params); + if (chan_limit && chan_limit < params_channels(params)) { + arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit); + bclk_target /= params_channels(params); + bclk_target *= chan_limit; + } + for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) { - if (rates[i] >= snd_soc_params_to_bclk(params) && + if (rates[i] >= bclk_target && rates[i] % params_rate(params) == 0) { bclk = i; break; @@ -675,16 +795,6 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } - for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++) - if (arizona_sr_vals[i] == params_rate(params)) - break; - if (i == ARRAY_SIZE(arizona_sr_vals)) { - arizona_aif_err(dai, "Unsupported sample rate %dHz\n", - params_rate(params)); - return -EINVAL; - } - sr_val = i; - lrclk = rates[bclk] / params_rate(params); arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n", @@ -693,28 +803,9 @@ static int arizona_hw_params(struct snd_pcm_substream *substream, wl = snd_pcm_format_width(params_format(params)); frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl; - /* - * We will need to be more flexible than this in future, - * currently we use a single sample rate for SYSCLK. - */ - switch (dai_priv->clk) { - case ARIZONA_CLK_SYSCLK: - snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1, - ARIZONA_SAMPLE_RATE_1_MASK, sr_val); - snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, - ARIZONA_AIF1_RATE_MASK, 0); - break; - case ARIZONA_CLK_ASYNCCLK: - snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1, - ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val); - snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, - ARIZONA_AIF1_RATE_MASK, - 8 << ARIZONA_AIF1_RATE_SHIFT); - break; - default: - arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk); - return -EINVAL; - } + ret = arizona_hw_params_rate(substream, params, dai); + if (ret != 0) + return ret; snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL, ARIZONA_AIF1_BCLK_FREQ_MASK, bclk); @@ -789,11 +880,27 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai, return snd_soc_dapm_sync(&codec->dapm); } +static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + struct snd_soc_codec *codec = dai->codec; + int base = dai->driver->base; + unsigned int reg; + + if (tristate) + reg = ARIZONA_AIF1_TRI; + else + reg = 0; + + return snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL, + ARIZONA_AIF1_TRI, reg); +} + const struct snd_soc_dai_ops arizona_dai_ops = { .startup = arizona_startup, .set_fmt = arizona_set_fmt, .hw_params = arizona_hw_params, .set_sysclk = arizona_dai_set_sysclk, + .set_tristate = arizona_set_tristate, }; EXPORT_SYMBOL_GPL(arizona_dai_ops); @@ -807,17 +914,6 @@ int arizona_init_dai(struct arizona_priv *priv, int id) } EXPORT_SYMBOL_GPL(arizona_init_dai); -static irqreturn_t arizona_fll_lock(int irq, void *data) -{ - struct arizona_fll *fll = data; - - arizona_fll_dbg(fll, "Lock status changed\n"); - - complete(&fll->lock); - - return IRQ_HANDLED; -} - static irqreturn_t arizona_fll_clock_ok(int irq, void *data) { struct arizona_fll *fll = data; @@ -910,7 +1006,7 @@ static int arizona_calc_fll(struct arizona_fll *fll, cfg->n = target / (ratio * Fref); - if (target % Fref) { + if (target % (ratio * Fref)) { gcd_fll = gcd(target, ratio * Fref); arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll); @@ -922,6 +1018,15 @@ static int arizona_calc_fll(struct arizona_fll *fll, cfg->lambda = 0; } + /* Round down to 16bit range with cost of accuracy lost. + * Denominator must be bigger than numerator so we only + * take care of it. + */ + while (cfg->lambda >= (1 << 16)) { + cfg->theta >>= 1; + cfg->lambda >>= 1; + } + arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n", cfg->n, cfg->theta, cfg->lambda); arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n", @@ -1057,7 +1162,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, { int ret; - init_completion(&fll->lock); init_completion(&fll->ok); fll->id = id; @@ -1068,13 +1172,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name), "FLL%d clock OK", id); - ret = arizona_request_irq(arizona, lock_irq, fll->lock_name, - arizona_fll_lock, fll); - if (ret != 0) { - dev_err(arizona->dev, "Failed to get FLL%d lock IRQ: %d\n", - id, ret); - } - ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name, arizona_fll_clock_ok, fll); if (ret != 0) { @@ -1089,6 +1186,40 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq, } EXPORT_SYMBOL_GPL(arizona_init_fll); +/** + * arizona_set_output_mode - Set the mode of the specified output + * + * @codec: Device to configure + * @output: Output number + * @diff: True to set the output to differential mode + * + * Some systems use external analogue switches to connect more + * analogue devices to the CODEC than are supported by the device. In + * some systems this requires changing the switched output from single + * ended to differential mode dynamically at runtime, an operation + * supported using this function. + * + * Most systems have a single static configuration and should use + * platform data instead. + */ +int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff) +{ + unsigned int reg, val; + + if (output < 1 || output > 6) + return -EINVAL; + + reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + (output - 1) * 8; + + if (diff) + val = ARIZONA_OUT1_MONO; + else + val = 0; + + return snd_soc_update_bits(codec, reg, ARIZONA_OUT1_MONO, val); +} +EXPORT_SYMBOL_GPL(arizona_set_output_mode); + MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support"); MODULE_AUTHOR("Mark Brown <[email protected]>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h index 4deebeb07177..116372c91f5d 100644 --- a/sound/soc/codecs/arizona.h +++ b/sound/soc/codecs/arizona.h @@ -66,7 +66,7 @@ struct arizona_priv { struct arizona_dai_priv dai[ARIZONA_MAX_DAI]; }; -#define ARIZONA_NUM_MIXER_INPUTS 75 +#define ARIZONA_NUM_MIXER_INPUTS 99 extern const unsigned int arizona_mixer_tlv[]; extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS]; @@ -176,6 +176,8 @@ extern const struct soc_enum arizona_lhpf2_mode; extern const struct soc_enum arizona_lhpf3_mode; extern const struct soc_enum arizona_lhpf4_mode; +extern const struct soc_enum arizona_ng_hold; + extern int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); @@ -195,7 +197,6 @@ struct arizona_fll { int id; unsigned int base; unsigned int vco_mult; - struct completion lock; struct completion ok; unsigned int fref; unsigned int fout; @@ -211,4 +212,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source, extern int arizona_init_dai(struct arizona_priv *priv, int dai); +int arizona_set_output_mode(struct snd_soc_codec *codec, int output, + bool diff); + #endif diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c index ac8742a1f25a..2415a4118dbd 100644 --- a/sound/soc/codecs/cs4271.c +++ b/sound/soc/codecs/cs4271.c @@ -167,6 +167,8 @@ struct cs4271_private { int gpio_nreset; /* GPIO that disable serial bus, if any */ int gpio_disable; + /* enable soft reset workaround */ + bool enable_soft_reset; }; /* @@ -325,6 +327,33 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream, int i, ret; unsigned int ratio, val; + if (cs4271->enable_soft_reset) { + /* + * Put the codec in soft reset and back again in case it's not + * currently streaming data. This way of bringing the codec in + * sync to the current clocks is not explicitly documented in + * the data sheet, but it seems to work fine, and in contrast + * to a read hardware reset, we don't have to sync back all + * registers every time. + */ + + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK && + !dai->capture_active) || + (substream->stream == SNDRV_PCM_STREAM_CAPTURE && + !dai->playback_active)) { + ret = snd_soc_update_bits(codec, CS4271_MODE2, + CS4271_MODE2_PDN, + CS4271_MODE2_PDN); + if (ret < 0) + return ret; + + ret = snd_soc_update_bits(codec, CS4271_MODE2, + CS4271_MODE2_PDN, 0); + if (ret < 0) + return ret; + } + } + cs4271->rate = params_rate(params); /* Configure DAC */ @@ -484,6 +513,10 @@ static int cs4271_probe(struct snd_soc_codec *codec) if (of_get_property(codec->dev->of_node, "cirrus,amutec-eq-bmutec", NULL)) amutec_eq_bmutec = true; + + if (of_get_property(codec->dev->of_node, + "cirrus,enable-soft-reset", NULL)) + cs4271->enable_soft_reset = true; } #endif @@ -492,6 +525,7 @@ static int cs4271_probe(struct snd_soc_codec *codec) gpio_nreset = cs4271plat->gpio_nreset; amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec; + cs4271->enable_soft_reset = cs4271plat->enable_soft_reset; } if (gpio_nreset >= 0) diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 9811a5478c87..0f6f481cec09 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1038,7 +1038,7 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec) struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); int ret; - cs42l52->beep = input_allocate_device(); + cs42l52->beep = devm_input_allocate_device(codec->dev); if (!cs42l52->beep) { dev_err(codec->dev, "Failed to allocate beep device\n"); return; @@ -1059,7 +1059,6 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec) ret = input_register_device(cs42l52->beep); if (ret != 0) { - input_free_device(cs42l52->beep); cs42l52->beep = NULL; dev_err(codec->dev, "Failed to register beep device\n"); } @@ -1076,7 +1075,6 @@ static void cs42l52_free_beep(struct snd_soc_codec *codec) struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec); device_remove_file(codec->dev, &dev_attr_beep); - input_unregister_device(cs42l52->beep); cancel_work_sync(&cs42l52->beep_work); cs42l52->beep = NULL; diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index c9772ca3da4f..fc176044994d 100644..100755 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -1,562 +1,2381 @@ /* * max98090.c -- MAX98090 ALSA SoC Audio driver - * based on Rev0p8 datasheet * - * Copyright (C) 2012 Renesas Solutions Corp. - * Kuninori Morimoto <[email protected]> - * - * Based on - * - * max98095.c - * Copyright 2011 Maxim Integrated Products - * - * https://github.com/hardkernel/linux/commit/\ - * 3417d7166b17113b3b33b0a337c74d1c7cc313df#sound/soc/codecs/max98090.c - * Copyright 2011 Maxim Integrated Products + * Copyright 2011-2012 Maxim Integrated Products * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ +#include <linux/delay.h> #include <linux/i2c.h> #include <linux/module.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/tlv.h> +#include <sound/max98090.h> +#include "max98090.h" + +#include <linux/version.h> + +#define DEBUG +#define EXTMIC_METHOD +#define EXTMIC_METHOD_TEST + +/* Allows for sparsely populated register maps */ +static struct reg_default max98090_reg[] = { + { 0x00, 0x00 }, /* 00 Software Reset */ + { 0x03, 0x04 }, /* 03 Interrupt Masks */ + { 0x04, 0x00 }, /* 04 System Clock Quick */ + { 0x05, 0x00 }, /* 05 Sample Rate Quick */ + { 0x06, 0x00 }, /* 06 DAI Interface Quick */ + { 0x07, 0x00 }, /* 07 DAC Path Quick */ + { 0x08, 0x00 }, /* 08 Mic/Direct to ADC Quick */ + { 0x09, 0x00 }, /* 09 Line to ADC Quick */ + { 0x0A, 0x00 }, /* 0A Analog Mic Loop Quick */ + { 0x0B, 0x00 }, /* 0B Analog Line Loop Quick */ + { 0x0C, 0x00 }, /* 0C Reserved */ + { 0x0D, 0x00 }, /* 0D Input Config */ + { 0x0E, 0x1B }, /* 0E Line Input Level */ + { 0x0F, 0x00 }, /* 0F Line Config */ + + { 0x10, 0x14 }, /* 10 Mic1 Input Level */ + { 0x11, 0x14 }, /* 11 Mic2 Input Level */ + { 0x12, 0x00 }, /* 12 Mic Bias Voltage */ + { 0x13, 0x00 }, /* 13 Digital Mic Config */ + { 0x14, 0x00 }, /* 14 Digital Mic Mode */ + { 0x15, 0x00 }, /* 15 Left ADC Mixer */ + { 0x16, 0x00 }, /* 16 Right ADC Mixer */ + { 0x17, 0x03 }, /* 17 Left ADC Level */ + { 0x18, 0x03 }, /* 18 Right ADC Level */ + { 0x19, 0x00 }, /* 19 ADC Biquad Level */ + { 0x1A, 0x00 }, /* 1A ADC Sidetone */ + { 0x1B, 0x00 }, /* 1B System Clock */ + { 0x1C, 0x00 }, /* 1C Clock Mode */ + { 0x1D, 0x00 }, /* 1D Any Clock 1 */ + { 0x1E, 0x00 }, /* 1E Any Clock 2 */ + { 0x1F, 0x00 }, /* 1F Any Clock 3 */ + + { 0x20, 0x00 }, /* 20 Any Clock 4 */ + { 0x21, 0x00 }, /* 21 Master Mode */ + { 0x22, 0x00 }, /* 22 Interface Format */ + { 0x23, 0x00 }, /* 23 TDM Format 1*/ + { 0x24, 0x00 }, /* 24 TDM Format 2*/ + { 0x25, 0x00 }, /* 25 I/O Configuration */ + { 0x26, 0x80 }, /* 26 Filter Config */ + { 0x27, 0x00 }, /* 27 DAI Playback Level */ + { 0x28, 0x00 }, /* 28 EQ Playback Level */ + { 0x29, 0x00 }, /* 29 Left HP Mixer */ + { 0x2A, 0x00 }, /* 2A Right HP Mixer */ + { 0x2B, 0x00 }, /* 2B HP Control */ + { 0x2C, 0x1A }, /* 2C Left HP Volume */ + { 0x2D, 0x1A }, /* 2D Right HP Volume */ + { 0x2E, 0x00 }, /* 2E Left Spk Mixer */ + { 0x2F, 0x00 }, /* 2F Right Spk Mixer */ + + { 0x30, 0x00 }, /* 30 Spk Control */ + { 0x31, 0x2C }, /* 31 Left Spk Volume */ + { 0x32, 0x2C }, /* 32 Right Spk Volume */ + { 0x33, 0x00 }, /* 33 ALC Timing */ + { 0x34, 0x00 }, /* 34 ALC Compressor */ + { 0x35, 0x00 }, /* 35 ALC Expander */ + { 0x36, 0x00 }, /* 36 ALC Gain */ + { 0x37, 0x00 }, /* 37 Rcv/Line OutL Mixer */ + { 0x38, 0x00 }, /* 38 Rcv/Line OutL Control */ + { 0x39, 0x15 }, /* 39 Rcv/Line OutL Volume */ + { 0x3A, 0x00 }, /* 3A Line OutR Mixer */ + { 0x3B, 0x00 }, /* 3B Line OutR Control */ + { 0x3C, 0x15 }, /* 3C Line OutR Volume */ + { 0x3D, 0x00 }, /* 3D Jack Detect */ + { 0x3E, 0x00 }, /* 3E Input Enable */ + { 0x3F, 0x00 }, /* 3F Output Enable */ + + { 0x40, 0x00 }, /* 40 Level Control */ + { 0x41, 0x00 }, /* 41 DSP Filter Enable */ + { 0x42, 0x00 }, /* 42 Bias Control */ + { 0x43, 0x00 }, /* 43 DAC Control */ + { 0x44, 0x06 }, /* 44 ADC Control */ + { 0x45, 0x00 }, /* 45 Device Shutdown */ + { 0x46, 0x00 }, /* 46 Equalizer Band 1 Coefficient B0 */ + { 0x47, 0x00 }, /* 47 Equalizer Band 1 Coefficient B0 */ + { 0x48, 0x00 }, /* 48 Equalizer Band 1 Coefficient B0 */ + { 0x49, 0x00 }, /* 49 Equalizer Band 1 Coefficient B1 */ + { 0x4A, 0x00 }, /* 4A Equalizer Band 1 Coefficient B1 */ + { 0x4B, 0x00 }, /* 4B Equalizer Band 1 Coefficient B1 */ + { 0x4C, 0x00 }, /* 4C Equalizer Band 1 Coefficient B2 */ + { 0x4D, 0x00 }, /* 4D Equalizer Band 1 Coefficient B2 */ + { 0x4E, 0x00 }, /* 4E Equalizer Band 1 Coefficient B2 */ + { 0x4F, 0x00 }, /* 4F Equalizer Band 1 Coefficient A1 */ + + { 0x50, 0x00 }, /* 50 Equalizer Band 1 Coefficient A1 */ + { 0x51, 0x00 }, /* 51 Equalizer Band 1 Coefficient A1 */ + { 0x52, 0x00 }, /* 52 Equalizer Band 1 Coefficient A2 */ + { 0x53, 0x00 }, /* 53 Equalizer Band 1 Coefficient A2 */ + { 0x54, 0x00 }, /* 54 Equalizer Band 1 Coefficient A2 */ + { 0x55, 0x00 }, /* 55 Equalizer Band 2 Coefficient B0 */ + { 0x56, 0x00 }, /* 56 Equalizer Band 2 Coefficient B0 */ + { 0x57, 0x00 }, /* 57 Equalizer Band 2 Coefficient B0 */ + { 0x58, 0x00 }, /* 58 Equalizer Band 2 Coefficient B1 */ + { 0x59, 0x00 }, /* 59 Equalizer Band 2 Coefficient B1 */ + { 0x5A, 0x00 }, /* 5A Equalizer Band 2 Coefficient B1 */ + { 0x5B, 0x00 }, /* 5B Equalizer Band 2 Coefficient B2 */ + { 0x5C, 0x00 }, /* 5C Equalizer Band 2 Coefficient B2 */ + { 0x5D, 0x00 }, /* 5D Equalizer Band 2 Coefficient B2 */ + { 0x5E, 0x00 }, /* 5E Equalizer Band 2 Coefficient A1 */ + { 0x5F, 0x00 }, /* 5F Equalizer Band 2 Coefficient A1 */ + + { 0x60, 0x00 }, /* 60 Equalizer Band 2 Coefficient A1 */ + { 0x61, 0x00 }, /* 61 Equalizer Band 2 Coefficient A2 */ + { 0x62, 0x00 }, /* 62 Equalizer Band 2 Coefficient A2 */ + { 0x63, 0x00 }, /* 63 Equalizer Band 2 Coefficient A2 */ + { 0x64, 0x00 }, /* 64 Equalizer Band 3 Coefficient B0 */ + { 0x65, 0x00 }, /* 65 Equalizer Band 3 Coefficient B0 */ + { 0x66, 0x00 }, /* 66 Equalizer Band 3 Coefficient B0 */ + { 0x67, 0x00 }, /* 67 Equalizer Band 3 Coefficient B1 */ + { 0x68, 0x00 }, /* 68 Equalizer Band 3 Coefficient B1 */ + { 0x69, 0x00 }, /* 69 Equalizer Band 3 Coefficient B1 */ + { 0x6A, 0x00 }, /* 6A Equalizer Band 3 Coefficient B2 */ + { 0x6B, 0x00 }, /* 6B Equalizer Band 3 Coefficient B2 */ + { 0x6C, 0x00 }, /* 6C Equalizer Band 3 Coefficient B2 */ + { 0x6D, 0x00 }, /* 6D Equalizer Band 3 Coefficient A1 */ + { 0x6E, 0x00 }, /* 6E Equalizer Band 3 Coefficient A1 */ + { 0x6F, 0x00 }, /* 6F Equalizer Band 3 Coefficient A1 */ + + { 0x70, 0x00 }, /* 70 Equalizer Band 3 Coefficient A2 */ + { 0x71, 0x00 }, /* 71 Equalizer Band 3 Coefficient A2 */ + { 0x72, 0x00 }, /* 72 Equalizer Band 3 Coefficient A2 */ + { 0x73, 0x00 }, /* 73 Equalizer Band 4 Coefficient B0 */ + { 0x74, 0x00 }, /* 74 Equalizer Band 4 Coefficient B0 */ + { 0x75, 0x00 }, /* 75 Equalizer Band 4 Coefficient B0 */ + { 0x76, 0x00 }, /* 76 Equalizer Band 4 Coefficient B1 */ + { 0x77, 0x00 }, /* 77 Equalizer Band 4 Coefficient B1 */ + { 0x78, 0x00 }, /* 78 Equalizer Band 4 Coefficient B1 */ + { 0x79, 0x00 }, /* 79 Equalizer Band 4 Coefficient B2 */ + { 0x7A, 0x00 }, /* 7A Equalizer Band 4 Coefficient B2 */ + { 0x7B, 0x00 }, /* 7B Equalizer Band 4 Coefficient B2 */ + { 0x7C, 0x00 }, /* 7C Equalizer Band 4 Coefficient A1 */ + { 0x7D, 0x00 }, /* 7D Equalizer Band 4 Coefficient A1 */ + { 0x7E, 0x00 }, /* 7E Equalizer Band 4 Coefficient A1 */ + { 0x7F, 0x00 }, /* 7F Equalizer Band 4 Coefficient A2 */ + + { 0x80, 0x00 }, /* 80 Equalizer Band 4 Coefficient A2 */ + { 0x81, 0x00 }, /* 81 Equalizer Band 4 Coefficient A2 */ + { 0x82, 0x00 }, /* 82 Equalizer Band 5 Coefficient B0 */ + { 0x83, 0x00 }, /* 83 Equalizer Band 5 Coefficient B0 */ + { 0x84, 0x00 }, /* 84 Equalizer Band 5 Coefficient B0 */ + { 0x85, 0x00 }, /* 85 Equalizer Band 5 Coefficient B1 */ + { 0x86, 0x00 }, /* 86 Equalizer Band 5 Coefficient B1 */ + { 0x87, 0x00 }, /* 87 Equalizer Band 5 Coefficient B1 */ + { 0x88, 0x00 }, /* 88 Equalizer Band 5 Coefficient B2 */ + { 0x89, 0x00 }, /* 89 Equalizer Band 5 Coefficient B2 */ + { 0x8A, 0x00 }, /* 8A Equalizer Band 5 Coefficient B2 */ + { 0x8B, 0x00 }, /* 8B Equalizer Band 5 Coefficient A1 */ + { 0x8C, 0x00 }, /* 8C Equalizer Band 5 Coefficient A1 */ + { 0x8D, 0x00 }, /* 8D Equalizer Band 5 Coefficient A1 */ + { 0x8E, 0x00 }, /* 8E Equalizer Band 5 Coefficient A2 */ + { 0x8F, 0x00 }, /* 8F Equalizer Band 5 Coefficient A2 */ + + { 0x90, 0x00 }, /* 90 Equalizer Band 5 Coefficient A2 */ + { 0x91, 0x00 }, /* 91 Equalizer Band 6 Coefficient B0 */ + { 0x92, 0x00 }, /* 92 Equalizer Band 6 Coefficient B0 */ + { 0x93, 0x00 }, /* 93 Equalizer Band 6 Coefficient B0 */ + { 0x94, 0x00 }, /* 94 Equalizer Band 6 Coefficient B1 */ + { 0x95, 0x00 }, /* 95 Equalizer Band 6 Coefficient B1 */ + { 0x96, 0x00 }, /* 96 Equalizer Band 6 Coefficient B1 */ + { 0x97, 0x00 }, /* 97 Equalizer Band 6 Coefficient B2 */ + { 0x98, 0x00 }, /* 98 Equalizer Band 6 Coefficient B2 */ + { 0x99, 0x00 }, /* 99 Equalizer Band 6 Coefficient B2 */ + { 0x9A, 0x00 }, /* 9A Equalizer Band 6 Coefficient A1 */ + { 0x9B, 0x00 }, /* 9B Equalizer Band 6 Coefficient A1 */ + { 0x9C, 0x00 }, /* 9C Equalizer Band 6 Coefficient A1 */ + { 0x9D, 0x00 }, /* 9D Equalizer Band 6 Coefficient A2 */ + { 0x9E, 0x00 }, /* 9E Equalizer Band 6 Coefficient A2 */ + { 0x9F, 0x00 }, /* 9F Equalizer Band 6 Coefficient A2 */ + + { 0xA0, 0x00 }, /* A0 Equalizer Band 7 Coefficient B0 */ + { 0xA1, 0x00 }, /* A1 Equalizer Band 7 Coefficient B0 */ + { 0xA2, 0x00 }, /* A2 Equalizer Band 7 Coefficient B0 */ + { 0xA3, 0x00 }, /* A3 Equalizer Band 7 Coefficient B1 */ + { 0xA4, 0x00 }, /* A4 Equalizer Band 7 Coefficient B1 */ + { 0xA5, 0x00 }, /* A5 Equalizer Band 7 Coefficient B1 */ + { 0xA6, 0x00 }, /* A6 Equalizer Band 7 Coefficient B2 */ + { 0xA7, 0x00 }, /* A7 Equalizer Band 7 Coefficient B2 */ + { 0xA8, 0x00 }, /* A8 Equalizer Band 7 Coefficient B2 */ + { 0xA9, 0x00 }, /* A9 Equalizer Band 7 Coefficient A1 */ + { 0xAA, 0x00 }, /* AA Equalizer Band 7 Coefficient A1 */ + { 0xAB, 0x00 }, /* AB Equalizer Band 7 Coefficient A1 */ + { 0xAC, 0x00 }, /* AC Equalizer Band 7 Coefficient A2 */ + { 0xAD, 0x00 }, /* AD Equalizer Band 7 Coefficient A2 */ + { 0xAE, 0x00 }, /* AE Equalizer Band 7 Coefficient A2 */ + { 0xAF, 0x00 }, /* AF ADC Biquad Coefficient B0 */ + + { 0xB0, 0x00 }, /* B0 ADC Biquad Coefficient B0 */ + { 0xB1, 0x00 }, /* B1 ADC Biquad Coefficient B0 */ + { 0xB2, 0x00 }, /* B2 ADC Biquad Coefficient B1 */ + { 0xB3, 0x00 }, /* B3 ADC Biquad Coefficient B1 */ + { 0xB4, 0x00 }, /* B4 ADC Biquad Coefficient B1 */ + { 0xB5, 0x00 }, /* B5 ADC Biquad Coefficient B2 */ + { 0xB6, 0x00 }, /* B6 ADC Biquad Coefficient B2 */ + { 0xB7, 0x00 }, /* B7 ADC Biquad Coefficient B2 */ + { 0xB8, 0x00 }, /* B8 ADC Biquad Coefficient A1 */ + { 0xB9, 0x00 }, /* B9 ADC Biquad Coefficient A1 */ + { 0xBA, 0x00 }, /* BA ADC Biquad Coefficient A1 */ + { 0xBB, 0x00 }, /* BB ADC Biquad Coefficient A2 */ + { 0xBC, 0x00 }, /* BC ADC Biquad Coefficient A2 */ + { 0xBD, 0x00 }, /* BD ADC Biquad Coefficient A2 */ + { 0xBE, 0x00 }, /* BE Digital Mic 3 Volume */ + { 0xBF, 0x00 }, /* BF Digital Mic 4 Volume */ + + { 0xC0, 0x00 }, /* C0 Digital Mic 34 Biquad Pre Atten */ + { 0xC1, 0x00 }, /* C1 Record TDM Slot */ + { 0xC2, 0x00 }, /* C2 Sample Rate */ + { 0xC3, 0x00 }, /* C3 Digital Mic 34 Biquad Coefficient C3 */ + { 0xC4, 0x00 }, /* C4 Digital Mic 34 Biquad Coefficient C4 */ + { 0xC5, 0x00 }, /* C5 Digital Mic 34 Biquad Coefficient C5 */ + { 0xC6, 0x00 }, /* C6 Digital Mic 34 Biquad Coefficient C6 */ + { 0xC7, 0x00 }, /* C7 Digital Mic 34 Biquad Coefficient C7 */ + { 0xC8, 0x00 }, /* C8 Digital Mic 34 Biquad Coefficient C8 */ + { 0xC9, 0x00 }, /* C9 Digital Mic 34 Biquad Coefficient C9 */ + { 0xCA, 0x00 }, /* CA Digital Mic 34 Biquad Coefficient CA */ + { 0xCB, 0x00 }, /* CB Digital Mic 34 Biquad Coefficient CB */ + { 0xCC, 0x00 }, /* CC Digital Mic 34 Biquad Coefficient CC */ + { 0xCD, 0x00 }, /* CD Digital Mic 34 Biquad Coefficient CD */ + { 0xCE, 0x00 }, /* CE Digital Mic 34 Biquad Coefficient CE */ + { 0xCF, 0x00 }, /* CF Digital Mic 34 Biquad Coefficient CF */ + + { 0xD0, 0x00 }, /* D0 Digital Mic 34 Biquad Coefficient D0 */ + { 0xD1, 0x00 }, /* D1 Digital Mic 34 Biquad Coefficient D1 */ +}; -/* - * - * MAX98090 Registers Definition - * - */ +static bool max98090_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case M98090_REG_DEVICE_STATUS: + case M98090_REG_JACK_STATUS: + case M98090_REG_REVISION_ID: + return true; + default: + return false; + } +} -/* RESET / STATUS / INTERRUPT REGISTERS */ -#define MAX98090_0x00_SW_RESET 0x00 -#define MAX98090_0x01_INT_STS 0x01 -#define MAX98090_0x02_JACK_STS 0x02 -#define MAX98090_0x03_INT_MASK 0x03 - -/* QUICK SETUP REGISTERS */ -#define MAX98090_0x04_SYS_CLK 0x04 -#define MAX98090_0x05_SAMPLE_RATE 0x05 -#define MAX98090_0x06_DAI_IF 0x06 -#define MAX98090_0x07_DAC_PATH 0x07 -#define MAX98090_0x08_MIC_TO_ADC 0x08 -#define MAX98090_0x09_LINE_TO_ADC 0x09 -#define MAX98090_0x0A_ANALOG_MIC_LOOP 0x0A -#define MAX98090_0x0B_ANALOG_LINE_LOOP 0x0B - -/* ANALOG INPUT CONFIGURATION REGISTERS */ -#define MAX98090_0x0D_INPUT_CONFIG 0x0D -#define MAX98090_0x0E_LINE_IN_LVL 0x0E -#define MAX98090_0x0F_LINI_IN_CFG 0x0F -#define MAX98090_0x10_MIC1_IN_LVL 0x10 -#define MAX98090_0x11_MIC2_IN_LVL 0x11 - -/* MICROPHONE CONFIGURATION REGISTERS */ -#define MAX98090_0x12_MIC_BIAS_VOL 0x12 -#define MAX98090_0x13_DIGITAL_MIC_CFG 0x13 -#define MAX98090_0x14_DIGITAL_MIC_MODE 0x14 - -/* ADC PATH AND CONFIGURATION REGISTERS */ -#define MAX98090_0x15_L_ADC_MIX 0x15 -#define MAX98090_0x16_R_ADC_MIX 0x16 -#define MAX98090_0x17_L_ADC_LVL 0x17 -#define MAX98090_0x18_R_ADC_LVL 0x18 -#define MAX98090_0x19_ADC_BIQUAD_LVL 0x19 -#define MAX98090_0x1A_ADC_SIDETONE 0x1A - -/* CLOCK CONFIGURATION REGISTERS */ -#define MAX98090_0x1B_SYS_CLK 0x1B -#define MAX98090_0x1C_CLK_MODE 0x1C -#define MAX98090_0x1D_ANY_CLK1 0x1D -#define MAX98090_0x1E_ANY_CLK2 0x1E -#define MAX98090_0x1F_ANY_CLK3 0x1F -#define MAX98090_0x20_ANY_CLK4 0x20 -#define MAX98090_0x21_MASTER_MODE 0x21 - -/* INTERFACE CONTROL REGISTERS */ -#define MAX98090_0x22_DAI_IF_FMT 0x22 -#define MAX98090_0x23_DAI_TDM_FMT1 0x23 -#define MAX98090_0x24_DAI_TDM_FMT2 0x24 -#define MAX98090_0x25_DAI_IO_CFG 0x25 -#define MAX98090_0x26_FILTER_CFG 0x26 -#define MAX98090_0x27_DAI_PLAYBACK_LVL 0x27 -#define MAX98090_0x28_EQ_PLAYBACK_LVL 0x28 - -/* HEADPHONE CONTROL REGISTERS */ -#define MAX98090_0x29_L_HP_MIX 0x29 -#define MAX98090_0x2A_R_HP_MIX 0x2A -#define MAX98090_0x2B_HP_CTR 0x2B -#define MAX98090_0x2C_L_HP_VOL 0x2C -#define MAX98090_0x2D_R_HP_VOL 0x2D - -/* SPEAKER CONFIGURATION REGISTERS */ -#define MAX98090_0x2E_L_SPK_MIX 0x2E -#define MAX98090_0x2F_R_SPK_MIX 0x2F -#define MAX98090_0x30_SPK_CTR 0x30 -#define MAX98090_0x31_L_SPK_VOL 0x31 -#define MAX98090_0x32_R_SPK_VOL 0x32 - -/* ALC CONFIGURATION REGISTERS */ -#define MAX98090_0x33_ALC_TIMING 0x33 -#define MAX98090_0x34_ALC_COMPRESSOR 0x34 -#define MAX98090_0x35_ALC_EXPANDER 0x35 -#define MAX98090_0x36_ALC_GAIN 0x36 - -/* RECEIVER AND LINE_OUTPUT REGISTERS */ -#define MAX98090_0x37_RCV_LOUT_L_MIX 0x37 -#define MAX98090_0x38_RCV_LOUT_L_CNTL 0x38 -#define MAX98090_0x39_RCV_LOUT_L_VOL 0x39 -#define MAX98090_0x3A_LOUT_R_MIX 0x3A -#define MAX98090_0x3B_LOUT_R_CNTL 0x3B -#define MAX98090_0x3C_LOUT_R_VOL 0x3C - -/* JACK DETECT AND ENABLE REGISTERS */ -#define MAX98090_0x3D_JACK_DETECT 0x3D -#define MAX98090_0x3E_IN_ENABLE 0x3E -#define MAX98090_0x3F_OUT_ENABLE 0x3F -#define MAX98090_0x40_LVL_CTR 0x40 -#define MAX98090_0x41_DSP_FILTER_ENABLE 0x41 - -/* BIAS AND POWER MODE CONFIGURATION REGISTERS */ -#define MAX98090_0x42_BIAS_CTR 0x42 -#define MAX98090_0x43_DAC_CTR 0x43 -#define MAX98090_0x44_ADC_CTR 0x44 -#define MAX98090_0x45_DEV_SHUTDOWN 0x45 - -/* REVISION ID REGISTER */ -#define MAX98090_0xFF_REV_ID 0xFF - -#define MAX98090_REG_MAX_CACHED 0x45 -#define MAX98090_REG_END 0xFF +static bool max98090_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case M98090_REG_DEVICE_STATUS: + case M98090_REG_JACK_STATUS: + case M98090_REG_INTERRUPT_S: + case M98090_REG_RESERVED: + case M98090_REG_LINE_INPUT_CONFIG: + case M98090_REG_LINE_INPUT_LEVEL: + case M98090_REG_INPUT_MODE: + case M98090_REG_MIC1_INPUT_LEVEL: + case M98090_REG_MIC2_INPUT_LEVEL: + case M98090_REG_MIC_BIAS_VOLTAGE: + case M98090_REG_DIGITAL_MIC_ENABLE: + case M98090_REG_DIGITAL_MIC_CONFIG: + case M98090_REG_LEFT_ADC_MIXER: + case M98090_REG_RIGHT_ADC_MIXER: + case M98090_REG_LEFT_ADC_LEVEL: + case M98090_REG_RIGHT_ADC_LEVEL: + case M98090_REG_ADC_BIQUAD_LEVEL: + case M98090_REG_ADC_SIDETONE: + case M98090_REG_SYSTEM_CLOCK: + case M98090_REG_CLOCK_MODE: + case M98090_REG_CLOCK_RATIO_NI_MSB: + case M98090_REG_CLOCK_RATIO_NI_LSB: + case M98090_REG_CLOCK_RATIO_MI_MSB: + case M98090_REG_CLOCK_RATIO_MI_LSB: + case M98090_REG_MASTER_MODE: + case M98090_REG_INTERFACE_FORMAT: + case M98090_REG_TDM_CONTROL: + case M98090_REG_TDM_FORMAT: + case M98090_REG_IO_CONFIGURATION: + case M98090_REG_FILTER_CONFIG: + case M98090_REG_DAI_PLAYBACK_LEVEL: + case M98090_REG_DAI_PLAYBACK_LEVEL_EQ: + case M98090_REG_LEFT_HP_MIXER: + case M98090_REG_RIGHT_HP_MIXER: + case M98090_REG_HP_CONTROL: + case M98090_REG_LEFT_HP_VOLUME: + case M98090_REG_RIGHT_HP_VOLUME: + case M98090_REG_LEFT_SPK_MIXER: + case M98090_REG_RIGHT_SPK_MIXER: + case M98090_REG_SPK_CONTROL: + case M98090_REG_LEFT_SPK_VOLUME: + case M98090_REG_RIGHT_SPK_VOLUME: + case M98090_REG_DRC_TIMING: + case M98090_REG_DRC_COMPRESSOR: + case M98090_REG_DRC_EXPANDER: + case M98090_REG_DRC_GAIN: + case M98090_REG_RCV_LOUTL_MIXER: + case M98090_REG_RCV_LOUTL_CONTROL: + case M98090_REG_RCV_LOUTL_VOLUME: + case M98090_REG_LOUTR_MIXER: + case M98090_REG_LOUTR_CONTROL: + case M98090_REG_LOUTR_VOLUME: + case M98090_REG_JACK_DETECT: + case M98090_REG_INPUT_ENABLE: + case M98090_REG_OUTPUT_ENABLE: + case M98090_REG_LEVEL_CONTROL: + case M98090_REG_DSP_FILTER_ENABLE: + case M98090_REG_BIAS_CONTROL: + case M98090_REG_DAC_CONTROL: + case M98090_REG_ADC_CONTROL: + case M98090_REG_DEVICE_SHUTDOWN: + case M98090_REG_EQUALIZER_BASE ... M98090_REG_EQUALIZER_BASE + 0x68: + case M98090_REG_RECORD_BIQUAD_BASE ... M98090_REG_RECORD_BIQUAD_BASE + 0x0E: + case M98090_REG_DMIC3_VOLUME: + case M98090_REG_DMIC4_VOLUME: + case M98090_REG_DMIC34_BQ_PREATTEN: + case M98090_REG_RECORD_TDM_SLOT: + case M98090_REG_SAMPLE_RATE: + case M98090_REG_DMIC34_BIQUAD_BASE ... M98090_REG_DMIC34_BIQUAD_BASE + 0x0E: + return true; + default: + return false; + } +} -/* - * - * MAX98090 Registers Bit Fields - * - */ +static int max98090_reset(struct max98090_priv *max98090) +{ + int ret; -/* MAX98090_0x06_DAI_IF */ -#define MAX98090_DAI_IF_MASK 0x3F -#define MAX98090_RJ_M (1 << 5) -#define MAX98090_RJ_S (1 << 4) -#define MAX98090_LJ_M (1 << 3) -#define MAX98090_LJ_S (1 << 2) -#define MAX98090_I2S_M (1 << 1) -#define MAX98090_I2S_S (1 << 0) - -/* MAX98090_0x45_DEV_SHUTDOWN */ -#define MAX98090_SHDNRUN (1 << 7) - -/* codec private data */ -struct max98090_priv { - struct regmap *regmap; -}; - -static const struct reg_default max98090_reg_defaults[] = { - /* RESET / STATUS / INTERRUPT REGISTERS */ - {MAX98090_0x00_SW_RESET, 0x00}, - {MAX98090_0x01_INT_STS, 0x00}, - {MAX98090_0x02_JACK_STS, 0x00}, - {MAX98090_0x03_INT_MASK, 0x04}, - - /* QUICK SETUP REGISTERS */ - {MAX98090_0x04_SYS_CLK, 0x00}, - {MAX98090_0x05_SAMPLE_RATE, 0x00}, - {MAX98090_0x06_DAI_IF, 0x00}, - {MAX98090_0x07_DAC_PATH, 0x00}, - {MAX98090_0x08_MIC_TO_ADC, 0x00}, - {MAX98090_0x09_LINE_TO_ADC, 0x00}, - {MAX98090_0x0A_ANALOG_MIC_LOOP, 0x00}, - {MAX98090_0x0B_ANALOG_LINE_LOOP, 0x00}, - - /* ANALOG INPUT CONFIGURATION REGISTERS */ - {MAX98090_0x0D_INPUT_CONFIG, 0x00}, - {MAX98090_0x0E_LINE_IN_LVL, 0x1B}, - {MAX98090_0x0F_LINI_IN_CFG, 0x00}, - {MAX98090_0x10_MIC1_IN_LVL, 0x11}, - {MAX98090_0x11_MIC2_IN_LVL, 0x11}, - - /* MICROPHONE CONFIGURATION REGISTERS */ - {MAX98090_0x12_MIC_BIAS_VOL, 0x00}, - {MAX98090_0x13_DIGITAL_MIC_CFG, 0x00}, - {MAX98090_0x14_DIGITAL_MIC_MODE, 0x00}, - - /* ADC PATH AND CONFIGURATION REGISTERS */ - {MAX98090_0x15_L_ADC_MIX, 0x00}, - {MAX98090_0x16_R_ADC_MIX, 0x00}, - {MAX98090_0x17_L_ADC_LVL, 0x03}, - {MAX98090_0x18_R_ADC_LVL, 0x03}, - {MAX98090_0x19_ADC_BIQUAD_LVL, 0x00}, - {MAX98090_0x1A_ADC_SIDETONE, 0x00}, - - /* CLOCK CONFIGURATION REGISTERS */ - {MAX98090_0x1B_SYS_CLK, 0x00}, - {MAX98090_0x1C_CLK_MODE, 0x00}, - {MAX98090_0x1D_ANY_CLK1, 0x00}, - {MAX98090_0x1E_ANY_CLK2, 0x00}, - {MAX98090_0x1F_ANY_CLK3, 0x00}, - {MAX98090_0x20_ANY_CLK4, 0x00}, - {MAX98090_0x21_MASTER_MODE, 0x00}, - - /* INTERFACE CONTROL REGISTERS */ - {MAX98090_0x22_DAI_IF_FMT, 0x00}, - {MAX98090_0x23_DAI_TDM_FMT1, 0x00}, - {MAX98090_0x24_DAI_TDM_FMT2, 0x00}, - {MAX98090_0x25_DAI_IO_CFG, 0x00}, - {MAX98090_0x26_FILTER_CFG, 0x80}, - {MAX98090_0x27_DAI_PLAYBACK_LVL, 0x00}, - {MAX98090_0x28_EQ_PLAYBACK_LVL, 0x00}, - - /* HEADPHONE CONTROL REGISTERS */ - {MAX98090_0x29_L_HP_MIX, 0x00}, - {MAX98090_0x2A_R_HP_MIX, 0x00}, - {MAX98090_0x2B_HP_CTR, 0x00}, - {MAX98090_0x2C_L_HP_VOL, 0x1A}, - {MAX98090_0x2D_R_HP_VOL, 0x1A}, - - /* SPEAKER CONFIGURATION REGISTERS */ - {MAX98090_0x2E_L_SPK_MIX, 0x00}, - {MAX98090_0x2F_R_SPK_MIX, 0x00}, - {MAX98090_0x30_SPK_CTR, 0x00}, - {MAX98090_0x31_L_SPK_VOL, 0x2C}, - {MAX98090_0x32_R_SPK_VOL, 0x2C}, - - /* ALC CONFIGURATION REGISTERS */ - {MAX98090_0x33_ALC_TIMING, 0x00}, - {MAX98090_0x34_ALC_COMPRESSOR, 0x00}, - {MAX98090_0x35_ALC_EXPANDER, 0x00}, - {MAX98090_0x36_ALC_GAIN, 0x00}, - - /* RECEIVER AND LINE_OUTPUT REGISTERS */ - {MAX98090_0x37_RCV_LOUT_L_MIX, 0x00}, - {MAX98090_0x38_RCV_LOUT_L_CNTL, 0x00}, - {MAX98090_0x39_RCV_LOUT_L_VOL, 0x15}, - {MAX98090_0x3A_LOUT_R_MIX, 0x00}, - {MAX98090_0x3B_LOUT_R_CNTL, 0x00}, - {MAX98090_0x3C_LOUT_R_VOL, 0x15}, - - /* JACK DETECT AND ENABLE REGISTERS */ - {MAX98090_0x3D_JACK_DETECT, 0x00}, - {MAX98090_0x3E_IN_ENABLE, 0x00}, - {MAX98090_0x3F_OUT_ENABLE, 0x00}, - {MAX98090_0x40_LVL_CTR, 0x00}, - {MAX98090_0x41_DSP_FILTER_ENABLE, 0x00}, - - /* BIAS AND POWER MODE CONFIGURATION REGISTERS */ - {MAX98090_0x42_BIAS_CTR, 0x00}, - {MAX98090_0x43_DAC_CTR, 0x00}, - {MAX98090_0x44_ADC_CTR, 0x06}, - {MAX98090_0x45_DEV_SHUTDOWN, 0x00}, + /* Reset the codec by writing to this write-only reset register */ + ret = regmap_write(max98090->regmap, M98090_REG_SOFTWARE_RESET, + M98090_SWRESET_MASK); + if (ret < 0) { + dev_err(max98090->codec->dev, + "Failed to reset codec: %d\n", ret); + return ret; + } + + msleep(20); + return ret; +} + +static const unsigned int max98090_micboost_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), + 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0), +}; + +static const DECLARE_TLV_DB_SCALE(max98090_mic_tlv, 0, 100, 0); + +static const DECLARE_TLV_DB_SCALE(max98090_line_single_ended_tlv, + -600, 600, 0); + +static const unsigned int max98090_line_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 3, TLV_DB_SCALE_ITEM(-600, 300, 0), + 4, 5, TLV_DB_SCALE_ITEM(1400, 600, 0), +}; + +static const DECLARE_TLV_DB_SCALE(max98090_avg_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(max98090_av_tlv, -1200, 100, 0); + +static const DECLARE_TLV_DB_SCALE(max98090_dvg_tlv, 0, 600, 0); +static const DECLARE_TLV_DB_SCALE(max98090_dv_tlv, -1500, 100, 0); + +static const DECLARE_TLV_DB_SCALE(max98090_sidetone_tlv, -6050, 200, 0); + +static const DECLARE_TLV_DB_SCALE(max98090_alc_tlv, -1500, 100, 0); +static const DECLARE_TLV_DB_SCALE(max98090_alcmakeup_tlv, 0, 100, 0); +static const DECLARE_TLV_DB_SCALE(max98090_alccomp_tlv, -3100, 100, 0); +static const DECLARE_TLV_DB_SCALE(max98090_drcexp_tlv, -6600, 100, 0); + +static const unsigned int max98090_mixout_tlv[] = { + TLV_DB_RANGE_HEAD(2), + 0, 1, TLV_DB_SCALE_ITEM(-1200, 250, 0), + 2, 3, TLV_DB_SCALE_ITEM(-600, 600, 0), }; static const unsigned int max98090_hp_tlv[] = { TLV_DB_RANGE_HEAD(5), - 0x0, 0x6, TLV_DB_SCALE_ITEM(-6700, 400, 0), - 0x7, 0xE, TLV_DB_SCALE_ITEM(-4000, 300, 0), - 0xF, 0x15, TLV_DB_SCALE_ITEM(-1700, 200, 0), - 0x16, 0x1B, TLV_DB_SCALE_ITEM(-400, 100, 0), - 0x1C, 0x1F, TLV_DB_SCALE_ITEM(150, 50, 0), + 0, 6, TLV_DB_SCALE_ITEM(-6700, 400, 0), + 7, 14, TLV_DB_SCALE_ITEM(-4000, 300, 0), + 15, 21, TLV_DB_SCALE_ITEM(-1700, 200, 0), + 22, 27, TLV_DB_SCALE_ITEM(-400, 100, 0), + 28, 31, TLV_DB_SCALE_ITEM(150, 50, 0), }; -static struct snd_kcontrol_new max98090_snd_controls[] = { - SOC_DOUBLE_R_TLV("Headphone Volume", MAX98090_0x2C_L_HP_VOL, - MAX98090_0x2D_R_HP_VOL, 0, 31, 0, max98090_hp_tlv), +static const unsigned int max98090_spk_tlv[] = { + TLV_DB_RANGE_HEAD(5), + 0, 4, TLV_DB_SCALE_ITEM(-4800, 400, 0), + 5, 10, TLV_DB_SCALE_ITEM(-2900, 300, 0), + 11, 14, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 15, 29, TLV_DB_SCALE_ITEM(-500, 100, 0), + 30, 39, TLV_DB_SCALE_ITEM(950, 50, 0), }; -/* Left HeadPhone Mixer Switch */ -static struct snd_kcontrol_new max98090_left_hp_mixer_controls[] = { - SOC_DAPM_SINGLE("DACR Switch", MAX98090_0x29_L_HP_MIX, 1, 1, 0), - SOC_DAPM_SINGLE("DACL Switch", MAX98090_0x29_L_HP_MIX, 0, 1, 0), +static const unsigned int max98090_rcv_lout_tlv[] = { + TLV_DB_RANGE_HEAD(5), + 0, 6, TLV_DB_SCALE_ITEM(-6200, 400, 0), + 7, 14, TLV_DB_SCALE_ITEM(-3500, 300, 0), + 15, 21, TLV_DB_SCALE_ITEM(-1200, 200, 0), + 22, 27, TLV_DB_SCALE_ITEM(100, 100, 0), + 28, 31, TLV_DB_SCALE_ITEM(650, 50, 0), }; -/* Right HeadPhone Mixer Switch */ -static struct snd_kcontrol_new max98090_right_hp_mixer_controls[] = { - SOC_DAPM_SINGLE("DACR Switch", MAX98090_0x2A_R_HP_MIX, 1, 1, 0), - SOC_DAPM_SINGLE("DACL Switch", MAX98090_0x2A_R_HP_MIX, 0, 1, 0), +static int max98090_get_enab_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mask = (1 << fls(mc->max)) - 1; + unsigned int val = snd_soc_read(codec, mc->reg); + unsigned int *select; + + switch (mc->reg) { + case M98090_REG_MIC1_INPUT_LEVEL: + select = &(max98090->pa1en); + break; + case M98090_REG_MIC2_INPUT_LEVEL: + select = &(max98090->pa2en); + break; + case M98090_REG_ADC_SIDETONE: + select = &(max98090->sidetone); + break; + default: + return -EINVAL; + } + + val = (val >> mc->shift) & mask; + + if (val >= 1) { + /* If on, return the volume */ + val = val - 1; + *select = val; + } else { + /* If off, return last stored value */ + val = *select; + } + + ucontrol->value.integer.value[0] = val; + return 0; +} + +static int max98090_put_enab_tlv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int mask = (1 << fls(mc->max)) - 1; + unsigned int sel = ucontrol->value.integer.value[0]; + unsigned int val = snd_soc_read(codec, mc->reg); + unsigned int *select; + + switch (mc->reg) { + case M98090_REG_MIC1_INPUT_LEVEL: + select = &(max98090->pa1en); + break; + case M98090_REG_MIC2_INPUT_LEVEL: + select = &(max98090->pa2en); + break; + case M98090_REG_ADC_SIDETONE: + select = &(max98090->sidetone); + break; + default: + return -EINVAL; + } + + val = (val >> mc->shift) & mask; + + *select = sel; + + /* Setting a volume is only valid if it is already On */ + if (val >= 1) { + sel = sel + 1; + } else { + /* Write what was already there */ + sel = val; + } + + snd_soc_update_bits(codec, mc->reg, + mask << mc->shift, + sel << mc->shift); + + return 0; +} + +static const char * max98090_perf_pwr_text[] = + { "High Performance", "Low Power" }; +static const char * max98090_pwr_perf_text[] = + { "Low Power", "High Performance" }; + +static const struct soc_enum max98090_vcmbandgap_enum = + SOC_ENUM_SINGLE(M98090_REG_BIAS_CONTROL, M98090_VCM_MODE_SHIFT, + ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text); + +static const char * max98090_osr128_text[] = { "64*fs", "128*fs" }; + +static const struct soc_enum max98090_osr128_enum = + SOC_ENUM_SINGLE(M98090_REG_ADC_CONTROL, M98090_OSR128_SHIFT, + ARRAY_SIZE(max98090_osr128_text), max98090_osr128_text); + +static const char *max98090_mode_text[] = { "Voice", "Music" }; + +static const struct soc_enum max98090_mode_enum = + SOC_ENUM_SINGLE(M98090_REG_FILTER_CONFIG, M98090_MODE_SHIFT, + ARRAY_SIZE(max98090_mode_text), max98090_mode_text); + +static const struct soc_enum max98090_filter_dmic34mode_enum = + SOC_ENUM_SINGLE(M98090_REG_FILTER_CONFIG, + M98090_FLT_DMIC34MODE_SHIFT, + ARRAY_SIZE(max98090_mode_text), max98090_mode_text); + +static const char * max98090_drcatk_text[] = + { "0.5ms", "1ms", "5ms", "10ms", "25ms", "50ms", "100ms", "200ms" }; + +static const struct soc_enum max98090_drcatk_enum = + SOC_ENUM_SINGLE(M98090_REG_DRC_TIMING, M98090_DRCATK_SHIFT, + ARRAY_SIZE(max98090_drcatk_text), max98090_drcatk_text); + +static const char * max98090_drcrls_text[] = + { "8s", "4s", "2s", "1s", "0.5s", "0.25s", "0.125s", "0.0625s" }; + +static const struct soc_enum max98090_drcrls_enum = + SOC_ENUM_SINGLE(M98090_REG_DRC_TIMING, M98090_DRCRLS_SHIFT, + ARRAY_SIZE(max98090_drcrls_text), max98090_drcrls_text); + +static const char * max98090_alccmp_text[] = + { "1:1", "1:1.5", "1:2", "1:4", "1:INF" }; + +static const struct soc_enum max98090_alccmp_enum = + SOC_ENUM_SINGLE(M98090_REG_DRC_COMPRESSOR, M98090_DRCCMP_SHIFT, + ARRAY_SIZE(max98090_alccmp_text), max98090_alccmp_text); + +static const char * max98090_drcexp_text[] = { "1:1", "2:1", "3:1" }; + +static const struct soc_enum max98090_drcexp_enum = + SOC_ENUM_SINGLE(M98090_REG_DRC_EXPANDER, M98090_DRCEXP_SHIFT, + ARRAY_SIZE(max98090_drcexp_text), max98090_drcexp_text); + +static const struct soc_enum max98090_dac_perfmode_enum = + SOC_ENUM_SINGLE(M98090_REG_DAC_CONTROL, M98090_PERFMODE_SHIFT, + ARRAY_SIZE(max98090_perf_pwr_text), max98090_perf_pwr_text); + +static const struct soc_enum max98090_dachp_enum = + SOC_ENUM_SINGLE(M98090_REG_DAC_CONTROL, M98090_DACHP_SHIFT, + ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text); + +static const struct soc_enum max98090_adchp_enum = + SOC_ENUM_SINGLE(M98090_REG_ADC_CONTROL, M98090_ADCHP_SHIFT, + ARRAY_SIZE(max98090_pwr_perf_text), max98090_pwr_perf_text); + +static const struct snd_kcontrol_new max98090_snd_controls[] = { + SOC_ENUM("MIC Bias VCM Bandgap", max98090_vcmbandgap_enum), + + SOC_SINGLE("DMIC MIC Comp Filter Config", M98090_REG_DIGITAL_MIC_CONFIG, + M98090_DMIC_COMP_SHIFT, M98090_DMIC_COMP_NUM - 1, 0), + + SOC_SINGLE_EXT_TLV("MIC1 Boost Volume", + M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT, + M98090_MIC_PA1EN_NUM - 1, 0, max98090_get_enab_tlv, + max98090_put_enab_tlv, max98090_micboost_tlv), + + SOC_SINGLE_EXT_TLV("MIC2 Boost Volume", + M98090_REG_MIC2_INPUT_LEVEL, M98090_MIC_PA2EN_SHIFT, + M98090_MIC_PA2EN_NUM - 1, 0, max98090_get_enab_tlv, + max98090_put_enab_tlv, max98090_micboost_tlv), + + SOC_SINGLE_TLV("MIC1 Volume", M98090_REG_MIC1_INPUT_LEVEL, + M98090_MIC_PGAM1_SHIFT, M98090_MIC_PGAM1_NUM - 1, 1, + max98090_mic_tlv), + + SOC_SINGLE_TLV("MIC2 Volume", M98090_REG_MIC2_INPUT_LEVEL, + M98090_MIC_PGAM2_SHIFT, M98090_MIC_PGAM2_NUM - 1, 1, + max98090_mic_tlv), + + SOC_SINGLE_RANGE_TLV("LINEA Single Ended Volume", + M98090_REG_LINE_INPUT_LEVEL, M98090_MIXG135_SHIFT, 0, + M98090_MIXG135_NUM - 1, 1, max98090_line_single_ended_tlv), + + SOC_SINGLE_RANGE_TLV("LINEB Single Ended Volume", + M98090_REG_LINE_INPUT_LEVEL, M98090_MIXG246_SHIFT, 0, + M98090_MIXG246_NUM - 1, 1, max98090_line_single_ended_tlv), + + SOC_SINGLE_RANGE_TLV("LINEA Volume", M98090_REG_LINE_INPUT_LEVEL, + M98090_LINAPGA_SHIFT, 0, M98090_LINAPGA_NUM - 1, 1, + max98090_line_tlv), + + SOC_SINGLE_RANGE_TLV("LINEB Volume", M98090_REG_LINE_INPUT_LEVEL, + M98090_LINBPGA_SHIFT, 0, M98090_LINBPGA_NUM - 1, 1, + max98090_line_tlv), + + SOC_SINGLE("LINEA Ext Resistor Gain Mode", M98090_REG_INPUT_MODE, + M98090_EXTBUFA_SHIFT, M98090_EXTBUFA_NUM - 1, 0), + SOC_SINGLE("LINEB Ext Resistor Gain Mode", M98090_REG_INPUT_MODE, + M98090_EXTBUFB_SHIFT, M98090_EXTBUFB_NUM - 1, 0), + + SOC_SINGLE_TLV("ADCL Boost Volume", M98090_REG_LEFT_ADC_LEVEL, + M98090_AVLG_SHIFT, M98090_AVLG_NUM - 1, 0, + max98090_avg_tlv), + SOC_SINGLE_TLV("ADCR Boost Volume", M98090_REG_RIGHT_ADC_LEVEL, + M98090_AVRG_SHIFT, M98090_AVLG_NUM - 1, 0, + max98090_avg_tlv), + + SOC_SINGLE_TLV("ADCL Volume", M98090_REG_LEFT_ADC_LEVEL, + M98090_AVL_SHIFT, M98090_AVL_NUM - 1, 1, + max98090_av_tlv), + SOC_SINGLE_TLV("ADCR Volume", M98090_REG_RIGHT_ADC_LEVEL, + M98090_AVR_SHIFT, M98090_AVR_NUM - 1, 1, + max98090_av_tlv), + + SOC_ENUM("ADC Oversampling Rate", max98090_osr128_enum), + SOC_SINGLE("ADC Quantizer Dither", M98090_REG_ADC_CONTROL, + M98090_ADCDITHER_SHIFT, M98090_ADCDITHER_NUM - 1, 0), + SOC_ENUM("ADC High Performance Mode", max98090_adchp_enum), + + SOC_SINGLE("DAC Mono Mode", M98090_REG_IO_CONFIGURATION, + M98090_DMONO_SHIFT, M98090_DMONO_NUM - 1, 0), + SOC_SINGLE("SDIN Mode", M98090_REG_IO_CONFIGURATION, + M98090_SDIEN_SHIFT, M98090_SDIEN_NUM - 1, 0), + SOC_SINGLE("SDOUT Mode", M98090_REG_IO_CONFIGURATION, + M98090_SDOEN_SHIFT, M98090_SDOEN_NUM - 1, 0), + SOC_SINGLE("SDOUT Hi-Z Mode", M98090_REG_IO_CONFIGURATION, + M98090_HIZOFF_SHIFT, M98090_HIZOFF_NUM - 1, 1), + SOC_ENUM("Filter Mode", max98090_mode_enum), + SOC_SINGLE("Record Path DC Blocking", M98090_REG_FILTER_CONFIG, + M98090_AHPF_SHIFT, M98090_AHPF_NUM - 1, 0), + SOC_SINGLE("Playback Path DC Blocking", M98090_REG_FILTER_CONFIG, + M98090_DHPF_SHIFT, M98090_DHPF_NUM - 1, 0), + SOC_SINGLE_TLV("Digital BQ Volume", M98090_REG_ADC_BIQUAD_LEVEL, + M98090_AVBQ_SHIFT, M98090_AVBQ_NUM - 1, 1, max98090_dv_tlv), + SOC_SINGLE_EXT_TLV("Digital Sidetone Volume", + M98090_REG_ADC_SIDETONE, M98090_DVST_SHIFT, + M98090_DVST_NUM - 1, 1, max98090_get_enab_tlv, + max98090_put_enab_tlv, max98090_micboost_tlv), + SOC_SINGLE_TLV("Digital Coarse Volume", M98090_REG_DAI_PLAYBACK_LEVEL, + M98090_DVG_SHIFT, M98090_DVG_NUM - 1, 0, + max98090_dvg_tlv), + SOC_SINGLE_TLV("Digital Volume", M98090_REG_DAI_PLAYBACK_LEVEL, + M98090_DV_SHIFT, M98090_DV_NUM - 1, 1, + max98090_dv_tlv), + SND_SOC_BYTES("EQ Coefficients", M98090_REG_EQUALIZER_BASE, 105), + SOC_SINGLE("Digital EQ 3 Band Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_EQ3BANDEN_SHIFT, M98090_EQ3BANDEN_NUM - 1, 0), + SOC_SINGLE("Digital EQ 5 Band Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_EQ5BANDEN_SHIFT, M98090_EQ5BANDEN_NUM - 1, 0), + SOC_SINGLE("Digital EQ 7 Band Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_EQ7BANDEN_SHIFT, M98090_EQ7BANDEN_NUM - 1, 0), + SOC_SINGLE("Digital EQ Clipping Detection", M98090_REG_DAI_PLAYBACK_LEVEL_EQ, + M98090_EQCLPN_SHIFT, M98090_EQCLPN_NUM - 1, + 1), + SOC_SINGLE_TLV("Digital EQ Volume", M98090_REG_DAI_PLAYBACK_LEVEL_EQ, + M98090_DVEQ_SHIFT, M98090_DVEQ_NUM - 1, 1, + max98090_dv_tlv), + + SOC_SINGLE("ALC Enable", M98090_REG_DRC_TIMING, + M98090_DRCEN_SHIFT, M98090_DRCEN_NUM - 1, 0), + SOC_ENUM("ALC Attack Time", max98090_drcatk_enum), + SOC_ENUM("ALC Release Time", max98090_drcrls_enum), + SOC_SINGLE_TLV("ALC Make Up Volume", M98090_REG_DRC_GAIN, + M98090_DRCG_SHIFT, M98090_DRCG_NUM - 1, 0, + max98090_alcmakeup_tlv), + SOC_ENUM("ALC Compression Ratio", max98090_alccmp_enum), + SOC_ENUM("ALC Expansion Ratio", max98090_drcexp_enum), + SOC_SINGLE_TLV("ALC Compression Threshold Volume", + M98090_REG_DRC_COMPRESSOR, M98090_DRCTHC_SHIFT, + M98090_DRCTHC_NUM - 1, 1, max98090_alccomp_tlv), + SOC_SINGLE_TLV("ALC Expansion Threshold Volume", + M98090_REG_DRC_EXPANDER, M98090_DRCTHE_SHIFT, + M98090_DRCTHE_NUM - 1, 1, max98090_drcexp_tlv), + + SOC_ENUM("DAC HP Playback Performance Mode", + max98090_dac_perfmode_enum), + SOC_ENUM("DAC High Performance Mode", max98090_dachp_enum), + + SOC_SINGLE_TLV("Headphone Left Mixer Volume", + M98090_REG_HP_CONTROL, M98090_MIXHPLG_SHIFT, + M98090_MIXHPLG_NUM - 1, 1, max98090_mixout_tlv), + SOC_SINGLE_TLV("Headphone Right Mixer Volume", + M98090_REG_HP_CONTROL, M98090_MIXHPRG_SHIFT, + M98090_MIXHPRG_NUM - 1, 1, max98090_mixout_tlv), + + SOC_SINGLE_TLV("Speaker Left Mixer Volume", + M98090_REG_SPK_CONTROL, M98090_MIXSPLG_SHIFT, + M98090_MIXSPLG_NUM - 1, 1, max98090_mixout_tlv), + SOC_SINGLE_TLV("Speaker Right Mixer Volume", + M98090_REG_SPK_CONTROL, M98090_MIXSPRG_SHIFT, + M98090_MIXSPRG_NUM - 1, 1, max98090_mixout_tlv), + + SOC_SINGLE_TLV("Receiver Left Mixer Volume", + M98090_REG_RCV_LOUTL_CONTROL, M98090_MIXRCVLG_SHIFT, + M98090_MIXRCVLG_NUM - 1, 1, max98090_mixout_tlv), + SOC_SINGLE_TLV("Receiver Right Mixer Volume", + M98090_REG_LOUTR_CONTROL, M98090_MIXRCVRG_SHIFT, + M98090_MIXRCVRG_NUM - 1, 1, max98090_mixout_tlv), + + SOC_DOUBLE_R_TLV("Headphone Volume", M98090_REG_LEFT_HP_VOLUME, + M98090_REG_RIGHT_HP_VOLUME, M98090_HPVOLL_SHIFT, + M98090_HPVOLL_NUM - 1, 0, max98090_hp_tlv), + + SOC_DOUBLE_R_RANGE_TLV("Speaker Volume", + M98090_REG_LEFT_SPK_VOLUME, M98090_REG_RIGHT_SPK_VOLUME, + M98090_SPVOLL_SHIFT, 24, M98090_SPVOLL_NUM - 1 + 24, + 0, max98090_spk_tlv), + + SOC_DOUBLE_R_TLV("Receiver Volume", M98090_REG_RCV_LOUTL_VOLUME, + M98090_REG_LOUTR_VOLUME, M98090_RCVLVOL_SHIFT, + M98090_RCVLVOL_NUM - 1, 0, max98090_rcv_lout_tlv), + + SOC_SINGLE("Headphone Left Switch", M98090_REG_LEFT_HP_VOLUME, + M98090_HPLM_SHIFT, 1, 1), + SOC_SINGLE("Headphone Right Switch", M98090_REG_RIGHT_HP_VOLUME, + M98090_HPRM_SHIFT, 1, 1), + + SOC_SINGLE("Speaker Left Switch", M98090_REG_LEFT_SPK_VOLUME, + M98090_SPLM_SHIFT, 1, 1), + SOC_SINGLE("Speaker Right Switch", M98090_REG_RIGHT_SPK_VOLUME, + M98090_SPRM_SHIFT, 1, 1), + + SOC_SINGLE("Receiver Left Switch", M98090_REG_RCV_LOUTL_VOLUME, + M98090_RCVLM_SHIFT, 1, 1), + SOC_SINGLE("Receiver Right Switch", M98090_REG_LOUTR_VOLUME, + M98090_RCVRM_SHIFT, 1, 1), + + SOC_SINGLE("Zero-Crossing Detection", M98090_REG_LEVEL_CONTROL, + M98090_ZDENN_SHIFT, M98090_ZDENN_NUM - 1, 1), + SOC_SINGLE("Enhanced Vol Smoothing", M98090_REG_LEVEL_CONTROL, + M98090_VS2ENN_SHIFT, M98090_VS2ENN_NUM - 1, 1), + SOC_SINGLE("Volume Adjustment Smoothing", M98090_REG_LEVEL_CONTROL, + M98090_VSENN_SHIFT, M98090_VSENN_NUM - 1, 1), + + SND_SOC_BYTES("Biquad Coefficients", M98090_REG_RECORD_BIQUAD_BASE, 15), + SOC_SINGLE("Biquad Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_ADCBQEN_SHIFT, M98090_ADCBQEN_NUM - 1, 0), }; -static struct snd_soc_dapm_widget max98090_dapm_widgets[] = { - /* Output */ +static const struct snd_kcontrol_new max98091_snd_controls[] = { + + SOC_SINGLE("DMIC34 Zeropad", M98090_REG_SAMPLE_RATE, + M98090_DMIC34_ZEROPAD_SHIFT, + M98090_DMIC34_ZEROPAD_NUM - 1, 0), + + SOC_ENUM("Filter DMIC34 Mode", max98090_filter_dmic34mode_enum), + SOC_SINGLE("DMIC34 DC Blocking", M98090_REG_FILTER_CONFIG, + M98090_FLT_DMIC34HPF_SHIFT, + M98090_FLT_DMIC34HPF_NUM - 1, 0), + + SOC_SINGLE_TLV("DMIC3 Boost Volume", M98090_REG_DMIC3_VOLUME, + M98090_DMIC_AV3G_SHIFT, M98090_DMIC_AV3G_NUM - 1, 0, + max98090_avg_tlv), + SOC_SINGLE_TLV("DMIC4 Boost Volume", M98090_REG_DMIC4_VOLUME, + M98090_DMIC_AV4G_SHIFT, M98090_DMIC_AV4G_NUM - 1, 0, + max98090_avg_tlv), + + SOC_SINGLE_TLV("DMIC3 Volume", M98090_REG_DMIC3_VOLUME, + M98090_DMIC_AV3_SHIFT, M98090_DMIC_AV3_NUM - 1, 1, + max98090_av_tlv), + SOC_SINGLE_TLV("DMIC4 Volume", M98090_REG_DMIC4_VOLUME, + M98090_DMIC_AV4_SHIFT, M98090_DMIC_AV4_NUM - 1, 1, + max98090_av_tlv), + + SND_SOC_BYTES("DMIC34 Biquad Coefficients", + M98090_REG_DMIC34_BIQUAD_BASE, 15), + SOC_SINGLE("DMIC34 Biquad Switch", M98090_REG_DSP_FILTER_ENABLE, + M98090_DMIC34BQEN_SHIFT, M98090_DMIC34BQEN_NUM - 1, 0), + + SOC_SINGLE_TLV("DMIC34 BQ PreAttenuation Volume", + M98090_REG_DMIC34_BQ_PREATTEN, M98090_AV34BQ_SHIFT, + M98090_AV34BQ_NUM - 1, 1, max98090_dv_tlv), +}; + +static int max98090_micinput_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + + unsigned int val = snd_soc_read(codec, w->reg); + + if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) + val = (val & M98090_MIC_PA1EN_MASK) >> M98090_MIC_PA1EN_SHIFT; + else + val = (val & M98090_MIC_PA2EN_MASK) >> M98090_MIC_PA2EN_SHIFT; + + + if (val >= 1) { + if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) { + max98090->pa1en = val - 1; /* Update for volatile */ + } else { + max98090->pa2en = val - 1; /* Update for volatile */ + } + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* If turning on, set to most recently selected volume */ + if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) + val = max98090->pa1en + 1; + else + val = max98090->pa2en + 1; + break; + case SND_SOC_DAPM_POST_PMD: + /* If turning off, turn off */ + val = 0; + break; + default: + return -EINVAL; + } + + if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) + snd_soc_update_bits(codec, w->reg, M98090_MIC_PA1EN_MASK, + val << M98090_MIC_PA1EN_SHIFT); + else + snd_soc_update_bits(codec, w->reg, M98090_MIC_PA2EN_MASK, + val << M98090_MIC_PA2EN_SHIFT); + + return 0; +} + +static const char *mic1_mux_text[] = { "IN12", "IN56" }; + +static const struct soc_enum mic1_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_INPUT_MODE, M98090_EXTMIC1_SHIFT, + ARRAY_SIZE(mic1_mux_text), mic1_mux_text); + +static const struct snd_kcontrol_new max98090_mic1_mux = + SOC_DAPM_ENUM("MIC1 Mux", mic1_mux_enum); + +static const char *mic2_mux_text[] = { "IN34", "IN56" }; + +static const struct soc_enum mic2_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_INPUT_MODE, M98090_EXTMIC2_SHIFT, + ARRAY_SIZE(mic2_mux_text), mic2_mux_text); + +static const struct snd_kcontrol_new max98090_mic2_mux = + SOC_DAPM_ENUM("MIC2 Mux", mic2_mux_enum); + +static const char * max98090_micpre_text[] = { "Off", "On" }; + +static const struct soc_enum max98090_pa1en_enum = + SOC_ENUM_SINGLE(M98090_REG_MIC1_INPUT_LEVEL, M98090_MIC_PA1EN_SHIFT, + ARRAY_SIZE(max98090_micpre_text), max98090_micpre_text); + +static const struct soc_enum max98090_pa2en_enum = + SOC_ENUM_SINGLE(M98090_REG_MIC2_INPUT_LEVEL, M98090_MIC_PA2EN_SHIFT, + ARRAY_SIZE(max98090_micpre_text), max98090_micpre_text); + +/* LINEA mixer switch */ +static const struct snd_kcontrol_new max98090_linea_mixer_controls[] = { + SOC_DAPM_SINGLE("IN1 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN1SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN3 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN3SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN5 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN5SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN34DIFF_SHIFT, 1, 0), +}; + +/* LINEB mixer switch */ +static const struct snd_kcontrol_new max98090_lineb_mixer_controls[] = { + SOC_DAPM_SINGLE("IN2 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN2SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN4 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN4SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN6 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN6SEEN_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_LINE_INPUT_CONFIG, + M98090_IN56DIFF_SHIFT, 1, 0), +}; + +/* Left ADC mixer switch */ +static const struct snd_kcontrol_new max98090_left_adc_mixer_controls[] = { + SOC_DAPM_SINGLE("IN12 Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_IN12DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_IN34DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_IN65DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_ADC_MIXER, + M98090_MIXADL_MIC2_SHIFT, 1, 0), +}; + +/* Right ADC mixer switch */ +static const struct snd_kcontrol_new max98090_right_adc_mixer_controls[] = { + SOC_DAPM_SINGLE("IN12 Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_IN12DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN34 Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_IN34DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("IN56 Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_IN65DIFF_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_ADC_MIXER, + M98090_MIXADR_MIC2_SHIFT, 1, 0), +}; + +static const char *lten_mux_text[] = { "Normal", "Loopthrough" }; + +static const struct soc_enum ltenl_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LTEN_SHIFT, + ARRAY_SIZE(lten_mux_text), lten_mux_text); + +static const struct soc_enum ltenr_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LTEN_SHIFT, + ARRAY_SIZE(lten_mux_text), lten_mux_text); + +static const struct snd_kcontrol_new max98090_ltenl_mux = + SOC_DAPM_ENUM("LTENL Mux", ltenl_mux_enum); + +static const struct snd_kcontrol_new max98090_ltenr_mux = + SOC_DAPM_ENUM("LTENR Mux", ltenr_mux_enum); + +static const char *lben_mux_text[] = { "Normal", "Loopback" }; + +static const struct soc_enum lbenl_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LBEN_SHIFT, + ARRAY_SIZE(lben_mux_text), lben_mux_text); + +static const struct soc_enum lbenr_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_IO_CONFIGURATION, M98090_LBEN_SHIFT, + ARRAY_SIZE(lben_mux_text), lben_mux_text); + +static const struct snd_kcontrol_new max98090_lbenl_mux = + SOC_DAPM_ENUM("LBENL Mux", lbenl_mux_enum); + +static const struct snd_kcontrol_new max98090_lbenr_mux = + SOC_DAPM_ENUM("LBENR Mux", lbenr_mux_enum); + +static const char *stenl_mux_text[] = { "Normal", "Sidetone Left" }; + +static const char *stenr_mux_text[] = { "Normal", "Sidetone Right" }; + +static const struct soc_enum stenl_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_ADC_SIDETONE, M98090_DSTSL_SHIFT, + ARRAY_SIZE(stenl_mux_text), stenl_mux_text); + +static const struct soc_enum stenr_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_ADC_SIDETONE, M98090_DSTSR_SHIFT, + ARRAY_SIZE(stenr_mux_text), stenr_mux_text); + +static const struct snd_kcontrol_new max98090_stenl_mux = + SOC_DAPM_ENUM("STENL Mux", stenl_mux_enum); + +static const struct snd_kcontrol_new max98090_stenr_mux = + SOC_DAPM_ENUM("STENR Mux", stenr_mux_enum); + +/* Left speaker mixer switch */ +static const struct + snd_kcontrol_new max98090_left_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_SPK_MIXER, + M98090_MIXSPL_MIC2_SHIFT, 1, 0), +}; + +/* Right speaker mixer switch */ +static const struct + snd_kcontrol_new max98090_right_speaker_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_SPK_MIXER, + M98090_MIXSPR_MIC2_SHIFT, 1, 0), +}; + +/* Left headphone mixer switch */ +static const struct snd_kcontrol_new max98090_left_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LEFT_HP_MIXER, + M98090_MIXHPL_MIC2_SHIFT, 1, 0), +}; + +/* Right headphone mixer switch */ +static const struct snd_kcontrol_new max98090_right_hp_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RIGHT_HP_MIXER, + M98090_MIXHPR_MIC2_SHIFT, 1, 0), +}; + +/* Left receiver mixer switch */ +static const struct snd_kcontrol_new max98090_left_rcv_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_RCV_LOUTL_MIXER, + M98090_MIXRCVL_MIC2_SHIFT, 1, 0), +}; + +/* Right receiver mixer switch */ +static const struct snd_kcontrol_new max98090_right_rcv_mixer_controls[] = { + SOC_DAPM_SINGLE("Left DAC Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_DACL_SHIFT, 1, 0), + SOC_DAPM_SINGLE("Right DAC Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_DACR_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEA Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_LINEA_SHIFT, 1, 0), + SOC_DAPM_SINGLE("LINEB Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_LINEB_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC1 Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_MIC1_SHIFT, 1, 0), + SOC_DAPM_SINGLE("MIC2 Switch", M98090_REG_LOUTR_MIXER, + M98090_MIXRCVR_MIC2_SHIFT, 1, 0), +}; + +static const char *linmod_mux_text[] = { "Left Only", "Left and Right" }; + +static const struct soc_enum linmod_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_LOUTR_MIXER, M98090_LINMOD_SHIFT, + ARRAY_SIZE(linmod_mux_text), linmod_mux_text); + +static const struct snd_kcontrol_new max98090_linmod_mux = + SOC_DAPM_ENUM("LINMOD Mux", linmod_mux_enum); + +static const char *mixhpsel_mux_text[] = { "DAC Only", "HP Mixer" }; + +/* + * This is a mux as it selects the HP output, but to DAPM it is a Mixer enable + */ +static const struct soc_enum mixhplsel_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_HP_CONTROL, M98090_MIXHPLSEL_SHIFT, + ARRAY_SIZE(mixhpsel_mux_text), mixhpsel_mux_text); + +static const struct snd_kcontrol_new max98090_mixhplsel_mux = + SOC_DAPM_ENUM("MIXHPLSEL Mux", mixhplsel_mux_enum); + +static const struct soc_enum mixhprsel_mux_enum = + SOC_ENUM_SINGLE(M98090_REG_HP_CONTROL, M98090_MIXHPRSEL_SHIFT, + ARRAY_SIZE(mixhpsel_mux_text), mixhpsel_mux_text); + +static const struct snd_kcontrol_new max98090_mixhprsel_mux = + SOC_DAPM_ENUM("MIXHPRSEL Mux", mixhprsel_mux_enum); + +static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { + + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("DMICL"), + SND_SOC_DAPM_INPUT("DMICR"), + SND_SOC_DAPM_INPUT("IN1"), + SND_SOC_DAPM_INPUT("IN2"), + SND_SOC_DAPM_INPUT("IN3"), + SND_SOC_DAPM_INPUT("IN4"), + SND_SOC_DAPM_INPUT("IN5"), + SND_SOC_DAPM_INPUT("IN6"), + SND_SOC_DAPM_INPUT("IN12"), + SND_SOC_DAPM_INPUT("IN34"), + SND_SOC_DAPM_INPUT("IN56"), + + SND_SOC_DAPM_SUPPLY("MICBIAS", M98090_REG_INPUT_ENABLE, + M98090_MBEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SHDN", M98090_REG_DEVICE_SHUTDOWN, + M98090_SHDNN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SDIEN", M98090_REG_IO_CONFIGURATION, + M98090_SDIEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SDOEN", M98090_REG_IO_CONFIGURATION, + M98090_SDOEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMICL_ENA", M98090_REG_DIGITAL_MIC_ENABLE, + M98090_DIGMICL_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMICR_ENA", M98090_REG_DIGITAL_MIC_ENABLE, + M98090_DIGMICR_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("AHPF", M98090_REG_FILTER_CONFIG, + M98090_AHPF_SHIFT, 0, NULL, 0), + +/* + * Note: Sysclk and misc power supplies are taken care of by SHDN + */ + + SND_SOC_DAPM_MUX("MIC1 Mux", SND_SOC_NOPM, + 0, 0, &max98090_mic1_mux), + + SND_SOC_DAPM_MUX("MIC2 Mux", SND_SOC_NOPM, + 0, 0, &max98090_mic2_mux), + + SND_SOC_DAPM_PGA_E("MIC1 Input", M98090_REG_MIC1_INPUT_LEVEL, + M98090_MIC_PA1EN_SHIFT, 0, NULL, 0, max98090_micinput_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("MIC2 Input", M98090_REG_MIC2_INPUT_LEVEL, + M98090_MIC_PA2EN_SHIFT, 0, NULL, 0, max98090_micinput_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("LINEA Mixer", SND_SOC_NOPM, 0, 0, + &max98090_linea_mixer_controls[0], + ARRAY_SIZE(max98090_linea_mixer_controls)), + + SND_SOC_DAPM_MIXER("LINEB Mixer", SND_SOC_NOPM, 0, 0, + &max98090_lineb_mixer_controls[0], + ARRAY_SIZE(max98090_lineb_mixer_controls)), + + SND_SOC_DAPM_PGA("LINEA Input", M98090_REG_INPUT_ENABLE, + M98090_LINEAEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("LINEB Input", M98090_REG_INPUT_ENABLE, + M98090_LINEBEN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0, + &max98090_left_adc_mixer_controls[0], + ARRAY_SIZE(max98090_left_adc_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0, + &max98090_right_adc_mixer_controls[0], + ARRAY_SIZE(max98090_right_adc_mixer_controls)), + + SND_SOC_DAPM_ADC("ADCL", NULL, M98090_REG_INPUT_ENABLE, + M98090_ADLEN_SHIFT, 0), + SND_SOC_DAPM_ADC("ADCR", NULL, M98090_REG_INPUT_ENABLE, + M98090_ADREN_SHIFT, 0), + + SND_SOC_DAPM_AIF_OUT("AIFOUTL", "HiFi Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIFOUTR", "HiFi Capture", 1, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MUX("LBENL Mux", SND_SOC_NOPM, + 0, 0, &max98090_lbenl_mux), + + SND_SOC_DAPM_MUX("LBENR Mux", SND_SOC_NOPM, + 0, 0, &max98090_lbenr_mux), + + SND_SOC_DAPM_MUX("LTENL Mux", SND_SOC_NOPM, + 0, 0, &max98090_ltenl_mux), + + SND_SOC_DAPM_MUX("LTENR Mux", SND_SOC_NOPM, + 0, 0, &max98090_ltenr_mux), + + SND_SOC_DAPM_MUX("STENL Mux", SND_SOC_NOPM, + 0, 0, &max98090_stenl_mux), + + SND_SOC_DAPM_MUX("STENR Mux", SND_SOC_NOPM, + 0, 0, &max98090_stenr_mux), + + SND_SOC_DAPM_AIF_IN("AIFINL", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIFINR", "HiFi Playback", 1, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DACL", NULL, M98090_REG_OUTPUT_ENABLE, + M98090_DALEN_SHIFT, 0), + SND_SOC_DAPM_DAC("DACR", NULL, M98090_REG_OUTPUT_ENABLE, + M98090_DAREN_SHIFT, 0), + + SND_SOC_DAPM_MIXER("Left Headphone Mixer", SND_SOC_NOPM, 0, 0, + &max98090_left_hp_mixer_controls[0], + ARRAY_SIZE(max98090_left_hp_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Headphone Mixer", SND_SOC_NOPM, 0, 0, + &max98090_right_hp_mixer_controls[0], + ARRAY_SIZE(max98090_right_hp_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left Speaker Mixer", SND_SOC_NOPM, 0, 0, + &max98090_left_speaker_mixer_controls[0], + ARRAY_SIZE(max98090_left_speaker_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Speaker Mixer", SND_SOC_NOPM, 0, 0, + &max98090_right_speaker_mixer_controls[0], + ARRAY_SIZE(max98090_right_speaker_mixer_controls)), + + SND_SOC_DAPM_MIXER("Left Receiver Mixer", SND_SOC_NOPM, 0, 0, + &max98090_left_rcv_mixer_controls[0], + ARRAY_SIZE(max98090_left_rcv_mixer_controls)), + + SND_SOC_DAPM_MIXER("Right Receiver Mixer", SND_SOC_NOPM, 0, 0, + &max98090_right_rcv_mixer_controls[0], + ARRAY_SIZE(max98090_right_rcv_mixer_controls)), + + SND_SOC_DAPM_MUX("LINMOD Mux", M98090_REG_LOUTR_MIXER, + M98090_LINMOD_SHIFT, 0, &max98090_linmod_mux), + + SND_SOC_DAPM_MUX("MIXHPLSEL Mux", M98090_REG_HP_CONTROL, + M98090_MIXHPLSEL_SHIFT, 0, &max98090_mixhplsel_mux), + + SND_SOC_DAPM_MUX("MIXHPRSEL Mux", M98090_REG_HP_CONTROL, + M98090_MIXHPRSEL_SHIFT, 0, &max98090_mixhprsel_mux), + + SND_SOC_DAPM_PGA("HP Left Out", M98090_REG_OUTPUT_ENABLE, + M98090_HPLEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("HP Right Out", M98090_REG_OUTPUT_ENABLE, + M98090_HPREN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_PGA("SPK Left Out", M98090_REG_OUTPUT_ENABLE, + M98090_SPLEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("SPK Right Out", M98090_REG_OUTPUT_ENABLE, + M98090_SPREN_SHIFT, 0, NULL, 0), + + SND_SOC_DAPM_PGA("RCV Left Out", M98090_REG_OUTPUT_ENABLE, + M98090_RCVLEN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_PGA("RCV Right Out", M98090_REG_OUTPUT_ENABLE, + M98090_RCVREN_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("HPL"), SND_SOC_DAPM_OUTPUT("HPR"), + SND_SOC_DAPM_OUTPUT("SPKL"), + SND_SOC_DAPM_OUTPUT("SPKR"), + SND_SOC_DAPM_OUTPUT("RCVL"), + SND_SOC_DAPM_OUTPUT("RCVR"), +}; + +static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = { - /* PGA */ - SND_SOC_DAPM_PGA("HPL Out", MAX98090_0x3F_OUT_ENABLE, 7, 0, NULL, 0), - SND_SOC_DAPM_PGA("HPR Out", MAX98090_0x3F_OUT_ENABLE, 6, 0, NULL, 0), + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), - /* Mixer */ - SND_SOC_DAPM_MIXER("HPL Mixer", SND_SOC_NOPM, 0, 0, - max98090_left_hp_mixer_controls, - ARRAY_SIZE(max98090_left_hp_mixer_controls)), + SND_SOC_DAPM_SUPPLY("DMIC3_ENA", M98090_REG_DIGITAL_MIC_ENABLE, + M98090_DIGMIC3_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DMIC4_ENA", M98090_REG_DIGITAL_MIC_ENABLE, + M98090_DIGMIC4_SHIFT, 0, NULL, 0), +}; - SND_SOC_DAPM_MIXER("HPR Mixer", SND_SOC_NOPM, 0, 0, - max98090_right_hp_mixer_controls, - ARRAY_SIZE(max98090_right_hp_mixer_controls)), +static const struct snd_soc_dapm_route max98090_dapm_routes[] = { + + {"MIC1 Input", NULL, "MIC1"}, + {"MIC2 Input", NULL, "MIC2"}, + + {"DMICL", NULL, "DMICL_ENA"}, + {"DMICR", NULL, "DMICR_ENA"}, + {"DMICL", NULL, "AHPF"}, + {"DMICR", NULL, "AHPF"}, + + /* MIC1 input mux */ + {"MIC1 Mux", "IN12", "IN12"}, + {"MIC1 Mux", "IN56", "IN56"}, + + /* MIC2 input mux */ + {"MIC2 Mux", "IN34", "IN34"}, + {"MIC2 Mux", "IN56", "IN56"}, + + {"MIC1 Input", NULL, "MIC1 Mux"}, + {"MIC2 Input", NULL, "MIC2 Mux"}, + + /* Left ADC input mixer */ + {"Left ADC Mixer", "IN12 Switch", "IN12"}, + {"Left ADC Mixer", "IN34 Switch", "IN34"}, + {"Left ADC Mixer", "IN56 Switch", "IN56"}, + {"Left ADC Mixer", "LINEA Switch", "LINEA Input"}, + {"Left ADC Mixer", "LINEB Switch", "LINEB Input"}, + {"Left ADC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left ADC Mixer", "MIC2 Switch", "MIC2 Input"}, + + /* Right ADC input mixer */ + {"Right ADC Mixer", "IN12 Switch", "IN12"}, + {"Right ADC Mixer", "IN34 Switch", "IN34"}, + {"Right ADC Mixer", "IN56 Switch", "IN56"}, + {"Right ADC Mixer", "LINEA Switch", "LINEA Input"}, + {"Right ADC Mixer", "LINEB Switch", "LINEB Input"}, + {"Right ADC Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right ADC Mixer", "MIC2 Switch", "MIC2 Input"}, + + /* Line A input mixer */ + {"LINEA Mixer", "IN1 Switch", "IN1"}, + {"LINEA Mixer", "IN3 Switch", "IN3"}, + {"LINEA Mixer", "IN5 Switch", "IN5"}, + {"LINEA Mixer", "IN34 Switch", "IN34"}, + + /* Line B input mixer */ + {"LINEB Mixer", "IN2 Switch", "IN2"}, + {"LINEB Mixer", "IN4 Switch", "IN4"}, + {"LINEB Mixer", "IN6 Switch", "IN6"}, + {"LINEB Mixer", "IN56 Switch", "IN56"}, + + {"LINEA Input", NULL, "LINEA Mixer"}, + {"LINEB Input", NULL, "LINEB Mixer"}, + + /* Inputs */ + {"ADCL", NULL, "Left ADC Mixer"}, + {"ADCR", NULL, "Right ADC Mixer"}, + {"ADCL", NULL, "SHDN"}, + {"ADCR", NULL, "SHDN"}, + + {"LBENL Mux", "Normal", "ADCL"}, + {"LBENL Mux", "Normal", "DMICL"}, + {"LBENL Mux", "Loopback", "LTENL Mux"}, + {"LBENR Mux", "Normal", "ADCR"}, + {"LBENR Mux", "Normal", "DMICR"}, + {"LBENR Mux", "Loopback", "LTENR Mux"}, + + {"AIFOUTL", NULL, "LBENL Mux"}, + {"AIFOUTR", NULL, "LBENR Mux"}, + {"AIFOUTL", NULL, "SHDN"}, + {"AIFOUTR", NULL, "SHDN"}, + {"AIFOUTL", NULL, "SDOEN"}, + {"AIFOUTR", NULL, "SDOEN"}, + + {"LTENL Mux", "Normal", "AIFINL"}, + {"LTENL Mux", "Loopthrough", "LBENL Mux"}, + {"LTENR Mux", "Normal", "AIFINR"}, + {"LTENR Mux", "Loopthrough", "LBENR Mux"}, + + {"DACL", NULL, "LTENL Mux"}, + {"DACR", NULL, "LTENR Mux"}, + + {"STENL Mux", "Sidetone Left", "ADCL"}, + {"STENL Mux", "Sidetone Left", "DMICL"}, + {"STENR Mux", "Sidetone Right", "ADCR"}, + {"STENR Mux", "Sidetone Right", "DMICR"}, + {"DACL", "NULL", "STENL Mux"}, + {"DACR", "NULL", "STENL Mux"}, + + {"AIFINL", NULL, "SHDN"}, + {"AIFINR", NULL, "SHDN"}, + {"AIFINL", NULL, "SDIEN"}, + {"AIFINR", NULL, "SDIEN"}, + {"DACL", NULL, "SHDN"}, + {"DACR", NULL, "SHDN"}, + + /* Left headphone output mixer */ + {"Left Headphone Mixer", "Left DAC Switch", "DACL"}, + {"Left Headphone Mixer", "Right DAC Switch", "DACR"}, + {"Left Headphone Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left Headphone Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left Headphone Mixer", "LINEA Switch", "LINEA Input"}, + {"Left Headphone Mixer", "LINEB Switch", "LINEB Input"}, + + /* Right headphone output mixer */ + {"Right Headphone Mixer", "Left DAC Switch", "DACL"}, + {"Right Headphone Mixer", "Right DAC Switch", "DACR"}, + {"Right Headphone Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right Headphone Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right Headphone Mixer", "LINEA Switch", "LINEA Input"}, + {"Right Headphone Mixer", "LINEB Switch", "LINEB Input"}, + + /* Left speaker output mixer */ + {"Left Speaker Mixer", "Left DAC Switch", "DACL"}, + {"Left Speaker Mixer", "Right DAC Switch", "DACR"}, + {"Left Speaker Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left Speaker Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left Speaker Mixer", "LINEA Switch", "LINEA Input"}, + {"Left Speaker Mixer", "LINEB Switch", "LINEB Input"}, + + /* Right speaker output mixer */ + {"Right Speaker Mixer", "Left DAC Switch", "DACL"}, + {"Right Speaker Mixer", "Right DAC Switch", "DACR"}, + {"Right Speaker Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right Speaker Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right Speaker Mixer", "LINEA Switch", "LINEA Input"}, + {"Right Speaker Mixer", "LINEB Switch", "LINEB Input"}, + + /* Left Receiver output mixer */ + {"Left Receiver Mixer", "Left DAC Switch", "DACL"}, + {"Left Receiver Mixer", "Right DAC Switch", "DACR"}, + {"Left Receiver Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Left Receiver Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Left Receiver Mixer", "LINEA Switch", "LINEA Input"}, + {"Left Receiver Mixer", "LINEB Switch", "LINEB Input"}, + + /* Right Receiver output mixer */ + {"Right Receiver Mixer", "Left DAC Switch", "DACL"}, + {"Right Receiver Mixer", "Right DAC Switch", "DACR"}, + {"Right Receiver Mixer", "MIC1 Switch", "MIC1 Input"}, + {"Right Receiver Mixer", "MIC2 Switch", "MIC2 Input"}, + {"Right Receiver Mixer", "LINEA Switch", "LINEA Input"}, + {"Right Receiver Mixer", "LINEB Switch", "LINEB Input"}, + + {"MIXHPLSEL Mux", "HP Mixer", "Left Headphone Mixer"}, + + /* + * Disable this for lowest power if bypassing + * the DAC with an analog signal + */ + {"HP Left Out", NULL, "DACL"}, + {"HP Left Out", NULL, "MIXHPLSEL Mux"}, + + {"MIXHPRSEL Mux", "HP Mixer", "Right Headphone Mixer"}, + + /* + * Disable this for lowest power if bypassing + * the DAC with an analog signal + */ + {"HP Right Out", NULL, "DACR"}, + {"HP Right Out", NULL, "MIXHPRSEL Mux"}, + + {"SPK Left Out", NULL, "Left Speaker Mixer"}, + {"SPK Right Out", NULL, "Right Speaker Mixer"}, + {"RCV Left Out", NULL, "Left Receiver Mixer"}, + + {"LINMOD Mux", "Left and Right", "Right Receiver Mixer"}, + {"LINMOD Mux", "Left Only", "Left Receiver Mixer"}, + {"RCV Right Out", NULL, "LINMOD Mux"}, + + {"HPL", NULL, "HP Left Out"}, + {"HPR", NULL, "HP Right Out"}, + {"SPKL", NULL, "SPK Left Out"}, + {"SPKR", NULL, "SPK Right Out"}, + {"RCVL", NULL, "RCV Left Out"}, + {"RCVR", NULL, "RCV Right Out"}, - /* DAC */ - SND_SOC_DAPM_DAC("DACL", "Hifi Playback", MAX98090_0x3F_OUT_ENABLE, 0, 0), - SND_SOC_DAPM_DAC("DACR", "Hifi Playback", MAX98090_0x3F_OUT_ENABLE, 1, 0), }; -static struct snd_soc_dapm_route max98090_audio_map[] = { - /* Output */ - {"HPL", NULL, "HPL Out"}, - {"HPR", NULL, "HPR Out"}, +static const struct snd_soc_dapm_route max98091_dapm_routes[] = { + + /* DMIC inputs */ + {"DMIC3", NULL, "DMIC3_ENA"}, + {"DMIC4", NULL, "DMIC4_ENA"}, + {"DMIC3", NULL, "AHPF"}, + {"DMIC4", NULL, "AHPF"}, + +}; + +static int max98090_add_widgets(struct snd_soc_codec *codec) +{ + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_add_codec_controls(codec, max98090_snd_controls, + ARRAY_SIZE(max98090_snd_controls)); - /* PGA */ - {"HPL Out", NULL, "HPL Mixer"}, - {"HPR Out", NULL, "HPR Mixer"}, + if (max98090->devtype == MAX98091) { + snd_soc_add_codec_controls(codec, max98091_snd_controls, + ARRAY_SIZE(max98091_snd_controls)); + } + + snd_soc_dapm_new_controls(dapm, max98090_dapm_widgets, + ARRAY_SIZE(max98090_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, max98090_dapm_routes, + ARRAY_SIZE(max98090_dapm_routes)); + + if (max98090->devtype == MAX98091) { + snd_soc_dapm_new_controls(dapm, max98091_dapm_widgets, + ARRAY_SIZE(max98091_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, max98091_dapm_routes, + ARRAY_SIZE(max98091_dapm_routes)); + + } - /* Mixer*/ - {"HPL Mixer", "DACR Switch", "DACR"}, - {"HPL Mixer", "DACL Switch", "DACL"}, + return 0; +} + +static const int pclk_rates[] = { + 12000000, 12000000, 13000000, 13000000, + 16000000, 16000000, 19200000, 19200000 +}; + +static const int lrclk_rates[] = { + 8000, 16000, 8000, 16000, + 8000, 16000, 8000, 16000 +}; - {"HPR Mixer", "DACR Switch", "DACR"}, - {"HPR Mixer", "DACL Switch", "DACL"}, +static const int user_pclk_rates[] = { + 13000000, 13000000 }; -static bool max98090_volatile(struct device *dev, unsigned int reg) +static const int user_lrclk_rates[] = { + 44100, 48000 +}; + +static const unsigned long long ni_value[] = { + 3528, 768 +}; + +static const unsigned long long mi_value[] = { + 8125, 1625 +}; + +static void max98090_configure_bclk(struct snd_soc_codec *codec) { - if ((reg == MAX98090_0x01_INT_STS) || - (reg == MAX98090_0x02_JACK_STS) || - (reg > MAX98090_REG_MAX_CACHED)) - return true; + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + unsigned long long ni; + int i; + + if (!max98090->sysclk) { + dev_err(codec->dev, "No SYSCLK configured\n"); + return; + } + + if (!max98090->bclk || !max98090->lrclk) { + dev_err(codec->dev, "No audio clocks configured\n"); + return; + } + + /* Skip configuration when operating as slave */ + if (!(snd_soc_read(codec, M98090_REG_MASTER_MODE) & + M98090_MAS_MASK)) { + return; + } - return false; + /* Check for supported PCLK to LRCLK ratios */ + for (i = 0; i < ARRAY_SIZE(pclk_rates); i++) { + if ((pclk_rates[i] == max98090->sysclk) && + (lrclk_rates[i] == max98090->lrclk)) { + dev_dbg(codec->dev, + "Found supported PCLK to LRCLK rates 0x%x\n", + i + 0x8); + + snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE, + M98090_FREQ_MASK, + (i + 0x8) << M98090_FREQ_SHIFT); + snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE, + M98090_USE_M1_MASK, 0); + return; + } + } + + /* Check for user calculated MI and NI ratios */ + for (i = 0; i < ARRAY_SIZE(user_pclk_rates); i++) { + if ((user_pclk_rates[i] == max98090->sysclk) && + (user_lrclk_rates[i] == max98090->lrclk)) { + dev_dbg(codec->dev, + "Found user supported PCLK to LRCLK rates\n"); + dev_dbg(codec->dev, "i %d ni %lld mi %lld\n", + i, ni_value[i], mi_value[i]); + + snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE, + M98090_FREQ_MASK, 0); + snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE, + M98090_USE_M1_MASK, + 1 << M98090_USE_M1_SHIFT); + + snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB, + (ni_value[i] >> 8) & 0x7F); + snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB, + ni_value[i] & 0xFF); + snd_soc_write(codec, M98090_REG_CLOCK_RATIO_MI_MSB, + (mi_value[i] >> 8) & 0x7F); + snd_soc_write(codec, M98090_REG_CLOCK_RATIO_MI_LSB, + mi_value[i] & 0xFF); + + return; + } + } + + /* + * Calculate based on MI = 65536 (not as good as either method above) + */ + snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE, + M98090_FREQ_MASK, 0); + snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE, + M98090_USE_M1_MASK, 0); + + /* + * Configure NI when operating as master + * Note: There is a small, but significant audio quality improvement + * by calculating ni and mi. + */ + ni = 65536ULL * (max98090->lrclk < 50000 ? 96ULL : 48ULL) + * (unsigned long long int)max98090->lrclk; + do_div(ni, (unsigned long long int)max98090->sysclk); + dev_info(codec->dev, "No better method found\n"); + dev_info(codec->dev, "Calculating ni %lld with mi 65536\n", ni); + snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_MSB, + (ni >> 8) & 0x7F); + snd_soc_write(codec, M98090_REG_CLOCK_RATIO_NI_LSB, ni & 0xFF); } -static int max98090_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) +static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) { - struct snd_soc_codec *codec = dai->codec; - unsigned int val; + struct snd_soc_codec *codec = codec_dai->codec; + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct max98090_cdata *cdata; + u8 regval; + + max98090->dai_fmt = fmt; + cdata = &max98090->dai[0]; + + if (fmt != cdata->fmt) { + cdata->fmt = fmt; + + regval = 0; + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* Set to slave mode PLL - MAS mode off */ + snd_soc_write(codec, + M98090_REG_CLOCK_RATIO_NI_MSB, 0x00); + snd_soc_write(codec, + M98090_REG_CLOCK_RATIO_NI_LSB, 0x00); + snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE, + M98090_USE_M1_MASK, 0); + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* Set to master mode */ + if (max98090->tdm_slots == 4) { + /* TDM */ + regval |= M98090_MAS_MASK | + M98090_BSEL_64; + } else if (max98090->tdm_slots == 3) { + /* TDM */ + regval |= M98090_MAS_MASK | + M98090_BSEL_48; + } else { + /* Few TDM slots, or No TDM */ + regval |= M98090_MAS_MASK | + M98090_BSEL_32; + } + break; + case SND_SOC_DAIFMT_CBS_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + default: + dev_err(codec->dev, "DAI clock mode unsupported"); + return -EINVAL; + } + snd_soc_write(codec, M98090_REG_MASTER_MODE, regval); + + regval = 0; + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + regval |= M98090_DLY_MASK; + break; + case SND_SOC_DAIFMT_LEFT_J: + break; + case SND_SOC_DAIFMT_RIGHT_J: + regval |= M98090_RJ_MASK; + break; + case SND_SOC_DAIFMT_DSP_A: + /* Not supported mode */ + default: + dev_err(codec->dev, "DAI format unsupported"); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_NB_IF: + regval |= M98090_WCI_MASK; + break; + case SND_SOC_DAIFMT_IB_NF: + regval |= M98090_BCI_MASK; + break; + case SND_SOC_DAIFMT_IB_IF: + regval |= M98090_BCI_MASK|M98090_WCI_MASK; + break; + default: + dev_err(codec->dev, "DAI invert mode unsupported"); + return -EINVAL; + } + + /* + * This accommodates an inverted logic in the MAX98090 chip + * for Bit Clock Invert (BCI). The inverted logic is only + * seen for the case of TDM mode. The remaining cases have + * normal logic. + */ + if (max98090->tdm_slots > 1) { + regval ^= M98090_BCI_MASK; + } + + snd_soc_write(codec, + M98090_REG_INTERFACE_FORMAT, regval); + } - switch (params_rate(params)) { - case 96000: - val = 1 << 5; - break; - case 32000: - val = 1 << 4; - break; - case 48000: - val = 1 << 3; + return 0; +} + +static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_codec *codec = codec_dai->codec; + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct max98090_cdata *cdata; + cdata = &max98090->dai[0]; + + if (slots < 0 || slots > 4) + return -EINVAL; + + max98090->tdm_slots = slots; + max98090->tdm_width = slot_width; + + if (max98090->tdm_slots > 1) { + /* SLOTL SLOTR SLOTDLY */ + snd_soc_write(codec, M98090_REG_TDM_FORMAT, + 0 << M98090_TDM_SLOTL_SHIFT | + 1 << M98090_TDM_SLOTR_SHIFT | + 0 << M98090_TDM_SLOTDLY_SHIFT); + + /* FSW TDM */ + snd_soc_update_bits(codec, M98090_REG_TDM_CONTROL, + M98090_TDM_MASK, + M98090_TDM_MASK); + } + + /* + * Normally advisable to set TDM first, but this permits either order + */ + cdata->fmt = 0; + max98090_dai_set_fmt(codec_dai, max98090->dai_fmt); + + return 0; +} + +static int max98090_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) +{ + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + int ret; + + switch (level) { + case SND_SOC_BIAS_ON: + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + ret = regcache_sync(max98090->regmap); + + if (ret != 0) { + dev_err(codec->dev, + "Failed to sync cache: %d\n", ret); + return ret; + } + } + + if (max98090->jack_state == M98090_JACK_STATE_HEADSET) { + /* + * Set to normal bias level. + */ + snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE, + M98090_MBVSEL_MASK, M98090_MBVSEL_2V8); + } break; - case 44100: - val = 1 << 2; + + case SND_SOC_BIAS_PREPARE: break; - case 16000: - val = 1 << 1; + + case SND_SOC_BIAS_STANDBY: + case SND_SOC_BIAS_OFF: + /* Set internal pull-up to lowest power mode */ + snd_soc_update_bits(codec, M98090_REG_JACK_DETECT, + M98090_JDWK_MASK, M98090_JDWK_MASK); + regcache_mark_dirty(max98090->regmap); break; - case 8000: - val = 1 << 0; + } + codec->dapm.bias_level = level; + return 0; +} + +static const int comp_pclk_rates[] = { + 11289600, 12288000, 12000000, 13000000, 19200000 +}; + +static const int dmic_micclk[] = { + 2, 2, 2, 2, 4, 2 +}; + +static const int comp_lrclk_rates[] = { + 8000, 16000, 32000, 44100, 48000, 96000 +}; + +static const int dmic_comp[6][6] = { + {7, 8, 3, 3, 3, 3}, + {7, 8, 3, 3, 3, 3}, + {7, 8, 3, 3, 3, 3}, + {7, 8, 3, 1, 1, 1}, + {7, 8, 3, 1, 2, 2}, + {7, 8, 3, 3, 3, 3} +}; + +static int max98090_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct max98090_cdata *cdata; + int i, j; + + cdata = &max98090->dai[0]; + max98090->bclk = snd_soc_params_to_bclk(params); + if (params_channels(params) == 1) + max98090->bclk *= 2; + + max98090->lrclk = params_rate(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_update_bits(codec, M98090_REG_INTERFACE_FORMAT, + M98090_WS_MASK, 0); break; default: - dev_err(codec->dev, "unsupported rate\n"); return -EINVAL; } - snd_soc_update_bits(codec, MAX98090_0x05_SAMPLE_RATE, 0x03F, val); + + max98090_configure_bclk(codec); + + cdata->rate = max98090->lrclk; + + /* Update filter mode */ + if (max98090->lrclk < 24000) + snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG, + M98090_MODE_MASK, 0); + else + snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG, + M98090_MODE_MASK, M98090_MODE_MASK); + + /* Update sample rate mode */ + if (max98090->lrclk < 50000) + snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG, + M98090_DHF_MASK, 0); + else + snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG, + M98090_DHF_MASK, M98090_DHF_MASK); + + /* Check for supported PCLK to LRCLK ratios */ + for (j = 0; j < ARRAY_SIZE(comp_pclk_rates); j++) { + if (comp_pclk_rates[j] == max98090->sysclk) { + break; + } + } + + for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) { + if (max98090->lrclk <= (comp_lrclk_rates[i] + + comp_lrclk_rates[i + 1]) / 2) { + break; + } + } + + snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_ENABLE, + M98090_MICCLK_MASK, + dmic_micclk[j] << M98090_MICCLK_SHIFT); + + snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_CONFIG, + M98090_DMIC_COMP_MASK, + dmic_comp[j][i] << M98090_DMIC_COMP_SHIFT); return 0; } +/* + * PLL / Sysclk + */ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai, - int clk_id, unsigned int freq, int dir) + int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = dai->codec; - unsigned int val; - - snd_soc_update_bits(codec, MAX98090_0x45_DEV_SHUTDOWN, - MAX98090_SHDNRUN, 0); - - switch (freq) { - case 26000000: - val = 1 << 7; - break; - case 19200000: - val = 1 << 6; - break; - case 13000000: - val = 1 << 5; - break; - case 12288000: - val = 1 << 4; - break; - case 12000000: - val = 1 << 3; - break; - case 11289600: - val = 1 << 2; - break; - default: + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + + /* Requested clock frequency is already setup */ + if (freq == max98090->sysclk) + return 0; + + /* Setup clocks for slave mode, and using the PLL + * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) + * 0x02 (when master clk is 20MHz to 40MHz).. + * 0x03 (when master clk is 40MHz to 60MHz).. + */ + if ((freq >= 10000000) && (freq < 20000000)) { + snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK, + M98090_PSCLK_DIV1); + } else if ((freq >= 20000000) && (freq < 40000000)) { + snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK, + M98090_PSCLK_DIV2); + } else if ((freq >= 40000000) && (freq < 60000000)) { + snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK, + M98090_PSCLK_DIV4); + } else { dev_err(codec->dev, "Invalid master clock frequency\n"); return -EINVAL; } - snd_soc_update_bits(codec, MAX98090_0x04_SYS_CLK, 0xFD, val); - snd_soc_update_bits(codec, MAX98090_0x45_DEV_SHUTDOWN, - MAX98090_SHDNRUN, MAX98090_SHDNRUN); + max98090->sysclk = freq; - dev_dbg(dai->dev, "sysclk is %uHz\n", freq); + max98090_configure_bclk(codec); return 0; } -static int max98090_dai_set_fmt(struct snd_soc_dai *dai, - unsigned int fmt) +static int max98090_dai_digital_mute(struct snd_soc_dai *codec_dai, int mute) { - struct snd_soc_codec *codec = dai->codec; - int is_master; - u8 val; + struct snd_soc_codec *codec = codec_dai->codec; + int regval; - /* master/slave mode */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - is_master = 1; - break; - case SND_SOC_DAIFMT_CBS_CFS: - is_master = 0; - break; - default: - dev_err(codec->dev, "unsupported clock\n"); - return -EINVAL; + regval = mute ? M98090_DVM_MASK : 0; + snd_soc_update_bits(codec, M98090_REG_DAI_PLAYBACK_LEVEL, + M98090_DVM_MASK, regval); + + return 0; +} + +static void max98090_jack_work(struct work_struct *work) +{ + struct max98090_priv *max98090 = container_of(work, + struct max98090_priv, + jack_work.work); + struct snd_soc_codec *codec = max98090->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + int status = 0; + int reg; + + /* Read a second time */ + if (max98090->jack_state == M98090_JACK_STATE_NO_HEADSET) { + + /* Strong pull up allows mic detection */ + snd_soc_update_bits(codec, M98090_REG_JACK_DETECT, + M98090_JDWK_MASK, 0); + + msleep(50); + + reg = snd_soc_read(codec, M98090_REG_JACK_STATUS); + + /* Weak pull up allows only insertion detection */ + snd_soc_update_bits(codec, M98090_REG_JACK_DETECT, + M98090_JDWK_MASK, M98090_JDWK_MASK); + } else { + reg = snd_soc_read(codec, M98090_REG_JACK_STATUS); } - /* format */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_I2S: - val = (is_master) ? MAX98090_I2S_M : MAX98090_I2S_S; - break; - case SND_SOC_DAIFMT_RIGHT_J: - val = (is_master) ? MAX98090_RJ_M : MAX98090_RJ_S; - break; - case SND_SOC_DAIFMT_LEFT_J: - val = (is_master) ? MAX98090_LJ_M : MAX98090_LJ_S; - break; - default: - dev_err(codec->dev, "unsupported format\n"); - return -EINVAL; + reg = snd_soc_read(codec, M98090_REG_JACK_STATUS); + + switch (reg & (M98090_LSNS_MASK | M98090_JKSNS_MASK)) { + case M98090_LSNS_MASK | M98090_JKSNS_MASK: + dev_dbg(codec->dev, "No Headset Detected\n"); + + max98090->jack_state = M98090_JACK_STATE_NO_HEADSET; + + status |= 0; + + break; + + case 0: + if (max98090->jack_state == + M98090_JACK_STATE_HEADSET) { + + dev_dbg(codec->dev, + "Headset Button Down Detected\n"); + + /* + * max98090_headset_button_event(codec) + * could be defined, then called here. + */ + + status |= SND_JACK_HEADSET; + status |= SND_JACK_BTN_0; + + break; + } + + /* Line is reported as Headphone */ + /* Nokia Headset is reported as Headphone */ + /* Mono Headphone is reported as Headphone */ + dev_dbg(codec->dev, "Headphone Detected\n"); + + max98090->jack_state = M98090_JACK_STATE_HEADPHONE; + + status |= SND_JACK_HEADPHONE; + + break; + + case M98090_JKSNS_MASK: + dev_dbg(codec->dev, "Headset Detected\n"); + + max98090->jack_state = M98090_JACK_STATE_HEADSET; + + status |= SND_JACK_HEADSET; + + break; + + default: + dev_dbg(codec->dev, "Unrecognized Jack Status\n"); + break; + } + + snd_soc_jack_report(max98090->jack, status, + SND_JACK_HEADSET | SND_JACK_BTN_0); + + snd_soc_dapm_sync(dapm); +} + +static irqreturn_t max98090_interrupt(int irq, void *data) +{ + struct snd_soc_codec *codec = data; + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + int ret; + unsigned int mask; + unsigned int active; + + dev_dbg(codec->dev, "***** max98090_interrupt *****\n"); + + ret = regmap_read(max98090->regmap, M98090_REG_INTERRUPT_S, &mask); + + if (ret != 0) { + dev_err(codec->dev, + "failed to read M98090_REG_INTERRUPT_S: %d\n", + ret); + return IRQ_NONE; + } + + ret = regmap_read(max98090->regmap, M98090_REG_DEVICE_STATUS, &active); + + if (ret != 0) { + dev_err(codec->dev, + "failed to read M98090_REG_DEVICE_STATUS: %d\n", + ret); + return IRQ_NONE; + } + + dev_dbg(codec->dev, "active=0x%02x mask=0x%02x -> active=0x%02x\n", + active, mask, active & mask); + + active &= mask; + + if (!active) + return IRQ_NONE; + + if (active & M98090_CLD_MASK) { + dev_err(codec->dev, "M98090_CLD_MASK\n"); + } + + if (active & M98090_SLD_MASK) { + dev_dbg(codec->dev, "M98090_SLD_MASK\n"); } - snd_soc_update_bits(codec, MAX98090_0x06_DAI_IF, - MAX98090_DAI_IF_MASK, val); + + if (active & M98090_ULK_MASK) { + dev_err(codec->dev, "M98090_ULK_MASK\n"); + } + + if (active & M98090_JDET_MASK) { + dev_dbg(codec->dev, "M98090_JDET_MASK\n"); + + pm_wakeup_event(codec->dev, 100); + + schedule_delayed_work(&max98090->jack_work, + msecs_to_jiffies(100)); + } + + if (active & M98090_DRCACT_MASK) { + dev_dbg(codec->dev, "M98090_DRCACT_MASK\n"); + } + + if (active & M98090_DRCCLP_MASK) { + dev_err(codec->dev, "M98090_DRCCLP_MASK\n"); + } + + return IRQ_HANDLED; +} + +/** + * max98090_mic_detect - Enable microphone detection via the MAX98090 IRQ + * + * @codec: MAX98090 codec + * @jack: jack to report detection events on + * + * Enable microphone detection via IRQ on the MAX98090. If GPIOs are + * being used to bring out signals to the processor then only platform + * data configuration is needed for MAX98090 and processor GPIOs should + * be configured using snd_soc_jack_add_gpios() instead. + * + * If no jack is supplied detection will be disabled. + */ +int max98090_mic_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack) +{ + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "max98090_mic_detect\n"); + + max98090->jack = jack; + if (jack) { + snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S, + M98090_IJDET_MASK, + 1 << M98090_IJDET_SHIFT); + } else { + snd_soc_update_bits(codec, M98090_REG_INTERRUPT_S, + M98090_IJDET_MASK, + 0); + } + + /* Send an initial empty report */ + snd_soc_jack_report(max98090->jack, 0, + SND_JACK_HEADSET | SND_JACK_BTN_0); + + schedule_delayed_work(&max98090->jack_work, + msecs_to_jiffies(100)); return 0; } +EXPORT_SYMBOL_GPL(max98090_mic_detect); #define MAX98090_RATES SNDRV_PCM_RATE_8000_96000 #define MAX98090_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) static struct snd_soc_dai_ops max98090_dai_ops = { - .set_sysclk = max98090_dai_set_sysclk, - .set_fmt = max98090_dai_set_fmt, - .hw_params = max98090_dai_hw_params, + .set_sysclk = max98090_dai_set_sysclk, + .set_fmt = max98090_dai_set_fmt, + .set_tdm_slot = max98090_set_tdm_slot, + .hw_params = max98090_dai_hw_params, + .digital_mute = max98090_dai_digital_mute, }; -static struct snd_soc_dai_driver max98090_dai = { - .name = "max98090-Hifi", +static struct snd_soc_dai_driver max98090_dai[] = { +{ + .name = "HiFi", .playback = { - .stream_name = "Playback", - .channels_min = 1, - .channels_max = 2, - .rates = MAX98090_RATES, - .formats = MAX98090_FORMATS, + .stream_name = "HiFi Playback", + .channels_min = 2, + .channels_max = 2, + .rates = MAX98090_RATES, + .formats = MAX98090_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98090_RATES, + .formats = MAX98090_FORMATS, }, - .ops = &max98090_dai_ops, + .ops = &max98090_dai_ops, +} }; +static void max98090_handle_pdata(struct snd_soc_codec *codec) +{ + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct max98090_pdata *pdata = max98090->pdata; + + if (!pdata) { + dev_err(codec->dev, "No platform data\n"); + return; + } + +} + static int max98090_probe(struct snd_soc_codec *codec) { - struct max98090_priv *priv = snd_soc_codec_get_drvdata(codec); - struct device *dev = codec->dev; - int ret; + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + struct max98090_cdata *cdata; + int ret = 0; + + dev_dbg(codec->dev, "max98090_probe\n"); + + max98090->codec = codec; + + codec->control_data = max98090->regmap; - codec->control_data = priv->regmap; ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_REGMAP); - if (ret < 0) { - dev_err(dev, "Failed to set cache I/O: %d\n", ret); + if (ret != 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); return ret; } - /* Device active */ - snd_soc_update_bits(codec, MAX98090_0x45_DEV_SHUTDOWN, - MAX98090_SHDNRUN, MAX98090_SHDNRUN); + /* Reset the codec, the DSP core, and disable all interrupts */ + max98090_reset(max98090); - return 0; + /* Initialize private data */ + + max98090->sysclk = (unsigned)-1; + + cdata = &max98090->dai[0]; + cdata->rate = (unsigned)-1; + cdata->fmt = (unsigned)-1; + + max98090->lin_state = 0; + max98090->pa1en = 0; + max98090->pa2en = 0; + max98090->extmic_mux = 0; + + ret = snd_soc_read(codec, M98090_REG_REVISION_ID); + if (ret < 0) { + dev_err(codec->dev, "Failed to read device revision: %d\n", + ret); + goto err_access; + } + + if ((ret >= M98090_REVA) && (ret <= M98090_REVA + 0x0f)) { + max98090->devtype = MAX98090; + dev_info(codec->dev, "MAX98090 REVID=0x%02x\n", ret); + } else if ((ret >= M98091_REVA) && (ret <= M98091_REVA + 0x0f)) { + max98090->devtype = MAX98091; + dev_info(codec->dev, "MAX98091 REVID=0x%02x\n", ret); + } else { + max98090->devtype = MAX98090; + dev_err(codec->dev, "Unrecognized revision 0x%02x\n", ret); + } + + max98090->jack_state = M98090_JACK_STATE_NO_HEADSET; + + INIT_DELAYED_WORK(&max98090->jack_work, max98090_jack_work); + + /* Enable jack detection */ + snd_soc_write(codec, M98090_REG_JACK_DETECT, + M98090_JDETEN_MASK | M98090_JDEB_25MS); + + /* Register for interrupts */ + dev_dbg(codec->dev, "irq = %d\n", max98090->irq); + + ret = request_threaded_irq(max98090->irq, NULL, + max98090_interrupt, IRQF_TRIGGER_FALLING, + "max98090_interrupt", codec); + if (ret < 0) { + dev_err(codec->dev, "request_irq failed: %d\n", + ret); + } + + /* + * Clear any old interrupts. + * An old interrupt ocurring prior to installing the ISR + * can keep a new interrupt from generating a trigger. + */ + snd_soc_read(codec, M98090_REG_DEVICE_STATUS); + + /* High Performance is default */ + snd_soc_update_bits(codec, M98090_REG_DAC_CONTROL, + M98090_DACHP_MASK, + 1 << M98090_DACHP_SHIFT); + snd_soc_update_bits(codec, M98090_REG_DAC_CONTROL, + M98090_PERFMODE_MASK, + 0 << M98090_PERFMODE_SHIFT); + snd_soc_update_bits(codec, M98090_REG_ADC_CONTROL, + M98090_ADCHP_MASK, + 1 << M98090_ADCHP_SHIFT); + + /* Turn on VCM bandgap reference */ + snd_soc_write(codec, M98090_REG_BIAS_CONTROL, + M98090_VCM_MODE_MASK); + + max98090_handle_pdata(codec); + + max98090_add_widgets(codec); + +err_access: + return ret; } static int max98090_remove(struct snd_soc_codec *codec) { + struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); + + cancel_delayed_work_sync(&max98090->jack_work); + return 0; } static struct snd_soc_codec_driver soc_codec_dev_max98090 = { - .probe = max98090_probe, - .remove = max98090_remove, - .controls = max98090_snd_controls, - .num_controls = ARRAY_SIZE(max98090_snd_controls), - .dapm_widgets = max98090_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(max98090_dapm_widgets), - .dapm_routes = max98090_audio_map, - .num_dapm_routes = ARRAY_SIZE(max98090_audio_map), + .probe = max98090_probe, + .remove = max98090_remove, + .set_bias_level = max98090_set_bias_level, }; static const struct regmap_config max98090_regmap = { - .reg_bits = 8, - .val_bits = 8, - .max_register = MAX98090_REG_END, - .volatile_reg = max98090_volatile, - .cache_type = REGCACHE_RBTREE, - .reg_defaults = max98090_reg_defaults, - .num_reg_defaults = ARRAY_SIZE(max98090_reg_defaults), + .reg_bits = 8, + .val_bits = 8, + + .max_register = MAX98090_MAX_REGISTER, + .reg_defaults = max98090_reg, + .num_reg_defaults = ARRAY_SIZE(max98090_reg), + .volatile_reg = max98090_volatile_register, + .readable_reg = max98090_readable_register, + .cache_type = REGCACHE_RBTREE, }; static int max98090_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { - struct max98090_priv *priv; - struct device *dev = &i2c->dev; - unsigned int val; + struct max98090_priv *max98090; int ret; - priv = devm_kzalloc(dev, sizeof(struct max98090_priv), - GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap); - if (IS_ERR(priv->regmap)) { - ret = PTR_ERR(priv->regmap); - dev_err(dev, "Failed to init regmap: %d\n", ret); - return ret; - } + pr_debug("max98090_i2c_probe\n"); - i2c_set_clientdata(i2c, priv); + max98090 = devm_kzalloc(&i2c->dev, sizeof(struct max98090_priv), + GFP_KERNEL); + if (max98090 == NULL) + return -ENOMEM; - ret = regmap_read(priv->regmap, MAX98090_0xFF_REV_ID, &val); - if (ret < 0) { - dev_err(dev, "Failed to read device revision: %d\n", ret); - return ret; + max98090->devtype = id->driver_data; + i2c_set_clientdata(i2c, max98090); + max98090->control_data = i2c; + max98090->pdata = i2c->dev.platform_data; + max98090->irq = i2c->irq; + + max98090->regmap = regmap_init_i2c(i2c, &max98090_regmap); + if (IS_ERR(max98090->regmap)) { + ret = PTR_ERR(max98090->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); + goto err_enable; } - dev_info(dev, "revision 0x%02x\n", val); - ret = snd_soc_register_codec(dev, - &soc_codec_dev_max98090, - &max98090_dai, 1); + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_max98090, max98090_dai, + ARRAY_SIZE(max98090_dai)); + if (ret < 0) + regmap_exit(max98090->regmap); +err_enable: return ret; } static int max98090_i2c_remove(struct i2c_client *client) { + struct max98090_priv *max98090 = dev_get_drvdata(&client->dev); snd_soc_unregister_codec(&client->dev); + regmap_exit(max98090->regmap); + return 0; +} + +static int max98090_runtime_resume(struct device *dev) +{ + struct max98090_priv *max98090 = dev_get_drvdata(dev); + + regcache_cache_only(max98090->regmap, false); + + regcache_sync(max98090->regmap); + return 0; } +static int max98090_runtime_suspend(struct device *dev) +{ + struct max98090_priv *max98090 = dev_get_drvdata(dev); + + regcache_cache_only(max98090->regmap, true); + + return 0; +} + +static struct dev_pm_ops max98090_pm = { + SET_RUNTIME_PM_OPS(max98090_runtime_suspend, + max98090_runtime_resume, NULL) +}; + static const struct i2c_device_id max98090_i2c_id[] = { - { "max98090", 0 }, + { "max98090", MAX98090 }, { } }; MODULE_DEVICE_TABLE(i2c, max98090_i2c_id); @@ -565,13 +2384,15 @@ static struct i2c_driver max98090_i2c_driver = { .driver = { .name = "max98090", .owner = THIS_MODULE, + .pm = &max98090_pm, }, - .probe = max98090_i2c_probe, - .remove = max98090_i2c_remove, - .id_table = max98090_i2c_id, + .probe = max98090_i2c_probe, + .remove = max98090_i2c_remove, + .id_table = max98090_i2c_id, }; + module_i2c_driver(max98090_i2c_driver); MODULE_DESCRIPTION("ALSA SoC MAX98090 driver"); -MODULE_AUTHOR("Peter Hsiang, Kuninori Morimoto"); +MODULE_AUTHOR("Peter Hsiang, Jesse Marroqin, Jerry Wong"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h new file mode 100755 index 000000000000..7e103f249053 --- /dev/null +++ b/sound/soc/codecs/max98090.h @@ -0,0 +1,1549 @@ +/* + * max98090.h -- MAX98090 ALSA SoC Audio driver + * + * Copyright 2011-2012 Maxim Integrated Products + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _MAX98090_H +#define _MAX98090_H + +#include <linux/version.h> + +/* One can override the Linux version here with an explicit version number */ +#define M98090_LINUX_VERSION LINUX_VERSION_CODE + +/* + * MAX98090 Register Definitions + */ + +#define M98090_REG_SOFTWARE_RESET 0x00 +#define M98090_REG_DEVICE_STATUS 0x01 +#define M98090_REG_JACK_STATUS 0x02 +#define M98090_REG_INTERRUPT_S 0x03 +#define M98090_REG_QUICK_SYSTEM_CLOCK 0x04 +#define M98090_REG_QUICK_SAMPLE_RATE 0x05 +#define M98090_REG_DAI_INTERFACE 0x06 +#define M98090_REG_DAC_PATH 0x07 +#define M98090_REG_MIC_DIRECT_TO_ADC 0x08 +#define M98090_REG_LINE_TO_ADC 0x09 +#define M98090_REG_ANALOG_MIC_LOOP 0x0A +#define M98090_REG_ANALOG_LINE_LOOP 0x0B +#define M98090_REG_RESERVED 0x0C +#define M98090_REG_LINE_INPUT_CONFIG 0x0D +#define M98090_REG_LINE_INPUT_LEVEL 0x0E +#define M98090_REG_INPUT_MODE 0x0F +#define M98090_REG_MIC1_INPUT_LEVEL 0x10 +#define M98090_REG_MIC2_INPUT_LEVEL 0x11 +#define M98090_REG_MIC_BIAS_VOLTAGE 0x12 +#define M98090_REG_DIGITAL_MIC_ENABLE 0x13 +#define M98090_REG_DIGITAL_MIC_CONFIG 0x14 +#define M98090_REG_LEFT_ADC_MIXER 0x15 +#define M98090_REG_RIGHT_ADC_MIXER 0x16 +#define M98090_REG_LEFT_ADC_LEVEL 0x17 +#define M98090_REG_RIGHT_ADC_LEVEL 0x18 +#define M98090_REG_ADC_BIQUAD_LEVEL 0x19 +#define M98090_REG_ADC_SIDETONE 0x1A +#define M98090_REG_SYSTEM_CLOCK 0x1B +#define M98090_REG_CLOCK_MODE 0x1C +#define M98090_REG_CLOCK_RATIO_NI_MSB 0x1D +#define M98090_REG_CLOCK_RATIO_NI_LSB 0x1E +#define M98090_REG_CLOCK_RATIO_MI_MSB 0x1F +#define M98090_REG_CLOCK_RATIO_MI_LSB 0x20 +#define M98090_REG_MASTER_MODE 0x21 +#define M98090_REG_INTERFACE_FORMAT 0x22 +#define M98090_REG_TDM_CONTROL 0x23 +#define M98090_REG_TDM_FORMAT 0x24 +#define M98090_REG_IO_CONFIGURATION 0x25 +#define M98090_REG_FILTER_CONFIG 0x26 +#define M98090_REG_DAI_PLAYBACK_LEVEL 0x27 +#define M98090_REG_DAI_PLAYBACK_LEVEL_EQ 0x28 +#define M98090_REG_LEFT_HP_MIXER 0x29 +#define M98090_REG_RIGHT_HP_MIXER 0x2A +#define M98090_REG_HP_CONTROL 0x2B +#define M98090_REG_LEFT_HP_VOLUME 0x2C +#define M98090_REG_RIGHT_HP_VOLUME 0x2D +#define M98090_REG_LEFT_SPK_MIXER 0x2E +#define M98090_REG_RIGHT_SPK_MIXER 0x2F +#define M98090_REG_SPK_CONTROL 0x30 +#define M98090_REG_LEFT_SPK_VOLUME 0x31 +#define M98090_REG_RIGHT_SPK_VOLUME 0x32 +#define M98090_REG_DRC_TIMING 0x33 +#define M98090_REG_DRC_COMPRESSOR 0x34 +#define M98090_REG_DRC_EXPANDER 0x35 +#define M98090_REG_DRC_GAIN 0x36 +#define M98090_REG_RCV_LOUTL_MIXER 0x37 +#define M98090_REG_RCV_LOUTL_CONTROL 0x38 +#define M98090_REG_RCV_LOUTL_VOLUME 0x39 +#define M98090_REG_LOUTR_MIXER 0x3A +#define M98090_REG_LOUTR_CONTROL 0x3B +#define M98090_REG_LOUTR_VOLUME 0x3C +#define M98090_REG_JACK_DETECT 0x3D +#define M98090_REG_INPUT_ENABLE 0x3E +#define M98090_REG_OUTPUT_ENABLE 0x3F +#define M98090_REG_LEVEL_CONTROL 0x40 +#define M98090_REG_DSP_FILTER_ENABLE 0x41 +#define M98090_REG_BIAS_CONTROL 0x42 +#define M98090_REG_DAC_CONTROL 0x43 +#define M98090_REG_ADC_CONTROL 0x44 +#define M98090_REG_DEVICE_SHUTDOWN 0x45 +#define M98090_REG_EQUALIZER_BASE 0x46 +#define M98090_REG_RECORD_BIQUAD_BASE 0xAF +#define M98090_REG_DMIC3_VOLUME 0xBE +#define M98090_REG_DMIC4_VOLUME 0xBF +#define M98090_REG_DMIC34_BQ_PREATTEN 0xC0 +#define M98090_REG_RECORD_TDM_SLOT 0xC1 +#define M98090_REG_SAMPLE_RATE 0xC2 +#define M98090_REG_DMIC34_BIQUAD_BASE 0xC3 +#define M98090_REG_REVISION_ID 0xFF + +#define M98090_REG_CNT (0xFF+1) +#define MAX98090_MAX_REGISTER 0xFF + +/* MAX98090 Register Bit Fields */ + +/* + * M98090_REG_SOFTWARE_RESET + */ +#define M98090_SWRESET_MASK (1<<7) +#define M98090_SWRESET_SHIFT 7 +#define M98090_SWRESET_WIDTH 1 + +/* + * M98090_REG_DEVICE_STATUS + */ +#define M98090_CLD_MASK (1<<7) +#define M98090_CLD_SHIFT 7 +#define M98090_CLD_WIDTH 1 +#define M98090_SLD_MASK (1<<6) +#define M98090_SLD_SHIFT 6 +#define M98090_SLD_WIDTH 1 +#define M98090_ULK_MASK (1<<5) +#define M98090_ULK_SHIFT 5 +#define M98090_ULK_WIDTH 1 +#define M98090_JDET_MASK (1<<2) +#define M98090_JDET_SHIFT 2 +#define M98090_JDET_WIDTH 1 +#define M98090_DRCACT_MASK (1<<1) +#define M98090_DRCACT_SHIFT 1 +#define M98090_DRCACT_WIDTH 1 +#define M98090_DRCCLP_MASK (1<<0) +#define M98090_DRCCLP_SHIFT 0 +#define M98090_DRCCLP_WIDTH 1 + +/* + * M98090_REG_JACK_STATUS + */ +#define M98090_LSNS_MASK (1<<2) +#define M98090_LSNS_SHIFT 2 +#define M98090_LSNS_WIDTH 1 +#define M98090_JKSNS_MASK (1<<1) +#define M98090_JKSNS_SHIFT 1 +#define M98090_JKSNS_WIDTH 1 + +/* + * M98090_REG_INTERRUPT_S + */ +#define M98090_ICLD_MASK (1<<7) +#define M98090_ICLD_SHIFT 7 +#define M98090_ICLD_WIDTH 1 +#define M98090_ISLD_MASK (1<<6) +#define M98090_ISLD_SHIFT 6 +#define M98090_ISLD_WIDTH 1 +#define M98090_IULK_MASK (1<<5) +#define M98090_IULK_SHIFT 5 +#define M98090_IULK_WIDTH 1 +#define M98090_IJDET_MASK (1<<2) +#define M98090_IJDET_SHIFT 2 +#define M98090_IJDET_WIDTH 1 +#define M98090_IDRCACT_MASK (1<<1) +#define M98090_IDRCACT_SHIFT 1 +#define M98090_IDRCACT_WIDTH 1 +#define M98090_IDRCCLP_MASK (1<<0) +#define M98090_IDRCCLP_SHIFT 0 +#define M98090_IDRCCLP_WIDTH 1 + +/* + * M98090_REG_QUICK_SYSTEM_CLOCK + */ +#define M98090_26M_MASK (1<<7) +#define M98090_26M_SHIFT 7 +#define M98090_26M_WIDTH 1 +#define M98090_19P2M_MASK (1<<6) +#define M98090_19P2M_SHIFT 6 +#define M98090_19P2M_WIDTH 1 +#define M98090_13M_MASK (1<<5) +#define M98090_13M_SHIFT 5 +#define M98090_13M_WIDTH 1 +#define M98090_12P288M_MASK (1<<4) +#define M98090_12P288M_SHIFT 4 +#define M98090_12P288M_WIDTH 1 +#define M98090_12M_MASK (1<<3) +#define M98090_12M_SHIFT 3 +#define M98090_12M_WIDTH 1 +#define M98090_11P2896M_MASK (1<<2) +#define M98090_11P2896M_SHIFT 2 +#define M98090_11P2896M_WIDTH 1 +#define M98090_256FS_MASK (1<<0) +#define M98090_256FS_SHIFT 0 +#define M98090_256FS_WIDTH 1 +#define M98090_CLK_ALL_SHIFT 0 +#define M98090_CLK_ALL_WIDTH 8 +#define M98090_CLK_ALL_NUM (1<<M98090_CLK_ALL_WIDTH) + +/* + * M98090_REG_QUICK_SAMPLE_RATE + */ +#define M98090_SR_96K_MASK (1<<5) +#define M98090_SR_96K_SHIFT 5 +#define M98090_SR_96K_WIDTH 1 +#define M98090_SR_32K_MASK (1<<4) +#define M98090_SR_32K_SHIFT 4 +#define M98090_SR_32K_WIDTH 1 +#define M98090_SR_48K_MASK (1<<3) +#define M98090_SR_48K_SHIFT 3 +#define M98090_SR_48K_WIDTH 1 +#define M98090_SR_44K1_MASK (1<<2) +#define M98090_SR_44K1_SHIFT 2 +#define M98090_SR_44K1_WIDTH 1 +#define M98090_SR_16K_MASK (1<<1) +#define M98090_SR_16K_SHIFT 1 +#define M98090_SR_16K_WIDTH 1 +#define M98090_SR_8K_MASK (1<<0) +#define M98090_SR_8K_SHIFT 0 +#define M98090_SR_8K_WIDTH 1 +#define M98090_SR_MASK 0x3F +#define M98090_SR_ALL_SHIFT 0 +#define M98090_SR_ALL_WIDTH 8 +#define M98090_SR_ALL_NUM (1<<M98090_SR_ALL_WIDTH) + +/* + * M98090_REG_DAI_INTERFACE + */ +#define M98090_RJ_M_MASK (1<<5) +#define M98090_RJ_M_SHIFT 5 +#define M98090_RJ_M_WIDTH 1 +#define M98090_RJ_S_MASK (1<<4) +#define M98090_RJ_S_SHIFT 4 +#define M98090_RJ_S_WIDTH 1 +#define M98090_LJ_M_MASK (1<<3) +#define M98090_LJ_M_SHIFT 3 +#define M98090_LJ_M_WIDTH 1 +#define M98090_LJ_S_MASK (1<<2) +#define M98090_LJ_S_SHIFT 2 +#define M98090_LJ_S_WIDTH 1 +#define M98090_I2S_M_MASK (1<<1) +#define M98090_I2S_M_SHIFT 1 +#define M98090_I2S_M_WIDTH 1 +#define M98090_I2S_S_MASK (1<<0) +#define M98090_I2S_S_SHIFT 0 +#define M98090_I2S_S_WIDTH 1 +#define M98090_DAI_ALL_SHIFT 0 +#define M98090_DAI_ALL_WIDTH 8 +#define M98090_DAI_ALL_NUM (1<<M98090_DAI_ALL_WIDTH) + +/* + * M98090_REG_DAC_PATH + */ +#define M98090_DIG2_HP_MASK (1<<7) +#define M98090_DIG2_HP_SHIFT 7 +#define M98090_DIG2_HP_WIDTH 1 +#define M98090_DIG2_EAR_MASK (1<<6) +#define M98090_DIG2_EAR_SHIFT 6 +#define M98090_DIG2_EAR_WIDTH 1 +#define M98090_DIG2_SPK_MASK (1<<5) +#define M98090_DIG2_SPK_SHIFT 5 +#define M98090_DIG2_SPK_WIDTH 1 +#define M98090_DIG2_LOUT_MASK (1<<4) +#define M98090_DIG2_LOUT_SHIFT 4 +#define M98090_DIG2_LOUT_WIDTH 1 +#define M98090_DIG2_ALL_SHIFT 0 +#define M98090_DIG2_ALL_WIDTH 8 +#define M98090_DIG2_ALL_NUM (1<<M98090_DIG2_ALL_WIDTH) + +/* + * M98090_REG_MIC_DIRECT_TO_ADC + */ +#define M98090_IN12_MIC1_MASK (1<<7) +#define M98090_IN12_MIC1_SHIFT 7 +#define M98090_IN12_MIC1_WIDTH 1 +#define M98090_IN34_MIC2_MASK (1<<6) +#define M98090_IN34_MIC2_SHIFT 6 +#define M98090_IN34_MIC2_WIDTH 1 +#define M98090_IN56_MIC1_MASK (1<<5) +#define M98090_IN56_MIC1_SHIFT 5 +#define M98090_IN56_MIC1_WIDTH 1 +#define M98090_IN56_MIC2_MASK (1<<4) +#define M98090_IN56_MIC2_SHIFT 4 +#define M98090_IN56_MIC2_WIDTH 1 +#define M98090_IN12_DADC_MASK (1<<3) +#define M98090_IN12_DADC_SHIFT 3 +#define M98090_IN12_DADC_WIDTH 1 +#define M98090_IN34_DADC_MASK (1<<2) +#define M98090_IN34_DADC_SHIFT 2 +#define M98090_IN34_DADC_WIDTH 1 +#define M98090_IN56_DADC_MASK (1<<1) +#define M98090_IN56_DADC_SHIFT 1 +#define M98090_IN56_DADC_WIDTH 1 +#define M98090_MIC_ALL_SHIFT 0 +#define M98090_MIC_ALL_WIDTH 8 +#define M98090_MIC_ALL_NUM (1<<M98090_MIC_ALL_WIDTH) + +/* + * M98090_REG_LINE_TO_ADC + */ +#define M98090_IN12S_AB_MASK (1<<7) +#define M98090_IN12S_AB_SHIFT 7 +#define M98090_IN12S_AB_WIDTH 1 +#define M98090_IN34S_AB_MASK (1<<6) +#define M98090_IN34S_AB_SHIFT 6 +#define M98090_IN34S_AB_WIDTH 1 +#define M98090_IN56S_AB_MASK (1<<5) +#define M98090_IN56S_AB_SHIFT 5 +#define M98090_IN56S_AB_WIDTH 1 +#define M98090_IN34D_A_MASK (1<<4) +#define M98090_IN34D_A_SHIFT 4 +#define M98090_IN34D_A_WIDTH 1 +#define M98090_IN56D_B_MASK (1<<3) +#define M98090_IN56D_B_SHIFT 3 +#define M98090_IN56D_B_WIDTH 1 +#define M98090_LINE_ALL_SHIFT 0 +#define M98090_LINE_ALL_WIDTH 8 +#define M98090_LINE_ALL_NUM (1<<M98090_LINE_ALL_WIDTH) + +/* + * M98090_REG_ANALOG_MIC_LOOP + */ +#define M98090_IN12_M1HPL_MASK (1<<7) +#define M98090_IN12_M1HPL_SHIFT 7 +#define M98090_IN12_M1HPL_WIDTH 1 +#define M98090_IN12_M1SPKL_MASK (1<<6) +#define M98090_IN12_M1SPKL_SHIFT 6 +#define M98090_IN12_M1SPKL_WIDTH 1 +#define M98090_IN12_M1EAR_MASK (1<<5) +#define M98090_IN12_M1EAR_SHIFT 5 +#define M98090_IN12_M1EAR_WIDTH 1 +#define M98090_IN12_M1LOUTL_MASK (1<<4) +#define M98090_IN12_M1LOUTL_SHIFT 4 +#define M98090_IN12_M1LOUTL_WIDTH 1 +#define M98090_IN34_M2HPR_MASK (1<<3) +#define M98090_IN34_M2HPR_SHIFT 3 +#define M98090_IN34_M2HPR_WIDTH 1 +#define M98090_IN34_M2SPKR_MASK (1<<2) +#define M98090_IN34_M2SPKR_SHIFT 2 +#define M98090_IN34_M2SPKR_WIDTH 1 +#define M98090_IN34_M2EAR_MASK (1<<1) +#define M98090_IN34_M2EAR_SHIFT 1 +#define M98090_IN34_M2EAR_WIDTH 1 +#define M98090_IN34_M2LOUTR_MASK (1<<0) +#define M98090_IN34_M2LOUTR_SHIFT 0 +#define M98090_IN34_M2LOUTR_WIDTH 1 +#define M98090_AMIC_ALL_SHIFT 0 +#define M98090_AMIC_ALL_WIDTH 8 +#define M98090_AMIC_ALL_NUM (1<<M98090_AMIC_ALL_WIDTH) + +/* + * M98090_REG_ANALOG_LINE_LOOP + */ +#define M98090_IN12S_ABHP_MASK (1<<7) +#define M98090_IN12S_ABHP_SHIFT 7 +#define M98090_IN12S_ABHP_WIDTH 1 +#define M98090_IN34D_ASPKL_MASK (1<<6) +#define M98090_IN34D_ASPKL_SHIFT 6 +#define M98090_IN34D_ASPKL_WIDTH 1 +#define M98090_IN34D_AEAR_MASK (1<<5) +#define M98090_IN34D_AEAR_SHIFT 5 +#define M98090_IN34D_AEAR_WIDTH 1 +#define M98090_IN12S_ABLOUT_MASK (1<<4) +#define M98090_IN12S_ABLOUT_SHIFT 4 +#define M98090_IN12S_ABLOUT_WIDTH 1 +#define M98090_IN34S_ABHP_MASK (1<<3) +#define M98090_IN34S_ABHP_SHIFT 3 +#define M98090_IN34S_ABHP_WIDTH 1 +#define M98090_IN56D_BSPKR_MASK (1<<2) +#define M98090_IN56D_BSPKR_SHIFT 2 +#define M98090_IN56D_BSPKR_WIDTH 1 +#define M98090_IN56D_BEAR_MASK (1<<1) +#define M98090_IN56D_BEAR_SHIFT 1 +#define M98090_IN56D_BEAR_WIDTH 1 +#define M98090_IN34S_ABLOUT_MASK (1<<0) +#define M98090_IN34S_ABLOUT_SHIFT 0 +#define M98090_IN34S_ABLOUT_WIDTH 1 +#define M98090_ALIN_ALL_SHIFT 0 +#define M98090_ALIN_ALL_WIDTH 8 +#define M98090_ALIN_ALL_NUM (1<<M98090_ALIN_ALL_WIDTH) + +/* + * M98090_REG_RESERVED + */ + +/* + * M98090_REG_LINE_INPUT_CONFIG + */ +#define M98090_IN34DIFF_MASK (1<<7) +#define M98090_IN34DIFF_SHIFT 7 +#define M98090_IN34DIFF_WIDTH 1 +#define M98090_IN56DIFF_MASK (1<<6) +#define M98090_IN56DIFF_SHIFT 6 +#define M98090_IN56DIFF_WIDTH 1 +#define M98090_IN1SEEN_MASK (1<<5) +#define M98090_IN1SEEN_SHIFT 5 +#define M98090_IN1SEEN_WIDTH 1 +#define M98090_IN2SEEN_MASK (1<<4) +#define M98090_IN2SEEN_SHIFT 4 +#define M98090_IN2SEEN_WIDTH 1 +#define M98090_IN3SEEN_MASK (1<<3) +#define M98090_IN3SEEN_SHIFT 3 +#define M98090_IN3SEEN_WIDTH 1 +#define M98090_IN4SEEN_MASK (1<<2) +#define M98090_IN4SEEN_SHIFT 2 +#define M98090_IN4SEEN_WIDTH 1 +#define M98090_IN5SEEN_MASK (1<<1) +#define M98090_IN5SEEN_SHIFT 1 +#define M98090_IN5SEEN_WIDTH 1 +#define M98090_IN6SEEN_MASK (1<<0) +#define M98090_IN6SEEN_SHIFT 0 +#define M98090_IN6SEEN_WIDTH 1 + +/* + * M98090_REG_LINE_INPUT_LEVEL + */ +#define M98090_MIXG135_MASK (1<<7) +#define M98090_MIXG135_SHIFT 7 +#define M98090_MIXG135_WIDTH 1 +#define M98090_MIXG135_NUM (1<<M98090_MIXG135_WIDTH) +#define M98090_MIXG246_MASK (1<<6) +#define M98090_MIXG246_SHIFT 6 +#define M98090_MIXG246_WIDTH 1 +#define M98090_MIXG246_NUM (1<<M98090_MIXG246_WIDTH) +#define M98090_LINAPGA_MASK (7<<3) +#define M98090_LINAPGA_SHIFT 3 +#define M98090_LINAPGA_WIDTH 3 +#define M98090_LINAPGA_NUM 6 +#define M98090_LINBPGA_MASK (7<<0) +#define M98090_LINBPGA_SHIFT 0 +#define M98090_LINBPGA_WIDTH 3 +#define M98090_LINBPGA_NUM 6 + +/* + * M98090_REG_INPUT_MODE + */ +#define M98090_EXTBUFA_MASK (1<<7) +#define M98090_EXTBUFA_SHIFT 7 +#define M98090_EXTBUFA_WIDTH 1 +#define M98090_EXTBUFA_NUM (1<<M98090_EXTBUFA_WIDTH) +#define M98090_EXTBUFB_MASK (1<<6) +#define M98090_EXTBUFB_SHIFT 6 +#define M98090_EXTBUFB_WIDTH 1 +#define M98090_EXTBUFB_NUM (1<<M98090_EXTBUFB_WIDTH) +#define M98090_EXTMIC_MASK (3<<0) +#define M98090_EXTMIC_SHIFT 0 +#define M98090_EXTMIC1_SHIFT 0 +#define M98090_EXTMIC2_SHIFT 1 +#define M98090_EXTMIC_WIDTH 2 +#define M98090_EXTMIC_NONE (0<<0) +#define M98090_EXTMIC_MIC1 (1<<0) +#define M98090_EXTMIC_MIC2 (2<<0) + +/* + * M98090_REG_MIC1_INPUT_LEVEL + */ +#define M98090_MIC_PA1EN_MASK (3<<5) +#define M98090_MIC_PA1EN_SHIFT 5 +#define M98090_MIC_PA1EN_WIDTH 2 +#define M98090_MIC_PA1EN_NUM 3 +#define M98090_MIC_PGAM1_MASK (31<<0) +#define M98090_MIC_PGAM1_SHIFT 0 +#define M98090_MIC_PGAM1_WIDTH 5 +#define M98090_MIC_PGAM1_NUM 21 + +/* + * M98090_REG_MIC2_INPUT_LEVEL + */ +#define M98090_MIC_PA2EN_MASK (3<<5) +#define M98090_MIC_PA2EN_SHIFT 5 +#define M98090_MIC_PA2EN_WIDTH 2 +#define M98090_MIC_PA2EN_NUM 3 +#define M98090_MIC_PGAM2_MASK (31<<0) +#define M98090_MIC_PGAM2_SHIFT 0 +#define M98090_MIC_PGAM2_WIDTH 5 +#define M98090_MIC_PGAM2_NUM 21 + +/* + * M98090_REG_MIC_BIAS_VOLTAGE + */ +#define M98090_MBVSEL_MASK (3<<0) +#define M98090_MBVSEL_SHIFT 0 +#define M98090_MBVSEL_WIDTH 2 +#define M98090_MBVSEL_2V8 (3<<0) +#define M98090_MBVSEL_2V55 (2<<0) +#define M98090_MBVSEL_2V4 (1<<0) +#define M98090_MBVSEL_2V2 (0<<0) + +/* + * M98090_REG_DIGITAL_MIC_ENABLE + */ +#define M98090_MICCLK_MASK (7<<4) +#define M98090_MICCLK_SHIFT 4 +#define M98090_MICCLK_WIDTH 3 +#define M98090_DIGMIC4_MASK (1<<3) +#define M98090_DIGMIC4_SHIFT 3 +#define M98090_DIGMIC4_WIDTH 1 +#define M98090_DIGMIC4_NUM (1<<M98090_DIGMIC4_WIDTH) +#define M98090_DIGMIC3_MASK (1<<2) +#define M98090_DIGMIC3_SHIFT 2 +#define M98090_DIGMIC3_WIDTH 1 +#define M98090_DIGMIC3_NUM (1<<M98090_DIGMIC3_WIDTH) +#define M98090_DIGMICR_MASK (1<<1) +#define M98090_DIGMICR_SHIFT 1 +#define M98090_DIGMICR_WIDTH 1 +#define M98090_DIGMICR_NUM (1<<M98090_DIGMICR_WIDTH) +#define M98090_DIGMICL_MASK (1<<0) +#define M98090_DIGMICL_SHIFT 0 +#define M98090_DIGMICL_WIDTH 1 +#define M98090_DIGMICL_NUM (1<<M98090_DIGMICL_WIDTH) + +/* + * M98090_REG_DIGITAL_MIC_CONFIG + */ +#define M98090_DMIC_COMP_MASK (15<<4) +#define M98090_DMIC_COMP_SHIFT 4 +#define M98090_DMIC_COMP_WIDTH 4 +#define M98090_DMIC_COMP_NUM (1<<M98090_DMIC_COMP_WIDTH) +#define M98090_DMIC_FREQ_MASK (3<<0) +#define M98090_DMIC_FREQ_SHIFT 0 +#define M98090_DMIC_FREQ_WIDTH 2 + +/* + * M98090_REG_LEFT_ADC_MIXER + */ +#define M98090_MIXADL_MIC2_MASK (1<<6) +#define M98090_MIXADL_MIC2_SHIFT 6 +#define M98090_MIXADL_MIC2_WIDTH 1 +#define M98090_MIXADL_MIC1_MASK (1<<5) +#define M98090_MIXADL_MIC1_SHIFT 5 +#define M98090_MIXADL_MIC1_WIDTH 1 +#define M98090_MIXADL_LINEB_MASK (1<<4) +#define M98090_MIXADL_LINEB_SHIFT 4 +#define M98090_MIXADL_LINEB_WIDTH 1 +#define M98090_MIXADL_LINEA_MASK (1<<3) +#define M98090_MIXADL_LINEA_SHIFT 3 +#define M98090_MIXADL_LINEA_WIDTH 1 +#define M98090_MIXADL_IN65DIFF_MASK (1<<2) +#define M98090_MIXADL_IN65DIFF_SHIFT 2 +#define M98090_MIXADL_IN65DIFF_WIDTH 1 +#define M98090_MIXADL_IN34DIFF_MASK (1<<1) +#define M98090_MIXADL_IN34DIFF_SHIFT 1 +#define M98090_MIXADL_IN34DIFF_WIDTH 1 +#define M98090_MIXADL_IN12DIFF_MASK (1<<0) +#define M98090_MIXADL_IN12DIFF_SHIFT 0 +#define M98090_MIXADL_IN12DIFF_WIDTH 1 +#define M98090_MIXADL_MASK (255<<0) +#define M98090_MIXADL_SHIFT 0 +#define M98090_MIXADL_WIDTH 8 + +/* + * M98090_REG_RIGHT_ADC_MIXER + */ +#define M98090_MIXADR_MIC2_MASK (1<<6) +#define M98090_MIXADR_MIC2_SHIFT 6 +#define M98090_MIXADR_MIC2_WIDTH 1 +#define M98090_MIXADR_MIC1_MASK (1<<5) +#define M98090_MIXADR_MIC1_SHIFT 5 +#define M98090_MIXADR_MIC1_WIDTH 1 +#define M98090_MIXADR_LINEB_MASK (1<<4) +#define M98090_MIXADR_LINEB_SHIFT 4 +#define M98090_MIXADR_LINEB_WIDTH 1 +#define M98090_MIXADR_LINEA_MASK (1<<3) +#define M98090_MIXADR_LINEA_SHIFT 3 +#define M98090_MIXADR_LINEA_WIDTH 1 +#define M98090_MIXADR_IN65DIFF_MASK (1<<2) +#define M98090_MIXADR_IN65DIFF_SHIFT 2 +#define M98090_MIXADR_IN65DIFF_WIDTH 1 +#define M98090_MIXADR_IN34DIFF_MASK (1<<1) +#define M98090_MIXADR_IN34DIFF_SHIFT 1 +#define M98090_MIXADR_IN34DIFF_WIDTH 1 +#define M98090_MIXADR_IN12DIFF_MASK (1<<0) +#define M98090_MIXADR_IN12DIFF_SHIFT 0 +#define M98090_MIXADR_IN12DIFF_WIDTH 1 +#define M98090_MIXADR_MASK (255<<0) +#define M98090_MIXADR_SHIFT 0 +#define M98090_MIXADR_WIDTH 8 + +/* + * M98090_REG_LEFT_ADC_LEVEL + */ +#define M98090_AVLG_MASK (7<<4) +#define M98090_AVLG_SHIFT 4 +#define M98090_AVLG_WIDTH 3 +#define M98090_AVLG_NUM (1<<M98090_AVLG_WIDTH) +#define M98090_AVL_MASK (15<<0) +#define M98090_AVL_SHIFT 0 +#define M98090_AVL_WIDTH 4 +#define M98090_AVL_NUM (1<<M98090_AVL_WIDTH) + +/* + * M98090_REG_RIGHT_ADC_LEVEL + */ +#define M98090_AVRG_MASK (7<<4) +#define M98090_AVRG_SHIFT 4 +#define M98090_AVRG_WIDTH 3 +#define M98090_AVRG_NUM (1<<M98090_AVRG_WIDTH) +#define M98090_AVR_MASK (15<<0) +#define M98090_AVR_SHIFT 0 +#define M98090_AVR_WIDTH 4 +#define M98090_AVR_NUM (1<<M98090_AVR_WIDTH) + +/* + * M98090_REG_ADC_BIQUAD_LEVEL + */ +#define M98090_AVBQ_MASK (15<<0) +#define M98090_AVBQ_SHIFT 0 +#define M98090_AVBQ_WIDTH 4 +#define M98090_AVBQ_NUM (1<<M98090_AVBQ_WIDTH) + +/* + * M98090_REG_ADC_SIDETONE + */ +#define M98090_DSTSR_MASK (1<<7) +#define M98090_DSTSR_SHIFT 7 +#define M98090_DSTSR_WIDTH 1 +#define M98090_DSTSL_MASK (1<<6) +#define M98090_DSTSL_SHIFT 6 +#define M98090_DSTSL_WIDTH 1 +#define M98090_DVST_MASK (31<<0) +#define M98090_DVST_SHIFT 0 +#define M98090_DVST_WIDTH 5 +#define M98090_DVST_NUM 31 + +/* + * M98090_REG_SYSTEM_CLOCK + */ +#define M98090_PSCLK_MASK (3<<4) +#define M98090_PSCLK_SHIFT 4 +#define M98090_PSCLK_WIDTH 2 +#define M98090_PSCLK_DISABLED (0<<4) +#define M98090_PSCLK_DIV1 (1<<4) +#define M98090_PSCLK_DIV2 (2<<4) +#define M98090_PSCLK_DIV4 (3<<4) + +/* + * M98090_REG_CLOCK_MODE + */ +#define M98090_FREQ_MASK (15<<4) +#define M98090_FREQ_SHIFT 4 +#define M98090_FREQ_WIDTH 4 +#define M98090_USE_M1_MASK (1<<0) +#define M98090_USE_M1_SHIFT 0 +#define M98090_USE_M1_WIDTH 1 +#define M98090_USE_M1_NUM (1<<M98090_USE_M1_WIDTH) + +/* + * M98090_REG_CLOCK_RATIO_NI_MSB + */ +#define M98090_NI_HI_MASK (127<<0) +#define M98090_NI_HI_SHIFT 0 +#define M98090_NI_HI_WIDTH 7 +#define M98090_NI_HI_NUM (1<<M98090_NI_HI_WIDTH) + +/* + * M98090_REG_CLOCK_RATIO_NI_LSB + */ +#define M98090_NI_LO_MASK (255<<0) +#define M98090_NI_LO_SHIFT 0 +#define M98090_NI_LO_WIDTH 8 +#define M98090_NI_LO_NUM (1<<M98090_NI_LO_WIDTH) + +/* + * M98090_REG_CLOCK_RATIO_MI_MSB + */ +#define M98090_MI_HI_MASK (255<<0) +#define M98090_MI_HI_SHIFT 0 +#define M98090_MI_HI_WIDTH 8 +#define M98090_MI_HI_NUM (1<<M98090_MI_HI_WIDTH) + +/* + * M98090_REG_CLOCK_RATIO_MI_LSB + */ +#define M98090_MI_LO_MASK (255<<0) +#define M98090_MI_LO_SHIFT 0 +#define M98090_MI_LO_WIDTH 8 +#define M98090_MI_LO_NUM (1<<M98090_MI_LO_WIDTH) + +/* + * M98090_REG_MASTER_MODE + */ +#define M98090_MAS_MASK (1<<7) +#define M98090_MAS_SHIFT 7 +#define M98090_MAS_WIDTH 1 +#define M98090_BSEL_MASK (1<<0) +#define M98090_BSEL_SHIFT 0 +#define M98090_BSEL_WIDTH 1 +#define M98090_BSEL_32 (1<<0) +#define M98090_BSEL_48 (2<<0) +#define M98090_BSEL_64 (3<<0) + +/* + * M98090_REG_INTERFACE_FORMAT + */ +#define M98090_RJ_MASK (1<<5) +#define M98090_RJ_SHIFT 5 +#define M98090_RJ_WIDTH 1 +#define M98090_WCI_MASK (1<<4) +#define M98090_WCI_SHIFT 4 +#define M98090_WCI_WIDTH 1 +#define M98090_BCI_MASK (1<<3) +#define M98090_BCI_SHIFT 3 +#define M98090_BCI_WIDTH 1 +#define M98090_DLY_MASK (1<<2) +#define M98090_DLY_SHIFT 2 +#define M98090_DLY_WIDTH 1 +#define M98090_WS_MASK (3<<0) +#define M98090_WS_SHIFT 0 +#define M98090_WS_WIDTH 2 +#define M98090_WS_NUM (1<<M98090_WS_WIDTH) + +/* + * M98090_REG_TDM_CONTROL + */ +#define M98090_FSW_MASK (1<<1) +#define M98090_FSW_SHIFT 1 +#define M98090_FSW_WIDTH 1 +#define M98090_TDM_MASK (1<<0) +#define M98090_TDM_SHIFT 0 +#define M98090_TDM_WIDTH 1 +#define M98090_TDM_NUM (1<<M98090_TDM_WIDTH) + +/* + * M98090_REG_TDM_FORMAT + */ +#define M98090_TDM_SLOTL_MASK (3<<6) +#define M98090_TDM_SLOTL_SHIFT 6 +#define M98090_TDM_SLOTL_WIDTH 2 +#define M98090_TDM_SLOTL_NUM (1<<M98090_TDM_SLOTL_WIDTH) +#define M98090_TDM_SLOTR_MASK (3<<4) +#define M98090_TDM_SLOTR_SHIFT 4 +#define M98090_TDM_SLOTR_WIDTH 2 +#define M98090_TDM_SLOTR_NUM (1<<M98090_TDM_SLOTR_WIDTH) +#define M98090_TDM_SLOTDLY_MASK (15<<0) +#define M98090_TDM_SLOTDLY_SHIFT 0 +#define M98090_TDM_SLOTDLY_WIDTH 4 +#define M98090_TDM_SLOTDLY_NUM (1<<M98090_TDM_SLOTDLY_WIDTH) + +/* + * M98090_REG_IO_CONFIGURATION + */ +#define M98090_LTEN_MASK (1<<5) +#define M98090_LTEN_SHIFT 5 +#define M98090_LTEN_WIDTH 1 +#define M98090_LTEN_NUM (1<<M98090_LTEN_WIDTH) +#define M98090_LBEN_MASK (1<<4) +#define M98090_LBEN_SHIFT 4 +#define M98090_LBEN_WIDTH 1 +#define M98090_LBEN_NUM (1<<M98090_LBEN_WIDTH) +#define M98090_DMONO_MASK (1<<3) +#define M98090_DMONO_SHIFT 3 +#define M98090_DMONO_WIDTH 1 +#define M98090_DMONO_NUM (1<<M98090_DMONO_WIDTH) +#define M98090_HIZOFF_MASK (1<<2) +#define M98090_HIZOFF_SHIFT 2 +#define M98090_HIZOFF_WIDTH 1 +#define M98090_HIZOFF_NUM (1<<M98090_HIZOFF_WIDTH) +#define M98090_SDOEN_MASK (1<<1) +#define M98090_SDOEN_SHIFT 1 +#define M98090_SDOEN_WIDTH 1 +#define M98090_SDOEN_NUM (1<<M98090_SDOEN_WIDTH) +#define M98090_SDIEN_MASK (1<<0) +#define M98090_SDIEN_SHIFT 0 +#define M98090_SDIEN_WIDTH 1 +#define M98090_SDIEN_NUM (1<<M98090_SDIEN_WIDTH) + +/* + * M98090_REG_FILTER_CONFIG + */ +#define M98090_MODE_MASK (1<<7) +#define M98090_MODE_SHIFT 7 +#define M98090_MODE_WIDTH 1 +#define M98090_AHPF_MASK (1<<6) +#define M98090_AHPF_SHIFT 6 +#define M98090_AHPF_WIDTH 1 +#define M98090_AHPF_NUM (1<<M98090_AHPF_WIDTH) +#define M98090_DHPF_MASK (1<<5) +#define M98090_DHPF_SHIFT 5 +#define M98090_DHPF_WIDTH 1 +#define M98090_DHPF_NUM (1<<M98090_DHPF_WIDTH) +#define M98090_DHF_MASK (1<<4) +#define M98090_DHF_SHIFT 4 +#define M98090_DHF_WIDTH 1 +#define M98090_FLT_DMIC34MODE_MASK (1<<3) +#define M98090_FLT_DMIC34MODE_SHIFT 3 +#define M98090_FLT_DMIC34MODE_WIDTH 1 +#define M98090_FLT_DMIC34HPF_MASK (1<<2) +#define M98090_FLT_DMIC34HPF_SHIFT 2 +#define M98090_FLT_DMIC34HPF_WIDTH 1 +#define M98090_FLT_DMIC34HPF_NUM (1<<M98090_FLT_DMIC34HPF_WIDTH) + +/* + * M98090_REG_DAI_PLAYBACK_LEVEL + */ +#define M98090_DVM_MASK (1<<7) +#define M98090_DVM_SHIFT 7 +#define M98090_DVM_WIDTH 1 +#define M98090_DVG_MASK (3<<4) +#define M98090_DVG_SHIFT 4 +#define M98090_DVG_WIDTH 2 +#define M98090_DVG_NUM (1<<M98090_DVG_WIDTH) +#define M98090_DV_MASK (15<<0) +#define M98090_DV_SHIFT 0 +#define M98090_DV_WIDTH 4 +#define M98090_DV_NUM (1<<M98090_DV_WIDTH) + +/* + * M98090_REG_DAI_PLAYBACK_LEVEL_EQ + */ +#define M98090_EQCLPN_MASK (1<<4) +#define M98090_EQCLPN_SHIFT 4 +#define M98090_EQCLPN_WIDTH 1 +#define M98090_EQCLPN_NUM (1<<M98090_EQCLPN_WIDTH) +#define M98090_DVEQ_MASK (15<<0) +#define M98090_DVEQ_SHIFT 0 +#define M98090_DVEQ_WIDTH 4 +#define M98090_DVEQ_NUM (1<<M98090_DVEQ_WIDTH) + +/* + * M98090_REG_LEFT_HP_MIXER + */ +#define M98090_MIXHPL_MIC2_MASK (1<<5) +#define M98090_MIXHPL_MIC2_SHIFT 5 +#define M98090_MIXHPL_MIC2_WIDTH 1 +#define M98090_MIXHPL_MIC1_MASK (1<<4) +#define M98090_MIXHPL_MIC1_SHIFT 4 +#define M98090_MIXHPL_MIC1_WIDTH 1 +#define M98090_MIXHPL_LINEB_MASK (1<<3) +#define M98090_MIXHPL_LINEB_SHIFT 3 +#define M98090_MIXHPL_LINEB_WIDTH 1 +#define M98090_MIXHPL_LINEA_MASK (1<<2) +#define M98090_MIXHPL_LINEA_SHIFT 2 +#define M98090_MIXHPL_LINEA_WIDTH 1 +#define M98090_MIXHPL_DACR_MASK (1<<1) +#define M98090_MIXHPL_DACR_SHIFT 1 +#define M98090_MIXHPL_DACR_WIDTH 1 +#define M98090_MIXHPL_DACL_MASK (1<<0) +#define M98090_MIXHPL_DACL_SHIFT 0 +#define M98090_MIXHPL_DACL_WIDTH 1 +#define M98090_MIXHPL_MASK (63<<0) +#define M98090_MIXHPL_SHIFT 0 +#define M98090_MIXHPL_WIDTH 6 + +/* + * M98090_REG_RIGHT_HP_MIXER + */ +#define M98090_MIXHPR_MIC2_MASK (1<<5) +#define M98090_MIXHPR_MIC2_SHIFT 5 +#define M98090_MIXHPR_MIC2_WIDTH 1 +#define M98090_MIXHPR_MIC1_MASK (1<<4) +#define M98090_MIXHPR_MIC1_SHIFT 4 +#define M98090_MIXHPR_MIC1_WIDTH 1 +#define M98090_MIXHPR_LINEB_MASK (1<<3) +#define M98090_MIXHPR_LINEB_SHIFT 3 +#define M98090_MIXHPR_LINEB_WIDTH 1 +#define M98090_MIXHPR_LINEA_MASK (1<<2) +#define M98090_MIXHPR_LINEA_SHIFT 2 +#define M98090_MIXHPR_LINEA_WIDTH 1 +#define M98090_MIXHPR_DACR_MASK (1<<1) +#define M98090_MIXHPR_DACR_SHIFT 1 +#define M98090_MIXHPR_DACR_WIDTH 1 +#define M98090_MIXHPR_DACL_MASK (1<<0) +#define M98090_MIXHPR_DACL_SHIFT 0 +#define M98090_MIXHPR_DACL_WIDTH 1 +#define M98090_MIXHPR_MASK (63<<0) +#define M98090_MIXHPR_SHIFT 0 +#define M98090_MIXHPR_WIDTH 6 + +/* + * M98090_REG_HP_CONTROL + */ +#define M98090_MIXHPRSEL_MASK (1<<5) +#define M98090_MIXHPRSEL_SHIFT 5 +#define M98090_MIXHPRSEL_WIDTH 1 +#define M98090_MIXHPLSEL_MASK (1<<4) +#define M98090_MIXHPLSEL_SHIFT 4 +#define M98090_MIXHPLSEL_WIDTH 1 +#define M98090_MIXHPRG_MASK (3<<2) +#define M98090_MIXHPRG_SHIFT 2 +#define M98090_MIXHPRG_WIDTH 2 +#define M98090_MIXHPRG_NUM (1<<M98090_MIXHPRG_WIDTH) +#define M98090_MIXHPLG_MASK (3<<0) +#define M98090_MIXHPLG_SHIFT 0 +#define M98090_MIXHPLG_WIDTH 2 +#define M98090_MIXHPLG_NUM (1<<M98090_MIXHPLG_WIDTH) + +/* + * M98090_REG_LEFT_HP_VOLUME + */ +#define M98090_HPLM_MASK (1<<7) +#define M98090_HPLM_SHIFT 7 +#define M98090_HPLM_WIDTH 1 +#define M98090_HPVOLL_MASK (31<<0) +#define M98090_HPVOLL_SHIFT 0 +#define M98090_HPVOLL_WIDTH 5 +#define M98090_HPVOLL_NUM (1<<M98090_HPVOLL_WIDTH) + +/* + * M98090_REG_RIGHT_HP_VOLUME + */ +#define M98090_HPRM_MASK (1<<7) +#define M98090_HPRM_SHIFT 7 +#define M98090_HPRM_WIDTH 1 +#define M98090_HPVOLR_MASK (31<<0) +#define M98090_HPVOLR_SHIFT 0 +#define M98090_HPVOLR_WIDTH 5 +#define M98090_HPVOLR_NUM (1<<M98090_HPVOLR_WIDTH) + +/* + * M98090_REG_LEFT_SPK_MIXER + */ +#define M98090_MIXSPL_MIC2_MASK (1<<5) +#define M98090_MIXSPL_MIC2_SHIFT 5 +#define M98090_MIXSPL_MIC2_WIDTH 1 +#define M98090_MIXSPL_MIC1_MASK (1<<4) +#define M98090_MIXSPL_MIC1_SHIFT 4 +#define M98090_MIXSPL_MIC1_WIDTH 1 +#define M98090_MIXSPL_LINEB_MASK (1<<3) +#define M98090_MIXSPL_LINEB_SHIFT 3 +#define M98090_MIXSPL_LINEB_WIDTH 1 +#define M98090_MIXSPL_LINEA_MASK (1<<2) +#define M98090_MIXSPL_LINEA_SHIFT 2 +#define M98090_MIXSPL_LINEA_WIDTH 1 +#define M98090_MIXSPL_DACR_MASK (1<<1) +#define M98090_MIXSPL_DACR_SHIFT 1 +#define M98090_MIXSPL_DACR_WIDTH 1 +#define M98090_MIXSPL_DACL_MASK (1<<0) +#define M98090_MIXSPL_DACL_SHIFT 0 +#define M98090_MIXSPL_DACL_WIDTH 1 +#define M98090_MIXSPL_MASK (63<<0) +#define M98090_MIXSPL_SHIFT 0 +#define M98090_MIXSPL_WIDTH 6 +#define M98090_MIXSPR_DACR_MASK (1<<1) +#define M98090_MIXSPR_DACR_SHIFT 1 +#define M98090_MIXSPR_DACR_WIDTH 1 + + +/* + * M98090_REG_RIGHT_SPK_MIXER + */ +#define M98090_SPK_SLAVE_MASK (1<<6) +#define M98090_SPK_SLAVE_SHIFT 6 +#define M98090_SPK_SLAVE_WIDTH 1 +#define M98090_MIXSPR_MIC2_MASK (1<<5) +#define M98090_MIXSPR_MIC2_SHIFT 5 +#define M98090_MIXSPR_MIC2_WIDTH 1 +#define M98090_MIXSPR_MIC1_MASK (1<<4) +#define M98090_MIXSPR_MIC1_SHIFT 4 +#define M98090_MIXSPR_MIC1_WIDTH 1 +#define M98090_MIXSPR_LINEB_MASK (1<<3) +#define M98090_MIXSPR_LINEB_SHIFT 3 +#define M98090_MIXSPR_LINEB_WIDTH 1 +#define M98090_MIXSPR_LINEA_MASK (1<<2) +#define M98090_MIXSPR_LINEA_SHIFT 2 +#define M98090_MIXSPR_LINEA_WIDTH 1 +#define M98090_MIXSPR_DACR_MASK (1<<1) +#define M98090_MIXSPR_DACR_SHIFT 1 +#define M98090_MIXSPR_DACR_WIDTH 1 +#define M98090_MIXSPR_DACL_MASK (1<<0) +#define M98090_MIXSPR_DACL_SHIFT 0 +#define M98090_MIXSPR_DACL_WIDTH 1 +#define M98090_MIXSPR_MASK (63<<0) +#define M98090_MIXSPR_SHIFT 0 +#define M98090_MIXSPR_WIDTH 6 + +/* + * M98090_REG_SPK_CONTROL + */ +#define M98090_MIXSPRG_MASK (3<<2) +#define M98090_MIXSPRG_SHIFT 2 +#define M98090_MIXSPRG_WIDTH 2 +#define M98090_MIXSPRG_NUM (1<<M98090_MIXSPRG_WIDTH) +#define M98090_MIXSPLG_MASK (3<<0) +#define M98090_MIXSPLG_SHIFT 0 +#define M98090_MIXSPLG_WIDTH 2 +#define M98090_MIXSPLG_NUM (1<<M98090_MIXSPLG_WIDTH) + +/* + * M98090_REG_LEFT_SPK_VOLUME + */ +#define M98090_SPLM_MASK (1<<7) +#define M98090_SPLM_SHIFT 7 +#define M98090_SPLM_WIDTH 1 +#define M98090_SPVOLL_MASK (63<<0) +#define M98090_SPVOLL_SHIFT 0 +#define M98090_SPVOLL_WIDTH 6 +#define M98090_SPVOLL_NUM 40 + +/* + * M98090_REG_RIGHT_SPK_VOLUME + */ +#define M98090_SPRM_MASK (1<<7) +#define M98090_SPRM_SHIFT 7 +#define M98090_SPRM_WIDTH 1 +#define M98090_SPVOLR_MASK (63<<0) +#define M98090_SPVOLR_SHIFT 0 +#define M98090_SPVOLR_WIDTH 6 +#define M98090_SPVOLR_NUM 40 + +/* + * M98090_REG_DRC_TIMING + */ +#define M98090_DRCEN_MASK (1<<7) +#define M98090_DRCEN_SHIFT 7 +#define M98090_DRCEN_WIDTH 1 +#define M98090_DRCEN_NUM (1<<M98090_DRCEN_WIDTH) +#define M98090_DRCRLS_MASK (7<<4) +#define M98090_DRCRLS_SHIFT 4 +#define M98090_DRCRLS_WIDTH 3 +#define M98090_DRCATK_MASK (7<<0) +#define M98090_DRCATK_SHIFT 0 +#define M98090_DRCATK_WIDTH 3 + +/* + * M98090_REG_DRC_COMPRESSOR + */ +#define M98090_DRCCMP_MASK (7<<5) +#define M98090_DRCCMP_SHIFT 5 +#define M98090_DRCCMP_WIDTH 3 +#define M98090_DRCTHC_MASK (31<<0) +#define M98090_DRCTHC_SHIFT 0 +#define M98090_DRCTHC_WIDTH 5 +#define M98090_DRCTHC_NUM (1<<M98090_DRCTHC_WIDTH) + +/* + * M98090_REG_DRC_EXPANDER + */ +#define M98090_DRCEXP_MASK (7<<5) +#define M98090_DRCEXP_SHIFT 5 +#define M98090_DRCEXP_WIDTH 3 +#define M98090_DRCTHE_MASK (31<<0) +#define M98090_DRCTHE_SHIFT 0 +#define M98090_DRCTHE_WIDTH 5 +#define M98090_DRCTHE_NUM (1<<M98090_DRCTHE_WIDTH) + +/* + * M98090_REG_DRC_GAIN + */ +#define M98090_DRCG_MASK (31<<0) +#define M98090_DRCG_SHIFT 0 +#define M98090_DRCG_WIDTH 5 +#define M98090_DRCG_NUM 13 + +/* + * M98090_REG_RCV_LOUTL_MIXER + */ +#define M98090_MIXRCVL_MIC2_MASK (1<<5) +#define M98090_MIXRCVL_MIC2_SHIFT 5 +#define M98090_MIXRCVL_MIC2_WIDTH 1 +#define M98090_MIXRCVL_MIC1_MASK (1<<4) +#define M98090_MIXRCVL_MIC1_SHIFT 4 +#define M98090_MIXRCVL_MIC1_WIDTH 1 +#define M98090_MIXRCVL_LINEB_MASK (1<<3) +#define M98090_MIXRCVL_LINEB_SHIFT 3 +#define M98090_MIXRCVL_LINEB_WIDTH 1 +#define M98090_MIXRCVL_LINEA_MASK (1<<2) +#define M98090_MIXRCVL_LINEA_SHIFT 2 +#define M98090_MIXRCVL_LINEA_WIDTH 1 +#define M98090_MIXRCVL_DACR_MASK (1<<1) +#define M98090_MIXRCVL_DACR_SHIFT 1 +#define M98090_MIXRCVL_DACR_WIDTH 1 +#define M98090_MIXRCVL_DACL_MASK (1<<0) +#define M98090_MIXRCVL_DACL_SHIFT 0 +#define M98090_MIXRCVL_DACL_WIDTH 1 +#define M98090_MIXRCVL_MASK (63<<0) +#define M98090_MIXRCVL_SHIFT 0 +#define M98090_MIXRCVL_WIDTH 6 + +/* + * M98090_REG_RCV_LOUTL_CONTROL + */ +#define M98090_MIXRCVLG_MASK (3<<0) +#define M98090_MIXRCVLG_SHIFT 0 +#define M98090_MIXRCVLG_WIDTH 2 +#define M98090_MIXRCVLG_NUM (1<<M98090_MIXRCVLG_WIDTH) + +/* + * M98090_REG_RCV_LOUTL_VOLUME + */ +#define M98090_RCVLM_MASK (1<<7) +#define M98090_RCVLM_SHIFT 7 +#define M98090_RCVLM_WIDTH 1 +#define M98090_RCVLVOL_MASK (31<<0) +#define M98090_RCVLVOL_SHIFT 0 +#define M98090_RCVLVOL_WIDTH 5 +#define M98090_RCVLVOL_NUM (1<<M98090_RCVLVOL_WIDTH) + +/* + * M98090_REG_LOUTR_MIXER + */ +#define M98090_LINMOD_MASK (1<<7) +#define M98090_LINMOD_SHIFT 7 +#define M98090_LINMOD_WIDTH 1 +#define M98090_MIXRCVR_MIC2_MASK (1<<5) +#define M98090_MIXRCVR_MIC2_SHIFT 5 +#define M98090_MIXRCVR_MIC2_WIDTH 1 +#define M98090_MIXRCVR_MIC1_MASK (1<<4) +#define M98090_MIXRCVR_MIC1_SHIFT 4 +#define M98090_MIXRCVR_MIC1_WIDTH 1 +#define M98090_MIXRCVR_LINEB_MASK (1<<3) +#define M98090_MIXRCVR_LINEB_SHIFT 3 +#define M98090_MIXRCVR_LINEB_WIDTH 1 +#define M98090_MIXRCVR_LINEA_MASK (1<<2) +#define M98090_MIXRCVR_LINEA_SHIFT 2 +#define M98090_MIXRCVR_LINEA_WIDTH 1 +#define M98090_MIXRCVR_DACR_MASK (1<<1) +#define M98090_MIXRCVR_DACR_SHIFT 1 +#define M98090_MIXRCVR_DACR_WIDTH 1 +#define M98090_MIXRCVR_DACL_MASK (1<<0) +#define M98090_MIXRCVR_DACL_SHIFT 0 +#define M98090_MIXRCVR_DACL_WIDTH 1 +#define M98090_MIXRCVR_MASK (63<<0) +#define M98090_MIXRCVR_SHIFT 0 +#define M98090_MIXRCVR_WIDTH 6 + +/* + * M98090_REG_LOUTR_CONTROL + */ +#define M98090_MIXRCVRG_MASK (3<<0) +#define M98090_MIXRCVRG_SHIFT 0 +#define M98090_MIXRCVRG_WIDTH 2 +#define M98090_MIXRCVRG_NUM (1<<M98090_MIXRCVRG_WIDTH) + +/* + * M98090_REG_LOUTR_VOLUME + */ +#define M98090_RCVRM_MASK (1<<7) +#define M98090_RCVRM_SHIFT 7 +#define M98090_RCVRM_WIDTH 1 +#define M98090_RCVRVOL_MASK (31<<0) +#define M98090_RCVRVOL_SHIFT 0 +#define M98090_RCVRVOL_WIDTH 5 +#define M98090_RCVRVOL_NUM (1<<M98090_RCVRVOL_WIDTH) + +/* + * M98090_REG_JACK_DETECT + */ +#define M98090_JDETEN_MASK (1<<7) +#define M98090_JDETEN_SHIFT 7 +#define M98090_JDETEN_WIDTH 1 +#define M98090_JDWK_MASK (1<<6) +#define M98090_JDWK_SHIFT 6 +#define M98090_JDWK_WIDTH 1 +#define M98090_JDEB_MASK (3<<0) +#define M98090_JDEB_SHIFT 0 +#define M98090_JDEB_WIDTH 2 +#define M98090_JDEB_25MS (0<<0) +#define M98090_JDEB_50MS (1<<0) +#define M98090_JDEB_100MS (2<<0) +#define M98090_JDEB_200MS (3<<0) + +/* + * M98090_REG_INPUT_ENABLE + */ +#define M98090_MBEN_MASK (1<<4) +#define M98090_MBEN_SHIFT 4 +#define M98090_MBEN_WIDTH 1 +#define M98090_LINEAEN_MASK (1<<3) +#define M98090_LINEAEN_SHIFT 3 +#define M98090_LINEAEN_WIDTH 1 +#define M98090_LINEBEN_MASK (1<<2) +#define M98090_LINEBEN_SHIFT 2 +#define M98090_LINEBEN_WIDTH 1 +#define M98090_ADREN_MASK (1<<1) +#define M98090_ADREN_SHIFT 1 +#define M98090_ADREN_WIDTH 1 +#define M98090_ADLEN_MASK (1<<0) +#define M98090_ADLEN_SHIFT 0 +#define M98090_ADLEN_WIDTH 1 + +/* + * M98090_REG_OUTPUT_ENABLE + */ +#define M98090_HPREN_MASK (1<<7) +#define M98090_HPREN_SHIFT 7 +#define M98090_HPREN_WIDTH 1 +#define M98090_HPLEN_MASK (1<<6) +#define M98090_HPLEN_SHIFT 6 +#define M98090_HPLEN_WIDTH 1 +#define M98090_SPREN_MASK (1<<5) +#define M98090_SPREN_SHIFT 5 +#define M98090_SPREN_WIDTH 1 +#define M98090_SPLEN_MASK (1<<4) +#define M98090_SPLEN_SHIFT 4 +#define M98090_SPLEN_WIDTH 1 +#define M98090_RCVLEN_MASK (1<<3) +#define M98090_RCVLEN_SHIFT 3 +#define M98090_RCVLEN_WIDTH 1 +#define M98090_RCVREN_MASK (1<<2) +#define M98090_RCVREN_SHIFT 2 +#define M98090_RCVREN_WIDTH 1 +#define M98090_DAREN_MASK (1<<1) +#define M98090_DAREN_SHIFT 1 +#define M98090_DAREN_WIDTH 1 +#define M98090_DALEN_MASK (1<<0) +#define M98090_DALEN_SHIFT 0 +#define M98090_DALEN_WIDTH 1 + +/* + * M98090_REG_LEVEL_CONTROL + */ +#define M98090_ZDENN_MASK (1<<2) +#define M98090_ZDENN_SHIFT 2 +#define M98090_ZDENN_WIDTH 1 +#define M98090_ZDENN_NUM (1<<M98090_ZDENN_WIDTH) +#define M98090_VS2ENN_MASK (1<<1) +#define M98090_VS2ENN_SHIFT 1 +#define M98090_VS2ENN_WIDTH 1 +#define M98090_VS2ENN_NUM (1<<M98090_VS2ENN_WIDTH) +#define M98090_VSENN_MASK (1<<0) +#define M98090_VSENN_SHIFT 0 +#define M98090_VSENN_WIDTH 1 +#define M98090_VSENN_NUM (1<<M98090_VSENN_WIDTH) + +/* + * M98090_REG_DSP_FILTER_ENABLE + */ +#define M98090_DMIC34BQEN_MASK (1<<4) +#define M98090_DMIC34BQEN_SHIFT 4 +#define M98090_DMIC34BQEN_WIDTH 1 +#define M98090_DMIC34BQEN_NUM (1<<M98090_DMIC34BQEN_WIDTH) +#define M98090_ADCBQEN_MASK (1<<3) +#define M98090_ADCBQEN_SHIFT 3 +#define M98090_ADCBQEN_WIDTH 1 +#define M98090_ADCBQEN_NUM (1<<M98090_ADCBQEN_WIDTH) +#define M98090_EQ3BANDEN_MASK (1<<2) +#define M98090_EQ3BANDEN_SHIFT 2 +#define M98090_EQ3BANDEN_WIDTH 1 +#define M98090_EQ3BANDEN_NUM (1<<M98090_EQ3BANDEN_WIDTH) +#define M98090_EQ5BANDEN_MASK (1<<1) +#define M98090_EQ5BANDEN_SHIFT 1 +#define M98090_EQ5BANDEN_WIDTH 1 +#define M98090_EQ5BANDEN_NUM (1<<M98090_EQ5BANDEN_WIDTH) +#define M98090_EQ7BANDEN_MASK (1<<0) +#define M98090_EQ7BANDEN_SHIFT 0 +#define M98090_EQ7BANDEN_WIDTH 1 +#define M98090_EQ7BANDEN_NUM (1<<M98090_EQ7BANDEN_WIDTH) + +/* + * M98090_REG_BIAS_CONTROL + */ +#define M98090_VCM_MODE_MASK (1<<0) +#define M98090_VCM_MODE_SHIFT 0 +#define M98090_VCM_MODE_WIDTH 1 +#define M98090_VCM_MODE_NUM (1<<M98090_VCM_MODE_WIDTH) + +/* + * M98090_REG_DAC_CONTROL + */ +#define M98090_PERFMODE_MASK (1<<1) +#define M98090_PERFMODE_SHIFT 1 +#define M98090_PERFMODE_WIDTH 1 +#define M98090_PERFMODE_NUM (1<<M98090_PERFMODE_WIDTH) +#define M98090_DACHP_MASK (1<<0) +#define M98090_DACHP_SHIFT 0 +#define M98090_DACHP_WIDTH 1 +#define M98090_DACHP_NUM (1<<M98090_DACHP_WIDTH) + +/* + * M98090_REG_ADC_CONTROL + */ +#define M98090_OSR128_MASK (1<<2) +#define M98090_OSR128_SHIFT 2 +#define M98090_OSR128_WIDTH 1 +#define M98090_ADCDITHER_MASK (1<<1) +#define M98090_ADCDITHER_SHIFT 1 +#define M98090_ADCDITHER_WIDTH 1 +#define M98090_ADCDITHER_NUM (1<<M98090_ADCDITHER_WIDTH) +#define M98090_ADCHP_MASK (1<<0) +#define M98090_ADCHP_SHIFT 0 +#define M98090_ADCHP_WIDTH 1 +#define M98090_ADCHP_NUM (1<<M98090_ADCHP_WIDTH) + +/* + * M98090_REG_DEVICE_SHUTDOWN + */ +#define M98090_SHDNN_MASK (1<<7) +#define M98090_SHDNN_SHIFT 7 +#define M98090_SHDNN_WIDTH 1 + +/* + * M98090_REG_EQUALIZER_BASE + */ +#define M98090_B0_1_HI_MASK (255<<0) +#define M98090_B0_1_HI_SHIFT 0 +#define M98090_B0_1_HI_WIDTH 8 +#define M98090_B0_1_MID_MASK (255<<0) +#define M98090_B0_1_MID_SHIFT 0 +#define M98090_B0_1_MID_WIDTH 8 +#define M98090_B0_1_LO_MASK (255<<0) +#define M98090_B0_1_LO_SHIFT 0 +#define M98090_B0_1_LO_WIDTH 8 +#define M98090_B1_1_HI_MASK (255<<0) +#define M98090_B1_1_HI_SHIFT 0 +#define M98090_B1_1_HI_WIDTH 8 +#define M98090_B1_1_MID_MASK (255<<0) +#define M98090_B1_1_MID_SHIFT 0 +#define M98090_B1_1_MID_WIDTH 8 +#define M98090_B1_1_LO_MASK (255<<0) +#define M98090_B1_1_LO_SHIFT 0 +#define M98090_B1_1_LO_WIDTH 8 +#define M98090_B2_1_HI_MASK (255<<0) +#define M98090_B2_1_HI_SHIFT 0 +#define M98090_B2_1_HI_WIDTH 8 +#define M98090_B2_1_MID_MASK (255<<0) +#define M98090_B2_1_MID_SHIFT 0 +#define M98090_B2_1_MID_WIDTH 8 +#define M98090_B2_1_LO_MASK (255<<0) +#define M98090_B2_1_LO_SHIFT 0 +#define M98090_B2_1_LO_WIDTH 8 +#define M98090_A1_1_HI_MASK (255<<0) +#define M98090_A1_1_HI_SHIFT 0 +#define M98090_A1_1_HI_WIDTH 8 +#define M98090_A1_1_MID_MASK (255<<0) +#define M98090_A1_1_MID_SHIFT 0 +#define M98090_A1_1_MID_WIDTH 8 +#define M98090_A1_1_LO_MASK (255<<0) +#define M98090_A1_1_LO_SHIFT 0 +#define M98090_A1_1_LO_WIDTH 8 +#define M98090_A2_1_HI_MASK (255<<0) +#define M98090_A2_1_HI_SHIFT 0 +#define M98090_A2_1_HI_WIDTH 8 +#define M98090_A2_1_MID_MASK (255<<0) +#define M98090_A2_1_MID_SHIFT 0 +#define M98090_A2_1_MID_WIDTH 8 +#define M98090_A2_1_LO_MASK (255<<0) +#define M98090_A2_1_LO_SHIFT 0 +#define M98090_A2_1_LO_WIDTH 8 + +#define M98090_COEFS_PER_BAND 5 +#define M98090_COEFS_BLK_SZ (M98090_COEFS_PER_BAND * 3) +#define M98090_COEFS_MAX_SZ (M98090_COEFS_BLK_SZ * 7) + +/* + * M98090_REG_RECORD_BIQUAD_BASE + */ +#define M98090_REC_B0_HI_MASK (255<<0) +#define M98090_REC_B0_HI_SHIFT 0 +#define M98090_REC_B0_HI_WIDTH 8 +#define M98090_REC_B0_MID_MASK (255<<0) +#define M98090_REC_B0_MID_SHIFT 0 +#define M98090_REC_B0_MID_WIDTH 8 +#define M98090_REC_B0_LO_MASK (255<<0) +#define M98090_REC_B0_LO_SHIFT 0 +#define M98090_REC_B0_LO_WIDTH 8 +#define M98090_REC_B1_HI_MASK (255<<0) +#define M98090_REC_B1_HI_SHIFT 0 +#define M98090_REC_B1_HI_WIDTH 8 +#define M98090_REC_B1_MID_MASK (255<<0) +#define M98090_REC_B1_MID_SHIFT 0 +#define M98090_REC_B1_MID_WIDTH 8 +#define M98090_REC_B1_LO_MASK (255<<0) +#define M98090_REC_B1_LO_SHIFT 0 +#define M98090_REC_B1_LO_WIDTH 8 +#define M98090_REC_B2_HI_MASK (255<<0) +#define M98090_REC_B2_HI_SHIFT 0 +#define M98090_REC_B2_HI_WIDTH 8 +#define M98090_REC_B2_MID_MASK (255<<0) +#define M98090_REC_B2_MID_SHIFT 0 +#define M98090_REC_B2_MID_WIDTH 8 +#define M98090_REC_B2_LO_MASK (255<<0) +#define M98090_REC_B2_LO_SHIFT 0 +#define M98090_REC_B2_LO_WIDTH 8 +#define M98090_REC_A1_HI_MASK (255<<0) +#define M98090_REC_A1_HI_SHIFT 0 +#define M98090_REC_A1_HI_WIDTH 8 +#define M98090_REC_A1_MID_MASK (255<<0) +#define M98090_REC_A1_MID_SHIFT 0 +#define M98090_REC_A1_MID_WIDTH 8 +#define M98090_REC_A1_LO_MASK (255<<0) +#define M98090_REC_A1_LO_SHIFT 0 +#define M98090_REC_A1_LO_WIDTH 8 +#define M98090_REC_A2_HI_MASK (255<<0) +#define M98090_REC_A2_HI_SHIFT 0 +#define M98090_REC_A2_HI_WIDTH 8 +#define M98090_REC_A2_MID_MASK (255<<0) +#define M98090_REC_A2_MID_SHIFT 0 +#define M98090_REC_A2_MID_WIDTH 8 +#define M98090_REC_A2_LO_MASK (255<<0) +#define M98090_REC_A2_LO_SHIFT 0 +#define M98090_REC_A2_LO_WIDTH 8 + +/* + * M98090_REG_DMIC3_VOLUME + */ +#define M98090_DMIC_AV3G_MASK (7<<4) +#define M98090_DMIC_AV3G_SHIFT 4 +#define M98090_DMIC_AV3G_WIDTH 3 +#define M98090_DMIC_AV3G_NUM (1<<M98090_DMIC_AV3G_WIDTH) +#define M98090_DMIC_AV3_MASK (15<<0) +#define M98090_DMIC_AV3_SHIFT 0 +#define M98090_DMIC_AV3_WIDTH 4 +#define M98090_DMIC_AV3_NUM (1<<M98090_DMIC_AV3_WIDTH) + +/* + * M98090_REG_DMIC4_VOLUME + */ +#define M98090_DMIC_AV4G_MASK (7<<4) +#define M98090_DMIC_AV4G_SHIFT 4 +#define M98090_DMIC_AV4G_WIDTH 3 +#define M98090_DMIC_AV4G_NUM (1<<M98090_DMIC_AV4G_WIDTH) +#define M98090_DMIC_AV4_MASK (15<<0) +#define M98090_DMIC_AV4_SHIFT 0 +#define M98090_DMIC_AV4_WIDTH 4 +#define M98090_DMIC_AV4_NUM (1<<M98090_DMIC_AV4_WIDTH) + +/* + * M98090_REG_DMIC34_BQ_PREATTEN + */ +#define M98090_AV34BQ_MASK (15<<0) +#define M98090_AV34BQ_SHIFT 0 +#define M98090_AV34BQ_WIDTH 4 +#define M98090_AV34BQ_NUM (1<<M98090_AV34BQ_WIDTH) + +/* + * M98090_REG_RECORD_TDM_SLOT + */ +#define M98090_TDM_SLOTADCL_MASK (3<<6) +#define M98090_TDM_SLOTADCL_SHIFT 6 +#define M98090_TDM_SLOTADCL_WIDTH 2 +#define M98090_TDM_SLOTADCL_NUM (1<<M98090_TDM_SLOTADCL_WIDTH) +#define M98090_TDM_SLOTADCR_MASK (3<<4) +#define M98090_TDM_SLOTADCR_SHIFT 4 +#define M98090_TDM_SLOTADCR_WIDTH 2 +#define M98090_TDM_SLOTADCR_NUM (1<<M98090_TDM_SLOTADCR_WIDTH) +#define M98090_TDM_SLOTDMIC3_MASK (3<<2) +#define M98090_TDM_SLOTDMIC3_SHIFT 2 +#define M98090_TDM_SLOTDMIC3_WIDTH 2 +#define M98090_TDM_SLOTDMIC3_NUM (1<<M98090_TDM_SLOTDMIC3_WIDTH) +#define M98090_TDM_SLOTDMIC4_MASK (3<<0) +#define M98090_TDM_SLOTDMIC4_SHIFT 0 +#define M98090_TDM_SLOTDMIC4_WIDTH 2 +#define M98090_TDM_SLOTDMIC4_NUM (1<<M98090_TDM_SLOTDMIC4_WIDTH) + +/* + * M98090_REG_SAMPLE_RATE + */ +#define M98090_DMIC34_ZEROPAD_MASK (1<<4) +#define M98090_DMIC34_ZEROPAD_SHIFT 4 +#define M98090_DMIC34_ZEROPAD_WIDTH 1 +#define M98090_DMIC34_ZEROPAD_NUM (1<<M98090_DIGMIC4_WIDTH) +#define M98090_DMIC34_SRDIV_MASK (7<<0) +#define M98090_DMIC34_SRDIV_SHIFT 0 +#define M98090_DMIC34_SRDIV_WIDTH 3 + +/* + * M98090_REG_DMIC34_BIQUAD_BASE + */ +#define M98090_DMIC34_B0_HI_MASK (255<<0) +#define M98090_DMIC34_B0_HI_SHIFT 0 +#define M98090_DMIC34_B0_HI_WIDTH 8 +#define M98090_DMIC34_B0_MID_MASK (255<<0) +#define M98090_DMIC34_B0_MID_SHIFT 0 +#define M98090_DMIC34_B0_MID_WIDTH 8 +#define M98090_DMIC34_B0_LO_MASK (255<<0) +#define M98090_DMIC34_B0_LO_SHIFT 0 +#define M98090_DMIC34_B0_LO_WIDTH 8 +#define M98090_DMIC34_B1_HI_MASK (255<<0) +#define M98090_DMIC34_B1_HI_SHIFT 0 +#define M98090_DMIC34_B1_HI_WIDTH 8 +#define M98090_DMIC34_B1_MID_MASK (255<<0) +#define M98090_DMIC34_B1_MID_SHIFT 0 +#define M98090_DMIC34_B1_MID_WIDTH 8 +#define M98090_DMIC34_B1_LO_MASK (255<<0) +#define M98090_DMIC34_B1_LO_SHIFT 0 +#define M98090_DMIC34_B1_LO_WIDTH 8 +#define M98090_DMIC34_B2_HI_MASK (255<<0) +#define M98090_DMIC34_B2_HI_SHIFT 0 +#define M98090_DMIC34_B2_HI_WIDTH 8 +#define M98090_DMIC34_B2_MID_MASK (255<<0) +#define M98090_DMIC34_B2_MID_SHIFT 0 +#define M98090_DMIC34_B2_MID_WIDTH 8 +#define M98090_DMIC34_B2_LO_MASK (255<<0) +#define M98090_DMIC34_B2_LO_SHIFT 0 +#define M98090_DMIC34_B2_LO_WIDTH 8 +#define M98090_DMIC34_A1_HI_MASK (255<<0) +#define M98090_DMIC34_A1_HI_SHIFT 0 +#define M98090_DMIC34_A1_HI_WIDTH 8 +#define M98090_DMIC34_A1_MID_MASK (255<<0) +#define M98090_DMIC34_A1_MID_SHIFT 0 +#define M98090_DMIC34_A1_MID_WIDTH 8 +#define M98090_DMIC34_A1_LO_MASK (255<<0) +#define M98090_DMIC34_A1_LO_SHIFT 0 +#define M98090_DMIC34_A1_LO_WIDTH 8 +#define M98090_DMIC34_A2_HI_MASK (255<<0) +#define M98090_DMIC34_A2_HI_SHIFT 0 +#define M98090_DMIC34_A2_HI_WIDTH 8 +#define M98090_DMIC34_A2_MID_MASK (255<<0) +#define M98090_DMIC34_A2_MID_SHIFT 0 +#define M98090_DMIC34_A2_MID_WIDTH 8 +#define M98090_DMIC34_A2_LO_MASK (255<<0) +#define M98090_DMIC34_A2_LO_SHIFT 0 +#define M98090_DMIC34_A2_LO_WIDTH 8 + +#define M98090_JACK_STATE_NO_HEADSET 0 +#define M98090_JACK_STATE_NO_HEADSET_2 1 +#define M98090_JACK_STATE_HEADPHONE 2 +#define M98090_JACK_STATE_HEADSET 3 + +/* + * M98090_REG_REVISION_ID + */ +#define M98090_REVID_MASK (255<<0) +#define M98090_REVID_SHIFT 0 +#define M98090_REVID_WIDTH 8 +#define M98090_REVID_NUM (1<<M98090_REVID_WIDTH) + +#define M98090_BYTE1(w) ((w >> 8) & 0xff) +#define M98090_BYTE0(w) (w & 0xff) + +/* Silicon revision number */ +#define M98090_REVA 0x40 +#define M98091_REVA 0x50 + +enum max98090_type { + MAX98090, + MAX98091, +}; + +struct max98090_cdata { + unsigned int rate; + unsigned int fmt; +}; + +struct max98090_priv { + struct regmap *regmap; + struct snd_soc_codec *codec; + enum max98090_type devtype; + void *control_data; + struct max98090_pdata *pdata; + unsigned int sysclk; + unsigned int bclk; + unsigned int lrclk; + struct max98090_cdata dai[1]; + int irq; + int jack_state; + struct delayed_work jack_work; + struct snd_soc_jack *jack; + unsigned int dai_fmt; + int tdm_slots; + int tdm_width; + u8 lin_state; + unsigned int pa1en; + unsigned int pa2en; + unsigned int extmic_mux; + unsigned int sidetone; +}; + +int max98090_mic_detect(struct snd_soc_codec *codec, + struct snd_soc_jack *jack); + +#endif diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index ba82ba2a7133..65d09d60b7c6 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1241,13 +1241,13 @@ static struct snd_soc_dai_driver aic3x_dai = { .name = "tlv320aic3x-hifi", .playback = { .stream_name = "Playback", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = AIC3X_RATES, .formats = AIC3X_FORMATS,}, .capture = { .stream_name = "Capture", - .channels_min = 1, + .channels_min = 2, .channels_max = 2, .rates = AIC3X_RATES, .formats = AIC3X_FORMATS,}, diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 12bcae63a7f0..eb96b8768098 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -76,6 +76,8 @@ struct wm2000_priv { int anc_download_size; char *anc_download; + + struct mutex lock; }; static int wm2000_write(struct i2c_client *i2c, unsigned int reg, @@ -599,13 +601,20 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); int anc_active = ucontrol->value.enumerated.item[0]; + int ret; if (anc_active > 1) return -EINVAL; + mutex_lock(&wm2000->lock); + wm2000->anc_active = anc_active; - return wm2000_anc_set_mode(wm2000); + ret = wm2000_anc_set_mode(wm2000); + + mutex_unlock(&wm2000->lock); + + return ret; } static int wm2000_speaker_get(struct snd_kcontrol *kcontrol, @@ -625,13 +634,20 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); int val = ucontrol->value.enumerated.item[0]; + int ret; if (val > 1) return -EINVAL; + mutex_lock(&wm2000->lock); + wm2000->spk_ena = val; - return wm2000_anc_set_mode(wm2000); + ret = wm2000_anc_set_mode(wm2000); + + mutex_unlock(&wm2000->lock); + + return ret; } static const struct snd_kcontrol_new wm2000_controls[] = { @@ -648,6 +664,9 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, { struct snd_soc_codec *codec = w->codec; struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev); + int ret; + + mutex_lock(&wm2000->lock); if (SND_SOC_DAPM_EVENT_ON(event)) wm2000->anc_eng_ena = 1; @@ -655,7 +674,11 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w, if (SND_SOC_DAPM_EVENT_OFF(event)) wm2000->anc_eng_ena = 0; - return wm2000_anc_set_mode(wm2000); + ret = wm2000_anc_set_mode(wm2000); + + mutex_unlock(&wm2000->lock); + + return ret; } static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = { @@ -782,6 +805,8 @@ static int wm2000_i2c_probe(struct i2c_client *i2c, return -ENOMEM; } + mutex_init(&wm2000->lock); + dev_set_drvdata(&i2c->dev, wm2000); wm2000->regmap = devm_regmap_init_i2c(i2c, &wm2000_regmap); diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index d8c65f574658..d5371e0d8909 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -1126,9 +1126,9 @@ SOC_DOUBLE_R_TLV("IN3 Volume", WM2200_IN3L_CONTROL, WM2200_IN3R_CONTROL, SOC_DOUBLE_R("IN1 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L, WM2200_ADC_DIGITAL_VOLUME_1R, WM2200_IN1L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN2 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L, +SOC_DOUBLE_R("IN2 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_2L, WM2200_ADC_DIGITAL_VOLUME_2R, WM2200_IN2L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN3 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L, +SOC_DOUBLE_R("IN3 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_3L, WM2200_ADC_DIGITAL_VOLUME_3R, WM2200_IN3L_MUTE_SHIFT, 1, 1), SOC_DOUBLE_R_TLV("IN1 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_1L, diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c index 1440b3f9b7bb..5e85b645f2eb 100644 --- a/sound/soc/codecs/wm5102.c +++ b/sound/soc/codecs/wm5102.c @@ -45,6 +45,7 @@ static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); static const struct wm_adsp_region wm5102_dsp1_regions[] = { { .type = WMFW_ADSP2_PM, .base = 0x100000 }, @@ -603,6 +604,17 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w, return 0; } +#define WM5102_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0) + static const struct snd_kcontrol_new wm5102_snd_controls[] = { SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, ARIZONA_IN1_OSR_SHIFT, 1, 0), @@ -611,32 +623,44 @@ SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL, SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL, ARIZONA_IN3_OSR_SHIFT, 1, 0), -SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL, - ARIZONA_IN1R_CONTROL, - ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL, - ARIZONA_IN2R_CONTROL, - ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL, - ARIZONA_IN3R_CONTROL, - ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), - -SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1), - -SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, + ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_MUTE_SHIFT, 1, 1), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), @@ -774,6 +798,22 @@ SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT, ARIZONA_SPK1R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM5102_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +WM5102_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +WM5102_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L), +WM5102_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R), +WM5102_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L), +WM5102_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L), +WM5102_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R), +WM5102_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L), +WM5102_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R), + ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), @@ -880,6 +920,18 @@ ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE); ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE); ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE); + +ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE); +ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE); + ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE); ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE); @@ -1002,6 +1054,26 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0, SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3, + ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0), + +SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0), +SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3, + ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, @@ -1138,6 +1210,18 @@ ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"), ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"), ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"), +ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"), +ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"), +ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"), + +ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"), +ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"), + +ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"), +ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"), + WM_ADSP2("DSP1", 0), SND_SOC_DAPM_OUTPUT("HPOUT1L"), @@ -1193,6 +1277,14 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"), { name, "ASRC1R", "ASRC1R" }, \ { name, "ASRC2L", "ASRC2L" }, \ { name, "ASRC2R", "ASRC2R" }, \ + { name, "ISRC1DEC1", "ISRC1DEC1" }, \ + { name, "ISRC1DEC2", "ISRC1DEC2" }, \ + { name, "ISRC1INT1", "ISRC1INT1" }, \ + { name, "ISRC1INT2", "ISRC1INT2" }, \ + { name, "ISRC2DEC1", "ISRC2DEC1" }, \ + { name, "ISRC2DEC2", "ISRC2DEC2" }, \ + { name, "ISRC2INT1", "ISRC2INT1" }, \ + { name, "ISRC2INT2", "ISRC2INT2" }, \ { name, "DSP1.1", "DSP1" }, \ { name, "DSP1.2", "DSP1" }, \ { name, "DSP1.3", "DSP1" }, \ @@ -1289,6 +1381,18 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { { "ASRC2L", NULL, "ASRC2L Input" }, { "ASRC2R", NULL, "ASRC2R Input" }, + { "ISRC1DEC1", NULL, "ISRC1DEC1 Input" }, + { "ISRC1DEC2", NULL, "ISRC1DEC2 Input" }, + + { "ISRC1INT1", NULL, "ISRC1INT1 Input" }, + { "ISRC1INT2", NULL, "ISRC1INT2 Input" }, + + { "ISRC2DEC1", NULL, "ISRC2DEC1 Input" }, + { "ISRC2DEC2", NULL, "ISRC2DEC2 Input" }, + + { "ISRC2INT1", NULL, "ISRC2INT1 Input" }, + { "ISRC2INT2", NULL, "ISRC2INT2 Input" }, + ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"), ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"), ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"), @@ -1336,6 +1440,18 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = { ARIZONA_MUX_ROUTES("ASRC2L"), ARIZONA_MUX_ROUTES("ASRC2R"), + ARIZONA_MUX_ROUTES("ISRC1INT1"), + ARIZONA_MUX_ROUTES("ISRC1INT2"), + + ARIZONA_MUX_ROUTES("ISRC1DEC1"), + ARIZONA_MUX_ROUTES("ISRC1DEC2"), + + ARIZONA_MUX_ROUTES("ISRC2INT1"), + ARIZONA_MUX_ROUTES("ISRC2INT2"), + + ARIZONA_MUX_ROUTES("ISRC2DEC1"), + ARIZONA_MUX_ROUTES("ISRC2DEC2"), + ARIZONA_DSP_ROUTES("DSP1"), { "AEC Loopback", "HPOUT1L", "OUT1L" }, @@ -1463,6 +1579,10 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec) if (ret != 0) return ret; + ret = snd_soc_add_codec_controls(codec, wm_adsp_fw_controls, 1); + if (ret != 0) + return ret; + snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS"); priv->core.arizona->dapm = &codec->dapm; diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c index 7a090968c4f7..23199372d518 100644 --- a/sound/soc/codecs/wm5110.c +++ b/sound/soc/codecs/wm5110.c @@ -41,6 +41,21 @@ static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0); static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0); static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0); +static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0); + +#define WM5110_NG_SRC(name, base) \ + SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \ + SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \ + SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \ + SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \ + SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \ + SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0) static const struct snd_kcontrol_new wm5110_snd_controls[] = { SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL, @@ -52,37 +67,52 @@ SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL, SOC_SINGLE("IN4 High Performance Switch", ARIZONA_IN4L_CONTROL, ARIZONA_IN4_OSR_SHIFT, 1, 0), -SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL, - ARIZONA_IN1R_CONTROL, - ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL, - ARIZONA_IN2R_CONTROL, - ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), -SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL, - ARIZONA_IN3R_CONTROL, - ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), - -SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1), -SOC_DOUBLE_R("IN4 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L, - ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_MUTE_SHIFT, 1, 1), - -SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, - ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, - ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, - ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), -SOC_DOUBLE_R_TLV("IN4 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L, - ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_DIG_VOL_SHIFT, - 0xbf, 0, digital_tlv), +SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL, + ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL, + ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL, + ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL, + ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL, + ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), +SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL, + ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv), + +SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN4L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L, + ARIZONA_IN4L_MUTE_SHIFT, 1, 1), +SOC_SINGLE("IN4R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4R, + ARIZONA_IN4R_MUTE_SHIFT, 1, 1), + +SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L, + ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R, + ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L, + ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R, + ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L, + ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R, + ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN4L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L, + ARIZONA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), +SOC_SINGLE_TLV("IN4R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4R, + ARIZONA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv), SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp), SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp), @@ -263,6 +293,25 @@ SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT, SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp), SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp), +SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_ENA_SHIFT, 1, 0), +SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL, + ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv), +SOC_ENUM("Noise Gate Hold", arizona_ng_hold), + +WM5110_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L), +WM5110_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R), +WM5110_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L), +WM5110_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R), +WM5110_NG_SRC("HPOUT3L", ARIZONA_NOISE_GATE_SELECT_3L), +WM5110_NG_SRC("HPOUT3R", ARIZONA_NOISE_GATE_SELECT_3R), +WM5110_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L), +WM5110_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R), +WM5110_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L), +WM5110_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R), +WM5110_NG_SRC("SPKDAT2L", ARIZONA_NOISE_GATE_SELECT_6L), +WM5110_NG_SRC("SPKDAT2R", ARIZONA_NOISE_GATE_SELECT_6R), + ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE), ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE), diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index b6b654837585..f3f7e75f8628 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -15,6 +15,7 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/firmware.h> +#include <linux/list.h> #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> @@ -103,9 +104,19 @@ #define ADSP1_START_SHIFT 0 /* DSP1_START */ #define ADSP1_START_WIDTH 1 /* DSP1_START */ -#define ADSP2_CONTROL 0 -#define ADSP2_CLOCKING 1 -#define ADSP2_STATUS1 4 +/* + * ADSP1 Control 31 + */ +#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */ +#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */ +#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */ + +#define ADSP2_CONTROL 0x0 +#define ADSP2_CLOCKING 0x1 +#define ADSP2_STATUS1 0x4 +#define ADSP2_WDMA_CONFIG_1 0x30 +#define ADSP2_WDMA_CONFIG_2 0x31 +#define ADSP2_RDMA_CONFIG_1 0x34 /* * ADSP2 Control @@ -143,6 +154,109 @@ #define ADSP2_RAM_RDY_SHIFT 0 #define ADSP2_RAM_RDY_WIDTH 1 +struct wm_adsp_buf { + struct list_head list; + void *buf; +}; + +static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len, + struct list_head *list) +{ + struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL); + + if (buf == NULL) + return NULL; + + buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA); + if (!buf->buf) { + kfree(buf); + return NULL; + } + + if (list) + list_add_tail(&buf->list, list); + + return buf; +} + +static void wm_adsp_buf_free(struct list_head *list) +{ + while (!list_empty(list)) { + struct wm_adsp_buf *buf = list_first_entry(list, + struct wm_adsp_buf, + list); + list_del(&buf->list); + kfree(buf->buf); + kfree(buf); + } +} + +#define WM_ADSP_NUM_FW 4 + +static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = { + "MBC/VSS", "Tx", "Tx Speaker", "Rx ANC" +}; + +static struct { + const char *file; +} wm_adsp_fw[WM_ADSP_NUM_FW] = { + { .file = "mbc-vss" }, + { .file = "tx" }, + { .file = "tx-spk" }, + { .file = "rx-anc" }, +}; + +static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = adsp[e->shift_l].fw; + + return 0; +} + +static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw) + return 0; + + if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW) + return -EINVAL; + + if (adsp[e->shift_l].running) + return -EBUSY; + + adsp[e->shift_l].fw = ucontrol->value.integer.value[0]; + + return 0; +} + +static const struct soc_enum wm_adsp_fw_enum[] = { + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), + SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text), +}; + +const struct snd_kcontrol_new wm_adsp_fw_controls[] = { + SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2], + wm_adsp_fw_get, wm_adsp_fw_put), + SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3], + wm_adsp_fw_get, wm_adsp_fw_put), +}; +EXPORT_SYMBOL_GPL(wm_adsp_fw_controls); static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, int type) @@ -156,8 +270,29 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp, return NULL; } +static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region, + unsigned int offset) +{ + switch (region->type) { + case WMFW_ADSP1_PM: + return region->base + (offset * 3); + case WMFW_ADSP1_DM: + return region->base + (offset * 2); + case WMFW_ADSP2_XM: + return region->base + (offset * 2); + case WMFW_ADSP2_YM: + return region->base + (offset * 2); + case WMFW_ADSP1_ZM: + return region->base + (offset * 2); + default: + WARN_ON(NULL != "Unknown memory region type"); + return offset; + } +} + static int wm_adsp_load(struct wm_adsp *dsp) { + LIST_HEAD(buf_list); const struct firmware *firmware; struct regmap *regmap = dsp->regmap; unsigned int pos = 0; @@ -169,7 +304,7 @@ static int wm_adsp_load(struct wm_adsp *dsp) const struct wm_adsp_region *mem; const char *region_name; char *file, *text; - void *buf; + struct wm_adsp_buf *buf; unsigned int reg; int regions = 0; int ret, offset, type, sizes; @@ -178,7 +313,8 @@ static int wm_adsp_load(struct wm_adsp *dsp) if (file == NULL) return -ENOMEM; - snprintf(file, PAGE_SIZE, "%s-dsp%d.wmfw", dsp->part, dsp->num); + snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num, + wm_adsp_fw[dsp->fw].file); file[PAGE_SIZE - 1] = '\0'; ret = request_firmware(&firmware, file, dsp->dev); @@ -283,27 +419,27 @@ static int wm_adsp_load(struct wm_adsp *dsp) case WMFW_ADSP1_PM: BUG_ON(!mem); region_name = "PM"; - reg = mem->base + (offset * 3); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP1_DM: BUG_ON(!mem); region_name = "DM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP2_XM: BUG_ON(!mem); region_name = "XM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP2_YM: BUG_ON(!mem); region_name = "YM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; case WMFW_ADSP1_ZM: BUG_ON(!mem); region_name = "ZM"; - reg = mem->base + (offset * 2); + reg = wm_adsp_region_to_reg(mem, offset); break; default: adsp_warn(dsp, @@ -323,18 +459,16 @@ static int wm_adsp_load(struct wm_adsp *dsp) } if (reg) { - buf = kmemdup(region->data, le32_to_cpu(region->len), - GFP_KERNEL | GFP_DMA); + buf = wm_adsp_buf_alloc(region->data, + le32_to_cpu(region->len), + &buf_list); if (!buf) { adsp_err(dsp, "Out of memory\n"); return -ENOMEM; } - ret = regmap_raw_write(regmap, reg, buf, - le32_to_cpu(region->len)); - - kfree(buf); - + ret = regmap_raw_write_async(regmap, reg, buf->buf, + le32_to_cpu(region->len)); if (ret != 0) { adsp_err(dsp, "%s.%d: Failed to write %d bytes at %d in %s: %d\n", @@ -348,12 +482,20 @@ static int wm_adsp_load(struct wm_adsp *dsp) pos += le32_to_cpu(region->len) + sizeof(*region); regions++; } - + + ret = regmap_async_complete(regmap); + if (ret != 0) { + adsp_err(dsp, "Failed to complete async write: %d\n", ret); + goto out_fw; + } + if (pos > firmware->size) adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", file, regions, pos - firmware->size); out_fw: + regmap_async_complete(regmap); + wm_adsp_buf_free(&buf_list); release_firmware(firmware); out: kfree(file); @@ -361,22 +503,222 @@ out: return ret; } +static int wm_adsp_setup_algs(struct wm_adsp *dsp) +{ + struct regmap *regmap = dsp->regmap; + struct wmfw_adsp1_id_hdr adsp1_id; + struct wmfw_adsp2_id_hdr adsp2_id; + struct wmfw_adsp1_alg_hdr *adsp1_alg; + struct wmfw_adsp2_alg_hdr *adsp2_alg; + void *alg, *buf; + struct wm_adsp_alg_region *region; + const struct wm_adsp_region *mem; + unsigned int pos, term; + size_t algs, buf_size; + __be32 val; + int i, ret; + + switch (dsp->type) { + case WMFW_ADSP1: + mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM); + break; + case WMFW_ADSP2: + mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM); + break; + default: + mem = NULL; + break; + } + + if (mem == NULL) { + BUG_ON(mem != NULL); + return -EINVAL; + } + + switch (dsp->type) { + case WMFW_ADSP1: + ret = regmap_raw_read(regmap, mem->base, &adsp1_id, + sizeof(adsp1_id)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm info: %d\n", + ret); + return ret; + } + + buf = &adsp1_id; + buf_size = sizeof(adsp1_id); + + algs = be32_to_cpu(adsp1_id.algs); + adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", + be32_to_cpu(adsp1_id.fw.id), + (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8, + be32_to_cpu(adsp1_id.fw.ver) & 0xff, + algs); + + pos = sizeof(adsp1_id) / 2; + term = pos + ((sizeof(*adsp1_alg) * algs) / 2); + break; + + case WMFW_ADSP2: + ret = regmap_raw_read(regmap, mem->base, &adsp2_id, + sizeof(adsp2_id)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm info: %d\n", + ret); + return ret; + } + + buf = &adsp2_id; + buf_size = sizeof(adsp2_id); + + algs = be32_to_cpu(adsp2_id.algs); + adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n", + be32_to_cpu(adsp2_id.fw.id), + (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8, + be32_to_cpu(adsp2_id.fw.ver) & 0xff, + algs); + + pos = sizeof(adsp2_id) / 2; + term = pos + ((sizeof(*adsp2_alg) * algs) / 2); + break; + + default: + BUG_ON(NULL == "Unknown DSP type"); + return -EINVAL; + } + + if (algs == 0) { + adsp_err(dsp, "No algorithms\n"); + return -EINVAL; + } + + if (algs > 1024) { + adsp_err(dsp, "Algorithm count %zx excessive\n", algs); + print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET, + buf, buf_size); + return -EINVAL; + } + + /* Read the terminator first to validate the length */ + ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val)); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm list end: %d\n", + ret); + return ret; + } + + if (be32_to_cpu(val) != 0xbedead) + adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n", + term, be32_to_cpu(val)); + + alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA); + if (!alg) + return -ENOMEM; + + ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2); + if (ret != 0) { + adsp_err(dsp, "Failed to read algorithm list: %d\n", + ret); + goto out; + } + + adsp1_alg = alg; + adsp2_alg = alg; + + for (i = 0; i < algs; i++) { + switch (dsp->type) { + case WMFW_ADSP1: + adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n", + i, be32_to_cpu(adsp1_alg[i].alg.id), + (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8, + be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff, + be32_to_cpu(adsp1_alg[i].dm), + be32_to_cpu(adsp1_alg[i].zm)); + + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP1_DM; + region->alg = be32_to_cpu(adsp1_alg[i].alg.id); + region->base = be32_to_cpu(adsp1_alg[i].dm); + list_add_tail(®ion->list, &dsp->alg_regions); + + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP1_ZM; + region->alg = be32_to_cpu(adsp1_alg[i].alg.id); + region->base = be32_to_cpu(adsp1_alg[i].zm); + list_add_tail(®ion->list, &dsp->alg_regions); + break; + + case WMFW_ADSP2: + adsp_info(dsp, + "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n", + i, be32_to_cpu(adsp2_alg[i].alg.id), + (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16, + (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8, + be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff, + be32_to_cpu(adsp2_alg[i].xm), + be32_to_cpu(adsp2_alg[i].ym), + be32_to_cpu(adsp2_alg[i].zm)); + + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_XM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].xm); + list_add_tail(®ion->list, &dsp->alg_regions); + + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_YM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].ym); + list_add_tail(®ion->list, &dsp->alg_regions); + + region = kzalloc(sizeof(*region), GFP_KERNEL); + if (!region) + return -ENOMEM; + region->type = WMFW_ADSP2_ZM; + region->alg = be32_to_cpu(adsp2_alg[i].alg.id); + region->base = be32_to_cpu(adsp2_alg[i].zm); + list_add_tail(®ion->list, &dsp->alg_regions); + break; + } + } + +out: + kfree(alg); + return ret; +} + static int wm_adsp_load_coeff(struct wm_adsp *dsp) { + LIST_HEAD(buf_list); struct regmap *regmap = dsp->regmap; struct wmfw_coeff_hdr *hdr; struct wmfw_coeff_item *blk; const struct firmware *firmware; + const struct wm_adsp_region *mem; + struct wm_adsp_alg_region *alg_region; const char *region_name; int ret, pos, blocks, type, offset, reg; char *file; - void *buf; + struct wm_adsp_buf *buf; + int tmp; file = kzalloc(PAGE_SIZE, GFP_KERNEL); if (file == NULL) return -ENOMEM; - snprintf(file, PAGE_SIZE, "%s-dsp%d.bin", dsp->part, dsp->num); + snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num, + wm_adsp_fw[dsp->fw].file); file[PAGE_SIZE - 1] = '\0'; ret = request_firmware(&firmware, file, dsp->dev); @@ -399,6 +741,16 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) goto out_fw; } + switch (be32_to_cpu(hdr->rev) & 0xff) { + case 1: + break; + default: + adsp_err(dsp, "%s: Unsupported coefficient file format %d\n", + file, be32_to_cpu(hdr->rev) & 0xff); + ret = -EINVAL; + goto out_fw; + } + adsp_dbg(dsp, "%s: v%d.%d.%d\n", file, (le32_to_cpu(hdr->ver) >> 16) & 0xff, (le32_to_cpu(hdr->ver) >> 8) & 0xff, @@ -411,8 +763,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) pos - firmware->size > sizeof(*blk)) { blk = (void*)(&firmware->data[pos]); - type = be32_to_cpu(blk->type) & 0xff; - offset = le32_to_cpu(blk->offset) & 0xffffff; + type = le16_to_cpu(blk->type); + offset = le16_to_cpu(blk->offset); adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n", file, blocks, le32_to_cpu(blk->id), @@ -425,52 +777,105 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp) reg = 0; region_name = "Unknown"; switch (type) { - case WMFW_NAME_TEXT: - case WMFW_INFO_TEXT: + case (WMFW_NAME_TEXT << 8): + case (WMFW_INFO_TEXT << 8): break; - case WMFW_ABSOLUTE: + case (WMFW_ABSOLUTE << 8): region_name = "register"; reg = offset; break; + + case WMFW_ADSP1_DM: + case WMFW_ADSP1_ZM: + case WMFW_ADSP2_XM: + case WMFW_ADSP2_YM: + adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n", + file, blocks, le32_to_cpu(blk->len), + type, le32_to_cpu(blk->id)); + + mem = wm_adsp_find_region(dsp, type); + if (!mem) { + adsp_err(dsp, "No base for region %x\n", type); + break; + } + + reg = 0; + list_for_each_entry(alg_region, + &dsp->alg_regions, list) { + if (le32_to_cpu(blk->id) == alg_region->alg && + type == alg_region->type) { + reg = alg_region->base; + reg = wm_adsp_region_to_reg(mem, + reg); + reg += offset; + } + } + + if (reg == 0) + adsp_err(dsp, "No %x for algorithm %x\n", + type, le32_to_cpu(blk->id)); + break; + default: - adsp_err(dsp, "Unknown region type %x\n", type); + adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n", + file, blocks, type, pos); break; } if (reg) { - buf = kmemdup(blk->data, le32_to_cpu(blk->len), - GFP_KERNEL | GFP_DMA); + buf = wm_adsp_buf_alloc(blk->data, + le32_to_cpu(blk->len), + &buf_list); if (!buf) { adsp_err(dsp, "Out of memory\n"); return -ENOMEM; } - ret = regmap_raw_write(regmap, reg, blk->data, - le32_to_cpu(blk->len)); + adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n", + file, blocks, le32_to_cpu(blk->len), + reg); + ret = regmap_raw_write_async(regmap, reg, buf->buf, + le32_to_cpu(blk->len)); if (ret != 0) { adsp_err(dsp, "%s.%d: Failed to write to %x in %s\n", file, blocks, reg, region_name); } - - kfree(buf); } - pos += le32_to_cpu(blk->len) + sizeof(*blk); + tmp = le32_to_cpu(blk->len) % 4; + if (tmp) + pos += le32_to_cpu(blk->len) + (4 - tmp) + sizeof(*blk); + else + pos += le32_to_cpu(blk->len) + sizeof(*blk); + blocks++; } + ret = regmap_async_complete(regmap); + if (ret != 0) + adsp_err(dsp, "Failed to complete async write: %d\n", ret); + if (pos > firmware->size) adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n", file, blocks, pos - firmware->size); out_fw: release_firmware(firmware); + wm_adsp_buf_free(&buf_list); out: kfree(file); return 0; } +int wm_adsp1_init(struct wm_adsp *adsp) +{ + INIT_LIST_HEAD(&adsp->alg_regions); + + return 0; +} +EXPORT_SYMBOL_GPL(wm_adsp1_init); + int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -479,16 +884,46 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; int ret; + int val; switch (event) { case SND_SOC_DAPM_POST_PMU: regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30, ADSP1_SYS_ENA, ADSP1_SYS_ENA); + /* + * For simplicity set the DSP clock rate to be the + * SYSCLK rate rather than making it configurable. + */ + if(dsp->sysclk_reg) { + ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val); + if (ret != 0) { + adsp_err(dsp, "Failed to read SYSCLK state: %d\n", + ret); + return ret; + } + + val = (val & dsp->sysclk_mask) + >> dsp->sysclk_shift; + + ret = regmap_update_bits(dsp->regmap, + dsp->base + ADSP1_CONTROL_31, + ADSP1_CLK_SEL_MASK, val); + if (ret != 0) { + adsp_err(dsp, "Failed to set clock rate: %d\n", + ret); + return ret; + } + } + ret = wm_adsp_load(dsp); if (ret != 0) goto err; + ret = wm_adsp_setup_algs(dsp); + if (ret != 0) + goto err; + ret = wm_adsp_load_coeff(dsp); if (ret != 0) goto err; @@ -560,6 +995,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, struct snd_soc_codec *codec = w->codec; struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec); struct wm_adsp *dsp = &dsps[w->shift]; + struct wm_adsp_alg_region *alg_region; unsigned int val; int ret; @@ -625,6 +1061,10 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, if (ret != 0) goto err; + ret = wm_adsp_setup_algs(dsp); + if (ret != 0) + goto err; + ret = wm_adsp_load_coeff(dsp); if (ret != 0) goto err; @@ -635,13 +1075,22 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, ADSP2_CORE_ENA | ADSP2_START); if (ret != 0) goto err; + + dsp->running = true; break; case SND_SOC_DAPM_PRE_PMD: + dsp->running = false; + regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL, ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0); + /* Make sure DMAs are quiesced */ + regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0); + regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0); + regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0); + if (dsp->dvfs) { ret = regulator_set_voltage(dsp->dvfs, 1200000, 1800000); @@ -656,6 +1105,14 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w, "Failed to enable supply: %d\n", ret); } + + while (!list_empty(&dsp->alg_regions)) { + alg_region = list_first_entry(&dsp->alg_regions, + struct wm_adsp_alg_region, + list); + list_del(&alg_region->list); + kfree(alg_region); + } break; default: @@ -685,6 +1142,8 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs) return ret; } + INIT_LIST_HEAD(&adsp->alg_regions); + if (dvfs) { adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD"); if (IS_ERR(adsp->dvfs)) { diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h index ffd29a4609e2..cb8871a3ec00 100644 --- a/sound/soc/codecs/wm_adsp.h +++ b/sound/soc/codecs/wm_adsp.h @@ -25,6 +25,13 @@ struct wm_adsp_region { unsigned int base; }; +struct wm_adsp_alg_region { + struct list_head list; + unsigned int alg; + int type; + unsigned int base; +}; + struct wm_adsp { const char *part; int num; @@ -33,10 +40,18 @@ struct wm_adsp { struct regmap *regmap; int base; + int sysclk_reg; + int sysclk_mask; + int sysclk_shift; + + struct list_head alg_regions; const struct wm_adsp_region *mem; int num_mems; + int fw; + bool running; + struct regulator *dvfs; }; @@ -50,6 +65,9 @@ struct wm_adsp { .shift = num, .event = wm_adsp2_event, \ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD } +extern const struct snd_kcontrol_new wm_adsp_fw_controls[]; + +int wm_adsp1_init(struct wm_adsp *adsp); int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs); int wm_adsp1_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h index 5632ded67fdd..ef163360a745 100644 --- a/sound/soc/codecs/wmfw.h +++ b/sound/soc/codecs/wmfw.h @@ -93,15 +93,20 @@ struct wmfw_adsp2_alg_hdr { struct wmfw_coeff_hdr { u8 magic[4]; __le32 len; - __le32 ver; + union { + __be32 rev; + __le32 ver; + }; + union { + __be32 core; + __le32 core_ver; + }; u8 data[]; } __packed; struct wmfw_coeff_item { - union { - __be32 type; - __le32 offset; - }; + __le16 offset; + __le16 type; __le32 id; __le32 ver; __le32 sr; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 55e2bf652bef..9321e5c9d8c1 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -626,7 +626,7 @@ static int davinci_config_channel_size(struct davinci_audio_dev *dev, int word_length) { u32 fmt; - u32 rotate = (32 - word_length) / 4; + u32 rotate = (word_length / 4) & 0x7; u32 mask = (1ULL << word_length) - 1; /* diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c index 1aa51300c564..deb30d59965e 100644 --- a/sound/soc/dwc/designware_i2s.c +++ b/sound/soc/dwc/designware_i2s.c @@ -210,15 +210,19 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream, switch (config->chan_nr) { case EIGHT_CHANNEL_SUPPORT: ch_reg = 3; + break; case SIX_CHANNEL_SUPPORT: ch_reg = 2; + break; case FOUR_CHANNEL_SUPPORT: ch_reg = 1; + break; case TWO_CHANNEL_SUPPORT: ch_reg = 0; break; default: dev_err(dev->dev, "channel not supported\n"); + return -EINVAL; } i2s_disable_channels(dev, substream->stream); diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index a210c8d7b4bc..3b98159d9645 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -108,13 +108,18 @@ if SND_IMX_SOC config SND_SOC_IMX_SSI tristate -config SND_SOC_IMX_PCM_FIQ +config SND_SOC_IMX_PCM tristate + +config SND_SOC_IMX_PCM_FIQ + bool select FIQ + select SND_SOC_IMX_PCM config SND_SOC_IMX_PCM_DMA - tristate + bool select SND_SOC_DMAENGINE_PCM + select SND_SOC_IMX_PCM config SND_SOC_IMX_AUDMUX tristate diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index ec1457915d7c..afd34794db53 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -41,10 +41,7 @@ endif obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o -obj-$(CONFIG_SND_SOC_IMX_PCM_FIQ) += snd-soc-imx-pcm-fiq.o -snd-soc-imx-pcm-fiq-y := imx-pcm-fiq.o imx-pcm.o -obj-$(CONFIG_SND_SOC_IMX_PCM_DMA) += snd-soc-imx-pcm-dma.o -snd-soc-imx-pcm-dma-y := imx-pcm-dma.o imx-pcm.o +obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o # i.MX Machine Support snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c index bf363d8d044a..500f8ce55d78 100644 --- a/sound/soc/fsl/imx-pcm-dma.c +++ b/sound/soc/fsl/imx-pcm-dma.c @@ -154,26 +154,7 @@ static struct snd_soc_platform_driver imx_soc_platform_mx2 = { .pcm_free = imx_pcm_free, }; -static int imx_soc_platform_probe(struct platform_device *pdev) +int imx_pcm_dma_init(struct platform_device *pdev) { return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2); } - -static int imx_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; -} - -static struct platform_driver imx_pcm_driver = { - .driver = { - .name = "imx-pcm-audio", - .owner = THIS_MODULE, - }, - .probe = imx_soc_platform_probe, - .remove = imx_soc_platform_remove, -}; - -module_platform_driver(imx_pcm_driver); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:imx-pcm-audio"); diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c index 5ec362ae4d01..920f945cb2f4 100644 --- a/sound/soc/fsl/imx-pcm-fiq.c +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -281,7 +281,7 @@ static struct snd_soc_platform_driver imx_soc_platform_fiq = { .pcm_free = imx_pcm_fiq_free, }; -static int imx_soc_platform_probe(struct platform_device *pdev) +int imx_pcm_fiq_init(struct platform_device *pdev) { struct imx_ssi *ssi = platform_get_drvdata(pdev); int ret; @@ -314,23 +314,3 @@ failed_register: return ret; } - -static int imx_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; -} - -static struct platform_driver imx_pcm_driver = { - .driver = { - .name = "imx-fiq-pcm-audio", - .owner = THIS_MODULE, - }, - - .probe = imx_soc_platform_probe, - .remove = imx_soc_platform_remove, -}; - -module_platform_driver(imx_pcm_driver); - -MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/imx-pcm.c b/sound/soc/fsl/imx-pcm.c index 0c9f188ddc68..0d0625bfcb65 100644 --- a/sound/soc/fsl/imx-pcm.c +++ b/sound/soc/fsl/imx-pcm.c @@ -31,6 +31,7 @@ int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, runtime->dma_bytes); return ret; } +EXPORT_SYMBOL_GPL(snd_imx_pcm_mmap); static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) { @@ -79,6 +80,7 @@ int imx_pcm_new(struct snd_soc_pcm_runtime *rtd) out: return ret; } +EXPORT_SYMBOL_GPL(imx_pcm_new); void imx_pcm_free(struct snd_pcm *pcm) { @@ -100,6 +102,39 @@ void imx_pcm_free(struct snd_pcm *pcm) buf->area = NULL; } } +EXPORT_SYMBOL_GPL(imx_pcm_free); + +static int imx_pcm_probe(struct platform_device *pdev) +{ + if (strcmp(pdev->id_entry->name, "imx-fiq-pcm-audio") == 0) + return imx_pcm_fiq_init(pdev); + + return imx_pcm_dma_init(pdev); +} + +static int imx_pcm_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static struct platform_device_id imx_pcm_devtype[] = { + { .name = "imx-pcm-audio", }, + { .name = "imx-fiq-pcm-audio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, imx_pcm_devtype); + +static struct platform_driver imx_pcm_driver = { + .driver = { + .name = "imx-pcm", + .owner = THIS_MODULE, + }, + .id_table = imx_pcm_devtype, + .probe = imx_pcm_probe, + .remove = imx_pcm_remove, +}; +module_platform_driver(imx_pcm_driver); MODULE_DESCRIPTION("Freescale i.MX PCM driver"); MODULE_AUTHOR("Sascha Hauer <[email protected]>"); diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h index 83c0ed7d55c9..5ae13a13a353 100644 --- a/sound/soc/fsl/imx-pcm.h +++ b/sound/soc/fsl/imx-pcm.h @@ -30,4 +30,22 @@ int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, int imx_pcm_new(struct snd_soc_pcm_runtime *rtd); void imx_pcm_free(struct snd_pcm *pcm); +#ifdef CONFIG_SND_SOC_IMX_PCM_DMA +int imx_pcm_dma_init(struct platform_device *pdev); +#else +static inline int imx_pcm_dma_init(struct platform_device *pdev) +{ + return -ENODEV; +} +#endif + +#ifdef CONFIG_SND_SOC_IMX_PCM_FIQ +int imx_pcm_fiq_init(struct platform_device *pdev); +#else +static inline int imx_pcm_fiq_init(struct platform_device *pdev) +{ + return -ENODEV; +} +#endif + #endif /* _IMX_PCM_H */ diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index b4b4cab30232..6cf8355a8542 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -16,33 +16,38 @@ #define asoc_simple_get_card_info(p) \ container_of(p->dai_link, struct asoc_simple_card_info, snd_link) +static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai, + struct asoc_simple_dai *set, + unsigned int daifmt) +{ + int ret = 0; + + daifmt |= set->fmt; + + if (!ret && daifmt) + ret = snd_soc_dai_set_fmt(dai, daifmt); + + if (!ret && set->sysclk) + ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); + + return ret; +} + static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct asoc_simple_card_info *cinfo = asoc_simple_get_card_info(rtd); - struct asoc_simple_dai_init_info *iinfo = cinfo->init; + struct asoc_simple_card_info *info = asoc_simple_get_card_info(rtd); struct snd_soc_dai *codec = rtd->codec_dai; struct snd_soc_dai *cpu = rtd->cpu_dai; - unsigned int cpu_daifmt = iinfo->fmt | iinfo->cpu_daifmt; - unsigned int codec_daifmt = iinfo->fmt | iinfo->codec_daifmt; + unsigned int daifmt = info->daifmt; int ret; - if (codec_daifmt) { - ret = snd_soc_dai_set_fmt(codec, codec_daifmt); - if (ret < 0) - return ret; - } - - if (iinfo->sysclk) { - ret = snd_soc_dai_set_sysclk(codec, 0, iinfo->sysclk, 0); - if (ret < 0) - return ret; - } + ret = __asoc_simple_card_dai_init(codec, &info->codec_dai, daifmt); + if (ret < 0) + return ret; - if (cpu_daifmt) { - ret = snd_soc_dai_set_fmt(cpu, cpu_daifmt); - if (ret < 0) - return ret; - } + ret = __asoc_simple_card_dai_init(cpu, &info->cpu_dai, daifmt); + if (ret < 0) + return ret; return 0; } @@ -50,19 +55,20 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) static int asoc_simple_card_probe(struct platform_device *pdev) { struct asoc_simple_card_info *cinfo = pdev->dev.platform_data; + struct device *dev = &pdev->dev; if (!cinfo) { - dev_err(&pdev->dev, "no info for asoc-simple-card\n"); + dev_err(dev, "no info for asoc-simple-card\n"); return -EINVAL; } if (!cinfo->name || !cinfo->card || - !cinfo->cpu_dai || !cinfo->codec || !cinfo->platform || - !cinfo->codec_dai) { - dev_err(&pdev->dev, "insufficient asoc_simple_card_info settings\n"); + !cinfo->cpu_dai.name || + !cinfo->codec_dai.name) { + dev_err(dev, "insufficient asoc_simple_card_info settings\n"); return -EINVAL; } @@ -71,14 +77,11 @@ static int asoc_simple_card_probe(struct platform_device *pdev) */ cinfo->snd_link.name = cinfo->name; cinfo->snd_link.stream_name = cinfo->name; - cinfo->snd_link.cpu_dai_name = cinfo->cpu_dai; + cinfo->snd_link.cpu_dai_name = cinfo->cpu_dai.name; cinfo->snd_link.platform_name = cinfo->platform; cinfo->snd_link.codec_name = cinfo->codec; - cinfo->snd_link.codec_dai_name = cinfo->codec_dai; - - /* enable snd_link.init if cinfo has settings */ - if (cinfo->init) - cinfo->snd_link.init = asoc_simple_card_dai_init; + cinfo->snd_link.codec_dai_name = cinfo->codec_dai.name; + cinfo->snd_link.init = asoc_simple_card_dai_init; /* * init snd_soc_card diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index 365d9d27a321..e70e6c844f96 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c @@ -32,7 +32,6 @@ #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/saif.h> #include <asm/mach-types.h> #include <mach/hardware.h> #include <mach/mxs.h> @@ -662,46 +661,40 @@ static int mxs_saif_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct resource *iores, *dmares; struct mxs_saif *saif; - struct mxs_saif_platform_data *pdata; struct pinctrl *pinctrl; int ret = 0; + struct device_node *master; - - if (!np && pdev->id >= ARRAY_SIZE(mxs_saif)) + if (!np) return -EINVAL; saif = devm_kzalloc(&pdev->dev, sizeof(*saif), GFP_KERNEL); if (!saif) return -ENOMEM; - if (np) { - struct device_node *master; - saif->id = of_alias_get_id(np, "saif"); - if (saif->id < 0) - return saif->id; - /* - * If there is no "fsl,saif-master" phandle, it's a saif - * master. Otherwise, it's a slave and its phandle points - * to the master. - */ - master = of_parse_phandle(np, "fsl,saif-master", 0); - if (!master) { - saif->master_id = saif->id; - } else { - saif->master_id = of_alias_get_id(master, "saif"); - if (saif->master_id < 0) - return saif->master_id; - } + ret = of_alias_get_id(np, "saif"); + if (ret < 0) + return ret; + else + saif->id = ret; + + /* + * If there is no "fsl,saif-master" phandle, it's a saif + * master. Otherwise, it's a slave and its phandle points + * to the master. + */ + master = of_parse_phandle(np, "fsl,saif-master", 0); + if (!master) { + saif->master_id = saif->id; } else { - saif->id = pdev->id; - pdata = pdev->dev.platform_data; - if (pdata && !pdata->master_mode) - saif->master_id = pdata->master_id; + ret = of_alias_get_id(master, "saif"); + if (ret < 0) + return ret; else - saif->master_id = saif->id; + saif->master_id = ret; } - if (saif->master_id < 0 || saif->master_id >= ARRAY_SIZE(mxs_saif)) { + if (saif->master_id >= ARRAY_SIZE(mxs_saif)) { dev_err(&pdev->dev, "get wrong master id\n"); return -EINVAL; } diff --git a/sound/soc/omap/omap-hdmi.c b/sound/soc/omap/omap-hdmi.c index 7ea24819d570..32fa840c493e 100644 --- a/sound/soc/omap/omap-hdmi.c +++ b/sound/soc/omap/omap-hdmi.c @@ -110,6 +110,8 @@ static int omap_hdmi_dai_hw_params(struct snd_pcm_substream *substream, /* * fill the IEC-60958 channel status word */ + /* initialize the word bytes */ + memset(iec->status, 0, sizeof(iec->status)); /* specify IEC-60958-3 (commercial use) */ iec->status[0] &= ~IEC958_AES0_PROFESSIONAL; diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c index 47bdbd415ad8..c722c2ef9665 100644 --- a/sound/soc/omap/omap-pcm.c +++ b/sound/soc/omap/omap-pcm.c @@ -174,23 +174,15 @@ static snd_pcm_uframes_t omap_pcm_pointer(struct snd_pcm_substream *substream) static int omap_pcm_open(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct omap_pcm_dma_data *dma_data; - int ret; snd_soc_set_runtime_hwparams(substream, &omap_pcm_hardware); - /* Ensure that buffer size is a multiple of period size */ - ret = snd_pcm_hw_constraint_integer(runtime, - SNDRV_PCM_HW_PARAM_PERIODS); - if (ret < 0) - return ret; - dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - ret = snd_dmaengine_pcm_open(substream, omap_dma_filter_fn, - &dma_data->dma_req); - return ret; + + return snd_dmaengine_pcm_open(substream, omap_dma_filter_fn, + &dma_data->dma_req); } static int omap_pcm_close(struct snd_pcm_substream *substream) diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index 2074e2daf9c6..e1ffcdd9a649 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -79,17 +79,6 @@ static int palm27x_ac97_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dapm_context *dapm = &codec->dapm; int err; - /* add palm27x specific widgets */ - err = snd_soc_dapm_new_controls(dapm, palm27x_dapm_widgets, - ARRAY_SIZE(palm27x_dapm_widgets)); - if (err) - return err; - - /* set up palm27x specific audio path audio_map */ - err = snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); - if (err) - return err; - /* connected pins */ if (machine_is_palmld()) snd_soc_dapm_enable_pin(dapm, "MIC1"); @@ -149,10 +138,12 @@ static struct snd_soc_card palm27x_asoc = { .owner = THIS_MODULE, .dai_link = palm27x_dai, .num_links = ARRAY_SIZE(palm27x_dai), + .dapm_widgets = palm27x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(palm27x_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map) }; -static struct platform_device *palm27x_snd_device; - static int palm27x_asoc_probe(struct platform_device *pdev) { int ret; @@ -169,27 +160,18 @@ static int palm27x_asoc_probe(struct platform_device *pdev) hs_jack_gpios[0].gpio = ((struct palm27x_asoc_info *) (pdev->dev.platform_data))->jack_gpio; - palm27x_snd_device = platform_device_alloc("soc-audio", -1); - if (!palm27x_snd_device) - return -ENOMEM; - - platform_set_drvdata(palm27x_snd_device, &palm27x_asoc); - ret = platform_device_add(palm27x_snd_device); - - if (ret != 0) - goto put_device; - - return 0; - -put_device: - platform_device_put(palm27x_snd_device); + palm27x_asoc.dev = &pdev->dev; + ret = snd_soc_register_card(&palm27x_asoc); + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n", + ret); return ret; } static int palm27x_asoc_remove(struct platform_device *pdev) { - platform_device_unregister(palm27x_snd_device); + snd_soc_unregister_card(&palm27x_asoc); return 0; } diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig index 3c7c3a59ed39..90e7e6653233 100644 --- a/sound/soc/samsung/Kconfig +++ b/sound/soc/samsung/Kconfig @@ -63,7 +63,7 @@ config SND_SOC_SAMSUNG_SMDK_WM8580 config SND_SOC_SAMSUNG_SMDK_WM8994 tristate "SoC I2S Audio support for WM8994 on SMDK" - depends on SND_SOC_SAMSUNG && (MACH_SMDKV310 || MACH_SMDKC210 || MACH_SMDK4212) + depends on SND_SOC_SAMSUNG depends on I2C=y && GENERIC_HARDIRQS select MFD_WM8994 select SND_SOC_WM8994 @@ -162,7 +162,7 @@ config SND_SOC_GONI_AQUILA_WM8994 config SND_SOC_SAMSUNG_SMDK_SPDIF tristate "SoC S/PDIF Audio support for SMDK" - depends on SND_SOC_SAMSUNG && (MACH_SMDKC100 || MACH_SMDKC110 || MACH_SMDKV210 || MACH_SMDKV310 || MACH_SMDK4212) + depends on SND_SOC_SAMSUNG select SND_SAMSUNG_SPDIF help Say Y if you want to add support for SoC S/PDIF audio on the SMDK. @@ -177,7 +177,7 @@ config SND_SOC_SMDK_WM8580_PCM config SND_SOC_SMDK_WM8994_PCM tristate "SoC PCM Audio support for WM8994 on SMDK" - depends on SND_SOC_SAMSUNG && (MACH_SMDKC210 || MACH_SMDKV310 || MACH_SMDK4212) + depends on SND_SOC_SAMSUNG depends on I2C=y && GENERIC_HARDIRQS select MFD_WM8994 select SND_SOC_WM8994 diff --git a/sound/soc/samsung/dma.c b/sound/soc/samsung/dma.c index db87628d7630..21b79262010e 100644 --- a/sound/soc/samsung/dma.c +++ b/sound/soc/samsung/dma.c @@ -174,7 +174,8 @@ static int dma_hw_params(struct snd_pcm_substream *substream, config.width = prtd->params->dma_size; config.fifo = prtd->params->dma_addr; prtd->params->ch = prtd->params->ops->request( - prtd->params->channel, &req); + prtd->params->channel, &req, rtd->cpu_dai->dev, + prtd->params->ch_name); prtd->params->ops->config(prtd->params->ch, &config); } diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h index 73d8c7c8a1e8..189a7a6d5020 100644 --- a/sound/soc/samsung/dma.h +++ b/sound/soc/samsung/dma.h @@ -19,6 +19,7 @@ struct s3c_dma_params { int dma_size; /* Size of the DMA transfer */ unsigned ch; struct samsung_dma_ops *ops; + char *ch_name; }; int asoc_dma_platform_register(struct device *dev); diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c index d2d124f1dd1b..d7231e336a7c 100644 --- a/sound/soc/samsung/i2s.c +++ b/sound/soc/samsung/i2s.c @@ -15,11 +15,15 @@ #include <linux/clk.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> #include <linux/pm_runtime.h> #include <sound/soc.h> #include <sound/pcm_params.h> +#include <mach/dma.h> + #include <linux/platform_data/asoc-s3c.h> #include "dma.h" @@ -29,6 +33,15 @@ #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) +enum samsung_dai_type { + TYPE_PRI, + TYPE_SEC, +}; + +struct samsung_i2s_dai_data { + int dai_type; +}; + struct i2s_dai { /* Platform device for this DAI */ struct platform_device *pdev; @@ -66,6 +79,7 @@ struct i2s_dai { u32 suspend_i2smod; u32 suspend_i2scon; u32 suspend_i2spsr; + unsigned long gpios[7]; /* i2s gpio line numbers */ }; /* Lock for cross i/f checks */ @@ -651,6 +665,9 @@ static int i2s_startup(struct snd_pcm_substream *substream, /* Enforce set_sysclk in Master mode */ i2s->rclk_srcrate = 0; + if (!any_active(i2s) && (i2s->quirks & QUIRK_NEED_RSTCLR)) + writel(CON_RSTCLR, i2s->addr + I2SCON); + spin_unlock_irqrestore(&lock, flags); return 0; @@ -981,8 +998,7 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) i2s->i2s_dai_drv.capture.formats = SAMSUNG_I2S_FMTS; } else { /* Create a new platform_device for Secondary */ i2s->pdev = platform_device_register_resndata(NULL, - pdev->name, pdev->id + SAMSUNG_I2S_SECOFF, - NULL, 0, NULL, 0); + "samsung-i2s-sec", -1, NULL, 0, NULL, 0); if (IS_ERR(i2s->pdev)) return NULL; } @@ -993,18 +1009,103 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec) return i2s; } +#ifdef CONFIG_OF +static int samsung_i2s_parse_dt_gpio(struct i2s_dai *i2s) +{ + struct device *dev = &i2s->pdev->dev; + int index, gpio, ret; + + for (index = 0; index < 7; index++) { + gpio = of_get_gpio(dev->of_node, index); + if (!gpio_is_valid(gpio)) { + dev_err(dev, "invalid gpio[%d]: %d\n", index, gpio); + goto free_gpio; + } + + ret = gpio_request(gpio, dev_name(dev)); + if (ret) { + dev_err(dev, "gpio [%d] request failed\n", gpio); + goto free_gpio; + } + i2s->gpios[index] = gpio; + } + return 0; + +free_gpio: + while (--index >= 0) + gpio_free(i2s->gpios[index]); + return -EINVAL; +} + +static void samsung_i2s_dt_gpio_free(struct i2s_dai *i2s) +{ + unsigned int index; + for (index = 0; index < 7; index++) + gpio_free(i2s->gpios[index]); +} +#else +static int samsung_i2s_parse_dt_gpio(struct i2s_dai *dai) +{ + return -EINVAL; +} + +static void samsung_i2s_dt_gpio_free(struct i2s_dai *dai) +{ +} + +#endif + +static const struct of_device_id exynos_i2s_match[]; + +static inline int samsung_i2s_get_driver_data(struct platform_device *pdev) +{ +#ifdef CONFIG_OF + struct samsung_i2s_dai_data *data; + if (pdev->dev.of_node) { + const struct of_device_id *match; + match = of_match_node(exynos_i2s_match, pdev->dev.of_node); + data = (struct samsung_i2s_dai_data *) match->data; + return data->dai_type; + } else +#endif + return platform_get_device_id(pdev)->driver_data; +} + +#ifdef CONFIG_PM_RUNTIME +static int i2s_runtime_suspend(struct device *dev) +{ + struct i2s_dai *i2s = dev_get_drvdata(dev); + + clk_disable_unprepare(i2s->clk); + + return 0; +} + +static int i2s_runtime_resume(struct device *dev) +{ + struct i2s_dai *i2s = dev_get_drvdata(dev); + + clk_prepare_enable(i2s->clk); + + return 0; +} +#endif /* CONFIG_PM_RUNTIME */ + static int samsung_i2s_probe(struct platform_device *pdev) { - u32 dma_pl_chan, dma_cp_chan, dma_pl_sec_chan; struct i2s_dai *pri_dai, *sec_dai = NULL; - struct s3c_audio_pdata *i2s_pdata; - struct samsung_i2s *i2s_cfg; + struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data; + struct samsung_i2s *i2s_cfg = NULL; struct resource *res; - u32 regs_base, quirks; + u32 regs_base, quirks = 0, idma_addr = 0; + struct device_node *np = pdev->dev.of_node; + enum samsung_dai_type samsung_dai_type; int ret = 0; /* Call during Seconday interface registration */ - if (pdev->id >= SAMSUNG_I2S_SECOFF) { + samsung_dai_type = samsung_i2s_get_driver_data(pdev); + + if (samsung_dai_type == TYPE_SEC) { sec_dai = dev_get_drvdata(&pdev->dev); snd_soc_register_dai(&sec_dai->pdev->dev, &sec_dai->i2s_dai_drv); @@ -1012,31 +1113,60 @@ static int samsung_i2s_probe(struct platform_device *pdev) return 0; } - i2s_pdata = pdev->dev.platform_data; - if (i2s_pdata == NULL) { - dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n"); - return -EINVAL; + pri_dai = i2s_alloc_dai(pdev, false); + if (!pri_dai) { + dev_err(&pdev->dev, "Unable to alloc I2S_pri\n"); + return -ENOMEM; } - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (!res) { - dev_err(&pdev->dev, "Unable to get I2S-TX dma resource\n"); - return -ENXIO; - } - dma_pl_chan = res->start; + if (!np) { + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!res) { + dev_err(&pdev->dev, + "Unable to get I2S-TX dma resource\n"); + return -ENXIO; + } + pri_dai->dma_playback.channel = res->start; - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (!res) { - dev_err(&pdev->dev, "Unable to get I2S-RX dma resource\n"); - return -ENXIO; - } - dma_cp_chan = res->start; + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!res) { + dev_err(&pdev->dev, + "Unable to get I2S-RX dma resource\n"); + return -ENXIO; + } + pri_dai->dma_capture.channel = res->start; - res = platform_get_resource(pdev, IORESOURCE_DMA, 2); - if (res) - dma_pl_sec_chan = res->start; - else - dma_pl_sec_chan = 0; + if (i2s_pdata == NULL) { + dev_err(&pdev->dev, "Can't work without s3c_audio_pdata\n"); + return -EINVAL; + } + + if (&i2s_pdata->type) + i2s_cfg = &i2s_pdata->type.i2s; + + if (i2s_cfg) { + quirks = i2s_cfg->quirks; + idma_addr = i2s_cfg->idma_addr; + } + } else { + if (of_find_property(np, "samsung,supports-6ch", NULL)) + quirks |= QUIRK_PRI_6CHAN; + + if (of_find_property(np, "samsung,supports-secdai", NULL)) + quirks |= QUIRK_SEC_DAI; + + if (of_find_property(np, "samsung,supports-rstclr", NULL)) + quirks |= QUIRK_NEED_RSTCLR; + + if (of_property_read_u32(np, "samsung,idma-addr", + &idma_addr)) { + if (quirks & QUIRK_SEC_DAI) { + dev_err(&pdev->dev, "idma address is not"\ + "specified"); + return -EINVAL; + } + } + } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -1051,24 +1181,14 @@ static int samsung_i2s_probe(struct platform_device *pdev) } regs_base = res->start; - i2s_cfg = &i2s_pdata->type.i2s; - quirks = i2s_cfg->quirks; - - pri_dai = i2s_alloc_dai(pdev, false); - if (!pri_dai) { - dev_err(&pdev->dev, "Unable to alloc I2S_pri\n"); - ret = -ENOMEM; - goto err; - } - pri_dai->dma_playback.dma_addr = regs_base + I2STXD; pri_dai->dma_capture.dma_addr = regs_base + I2SRXD; pri_dai->dma_playback.client = (struct s3c2410_dma_client *)&pri_dai->dma_playback; + pri_dai->dma_playback.ch_name = "tx"; pri_dai->dma_capture.client = (struct s3c2410_dma_client *)&pri_dai->dma_capture; - pri_dai->dma_playback.channel = dma_pl_chan; - pri_dai->dma_capture.channel = dma_cp_chan; + pri_dai->dma_capture.ch_name = "rx"; pri_dai->dma_playback.dma_size = 4; pri_dai->dma_capture.dma_size = 4; pri_dai->base = regs_base; @@ -1087,20 +1207,34 @@ static int samsung_i2s_probe(struct platform_device *pdev) sec_dai->dma_playback.dma_addr = regs_base + I2STXDS; sec_dai->dma_playback.client = (struct s3c2410_dma_client *)&sec_dai->dma_playback; - /* Use iDMA always if SysDMA not provided */ - sec_dai->dma_playback.channel = dma_pl_sec_chan ? : -1; + sec_dai->dma_playback.ch_name = "tx-sec"; + + if (!np) { + res = platform_get_resource(pdev, IORESOURCE_DMA, 2); + if (res) + sec_dai->dma_playback.channel = res->start; + } + sec_dai->dma_playback.dma_size = 4; sec_dai->base = regs_base; sec_dai->quirks = quirks; - sec_dai->idma_playback.dma_addr = i2s_cfg->idma_addr; + sec_dai->idma_playback.dma_addr = idma_addr; sec_dai->pri_dai = pri_dai; pri_dai->sec_dai = sec_dai; } - if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { - dev_err(&pdev->dev, "Unable to configure gpio\n"); - ret = -EINVAL; - goto err; + if (np) { + if (samsung_i2s_parse_dt_gpio(pri_dai)) { + dev_err(&pdev->dev, "Unable to configure gpio\n"); + ret = -EINVAL; + goto err; + } + } else { + if (i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { + dev_err(&pdev->dev, "Unable to configure gpio\n"); + ret = -EINVAL; + goto err; + } } snd_soc_register_dai(&pri_dai->pdev->dev, &pri_dai->i2s_dai_drv); @@ -1120,10 +1254,14 @@ static int samsung_i2s_remove(struct platform_device *pdev) { struct i2s_dai *i2s, *other; struct resource *res; + struct s3c_audio_pdata *i2s_pdata = pdev->dev.platform_data; i2s = dev_get_drvdata(&pdev->dev); other = i2s->pri_dai ? : i2s->sec_dai; + if (!i2s_pdata->cfg_gpio && pdev->dev.of_node) + samsung_i2s_dt_gpio_free(i2s->pri_dai); + if (other) { other->pri_dai = NULL; other->sec_dai = NULL; @@ -1143,12 +1281,47 @@ static int samsung_i2s_remove(struct platform_device *pdev) return 0; } +static struct platform_device_id samsung_i2s_driver_ids[] = { + { + .name = "samsung-i2s", + .driver_data = TYPE_PRI, + }, { + .name = "samsung-i2s-sec", + .driver_data = TYPE_SEC, + }, + {}, +}; +MODULE_DEVICE_TABLE(platform, samsung-i2s-driver-ids); + +#ifdef CONFIG_OF +static struct samsung_i2s_dai_data samsung_i2s_dai_data_array[] = { + [TYPE_PRI] = { TYPE_PRI }, + [TYPE_SEC] = { TYPE_SEC }, +}; + +static const struct of_device_id exynos_i2s_match[] = { + { .compatible = "samsung,i2s-v5", + .data = &samsung_i2s_dai_data_array[TYPE_PRI], + }, + {}, +}; +MODULE_DEVICE_TABLE(of, exynos_i2s_match); +#endif + +static const struct dev_pm_ops samsung_i2s_pm = { + SET_RUNTIME_PM_OPS(i2s_runtime_suspend, + i2s_runtime_resume, NULL) +}; + static struct platform_driver samsung_i2s_driver = { .probe = samsung_i2s_probe, .remove = samsung_i2s_remove, + .id_table = samsung_i2s_driver_ids, .driver = { .name = "samsung-i2s", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(exynos_i2s_match), + .pm = &samsung_i2s_pm, }, }; diff --git a/sound/soc/samsung/i2s.h b/sound/soc/samsung/i2s.h index d420a7ca56ca..7966afc934db 100644 --- a/sound/soc/samsung/i2s.h +++ b/sound/soc/samsung/i2s.h @@ -13,13 +13,6 @@ #ifndef __SND_SOC_SAMSUNG_I2S_H #define __SND_SOC_SAMSUNG_I2S_H -/* - * Maximum number of I2S blocks that any SoC can have. - * The secondary interface of a CPU dai(if there exists any), - * is indexed at [cpu-dai's ID + SAMSUNG_I2S_SECOFF] - */ -#define SAMSUNG_I2S_SECOFF 4 - #define SAMSUNG_I2S_DIV_BCLK 1 #define SAMSUNG_I2S_RCLKSRC_0 0 diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c index ee10e8704e97..13f6dd1ceb00 100644 --- a/sound/soc/samsung/s3c24xx-i2s.c +++ b/sound/soc/samsung/s3c24xx-i2s.c @@ -469,7 +469,7 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev) { int ret = 0; - ret = s3c_i2sv2_register_dai(&pdev->dev, -1, &s3c2412_i2s_dai); + ret = snd_soc_register_dai(&pdev->dev, &s3c24xx_i2s_dai); if (ret) { pr_err("failed to register the dai\n"); return ret; diff --git a/sound/soc/samsung/smdk_wm8580.c b/sound/soc/samsung/smdk_wm8580.c index 7e2b710763be..7a16b32ed673 100644 --- a/sound/soc/samsung/smdk_wm8580.c +++ b/sound/soc/samsung/smdk_wm8580.c @@ -193,9 +193,9 @@ static struct snd_soc_dai_link smdk_dai[] = { [SEC_PLAYBACK] = { /* Sec_Fifo Playback i/f */ .name = "Sec_FIFO TX", .stream_name = "Playback", - .cpu_dai_name = "samsung-i2s.x", + .cpu_dai_name = "samsung-i2s-sec", .codec_dai_name = "wm8580-hifi-playback", - .platform_name = "samsung-i2s.x", + .platform_name = "samsung-i2s-sec", .codec_name = "wm8580.0-001b", .ops = &smdk_ops, }, @@ -223,9 +223,6 @@ static int __init smdk_audio_init(void) if (machine_is_smdkc100() || machine_is_smdkv210() || machine_is_smdkc110()) { smdk.num_links = 3; - /* Secondary is at offset SAMSUNG_I2S_SECOFF from Primary */ - str = (char *)smdk_dai[SEC_PLAYBACK].cpu_dai_name; - str[strlen(str) - 1] = '0' + SAMSUNG_I2S_SECOFF; } else if (machine_is_smdk6410()) { str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name; str[strlen(str) - 1] = '2'; diff --git a/sound/soc/samsung/smdk_wm8994.c b/sound/soc/samsung/smdk_wm8994.c index b0d0ab8bff5a..581ea4a06fc6 100644 --- a/sound/soc/samsung/smdk_wm8994.c +++ b/sound/soc/samsung/smdk_wm8994.c @@ -10,6 +10,7 @@ #include "../codecs/wm8994.h" #include <sound/pcm_params.h> #include <linux/module.h> +#include <linux/of.h> /* * Default CFG switch settings to use this driver: @@ -134,9 +135,9 @@ static struct snd_soc_dai_link smdk_dai[] = { }, { /* Sec_Fifo Playback i/f */ .name = "Sec_FIFO TX", .stream_name = "Sec_Dai", - .cpu_dai_name = "samsung-i2s.4", + .cpu_dai_name = "samsung-i2s-sec", .codec_dai_name = "wm8994-aif1", - .platform_name = "samsung-i2s.4", + .platform_name = "samsung-i2s-sec", .codec_name = "wm8994-codec", .ops = &smdk_ops, }, @@ -153,9 +154,25 @@ static struct snd_soc_card smdk = { static int smdk_audio_probe(struct platform_device *pdev) { int ret; + struct device_node *np = pdev->dev.of_node; struct snd_soc_card *card = &smdk; card->dev = &pdev->dev; + + if (np) { + smdk_dai[0].cpu_dai_name = NULL; + smdk_dai[0].cpu_of_node = of_parse_phandle(np, + "samsung,i2s-controller", 0); + if (!smdk_dai[0].cpu_of_node) { + dev_err(&pdev->dev, + "Property 'samsung,i2s-controller' missing or invalid\n"); + ret = -EINVAL; + } + + smdk_dai[0].platform_name = NULL; + smdk_dai[0].platform_of_node = smdk_dai[0].cpu_of_node; + } + ret = snd_soc_register_card(card); if (ret) @@ -173,10 +190,19 @@ static int smdk_audio_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id samsung_wm8994_of_match[] = { + { .compatible = "samsung,smdk-wm8994", }, + {}, +}; +MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match); +#endif /* CONFIG_OF */ + static struct platform_driver smdk_audio_driver = { .driver = { .name = "smdk-audio", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(samsung_wm8994_of_match), }, .probe = smdk_audio_probe, .remove = smdk_audio_remove, diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c index a606d0f93d1c..c724026a246f 100644 --- a/sound/soc/sh/fsi.c +++ b/sound/soc/sh/fsi.c @@ -16,6 +16,8 @@ #include <linux/dma-mapping.h> #include <linux/pm_runtime.h> #include <linux/io.h> +#include <linux/of.h> +#include <linux/of_device.h> #include <linux/scatterlist.h> #include <linux/sh_dma.h> #include <linux/slab.h> @@ -131,8 +133,6 @@ #define FSI_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) -typedef int (*set_rate_func)(struct device *dev, int rate, int enable); - /* * bus options * @@ -244,8 +244,7 @@ struct fsi_clk { struct clk *ick; struct clk *div; int (*set_rate)(struct device *dev, - struct fsi_priv *fsi, - unsigned long rate); + struct fsi_priv *fsi); unsigned long rate; unsigned int count; @@ -254,7 +253,6 @@ struct fsi_clk { struct fsi_priv { void __iomem *base; struct fsi_master *master; - struct sh_fsi_port_info *info; struct fsi_stream playback; struct fsi_stream capture; @@ -270,8 +268,6 @@ struct fsi_priv { int enable_stream:1; int bit_clk_inv:1; int lr_clk_inv:1; - - long rate; }; struct fsi_stream_handler { @@ -303,7 +299,7 @@ struct fsi_master { int irq; struct fsi_priv fsia; struct fsi_priv fsib; - struct fsi_core *core; + const struct fsi_core *core; spinlock_t lock; }; @@ -431,22 +427,6 @@ static struct fsi_priv *fsi_get_priv(struct snd_pcm_substream *substream) return fsi_get_priv_frm_dai(fsi_get_dai(substream)); } -static set_rate_func fsi_get_info_set_rate(struct fsi_priv *fsi) -{ - if (!fsi->info) - return NULL; - - return fsi->info->set_rate; -} - -static u32 fsi_get_info_flags(struct fsi_priv *fsi) -{ - if (!fsi->info) - return 0; - - return fsi->info->flags; -} - static u32 fsi_get_port_shift(struct fsi_priv *fsi, struct fsi_stream *io) { int is_play = fsi_stream_is_play(fsi, io); @@ -757,8 +737,7 @@ static int fsi_clk_init(struct device *dev, int ick, int div, int (*set_rate)(struct device *dev, - struct fsi_priv *fsi, - unsigned long rate)) + struct fsi_priv *fsi)) { struct fsi_clk *clock = &fsi->clock; int is_porta = fsi_is_port_a(fsi); @@ -829,8 +808,7 @@ static int fsi_clk_is_valid(struct fsi_priv *fsi) } static int fsi_clk_enable(struct device *dev, - struct fsi_priv *fsi, - unsigned long rate) + struct fsi_priv *fsi) { struct fsi_clk *clock = &fsi->clock; int ret = -EINVAL; @@ -839,7 +817,7 @@ static int fsi_clk_enable(struct device *dev, return ret; if (0 == clock->count) { - ret = clock->set_rate(dev, fsi, rate); + ret = clock->set_rate(dev, fsi); if (ret < 0) { fsi_clk_invalid(fsi); return ret; @@ -946,11 +924,11 @@ static int fsi_clk_set_ackbpf(struct device *dev, } static int fsi_clk_set_rate_external(struct device *dev, - struct fsi_priv *fsi, - unsigned long rate) + struct fsi_priv *fsi) { struct clk *xck = fsi->clock.xck; struct clk *ick = fsi->clock.ick; + unsigned long rate = fsi->clock.rate; unsigned long xrate; int ackmd, bpfmd; int ret = 0; @@ -978,11 +956,11 @@ static int fsi_clk_set_rate_external(struct device *dev, } static int fsi_clk_set_rate_cpg(struct device *dev, - struct fsi_priv *fsi, - unsigned long rate) + struct fsi_priv *fsi) { struct clk *ick = fsi->clock.ick; struct clk *div = fsi->clock.div; + unsigned long rate = fsi->clock.rate; unsigned long target = 0; /* 12288000 or 11289600 */ unsigned long actual, cout; unsigned long diff, min; @@ -1063,85 +1041,6 @@ static int fsi_clk_set_rate_cpg(struct device *dev, return ret; } -static int fsi_set_master_clk(struct device *dev, struct fsi_priv *fsi, - long rate, int enable) -{ - set_rate_func set_rate = fsi_get_info_set_rate(fsi); - int ret; - - /* - * CAUTION - * - * set_rate will be deleted - */ - if (!set_rate) { - if (enable) - return fsi_clk_enable(dev, fsi, rate); - else - return fsi_clk_disable(dev, fsi); - } - - ret = set_rate(dev, rate, enable); - if (ret < 0) /* error */ - return ret; - - if (!enable) - return 0; - - if (ret > 0) { - u32 data = 0; - - switch (ret & SH_FSI_ACKMD_MASK) { - default: - /* FALL THROUGH */ - case SH_FSI_ACKMD_512: - data |= (0x0 << 12); - break; - case SH_FSI_ACKMD_256: - data |= (0x1 << 12); - break; - case SH_FSI_ACKMD_128: - data |= (0x2 << 12); - break; - case SH_FSI_ACKMD_64: - data |= (0x3 << 12); - break; - case SH_FSI_ACKMD_32: - data |= (0x4 << 12); - break; - } - - switch (ret & SH_FSI_BPFMD_MASK) { - default: - /* FALL THROUGH */ - case SH_FSI_BPFMD_32: - data |= (0x0 << 8); - break; - case SH_FSI_BPFMD_64: - data |= (0x1 << 8); - break; - case SH_FSI_BPFMD_128: - data |= (0x2 << 8); - break; - case SH_FSI_BPFMD_256: - data |= (0x3 << 8); - break; - case SH_FSI_BPFMD_512: - data |= (0x4 << 8); - break; - case SH_FSI_BPFMD_16: - data |= (0x7 << 8); - break; - } - - fsi_reg_mask_set(fsi, CKG1, (ACKMD_MASK | BPFMD_MASK) , data); - udelay(10); - ret = 0; - } - - return ret; -} - /* * pio data transfer handler */ @@ -1637,7 +1536,6 @@ static int fsi_hw_startup(struct fsi_priv *fsi, struct fsi_stream *io, struct device *dev) { - u32 flags = fsi_get_info_flags(fsi); u32 data = 0; /* clock setting */ @@ -1654,19 +1552,6 @@ static int fsi_hw_startup(struct fsi_priv *fsi, data |= (1 << 4); if (fsi_is_clk_master(fsi)) data <<= 8; - /* FIXME - * - * SH_FSI_xxx_INV style will be removed - */ - if (SH_FSI_LRM_INV & flags) - data |= 1 << 12; - if (SH_FSI_BRM_INV & flags) - data |= 1 << 8; - if (SH_FSI_LRS_INV & flags) - data |= 1 << 4; - if (SH_FSI_BRS_INV & flags) - data |= 1 << 0; - fsi_reg_write(fsi, CKG2, data); /* spdif ? */ @@ -1698,7 +1583,7 @@ static int fsi_hw_startup(struct fsi_priv *fsi, /* start master clock */ if (fsi_is_clk_master(fsi)) - return fsi_set_master_clk(dev, fsi, fsi->rate, 1); + return fsi_clk_enable(dev, fsi); return 0; } @@ -1708,7 +1593,7 @@ static int fsi_hw_shutdown(struct fsi_priv *fsi, { /* stop master clock */ if (fsi_is_clk_master(fsi)) - return fsi_set_master_clk(dev, fsi, fsi->rate, 0); + return fsi_clk_disable(dev, fsi); return 0; } @@ -1719,7 +1604,6 @@ static int fsi_dai_startup(struct snd_pcm_substream *substream, struct fsi_priv *fsi = fsi_get_priv(substream); fsi_clk_invalid(fsi); - fsi->rate = 0; return 0; } @@ -1730,7 +1614,6 @@ static void fsi_dai_shutdown(struct snd_pcm_substream *substream, struct fsi_priv *fsi = fsi_get_priv(substream); fsi_clk_invalid(fsi); - fsi->rate = 0; } static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd, @@ -1795,7 +1678,6 @@ static int fsi_set_fmt_spdif(struct fsi_priv *fsi) static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct fsi_priv *fsi = fsi_get_priv_frm_dai(dai); - set_rate_func set_rate = fsi_get_info_set_rate(fsi); int ret; /* set master/slave audio interface */ @@ -1831,14 +1713,6 @@ static int fsi_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } if (fsi_is_clk_master(fsi)) { - /* - * CAUTION - * - * set_rate will be deleted - */ - if (set_rate) - dev_warn(dai->dev, "set_rate will be removed soon\n"); - if (fsi->clk_cpg) fsi_clk_init(dai->dev, fsi, 0, 1, 1, fsi_clk_set_rate_cpg); @@ -1862,10 +1736,8 @@ static int fsi_dai_hw_params(struct snd_pcm_substream *substream, { struct fsi_priv *fsi = fsi_get_priv(substream); - if (fsi_is_clk_master(fsi)) { - fsi->rate = params_rate(params); - fsi_clk_valid(fsi, fsi->rate); - } + if (fsi_is_clk_master(fsi)) + fsi_clk_valid(fsi, params_rate(params)); return 0; } @@ -2017,6 +1889,33 @@ static struct snd_soc_platform_driver fsi_soc_platform = { /* * platform function */ +static void fsi_of_parse(char *name, + struct device_node *np, + struct sh_fsi_port_info *info, + struct device *dev) +{ + int i; + char prop[128]; + unsigned long flags = 0; + struct { + char *name; + unsigned int val; + } of_parse_property[] = { + { "spdif-connection", SH_FSI_FMT_SPDIF }, + { "stream-mode-support", SH_FSI_ENABLE_STREAM_MODE }, + { "use-internal-clock", SH_FSI_CLK_CPG }, + }; + + for (i = 0; i < ARRAY_SIZE(of_parse_property); i++) { + sprintf(prop, "%s,%s", name, of_parse_property[i].name); + if (of_get_property(np, prop, NULL)) + flags |= of_parse_property[i].val; + } + info->flags = flags; + + dev_dbg(dev, "%s flags : %lx\n", name, info->flags); +} + static void fsi_port_info_init(struct fsi_priv *fsi, struct sh_fsi_port_info *info) { @@ -2044,23 +1943,40 @@ static void fsi_handler_init(struct fsi_priv *fsi, } } +static struct of_device_id fsi_of_match[]; static int fsi_probe(struct platform_device *pdev) { struct fsi_master *master; - const struct platform_device_id *id_entry; - struct sh_fsi_platform_info *info = pdev->dev.platform_data; - struct sh_fsi_port_info nul_info, *pinfo; + struct device_node *np = pdev->dev.of_node; + struct sh_fsi_platform_info info; + const struct fsi_core *core; struct fsi_priv *fsi; struct resource *res; unsigned int irq; int ret; - nul_info.flags = 0; - nul_info.tx_id = 0; - nul_info.rx_id = 0; + memset(&info, 0, sizeof(info)); + + core = NULL; + if (np) { + const struct of_device_id *of_id; - id_entry = pdev->id_entry; - if (!id_entry) { + of_id = of_match_device(fsi_of_match, &pdev->dev); + if (of_id) { + core = of_id->data; + fsi_of_parse("fsia", np, &info.port_a, &pdev->dev); + fsi_of_parse("fsib", np, &info.port_b, &pdev->dev); + } + } else { + const struct platform_device_id *id_entry = pdev->id_entry; + if (id_entry) + core = (struct fsi_core *)id_entry->driver_data; + + if (pdev->dev.platform_data) + memcpy(&info, pdev->dev.platform_data, sizeof(info)); + } + + if (!core) { dev_err(&pdev->dev, "unknown fsi device\n"); return -ENODEV; } @@ -2087,17 +2003,15 @@ static int fsi_probe(struct platform_device *pdev) /* master setting */ master->irq = irq; - master->core = (struct fsi_core *)id_entry->driver_data; + master->core = core; spin_lock_init(&master->lock); /* FSI A setting */ - pinfo = (info) ? &info->port_a : &nul_info; fsi = &master->fsia; fsi->base = master->base; fsi->master = master; - fsi->info = pinfo; - fsi_port_info_init(fsi, pinfo); - fsi_handler_init(fsi, pinfo); + fsi_port_info_init(fsi, &info.port_a); + fsi_handler_init(fsi, &info.port_a); ret = fsi_stream_probe(fsi, &pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "FSIA stream probe failed\n"); @@ -2105,13 +2019,11 @@ static int fsi_probe(struct platform_device *pdev) } /* FSI B setting */ - pinfo = (info) ? &info->port_b : &nul_info; fsi = &master->fsib; fsi->base = master->base + 0x40; fsi->master = master; - fsi->info = pinfo; - fsi_port_info_init(fsi, pinfo); - fsi_handler_init(fsi, pinfo); + fsi_port_info_init(fsi, &info.port_b); + fsi_handler_init(fsi, &info.port_b); ret = fsi_stream_probe(fsi, &pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "FSIB stream probe failed\n"); @@ -2122,7 +2034,7 @@ static int fsi_probe(struct platform_device *pdev) dev_set_drvdata(&pdev->dev, master); ret = devm_request_irq(&pdev->dev, irq, &fsi_interrupt, 0, - id_entry->name, master); + dev_name(&pdev->dev), master); if (ret) { dev_err(&pdev->dev, "irq request err\n"); goto exit_fsib; @@ -2248,6 +2160,13 @@ static struct fsi_core fsi2_core = { .b_mclk = B_MST_CTLR, }; +static struct of_device_id fsi_of_match[] = { + { .compatible = "renesas,sh_fsi", .data = &fsi1_core}, + { .compatible = "renesas,sh_fsi2", .data = &fsi2_core}, + {}, +}; +MODULE_DEVICE_TABLE(of, fsi_of_match); + static struct platform_device_id fsi_id_table[] = { { "sh_fsi", (kernel_ulong_t)&fsi1_core }, { "sh_fsi2", (kernel_ulong_t)&fsi2_core }, @@ -2259,6 +2178,7 @@ static struct platform_driver fsi_driver = { .driver = { .name = "fsi-pcm-audio", .pm = &fsi_pm_ops, + .of_match_table = fsi_of_match, }, .probe = fsi_probe, .remove = fsi_remove, diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 5fbfb06e8083..b5b3db71e253 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -33,6 +33,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream) struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->open) { ret = platform->driver->compr_ops->open(cstream); if (ret < 0) { @@ -61,15 +63,46 @@ static int soc_compr_open(struct snd_compr_stream *cstream) codec_dai->active++; rtd->codec->active++; + mutex_unlock(&rtd->pcm_mutex); + return 0; machine_err: if (platform->driver->compr_ops && platform->driver->compr_ops->free) platform->driver->compr_ops->free(cstream); out: + mutex_unlock(&rtd->pcm_mutex); return ret; } +/* + * Power down the audio subsystem pmdown_time msecs after close is called. + * This is to ensure there are no pops or clicks in between any music tracks + * due to DAPM power cycling. + */ +static void close_delayed_work(struct work_struct *work) +{ + struct snd_soc_pcm_runtime *rtd = + container_of(work, struct snd_soc_pcm_runtime, delayed_work.work); + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n", + codec_dai->driver->playback.stream_name, + codec_dai->playback_active ? "active" : "inactive", + rtd->pop_wait ? "yes" : "no"); + + /* are we waiting on this codec DAI stream */ + if (rtd->pop_wait == 1) { + rtd->pop_wait = 0; + snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, + SND_SOC_DAPM_STREAM_STOP); + } + + mutex_unlock(&rtd->pcm_mutex); +} + static int soc_compr_free(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; @@ -78,6 +111,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream) struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_codec *codec = rtd->codec; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (cstream->direction == SND_COMPRESS_PLAYBACK) { cpu_dai->playback_active--; codec_dai->playback_active--; @@ -86,7 +121,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream) codec_dai->capture_active--; } - snd_soc_dai_digital_mute(codec_dai, 1); + snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); cpu_dai->active--; codec_dai->active--; @@ -112,10 +147,11 @@ static int soc_compr_free(struct snd_compr_stream *cstream) snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_STOP); - } else + } else { rtd->pop_wait = 1; schedule_delayed_work(&rtd->delayed_work, msecs_to_jiffies(rtd->pmdown_time)); + } } else { /* capture streams can be powered down now */ snd_soc_dapm_stream_event(rtd, @@ -123,6 +159,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream) SND_SOC_DAPM_STREAM_STOP); } + mutex_unlock(&rtd->pcm_mutex); return 0; } @@ -134,17 +171,25 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd) struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) { ret = platform->driver->compr_ops->trigger(cstream, cmd); if (ret < 0) - return ret; + goto out; } - if (cmd == SNDRV_PCM_TRIGGER_START) - snd_soc_dai_digital_mute(codec_dai, 0); - else if (cmd == SNDRV_PCM_TRIGGER_STOP) - snd_soc_dai_digital_mute(codec_dai, 1); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction); + break; + case SNDRV_PCM_TRIGGER_STOP: + snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction); + break; + } +out: + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -155,6 +200,8 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + /* first we call set_params for the platform driver * this should configure the soc side * if the machine has compressed ops then we call that as well @@ -164,18 +211,20 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream, if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) { ret = platform->driver->compr_ops->set_params(cstream, params); if (ret < 0) - return ret; + goto out; } if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) { ret = rtd->dai_link->compr_ops->set_params(cstream); if (ret < 0) - return ret; + goto out; } snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK, SND_SOC_DAPM_STREAM_START); +out: + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -186,9 +235,12 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->get_params) ret = platform->driver->compr_ops->get_params(cstream, params); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -199,9 +251,12 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps) ret = platform->driver->compr_ops->get_caps(cstream, caps); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -212,9 +267,12 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream, struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps) ret = platform->driver->compr_ops->get_codec_caps(cstream, codec); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -224,9 +282,12 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes) struct snd_soc_platform *platform = rtd->platform; int ret = 0; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->ack) ret = platform->driver->compr_ops->ack(cstream, bytes); + mutex_unlock(&rtd->pcm_mutex); return ret; } @@ -236,12 +297,31 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream, struct snd_soc_pcm_runtime *rtd = cstream->private_data; struct snd_soc_platform *platform = rtd->platform; + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + if (platform->driver->compr_ops && platform->driver->compr_ops->pointer) platform->driver->compr_ops->pointer(cstream, tstamp); + mutex_unlock(&rtd->pcm_mutex); return 0; } +static int soc_compr_copy(struct snd_compr_stream *cstream, + const char __user *buf, size_t count) +{ + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct snd_soc_platform *platform = rtd->platform; + int ret = 0; + + mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass); + + if (platform->driver->compr_ops && platform->driver->compr_ops->copy) + ret = platform->driver->compr_ops->copy(cstream, buf, count); + + mutex_unlock(&rtd->pcm_mutex); + return ret; +} + /* ASoC Compress operations */ static struct snd_compr_ops soc_compr_ops = { .open = soc_compr_open, @@ -259,6 +339,7 @@ static struct snd_compr_ops soc_compr_ops = { int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) { struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_platform *platform = rtd->platform; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; struct snd_compr *compr; @@ -275,20 +356,38 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num) return -ENOMEM; } - compr->ops = &soc_compr_ops; + compr->ops = devm_kzalloc(rtd->card->dev, sizeof(soc_compr_ops), + GFP_KERNEL); + if (compr->ops == NULL) { + dev_err(rtd->card->dev, "Cannot allocate compressed ops\n"); + ret = -ENOMEM; + goto compr_err; + } + memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops)); + + /* Add copy callback for not memory mapped DSPs */ + if (platform->driver->compr_ops && platform->driver->compr_ops->copy) + compr->ops->copy = soc_compr_copy; + mutex_init(&compr->lock); ret = snd_compress_new(rtd->card->snd_card, num, direction, compr); if (ret < 0) { pr_err("compress asoc: can't create compress for codec %s\n", codec->name); - kfree(compr); - return ret; + goto compr_err; } + /* DAPM dai link stream work */ + INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work); + rtd->compr = compr; compr->private_data = rtd; printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name, cpu_dai->name); return ret; + +compr_err: + kfree(compr); + return ret; } diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2370063b5824..8df1b3feaf2b 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1107,6 +1107,10 @@ static int soc_probe_codec(struct snd_soc_card *card, "ASoC: failed to probe CODEC %d\n", ret); goto err_probe; } + WARN(codec->dapm.idle_bias_off && + codec->dapm.bias_level != SND_SOC_BIAS_OFF, + "codec %s can not start from non-off bias" + " with idle_bias_off==1\n", codec->name); } /* If the driver didn't set I/O up try regmap */ @@ -3122,9 +3126,12 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, if (!codec->using_regmap) return -EINVAL; - data = ucontrol->value.bytes.data; len = params->num_regs * codec->val_bytes; + data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + /* * If we've got a mask then we need to preserve the register * bits. We shouldn't modify the incoming data so take a @@ -3137,10 +3144,6 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, val &= params->mask; - data = kmemdup(data, len, GFP_KERNEL); - if (!data) - return -ENOMEM; - switch (codec->val_bytes) { case 1: ((u8 *)data)[0] &= ~params->mask; @@ -3162,8 +3165,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol, ret = regmap_raw_write(codec->control_data, params->base, data, len); - if (params->mask) - kfree(data); + kfree(data); return ret; } @@ -3540,12 +3542,20 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate); * snd_soc_dai_digital_mute - configure DAI system or master clock. * @dai: DAI * @mute: mute enable + * @direction: stream to mute * * Mutes the DAI DAC. */ -int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute) +int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute, + int direction) { - if (dai->driver && dai->driver->ops->digital_mute) + if (!dai->driver) + return -ENOTSUPP; + + if (dai->driver->ops->mute_stream) + return dai->driver->ops->mute_stream(dai, mute, direction); + else if (direction == SNDRV_PCM_STREAM_PLAYBACK && + dai->driver->ops->digital_mute) return dai->driver->ops->digital_mute(dai, mute); else return -ENOTSUPP; @@ -4208,6 +4218,113 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing); +unsigned int snd_soc_of_parse_daifmt(struct device_node *np, + const char *prefix) +{ + int ret, i; + char prop[128]; + unsigned int format = 0; + int bit, frame; + const char *str; + struct { + char *name; + unsigned int val; + } of_fmt_table[] = { + { "i2s", SND_SOC_DAIFMT_I2S }, + { "right_j", SND_SOC_DAIFMT_RIGHT_J }, + { "left_j", SND_SOC_DAIFMT_LEFT_J }, + { "dsp_a", SND_SOC_DAIFMT_DSP_A }, + { "dsp_b", SND_SOC_DAIFMT_DSP_B }, + { "ac97", SND_SOC_DAIFMT_AC97 }, + { "pdm", SND_SOC_DAIFMT_PDM}, + { "msb", SND_SOC_DAIFMT_MSB }, + { "lsb", SND_SOC_DAIFMT_LSB }, + }; + + if (!prefix) + prefix = ""; + + /* + * check "[prefix]format = xxx" + * SND_SOC_DAIFMT_FORMAT_MASK area + */ + snprintf(prop, sizeof(prop), "%sformat", prefix); + ret = of_property_read_string(np, prop, &str); + if (ret == 0) { + for (i = 0; i < ARRAY_SIZE(of_fmt_table); i++) { + if (strcmp(str, of_fmt_table[i].name) == 0) { + format |= of_fmt_table[i].val; + break; + } + } + } + + /* + * check "[prefix]continuous-clock" + * SND_SOC_DAIFMT_CLOCK_MASK area + */ + snprintf(prop, sizeof(prop), "%scontinuous-clock", prefix); + if (of_get_property(np, prop, NULL)) + format |= SND_SOC_DAIFMT_CONT; + else + format |= SND_SOC_DAIFMT_GATED; + + /* + * check "[prefix]bitclock-inversion" + * check "[prefix]frame-inversion" + * SND_SOC_DAIFMT_INV_MASK area + */ + snprintf(prop, sizeof(prop), "%sbitclock-inversion", prefix); + bit = !!of_get_property(np, prop, NULL); + + snprintf(prop, sizeof(prop), "%sframe-inversion", prefix); + frame = !!of_get_property(np, prop, NULL); + + switch ((bit << 4) + frame) { + case 0x11: + format |= SND_SOC_DAIFMT_IB_IF; + break; + case 0x10: + format |= SND_SOC_DAIFMT_IB_NF; + break; + case 0x01: + format |= SND_SOC_DAIFMT_NB_IF; + break; + default: + /* SND_SOC_DAIFMT_NB_NF is default */ + break; + } + + /* + * check "[prefix]bitclock-master" + * check "[prefix]frame-master" + * SND_SOC_DAIFMT_MASTER_MASK area + */ + snprintf(prop, sizeof(prop), "%sbitclock-master", prefix); + bit = !!of_get_property(np, prop, NULL); + + snprintf(prop, sizeof(prop), "%sframe-master", prefix); + frame = !!of_get_property(np, prop, NULL); + + switch ((bit << 4) + frame) { + case 0x11: + format |= SND_SOC_DAIFMT_CBM_CFM; + break; + case 0x10: + format |= SND_SOC_DAIFMT_CBM_CFS; + break; + case 0x01: + format |= SND_SOC_DAIFMT_CBS_CFM; + break; + default: + format |= SND_SOC_DAIFMT_CBS_CFS; + break; + } + + return format; +} +EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt); + static int __init snd_soc_init(void) { #ifdef CONFIG_DEBUG_FS diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 258acadb9e7d..1d6a9b3ceb27 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3255,14 +3255,16 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMU: - ret = snd_soc_dai_digital_mute(sink, 0); + ret = snd_soc_dai_digital_mute(sink, 0, + SNDRV_PCM_STREAM_PLAYBACK); if (ret != 0 && ret != -ENOTSUPP) dev_warn(sink->dev, "ASoC: Failed to unmute: %d\n", ret); ret = 0; break; case SND_SOC_DAPM_PRE_PMD: - ret = snd_soc_dai_digital_mute(sink, 1); + ret = snd_soc_dai_digital_mute(sink, 1, + SNDRV_PCM_STREAM_PLAYBACK); if (ret != 0 && ret != -ENOTSUPP) dev_warn(sink->dev, "ASoC: Failed to mute: %d\n", ret); ret = 0; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index cf191e6aebbe..73bb8eefa491 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -383,8 +383,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) /* Muting the DAC suppresses artifacts caused during digital * shutdown, for example from stopping clocks. */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_soc_dai_digital_mute(codec_dai, 1); + snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); if (cpu_dai->driver->ops->shutdown) cpu_dai->driver->ops->shutdown(substream, cpu_dai); @@ -488,7 +487,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) snd_soc_dapm_stream_event(rtd, substream->stream, SND_SOC_DAPM_STREAM_START); - snd_soc_dai_digital_mute(codec_dai, 0); + snd_soc_dai_digital_mute(codec_dai, 0, substream->stream); out: mutex_unlock(&rtd->pcm_mutex); @@ -586,7 +585,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) /* apply codec digital mute */ if (!codec->active) - snd_soc_dai_digital_mute(codec_dai, 1); + snd_soc_dai_digital_mute(codec_dai, 1, substream->stream); /* free any machine hw params */ if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free) @@ -1729,20 +1728,16 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) /* startup must always be called for new BEs */ ret = dpcm_be_dai_startup(fe, stream); - if (ret < 0) { + if (ret < 0) goto disconnect; - return ret; - } /* keep going if FE state is > open */ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN) return 0; ret = dpcm_be_dai_hw_params(fe, stream); - if (ret < 0) { + if (ret < 0) goto close; - return ret; - } /* keep going if FE state is > hw_params */ if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS) @@ -1750,10 +1745,8 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream) ret = dpcm_be_dai_prepare(fe, stream); - if (ret < 0) { + if (ret < 0) goto hw_free; - return ret; - } /* run the stream event for each BE */ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP); diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 19e5fe7cc403..dbc27ce1d4de 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -6,6 +6,16 @@ config SND_SOC_TEGRA help Say Y or M here if you want support for SoC audio on Tegra. +config SND_SOC_TEGRA20_AC97 + tristate + depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC + select SND_SOC_AC97_BUS + select SND_SOC_TEGRA20_DAS + help + Say Y or M if you want to add support for codecs attached to the + Tegra20 AC97 interface. You will also need to select the individual + machine drivers to support below. + config SND_SOC_TEGRA20_DAS tristate depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC @@ -70,6 +80,15 @@ config SND_SOC_TEGRA_WM8903 boards using the WM8093 codec. Currently, the supported boards are Harmony, Ventana, Seaboard, Kaen, and Aebl. +config SND_SOC_TEGRA_WM9712 + tristate "SoC Audio support for Tegra boards using a WM9712 codec" + depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA20_AC97 + select SND_SOC_WM9712 + help + Say Y or M here if you want to add support for SoC audio on Tegra + boards using the WM9712 (or compatible) codec. + config SND_SOC_TEGRA_TRIMSLICE tristate "SoC Audio support for TrimSlice board" depends on SND_SOC_TEGRA && I2C diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 391e78a34c06..416a14bde41b 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -1,6 +1,7 @@ # Tegra platform Support snd-soc-tegra-pcm-objs := tegra_pcm.o snd-soc-tegra-utils-objs += tegra_asoc_utils.o +snd-soc-tegra20-ac97-objs := tegra20_ac97.o snd-soc-tegra20-das-objs := tegra20_das.o snd-soc-tegra20-i2s-objs := tegra20_i2s.o snd-soc-tegra20-spdif-objs := tegra20_spdif.o @@ -9,6 +10,7 @@ snd-soc-tegra30-i2s-objs := tegra30_i2s.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o +obj-$(CONFIG_SND_SOC_TEGRA20_AC97) += snd-soc-tegra20-ac97.o obj-$(CONFIG_SND_SOC_TEGRA20_DAS) += snd-soc-tegra20-das.o obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o obj-$(CONFIG_SND_SOC_TEGRA20_SPDIF) += snd-soc-tegra20-spdif.o @@ -18,10 +20,12 @@ obj-$(CONFIG_SND_SOC_TEGRA30_I2S) += snd-soc-tegra30-i2s.o # Tegra machine Support snd-soc-tegra-wm8753-objs := tegra_wm8753.o snd-soc-tegra-wm8903-objs := tegra_wm8903.o +snd-soc-tegra-wm9712-objs := tegra_wm9712.o snd-soc-tegra-trimslice-objs := trimslice.o snd-soc-tegra-alc5632-objs := tegra_alc5632.o obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o +obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c new file mode 100644 index 000000000000..336dcdd3e8a4 --- /dev/null +++ b/sound/soc/tegra/tegra20_ac97.c @@ -0,0 +1,480 @@ +/* + * tegra20_ac97.c - Tegra20 AC97 platform driver + * + * Copyright (c) 2012 Lucas Stach <[email protected]> + * + * Partly based on code copyright/by: + * + * Copyright (c) 2011,2012 Toradex 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 <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/gpio.h> +#include <linux/io.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#include "tegra_asoc_utils.h" +#include "tegra20_ac97.h" + +#define DRV_NAME "tegra20-ac97" + +static struct tegra20_ac97 *workdata; + +static void tegra20_ac97_codec_reset(struct snd_ac97 *ac97) +{ + u32 readback; + unsigned long timeout; + + /* reset line is not driven by DAC pad group, have to toggle GPIO */ + gpio_set_value(workdata->reset_gpio, 0); + udelay(2); + + gpio_set_value(workdata->reset_gpio, 1); + udelay(2); + + timeout = jiffies + msecs_to_jiffies(100); + + do { + regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback); + if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY) + break; + usleep_range(1000, 2000); + } while (!time_after(jiffies, timeout)); +} + +static void tegra20_ac97_codec_warm_reset(struct snd_ac97 *ac97) +{ + u32 readback; + unsigned long timeout; + + /* + * although sync line is driven by the DAC pad group warm reset using + * the controller cmd is not working, have to toggle sync line + * manually. + */ + gpio_request(workdata->sync_gpio, "codec-sync"); + + gpio_direction_output(workdata->sync_gpio, 1); + + udelay(2); + gpio_set_value(workdata->sync_gpio, 0); + udelay(2); + gpio_free(workdata->sync_gpio); + + timeout = jiffies + msecs_to_jiffies(100); + + do { + regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback); + if (readback & TEGRA20_AC97_STATUS1_CODEC1_RDY) + break; + usleep_range(1000, 2000); + } while (!time_after(jiffies, timeout)); +} + +static unsigned short tegra20_ac97_codec_read(struct snd_ac97 *ac97_snd, + unsigned short reg) +{ + u32 readback; + unsigned long timeout; + + regmap_write(workdata->regmap, TEGRA20_AC97_CMD, + (((reg | 0x80) << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) & + TEGRA20_AC97_CMD_CMD_ADDR_MASK) | + TEGRA20_AC97_CMD_BUSY); + + timeout = jiffies + msecs_to_jiffies(100); + + do { + regmap_read(workdata->regmap, TEGRA20_AC97_STATUS1, &readback); + if (readback & TEGRA20_AC97_STATUS1_STA_VALID1) + break; + usleep_range(1000, 2000); + } while (!time_after(jiffies, timeout)); + + return ((readback & TEGRA20_AC97_STATUS1_STA_DATA1_MASK) >> + TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT); +} + +static void tegra20_ac97_codec_write(struct snd_ac97 *ac97_snd, + unsigned short reg, unsigned short val) +{ + u32 readback; + unsigned long timeout; + + regmap_write(workdata->regmap, TEGRA20_AC97_CMD, + ((reg << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) & + TEGRA20_AC97_CMD_CMD_ADDR_MASK) | + ((val << TEGRA20_AC97_CMD_CMD_DATA_SHIFT) & + TEGRA20_AC97_CMD_CMD_DATA_MASK) | + TEGRA20_AC97_CMD_BUSY); + + timeout = jiffies + msecs_to_jiffies(100); + + do { + regmap_read(workdata->regmap, TEGRA20_AC97_CMD, &readback); + if (!(readback & TEGRA20_AC97_CMD_BUSY)) + break; + usleep_range(1000, 2000); + } while (!time_after(jiffies, timeout)); +} + +struct snd_ac97_bus_ops soc_ac97_ops = { + .read = tegra20_ac97_codec_read, + .write = tegra20_ac97_codec_write, + .reset = tegra20_ac97_codec_reset, + .warm_reset = tegra20_ac97_codec_warm_reset, +}; +EXPORT_SYMBOL_GPL(soc_ac97_ops); + +static inline void tegra20_ac97_start_playback(struct tegra20_ac97 *ac97) +{ + regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, + TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN, + TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN); + + regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL, + TEGRA20_AC97_CTRL_PCM_DAC_EN | + TEGRA20_AC97_CTRL_STM_EN, + TEGRA20_AC97_CTRL_PCM_DAC_EN | + TEGRA20_AC97_CTRL_STM_EN); +} + +static inline void tegra20_ac97_stop_playback(struct tegra20_ac97 *ac97) +{ + regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, + TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN, 0); + + regmap_update_bits(ac97->regmap, TEGRA20_AC97_CTRL, + TEGRA20_AC97_CTRL_PCM_DAC_EN, 0); +} + +static inline void tegra20_ac97_start_capture(struct tegra20_ac97 *ac97) +{ + regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, + TEGRA20_AC97_FIFO_SCR_REC_FULL_EN, + TEGRA20_AC97_FIFO_SCR_REC_FULL_EN); +} + +static inline void tegra20_ac97_stop_capture(struct tegra20_ac97 *ac97) +{ + regmap_update_bits(ac97->regmap, TEGRA20_AC97_FIFO1_SCR, + TEGRA20_AC97_FIFO_SCR_REC_FULL_EN, 0); +} + +static int tegra20_ac97_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra20_ac97_start_playback(ac97); + else + tegra20_ac97_start_capture(ac97); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + tegra20_ac97_stop_playback(ac97); + else + tegra20_ac97_stop_capture(ac97); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops tegra20_ac97_dai_ops = { + .trigger = tegra20_ac97_trigger, +}; + +static int tegra20_ac97_probe(struct snd_soc_dai *dai) +{ + struct tegra20_ac97 *ac97 = snd_soc_dai_get_drvdata(dai); + + dai->capture_dma_data = &ac97->capture_dma_data; + dai->playback_dma_data = &ac97->playback_dma_data; + + return 0; +} + +static struct snd_soc_dai_driver tegra20_ac97_dai = { + .name = "tegra-ac97-pcm", + .ac97_control = 1, + .probe = tegra20_ac97_probe, + .playback = { + .stream_name = "PCM Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "PCM Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &tegra20_ac97_dai_ops, +}; + +static bool tegra20_ac97_wr_rd_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA20_AC97_CTRL: + case TEGRA20_AC97_CMD: + case TEGRA20_AC97_STATUS1: + case TEGRA20_AC97_FIFO1_SCR: + case TEGRA20_AC97_FIFO_TX1: + case TEGRA20_AC97_FIFO_RX1: + return true; + default: + break; + } + + return false; +} + +static bool tegra20_ac97_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA20_AC97_STATUS1: + case TEGRA20_AC97_FIFO1_SCR: + case TEGRA20_AC97_FIFO_TX1: + case TEGRA20_AC97_FIFO_RX1: + return true; + default: + break; + } + + return false; +} + +static bool tegra20_ac97_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TEGRA20_AC97_FIFO_TX1: + case TEGRA20_AC97_FIFO_RX1: + return true; + default: + break; + } + + return false; +} + +static const struct regmap_config tegra20_ac97_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = TEGRA20_AC97_FIFO_RX1, + .writeable_reg = tegra20_ac97_wr_rd_reg, + .readable_reg = tegra20_ac97_wr_rd_reg, + .volatile_reg = tegra20_ac97_volatile_reg, + .precious_reg = tegra20_ac97_precious_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static int tegra20_ac97_platform_probe(struct platform_device *pdev) +{ + struct tegra20_ac97 *ac97; + struct resource *mem, *memregion; + u32 of_dma[2]; + void __iomem *regs; + int ret = 0; + + ac97 = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_ac97), + GFP_KERNEL); + if (!ac97) { + dev_err(&pdev->dev, "Can't allocate tegra20_ac97\n"); + ret = -ENOMEM; + goto err; + } + dev_set_drvdata(&pdev->dev, ac97); + + ac97->clk_ac97 = clk_get(&pdev->dev, NULL); + if (IS_ERR(ac97->clk_ac97)) { + dev_err(&pdev->dev, "Can't retrieve ac97 clock\n"); + ret = PTR_ERR(ac97->clk_ac97); + goto err; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "No memory resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + memregion = devm_request_mem_region(&pdev->dev, mem->start, + resource_size(mem), DRV_NAME); + if (!memregion) { + dev_err(&pdev->dev, "Memory region already claimed\n"); + ret = -EBUSY; + goto err_clk_put; + } + + regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); + if (!regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto err_clk_put; + } + + ac97->regmap = devm_regmap_init_mmio(&pdev->dev, regs, + &tegra20_ac97_regmap_config); + if (IS_ERR(ac97->regmap)) { + dev_err(&pdev->dev, "regmap init failed\n"); + ret = PTR_ERR(ac97->regmap); + goto err_clk_put; + } + + if (of_property_read_u32_array(pdev->dev.of_node, + "nvidia,dma-request-selector", + of_dma, 2) < 0) { + dev_err(&pdev->dev, "No DMA resource\n"); + ret = -ENODEV; + goto err_clk_put; + } + + ac97->reset_gpio = of_get_named_gpio(pdev->dev.of_node, + "nvidia,codec-reset-gpio", 0); + if (gpio_is_valid(ac97->reset_gpio)) { + ret = devm_gpio_request_one(&pdev->dev, ac97->reset_gpio, + GPIOF_OUT_INIT_HIGH, "codec-reset"); + if (ret) { + dev_err(&pdev->dev, "could not get codec-reset GPIO\n"); + goto err_clk_put; + } + } else { + dev_err(&pdev->dev, "no codec-reset GPIO supplied\n"); + goto err_clk_put; + } + + ac97->sync_gpio = of_get_named_gpio(pdev->dev.of_node, + "nvidia,codec-sync-gpio", 0); + if (!gpio_is_valid(ac97->sync_gpio)) { + dev_err(&pdev->dev, "no codec-sync GPIO supplied\n"); + goto err_clk_put; + } + + ac97->capture_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_RX1; + ac97->capture_dma_data.wrap = 4; + ac97->capture_dma_data.width = 32; + ac97->capture_dma_data.req_sel = of_dma[1]; + + ac97->playback_dma_data.addr = mem->start + TEGRA20_AC97_FIFO_TX1; + ac97->playback_dma_data.wrap = 4; + ac97->playback_dma_data.width = 32; + ac97->playback_dma_data.req_sel = of_dma[1]; + + ret = snd_soc_register_dais(&pdev->dev, &tegra20_ac97_dai, 1); + if (ret) { + dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); + ret = -ENOMEM; + goto err_clk_put; + } + + ret = tegra_pcm_platform_register(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Could not register PCM: %d\n", ret); + goto err_unregister_dai; + } + + ret = tegra_asoc_utils_init(&ac97->util_data, &pdev->dev); + if (ret) + goto err_unregister_pcm; + + ret = tegra_asoc_utils_set_ac97_rate(&ac97->util_data); + if (ret) + goto err_asoc_utils_fini; + + ret = clk_prepare_enable(ac97->clk_ac97); + if (ret) { + dev_err(&pdev->dev, "clk_enable failed: %d\n", ret); + goto err_asoc_utils_fini; + } + + /* XXX: crufty ASoC AC97 API - only one AC97 codec allowed */ + workdata = ac97; + + return 0; + +err_asoc_utils_fini: + tegra_asoc_utils_fini(&ac97->util_data); +err_unregister_pcm: + tegra_pcm_platform_unregister(&pdev->dev); +err_unregister_dai: + snd_soc_unregister_dai(&pdev->dev); +err_clk_put: + clk_put(ac97->clk_ac97); +err: + return ret; +} + +static int tegra20_ac97_platform_remove(struct platform_device *pdev) +{ + struct tegra20_ac97 *ac97 = dev_get_drvdata(&pdev->dev); + + tegra_pcm_platform_unregister(&pdev->dev); + snd_soc_unregister_dai(&pdev->dev); + + tegra_asoc_utils_fini(&ac97->util_data); + + clk_disable_unprepare(ac97->clk_ac97); + clk_put(ac97->clk_ac97); + + return 0; +} + +static const struct of_device_id tegra20_ac97_of_match[] = { + { .compatible = "nvidia,tegra20-ac97", }, + {}, +}; + +static struct platform_driver tegra20_ac97_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = tegra20_ac97_of_match, + }, + .probe = tegra20_ac97_platform_probe, + .remove = tegra20_ac97_platform_remove, +}; +module_platform_driver(tegra20_ac97_driver); + +MODULE_AUTHOR("Lucas Stach"); +MODULE_DESCRIPTION("Tegra20 AC97 ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra20_ac97_of_match); diff --git a/sound/soc/tegra/tegra20_ac97.h b/sound/soc/tegra/tegra20_ac97.h new file mode 100644 index 000000000000..dddc6828004e --- /dev/null +++ b/sound/soc/tegra/tegra20_ac97.h @@ -0,0 +1,95 @@ +/* + * tegra20_ac97.h - Definitions for the Tegra20 AC97 controller driver + * + * Copyright (c) 2012 Lucas Stach <[email protected]> + * + * Partly based on code copyright/by: + * + * Copyright (c) 2011,2012 Toradex Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#ifndef __TEGRA20_AC97_H__ +#define __TEGRA20_AC97_H__ + +#include "tegra_pcm.h" + +#define TEGRA20_AC97_CTRL 0x00 +#define TEGRA20_AC97_CMD 0x04 +#define TEGRA20_AC97_STATUS1 0x08 +/* ... */ +#define TEGRA20_AC97_FIFO1_SCR 0x1c +/* ... */ +#define TEGRA20_AC97_FIFO_TX1 0x40 +#define TEGRA20_AC97_FIFO_RX1 0x80 + +/* TEGRA20_AC97_CTRL */ +#define TEGRA20_AC97_CTRL_STM2_EN (1 << 16) +#define TEGRA20_AC97_CTRL_DOUBLE_SAMPLING_EN (1 << 11) +#define TEGRA20_AC97_CTRL_IO_CNTRL_EN (1 << 10) +#define TEGRA20_AC97_CTRL_HSET_DAC_EN (1 << 9) +#define TEGRA20_AC97_CTRL_LINE2_DAC_EN (1 << 8) +#define TEGRA20_AC97_CTRL_PCM_LFE_EN (1 << 7) +#define TEGRA20_AC97_CTRL_PCM_SUR_EN (1 << 6) +#define TEGRA20_AC97_CTRL_PCM_CEN_DAC_EN (1 << 5) +#define TEGRA20_AC97_CTRL_LINE1_DAC_EN (1 << 4) +#define TEGRA20_AC97_CTRL_PCM_DAC_EN (1 << 3) +#define TEGRA20_AC97_CTRL_COLD_RESET (1 << 2) +#define TEGRA20_AC97_CTRL_WARM_RESET (1 << 1) +#define TEGRA20_AC97_CTRL_STM_EN (1 << 0) + +/* TEGRA20_AC97_CMD */ +#define TEGRA20_AC97_CMD_CMD_ADDR_SHIFT 24 +#define TEGRA20_AC97_CMD_CMD_ADDR_MASK (0xff << TEGRA20_AC97_CMD_CMD_ADDR_SHIFT) +#define TEGRA20_AC97_CMD_CMD_DATA_SHIFT 8 +#define TEGRA20_AC97_CMD_CMD_DATA_MASK (0xffff << TEGRA20_AC97_CMD_CMD_DATA_SHIFT) +#define TEGRA20_AC97_CMD_CMD_ID_SHIFT 2 +#define TEGRA20_AC97_CMD_CMD_ID_MASK (0x3 << TEGRA20_AC97_CMD_CMD_ID_SHIFT) +#define TEGRA20_AC97_CMD_BUSY (1 << 0) + +/* TEGRA20_AC97_STATUS1 */ +#define TEGRA20_AC97_STATUS1_STA_ADDR1_SHIFT 24 +#define TEGRA20_AC97_STATUS1_STA_ADDR1_MASK (0xff << TEGRA20_AC97_STATUS1_STA_ADDR1_SHIFT) +#define TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT 8 +#define TEGRA20_AC97_STATUS1_STA_DATA1_MASK (0xffff << TEGRA20_AC97_STATUS1_STA_DATA1_SHIFT) +#define TEGRA20_AC97_STATUS1_STA_VALID1 (1 << 2) +#define TEGRA20_AC97_STATUS1_STANDBY1 (1 << 1) +#define TEGRA20_AC97_STATUS1_CODEC1_RDY (1 << 0) + +/* TEGRA20_AC97_FIFO1_SCR */ +#define TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_SHIFT 27 +#define TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_MASK (0x1f << TEGRA20_AC97_FIFO_SCR_REC_MT_CNT_SHIFT) +#define TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_SHIFT 22 +#define TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_MASK (0x1f << TEGRA20_AC97_FIFO_SCR_PB_MT_CNT_SHIFT) +#define TEGRA20_AC97_FIFO_SCR_REC_OVERRUN_INT_STA (1 << 19) +#define TEGRA20_AC97_FIFO_SCR_PB_UNDERRUN_INT_STA (1 << 18) +#define TEGRA20_AC97_FIFO_SCR_REC_FORCE_MT (1 << 17) +#define TEGRA20_AC97_FIFO_SCR_PB_FORCE_MT (1 << 16) +#define TEGRA20_AC97_FIFO_SCR_REC_FULL_EN (1 << 15) +#define TEGRA20_AC97_FIFO_SCR_REC_3QRT_FULL_EN (1 << 14) +#define TEGRA20_AC97_FIFO_SCR_REC_QRT_FULL_EN (1 << 13) +#define TEGRA20_AC97_FIFO_SCR_REC_EMPTY_EN (1 << 12) +#define TEGRA20_AC97_FIFO_SCR_PB_NOT_FULL_EN (1 << 11) +#define TEGRA20_AC97_FIFO_SCR_PB_QRT_MT_EN (1 << 10) +#define TEGRA20_AC97_FIFO_SCR_PB_3QRT_MT_EN (1 << 9) +#define TEGRA20_AC97_FIFO_SCR_PB_EMPTY_MT_EN (1 << 8) + +struct tegra20_ac97 { + struct clk *clk_ac97; + struct tegra_pcm_dma_params capture_dma_data; + struct tegra_pcm_dma_params playback_dma_data; + struct regmap *regmap; + int reset_gpio; + int sync_gpio; + struct tegra_asoc_utils_data util_data; +}; +#endif /* __TEGRA20_AC97_H__ */ diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c index 654318483877..e72392927bd2 100644 --- a/sound/soc/tegra/tegra20_das.c +++ b/sound/soc/tegra/tegra20_das.c @@ -191,6 +191,19 @@ static int tegra20_das_probe(struct platform_device *pdev) goto err; } + ret = tegra20_das_connect_dap_to_dac(TEGRA20_DAS_DAP_ID_3, + TEGRA20_DAS_DAP_SEL_DAC3); + if (ret) { + dev_err(&pdev->dev, "Can't set up DAS DAP connection\n"); + goto err; + } + ret = tegra20_das_connect_dac_to_dap(TEGRA20_DAS_DAC_ID_3, + TEGRA20_DAS_DAC_SEL_DAP3); + if (ret) { + dev_err(&pdev->dev, "Can't set up DAS DAC connection\n"); + goto err; + } + platform_set_drvdata(pdev, das); return 0; diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c index f354dc390a0b..dd146f10fef2 100644 --- a/sound/soc/tegra/tegra30_ahub.c +++ b/sound/soc/tegra/tegra30_ahub.c @@ -580,7 +580,7 @@ err_clk_put_apbif: clk_put(ahub->clk_apbif); err_clk_put_d_audio: clk_put(ahub->clk_d_audio); - ahub = 0; + ahub = NULL; err: return ret; } @@ -597,7 +597,7 @@ static int tegra30_ahub_remove(struct platform_device *pdev) clk_put(ahub->clk_apbif); clk_put(ahub->clk_d_audio); - ahub = 0; + ahub = NULL; return 0; } diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c index 27e91dd0b91c..f4e1ce82750a 100644 --- a/sound/soc/tegra/tegra30_i2s.c +++ b/sound/soc/tegra/tegra30_i2s.c @@ -71,7 +71,7 @@ static int tegra30_i2s_runtime_resume(struct device *dev) return 0; } -int tegra30_i2s_startup(struct snd_pcm_substream *substream, +static int tegra30_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); @@ -98,7 +98,7 @@ int tegra30_i2s_startup(struct snd_pcm_substream *substream, return ret; } -void tegra30_i2s_shutdown(struct snd_pcm_substream *substream, +static void tegra30_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai); diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c index 6872c77a1196..ba419f86384d 100644 --- a/sound/soc/tegra/tegra_asoc_utils.c +++ b/sound/soc/tegra/tegra_asoc_utils.c @@ -112,6 +112,59 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, } EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate); +int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data) +{ + const int pll_rate = 73728000; + const int ac97_rate = 24576000; + int err; + + clk_disable_unprepare(data->clk_cdev1); + clk_disable_unprepare(data->clk_pll_a_out0); + clk_disable_unprepare(data->clk_pll_a); + + /* + * AC97 rate is fixed at 24.576MHz and is used for both the host + * controller and the external codec + */ + err = clk_set_rate(data->clk_pll_a, pll_rate); + if (err) { + dev_err(data->dev, "Can't set pll_a rate: %d\n", err); + return err; + } + + err = clk_set_rate(data->clk_pll_a_out0, ac97_rate); + if (err) { + dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err); + return err; + } + + /* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */ + + err = clk_prepare_enable(data->clk_pll_a); + if (err) { + dev_err(data->dev, "Can't enable pll_a: %d\n", err); + return err; + } + + err = clk_prepare_enable(data->clk_pll_a_out0); + if (err) { + dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err); + return err; + } + + err = clk_prepare_enable(data->clk_cdev1); + if (err) { + dev_err(data->dev, "Can't enable cdev1: %d\n", err); + return err; + } + + data->set_baseclock = pll_rate; + data->set_mclk = ac97_rate; + + return 0; +} +EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate); + int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, struct device *dev) { diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h index 44db1dbb8f21..974c9f8830f9 100644 --- a/sound/soc/tegra/tegra_asoc_utils.h +++ b/sound/soc/tegra/tegra_asoc_utils.h @@ -43,6 +43,7 @@ struct tegra_asoc_utils_data { int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, int mclk); +int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data); int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, struct device *dev); void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data); diff --git a/sound/soc/tegra/tegra_wm9712.c b/sound/soc/tegra/tegra_wm9712.c new file mode 100644 index 000000000000..68d42403d9b5 --- /dev/null +++ b/sound/soc/tegra/tegra_wm9712.c @@ -0,0 +1,176 @@ +/* + * tegra20_wm9712.c - Tegra machine ASoC driver for boards using WM9712 codec. + * + * Copyright 2012 Lucas Stach <[email protected]> + * + * Partly based on code copyright/by: + * Copyright 2011,2012 Toradex 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 <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> + +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> + +#define DRV_NAME "tegra-snd-wm9712" + +struct tegra_wm9712 { + struct platform_device *codec; +}; + +static const struct snd_soc_dapm_widget tegra_wm9712_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_LINE("LineIn", NULL), + SND_SOC_DAPM_MIC("Mic", NULL), +}; + +static int tegra_wm9712_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + + return snd_soc_dapm_sync(dapm); +} + +static struct snd_soc_dai_link tegra_wm9712_dai = { + .name = "AC97 HiFi", + .stream_name = "AC97 HiFi", + .cpu_dai_name = "tegra-ac97-pcm", + .codec_dai_name = "wm9712-hifi", + .codec_name = "wm9712-codec", + .init = tegra_wm9712_init, +}; + +static struct snd_soc_card snd_soc_tegra_wm9712 = { + .name = "tegra-wm9712", + .owner = THIS_MODULE, + .dai_link = &tegra_wm9712_dai, + .num_links = 1, + + .dapm_widgets = tegra_wm9712_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tegra_wm9712_dapm_widgets), + .fully_routed = true, +}; + +static int tegra_wm9712_driver_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct snd_soc_card *card = &snd_soc_tegra_wm9712; + struct tegra_wm9712 *machine; + int ret; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "No platform data supplied\n"); + return -EINVAL; + } + + machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm9712), + GFP_KERNEL); + if (!machine) { + dev_err(&pdev->dev, "Can't allocate tegra_wm9712 struct\n"); + return -ENOMEM; + } + + card->dev = &pdev->dev; + platform_set_drvdata(pdev, card); + snd_soc_card_set_drvdata(card, machine); + + machine->codec = platform_device_alloc("wm9712-codec", -1); + if (!machine->codec) { + dev_err(&pdev->dev, "Can't allocate wm9712 platform device\n"); + return -ENOMEM; + } + + ret = platform_device_add(machine->codec); + if (ret) + goto codec_put; + + ret = snd_soc_of_parse_card_name(card, "nvidia,model"); + if (ret) + goto codec_unregister; + + ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing"); + if (ret) + goto codec_unregister; + + tegra_wm9712_dai.cpu_of_node = of_parse_phandle(np, + "nvidia,ac97-controller", 0); + if (!tegra_wm9712_dai.cpu_of_node) { + dev_err(&pdev->dev, + "Property 'nvidia,ac97-controller' missing or invalid\n"); + ret = -EINVAL; + goto codec_unregister; + } + + tegra_wm9712_dai.platform_of_node = tegra_wm9712_dai.cpu_of_node; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto codec_unregister; + } + + return 0; + +codec_unregister: + platform_device_del(machine->codec); +codec_put: + platform_device_put(machine->codec); + return ret; +} + +static int tegra_wm9712_driver_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct tegra_wm9712 *machine = snd_soc_card_get_drvdata(card); + + snd_soc_unregister_card(card); + + platform_device_unregister(machine->codec); + + return 0; +} + +static const struct of_device_id tegra_wm9712_of_match[] = { + { .compatible = "nvidia,tegra-audio-wm9712", }, + {}, +}; + +static struct platform_driver tegra_wm9712_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = tegra_wm9712_of_match, + }, + .probe = tegra_wm9712_driver_probe, + .remove = tegra_wm9712_driver_remove, +}; +module_platform_driver(tegra_wm9712_driver); + +MODULE_AUTHOR("Lucas Stach"); +MODULE_DESCRIPTION("Tegra+WM9712 machine ASoC driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, tegra_wm9712_of_match); diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c index ae6990738783..204b899c2311 100644 --- a/sound/soc/ux500/mop500.c +++ b/sound/soc/ux500/mop500.c @@ -24,7 +24,7 @@ #include "ux500_pcm.h" #include "ux500_msp_dai.h" -#include <mop500_ab8500.h> +#include "mop500_ab8500.h" /* Define the whole MOP500 soundcard, linking platform to the codec-drivers */ struct snd_soc_dai_link mop500_dai_links[] = { diff --git a/tools/vm/.gitignore b/tools/vm/.gitignore new file mode 100644 index 000000000000..44f095fa2604 --- /dev/null +++ b/tools/vm/.gitignore @@ -0,0 +1,2 @@ +slabinfo +page-types |